1 /*
2  * Copyright (c) 1998-2010 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 #define IOKIT_ENABLE_SHARED_PTR
30 
31 #include <IOKit/IOFilterInterruptEventSource.h>
32 #include <IOKit/IOService.h>
33 #include <IOKit/IOKitDebug.h>
34 #include <IOKit/IOTimeStamp.h>
35 #include <IOKit/IOWorkLoop.h>
36 #include <IOKit/IOInterruptAccountingPrivate.h>
37 #include <libkern/Block.h>
38 
39 #if IOKITSTATS
40 
41 #define IOStatisticsInitializeCounter() \
42 do { \
43 	IOStatistics::setCounterType(IOEventSource::reserved->counter, kIOStatisticsFilterInterruptEventSourceCounter); \
44 } while (0)
45 
46 #define IOStatisticsInterrupt() \
47 do { \
48 	IOStatistics::countInterrupt(IOEventSource::reserved->counter); \
49 } while (0)
50 
51 #else
52 
53 #define IOStatisticsInitializeCounter()
54 #define IOStatisticsInterrupt()
55 
56 #endif /* IOKITSTATS */
57 
58 #define super IOInterruptEventSource
59 
60 OSDefineMetaClassAndStructors
61 (IOFilterInterruptEventSource, IOInterruptEventSource)
62 OSMetaClassDefineReservedUnused(IOFilterInterruptEventSource, 0);
63 OSMetaClassDefineReservedUnused(IOFilterInterruptEventSource, 1);
64 OSMetaClassDefineReservedUnused(IOFilterInterruptEventSource, 2);
65 OSMetaClassDefineReservedUnused(IOFilterInterruptEventSource, 3);
66 OSMetaClassDefineReservedUnused(IOFilterInterruptEventSource, 4);
67 OSMetaClassDefineReservedUnused(IOFilterInterruptEventSource, 5);
68 OSMetaClassDefineReservedUnused(IOFilterInterruptEventSource, 6);
69 OSMetaClassDefineReservedUnused(IOFilterInterruptEventSource, 7);
70 
71 /*
72  * Implement the call throughs for the private protection conversion
73  */
74 bool
init(OSObject * inOwner,Action inAction,IOService * inProvider,int inIntIndex)75 IOFilterInterruptEventSource::init(OSObject *inOwner,
76     Action inAction,
77     IOService *inProvider,
78     int inIntIndex)
79 {
80 	return false;
81 }
82 
83 OSSharedPtr<IOInterruptEventSource>
interruptEventSource(OSObject * inOwner,Action inAction,IOService * inProvider,int inIntIndex)84 IOFilterInterruptEventSource::interruptEventSource(OSObject *inOwner,
85     Action inAction,
86     IOService *inProvider,
87     int inIntIndex)
88 {
89 	return NULL;
90 }
91 
92 bool
init(OSObject * inOwner,Action inAction,Filter inFilterAction,IOService * inProvider,int inIntIndex)93 IOFilterInterruptEventSource::init(OSObject *inOwner,
94     Action inAction,
95     Filter inFilterAction,
96     IOService *inProvider,
97     int inIntIndex)
98 {
99 	if (!super::init(inOwner, inAction, inProvider, inIntIndex)) {
100 		return false;
101 	}
102 
103 	if (!inFilterAction) {
104 		return false;
105 	}
106 
107 	filterAction = inFilterAction;
108 
109 	IOStatisticsInitializeCounter();
110 
111 	return true;
112 }
113 
114 OSSharedPtr<IOFilterInterruptEventSource>
115 IOFilterInterruptEventSource
filterInterruptEventSource(OSObject * inOwner,Action inAction,Filter inFilterAction,IOService * inProvider,int inIntIndex)116 ::filterInterruptEventSource(OSObject *inOwner,
117     Action inAction,
118     Filter inFilterAction,
119     IOService *inProvider,
120     int inIntIndex)
121 {
122 	OSSharedPtr<IOFilterInterruptEventSource> me = OSMakeShared<IOFilterInterruptEventSource>();
123 
124 	if (me
125 	    && !me->init(inOwner, inAction, inFilterAction, inProvider, inIntIndex)) {
126 		return nullptr;
127 	}
128 
129 	return me;
130 }
131 
132 
133 OSSharedPtr<IOFilterInterruptEventSource>
134 IOFilterInterruptEventSource
filterInterruptEventSource(OSObject * inOwner,IOService * inProvider,int inIntIndex,ActionBlock inAction,FilterBlock inFilterAction)135 ::filterInterruptEventSource(OSObject *inOwner,
136     IOService *inProvider,
137     int inIntIndex,
138     ActionBlock inAction,
139     FilterBlock inFilterAction)
140 {
141 	OSSharedPtr<IOFilterInterruptEventSource> me = OSMakeShared<IOFilterInterruptEventSource>();
142 
143 	FilterBlock filter = Block_copy(inFilterAction);
144 	if (!filter) {
145 		return nullptr;
146 	}
147 
148 	if (me
149 	    && !me->init(inOwner, (Action) NULL, (Filter) (void (*)(void))filter, inProvider, inIntIndex)) {
150 		Block_release(filter);
151 		return nullptr;
152 	}
153 	me->flags |= kFilterBlock;
154 	me->setActionBlock((IOEventSource::ActionBlock) inAction);
155 
156 	return me;
157 }
158 
159 
160 void
free(void)161 IOFilterInterruptEventSource::free( void )
162 {
163 	if ((kFilterBlock & flags) && filterActionBlock) {
164 		Block_release(filterActionBlock);
165 	}
166 
167 	super::free();
168 }
169 
170 void
signalInterrupt()171 IOFilterInterruptEventSource::signalInterrupt()
172 {
173 	bool trace = (gIOKitTrace & kIOTraceIntEventSource) ? true : false;
174 
175 	IOStatisticsInterrupt();
176 	producerCount++;
177 
178 	if (trace) {
179 		IOTimeStampStartConstant(IODBG_INTES(IOINTES_SEMA), VM_KERNEL_ADDRHIDE(this), VM_KERNEL_ADDRHIDE(owner));
180 	}
181 
182 	signalWorkAvailable();
183 
184 	if (trace) {
185 		IOTimeStampEndConstant(IODBG_INTES(IOINTES_SEMA), VM_KERNEL_ADDRHIDE(this), VM_KERNEL_ADDRHIDE(owner));
186 	}
187 }
188 
189 
190 IOFilterInterruptEventSource::Filter
getFilterAction() const191 IOFilterInterruptEventSource::getFilterAction() const
192 {
193 	if (kFilterBlock & flags) {
194 		return NULL;
195 	}
196 	return filterAction;
197 }
198 
199 IOFilterInterruptEventSource::FilterBlock
getFilterActionBlock() const200 IOFilterInterruptEventSource::getFilterActionBlock() const
201 {
202 	if (kFilterBlock & flags) {
203 		return filterActionBlock;
204 	}
205 	return NULL;
206 }
207 
208 void
normalInterruptOccurred(void *,IOService *,int)209 IOFilterInterruptEventSource::normalInterruptOccurred
210 (void */*refcon*/, IOService */*prov*/, int /*source*/)
211 {
212 	bool        filterRes;
213 	uint64_t    startTime = 0;
214 	uint64_t    endTime = 0;
215 	bool    trace = (gIOKitTrace & kIOTraceIntEventSource) ? true : false;
216 
217 	if (trace) {
218 		IOTimeStampStartConstant(IODBG_INTES(IOINTES_FILTER),
219 		    VM_KERNEL_UNSLIDE(filterAction), VM_KERNEL_ADDRHIDE(owner), VM_KERNEL_ADDRHIDE(this), VM_KERNEL_ADDRHIDE(workLoop));
220 	}
221 
222 	if (IOInterruptEventSource::reserved->statistics) {
223 		if (IA_GET_STATISTIC_ENABLED(kInterruptAccountingFirstLevelTimeIndex)
224 		    || IOInterruptEventSource::reserved->statistics->enablePrimaryTimestamp) {
225 			startTime = mach_absolute_time();
226 		}
227 		if (IOInterruptEventSource::reserved->statistics->enablePrimaryTimestamp) {
228 			IOInterruptEventSource::reserved->statistics->primaryTimestamp = startTime;
229 		}
230 	}
231 
232 	// Call the filter.
233 	if (kFilterBlock & flags) {
234 		filterRes = (filterActionBlock)(this);
235 	} else {
236 		filterRes = (*filterAction)(owner, this);
237 	}
238 
239 	if (IOInterruptEventSource::reserved->statistics) {
240 		if (IA_GET_STATISTIC_ENABLED(kInterruptAccountingFirstLevelCountIndex)) {
241 			IA_ADD_VALUE(&IOInterruptEventSource::reserved->statistics->interruptStatistics[kInterruptAccountingFirstLevelCountIndex], 1);
242 		}
243 
244 		if (IA_GET_STATISTIC_ENABLED(kInterruptAccountingFirstLevelTimeIndex)) {
245 			endTime = mach_absolute_time();
246 			IA_ADD_VALUE(&IOInterruptEventSource::reserved->statistics->interruptStatistics[kInterruptAccountingFirstLevelTimeIndex], endTime - startTime);
247 		}
248 	}
249 
250 	if (trace) {
251 		IOTimeStampEndConstant(IODBG_INTES(IOINTES_FILTER),
252 		    VM_KERNEL_ADDRHIDE(filterAction), VM_KERNEL_ADDRHIDE(owner),
253 		    VM_KERNEL_ADDRHIDE(this), VM_KERNEL_ADDRHIDE(workLoop));
254 	}
255 
256 	if (filterRes) {
257 		signalInterrupt();
258 	}
259 }
260 
261 void
disableInterruptOccurred(void *,IOService * prov,int source)262 IOFilterInterruptEventSource::disableInterruptOccurred
263 (void */*refcon*/, IOService *prov, int source)
264 {
265 	bool        filterRes;
266 	uint64_t    startTime = 0;
267 	uint64_t    endTime = 0;
268 	bool    trace = (gIOKitTrace & kIOTraceIntEventSource) ? true : false;
269 
270 	if (trace) {
271 		IOTimeStampStartConstant(IODBG_INTES(IOINTES_FILTER),
272 		    VM_KERNEL_UNSLIDE(filterAction), VM_KERNEL_ADDRHIDE(owner), VM_KERNEL_ADDRHIDE(this), VM_KERNEL_ADDRHIDE(workLoop));
273 	}
274 
275 	if (IOInterruptEventSource::reserved->statistics) {
276 		if (IA_GET_STATISTIC_ENABLED(kInterruptAccountingFirstLevelTimeIndex)
277 		    || IOInterruptEventSource::reserved->statistics->enablePrimaryTimestamp) {
278 			startTime = mach_absolute_time();
279 		}
280 		if (IOInterruptEventSource::reserved->statistics->enablePrimaryTimestamp) {
281 			IOInterruptEventSource::reserved->statistics->primaryTimestamp = startTime;
282 		}
283 	}
284 
285 	// Call the filter.
286 	if (kFilterBlock & flags) {
287 		filterRes = (filterActionBlock)(this);
288 	} else {
289 		filterRes = (*filterAction)(owner, this);
290 	}
291 
292 	if (IOInterruptEventSource::reserved->statistics) {
293 		if (IA_GET_STATISTIC_ENABLED(kInterruptAccountingFirstLevelCountIndex)) {
294 			IA_ADD_VALUE(&IOInterruptEventSource::reserved->statistics->interruptStatistics[kInterruptAccountingFirstLevelCountIndex], 1);
295 		}
296 
297 		if (IA_GET_STATISTIC_ENABLED(kInterruptAccountingFirstLevelTimeIndex)) {
298 			endTime = mach_absolute_time();
299 			IA_ADD_VALUE(&IOInterruptEventSource::reserved->statistics->interruptStatistics[kInterruptAccountingFirstLevelTimeIndex], endTime - startTime);
300 		}
301 	}
302 
303 	if (trace) {
304 		IOTimeStampEndConstant(IODBG_INTES(IOINTES_FILTER),
305 		    VM_KERNEL_UNSLIDE(filterAction), VM_KERNEL_ADDRHIDE(owner), VM_KERNEL_ADDRHIDE(this), VM_KERNEL_ADDRHIDE(workLoop));
306 	}
307 
308 	if (filterRes) {
309 		prov->disableInterrupt(source); /* disable the interrupt */
310 		signalInterrupt();
311 	}
312 }
313