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