1/*
2 * Copyright (c) 2019 Apple Computer, 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
29extern "C" {
30#include <mach/task.h>
31#include <pexpert/pexpert.h>
32};
33
34#include <machine/machine_routines.h>
35#include <IOKit/IOPlatformExpert.h>
36#include <IOKit/IOService.h>
37#include <IOKit/PassthruInterruptController.h>
38
39#define super IOInterruptController
40OSDefineMetaClassAndStructors(PassthruInterruptController, IOInterruptController);
41
42bool
43PassthruInterruptController::init(void)
44{
45 if (!super::init() ||
46 !this->setProperty(aKey: gPlatformInterruptControllerName, anObject: kOSBooleanTrue) ||
47 !this->attach(provider: getPlatform())) {
48 return false;
49 }
50 registerService();
51 if (getPlatform()->registerInterruptController(name: gPlatformInterruptControllerName, interruptController: this) != kIOReturnSuccess) {
52 return false;
53 }
54 if (semaphore_create(task: kernel_task, semaphore: &child_sentinel, SYNC_POLICY_FIFO, value: 0) != KERN_SUCCESS) {
55 return false;
56 }
57 return true;
58}
59
60void
61PassthruInterruptController::setCPUInterruptProperties(IOService *service)
62{
63 if ((service->getProperty(aKey: gIOInterruptControllersKey) != NULL) &&
64 (service->getProperty(aKey: gIOInterruptSpecifiersKey) != NULL)) {
65 return;
66 }
67
68 long zero = 0;
69 OSArray *specifier = OSArray::withCapacity(capacity: 1);
70 OSData *tmpData = OSData::withValue(value: zero);
71 specifier->setObject(tmpData);
72 tmpData->release();
73 service->setProperty(aKey: gIOInterruptSpecifiersKey, anObject: specifier);
74 specifier->release();
75
76 OSArray *controller = OSArray::withCapacity(capacity: 1);
77 controller->setObject(gPlatformInterruptControllerName);
78 service->setProperty(aKey: gIOInterruptControllersKey, anObject: controller);
79 controller->release();
80}
81
82IOReturn
83PassthruInterruptController::registerInterrupt(IOService *nub,
84 int source,
85 void *target,
86 IOInterruptHandler handler,
87 void *refCon)
88{
89 child_handler = handler;
90 child_nub = nub;
91 child_target = target;
92 child_refCon = refCon;
93
94 // Wake up waitForChildController() to tell it that AIC is registered
95 semaphore_signal(semaphore: child_sentinel);
96 return kIOReturnSuccess;
97}
98
99void *
100PassthruInterruptController::waitForChildController(void)
101{
102 // Block if child controller isn't registered yet. Assumes that this
103 // is only called from one place.
104 semaphore_wait(semaphore: child_sentinel);
105
106 // NOTE: Assumes that AppleInterruptController passes |this| as the target argument.
107 return child_target;
108}
109
110IOReturn
111PassthruInterruptController::getInterruptType(IOService */*nub*/,
112 int /*source*/,
113 int *interruptType)
114{
115 if (interruptType == NULL) {
116 return kIOReturnBadArgument;
117 }
118
119 *interruptType = kIOInterruptTypeLevel;
120
121 return kIOReturnSuccess;
122}
123
124IOReturn
125PassthruInterruptController::enableInterrupt(IOService */*nub*/,
126 int /*source*/)
127{
128 return kIOReturnSuccess;
129}
130
131IOReturn
132PassthruInterruptController::disableInterrupt(IOService */*nub*/,
133 int /*source*/)
134{
135 return kIOReturnSuccess;
136}
137
138IOReturn
139PassthruInterruptController::causeInterrupt(IOService */*nub*/,
140 int /*source*/)
141{
142 ml_cause_interrupt();
143 return kIOReturnSuccess;
144}
145
146IOReturn
147PassthruInterruptController::handleInterrupt(void */*refCon*/,
148 IOService */*nub*/,
149 int source)
150{
151 panic("handleInterrupt shouldn't be invoked directly");
152}
153
154void
155PassthruInterruptController::externalInterrupt(void)
156{
157 child_handler(child_target, child_refCon, child_nub, 0);
158}
159