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