xref: /xnu-12377.1.9/iokit/Exclaves/Exclaves.cpp (revision f6217f891ac0bb64f3d375211650a4c1ff8ca1ea)
1 /*
2  * Copyright (c) 1998-2023 Apple Inc. All rights reserved.
3  *
4  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5  *
6  * This file contains Original Code and/or Modifications of Original Code
7  * as defined in and that are subject to the Apple Public Source License
8  * Version 2.0 (the 'License'). You may not use this file except in
9  * compliance with the License. The rights granted to you under the License
10  * may not be used to create, or enable the creation or redistribution of,
11  * unlawful or unlicensed copies of an Apple operating system, or to
12  * circumvent, violate, or enable the circumvention or violation of, any
13  * terms of an Apple operating system software license agreement.
14  *
15  * Please obtain a copy of the License at
16  * http://www.opensource.apple.com/apsl/ and read it before using this file.
17  *
18  * The Original Code and all software distributed under the License are
19  * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23  * Please see the License for the specific language governing rights and
24  * limitations under the License.
25  *
26  * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27  */
28 
29 #include <IOKit/IOService.h>
30 #include <IOKit/IOInterruptEventSource.h>
31 #include <IOKit/IOTimerEventSource.h>
32 #include <IOKit/IOMapper.h>
33 #include <IOKit/pwr_mgt/RootDomain.h>
34 #include "../Kernel/IOServicePrivate.h"
35 
36 #include <Exclaves/Exclaves.h>
37 
38 #if CONFIG_EXCLAVES
39 #include <mach/exclaves.h>
40 #include <Exclaves/IOService.tightbeam.h>
41 
42 #define EXLOG(x...)  do { \
43     if (kIOLogExclaves & gIOKitDebug) \
44 	IOLog(x); \
45 } while (false)
46 
47 /* Global IOExclaveProxyState lookup table */
48 
49 OSDictionary    *gExclaveProxyStates;
50 IORecursiveLock *gExclaveProxyStateLock;
51 const OSSymbol  *gDARTMapperFunctionSetActive;
52 
53 /* IOExclaveProxyState */
54 
55 class IOExclaveWorkLoopAperture {
56 public:
57 	IOWorkLoop *workLoop;
58 	void
closeGate()59 	closeGate()
60 	{
61 		workLoop->closeGate();
62 	}
63 	void
openGate()64 	openGate()
65 	{
66 		workLoop->openGate();
67 	}
68 };
69 
70 #endif /* CONFIG_EXCLAVES */
71 
72 struct IOService::IOExclaveProxyState {
73 	IOService    *service;
74 	uint64_t      mach_endpoint;
75 #if CONFIG_EXCLAVES
76 	tb_endpoint_t tb_endpoint;
77 	ioservice_ioserviceconcrete client;
78 	// ExclaveDriverKit related state
79 	uint64_t      edk_mach_endpoint;
80 	tb_endpoint_t edk_tb_endpoint;
81 	ioservice_ioserviceprivate edk_client;
82 	OSDictionary *exclave_interrupts;
83 	IOLock *exclave_interrupts_lock;
84 	OSDictionary *exclave_timers;
85 	IOLock *exclave_timers_lock;
86 	uint32_t nextExclaveTimerId;
87 
88 	// TODO: implement properly once ExclaveAperture removed
89 	IOExclaveWorkLoopAperture *ewla;
90 
91 	IOLock * exclaveAsyncNotificationEventSourcesLock;
92 	OSArray *exclaveAsyncNotificationEventSources;
93 
94 	// ANE specific upcalls
95 	ANEUpcallSetPowerStateHandler aneSetPowerStateUpcallHandler;
96 	ANEUpcallWorkHandler aneWorkSubmitUpcallHandler;
97 	ANEUpcallWorkHandler aneWorkBeginUpcallHandler;
98 	ANEUpcallWorkHandler aneWorkEndUpcallHandler;
99 #endif /* CONFIG_EXCLAVES */
100 };
101 
102 #if CONFIG_EXCLAVES
103 class IOExclaveProxyStateWrapper : public OSObject {
104 	OSDeclareFinalStructors(IOExclaveProxyStateWrapper);
105 public:
106 	IOService::IOExclaveProxyState *proxyState;
107 };
108 OSDefineMetaClassAndFinalStructors(IOExclaveProxyStateWrapper, OSObject);
109 
110 extern "C" kern_return_t
111 exclaves_driver_service_lookup(const char *service_name, uint64_t *endpoint);
112 #endif /* CONFIG_EXCLAVES */
113 
114 bool
exclaveStart(IOService * provider,IOExclaveProxyState ** pRef)115 IOService::exclaveStart(IOService * provider, IOExclaveProxyState ** pRef)
116 {
117 	IOExclaveProxyState * ref;
118 
119 	ref = NULL;
120 #if CONFIG_EXCLAVES
121 	int err = 1;
122 	do {
123 		char     key[16];
124 		uint64_t serviceID;
125 		uint64_t mach_endpoint = 0;
126 		uint64_t edk_mach_endpoint = 0;
127 		tb_error_t                   tberr;
128 		tb_endpoint_t                tb_endpoint;
129 		tb_endpoint_t                edk_tb_endpoint;
130 		ioservice_ioserviceconcrete  client;
131 		ioservice_ioserviceprivate   edk_client;
132 		OSObject *                   prop;
133 		OSData *                     data;
134 		IOWorkLoop *                 wl;
135 		IOExclaveProxyStateWrapper * wrapper;
136 		bool result;
137 
138 		// exit early if Exclaves are not available
139 		if (exclaves_get_status() == EXCLAVES_STATUS_NOT_SUPPORTED) {
140 			break;
141 		}
142 
143 		prop = provider->copyProperty("exclave-endpoint");
144 		if ((data = OSDynamicCast(OSData, prop))) {
145 			mach_endpoint = ((uint32_t *)data->getBytesNoCopy())[0];
146 		}
147 		OSSafeReleaseNULL(prop);
148 
149 		prop = provider->copyProperty("exclave-service");
150 		if ((data = OSDynamicCast(OSData, prop))) {
151 			const char *exclave_service = (const char *) data->getBytesNoCopy();
152 			if (exclave_service[data->getLength() - 1] != '\0') {
153 				IOLog("%s: %s-0x%qx exclave-service property is invalid\n", __func__, provider->getName(), provider->getRegistryEntryID());
154 				OSSafeReleaseNULL(prop);
155 				break;
156 			}
157 			if (exclaves_driver_service_lookup(exclave_service, &mach_endpoint) != KERN_SUCCESS) {
158 				IOLog("%s: %s-0x%qx could not find exclave-service %s\n", __func__, provider->getName(), provider->getRegistryEntryID(), exclave_service);
159 				OSSafeReleaseNULL(prop);
160 				break;
161 			}
162 		}
163 		OSSafeReleaseNULL(prop);
164 
165 		prop = provider->copyProperty("exclave-edk-endpoint");
166 		if ((data = OSDynamicCast(OSData, prop))) {
167 			edk_mach_endpoint = ((uint32_t *)data->getBytesNoCopy())[0];
168 		}
169 		OSSafeReleaseNULL(prop);
170 
171 		prop = provider->copyProperty("exclave-edk-service");
172 		if ((data = OSDynamicCast(OSData, prop))) {
173 			const char *exclave_edk_service = (const char *) data->getBytesNoCopy();
174 			if (exclave_edk_service[data->getLength() - 1] != '\0') {
175 				IOLog("%s: %s-0x%qx exclave-edk-service property is invalid\n", __func__, provider->getName(), provider->getRegistryEntryID());
176 				OSSafeReleaseNULL(prop);
177 				break;
178 			}
179 			if (exclaves_driver_service_lookup(exclave_edk_service, &edk_mach_endpoint) != KERN_SUCCESS) {
180 				IOLog("%s: %s-0x%qx could not find exclave-edk-service %s\n", __func__, provider->getName(), provider->getRegistryEntryID(), exclave_edk_service);
181 				OSSafeReleaseNULL(prop);
182 				break;
183 			}
184 		}
185 		OSSafeReleaseNULL(prop);
186 
187 		// Initialize IOServiceConcrete endpoint
188 		tb_endpoint = tb_endpoint_create_with_value(TB_TRANSPORT_TYPE_XNU, mach_endpoint, TB_ENDPOINT_OPTIONS_NONE);
189 		assert(NULL != tb_endpoint);
190 		if (NULL == tb_endpoint) {
191 			break;
192 		}
193 		tberr = ioservice_ioserviceconcrete_init(&client, tb_endpoint);
194 		assert(TB_ERROR_SUCCESS == tberr);
195 		if (TB_ERROR_SUCCESS != tberr) {
196 			break;
197 		}
198 
199 		// Initialize IOServicePrivate endpoint
200 		edk_tb_endpoint = tb_endpoint_create_with_value(TB_TRANSPORT_TYPE_XNU, edk_mach_endpoint, TB_ENDPOINT_OPTIONS_NONE);
201 		assert(NULL != edk_tb_endpoint);
202 		if (NULL == edk_tb_endpoint) {
203 			printf("%s: ERROR: Failed to create endpoint\n", __func__);
204 			break;
205 		}
206 		tberr = ioservice_ioserviceprivate_init(&edk_client, edk_tb_endpoint);
207 		assert(TB_ERROR_SUCCESS == tberr);
208 		if (TB_ERROR_SUCCESS != tberr) {
209 			printf("%s: ERROR: Failed to init IOServicePrivate\n", __func__);
210 			break;
211 		}
212 
213 		ref = IONewZero(IOExclaveProxyState, 1);
214 		if (!ref) {
215 			break;
216 		}
217 		ref->service = this;
218 		ref->mach_endpoint = mach_endpoint;
219 		ref->tb_endpoint   = tb_endpoint;
220 		ref->client = client;
221 		ref->edk_mach_endpoint = edk_mach_endpoint;
222 		ref->edk_tb_endpoint   = edk_tb_endpoint;
223 		ref->edk_client = edk_client;
224 		ref->exclave_interrupts = OSDictionary::withCapacity(1);
225 		ref->exclave_interrupts_lock = IOLockAlloc();
226 		ref->exclave_timers = OSDictionary::withCapacity(1);
227 		ref->exclave_timers_lock = IOLockAlloc();
228 		ref->exclaveAsyncNotificationEventSourcesLock = IOLockAlloc();
229 
230 		// TODO: remove once workloop aperture workaround removed
231 		wl = getWorkLoop();
232 		if (!wl) {
233 			printf("%s ERROR: getWorkLoop failed\n", __func__);
234 			break;
235 		}
236 		ref->ewla = IONew(IOExclaveWorkLoopAperture, 1);
237 		if (!ref->ewla) {
238 			printf("%s ERROR: exclaveWorkLoopAperture init failed\n", __func__);
239 			break;
240 		}
241 		ref->ewla->workLoop = wl;
242 
243 		// Add proxy state to global lookup table
244 		serviceID = getRegistryEntryID();
245 		snprintf(key, sizeof(key), "%llu", serviceID);
246 		wrapper = OSTypeAlloc(IOExclaveProxyStateWrapper);
247 		wrapper->proxyState = ref;
248 		IORecursiveLockLock(gExclaveProxyStateLock);
249 		gExclaveProxyStates->setObject(key, wrapper);
250 		IORecursiveLockUnlock(gExclaveProxyStateLock);
251 
252 		// Start() called after lookup table registration in case upcalls are made during exclave start().
253 		// Use registry ID as exclave's upcall identifer
254 		tberr = ioservice_ioserviceprivate_startprivate(&edk_client, serviceID, &result);
255 		if (TB_ERROR_SUCCESS != tberr || !result) {
256 			printf("%s ERROR: Failed StartPrivate\n", __func__);
257 			// Deregister from lookup table if start() fails
258 			IORecursiveLockLock(gExclaveProxyStateLock);
259 			gExclaveProxyStates->removeObject(key);
260 			IORecursiveLockUnlock(gExclaveProxyStateLock);
261 			wrapper->release();
262 			break;
263 		}
264 
265 		err = 0;
266 	} while (false);
267 
268 	if (err) {
269 		if (ref) {
270 			OSSafeReleaseNULL(ref->exclave_interrupts);
271 			if (ref->exclave_interrupts_lock) {
272 				IOLockFree(ref->exclave_interrupts_lock);
273 				ref->exclave_interrupts_lock = NULL;
274 			}
275 			OSSafeReleaseNULL(ref->exclave_timers);
276 			if (ref->exclave_timers_lock) {
277 				IOLockFree(ref->exclave_timers_lock);
278 				ref->exclave_timers_lock = NULL;
279 			}
280 			if (ref->exclaveAsyncNotificationEventSourcesLock) {
281 				IOLockFree(ref->exclaveAsyncNotificationEventSourcesLock);
282 				ref->exclaveAsyncNotificationEventSourcesLock = NULL;
283 			}
284 			if (ref->ewla) {
285 				IODelete(ref->ewla, IOExclaveWorkLoopAperture, 1);
286 				ref->ewla = NULL;
287 			}
288 			IODelete(ref, IOExclaveProxyState, 1);
289 			ref = NULL;
290 		}
291 	}
292 #endif /* CONFIG_EXCLAVES */
293 
294 	if (!ref) {
295 		return false;
296 	}
297 
298 	*pRef = ref;
299 	return true;
300 }
301 
302 uint64_t
exclaveEndpoint(IOExclaveProxyState * pRef)303 IOService::exclaveEndpoint(IOExclaveProxyState * pRef)
304 {
305 	return pRef->mach_endpoint;
306 }
307 
308 bool
start(IOService * provider)309 IOExclaveProxy::start(IOService * provider)
310 {
311 	bool ok;
312 
313 	ok = exclaveStart(provider, &exclaveState);
314 
315 	return ok;
316 }
317 
318 /* Exclave upcall handlers */
319 
320 #if CONFIG_EXCLAVES
321 
322 static IOService::IOExclaveProxyState *
getProxyStateFromRegistryID(uint64_t id)323 getProxyStateFromRegistryID(uint64_t id)
324 {
325 	OSObject *obj = NULL;
326 	IOExclaveProxyStateWrapper *wrapper = NULL;
327 	char key[15];
328 
329 	snprintf(key, sizeof(key), "%llu", id);
330 	IORecursiveLockLock(gExclaveProxyStateLock);
331 	obj = gExclaveProxyStates->getObject(key);
332 	IORecursiveLockUnlock(gExclaveProxyStateLock);
333 	if (!obj) {
334 		printf("%s ERROR: failed to find proxy state\n", __func__);
335 		return NULL;
336 	}
337 
338 	wrapper = OSDynamicCast(IOExclaveProxyStateWrapper, obj);
339 	if (!wrapper) {
340 		printf("%s ERROR: failed to cast IOExclaveProxyStateWrapper\n", __func__);
341 		return NULL;
342 	}
343 
344 	if (!wrapper->proxyState) {
345 		printf("%s ERROR: IOExclaveProxyStateWrapper contains NULL proxy state\n", __func__);
346 		return NULL;
347 	}
348 
349 	return wrapper->proxyState;
350 }
351 
352 bool
IOExclaveInterruptUpcallHandler(uint64_t id,IOExclaveInterruptUpcallArgs * args)353 IOExclaveInterruptUpcallHandler(uint64_t id, IOExclaveInterruptUpcallArgs *args)
354 {
355 	assert(args);
356 	IOService::IOExclaveProxyState *ref = getProxyStateFromRegistryID(id);
357 	if (!ref || !args) {
358 		return false;
359 	}
360 	ref->service->retain();
361 
362 	bool res;
363 	switch (args->type) {
364 	case kIOExclaveInterruptUpcallTypeRegister:
365 		// Register interrupt
366 		res = ref->service->exclaveRegisterInterrupt(ref, args->index, args->data.register_args.test_irq);
367 		break;
368 	case kIOExclaveInterruptUpcallTypeRemove:
369 		// Remove interrupt
370 		res = ref->service->exclaveRemoveInterrupt(ref, args->index);
371 		break;
372 	case kIOExclaveInterruptUpcallTypeEnable:
373 		// Enable/disable interrupt
374 		res = ref->service->exclaveEnableInterrupt(ref, args->index, args->data.enable_args.enable);
375 		break;
376 	default:
377 		res = false;
378 		printf("%s ERROR: invalid upcall type\n", __func__);
379 	}
380 
381 	if (!res) {
382 		printf("%s ERROR: upcall handler type %d failed\n", __func__, args->type);
383 		ref->service->release();
384 		return false;
385 	}
386 
387 	ref->service->release();
388 	return true;
389 }
390 
391 bool
IOExclaveTimerUpcallHandler(uint64_t id,IOExclaveTimerUpcallArgs * args)392 IOExclaveTimerUpcallHandler(uint64_t id, IOExclaveTimerUpcallArgs *args)
393 {
394 	assert(args);
395 	IOService::IOExclaveProxyState *ref = getProxyStateFromRegistryID(id);
396 	if (!ref || !args) {
397 		return false;
398 	}
399 	ref->service->retain();
400 
401 	bool res;
402 	uint32_t timer_id = args->timer_id;
403 	switch (args->type) {
404 	case kIOExclaveTimerUpcallTypeRegister:
405 		// Register timer
406 		res = ref->service->exclaveRegisterTimer(ref, &args->timer_id);
407 		break;
408 	case kIOExclaveTimerUpcallTypeRemove:
409 		// Remove timer
410 		res = ref->service->exclaveRemoveTimer(ref, timer_id);
411 		break;
412 	case kIOExclaveTimerUpcallTypeEnable:
413 	{
414 		// Enable/disable timer
415 		bool enable = args->data.enable_args.enable;
416 		res = ref->service->exclaveEnableTimer(ref, timer_id, enable);
417 		break;
418 	}
419 	case kIOExclaveTimerUpcallTypeSetTimeout:
420 	{
421 		// Set timeout
422 		uint32_t options = args->data.set_timeout_args.clock_continuous ? kIOTimeOptionsContinuous : 0;
423 		AbsoluteTime duration = args->data.set_timeout_args.duration;
424 		kern_return_t *kr = &args->data.set_timeout_args.kr;
425 		res = ref->service->exclaveTimerSetTimeout(ref, timer_id, options, duration, 0, kr);
426 		break;
427 	}
428 	case kIOExclaveTimerUpcallTypeCancelTimeout:
429 		// Cancel timeout
430 		res = ref->service->exclaveTimerCancelTimeout(ref, timer_id);
431 		break;
432 	default:
433 		res = false;
434 		printf("%s ERROR: invalid upcall type\n", __func__);
435 	}
436 
437 	if (!res) {
438 		printf("%s ERROR: upcall handler type %d failed\n", __func__, args->type);
439 		ref->service->release();
440 		return false;
441 	}
442 
443 	ref->service->release();
444 	return true;
445 }
446 
447 bool
IOExclaveLockWorkloop(uint64_t id,bool lock)448 IOExclaveLockWorkloop(uint64_t id, bool lock)
449 {
450 	IOService::IOExclaveProxyState *ref = getProxyStateFromRegistryID(id);
451 	if (!ref) {
452 		return false;
453 	}
454 
455 	// Lock or unlock workloop
456 	if (lock) {
457 		ref->ewla->closeGate();
458 		EXLOG("%s locked workloop\n", __func__);
459 	} else {
460 		ref->ewla->openGate();
461 		EXLOG("%s unlocked workloop\n", __func__);
462 	}
463 	return true;
464 }
465 
466 static void
getExclaveInterruptKey(int index,char * key,size_t size)467 getExclaveInterruptKey(int index, char *key, size_t size)
468 {
469 	snprintf(key, size, "%d", index);
470 }
471 
472 static IOInterruptEventSource *
copyExclaveInterruptEventSource(IOService::IOExclaveProxyState * pRef,int index)473 copyExclaveInterruptEventSource(IOService::IOExclaveProxyState * pRef, int index)
474 {
475 	OSObject *obj;
476 	IOInterruptEventSource *ies;
477 	char irqKey[5];
478 
479 	if (!pRef) {
480 		return NULL;
481 	}
482 
483 	getExclaveInterruptKey(index, irqKey, sizeof(irqKey));
484 	IOLockAssert(pRef->exclave_interrupts_lock, kIOLockAssertOwned);
485 	obj = pRef->exclave_interrupts->getObject(irqKey);
486 	if (!obj) {
487 		return NULL;
488 	}
489 
490 	ies = OSDynamicCast(IOInterruptEventSource, obj);
491 	if (ies) {
492 		ies->retain();
493 	}
494 	return ies;
495 }
496 
497 // TODO: Remove after testing
498 void
IOExclaveTestSignalInterrupt(thread_call_param_t arg0,__unused thread_call_param_t arg1)499 IOExclaveTestSignalInterrupt(thread_call_param_t arg0, __unused thread_call_param_t arg1)
500 {
501 	EXLOG("%s called\n", __func__);
502 
503 	// Unpackage params
504 	struct IOExclaveTestSignalInterruptParam *params = (struct IOExclaveTestSignalInterruptParam *) arg0;
505 	if (params->id == -1 || params->index == -1) {
506 		printf("%s: ERROR: id and irq index not initialized\n", __func__);
507 		return;
508 	}
509 
510 	uint64_t id = params->id;
511 	int index = (int) params->index;
512 
513 	IOService::IOExclaveProxyState *ref = getProxyStateFromRegistryID(id);
514 	if (!ref) {
515 		return;
516 	}
517 	ref->service->retain();
518 
519 	// Get interrupt
520 	char irqKey[5];
521 	getExclaveInterruptKey(index, irqKey, sizeof(irqKey));
522 	OSObject *obj2 = ref->exclave_interrupts->getObject(irqKey);
523 	if (!obj2) {
524 		printf("%s: ERROR: failed to get ies\n", __func__);
525 		ref->service->release();
526 		return;
527 	}
528 
529 	IOInterruptEventSource *ies = OSDynamicCast(IOInterruptEventSource, obj2);
530 	if (!ies) {
531 		printf("%s: ERROR: failed to cast ies\n", __func__);
532 		ref->service->release();
533 		return;
534 	}
535 
536 	// Signal interrupt
537 	ies->interruptOccurred(NULL, NULL, 1);
538 
539 	ref->service->release();
540 }
541 
542 bool
IOExclaveAsyncNotificationUpcallHandler(uint64_t id,struct IOExclaveAsyncNotificationUpcallArgs * args)543 IOExclaveAsyncNotificationUpcallHandler(uint64_t id, struct IOExclaveAsyncNotificationUpcallArgs *args)
544 {
545 	IOService::IOExclaveProxyState *ref = getProxyStateFromRegistryID(id);
546 	bool ret = false;
547 	if (!ref) {
548 		return false;
549 	}
550 
551 	switch (args->type) {
552 	case AsyncNotificationUpcallTypeSignal:
553 		ret = ref->service->exclaveAsyncNotificationSignal(ref, args->notificationID) == kIOReturnSuccess;
554 		break;
555 	default:
556 		ret = false;
557 		break;
558 	}
559 	return ret;
560 }
561 
562 bool
IOExclaveMapperOperationUpcallHandler(uint64_t id,IOExclaveMapperOperationUpcallArgs * args)563 IOExclaveMapperOperationUpcallHandler(uint64_t id, IOExclaveMapperOperationUpcallArgs *args)
564 {
565 	assert(args);
566 	IOService *provider = NULL;
567 	bool res = false;
568 	IOService::IOExclaveProxyState *ref = getProxyStateFromRegistryID(id);
569 	if (!ref) {
570 		return false;
571 	}
572 	provider = ref->service->getProvider();
573 
574 	IOMapper *mapper = IOMapper::copyMapperForDeviceWithIndex(provider, (unsigned int)(args->mapperIndex));
575 	if (!mapper) {
576 		goto finish;
577 	}
578 
579 	switch (args->type) {
580 	case MapperActivate:
581 		res = kIOReturnSuccess == mapper->callPlatformFunction(gDARTMapperFunctionSetActive, false, (void *)(true), (void *)(false), NULL, NULL);
582 		break;
583 	case MapperDeactivate:
584 		res = kIOReturnSuccess == mapper->callPlatformFunction(gDARTMapperFunctionSetActive, false, (void *)(false), (void *)(false), NULL, NULL);
585 		break;
586 	default:
587 		break;
588 	}
589 
590 finish:
591 	return res;
592 }
593 
594 bool
IOExclaveANEUpcallHandler(uint64_t id,struct IOExclaveANEUpcallArgs * args,bool * result)595 IOExclaveANEUpcallHandler(uint64_t id, struct IOExclaveANEUpcallArgs *args, bool *result)
596 {
597 	IOService::IOExclaveProxyState *ref = getProxyStateFromRegistryID(id);
598 	bool ret = false;
599 	bool _result = false;
600 	if (!ref || !args) {
601 		return false;
602 	}
603 
604 	switch (args->type) {
605 	case kIOExclaveANEUpcallTypeSetPowerState:
606 		if (ref->aneSetPowerStateUpcallHandler) {
607 			_result = (ref->aneSetPowerStateUpcallHandler)(
608 				args->setpowerstate_args.desired_state
609 				);
610 			ret = true;
611 		} else {
612 			printf("%s: no handler for upcall %d registered\n", __func__, (int)args->type);
613 		}
614 		break;
615 	case kIOExclaveANEUpcallTypeWorkSubmit:
616 		if (ref->aneWorkSubmitUpcallHandler) {
617 			_result = (ref->aneWorkSubmitUpcallHandler)(
618 				args->work_args.arg0,
619 				args->work_args.arg1,
620 				args->work_args.arg2
621 				);
622 			ret = true;
623 		} else {
624 			printf("%s: no handler for upcall %d registered\n", __func__, (int)args->type);
625 		}
626 		break;
627 	case kIOExclaveANEUpcallTypeWorkBegin:
628 		if (ref->aneWorkBeginUpcallHandler) {
629 			_result = (ref->aneWorkBeginUpcallHandler)(
630 				args->work_args.arg0,
631 				args->work_args.arg1,
632 				args->work_args.arg2
633 				);
634 			ret = true;
635 		} else {
636 			printf("%s: no handler for upcall %d registered\n", __func__, (int)args->type);
637 		}
638 		break;
639 	case kIOExclaveANEUpcallTypeWorkEnd:
640 		if (ref->aneWorkEndUpcallHandler) {
641 			_result = (ref->aneWorkEndUpcallHandler)(
642 				args->work_args.arg0,
643 				args->work_args.arg1,
644 				args->work_args.arg2
645 				);
646 			ret = true;
647 		} else {
648 			printf("%s: no handler for upcall %d registered\n", __func__, (int)args->type);
649 		}
650 		break;
651 	default:
652 		ret = false;
653 		break;
654 	}
655 
656 	if (result) {
657 		*result = _result;
658 	}
659 
660 	return ret;
661 }
662 
663 IOReturn
IOExclaveLPWUpcallHandler(struct IOExclaveLPWUpcallArgs * args)664 IOExclaveLPWUpcallHandler(struct IOExclaveLPWUpcallArgs *args)
665 {
666 	IOReturn ret;
667 
668 	if (!args) {
669 		return kIOReturnBadArgument;
670 	}
671 
672 	IOPMrootDomain *rootDomain = IOService::getPMRootDomain();
673 	if (!rootDomain) {
674 		return kIOReturnInternalError;
675 	}
676 
677 	switch (args->type) {
678 	case kIOExclaveLPWUpcallTypeCreateAssertion:
679 		return IOExclaveLPWCreateAssertion(&args->data.createassertion.id_out, "Exclave LPW assertion");
680 	case kIOExclaveLPWUpcallTypeReleaseAssertion:
681 		return IOExclaveLPWReleaseAssertion(args->data.releaseassertion.id);
682 	case kIOExclaveLPWUpcallTypeRequestRunMode:
683 		ret = rootDomain->requestRunMode(args->data.requestrunmode.runmode_mask);
684 		break;
685 	default:
686 		return kIOReturnBadArgument;
687 	}
688 
689 	return ret;
690 }
691 
692 IOReturn
IOExclaveLPWCreateAssertion(uint64_t * id_out,const char * desc)693 IOExclaveLPWCreateAssertion(uint64_t *id_out, const char *desc)
694 {
695 	IOPMDriverAssertionID assertionID;
696 
697 	IOPMrootDomain *rootDomain = IOService::getPMRootDomain();
698 	if (!rootDomain) {
699 		return kIOReturnInternalError;
700 	}
701 
702 	assertionID = rootDomain->createPMAssertion(
703 		kIOPMDriverAssertionCPUBit | kIOPMDriverAssertionForceWakeupBit,
704 		kIOPMDriverAssertionLevelOn,
705 		rootDomain,
706 		desc);
707 	if (assertionID == 0) {
708 		return kIOReturnInternalError;
709 	}
710 
711 	*id_out = (uint64_t) assertionID;
712 	return kIOReturnSuccess;
713 }
714 
715 IOReturn
IOExclaveLPWReleaseAssertion(uint64_t id)716 IOExclaveLPWReleaseAssertion(uint64_t id)
717 {
718 	IOPMrootDomain *rootDomain = IOService::getPMRootDomain();
719 	if (!rootDomain) {
720 		return kIOReturnInternalError;
721 	}
722 
723 	return rootDomain->releasePMAssertion(id);
724 }
725 
726 /* IOService exclave methods */
727 
728 #endif /* CONFIG_EXCLAVES */
729 
730 bool
exclaveRegisterInterrupt(IOExclaveProxyState * pRef,int index,bool noProvider=false)731 IOService::exclaveRegisterInterrupt(IOExclaveProxyState * pRef, int index, bool noProvider = false)
732 {
733 #if CONFIG_EXCLAVES
734 	IOInterruptEventSource *ies = NULL;
735 	IOInterruptEventSource::Action action;
736 	IOWorkLoop *wl;
737 	char irqKey[5];
738 
739 	assert(getWorkLoop());
740 
741 	if (!pRef) {
742 		return false;
743 	}
744 
745 	action = OSMemberFunctionCast(IOInterruptEventSource::Action,
746 	    this, &IOService::exclaveInterruptOccurred);
747 	ies = IOInterruptEventSource::interruptEventSource(this, action, noProvider ? nullptr : getProvider(), index);
748 	if (!ies) {
749 		return false;
750 	}
751 
752 	wl = getWorkLoop();
753 	if (!wl) {
754 		ies->release();
755 		return false;
756 	}
757 	if (wl->addEventSource(ies) != kIOReturnSuccess) {
758 		ies->release();
759 		return false;
760 	}
761 
762 	// Register IOIES in exclave proxy state
763 	getExclaveInterruptKey(index, irqKey, sizeof(irqKey));
764 	IOLockLock(pRef->exclave_interrupts_lock);
765 	pRef->exclave_interrupts->setObject(irqKey, ies);
766 	IOLockUnlock(pRef->exclave_interrupts_lock);
767 	OSSafeReleaseNULL(ies);
768 
769 	EXLOG("%s: IRQ %d register success!\n", __func__, index);
770 	return true;
771 #else /* CONFIG_EXCLAVES */
772 	return false;
773 #endif /* CONFIG_EXCLAVES */
774 }
775 
776 bool
exclaveRemoveInterrupt(IOExclaveProxyState * pRef,int index)777 IOService::exclaveRemoveInterrupt(IOExclaveProxyState * pRef, int index)
778 {
779 #if CONFIG_EXCLAVES
780 	IOInterruptEventSource *ies;
781 	IOWorkLoop *wl;
782 	char irqKey[5];
783 
784 	assert(getWorkLoop());
785 
786 	if (!pRef) {
787 		return false;
788 	}
789 	getExclaveInterruptKey(index, irqKey, sizeof(irqKey));
790 
791 	IOLockLock(pRef->exclave_interrupts_lock);
792 
793 	ies = copyExclaveInterruptEventSource(pRef, index);
794 
795 	if (!ies) {
796 		IOLockUnlock(pRef->exclave_interrupts_lock);
797 		OSSafeReleaseNULL(ies);
798 		return false;
799 	}
800 	pRef->exclave_interrupts->removeObject(irqKey);
801 	IOLockUnlock(pRef->exclave_interrupts_lock);
802 
803 	wl = getWorkLoop();
804 	if (!wl) {
805 		OSSafeReleaseNULL(ies);
806 		return false;
807 	}
808 
809 	wl->removeEventSource(ies);
810 	OSSafeReleaseNULL(ies);
811 
812 	EXLOG("%s: IRQ %d removed successfully\n", __func__, index);
813 	return true;
814 #else /* CONFIG_EXCLAVES */
815 	return false;
816 #endif /* CONFIG_EXCLAVES */
817 }
818 
819 bool
exclaveEnableInterrupt(IOExclaveProxyState * pRef,int index,bool enable)820 IOService::exclaveEnableInterrupt(IOExclaveProxyState * pRef, int index, bool enable)
821 {
822 #if CONFIG_EXCLAVES
823 	IOInterruptEventSource *ies;
824 
825 	assert(getWorkLoop());
826 
827 	if (!pRef) {
828 		return false;
829 	}
830 
831 	IOLockLock(pRef->exclave_interrupts_lock);
832 	ies = copyExclaveInterruptEventSource(pRef, index);
833 	if (!ies) {
834 		IOLockUnlock(pRef->exclave_interrupts_lock);
835 		OSSafeReleaseNULL(ies);
836 		return false;
837 	}
838 
839 	if (enable) {
840 		ies->enable();
841 	} else {
842 		ies->disable();
843 	}
844 	IOLockUnlock(pRef->exclave_interrupts_lock);
845 	OSSafeReleaseNULL(ies);
846 
847 	EXLOG("%s: IRQ %s success!\n", __func__, enable ? "enable" : "disable");
848 	return true;
849 #else /* CONFIG_EXCLAVES */
850 	return false;
851 #endif /* CONFIG_EXCLAVES */
852 }
853 
854 
855 void
exclaveInterruptOccurred(IOInterruptEventSource * eventSource,int count)856 IOService::exclaveInterruptOccurred(IOInterruptEventSource *eventSource, int count)
857 {
858 #if CONFIG_EXCLAVES
859 	tb_error_t tberr;
860 	IOService::IOExclaveProxyState *ref;
861 
862 	if (!eventSource) {
863 		printf("%s ERROR: IOInterruptEventSource is null\n", __func__);
864 		return;
865 	}
866 
867 	EXLOG("%s id 0x%llx (irq index %d)\n", __func__, getRegistryEntryID(), eventSource->getIntIndex());
868 
869 	ref = getProxyStateFromRegistryID(getRegistryEntryID());
870 	if (!ref) {
871 		printf("%s ERROR: failed to get IOExclaveProxyState\n", __func__);
872 		return;
873 	}
874 
875 	tberr = ioservice_ioserviceprivate_interruptoccurredprivate(&ref->edk_client, eventSource->getIntIndex(), count);
876 	assert(TB_ERROR_SUCCESS == tberr);
877 	if (TB_ERROR_SUCCESS != tberr) {
878 		printf("%s ERROR: tightbeam call failed\n", __func__);
879 		return;
880 	}
881 #endif /* CONFIG_EXCLAVES */
882 }
883 
884 #if CONFIG_EXCLAVES
885 static void
getExclaveTimerKey(uint32_t timer_id,char * key,size_t size)886 getExclaveTimerKey(uint32_t timer_id, char *key, size_t size)
887 {
888 	snprintf(key, size, "%d", timer_id);
889 }
890 
891 static IOTimerEventSource *
copyExclaveTimerEventSource(IOService::IOExclaveProxyState * pRef,uint32_t timer_id)892 copyExclaveTimerEventSource(IOService::IOExclaveProxyState * pRef, uint32_t timer_id)
893 {
894 	OSObject *obj;
895 	IOTimerEventSource *tes;
896 	char timerKey[5];
897 
898 	if (!pRef) {
899 		return NULL;
900 	}
901 
902 	getExclaveTimerKey(timer_id, timerKey, sizeof(timerKey));
903 	IOLockAssert(pRef->exclave_timers_lock, kIOLockAssertOwned);
904 	obj = pRef->exclave_timers->getObject(timerKey);
905 	if (!obj) {
906 		return NULL;
907 	}
908 
909 	tes = OSDynamicCast(IOTimerEventSource, obj);
910 	if (tes) {
911 		tes->retain();
912 	}
913 	return tes;
914 }
915 #endif /* CONFIG_EXCLAVES */
916 
917 bool
exclaveRegisterTimer(IOExclaveProxyState * pRef,uint32_t * timer_id)918 IOService::exclaveRegisterTimer(IOExclaveProxyState * pRef, uint32_t *timer_id)
919 {
920 #if CONFIG_EXCLAVES
921 	IOTimerEventSource *tes = NULL;
922 	IOTimerEventSource::Action action;
923 	IOWorkLoop *wl;
924 	char timerKey[5];
925 
926 	assert(getWorkLoop());
927 
928 	if (!pRef || !timer_id) {
929 		return false;
930 	}
931 
932 	action = OSMemberFunctionCast(IOTimerEventSource::Action,
933 	    this, &IOService::exclaveTimerFired);
934 	tes = IOTimerEventSource::timerEventSource(this, action);
935 	if (!tes) {
936 		return false;
937 	}
938 
939 	wl = getWorkLoop();
940 	if (!wl) {
941 		tes->release();
942 		return false;
943 	}
944 	if (wl->addEventSource(tes) != kIOReturnSuccess) {
945 		tes->release();
946 		return false;
947 	}
948 
949 	// Register IOTES in exclave proxy state
950 	IOLockLock(pRef->exclave_timers_lock);
951 	*timer_id = pRef->nextExclaveTimerId++;
952 	getExclaveTimerKey(*timer_id, timerKey, sizeof(timerKey));
953 	pRef->exclave_timers->setObject(timerKey, tes);
954 	IOLockUnlock(pRef->exclave_timers_lock);
955 	OSSafeReleaseNULL(tes);
956 
957 	EXLOG("%s: timer %u register success!\n", __func__, *timer_id);
958 	return true;
959 #else /* CONFIG_EXCLAVES */
960 	return false;
961 #endif /* CONFIG_EXCLAVES */
962 }
963 
964 bool
exclaveRemoveTimer(IOExclaveProxyState * pRef,uint32_t timer_id)965 IOService::exclaveRemoveTimer(IOExclaveProxyState * pRef, uint32_t timer_id)
966 {
967 #if CONFIG_EXCLAVES
968 	IOTimerEventSource *tes;
969 	IOWorkLoop *wl;
970 	char timerKey[5];
971 
972 	assert(getWorkLoop());
973 
974 	if (!pRef) {
975 		return false;
976 	}
977 
978 	getExclaveTimerKey(timer_id, timerKey, sizeof(timerKey));
979 
980 	IOLockLock(pRef->exclave_timers_lock);
981 	tes = copyExclaveTimerEventSource(pRef, timer_id);
982 	if (!tes) {
983 		IOLockUnlock(pRef->exclave_timers_lock);
984 		OSSafeReleaseNULL(tes);
985 		return false;
986 	}
987 	pRef->exclave_timers->removeObject(timerKey);
988 	IOLockUnlock(pRef->exclave_timers_lock);
989 
990 	wl = getWorkLoop();
991 	if (!wl) {
992 		OSSafeReleaseNULL(tes);
993 		return false;
994 	}
995 
996 	wl->removeEventSource(tes);
997 	OSSafeReleaseNULL(tes);
998 
999 	EXLOG("%s: timer %u removed successfully\n", __func__, timer_id);
1000 	return true;
1001 #else /* CONFIG_EXCLAVES */
1002 	return false;
1003 #endif /* CONFIG_EXCLAVES */
1004 }
1005 
1006 bool
exclaveEnableTimer(IOExclaveProxyState * pRef,uint32_t timer_id,bool enable)1007 IOService::exclaveEnableTimer(IOExclaveProxyState * pRef, uint32_t timer_id, bool enable)
1008 {
1009 #if CONFIG_EXCLAVES
1010 	IOTimerEventSource *tes;
1011 
1012 	assert(getWorkLoop());
1013 
1014 	if (!pRef) {
1015 		return false;
1016 	}
1017 
1018 	IOLockLock(pRef->exclave_timers_lock);
1019 	tes = copyExclaveTimerEventSource(pRef, timer_id);
1020 	if (!tes) {
1021 		IOLockUnlock(pRef->exclave_timers_lock);
1022 		OSSafeReleaseNULL(tes);
1023 		return false;
1024 	}
1025 
1026 	if (enable) {
1027 		tes->enable();
1028 	} else {
1029 		tes->disable();
1030 	}
1031 	IOLockUnlock(pRef->exclave_timers_lock);
1032 	OSSafeReleaseNULL(tes);
1033 
1034 	EXLOG("%s: timer %u %s success\n", __func__, timer_id, enable ? "enable" : "disable");
1035 	return true;
1036 #else /* CONFIG_EXCLAVES */
1037 	return false;
1038 #endif /* CONFIG_EXCLAVES */
1039 }
1040 
1041 bool
exclaveTimerSetTimeout(IOExclaveProxyState * pRef,uint32_t timer_id,uint32_t options,AbsoluteTime interval,AbsoluteTime leeway,kern_return_t * kr)1042 IOService::exclaveTimerSetTimeout(IOExclaveProxyState * pRef, uint32_t timer_id, uint32_t options, AbsoluteTime interval, AbsoluteTime leeway, kern_return_t *kr)
1043 {
1044 #if CONFIG_EXCLAVES
1045 	IOTimerEventSource *tes;
1046 
1047 	assert(getWorkLoop());
1048 
1049 	if (!pRef || !kr) {
1050 		return false;
1051 	}
1052 
1053 	IOLockLock(pRef->exclave_timers_lock);
1054 	tes = copyExclaveTimerEventSource(pRef, timer_id);
1055 	if (!tes) {
1056 		IOLockUnlock(pRef->exclave_timers_lock);
1057 		OSSafeReleaseNULL(tes);
1058 		return false;
1059 	}
1060 
1061 	*kr = tes->setTimeout(options, interval, leeway);
1062 	IOLockUnlock(pRef->exclave_timers_lock);
1063 	OSSafeReleaseNULL(tes);
1064 
1065 	EXLOG("%s: timer %u setTimeout completed (kr %d)\n", __func__, timer_id, *kr);
1066 	return true;
1067 #else /* CONFIG_EXCLAVES */
1068 	return false;
1069 #endif /* CONFIG_EXCLAVES */
1070 }
1071 
1072 bool
exclaveTimerCancelTimeout(IOExclaveProxyState * pRef,uint32_t timer_id)1073 IOService::exclaveTimerCancelTimeout(IOExclaveProxyState * pRef, uint32_t timer_id)
1074 {
1075 #if CONFIG_EXCLAVES
1076 	IOTimerEventSource *tes;
1077 
1078 	assert(getWorkLoop());
1079 
1080 	if (!pRef) {
1081 		return false;
1082 	}
1083 
1084 	IOLockLock(pRef->exclave_timers_lock);
1085 	tes = copyExclaveTimerEventSource(pRef, timer_id);
1086 	if (!tes) {
1087 		IOLockUnlock(pRef->exclave_timers_lock);
1088 		OSSafeReleaseNULL(tes);
1089 		return false;
1090 	}
1091 
1092 	tes->cancelTimeout();
1093 	IOLockUnlock(pRef->exclave_timers_lock);
1094 	OSSafeReleaseNULL(tes);
1095 	EXLOG("%s: timer %u setTimeout success\n", __func__, timer_id);
1096 	return true;
1097 #else /* CONFIG_EXCLAVES */
1098 	return false;
1099 #endif /* CONFIG_EXCLAVES */
1100 }
1101 
1102 void
exclaveTimerFired(IOTimerEventSource * eventSource)1103 IOService::exclaveTimerFired(IOTimerEventSource *eventSource)
1104 {
1105 #if CONFIG_EXCLAVES
1106 	tb_error_t tberr;
1107 	IOService::IOExclaveProxyState *ref;
1108 	__block bool found = false;
1109 	__block uint32_t timer_id;
1110 
1111 	if (!eventSource) {
1112 		printf("%s ERROR: IOTimerEventSource is null\n", __func__);
1113 		return;
1114 	}
1115 
1116 	ref = getProxyStateFromRegistryID(getRegistryEntryID());
1117 	if (!ref) {
1118 		printf("%s ERROR: failed to get IOExclaveProxyState\n", __func__);
1119 		return;
1120 	}
1121 
1122 	// Find timer ID
1123 	IOLockLock(ref->exclave_timers_lock);
1124 	ref->exclave_timers->iterateObjects(^bool (const OSSymbol * id, OSObject * obj)
1125 	{
1126 		if (obj == eventSource) {
1127 		        found = true;
1128 		        const char *key = id->getCStringNoCopy();
1129 		        timer_id = (uint32_t) strtol(key, NULL, 0);
1130 		        return true;
1131 		}
1132 		return false;
1133 	});
1134 	IOLockUnlock(ref->exclave_timers_lock);
1135 
1136 	if (!found) {
1137 		printf("%s ERROR: Could not find timer ID\n", __func__);
1138 		return;
1139 	}
1140 
1141 	EXLOG("%s id 0x%llx (timer_id %u)\n", __func__, getRegistryEntryID(), timer_id);
1142 	tberr = ioservice_ioserviceprivate_timerfiredprivate(&ref->edk_client, timer_id);
1143 	assert(TB_ERROR_SUCCESS == tberr);
1144 	if (TB_ERROR_SUCCESS != tberr) {
1145 		printf("%s ERROR: tightbeam call failed\n", __func__);
1146 		return;
1147 	}
1148 #endif /* CONFIG_EXCLAVES */
1149 }
1150 
1151 
1152 kern_return_t
exclaveAsyncNotificationRegister(IOExclaveProxyState * pRef,IOInterruptEventSource * notification,uint32_t * notificationID)1153 IOService::exclaveAsyncNotificationRegister(IOExclaveProxyState * pRef, IOInterruptEventSource *notification, uint32_t *notificationID)
1154 {
1155 #if CONFIG_EXCLAVES
1156 	kern_return_t ret;
1157 	if (notification == NULL) {
1158 		return kIOReturnBadArgument;
1159 	}
1160 
1161 	IOLockLock(pRef->exclaveAsyncNotificationEventSourcesLock);
1162 	if (!pRef->exclaveAsyncNotificationEventSources) {
1163 		pRef->exclaveAsyncNotificationEventSources = OSArray::withCapacity(1);
1164 	}
1165 	if (pRef->exclaveAsyncNotificationEventSources) {
1166 		*notificationID = (uint32_t) pRef->exclaveAsyncNotificationEventSources->getCount();
1167 		pRef->exclaveAsyncNotificationEventSources->setObject(notification);
1168 		ret = kIOReturnSuccess;
1169 	} else {
1170 		ret = kIOReturnNoMemory;
1171 	}
1172 	IOLockUnlock(pRef->exclaveAsyncNotificationEventSourcesLock);
1173 	return ret;
1174 #else
1175 #pragma unused(pRef, notification, notificationID)
1176 	return kIOReturnUnsupported;
1177 #endif /* CONFIG_EXCLAVES*/
1178 }
1179 
1180 kern_return_t
exclaveAsyncNotificationSignal(IOExclaveProxyState * pRef,uint32_t notificationID)1181 IOService::exclaveAsyncNotificationSignal(IOExclaveProxyState * pRef, uint32_t notificationID)
1182 {
1183 #if CONFIG_EXCLAVES
1184 	kern_return_t ret;
1185 	IOInterruptEventSource *event;
1186 
1187 	IOLockLock(pRef->exclaveAsyncNotificationEventSourcesLock);
1188 	if (pRef->exclaveAsyncNotificationEventSources && (event = OSDynamicCast(IOInterruptEventSource, pRef->exclaveAsyncNotificationEventSources->getObject((unsigned int)notificationID)))) {
1189 		event->interruptOccurred(NULL, NULL, 0);
1190 		ret = kIOReturnSuccess;
1191 	} else {
1192 		ret = kIOReturnError;
1193 	}
1194 	IOLockUnlock(pRef->exclaveAsyncNotificationEventSourcesLock);
1195 	return ret;
1196 #else
1197 #pragma unused(pRef, notificationID)
1198 	return kIOReturnUnsupported;
1199 #endif /* CONFIG_EXCLAVES */
1200 }
1201 
1202 #if CONFIG_EXCLAVES
1203 
1204 void
exclaves_wait_for_cpu_init()1205 exclaves_wait_for_cpu_init()
1206 {
1207 	OSDictionary *match_dict = IOService::resourceMatching(gIOAllCPUInitializedKey);
1208 	IOService *match = IOService::waitForMatchingService(match_dict);
1209 	match_dict->release();
1210 	match->release();
1211 }
1212 
1213 /* ANE Upcalls */
1214 
1215 static kern_return_t
exclaveRegisterANEUpcallHelper(IOService::IOExclaveProxyState * pRef,IOExclaveANEUpcallType type,IOWorkLoop * wl,void * block)1216 exclaveRegisterANEUpcallHelper(IOService::IOExclaveProxyState * pRef, IOExclaveANEUpcallType type, IOWorkLoop *wl, void *block)
1217 {
1218 	void __block * _block = block;
1219 
1220 	if (!_block) {
1221 		return kIOReturnBadArgument;
1222 	}
1223 
1224 	if (!pRef) {
1225 		Block_release(_block);
1226 		return kIOReturnBadArgument;
1227 	}
1228 
1229 	if (wl != NULL) {
1230 		return wl->runActionBlock(^{
1231 			switch (type) {
1232 			case kIOExclaveANEUpcallTypeSetPowerState:
1233 				if (pRef->aneSetPowerStateUpcallHandler) {
1234 				        Block_release(pRef->aneSetPowerStateUpcallHandler);
1235 				}
1236 				pRef->aneSetPowerStateUpcallHandler = (ANEUpcallSetPowerStateHandler) _block;
1237 				break;
1238 			case kIOExclaveANEUpcallTypeWorkSubmit:
1239 				if (pRef->aneWorkSubmitUpcallHandler) {
1240 				        Block_release(pRef->aneWorkSubmitUpcallHandler);
1241 				}
1242 				pRef->aneWorkSubmitUpcallHandler = (ANEUpcallWorkHandler) _block;
1243 				break;
1244 			case kIOExclaveANEUpcallTypeWorkBegin:
1245 				if (pRef->aneWorkBeginUpcallHandler) {
1246 				        Block_release(pRef->aneWorkBeginUpcallHandler);
1247 				}
1248 				pRef->aneWorkBeginUpcallHandler = (ANEUpcallWorkHandler) _block;
1249 				break;
1250 			case kIOExclaveANEUpcallTypeWorkEnd:
1251 				if (pRef->aneWorkEndUpcallHandler) {
1252 				        Block_release(pRef->aneWorkEndUpcallHandler);
1253 				}
1254 				pRef->aneWorkEndUpcallHandler = (ANEUpcallWorkHandler) _block;
1255 				break;
1256 			default:
1257 				Block_release(_block);
1258 				return kIOReturnBadArgument;
1259 			}
1260 			return kIOReturnSuccess;
1261 		});
1262 	} else {
1263 		Block_release(_block);
1264 		return kIOReturnError;
1265 	}
1266 }
1267 
1268 #endif /* CONFIG_EXCLAVES */
1269 
1270 kern_return_t
exclaveRegisterANEUpcallSetPowerState(IOExclaveProxyState * pRef,ANEUpcallSetPowerStateHandler handler)1271 IOService::exclaveRegisterANEUpcallSetPowerState(IOExclaveProxyState * pRef, ANEUpcallSetPowerStateHandler handler)
1272 {
1273 #if CONFIG_EXCLAVES
1274 	return exclaveRegisterANEUpcallHelper(pRef, kIOExclaveANEUpcallTypeSetPowerState, getWorkLoop(), Block_copy(handler));
1275 #else
1276 #pragma unused(pRef, handler)
1277 	return kIOReturnUnsupported;
1278 #endif /* CONFIG_EXCLAVES*/
1279 }
1280 
1281 kern_return_t
exclaveRegisterANEUpcallWorkSubmit(IOExclaveProxyState * pRef,ANEUpcallWorkHandler handler)1282 IOService::exclaveRegisterANEUpcallWorkSubmit(IOExclaveProxyState * pRef, ANEUpcallWorkHandler handler)
1283 {
1284 #if CONFIG_EXCLAVES
1285 	return exclaveRegisterANEUpcallHelper(pRef, kIOExclaveANEUpcallTypeWorkSubmit, getWorkLoop(), Block_copy(handler));
1286 #else
1287 #pragma unused(pRef, handler)
1288 	return kIOReturnUnsupported;
1289 #endif /* CONFIG_EXCLAVES*/
1290 }
1291 
1292 kern_return_t
exclaveRegisterANEUpcallWorkBegin(IOExclaveProxyState * pRef,ANEUpcallWorkHandler handler)1293 IOService::exclaveRegisterANEUpcallWorkBegin(IOExclaveProxyState * pRef, ANEUpcallWorkHandler handler)
1294 {
1295 #if CONFIG_EXCLAVES
1296 	return exclaveRegisterANEUpcallHelper(pRef, kIOExclaveANEUpcallTypeWorkBegin, getWorkLoop(), Block_copy(handler));
1297 #else
1298 #pragma unused(pRef, handler)
1299 	return kIOReturnUnsupported;
1300 #endif /* CONFIG_EXCLAVES*/
1301 }
1302 
1303 kern_return_t
exclaveRegisterANEUpcallWorkEnd(IOExclaveProxyState * pRef,ANEUpcallWorkHandler handler)1304 IOService::exclaveRegisterANEUpcallWorkEnd(IOExclaveProxyState * pRef, ANEUpcallWorkHandler handler)
1305 {
1306 #if CONFIG_EXCLAVES
1307 	return exclaveRegisterANEUpcallHelper(pRef, kIOExclaveANEUpcallTypeWorkEnd, getWorkLoop(), Block_copy(handler));
1308 #else
1309 #pragma unused(pRef, handler)
1310 	return kIOReturnUnsupported;
1311 #endif /* CONFIG_EXCLAVES*/
1312 }
1313