]>
Commit | Line | Data |
---|---|---|
1c79356b | 1 | /* |
39037602 | 2 | * Copyright (c) 1998-2016 Apple Inc. All rights reserved. |
1c79356b | 3 | * |
2d21ac55 | 4 | * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ |
0a7de745 | 5 | * |
2d21ac55 A |
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. | |
0a7de745 | 14 | * |
2d21ac55 A |
15 | * Please obtain a copy of the License at |
16 | * http://www.opensource.apple.com/apsl/ and read it before using this file. | |
0a7de745 | 17 | * |
2d21ac55 A |
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 | |
8f6c56a5 A |
20 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, |
21 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
2d21ac55 A |
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. | |
0a7de745 | 25 | * |
2d21ac55 | 26 | * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ |
1c79356b | 27 | */ |
0a7de745 | 28 | |
1c79356b | 29 | #include <IOKit/system.h> |
1c79356b | 30 | #include <IOKit/IOService.h> |
b0d623f7 | 31 | #include <libkern/OSDebug.h> |
1c79356b | 32 | #include <libkern/c++/OSContainers.h> |
b0d623f7 | 33 | #include <libkern/c++/OSKext.h> |
1c79356b | 34 | #include <libkern/c++/OSUnserialize.h> |
cb323159 | 35 | #include <libkern/c++/OSKext.h> |
f427ee49 | 36 | #include <libkern/c++/OSSharedPtr.h> |
d9a64523 | 37 | #include <libkern/Block.h> |
1c79356b | 38 | #include <IOKit/IOCatalogue.h> |
91447636 | 39 | #include <IOKit/IOCommand.h> |
316670eb | 40 | #include <IOKit/IODeviceTreeSupport.h> |
1c79356b A |
41 | #include <IOKit/IODeviceMemory.h> |
42 | #include <IOKit/IOInterrupts.h> | |
43 | #include <IOKit/IOInterruptController.h> | |
44 | #include <IOKit/IOPlatformExpert.h> | |
45 | #include <IOKit/IOMessage.h> | |
46 | #include <IOKit/IOLib.h> | |
55e303ae | 47 | #include <IOKit/IOKitKeysPrivate.h> |
1c79356b A |
48 | #include <IOKit/IOBSD.h> |
49 | #include <IOKit/IOUserClient.h> | |
cb323159 | 50 | #include <IOKit/IOUserServer.h> |
0b4e3aa0 | 51 | #include <IOKit/IOWorkLoop.h> |
b0d623f7 | 52 | #include <IOKit/IOTimeStamp.h> |
316670eb | 53 | #include <IOKit/IOHibernatePrivate.h> |
fe8ab488 A |
54 | #include <IOKit/IOInterruptAccountingPrivate.h> |
55 | #include <IOKit/IOKernelReporters.h> | |
3e170ce0 | 56 | #include <IOKit/AppleKeyStoreInterface.h> |
5ba3f43e | 57 | #include <IOKit/pwr_mgt/RootDomain.h> |
3e170ce0 | 58 | #include <IOKit/IOCPU.h> |
0b4e3aa0 | 59 | #include <mach/sync_policy.h> |
cb323159 | 60 | #include <mach/thread_info.h> |
0b4e3aa0 A |
61 | #include <IOKit/assert.h> |
62 | #include <sys/errno.h> | |
3e170ce0 A |
63 | #include <sys/kdebug.h> |
64 | #include <string.h> | |
1c79356b | 65 | |
6d2010ae A |
66 | #include <machine/pal_routines.h> |
67 | ||
b0d623f7 A |
68 | #define LOG kprintf |
69 | //#define LOG IOLog | |
0a7de745 | 70 | #define MATCH_DEBUG 0 |
5ba3f43e A |
71 | #define IOSERVICE_OBFUSCATE(x) ((void *)(VM_KERNEL_ADDRPERM(x))) |
72 | ||
73 | // disabled since lockForArbitration() can be held externally | |
0a7de745 | 74 | #define DEBUG_NOTIFIER_LOCKED 0 |
1c79356b | 75 | |
cb323159 A |
76 | enum{ |
77 | kIOUserServerCheckInTimeoutSecs = 120ULL | |
78 | }; | |
79 | ||
1c79356b | 80 | #include "IOServicePrivate.h" |
6d2010ae | 81 | #include "IOKitKernelInternal.h" |
1c79356b | 82 | |
0b4e3aa0 | 83 | // take lockForArbitration before LOCKNOTIFY |
1c79356b A |
84 | |
85 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ | |
86 | ||
87 | #define super IORegistryEntry | |
88 | ||
89 | OSDefineMetaClassAndStructors(IOService, IORegistryEntry) | |
90 | ||
91 | OSDefineMetaClassAndStructors(_IOServiceNotifier, IONotifier) | |
39037602 | 92 | OSDefineMetaClassAndStructors(_IOServiceNullNotifier, IONotifier) |
1c79356b A |
93 | |
94 | OSDefineMetaClassAndStructors(_IOServiceInterestNotifier, IONotifier) | |
95 | ||
96 | OSDefineMetaClassAndStructors(_IOConfigThread, OSObject) | |
97 | ||
98 | OSDefineMetaClassAndStructors(_IOServiceJob, OSObject) | |
99 | ||
100 | OSDefineMetaClassAndStructors(IOResources, IOService) | |
cb323159 | 101 | OSDefineMetaClassAndStructors(IOUserResources, IOService) |
1c79356b A |
102 | |
103 | OSDefineMetaClassAndStructors(_IOOpenServiceIterator, OSIterator) | |
104 | ||
105 | OSDefineMetaClassAndAbstractStructors(IONotifier, OSObject) | |
106 | ||
f427ee49 A |
107 | OSDefineMetaClassAndStructors(IOServiceCompatibility, IOService) |
108 | ||
1c79356b A |
109 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ |
110 | ||
0a7de745 A |
111 | static IOPlatformExpert * gIOPlatform; |
112 | static class IOPMrootDomain * gIOPMRootDomain; | |
113 | const IORegistryPlane * gIOServicePlane; | |
114 | const IORegistryPlane * gIOPowerPlane; | |
115 | const OSSymbol * gIODeviceMemoryKey; | |
116 | const OSSymbol * gIOInterruptControllersKey; | |
117 | const OSSymbol * gIOInterruptSpecifiersKey; | |
118 | ||
119 | const OSSymbol * gIOResourcesKey; | |
cb323159 | 120 | const OSSymbol * gIOUserResourcesKey; |
0a7de745 A |
121 | const OSSymbol * gIOResourceMatchKey; |
122 | const OSSymbol * gIOResourceMatchedKey; | |
cb323159 A |
123 | const OSSymbol * gIOResourceIOKitKey; |
124 | ||
0a7de745 A |
125 | const OSSymbol * gIOProviderClassKey; |
126 | const OSSymbol * gIONameMatchKey; | |
127 | const OSSymbol * gIONameMatchedKey; | |
128 | const OSSymbol * gIOPropertyMatchKey; | |
129 | const OSSymbol * gIOPropertyExistsMatchKey; | |
130 | const OSSymbol * gIOLocationMatchKey; | |
131 | const OSSymbol * gIOParentMatchKey; | |
132 | const OSSymbol * gIOPathMatchKey; | |
133 | const OSSymbol * gIOMatchCategoryKey; | |
134 | const OSSymbol * gIODefaultMatchCategoryKey; | |
135 | const OSSymbol * gIOMatchedServiceCountKey; | |
cb323159 A |
136 | const OSSymbol * gIOMatchedPersonalityKey; |
137 | const OSSymbol * gIORematchPersonalityKey; | |
138 | const OSSymbol * gIORematchCountKey; | |
139 | const OSSymbol * gIODEXTMatchCountKey; | |
ea3f0419 A |
140 | const OSSymbol * gIOSupportedPropertiesKey; |
141 | const OSSymbol * gIOUserServicePropertiesKey; | |
f427ee49 | 142 | #if defined(XNU_TARGET_OS_OSX) |
0a7de745 | 143 | const OSSymbol * gIOServiceLegacyMatchingRegistryIDKey; |
f427ee49 A |
144 | #endif /* defined(XNU_TARGET_OS_OSX) */ |
145 | ||
146 | const OSSymbol * gIOCompatibilityMatchKey; | |
147 | const OSSymbol * gIOCompatibilityPropertiesKey; | |
148 | const OSSymbol * gIOPathKey; | |
1c79356b | 149 | |
0a7de745 A |
150 | const OSSymbol * gIOMapperIDKey; |
151 | const OSSymbol * gIOUserClientClassKey; | |
cb323159 A |
152 | |
153 | const OSSymbol * gIOUserClassKey; | |
154 | const OSSymbol * gIOUserServerClassKey; | |
155 | const OSSymbol * gIOUserServerNameKey; | |
156 | const OSSymbol * gIOUserServerTagKey; | |
cb323159 A |
157 | const OSSymbol * gIOUserUserClientKey; |
158 | ||
0a7de745 A |
159 | const OSSymbol * gIOKitDebugKey; |
160 | ||
161 | const OSSymbol * gIOCommandPoolSizeKey; | |
162 | ||
163 | const OSSymbol * gIOConsoleLockedKey; | |
164 | const OSSymbol * gIOConsoleUsersKey; | |
165 | const OSSymbol * gIOConsoleSessionUIDKey; | |
166 | const OSSymbol * gIOConsoleSessionAuditIDKey; | |
167 | const OSSymbol * gIOConsoleUsersSeedKey; | |
168 | const OSSymbol * gIOConsoleSessionOnConsoleKey; | |
169 | const OSSymbol * gIOConsoleSessionLoginDoneKey; | |
170 | const OSSymbol * gIOConsoleSessionSecureInputPIDKey; | |
171 | const OSSymbol * gIOConsoleSessionScreenLockedTimeKey; | |
172 | const OSSymbol * gIOConsoleSessionScreenIsLockedKey; | |
173 | clock_sec_t gIOConsoleLockTime; | |
174 | static bool gIOConsoleLoggedIn; | |
39236c6e | 175 | #if HIBERNATION |
0a7de745 A |
176 | static OSBoolean * gIOConsoleBooterLockState; |
177 | static uint32_t gIOScreenLockState; | |
39236c6e | 178 | #endif |
316670eb | 179 | static IORegistryEntry * gIOChosenEntry; |
55e303ae | 180 | |
0a7de745 | 181 | static int gIOResourceGenerationCount; |
1c79356b | 182 | |
0a7de745 A |
183 | const OSSymbol * gIOServiceKey; |
184 | const OSSymbol * gIOPublishNotification; | |
185 | const OSSymbol * gIOFirstPublishNotification; | |
186 | const OSSymbol * gIOMatchedNotification; | |
187 | const OSSymbol * gIOFirstMatchNotification; | |
188 | const OSSymbol * gIOTerminatedNotification; | |
189 | const OSSymbol * gIOWillTerminateNotification; | |
1c79356b | 190 | |
cb323159 A |
191 | const OSSymbol * gIOServiceDEXTEntitlementsKey; |
192 | const OSSymbol * gIODriverKitEntitlementKey; | |
193 | const OSSymbol * gIODriverKitUserClientEntitlementsKey; | |
eb6b6ca3 | 194 | const OSSymbol * gIODriverKitUserClientEntitlementAllowAnyKey; |
cb323159 | 195 | const OSSymbol * gIOMatchDeferKey; |
f427ee49 | 196 | const OSSymbol * gIOAllCPUInitializedKey; |
cb323159 | 197 | |
0a7de745 A |
198 | const OSSymbol * gIOGeneralInterest; |
199 | const OSSymbol * gIOBusyInterest; | |
200 | const OSSymbol * gIOAppPowerStateInterest; | |
201 | const OSSymbol * gIOPriorityPowerStateInterest; | |
202 | const OSSymbol * gIOConsoleSecurityInterest; | |
1c79356b | 203 | |
0a7de745 A |
204 | const OSSymbol * gIOBSDKey; |
205 | const OSSymbol * gIOBSDNameKey; | |
206 | const OSSymbol * gIOBSDMajorKey; | |
207 | const OSSymbol * gIOBSDMinorKey; | |
208 | const OSSymbol * gIOBSDUnitKey; | |
39037602 | 209 | |
3e170ce0 A |
210 | const OSSymbol * gAKSGetKey; |
211 | #if defined(__i386__) || defined(__x86_64__) | |
212 | const OSSymbol * gIOCreateEFIDevicePathSymbol; | |
213 | #endif | |
214 | ||
0a7de745 A |
215 | static OSDictionary * gNotifications; |
216 | static IORecursiveLock * gNotificationLock; | |
1c79356b | 217 | |
0a7de745 | 218 | static IOService * gIOResources; |
cb323159 | 219 | static IOService * gIOUserResources; |
0a7de745 | 220 | static IOService * gIOServiceRoot; |
1c79356b | 221 | |
0a7de745 A |
222 | static OSOrderedSet * gJobs; |
223 | static semaphore_port_t gJobsSemaphore; | |
224 | static IOLock * gJobsLock; | |
225 | static int gOutstandingJobs; | |
226 | static int gNumConfigThreads; | |
f427ee49 A |
227 | static int gHighNumConfigThreads; |
228 | static int gMaxConfigThreads = kMaxConfigThreads; | |
0a7de745 A |
229 | static int gNumWaitingThreads; |
230 | static IOLock * gIOServiceBusyLock; | |
231 | bool gCPUsRunning; | |
f427ee49 A |
232 | bool gIOKitWillTerminate; |
233 | bool gInUserspaceReboot; | |
1c79356b | 234 | |
0a7de745 A |
235 | static thread_t gIOTerminateThread; |
236 | static thread_t gIOTerminateWorkerThread; | |
237 | static UInt32 gIOTerminateWork; | |
238 | static OSArray * gIOTerminatePhase2List; | |
239 | static OSArray * gIOStopList; | |
240 | static OSArray * gIOStopProviderList; | |
241 | static OSArray * gIOFinalizeList; | |
0b4e3aa0 | 242 | |
cb323159 A |
243 | #if !NO_KEXTD |
244 | static OSArray * gIOMatchDeferList; | |
245 | #endif | |
246 | ||
0a7de745 A |
247 | static SInt32 gIOConsoleUsersSeed; |
248 | static OSData * gIOConsoleUsersSeedValue; | |
55e303ae | 249 | |
0a7de745 | 250 | extern const OSSymbol * gIODTPHandleKey; |
2d21ac55 | 251 | |
0a7de745 | 252 | const OSSymbol * gIOPlatformFunctionHandlerSet; |
593a1d5f | 253 | |
cb323159 | 254 | |
0a7de745 A |
255 | static IOLock * gIOConsoleUsersLock; |
256 | static thread_call_t gIOConsoleLockCallout; | |
39037602 | 257 | static IONotifier * gIOServiceNullNotifier; |
6d2010ae | 258 | |
cb323159 A |
259 | static uint32_t gIODextRelaunchMax = 1000; |
260 | ||
f427ee49 A |
261 | #if DEVELOPMENT || DEBUG |
262 | uint64_t driverkit_checkin_timed_out = 0; | |
263 | #endif | |
264 | ||
1c79356b A |
265 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ |
266 | ||
0a7de745 | 267 | #define LOCKREADNOTIFY() \ |
1c79356b | 268 | IORecursiveLockLock( gNotificationLock ) |
0a7de745 | 269 | #define LOCKWRITENOTIFY() \ |
1c79356b A |
270 | IORecursiveLockLock( gNotificationLock ) |
271 | #define LOCKWRITE2READNOTIFY() | |
0a7de745 | 272 | #define UNLOCKNOTIFY() \ |
1c79356b | 273 | IORecursiveLockUnlock( gNotificationLock ) |
9bccf70c A |
274 | #define SLEEPNOTIFY(event) \ |
275 | IORecursiveLockSleep( gNotificationLock, (void *)(event), THREAD_UNINT ) | |
b0d623f7 A |
276 | #define SLEEPNOTIFYTO(event, deadline) \ |
277 | IORecursiveLockSleepDeadline( gNotificationLock, (void *)(event), deadline, THREAD_UNINT ) | |
9bccf70c A |
278 | #define WAKEUPNOTIFY(event) \ |
279 | IORecursiveLockWakeup( gNotificationLock, (void *)(event), /* wake one */ false ) | |
1c79356b | 280 | |
0a7de745 A |
281 | #define randomDelay() \ |
282 | int del = read_processor_clock(); \ | |
283 | del = (((int)IOThreadSelf()) ^ del ^ (del >> 10)) & 0x3ff; \ | |
284 | IOSleep( del ); | |
0b4e3aa0 | 285 | |
1c79356b A |
286 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ |
287 | ||
0a7de745 A |
288 | #define queue_element(entry, element, type, field) do { \ |
289 | vm_address_t __ele = (vm_address_t) (entry); \ | |
290 | __ele -= -4 + ((size_t)(&((type) 4)->field)); \ | |
291 | (element) = (type) __ele; \ | |
91447636 A |
292 | } while(0) |
293 | ||
0a7de745 A |
294 | #define iterqueue(que, elt) \ |
295 | for (queue_entry_t elt = queue_first(que); \ | |
296 | !queue_end(que, elt); \ | |
91447636 A |
297 | elt = queue_next(elt)) |
298 | ||
299 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ | |
300 | ||
fe8ab488 | 301 | struct IOInterruptAccountingReporter { |
0a7de745 A |
302 | IOSimpleReporter * reporter; /* Reporter responsible for communicating the statistics */ |
303 | IOInterruptAccountingData * statistics; /* The live statistics values, if any */ | |
fe8ab488 A |
304 | }; |
305 | ||
1c79356b | 306 | struct ArbitrationLockQueueElement { |
0a7de745 A |
307 | queue_chain_t link; |
308 | IOThread thread; | |
309 | IOService * service; | |
310 | unsigned count; | |
311 | bool required; | |
312 | bool aborted; | |
1c79356b A |
313 | }; |
314 | ||
315 | static queue_head_t gArbitrationLockQueueActive; | |
316 | static queue_head_t gArbitrationLockQueueWaiting; | |
317 | static queue_head_t gArbitrationLockQueueFree; | |
318 | static IOLock * gArbitrationLockQueueLock; | |
319 | ||
0a7de745 A |
320 | bool |
321 | IOService::isInactive( void ) const | |
322 | { | |
323 | return 0 != (kIOServiceInactiveState & getState()); | |
324 | } | |
9bccf70c | 325 | |
1c79356b A |
326 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ |
327 | ||
0c530ab8 | 328 | // Only used by the intel implementation of |
593a1d5f A |
329 | // IOService::requireMaxBusStall(UInt32 ns) |
330 | // IOService::requireMaxInterruptDelay(uint32_t ns) | |
0a7de745 A |
331 | struct CpuDelayEntry { |
332 | IOService * fService; | |
333 | UInt32 fMaxDelay; | |
334 | UInt32 fDelayType; | |
593a1d5f A |
335 | }; |
336 | ||
337 | enum { | |
f427ee49 A |
338 | kCpuDelayBusStall, |
339 | #if defined(__x86_64__) | |
340 | kCpuDelayInterrupt, | |
341 | #endif /* defined(__x86_64__) */ | |
0a7de745 | 342 | kCpuNumDelayTypes |
0c530ab8 A |
343 | }; |
344 | ||
593a1d5f A |
345 | static OSData *sCpuDelayData = OSData::withCapacity(8 * sizeof(CpuDelayEntry)); |
346 | static IORecursiveLock *sCpuDelayLock = IORecursiveLockAlloc(); | |
347 | static OSArray *sCpuLatencyHandlers[kCpuNumDelayTypes]; | |
348 | const OSSymbol *sCPULatencyFunctionName[kCpuNumDelayTypes]; | |
fe8ab488 | 349 | static OSNumber * sCPULatencyHolder[kCpuNumDelayTypes]; |
3e170ce0 | 350 | static char sCPULatencyHolderName[kCpuNumDelayTypes][128]; |
fe8ab488 | 351 | static OSNumber * sCPULatencySet[kCpuNumDelayTypes]; |
593a1d5f A |
352 | |
353 | static void | |
354 | requireMaxCpuDelay(IOService * service, UInt32 ns, UInt32 delayType); | |
355 | static IOReturn | |
356 | setLatencyHandler(UInt32 delayType, IOService * target, bool enable); | |
357 | ||
0c530ab8 A |
358 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ |
359 | ||
f427ee49 A |
360 | static IOMessage sSystemPower; |
361 | ||
cb323159 A |
362 | namespace IOServicePH |
363 | { | |
364 | IONotifier * fRootNotifier; | |
365 | OSArray * fUserServers; | |
366 | OSArray * fUserServersWait; | |
367 | OSArray * fMatchingWork; | |
368 | OSArray * fMatchingDelayed; | |
369 | IOService * fSystemPowerAckTo; | |
370 | uint32_t fSystemPowerAckRef; | |
371 | uint8_t fSystemOff; | |
372 | uint8_t fUserServerOff; | |
373 | ||
374 | void lock(); | |
375 | void unlock(); | |
376 | ||
377 | void init(IOPMrootDomain * root); | |
378 | ||
379 | IOReturn systemPowerChange( | |
380 | void * target, | |
381 | void * refCon, | |
382 | UInt32 messageType, IOService * service, | |
383 | void * messageArgument, vm_size_t argSize); | |
384 | ||
385 | bool matchingStart(IOService * service); | |
386 | void matchingEnd(IOService * service); | |
387 | }; | |
388 | ||
389 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ | |
390 | ||
0a7de745 A |
391 | void |
392 | IOService::initialize( void ) | |
1c79356b | 393 | { |
0a7de745 | 394 | kern_return_t err; |
1c79356b | 395 | |
0a7de745 A |
396 | gIOServicePlane = IORegistryEntry::makePlane( kIOServicePlane ); |
397 | gIOPowerPlane = IORegistryEntry::makePlane( kIOPowerPlane ); | |
1c79356b | 398 | |
0a7de745 A |
399 | gIOProviderClassKey = OSSymbol::withCStringNoCopy( kIOProviderClassKey ); |
400 | gIONameMatchKey = OSSymbol::withCStringNoCopy( kIONameMatchKey ); | |
401 | gIONameMatchedKey = OSSymbol::withCStringNoCopy( kIONameMatchedKey ); | |
402 | gIOPropertyMatchKey = OSSymbol::withCStringNoCopy( kIOPropertyMatchKey ); | |
403 | gIOPropertyExistsMatchKey = OSSymbol::withCStringNoCopy( kIOPropertyExistsMatchKey ); | |
404 | gIOPathMatchKey = OSSymbol::withCStringNoCopy( kIOPathMatchKey ); | |
405 | gIOLocationMatchKey = OSSymbol::withCStringNoCopy( kIOLocationMatchKey ); | |
406 | gIOParentMatchKey = OSSymbol::withCStringNoCopy( kIOParentMatchKey ); | |
1c79356b | 407 | |
0a7de745 A |
408 | gIOMatchCategoryKey = OSSymbol::withCStringNoCopy( kIOMatchCategoryKey ); |
409 | gIODefaultMatchCategoryKey = OSSymbol::withCStringNoCopy( | |
410 | kIODefaultMatchCategoryKey ); | |
411 | gIOMatchedServiceCountKey = OSSymbol::withCStringNoCopy( | |
412 | kIOMatchedServiceCountKey ); | |
cb323159 A |
413 | gIOMatchedPersonalityKey = OSSymbol::withCStringNoCopy( |
414 | kIOMatchedPersonalityKey ); | |
415 | gIORematchPersonalityKey = OSSymbol::withCStringNoCopy( | |
416 | kIORematchPersonalityKey ); | |
417 | gIORematchCountKey = OSSymbol::withCStringNoCopy( | |
418 | kIORematchCountKey ); | |
419 | gIODEXTMatchCountKey = OSSymbol::withCStringNoCopy( | |
420 | kIODEXTMatchCountKey ); | |
421 | ||
f427ee49 | 422 | #if defined(XNU_TARGET_OS_OSX) |
0a7de745 A |
423 | gIOServiceLegacyMatchingRegistryIDKey = OSSymbol::withCStringNoCopy( |
424 | kIOServiceLegacyMatchingRegistryIDKey ); | |
f427ee49 | 425 | #endif /* defined(XNU_TARGET_OS_OSX) */ |
1c79356b | 426 | |
cb323159 | 427 | PE_parse_boot_argn("dextrelaunch", &gIODextRelaunchMax, sizeof(gIODextRelaunchMax)); |
f427ee49 | 428 | PE_parse_boot_argn("iocthreads", &gMaxConfigThreads, sizeof(gMaxConfigThreads)); |
cb323159 | 429 | |
0a7de745 A |
430 | gIOUserClientClassKey = OSSymbol::withCStringNoCopy( kIOUserClientClassKey ); |
431 | ||
cb323159 A |
432 | gIOUserClassKey = OSSymbol::withCStringNoCopy(kIOUserClassKey); |
433 | ||
434 | gIOUserServerClassKey = OSSymbol::withCStringNoCopy(kIOUserServerClassKey); | |
435 | gIOUserServerNameKey = OSSymbol::withCStringNoCopy(kIOUserServerNameKey); | |
436 | gIOUserServerTagKey = OSSymbol::withCStringNoCopy(kIOUserServerTagKey); | |
cb323159 A |
437 | gIOUserUserClientKey = OSSymbol::withCStringNoCopy(kIOUserUserClientKey); |
438 | ||
0a7de745 A |
439 | gIOResourcesKey = OSSymbol::withCStringNoCopy( kIOResourcesClass ); |
440 | gIOResourceMatchKey = OSSymbol::withCStringNoCopy( kIOResourceMatchKey ); | |
441 | gIOResourceMatchedKey = OSSymbol::withCStringNoCopy( kIOResourceMatchedKey ); | |
cb323159 | 442 | gIOResourceIOKitKey = OSSymbol::withCStringNoCopy("IOKit"); |
0a7de745 A |
443 | |
444 | gIODeviceMemoryKey = OSSymbol::withCStringNoCopy( "IODeviceMemory" ); | |
445 | gIOInterruptControllersKey | |
446 | = OSSymbol::withCStringNoCopy("IOInterruptControllers"); | |
447 | gIOInterruptSpecifiersKey | |
448 | = OSSymbol::withCStringNoCopy("IOInterruptSpecifiers"); | |
449 | ||
f427ee49 A |
450 | gIOCompatibilityMatchKey = OSSymbol::withCStringNoCopy(kIOCompatibilityMatchKey); |
451 | gIOCompatibilityPropertiesKey = OSSymbol::withCStringNoCopy(kIOCompatibilityPropertiesKey); | |
452 | gIOPathKey = OSSymbol::withCStringNoCopy(kIOPathKey); | |
ea3f0419 A |
453 | gIOSupportedPropertiesKey = OSSymbol::withCStringNoCopy(kIOSupportedPropertiesKey); |
454 | gIOUserServicePropertiesKey = OSSymbol::withCStringNoCopy(kIOUserServicePropertiesKey); | |
455 | ||
0a7de745 A |
456 | gIOMapperIDKey = OSSymbol::withCStringNoCopy(kIOMapperIDKey); |
457 | ||
458 | gIOKitDebugKey = OSSymbol::withCStringNoCopy( kIOKitDebugKey ); | |
459 | ||
460 | gIOCommandPoolSizeKey = OSSymbol::withCStringNoCopy( kIOCommandPoolSizeKey ); | |
461 | ||
462 | gIOGeneralInterest = OSSymbol::withCStringNoCopy( kIOGeneralInterest ); | |
463 | gIOBusyInterest = OSSymbol::withCStringNoCopy( kIOBusyInterest ); | |
464 | gIOAppPowerStateInterest = OSSymbol::withCStringNoCopy( kIOAppPowerStateInterest ); | |
465 | gIOPriorityPowerStateInterest = OSSymbol::withCStringNoCopy( kIOPriorityPowerStateInterest ); | |
466 | gIOConsoleSecurityInterest = OSSymbol::withCStringNoCopy( kIOConsoleSecurityInterest ); | |
467 | ||
468 | gIOBSDKey = OSSymbol::withCStringNoCopy(kIOBSDKey); | |
469 | gIOBSDNameKey = OSSymbol::withCStringNoCopy(kIOBSDNameKey); | |
470 | gIOBSDMajorKey = OSSymbol::withCStringNoCopy(kIOBSDMajorKey); | |
471 | gIOBSDMinorKey = OSSymbol::withCStringNoCopy(kIOBSDMinorKey); | |
472 | gIOBSDUnitKey = OSSymbol::withCStringNoCopy(kIOBSDUnitKey); | |
473 | ||
474 | gNotifications = OSDictionary::withCapacity( 1 ); | |
475 | gIOPublishNotification = OSSymbol::withCStringNoCopy( | |
476 | kIOPublishNotification ); | |
477 | gIOFirstPublishNotification = OSSymbol::withCStringNoCopy( | |
478 | kIOFirstPublishNotification ); | |
479 | gIOMatchedNotification = OSSymbol::withCStringNoCopy( | |
480 | kIOMatchedNotification ); | |
481 | gIOFirstMatchNotification = OSSymbol::withCStringNoCopy( | |
482 | kIOFirstMatchNotification ); | |
483 | gIOTerminatedNotification = OSSymbol::withCStringNoCopy( | |
484 | kIOTerminatedNotification ); | |
485 | gIOWillTerminateNotification = OSSymbol::withCStringNoCopy( | |
486 | kIOWillTerminateNotification ); | |
487 | gIOServiceKey = OSSymbol::withCStringNoCopy( kIOServiceClass); | |
488 | ||
cb323159 | 489 | |
0a7de745 A |
490 | gIOConsoleLockedKey = OSSymbol::withCStringNoCopy( kIOConsoleLockedKey); |
491 | gIOConsoleUsersKey = OSSymbol::withCStringNoCopy( kIOConsoleUsersKey); | |
492 | gIOConsoleSessionUIDKey = OSSymbol::withCStringNoCopy( kIOConsoleSessionUIDKey); | |
493 | gIOConsoleSessionAuditIDKey = OSSymbol::withCStringNoCopy( kIOConsoleSessionAuditIDKey); | |
494 | ||
495 | gIOConsoleUsersSeedKey = OSSymbol::withCStringNoCopy(kIOConsoleUsersSeedKey); | |
496 | gIOConsoleSessionOnConsoleKey = OSSymbol::withCStringNoCopy(kIOConsoleSessionOnConsoleKey); | |
497 | gIOConsoleSessionLoginDoneKey = OSSymbol::withCStringNoCopy(kIOConsoleSessionLoginDoneKey); | |
498 | gIOConsoleSessionSecureInputPIDKey = OSSymbol::withCStringNoCopy(kIOConsoleSessionSecureInputPIDKey); | |
499 | gIOConsoleSessionScreenLockedTimeKey = OSSymbol::withCStringNoCopy(kIOConsoleSessionScreenLockedTimeKey); | |
500 | gIOConsoleSessionScreenIsLockedKey = OSSymbol::withCStringNoCopy(kIOConsoleSessionScreenIsLockedKey); | |
501 | ||
502 | gIOConsoleUsersSeedValue = OSData::withBytesNoCopy(&gIOConsoleUsersSeed, sizeof(gIOConsoleUsersSeed)); | |
503 | ||
cb323159 A |
504 | gIOServiceDEXTEntitlementsKey = OSSymbol::withCStringNoCopy( kIOServiceDEXTEntitlementsKey ); |
505 | gIODriverKitEntitlementKey = OSSymbol::withCStringNoCopy( kIODriverKitEntitlementKey ); | |
506 | gIODriverKitUserClientEntitlementsKey = OSSymbol::withCStringNoCopy( kIODriverKitUserClientEntitlementsKey ); | |
eb6b6ca3 | 507 | gIODriverKitUserClientEntitlementAllowAnyKey = OSSymbol::withCStringNoCopy( kIODriverKitUserClientEntitlementAllowAnyKey ); |
cb323159 | 508 | gIOMatchDeferKey = OSSymbol::withCStringNoCopy( kIOMatchDeferKey ); |
f427ee49 | 509 | gIOAllCPUInitializedKey = OSSymbol::withCStringNoCopy( kIOAllCPUInitializedKey ); |
cb323159 | 510 | |
0a7de745 | 511 | gIOPlatformFunctionHandlerSet = OSSymbol::withCStringNoCopy(kIOPlatformFunctionHandlerSet); |
0a7de745 | 512 | sCPULatencyFunctionName[kCpuDelayBusStall] = OSSymbol::withCStringNoCopy(kIOPlatformFunctionHandlerMaxBusDelay); |
f427ee49 | 513 | #if defined(__x86_64__) |
0a7de745 | 514 | sCPULatencyFunctionName[kCpuDelayInterrupt] = OSSymbol::withCStringNoCopy(kIOPlatformFunctionHandlerMaxInterruptDelay); |
f427ee49 | 515 | #endif /* defined(__x86_64__) */ |
0a7de745 A |
516 | uint32_t idx; |
517 | for (idx = 0; idx < kCpuNumDelayTypes; idx++) { | |
f427ee49 | 518 | sCPULatencySet[idx] = OSNumber::withNumber(UINT_MAX, 32); |
0a7de745 A |
519 | sCPULatencyHolder[idx] = OSNumber::withNumber(0ULL, 64); |
520 | assert(sCPULatencySet[idx] && sCPULatencyHolder[idx]); | |
521 | } | |
f427ee49 A |
522 | |
523 | #if defined(__x86_64__) | |
0a7de745 | 524 | gIOCreateEFIDevicePathSymbol = OSSymbol::withCString("CreateEFIDevicePath"); |
f427ee49 A |
525 | #endif /* defined(__x86_64__) */ |
526 | ||
0a7de745 | 527 | gNotificationLock = IORecursiveLockAlloc(); |
1c79356b | 528 | |
0a7de745 | 529 | gAKSGetKey = OSSymbol::withCStringNoCopy(AKS_PLATFORM_FUNCTION_GETKEY); |
3e170ce0 | 530 | |
0a7de745 A |
531 | assert( gIOServicePlane && gIODeviceMemoryKey |
532 | && gIOInterruptControllersKey && gIOInterruptSpecifiersKey | |
533 | && gIOResourcesKey && gNotifications && gNotificationLock | |
534 | && gIOProviderClassKey && gIONameMatchKey && gIONameMatchedKey | |
535 | && gIOMatchCategoryKey && gIODefaultMatchCategoryKey | |
536 | && gIOPublishNotification && gIOMatchedNotification | |
537 | && gIOTerminatedNotification && gIOServiceKey | |
538 | && gIOConsoleUsersKey && gIOConsoleSessionUIDKey | |
539 | && gIOConsoleSessionOnConsoleKey && gIOConsoleSessionSecureInputPIDKey | |
540 | && gIOConsoleUsersSeedKey && gIOConsoleUsersSeedValue); | |
1c79356b | 541 | |
0a7de745 A |
542 | gJobsLock = IOLockAlloc(); |
543 | gJobs = OSOrderedSet::withCapacity( 10 ); | |
1c79356b | 544 | |
0a7de745 | 545 | gIOServiceBusyLock = IOLockAlloc(); |
1c79356b | 546 | |
0a7de745 | 547 | gIOConsoleUsersLock = IOLockAlloc(); |
6d2010ae | 548 | |
0a7de745 | 549 | err = semaphore_create(kernel_task, &gJobsSemaphore, SYNC_POLICY_FIFO, 0); |
1c79356b | 550 | |
0a7de745 | 551 | gIOConsoleLockCallout = thread_call_allocate(&IOService::consoleLockTimer, NULL); |
6d2010ae | 552 | |
0a7de745 | 553 | IORegistryEntry::getRegistryRoot()->setProperty(gIOConsoleLockedKey, kOSBooleanTrue); |
ebb1b9f4 | 554 | |
0a7de745 A |
555 | assert( gIOServiceBusyLock && gJobs && gJobsLock && gIOConsoleUsersLock |
556 | && gIOConsoleLockCallout && (err == KERN_SUCCESS)); | |
1c79356b | 557 | |
0a7de745 | 558 | gIOResources = IOResources::resources(); |
cb323159 A |
559 | gIOUserResources = IOUserResources::resources(); |
560 | assert( gIOResources && gIOUserResources ); | |
1c79356b | 561 | |
0a7de745 A |
562 | gIOServiceNullNotifier = OSTypeAlloc(_IOServiceNullNotifier); |
563 | assert(gIOServiceNullNotifier); | |
39037602 | 564 | |
0a7de745 A |
565 | gArbitrationLockQueueLock = IOLockAlloc(); |
566 | queue_init(&gArbitrationLockQueueActive); | |
567 | queue_init(&gArbitrationLockQueueWaiting); | |
568 | queue_init(&gArbitrationLockQueueFree); | |
1c79356b | 569 | |
0a7de745 | 570 | assert( gArbitrationLockQueueLock ); |
1c79356b | 571 | |
0a7de745 A |
572 | gIOTerminatePhase2List = OSArray::withCapacity( 2 ); |
573 | gIOStopList = OSArray::withCapacity( 16 ); | |
574 | gIOStopProviderList = OSArray::withCapacity( 16 ); | |
575 | gIOFinalizeList = OSArray::withCapacity( 16 ); | |
cb323159 A |
576 | #if !NO_KEXTD |
577 | gIOMatchDeferList = OSArray::withCapacity( 16 ); | |
578 | #endif | |
0a7de745 | 579 | assert( gIOTerminatePhase2List && gIOStopList && gIOStopProviderList && gIOFinalizeList ); |
5c9f4661 | 580 | |
0a7de745 A |
581 | // worker thread that is responsible for terminating / cleaning up threads |
582 | kernel_thread_start(&terminateThread, NULL, &gIOTerminateWorkerThread); | |
583 | assert(gIOTerminateWorkerThread); | |
584 | thread_set_thread_name(gIOTerminateWorkerThread, "IOServiceTerminateThread"); | |
1c79356b A |
585 | } |
586 | ||
587 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ | |
588 | ||
f427ee49 | 589 | #if defined(__x86_64__) |
3e170ce0 | 590 | extern "C" { |
39037602 | 591 | const char *getCpuDelayBusStallHolderName(void); |
0a7de745 A |
592 | const char * |
593 | getCpuDelayBusStallHolderName(void) | |
594 | { | |
595 | return sCPULatencyHolderName[kCpuDelayBusStall]; | |
3e170ce0 A |
596 | } |
597 | ||
d26ffc64 | 598 | const char *getCpuInterruptDelayHolderName(void); |
0a7de745 A |
599 | const char * |
600 | getCpuInterruptDelayHolderName(void) | |
601 | { | |
602 | return sCPULatencyHolderName[kCpuDelayInterrupt]; | |
d26ffc64 | 603 | } |
3e170ce0 | 604 | } |
f427ee49 A |
605 | #endif /* defined(__x86_64__) */ |
606 | ||
607 | ||
3e170ce0 | 608 | |
1c79356b | 609 | #if IOMATCHDEBUG |
0a7de745 A |
610 | static UInt64 |
611 | getDebugFlags( OSDictionary * props ) | |
612 | { | |
613 | OSNumber * debugProp; | |
614 | UInt64 debugFlags; | |
615 | ||
616 | debugProp = OSDynamicCast( OSNumber, | |
617 | props->getObject( gIOKitDebugKey )); | |
618 | if (debugProp) { | |
619 | debugFlags = debugProp->unsigned64BitValue(); | |
620 | } else { | |
621 | debugFlags = gIOKitDebug; | |
622 | } | |
1c79356b | 623 | |
0a7de745 | 624 | return debugFlags; |
1c79356b | 625 | } |
5ba3f43e | 626 | |
0a7de745 A |
627 | static UInt64 |
628 | getDebugFlags( IOService * inst ) | |
5ba3f43e | 629 | { |
0a7de745 A |
630 | OSObject * prop; |
631 | OSNumber * debugProp; | |
632 | UInt64 debugFlags; | |
5ba3f43e | 633 | |
0a7de745 A |
634 | prop = inst->copyProperty(gIOKitDebugKey); |
635 | debugProp = OSDynamicCast(OSNumber, prop); | |
636 | if (debugProp) { | |
637 | debugFlags = debugProp->unsigned64BitValue(); | |
638 | } else { | |
639 | debugFlags = gIOKitDebug; | |
640 | } | |
5ba3f43e | 641 | |
0a7de745 | 642 | OSSafeReleaseNULL(prop); |
5ba3f43e | 643 | |
0a7de745 | 644 | return debugFlags; |
5ba3f43e | 645 | } |
1c79356b A |
646 | #endif |
647 | ||
648 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ | |
649 | ||
650 | // Probe a matched service and return an instance to be started. | |
651 | // The default score is from the property table, & may be altered | |
652 | // during probe to change the start order. | |
653 | ||
0a7de745 A |
654 | IOService * |
655 | IOService::probe( IOService * provider, | |
656 | SInt32 * score ) | |
1c79356b | 657 | { |
0a7de745 | 658 | return this; |
1c79356b A |
659 | } |
660 | ||
0a7de745 A |
661 | bool |
662 | IOService::start( IOService * provider ) | |
1c79356b | 663 | { |
0a7de745 | 664 | return true; |
1c79356b A |
665 | } |
666 | ||
0a7de745 A |
667 | void |
668 | IOService::stop( IOService * provider ) | |
1c79356b | 669 | { |
cb323159 A |
670 | if (reserved->uvars && reserved->uvars->started && reserved->uvars->userServer) { |
671 | reserved->uvars->userServer->serviceStop(this, provider); | |
672 | } | |
1c79356b A |
673 | } |
674 | ||
0a7de745 A |
675 | bool |
676 | IOService::init( OSDictionary * dictionary ) | |
fe8ab488 | 677 | { |
0a7de745 | 678 | bool ret; |
fe8ab488 | 679 | |
0a7de745 A |
680 | ret = super::init(dictionary); |
681 | if (!ret) { | |
682 | return false; | |
683 | } | |
684 | if (reserved) { | |
685 | return true; | |
686 | } | |
fe8ab488 | 687 | |
0a7de745 A |
688 | reserved = IONew(ExpansionData, 1); |
689 | if (!reserved) { | |
690 | return false; | |
691 | } | |
692 | bzero(reserved, sizeof(*reserved)); | |
693 | ||
694 | /* | |
695 | * TODO: Improve on this. Previous efforts to more lazily allocate this | |
696 | * lock based on the presence of specifiers ran into issues as some | |
697 | * platforms set up the specifiers after IOService initialization. | |
698 | * | |
699 | * We may be able to get away with a global lock, as this should only be | |
700 | * contended by IOReporting clients and driver start/stop (unless a | |
701 | * driver wants to remove/add handlers in the course of normal operation, | |
702 | * which should be unlikely). | |
703 | */ | |
704 | reserved->interruptStatisticsLock = IOLockAlloc(); | |
705 | if (!reserved->interruptStatisticsLock) { | |
706 | return false; | |
707 | } | |
fe8ab488 | 708 | |
0a7de745 | 709 | return true; |
fe8ab488 A |
710 | } |
711 | ||
0a7de745 A |
712 | bool |
713 | IOService::init( IORegistryEntry * from, | |
714 | const IORegistryPlane * inPlane ) | |
fe8ab488 | 715 | { |
0a7de745 | 716 | bool ret; |
fe8ab488 | 717 | |
0a7de745 A |
718 | ret = super::init(from, inPlane); |
719 | if (!ret) { | |
720 | return false; | |
721 | } | |
722 | if (reserved) { | |
723 | return true; | |
724 | } | |
fe8ab488 | 725 | |
0a7de745 A |
726 | reserved = IONew(ExpansionData, 1); |
727 | if (!reserved) { | |
728 | return false; | |
729 | } | |
730 | bzero(reserved, sizeof(*reserved)); | |
731 | ||
732 | /* | |
733 | * TODO: Improve on this. Previous efforts to more lazily allocate this | |
734 | * lock based on the presence of specifiers ran into issues as some | |
735 | * platforms set up the specifiers after IOService initialization. | |
736 | * | |
737 | * We may be able to get away with a global lock, as this should only be | |
738 | * contended by IOReporting clients and driver start/stop (unless a | |
739 | * driver wants to remove/add handlers in the course of normal operation, | |
740 | * which should be unlikely). | |
741 | */ | |
742 | reserved->interruptStatisticsLock = IOLockAlloc(); | |
743 | if (!reserved->interruptStatisticsLock) { | |
744 | return false; | |
745 | } | |
fe8ab488 | 746 | |
0a7de745 | 747 | return true; |
fe8ab488 A |
748 | } |
749 | ||
0a7de745 A |
750 | void |
751 | IOService::free( void ) | |
0b4e3aa0 | 752 | { |
0a7de745 A |
753 | int i = 0; |
754 | requireMaxBusStall(0); | |
f427ee49 | 755 | #if defined(__x86_64__) |
0a7de745 | 756 | requireMaxInterruptDelay(0); |
f427ee49 | 757 | #endif /* defined(__x86_64__) */ |
0a7de745 A |
758 | if (getPropertyTable()) { |
759 | unregisterAllInterest(); | |
760 | } | |
761 | PMfree(); | |
fe8ab488 | 762 | |
0a7de745 A |
763 | if (reserved) { |
764 | if (reserved->interruptStatisticsArray) { | |
765 | for (i = 0; i < reserved->interruptStatisticsArrayCount; i++) { | |
766 | if (reserved->interruptStatisticsArray[i].reporter) { | |
767 | reserved->interruptStatisticsArray[i].reporter->release(); | |
768 | } | |
769 | } | |
fe8ab488 | 770 | |
0a7de745 A |
771 | IODelete(reserved->interruptStatisticsArray, IOInterruptAccountingReporter, reserved->interruptStatisticsArrayCount); |
772 | } | |
fe8ab488 | 773 | |
0a7de745 A |
774 | if (reserved->interruptStatisticsLock) { |
775 | IOLockFree(reserved->interruptStatisticsLock); | |
776 | } | |
cb323159 A |
777 | if (reserved->uvars && reserved->uvars->userServer) { |
778 | reserved->uvars->userServer->serviceFree(this); | |
779 | } | |
0a7de745 A |
780 | IODelete(reserved, ExpansionData, 1); |
781 | } | |
fe8ab488 | 782 | |
0a7de745 A |
783 | if (_numInterruptSources && _interruptSources) { |
784 | for (i = 0; i < _numInterruptSources; i++) { | |
785 | void * block = _interruptSourcesPrivate(this)[i].vectorBlock; | |
786 | if (block) { | |
787 | Block_release(block); | |
788 | } | |
789 | } | |
790 | IOFree(_interruptSources, | |
791 | _numInterruptSources * sizeofAllIOInterruptSource); | |
cb323159 | 792 | _interruptSources = NULL; |
d9a64523 | 793 | } |
3e170ce0 | 794 | |
0a7de745 | 795 | super::free(); |
0b4e3aa0 A |
796 | } |
797 | ||
1c79356b A |
798 | /* |
799 | * Attach in service plane | |
800 | */ | |
0a7de745 A |
801 | bool |
802 | IOService::attach( IOService * provider ) | |
803 | { | |
804 | bool ok; | |
805 | uint32_t count; | |
806 | AbsoluteTime deadline; | |
807 | int waitResult = THREAD_AWAKENED; | |
808 | bool wait, computeDeadline = true; | |
809 | ||
810 | if (provider) { | |
811 | if (gIOKitDebug & kIOLogAttach) { | |
812 | LOG( "%s::attach(%s)\n", getName(), | |
813 | provider->getName()); | |
39037602 | 814 | } |
1c79356b | 815 | |
0a7de745 A |
816 | ok = false; |
817 | do{ | |
818 | wait = false; | |
819 | provider->lockForArbitration(); | |
820 | if (provider->__state[0] & kIOServiceInactiveState) { | |
821 | ok = false; | |
822 | } else { | |
823 | count = provider->getChildCount(gIOServicePlane); | |
824 | wait = (count > (kIOServiceBusyMax - 4)); | |
825 | if (!wait) { | |
826 | ok = attachToParent(provider, gIOServicePlane); | |
827 | } else { | |
828 | IOLog("stalling for detach from %s\n", provider->getName()); | |
829 | IOLockLock( gIOServiceBusyLock ); | |
830 | provider->__state[1] |= kIOServiceWaitDetachState; | |
831 | } | |
832 | } | |
833 | provider->unlockForArbitration(); | |
834 | if (wait) { | |
835 | if (computeDeadline) { | |
836 | clock_interval_to_deadline(15, kSecondScale, &deadline); | |
837 | computeDeadline = false; | |
838 | } | |
839 | assert_wait_deadline((event_t)&provider->__provider, THREAD_UNINT, deadline); | |
840 | IOLockUnlock( gIOServiceBusyLock ); | |
841 | waitResult = thread_block(THREAD_CONTINUE_NULL); | |
842 | wait = (waitResult != THREAD_TIMED_OUT); | |
843 | } | |
844 | }while (wait); | |
845 | } else { | |
846 | gIOServiceRoot = this; | |
847 | ok = attachToParent( getRegistryRoot(), gIOServicePlane); | |
848 | } | |
1c79356b | 849 | |
0a7de745 A |
850 | if (ok && !__provider) { |
851 | (void) getProvider(); | |
852 | } | |
99c3a104 | 853 | |
0a7de745 | 854 | return ok; |
1c79356b A |
855 | } |
856 | ||
0a7de745 A |
857 | IOService * |
858 | IOService::getServiceRoot( void ) | |
1c79356b | 859 | { |
0a7de745 | 860 | return gIOServiceRoot; |
1c79356b A |
861 | } |
862 | ||
0a7de745 A |
863 | void |
864 | IOService::detach( IOService * provider ) | |
1c79356b | 865 | { |
cb323159 | 866 | IOService * newProvider = NULL; |
0a7de745 A |
867 | SInt32 busy; |
868 | bool adjParent; | |
0b4e3aa0 | 869 | |
0a7de745 A |
870 | if (gIOKitDebug & kIOLogAttach) { |
871 | LOG("%s::detach(%s)\n", getName(), provider->getName()); | |
872 | } | |
1c79356b | 873 | |
cb323159 A |
874 | #if !NO_KEXTD |
875 | IOLockLock(gJobsLock); | |
876 | if (gIOMatchDeferList) { | |
877 | auto idx = gIOMatchDeferList->getNextIndexOfObject(this, 0); | |
878 | if (-1U != idx) { | |
879 | gIOMatchDeferList->removeObject(idx); | |
880 | } | |
881 | } | |
882 | if (IOServicePH::fMatchingDelayed) { | |
883 | auto idx = IOServicePH::fMatchingDelayed->getNextIndexOfObject(this, 0); | |
884 | if (-1U != idx) { | |
885 | IOServicePH::fMatchingDelayed->removeObject(idx); | |
886 | } | |
887 | } | |
888 | IOLockUnlock(gJobsLock); | |
889 | #endif /* NO_KEXTD */ | |
890 | ||
0a7de745 | 891 | lockForArbitration(); |
3e170ce0 | 892 | |
0a7de745 A |
893 | uint64_t regID1 = provider->getRegistryEntryID(); |
894 | uint64_t regID2 = getRegistryEntryID(); | |
895 | IOServiceTrace( | |
896 | IOSERVICE_DETACH, | |
897 | (uintptr_t) regID1, | |
898 | (uintptr_t) (regID1 >> 32), | |
899 | (uintptr_t) regID2, | |
900 | (uintptr_t) (regID2 >> 32)); | |
1c79356b | 901 | |
0a7de745 A |
902 | adjParent = ((busy = (__state[1] & kIOServiceBusyStateMask)) |
903 | && (provider == getProvider())); | |
1c79356b | 904 | |
0a7de745 | 905 | detachFromParent( provider, gIOServicePlane ); |
0b4e3aa0 | 906 | |
0a7de745 A |
907 | if (busy) { |
908 | newProvider = getProvider(); | |
cb323159 | 909 | if (busy && (__state[1] & kIOServiceTermPhase3State) && (NULL == newProvider)) { |
0a7de745 A |
910 | _adjustBusy( -busy ); |
911 | } | |
912 | } | |
316670eb | 913 | |
0a7de745 A |
914 | if (kIOServiceInactiveState & __state[0]) { |
915 | getMetaClass()->removeInstance(this); | |
916 | IORemoveServicePlatformActions(this); | |
917 | } | |
0b4e3aa0 | 918 | |
0a7de745 | 919 | unlockForArbitration(); |
0b4e3aa0 | 920 | |
0a7de745 A |
921 | if (newProvider && adjParent) { |
922 | newProvider->lockForArbitration(); | |
923 | newProvider->_adjustBusy(1); | |
924 | newProvider->unlockForArbitration(); | |
fe8ab488 | 925 | } |
39037602 | 926 | |
0a7de745 A |
927 | // check for last client detach from a terminated service |
928 | if (provider->lockForArbitration( true )) { | |
929 | if (kIOServiceStartState & __state[1]) { | |
930 | provider->scheduleTerminatePhase2(); | |
931 | } | |
932 | if (adjParent) { | |
933 | provider->_adjustBusy( -1 ); | |
934 | } | |
935 | if ((provider->__state[1] & kIOServiceTermPhase3State) | |
cb323159 | 936 | && (NULL == provider->getClient())) { |
0a7de745 A |
937 | provider->scheduleFinalize(false); |
938 | } | |
39037602 | 939 | |
0a7de745 A |
940 | IOLockLock( gIOServiceBusyLock ); |
941 | if (kIOServiceWaitDetachState & provider->__state[1]) { | |
942 | provider->__state[1] &= ~kIOServiceWaitDetachState; | |
943 | thread_wakeup(&provider->__provider); | |
944 | } | |
945 | IOLockUnlock( gIOServiceBusyLock ); | |
946 | ||
947 | provider->unlockForArbitration(); | |
948 | } | |
1c79356b A |
949 | } |
950 | ||
951 | /* | |
952 | * Register instance - publish it for matching | |
953 | */ | |
954 | ||
0a7de745 A |
955 | void |
956 | IOService::registerService( IOOptionBits options ) | |
1c79356b | 957 | { |
0a7de745 A |
958 | char * pathBuf; |
959 | const char * path; | |
960 | char * skip; | |
961 | int len; | |
962 | enum { kMaxPathLen = 256 }; | |
963 | enum { kMaxChars = 63 }; | |
1c79356b | 964 | |
0a7de745 A |
965 | IORegistryEntry * parent = this; |
966 | IORegistryEntry * root = getRegistryRoot(); | |
967 | while (parent && (parent != root)) { | |
968 | parent = parent->getParentEntry( gIOServicePlane); | |
969 | } | |
1c79356b | 970 | |
0a7de745 A |
971 | if (parent != root) { |
972 | IOLog("%s: not registry member at registerService()\n", getName()); | |
973 | return; | |
974 | } | |
3e170ce0 | 975 | |
0a7de745 A |
976 | // Allow the Platform Expert to adjust this node. |
977 | if (gIOPlatform && (!gIOPlatform->platformAdjustService(this))) { | |
978 | return; | |
979 | } | |
1c79356b | 980 | |
0a7de745 | 981 | IOInstallServicePlatformActions(this); |
f427ee49 | 982 | IOInstallServiceSleepPlatformActions(this); |
1c79356b | 983 | |
0a7de745 A |
984 | if ((this != gIOResources) |
985 | && (kIOLogRegister & gIOKitDebug)) { | |
986 | pathBuf = (char *) IOMalloc( kMaxPathLen ); | |
1c79356b | 987 | |
0a7de745 | 988 | IOLog( "Registering: " ); |
1c79356b | 989 | |
0a7de745 A |
990 | len = kMaxPathLen; |
991 | if (pathBuf && getPath( pathBuf, &len, gIOServicePlane)) { | |
992 | path = pathBuf; | |
993 | if (len > kMaxChars) { | |
994 | IOLog(".."); | |
995 | len -= kMaxChars; | |
996 | path += len; | |
997 | if ((skip = strchr( path, '/'))) { | |
998 | path = skip; | |
999 | } | |
1000 | } | |
1001 | } else { | |
1002 | path = getName(); | |
1003 | } | |
1c79356b | 1004 | |
0a7de745 | 1005 | IOLog( "%s\n", path ); |
1c79356b | 1006 | |
0a7de745 A |
1007 | if (pathBuf) { |
1008 | IOFree( pathBuf, kMaxPathLen ); | |
1009 | } | |
1010 | } | |
1c79356b | 1011 | |
0a7de745 | 1012 | startMatching( options ); |
1c79356b A |
1013 | } |
1014 | ||
0a7de745 A |
1015 | void |
1016 | IOService::startMatching( IOOptionBits options ) | |
1c79356b | 1017 | { |
0a7de745 A |
1018 | IOService * provider; |
1019 | UInt32 prevBusy = 0; | |
1020 | bool needConfig; | |
1021 | bool needWake = false; | |
1022 | bool ok; | |
1023 | bool sync; | |
1024 | bool waitAgain; | |
1c79356b | 1025 | |
0a7de745 | 1026 | lockForArbitration(); |
1c79356b | 1027 | |
0a7de745 A |
1028 | sync = (options & kIOServiceSynchronous) |
1029 | || ((provider = getProvider()) | |
1030 | && (provider->__state[1] & kIOServiceSynchronousState)); | |
1c79356b | 1031 | |
0a7de745 | 1032 | if (options & kIOServiceAsynchronous) { |
0c530ab8 | 1033 | sync = false; |
0a7de745 | 1034 | } |
0b4e3aa0 | 1035 | |
0a7de745 A |
1036 | needConfig = (0 == (__state[1] & (kIOServiceNeedConfigState | kIOServiceConfigRunning))) |
1037 | && (0 == (__state[0] & kIOServiceInactiveState)); | |
0b4e3aa0 | 1038 | |
0a7de745 | 1039 | __state[1] |= kIOServiceNeedConfigState; |
0b4e3aa0 | 1040 | |
1c79356b A |
1041 | // __state[0] &= ~kIOServiceInactiveState; |
1042 | ||
1043 | // if( sync) LOG("OSKernelStackRemaining = %08x @ %s\n", | |
1044 | // OSKernelStackRemaining(), getName()); | |
1045 | ||
0a7de745 A |
1046 | if (needConfig) { |
1047 | needWake = (0 != (kIOServiceSyncPubState & __state[1])); | |
1048 | } | |
0b4e3aa0 | 1049 | |
0a7de745 A |
1050 | if (sync) { |
1051 | __state[1] |= kIOServiceSynchronousState; | |
1052 | } else { | |
1053 | __state[1] &= ~kIOServiceSynchronousState; | |
1054 | } | |
0b4e3aa0 | 1055 | |
0a7de745 A |
1056 | if (needConfig) { |
1057 | prevBusy = _adjustBusy( 1 ); | |
1058 | } | |
0b4e3aa0 | 1059 | |
0a7de745 | 1060 | unlockForArbitration(); |
0b4e3aa0 | 1061 | |
0a7de745 A |
1062 | if (needConfig) { |
1063 | if (needWake) { | |
1064 | IOLockLock( gIOServiceBusyLock ); | |
1065 | thread_wakeup((event_t) this /*&__state[1]*/ ); | |
1066 | IOLockUnlock( gIOServiceBusyLock ); | |
1067 | } else if (!sync || (kIOServiceAsynchronous & options)) { | |
cb323159 | 1068 | ok = (NULL != _IOServiceJob::startJob( this, kMatchNubJob, options )); |
0a7de745 A |
1069 | } else { |
1070 | do { | |
1071 | if ((__state[1] & kIOServiceNeedConfigState)) { | |
1072 | doServiceMatch( options ); | |
1073 | } | |
0b4e3aa0 | 1074 | |
0a7de745 A |
1075 | lockForArbitration(); |
1076 | IOLockLock( gIOServiceBusyLock ); | |
316670eb | 1077 | |
0a7de745 A |
1078 | waitAgain = ((prevBusy < (__state[1] & kIOServiceBusyStateMask)) |
1079 | && (0 == (__state[0] & kIOServiceInactiveState))); | |
0b4e3aa0 | 1080 | |
0a7de745 A |
1081 | if (waitAgain) { |
1082 | __state[1] |= kIOServiceSyncPubState | kIOServiceBusyWaiterState; | |
1083 | } else { | |
1084 | __state[1] &= ~kIOServiceSyncPubState; | |
1085 | } | |
0b4e3aa0 | 1086 | |
0a7de745 | 1087 | unlockForArbitration(); |
0b4e3aa0 | 1088 | |
0a7de745 A |
1089 | if (waitAgain) { |
1090 | assert_wait((event_t) this /*&__state[1]*/, THREAD_UNINT); | |
1091 | } | |
0b4e3aa0 | 1092 | |
0a7de745 A |
1093 | IOLockUnlock( gIOServiceBusyLock ); |
1094 | if (waitAgain) { | |
1095 | thread_block(THREAD_CONTINUE_NULL); | |
1096 | } | |
1097 | } while (waitAgain); | |
1098 | } | |
1099 | } | |
1c79356b A |
1100 | } |
1101 | ||
cb323159 A |
1102 | |
1103 | void | |
1104 | IOService::startDeferredMatches(void) | |
1105 | { | |
1106 | #if !NO_KEXTD | |
1107 | OSArray * array; | |
1108 | ||
1109 | IOLockLock(gJobsLock); | |
1110 | array = gIOMatchDeferList; | |
1111 | gIOMatchDeferList = NULL; | |
1112 | IOLockUnlock(gJobsLock); | |
1113 | ||
1114 | if (array) { | |
1115 | IOLog("deferred rematching count %d\n", array->getCount()); | |
1116 | array->iterateObjects(^bool (OSObject * obj) | |
1117 | { | |
1118 | ((IOService *)obj)->startMatching(kIOServiceAsynchronous); | |
1119 | return false; | |
1120 | }); | |
1121 | array->release(); | |
1122 | } | |
1123 | #endif /* !NO_KEXTD */ | |
1124 | } | |
1125 | ||
1126 | void | |
f427ee49 | 1127 | IOService::iokitDaemonLaunched(void) |
cb323159 A |
1128 | { |
1129 | #if !NO_KEXTD | |
1130 | IOServiceTrace(IOSERVICE_KEXTD_READY, 0, 0, 0, 0); | |
1131 | startDeferredMatches(); | |
1132 | getServiceRoot()->adjustBusy(-1); | |
1133 | IOService::publishUserResource(gIOResourceIOKitKey); | |
1134 | #endif /* !NO_KEXTD */ | |
1135 | } | |
1136 | ||
0a7de745 A |
1137 | IOReturn |
1138 | IOService::catalogNewDrivers( OSOrderedSet * newTables ) | |
1c79356b | 1139 | { |
0a7de745 A |
1140 | OSDictionary * table; |
1141 | OSSet * set; | |
cb323159 | 1142 | OSSet * allSet = NULL; |
0a7de745 | 1143 | IOService * service; |
1c79356b | 1144 | #if IOMATCHDEBUG |
0a7de745 | 1145 | SInt32 count = 0; |
1c79356b A |
1146 | #endif |
1147 | ||
0a7de745 | 1148 | newTables->retain(); |
91447636 | 1149 | |
0a7de745 A |
1150 | while ((table = (OSDictionary *) newTables->getFirstObject())) { |
1151 | LOCKWRITENOTIFY(); | |
1152 | set = (OSSet *) copyExistingServices( table, | |
1153 | kIOServiceRegisteredState, | |
1154 | kIOServiceExistingSet); | |
1155 | UNLOCKNOTIFY(); | |
1156 | if (set) { | |
1c79356b | 1157 | #if IOMATCHDEBUG |
0a7de745 | 1158 | count += set->getCount(); |
1c79356b | 1159 | #endif |
0a7de745 A |
1160 | if (allSet) { |
1161 | allSet->merge((const OSSet *) set); | |
1162 | set->release(); | |
1163 | } else { | |
1164 | allSet = set; | |
1165 | } | |
1166 | } | |
91447636 | 1167 | |
1c79356b | 1168 | #if IOMATCHDEBUG |
0a7de745 A |
1169 | if (getDebugFlags( table ) & kIOLogMatch) { |
1170 | LOG("Matching service count = %ld\n", (long)count); | |
1171 | } | |
1c79356b | 1172 | #endif |
0a7de745 A |
1173 | newTables->removeObject(table); |
1174 | } | |
1c79356b | 1175 | |
0a7de745 A |
1176 | if (allSet) { |
1177 | while ((service = (IOService *) allSet->getAnyObject())) { | |
1178 | service->startMatching(kIOServiceAsynchronous); | |
1179 | allSet->removeObject(service); | |
1180 | } | |
1181 | allSet->release(); | |
1182 | } | |
91447636 | 1183 | |
0a7de745 | 1184 | newTables->release(); |
1c79356b | 1185 | |
0a7de745 | 1186 | return kIOReturnSuccess; |
1c79356b A |
1187 | } |
1188 | ||
0a7de745 A |
1189 | _IOServiceJob * |
1190 | _IOServiceJob::startJob( IOService * nub, int type, | |
1191 | IOOptionBits options ) | |
1c79356b | 1192 | { |
0a7de745 | 1193 | _IOServiceJob * job; |
1c79356b | 1194 | |
0a7de745 A |
1195 | job = new _IOServiceJob; |
1196 | if (job && !job->init()) { | |
1197 | job->release(); | |
cb323159 | 1198 | job = NULL; |
0a7de745 | 1199 | } |
1c79356b | 1200 | |
0a7de745 A |
1201 | if (job) { |
1202 | job->type = type; | |
1203 | job->nub = nub; | |
1204 | job->options = options; | |
1205 | nub->retain(); // thread will release() | |
1206 | pingConfig( job ); | |
1207 | } | |
1c79356b | 1208 | |
0a7de745 | 1209 | return job; |
1c79356b A |
1210 | } |
1211 | ||
1212 | /* | |
1213 | * Called on a registered service to see if it matches | |
1214 | * a property table. | |
1215 | */ | |
1216 | ||
0a7de745 A |
1217 | bool |
1218 | IOService::matchPropertyTable( OSDictionary * table, SInt32 * score ) | |
1c79356b | 1219 | { |
0a7de745 | 1220 | return matchPropertyTable(table); |
1c79356b A |
1221 | } |
1222 | ||
0a7de745 A |
1223 | bool |
1224 | IOService::matchPropertyTable( OSDictionary * table ) | |
1c79356b | 1225 | { |
0a7de745 | 1226 | return true; |
1c79356b A |
1227 | } |
1228 | ||
1229 | /* | |
1230 | * Called on a matched service to allocate resources | |
1231 | * before first driver is attached. | |
1232 | */ | |
1233 | ||
0a7de745 A |
1234 | IOReturn |
1235 | IOService::getResources( void ) | |
1c79356b | 1236 | { |
0a7de745 | 1237 | return kIOReturnSuccess; |
1c79356b A |
1238 | } |
1239 | ||
1240 | /* | |
1241 | * Client/provider accessors | |
1242 | */ | |
1243 | ||
0a7de745 A |
1244 | IOService * |
1245 | IOService::getProvider( void ) const | |
1c79356b | 1246 | { |
0a7de745 A |
1247 | IOService * self = (IOService *) this; |
1248 | IOService * parent; | |
1249 | SInt32 generation; | |
1c79356b | 1250 | |
0a7de745 A |
1251 | generation = getRegistryEntryGenerationCount(); |
1252 | if (__providerGeneration == generation) { | |
1253 | return __provider; | |
1254 | } | |
1c79356b | 1255 | |
0a7de745 A |
1256 | parent = (IOService *) getParentEntry( gIOServicePlane); |
1257 | if (parent == IORegistryEntry::getRegistryRoot()) { | |
1258 | /* root is not an IOService */ | |
cb323159 | 1259 | parent = NULL; |
0a7de745 | 1260 | } |
1c79356b | 1261 | |
0a7de745 A |
1262 | self->__provider = parent; |
1263 | OSMemoryBarrier(); | |
1264 | // save the count from before call to getParentEntry() | |
1265 | self->__providerGeneration = generation; | |
1c79356b | 1266 | |
0a7de745 | 1267 | return parent; |
1c79356b A |
1268 | } |
1269 | ||
0a7de745 A |
1270 | IOWorkLoop * |
1271 | IOService::getWorkLoop() const | |
1272 | { | |
1273 | IOService *provider = getProvider(); | |
1c79356b | 1274 | |
0a7de745 A |
1275 | if (provider) { |
1276 | return provider->getWorkLoop(); | |
1277 | } else { | |
cb323159 | 1278 | return NULL; |
0a7de745 | 1279 | } |
1c79356b A |
1280 | } |
1281 | ||
0a7de745 A |
1282 | OSIterator * |
1283 | IOService::getProviderIterator( void ) const | |
1c79356b | 1284 | { |
0a7de745 | 1285 | return getParentIterator( gIOServicePlane); |
1c79356b A |
1286 | } |
1287 | ||
0a7de745 A |
1288 | IOService * |
1289 | IOService::getClient( void ) const | |
1c79356b | 1290 | { |
0a7de745 | 1291 | return (IOService *) getChildEntry( gIOServicePlane); |
1c79356b A |
1292 | } |
1293 | ||
0a7de745 A |
1294 | OSIterator * |
1295 | IOService::getClientIterator( void ) const | |
1c79356b | 1296 | { |
0a7de745 | 1297 | return getChildIterator( gIOServicePlane); |
1c79356b A |
1298 | } |
1299 | ||
0a7de745 A |
1300 | OSIterator * |
1301 | _IOOpenServiceIterator::iterator( OSIterator * _iter, | |
1302 | const IOService * client, | |
1303 | const IOService * provider ) | |
1c79356b | 1304 | { |
0a7de745 | 1305 | _IOOpenServiceIterator * inst; |
1c79356b | 1306 | |
0a7de745 | 1307 | if (!_iter) { |
cb323159 | 1308 | return NULL; |
0a7de745 | 1309 | } |
1c79356b | 1310 | |
0a7de745 | 1311 | inst = new _IOOpenServiceIterator; |
1c79356b | 1312 | |
0a7de745 A |
1313 | if (inst && !inst->init()) { |
1314 | inst->release(); | |
cb323159 | 1315 | inst = NULL; |
0a7de745 A |
1316 | } |
1317 | if (inst) { | |
1318 | inst->iter = _iter; | |
1319 | inst->client = client; | |
1320 | inst->provider = provider; | |
1321 | } | |
1c79356b | 1322 | |
0a7de745 | 1323 | return inst; |
1c79356b A |
1324 | } |
1325 | ||
0a7de745 A |
1326 | void |
1327 | _IOOpenServiceIterator::free() | |
1c79356b | 1328 | { |
0a7de745 A |
1329 | iter->release(); |
1330 | if (last) { | |
1331 | last->unlockForArbitration(); | |
1332 | } | |
1333 | OSIterator::free(); | |
1c79356b A |
1334 | } |
1335 | ||
0a7de745 A |
1336 | OSObject * |
1337 | _IOOpenServiceIterator::getNextObject() | |
1c79356b | 1338 | { |
0a7de745 | 1339 | IOService * next; |
1c79356b | 1340 | |
0a7de745 A |
1341 | if (last) { |
1342 | last->unlockForArbitration(); | |
1343 | } | |
1c79356b | 1344 | |
0a7de745 A |
1345 | while ((next = (IOService *) iter->getNextObject())) { |
1346 | next->lockForArbitration(); | |
1347 | if ((client && (next->isOpen( client ))) | |
1348 | || (provider && (provider->isOpen( next )))) { | |
1349 | break; | |
1350 | } | |
1351 | next->unlockForArbitration(); | |
1352 | } | |
1c79356b | 1353 | |
0a7de745 | 1354 | last = next; |
1c79356b | 1355 | |
0a7de745 | 1356 | return next; |
1c79356b A |
1357 | } |
1358 | ||
0a7de745 A |
1359 | bool |
1360 | _IOOpenServiceIterator::isValid() | |
1c79356b | 1361 | { |
0a7de745 | 1362 | return iter->isValid(); |
1c79356b A |
1363 | } |
1364 | ||
0a7de745 A |
1365 | void |
1366 | _IOOpenServiceIterator::reset() | |
1c79356b | 1367 | { |
0a7de745 A |
1368 | if (last) { |
1369 | last->unlockForArbitration(); | |
cb323159 | 1370 | last = NULL; |
0a7de745 A |
1371 | } |
1372 | iter->reset(); | |
1c79356b A |
1373 | } |
1374 | ||
0a7de745 A |
1375 | OSIterator * |
1376 | IOService::getOpenProviderIterator( void ) const | |
1c79356b | 1377 | { |
cb323159 | 1378 | return _IOOpenServiceIterator::iterator( getProviderIterator(), this, NULL ); |
1c79356b A |
1379 | } |
1380 | ||
0a7de745 A |
1381 | OSIterator * |
1382 | IOService::getOpenClientIterator( void ) const | |
1c79356b | 1383 | { |
cb323159 | 1384 | return _IOOpenServiceIterator::iterator( getClientIterator(), NULL, this ); |
1c79356b A |
1385 | } |
1386 | ||
1387 | ||
0a7de745 A |
1388 | IOReturn |
1389 | IOService::callPlatformFunction( const OSSymbol * functionName, | |
1390 | bool waitForFunction, | |
1391 | void *param1, void *param2, | |
1392 | void *param3, void *param4 ) | |
1c79356b | 1393 | { |
0a7de745 A |
1394 | IOReturn result = kIOReturnUnsupported; |
1395 | IOService *provider; | |
593a1d5f | 1396 | |
cb323159 A |
1397 | if (functionName == gIOPlatformQuiesceActionKey || |
1398 | functionName == gIOPlatformActiveActionKey) { | |
1399 | /* | |
1400 | * Services which register for IOPlatformQuiesceAction / IOPlatformActiveAction | |
1401 | * must consume that event themselves, without passing it up to super/IOService. | |
1402 | */ | |
1403 | if (gEnforceQuiesceSafety) { | |
1404 | panic("Class %s passed the quiesce/active action to IOService", | |
1405 | getMetaClass()->getClassName()); | |
1406 | } | |
1407 | } | |
1408 | ||
0a7de745 | 1409 | if (gIOPlatformFunctionHandlerSet == functionName) { |
0a7de745 A |
1410 | const OSSymbol * functionHandlerName = (const OSSymbol *) param1; |
1411 | IOService * target = (IOService *) param2; | |
cb323159 | 1412 | bool enable = (param3 != NULL); |
0a7de745 A |
1413 | |
1414 | if (sCPULatencyFunctionName[kCpuDelayBusStall] == functionHandlerName) { | |
1415 | result = setLatencyHandler(kCpuDelayBusStall, target, enable); | |
f427ee49 A |
1416 | } |
1417 | #if defined(__x86_64__) | |
1418 | else if (sCPULatencyFunctionName[kCpuDelayInterrupt] == param1) { | |
0a7de745 A |
1419 | result = setLatencyHandler(kCpuDelayInterrupt, target, enable); |
1420 | } | |
f427ee49 | 1421 | #endif /* defined(__x86_64__) */ |
0a7de745 A |
1422 | } |
1423 | ||
1424 | if ((kIOReturnUnsupported == result) && (provider = getProvider())) { | |
1425 | result = provider->callPlatformFunction(functionName, waitForFunction, | |
1426 | param1, param2, param3, param4); | |
1427 | } | |
593a1d5f | 1428 | |
0a7de745 | 1429 | return result; |
1c79356b A |
1430 | } |
1431 | ||
0a7de745 A |
1432 | IOReturn |
1433 | IOService::callPlatformFunction( const char * functionName, | |
1434 | bool waitForFunction, | |
1435 | void *param1, void *param2, | |
1436 | void *param3, void *param4 ) | |
1c79356b | 1437 | { |
0a7de745 A |
1438 | IOReturn result = kIOReturnNoMemory; |
1439 | const OSSymbol *functionSymbol = OSSymbol::withCString(functionName); | |
1440 | ||
cb323159 | 1441 | if (functionSymbol != NULL) { |
0a7de745 A |
1442 | result = callPlatformFunction(functionSymbol, waitForFunction, |
1443 | param1, param2, param3, param4); | |
1444 | functionSymbol->release(); | |
1445 | } | |
1446 | ||
1447 | return result; | |
1c79356b A |
1448 | } |
1449 | ||
1450 | ||
1451 | /* | |
0b4e3aa0 | 1452 | * Accessors for global services |
1c79356b A |
1453 | */ |
1454 | ||
0a7de745 A |
1455 | IOPlatformExpert * |
1456 | IOService::getPlatform( void ) | |
1c79356b | 1457 | { |
0a7de745 | 1458 | return gIOPlatform; |
1c79356b A |
1459 | } |
1460 | ||
0a7de745 A |
1461 | class IOPMrootDomain * |
1462 | IOService::getPMRootDomain( void ) | |
0b4e3aa0 | 1463 | { |
0a7de745 | 1464 | return gIOPMRootDomain; |
0b4e3aa0 A |
1465 | } |
1466 | ||
0a7de745 A |
1467 | IOService * |
1468 | IOService::getResourceService( void ) | |
0b4e3aa0 | 1469 | { |
0a7de745 | 1470 | return gIOResources; |
0b4e3aa0 A |
1471 | } |
1472 | ||
0a7de745 A |
1473 | void |
1474 | IOService::setPlatform( IOPlatformExpert * platform) | |
1c79356b | 1475 | { |
0a7de745 A |
1476 | gIOPlatform = platform; |
1477 | gIOResources->attachToParent( gIOServiceRoot, gIOServicePlane ); | |
cb323159 | 1478 | gIOUserResources->attachToParent( gIOServiceRoot, gIOServicePlane ); |
fe8ab488 | 1479 | |
0a7de745 | 1480 | static const char * keys[kCpuNumDelayTypes] = { |
f427ee49 A |
1481 | kIOPlatformMaxBusDelay, |
1482 | #if defined(__x86_64__) | |
1483 | kIOPlatformMaxInterruptDelay | |
1484 | #endif /* defined(__x86_64__) */ | |
1485 | }; | |
0a7de745 A |
1486 | const OSObject * objs[2]; |
1487 | OSArray * array; | |
1488 | uint32_t idx; | |
1489 | ||
1490 | for (idx = 0; idx < kCpuNumDelayTypes; idx++) { | |
1491 | objs[0] = sCPULatencySet[idx]; | |
1492 | objs[1] = sCPULatencyHolder[idx]; | |
1493 | array = OSArray::withObjects(objs, 2); | |
1494 | if (!array) { | |
1495 | break; | |
1496 | } | |
1497 | platform->setProperty(keys[idx], array); | |
1498 | array->release(); | |
1499 | } | |
0b4e3aa0 A |
1500 | } |
1501 | ||
0a7de745 A |
1502 | void |
1503 | IOService::setPMRootDomain( class IOPMrootDomain * rootDomain) | |
0b4e3aa0 | 1504 | { |
0a7de745 | 1505 | gIOPMRootDomain = rootDomain; |
cb323159 A |
1506 | publishResource(gIOResourceIOKitKey); |
1507 | IOServicePH::init(rootDomain); | |
1c79356b A |
1508 | } |
1509 | ||
1510 | /* | |
1511 | * Stacking change | |
1512 | */ | |
1513 | ||
0a7de745 A |
1514 | bool |
1515 | IOService::lockForArbitration( bool isSuccessRequired ) | |
1516 | { | |
1517 | bool found; | |
1518 | bool success; | |
1519 | ArbitrationLockQueueElement * element; | |
1520 | ArbitrationLockQueueElement * active; | |
1521 | ArbitrationLockQueueElement * waiting; | |
1522 | ||
1523 | enum { kPutOnFreeQueue, kPutOnActiveQueue, kPutOnWaitingQueue } action; | |
1524 | ||
1525 | // lock global access | |
1526 | IOTakeLock( gArbitrationLockQueueLock ); | |
1527 | ||
1528 | // obtain an unused queue element | |
1529 | if (!queue_empty( &gArbitrationLockQueueFree )) { | |
1530 | queue_remove_first( &gArbitrationLockQueueFree, | |
1531 | element, | |
1532 | ArbitrationLockQueueElement *, | |
1533 | link ); | |
1534 | } else { | |
1535 | element = IONew( ArbitrationLockQueueElement, 1 ); | |
1536 | assert( element ); | |
1537 | } | |
1538 | ||
1539 | // prepare the queue element | |
1540 | element->thread = IOThreadSelf(); | |
1541 | element->service = this; | |
1542 | element->count = 1; | |
1543 | element->required = isSuccessRequired; | |
1544 | element->aborted = false; | |
1545 | ||
1546 | // determine whether this object is already locked (ie. on active queue) | |
1547 | found = false; | |
1548 | queue_iterate( &gArbitrationLockQueueActive, | |
1549 | active, | |
1550 | ArbitrationLockQueueElement *, | |
1551 | link ) | |
1552 | { | |
1553 | if (active->service == element->service) { | |
1554 | found = true; | |
1555 | break; | |
1556 | } | |
1557 | } | |
1558 | ||
1559 | if (found) { // this object is already locked | |
1560 | // determine whether it is the same or a different thread trying to lock | |
1561 | if (active->thread != element->thread) { // it is a different thread | |
cb323159 | 1562 | ArbitrationLockQueueElement * victim = NULL; |
0a7de745 A |
1563 | |
1564 | // before placing this new thread on the waiting queue, we look for | |
1565 | // a deadlock cycle... | |
1566 | ||
1567 | while (1) { | |
1568 | // determine whether the active thread holding the object we | |
1569 | // want is waiting for another object to be unlocked | |
1570 | found = false; | |
1571 | queue_iterate( &gArbitrationLockQueueWaiting, | |
1572 | waiting, | |
1573 | ArbitrationLockQueueElement *, | |
1574 | link ) | |
1575 | { | |
1576 | if (waiting->thread == active->thread) { | |
1577 | assert( false == waiting->aborted ); | |
1578 | found = true; | |
1579 | break; | |
1580 | } | |
1581 | } | |
1582 | ||
1583 | if (found) { // yes, active thread waiting for another object | |
1584 | // this may be a candidate for rejection if the required | |
1585 | // flag is not set, should we detect a deadlock later on | |
1586 | if (false == waiting->required) { | |
1587 | victim = waiting; | |
1588 | } | |
1589 | ||
1590 | // find the thread that is holding this other object, that | |
1591 | // is blocking the active thread from proceeding (fun :-) | |
1592 | found = false; | |
1593 | queue_iterate( &gArbitrationLockQueueActive, | |
1594 | active, // (reuse active queue element) | |
1595 | ArbitrationLockQueueElement *, | |
1596 | link ) | |
1597 | { | |
1598 | if (active->service == waiting->service) { | |
1599 | found = true; | |
1600 | break; | |
1601 | } | |
1602 | } | |
1603 | ||
1604 | // someone must be holding it or it wouldn't be waiting | |
1605 | assert( found ); | |
1606 | ||
1607 | if (active->thread == element->thread) { | |
1608 | // doh, it's waiting for the thread that originated | |
1609 | // this whole lock (ie. current thread) -> deadlock | |
1610 | if (false == element->required) { // willing to fail? | |
1611 | // the originating thread doesn't have the required | |
1612 | // flag, so it can fail | |
1613 | success = false; // (fail originating lock request) | |
1614 | break; // (out of while) | |
1615 | } else { // originating thread is not willing to fail | |
1616 | // see if we came across a waiting thread that did | |
1617 | // not have the 'required' flag set: we'll fail it | |
1618 | if (victim) { | |
1619 | // we do have a willing victim, fail it's lock | |
1620 | victim->aborted = true; | |
1621 | ||
1622 | // take the victim off the waiting queue | |
1623 | queue_remove( &gArbitrationLockQueueWaiting, | |
1624 | victim, | |
1625 | ArbitrationLockQueueElement *, | |
1626 | link ); | |
1627 | ||
1628 | // wake the victim | |
1629 | IOLockWakeup( gArbitrationLockQueueLock, | |
1630 | victim, | |
1631 | /* one thread */ true ); | |
1632 | ||
1633 | // allow this thread to proceed (ie. wait) | |
1634 | success = true; // (put request on wait queue) | |
1635 | break; // (out of while) | |
1636 | } else { | |
1637 | // all the waiting threads we came across in | |
1638 | // finding this loop had the 'required' flag | |
1639 | // set, so we've got a deadlock we can't avoid | |
1640 | panic("I/O Kit: Unrecoverable deadlock."); | |
1641 | } | |
1642 | } | |
1643 | } else { | |
1644 | // repeat while loop, redefining active thread to be the | |
1645 | // thread holding "this other object" (see above), and | |
1646 | // looking for threads waiting on it; note the active | |
1647 | // variable points to "this other object" already... so | |
1648 | // there nothing to do in this else clause. | |
1649 | } | |
1650 | } else { // no, active thread is not waiting for another object | |
1651 | success = true; // (put request on wait queue) | |
1652 | break; // (out of while) | |
1653 | } | |
1654 | } // while forever | |
1655 | ||
1656 | if (success) { // put the request on the waiting queue? | |
1657 | kern_return_t wait_result; | |
1658 | ||
1659 | // place this thread on the waiting queue and put it to sleep; | |
1660 | // we place it at the tail of the queue... | |
1661 | queue_enter( &gArbitrationLockQueueWaiting, | |
1662 | element, | |
1663 | ArbitrationLockQueueElement *, | |
1664 | link ); | |
1665 | ||
1666 | // declare that this thread will wait for a given event | |
1667 | restart_sleep: wait_result = assert_wait( element, | |
1668 | element->required ? THREAD_UNINT | |
1669 | : THREAD_INTERRUPTIBLE ); | |
1670 | ||
1671 | // unlock global access | |
1672 | IOUnlock( gArbitrationLockQueueLock ); | |
1673 | ||
1674 | // put thread to sleep, waiting for our event to fire... | |
1675 | if (wait_result == THREAD_WAITING) { | |
1676 | wait_result = thread_block(THREAD_CONTINUE_NULL); | |
1677 | } | |
1678 | ||
1679 | ||
1680 | // ...and we've been woken up; we might be in one of two states: | |
1681 | // (a) we've been aborted and our queue element is not on | |
1682 | // any of the three queues, but is floating around | |
1683 | // (b) we're allowed to proceed with the lock and we have | |
1684 | // already been moved from the waiting queue to the | |
1685 | // active queue. | |
1686 | // ...plus a 3rd state, should the thread have been interrupted: | |
1687 | // (c) we're still on the waiting queue | |
1688 | ||
1689 | // determine whether we were interrupted out of our sleep | |
1690 | if (THREAD_INTERRUPTED == wait_result) { | |
1691 | // re-lock global access | |
1692 | IOTakeLock( gArbitrationLockQueueLock ); | |
1693 | ||
1694 | // determine whether we're still on the waiting queue | |
1695 | found = false; | |
1696 | queue_iterate( &gArbitrationLockQueueWaiting, | |
1697 | waiting, // (reuse waiting queue element) | |
1698 | ArbitrationLockQueueElement *, | |
1699 | link ) | |
1700 | { | |
1701 | if (waiting == element) { | |
1702 | found = true; | |
1703 | break; | |
1704 | } | |
1705 | } | |
1706 | ||
1707 | if (found) { // yes, we're still on the waiting queue | |
1708 | // determine whether we're willing to fail | |
1709 | if (false == element->required) { | |
1710 | // mark us as aborted | |
1711 | element->aborted = true; | |
1712 | ||
1713 | // take us off the waiting queue | |
1714 | queue_remove( &gArbitrationLockQueueWaiting, | |
1715 | element, | |
1716 | ArbitrationLockQueueElement *, | |
1717 | link ); | |
1718 | } else { // we are not willing to fail | |
1719 | // ignore interruption, go back to sleep | |
1720 | goto restart_sleep; | |
1721 | } | |
1722 | } | |
1723 | ||
1724 | // unlock global access | |
1725 | IOUnlock( gArbitrationLockQueueLock ); | |
1726 | ||
1727 | // proceed as though this were a normal wake up | |
1728 | wait_result = THREAD_AWAKENED; | |
1729 | } | |
1730 | ||
1731 | assert( THREAD_AWAKENED == wait_result ); | |
1732 | ||
1733 | // determine whether we've been aborted while we were asleep | |
1734 | if (element->aborted) { | |
1735 | assert( false == element->required ); | |
1736 | ||
1737 | // re-lock global access | |
1738 | IOTakeLock( gArbitrationLockQueueLock ); | |
1739 | ||
1740 | action = kPutOnFreeQueue; | |
1741 | success = false; | |
1742 | } else { // we weren't aborted, so we must be ready to go :-) | |
1743 | // we've already been moved from waiting to active queue | |
1744 | return true; | |
1745 | } | |
1746 | } else { // the lock request is to be failed | |
1747 | // return unused queue element to queue | |
1748 | action = kPutOnFreeQueue; | |
1749 | } | |
1750 | } else { // it is the same thread, recursive access is allowed | |
1751 | // add one level of recursion | |
1752 | active->count++; | |
1753 | ||
1754 | // return unused queue element to queue | |
1755 | action = kPutOnFreeQueue; | |
1756 | success = true; | |
1757 | } | |
1758 | } else { // this object is not already locked, so let this thread through | |
1759 | action = kPutOnActiveQueue; | |
1760 | success = true; | |
1761 | } | |
1762 | ||
1763 | // put the new element on a queue | |
1764 | if (kPutOnActiveQueue == action) { | |
1765 | queue_enter( &gArbitrationLockQueueActive, | |
1766 | element, | |
1767 | ArbitrationLockQueueElement *, | |
1768 | link ); | |
1769 | } else if (kPutOnFreeQueue == action) { | |
1770 | queue_enter( &gArbitrationLockQueueFree, | |
1771 | element, | |
1772 | ArbitrationLockQueueElement *, | |
1773 | link ); | |
1774 | } else { | |
1775 | assert( 0 ); // kPutOnWaitingQueue never occurs, handled specially above | |
1776 | } | |
1777 | ||
1778 | // unlock global access | |
1779 | IOUnlock( gArbitrationLockQueueLock ); | |
1780 | ||
1781 | return success; | |
1782 | } | |
1783 | ||
1784 | void | |
1785 | IOService::unlockForArbitration( void ) | |
1786 | { | |
1787 | bool found; | |
1788 | ArbitrationLockQueueElement * element; | |
1789 | ||
1790 | // lock global access | |
1791 | IOTakeLock( gArbitrationLockQueueLock ); | |
1792 | ||
1793 | // find the lock element for this object (ie. on active queue) | |
1794 | found = false; | |
1795 | queue_iterate( &gArbitrationLockQueueActive, | |
1796 | element, | |
1797 | ArbitrationLockQueueElement *, | |
1798 | link ) | |
1799 | { | |
1800 | if (element->service == this) { | |
1801 | found = true; | |
1802 | break; | |
1803 | } | |
1804 | } | |
1805 | ||
1806 | assert( found ); | |
1807 | ||
1808 | // determine whether the lock has been taken recursively | |
1809 | if (element->count > 1) { | |
1810 | // undo one level of recursion | |
1811 | element->count--; | |
1812 | } else { | |
1813 | // remove it from the active queue | |
1814 | queue_remove( &gArbitrationLockQueueActive, | |
1815 | element, | |
1816 | ArbitrationLockQueueElement *, | |
1817 | link ); | |
1818 | ||
1819 | // put it on the free queue | |
1820 | queue_enter( &gArbitrationLockQueueFree, | |
1821 | element, | |
1822 | ArbitrationLockQueueElement *, | |
1823 | link ); | |
1824 | ||
1825 | // determine whether a thread is waiting for object (head to tail scan) | |
1826 | found = false; | |
1827 | queue_iterate( &gArbitrationLockQueueWaiting, | |
1828 | element, | |
1829 | ArbitrationLockQueueElement *, | |
1830 | link ) | |
1831 | { | |
1832 | if (element->service == this) { | |
1833 | found = true; | |
1834 | break; | |
1835 | } | |
1836 | } | |
1837 | ||
1838 | if (found) { // we found an interested thread on waiting queue | |
1839 | // remove it from the waiting queue | |
1840 | queue_remove( &gArbitrationLockQueueWaiting, | |
1841 | element, | |
1842 | ArbitrationLockQueueElement *, | |
1843 | link ); | |
1844 | ||
1845 | // put it on the active queue | |
1846 | queue_enter( &gArbitrationLockQueueActive, | |
1847 | element, | |
1848 | ArbitrationLockQueueElement *, | |
1849 | link ); | |
1850 | ||
1851 | // wake the waiting thread | |
1852 | IOLockWakeup( gArbitrationLockQueueLock, | |
1853 | element, | |
1854 | /* one thread */ true ); | |
1855 | } | |
1856 | } | |
1857 | ||
1858 | // unlock global access | |
1859 | IOUnlock( gArbitrationLockQueueLock ); | |
1860 | } | |
1861 | ||
1862 | uint32_t | |
1863 | IOService::isLockedForArbitration(IOService * service) | |
5ba3f43e A |
1864 | { |
1865 | #if DEBUG_NOTIFIER_LOCKED | |
0a7de745 A |
1866 | uint32_t count; |
1867 | ArbitrationLockQueueElement * active; | |
1868 | ||
1869 | // lock global access | |
1870 | IOLockLock(gArbitrationLockQueueLock); | |
1871 | ||
1872 | // determine whether this object is already locked (ie. on active queue) | |
1873 | count = 0; | |
1874 | queue_iterate(&gArbitrationLockQueueActive, | |
1875 | active, | |
1876 | ArbitrationLockQueueElement *, | |
1877 | link) | |
1878 | { | |
1879 | if ((active->thread == IOThreadSelf()) | |
1880 | && (!service || (active->service == service))) { | |
1881 | count += 0x10000; | |
1882 | count += active->count; | |
1883 | } | |
1884 | } | |
1885 | ||
1886 | IOLockUnlock(gArbitrationLockQueueLock); | |
1887 | ||
1888 | return count; | |
5ba3f43e A |
1889 | |
1890 | #else /* DEBUG_NOTIFIER_LOCKED */ | |
1891 | ||
0a7de745 | 1892 | return 0; |
5ba3f43e A |
1893 | |
1894 | #endif /* DEBUG_NOTIFIER_LOCKED */ | |
1895 | } | |
1896 | ||
0a7de745 A |
1897 | void |
1898 | IOService::applyToProviders( IOServiceApplierFunction applier, | |
1899 | void * context ) | |
1c79356b | 1900 | { |
0a7de745 A |
1901 | applyToParents((IORegistryEntryApplierFunction) applier, |
1902 | context, gIOServicePlane ); | |
1c79356b A |
1903 | } |
1904 | ||
0a7de745 A |
1905 | void |
1906 | IOService::applyToClients( IOServiceApplierFunction applier, | |
1907 | void * context ) | |
1c79356b | 1908 | { |
0a7de745 A |
1909 | applyToChildren((IORegistryEntryApplierFunction) applier, |
1910 | context, gIOServicePlane ); | |
1c79356b A |
1911 | } |
1912 | ||
1913 | ||
f427ee49 A |
1914 | static void |
1915 | IOServiceApplierToBlock(IOService * next, void * context) | |
1916 | { | |
1917 | IOServiceApplierBlock block = (IOServiceApplierBlock) context; | |
1918 | block(next); | |
1919 | } | |
1920 | ||
1921 | void | |
1922 | IOService::applyToProviders(IOServiceApplierBlock applier) | |
1923 | { | |
1924 | applyToProviders(&IOServiceApplierToBlock, applier); | |
1925 | } | |
1926 | ||
1927 | void | |
1928 | IOService::applyToClients(IOServiceApplierBlock applier) | |
1929 | { | |
1930 | applyToClients(&IOServiceApplierToBlock, applier); | |
1931 | } | |
1932 | ||
1c79356b A |
1933 | /* |
1934 | * Client messages | |
1935 | */ | |
1936 | ||
1937 | ||
1938 | // send a message to a client or interested party of this service | |
0a7de745 A |
1939 | IOReturn |
1940 | IOService::messageClient( UInt32 type, OSObject * client, | |
1941 | void * argument, vm_size_t argSize ) | |
1942 | { | |
1943 | IOReturn ret; | |
1944 | IOService * service; | |
1945 | _IOServiceInterestNotifier * notify; | |
1946 | ||
1947 | if ((service = OSDynamicCast( IOService, client))) { | |
1948 | ret = service->message( type, this, argument ); | |
1949 | } else if ((notify = OSDynamicCast( _IOServiceInterestNotifier, client))) { | |
1950 | _IOServiceNotifierInvocation invocation; | |
1951 | bool willNotify; | |
1952 | ||
1953 | invocation.thread = current_thread(); | |
1954 | ||
1955 | LOCKWRITENOTIFY(); | |
1956 | willNotify = (0 != (kIOServiceNotifyEnable & notify->state)); | |
1957 | ||
1958 | if (willNotify) { | |
1959 | queue_enter( ¬ify->handlerInvocations, &invocation, | |
1960 | _IOServiceNotifierInvocation *, link ); | |
1961 | } | |
1962 | UNLOCKNOTIFY(); | |
1963 | ||
1964 | if (willNotify) { | |
1965 | ret = (*notify->handler)( notify->target, notify->ref, | |
1966 | type, this, argument, argSize ); | |
1967 | ||
1968 | LOCKWRITENOTIFY(); | |
1969 | queue_remove( ¬ify->handlerInvocations, &invocation, | |
1970 | _IOServiceNotifierInvocation *, link ); | |
1971 | if (kIOServiceNotifyWaiter & notify->state) { | |
1972 | notify->state &= ~kIOServiceNotifyWaiter; | |
1973 | WAKEUPNOTIFY( notify ); | |
1974 | } | |
1975 | UNLOCKNOTIFY(); | |
1976 | } else { | |
1977 | ret = kIOReturnSuccess; | |
1978 | } | |
1979 | } else { | |
1980 | ret = kIOReturnBadArgument; | |
1981 | } | |
1982 | ||
1983 | return ret; | |
1c79356b A |
1984 | } |
1985 | ||
c0fea474 A |
1986 | static void |
1987 | applyToInterestNotifiers(const IORegistryEntry *target, | |
0a7de745 A |
1988 | const OSSymbol * typeOfInterest, |
1989 | OSObjectApplierFunction applier, | |
1990 | void * context ) | |
1c79356b | 1991 | { |
cb323159 | 1992 | OSArray * copyArray = NULL; |
0a7de745 | 1993 | OSObject * prop; |
1c79356b | 1994 | |
0a7de745 | 1995 | LOCKREADNOTIFY(); |
91447636 | 1996 | |
0a7de745 A |
1997 | prop = target->copyProperty(typeOfInterest); |
1998 | IOCommand *notifyList = OSDynamicCast(IOCommand, prop); | |
91447636 | 1999 | |
0a7de745 A |
2000 | if (notifyList) { |
2001 | copyArray = OSArray::withCapacity(1); | |
91447636 | 2002 | |
0a7de745 A |
2003 | // iterate over queue, entry is set to each element in the list |
2004 | iterqueue(¬ifyList->fCommandChain, entry) { | |
2005 | _IOServiceInterestNotifier * notify; | |
91447636 | 2006 | |
0a7de745 A |
2007 | queue_element(entry, notify, _IOServiceInterestNotifier *, chain); |
2008 | copyArray->setObject(notify); | |
2009 | } | |
91447636 | 2010 | } |
0a7de745 | 2011 | UNLOCKNOTIFY(); |
91447636 | 2012 | |
0a7de745 A |
2013 | if (copyArray) { |
2014 | unsigned int index; | |
2015 | OSObject * next; | |
91447636 | 2016 | |
0a7de745 A |
2017 | for (index = 0; (next = copyArray->getObject( index )); index++) { |
2018 | (*applier)(next, context); | |
2019 | } | |
2020 | copyArray->release(); | |
2021 | } | |
5ba3f43e | 2022 | |
0a7de745 | 2023 | OSSafeReleaseNULL(prop); |
1c79356b A |
2024 | } |
2025 | ||
0a7de745 A |
2026 | void |
2027 | IOService::applyToInterested( const OSSymbol * typeOfInterest, | |
2028 | OSObjectApplierFunction applier, | |
2029 | void * context ) | |
c0fea474 | 2030 | { |
0a7de745 A |
2031 | if (gIOGeneralInterest == typeOfInterest) { |
2032 | applyToClients((IOServiceApplierFunction) applier, context ); | |
2033 | } | |
2034 | applyToInterestNotifiers(this, typeOfInterest, applier, context); | |
c0fea474 A |
2035 | } |
2036 | ||
1c79356b | 2037 | struct MessageClientsContext { |
0a7de745 A |
2038 | IOService * service; |
2039 | UInt32 type; | |
2040 | void * argument; | |
2041 | vm_size_t argSize; | |
2042 | IOReturn ret; | |
1c79356b A |
2043 | }; |
2044 | ||
0a7de745 A |
2045 | static void |
2046 | messageClientsApplier( OSObject * object, void * ctx ) | |
1c79356b | 2047 | { |
0a7de745 A |
2048 | IOReturn ret; |
2049 | MessageClientsContext * context = (MessageClientsContext *) ctx; | |
1c79356b | 2050 | |
0a7de745 A |
2051 | ret = context->service->messageClient( context->type, |
2052 | object, context->argument, context->argSize ); | |
2053 | if (kIOReturnSuccess != ret) { | |
2054 | context->ret = ret; | |
2055 | } | |
1c79356b A |
2056 | } |
2057 | ||
2058 | // send a message to all clients | |
0a7de745 A |
2059 | IOReturn |
2060 | IOService::messageClients( UInt32 type, | |
2061 | void * argument, vm_size_t argSize ) | |
1c79356b | 2062 | { |
0a7de745 | 2063 | MessageClientsContext context; |
1c79356b | 2064 | |
0a7de745 A |
2065 | context.service = this; |
2066 | context.type = type; | |
2067 | context.argument = argument; | |
2068 | context.argSize = argSize; | |
2069 | context.ret = kIOReturnSuccess; | |
1c79356b | 2070 | |
0a7de745 A |
2071 | applyToInterested( gIOGeneralInterest, |
2072 | &messageClientsApplier, &context ); | |
1c79356b | 2073 | |
0a7de745 | 2074 | return context.ret; |
1c79356b A |
2075 | } |
2076 | ||
0a7de745 A |
2077 | IOReturn |
2078 | IOService::acknowledgeNotification( IONotificationRef notification, | |
2079 | IOOptionBits response ) | |
1c79356b | 2080 | { |
0a7de745 | 2081 | return kIOReturnUnsupported; |
1c79356b A |
2082 | } |
2083 | ||
0a7de745 A |
2084 | IONotifier * |
2085 | IOService::registerInterest( const OSSymbol * typeOfInterest, | |
2086 | IOServiceInterestHandler handler, void * target, void * ref ) | |
1c79356b | 2087 | { |
cb323159 | 2088 | _IOServiceInterestNotifier * notify = NULL; |
0a7de745 | 2089 | IOReturn rc = kIOReturnError; |
fe8ab488 | 2090 | |
0a7de745 A |
2091 | notify = new _IOServiceInterestNotifier; |
2092 | if (!notify) { | |
2093 | return NULL; | |
2094 | } | |
fe8ab488 | 2095 | |
0a7de745 A |
2096 | if (notify->init()) { |
2097 | rc = registerInterestForNotifier(notify, typeOfInterest, | |
2098 | handler, target, ref); | |
2099 | } | |
fe8ab488 | 2100 | |
0a7de745 A |
2101 | if (rc != kIOReturnSuccess) { |
2102 | notify->release(); | |
cb323159 | 2103 | notify = NULL; |
0a7de745 | 2104 | } |
fe8ab488 | 2105 | |
0a7de745 | 2106 | return notify; |
fe8ab488 A |
2107 | } |
2108 | ||
d9a64523 A |
2109 | |
2110 | ||
2111 | static IOReturn | |
2112 | IOServiceInterestHandlerToBlock( void * target __unused, void * refCon, | |
0a7de745 A |
2113 | UInt32 messageType, IOService * provider, |
2114 | void * messageArgument, vm_size_t argSize ) | |
d9a64523 | 2115 | { |
0a7de745 | 2116 | return ((IOServiceInterestHandlerBlock) refCon)(messageType, provider, messageArgument, argSize); |
d9a64523 A |
2117 | } |
2118 | ||
0a7de745 A |
2119 | IONotifier * |
2120 | IOService::registerInterest(const OSSymbol * typeOfInterest, | |
2121 | IOServiceInterestHandlerBlock handler) | |
d9a64523 | 2122 | { |
0a7de745 A |
2123 | IONotifier * notify; |
2124 | void * block; | |
d9a64523 | 2125 | |
0a7de745 A |
2126 | block = Block_copy(handler); |
2127 | if (!block) { | |
2128 | return NULL; | |
2129 | } | |
d9a64523 | 2130 | |
0a7de745 | 2131 | notify = registerInterest(typeOfInterest, &IOServiceInterestHandlerToBlock, NULL, block); |
d9a64523 | 2132 | |
0a7de745 A |
2133 | if (!notify) { |
2134 | Block_release(block); | |
2135 | } | |
d9a64523 | 2136 | |
0a7de745 | 2137 | return notify; |
d9a64523 A |
2138 | } |
2139 | ||
0a7de745 A |
2140 | IOReturn |
2141 | IOService::registerInterestForNotifier( IONotifier *svcNotify, const OSSymbol * typeOfInterest, | |
2142 | IOServiceInterestHandler handler, void * target, void * ref ) | |
fe8ab488 | 2143 | { |
0a7de745 | 2144 | IOReturn rc = kIOReturnSuccess; |
cb323159 | 2145 | _IOServiceInterestNotifier *notify = NULL; |
fe8ab488 | 2146 | |
0a7de745 A |
2147 | if (!svcNotify || !(notify = OSDynamicCast(_IOServiceInterestNotifier, svcNotify))) { |
2148 | return kIOReturnBadArgument; | |
2149 | } | |
91447636 | 2150 | |
0a7de745 A |
2151 | notify->handler = handler; |
2152 | notify->target = target; | |
2153 | notify->ref = ref; | |
2154 | ||
2155 | if ((typeOfInterest != gIOGeneralInterest) | |
2156 | && (typeOfInterest != gIOBusyInterest) | |
2157 | && (typeOfInterest != gIOAppPowerStateInterest) | |
2158 | && (typeOfInterest != gIOConsoleSecurityInterest) | |
2159 | && (typeOfInterest != gIOPriorityPowerStateInterest)) { | |
2160 | return kIOReturnBadArgument; | |
2161 | } | |
91447636 | 2162 | |
0a7de745 A |
2163 | lockForArbitration(); |
2164 | if (0 == (__state[0] & kIOServiceInactiveState)) { | |
2165 | notify->state = kIOServiceNotifyEnable; | |
2166 | ||
2167 | ////// queue | |
2168 | ||
2169 | LOCKWRITENOTIFY(); | |
2170 | ||
2171 | // Get the head of the notifier linked list | |
2172 | IOCommand * notifyList; | |
2173 | OSObject * obj = copyProperty( typeOfInterest ); | |
2174 | if (!(notifyList = OSDynamicCast(IOCommand, obj))) { | |
2175 | notifyList = OSTypeAlloc(IOCommand); | |
2176 | if (notifyList) { | |
2177 | notifyList->init(); | |
2178 | bool ok = setProperty( typeOfInterest, notifyList); | |
2179 | notifyList->release(); | |
2180 | if (!ok) { | |
cb323159 | 2181 | notifyList = NULL; |
0a7de745 A |
2182 | } |
2183 | } | |
2184 | } | |
2185 | if (obj) { | |
2186 | obj->release(); | |
2187 | } | |
91447636 | 2188 | |
0a7de745 A |
2189 | if (notifyList) { |
2190 | enqueue(¬ifyList->fCommandChain, ¬ify->chain); | |
2191 | notify->retain(); // ref'ed while in list | |
2192 | } | |
fe8ab488 | 2193 | |
0a7de745 A |
2194 | UNLOCKNOTIFY(); |
2195 | } else { | |
2196 | rc = kIOReturnNotReady; | |
2197 | } | |
2198 | unlockForArbitration(); | |
1c79356b | 2199 | |
0a7de745 | 2200 | return rc; |
1c79356b A |
2201 | } |
2202 | ||
0a7de745 A |
2203 | static void |
2204 | cleanInterestList( OSObject * head ) | |
1c79356b | 2205 | { |
0a7de745 A |
2206 | IOCommand *notifyHead = OSDynamicCast(IOCommand, head); |
2207 | if (!notifyHead) { | |
2208 | return; | |
2209 | } | |
91447636 | 2210 | |
0a7de745 A |
2211 | LOCKWRITENOTIFY(); |
2212 | while (queue_entry_t entry = dequeue(¬ifyHead->fCommandChain)) { | |
cb323159 | 2213 | queue_next(entry) = queue_prev(entry) = NULL; |
91447636 | 2214 | |
0a7de745 | 2215 | _IOServiceInterestNotifier * notify; |
91447636 | 2216 | |
0a7de745 A |
2217 | queue_element(entry, notify, _IOServiceInterestNotifier *, chain); |
2218 | notify->release(); | |
2219 | } | |
2220 | UNLOCKNOTIFY(); | |
1c79356b A |
2221 | } |
2222 | ||
0a7de745 A |
2223 | void |
2224 | IOService::unregisterAllInterest( void ) | |
1c79356b | 2225 | { |
0a7de745 | 2226 | OSObject * prop; |
5ba3f43e | 2227 | |
0a7de745 A |
2228 | prop = copyProperty(gIOGeneralInterest); |
2229 | cleanInterestList(prop); | |
2230 | OSSafeReleaseNULL(prop); | |
5ba3f43e | 2231 | |
0a7de745 A |
2232 | prop = copyProperty(gIOBusyInterest); |
2233 | cleanInterestList(prop); | |
2234 | OSSafeReleaseNULL(prop); | |
5ba3f43e | 2235 | |
0a7de745 A |
2236 | prop = copyProperty(gIOAppPowerStateInterest); |
2237 | cleanInterestList(prop); | |
2238 | OSSafeReleaseNULL(prop); | |
5ba3f43e | 2239 | |
0a7de745 A |
2240 | prop = copyProperty(gIOPriorityPowerStateInterest); |
2241 | cleanInterestList(prop); | |
2242 | OSSafeReleaseNULL(prop); | |
5ba3f43e | 2243 | |
0a7de745 A |
2244 | prop = copyProperty(gIOConsoleSecurityInterest); |
2245 | cleanInterestList(prop); | |
2246 | OSSafeReleaseNULL(prop); | |
1c79356b A |
2247 | } |
2248 | ||
2249 | /* | |
2250 | * _IOServiceInterestNotifier | |
2251 | */ | |
2252 | ||
2253 | // wait for all threads, other than the current one, | |
2254 | // to exit the handler | |
2255 | ||
0a7de745 A |
2256 | void |
2257 | _IOServiceInterestNotifier::wait() | |
1c79356b | 2258 | { |
0a7de745 A |
2259 | _IOServiceNotifierInvocation * next; |
2260 | bool doWait; | |
1c79356b | 2261 | |
0a7de745 A |
2262 | do { |
2263 | doWait = false; | |
2264 | queue_iterate( &handlerInvocations, next, | |
2265 | _IOServiceNotifierInvocation *, link) { | |
2266 | if (next->thread != current_thread()) { | |
2267 | doWait = true; | |
2268 | break; | |
2269 | } | |
2270 | } | |
2271 | if (doWait) { | |
2272 | state |= kIOServiceNotifyWaiter; | |
2273 | SLEEPNOTIFY(this); | |
2274 | } | |
2275 | } while (doWait); | |
1c79356b A |
2276 | } |
2277 | ||
0a7de745 A |
2278 | void |
2279 | _IOServiceInterestNotifier::free() | |
1c79356b | 2280 | { |
0a7de745 | 2281 | assert( queue_empty( &handlerInvocations )); |
d9a64523 | 2282 | |
0a7de745 A |
2283 | if (handler == &IOServiceInterestHandlerToBlock) { |
2284 | Block_release(ref); | |
2285 | } | |
d9a64523 | 2286 | |
0a7de745 | 2287 | OSObject::free(); |
1c79356b A |
2288 | } |
2289 | ||
0a7de745 A |
2290 | void |
2291 | _IOServiceInterestNotifier::remove() | |
1c79356b | 2292 | { |
0a7de745 | 2293 | LOCKWRITENOTIFY(); |
1c79356b | 2294 | |
0a7de745 A |
2295 | if (queue_next( &chain )) { |
2296 | remqueue(&chain); | |
cb323159 | 2297 | queue_next( &chain) = queue_prev( &chain) = NULL; |
0a7de745 A |
2298 | release(); |
2299 | } | |
2300 | ||
2301 | state &= ~kIOServiceNotifyEnable; | |
1c79356b | 2302 | |
0a7de745 | 2303 | wait(); |
1c79356b | 2304 | |
0a7de745 | 2305 | UNLOCKNOTIFY(); |
1c79356b | 2306 | |
0a7de745 | 2307 | release(); |
1c79356b A |
2308 | } |
2309 | ||
0a7de745 A |
2310 | bool |
2311 | _IOServiceInterestNotifier::disable() | |
1c79356b | 2312 | { |
0a7de745 | 2313 | bool ret; |
1c79356b | 2314 | |
0a7de745 | 2315 | LOCKWRITENOTIFY(); |
1c79356b | 2316 | |
0a7de745 A |
2317 | ret = (0 != (kIOServiceNotifyEnable & state)); |
2318 | state &= ~kIOServiceNotifyEnable; | |
2319 | if (ret) { | |
2320 | wait(); | |
2321 | } | |
1c79356b | 2322 | |
0a7de745 | 2323 | UNLOCKNOTIFY(); |
1c79356b | 2324 | |
0a7de745 | 2325 | return ret; |
1c79356b A |
2326 | } |
2327 | ||
0a7de745 A |
2328 | void |
2329 | _IOServiceInterestNotifier::enable( bool was ) | |
1c79356b | 2330 | { |
0a7de745 A |
2331 | LOCKWRITENOTIFY(); |
2332 | if (was) { | |
2333 | state |= kIOServiceNotifyEnable; | |
2334 | } else { | |
2335 | state &= ~kIOServiceNotifyEnable; | |
2336 | } | |
2337 | UNLOCKNOTIFY(); | |
1c79356b A |
2338 | } |
2339 | ||
0a7de745 A |
2340 | bool |
2341 | _IOServiceInterestNotifier::init() | |
fe8ab488 | 2342 | { |
0a7de745 A |
2343 | queue_init( &handlerInvocations ); |
2344 | return OSObject::init(); | |
fe8ab488 | 2345 | } |
0b4e3aa0 | 2346 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ |
1c79356b A |
2347 | |
2348 | /* | |
0b4e3aa0 | 2349 | * Termination |
1c79356b A |
2350 | */ |
2351 | ||
0a7de745 A |
2352 | #define tailQ(o) setObject(o) |
2353 | #define headQ(o) setObject(0, o) | |
2354 | #define TLOG(fmt, args...) { if(kIOLogYield & gIOKitDebug) { IOLog("[%llx] ", thread_tid(current_thread())); IOLog(fmt, ## args); }} | |
0b4e3aa0 | 2355 | |
0a7de745 A |
2356 | static void |
2357 | _workLoopAction( IOWorkLoop::Action action, | |
2358 | IOService * service, | |
cb323159 A |
2359 | void * p0 = NULL, void * p1 = NULL, |
2360 | void * p2 = NULL, void * p3 = NULL ) | |
0a7de745 A |
2361 | { |
2362 | IOWorkLoop * wl; | |
2363 | ||
2364 | if ((wl = service->getWorkLoop())) { | |
2365 | wl->retain(); | |
2366 | wl->runAction( action, service, p0, p1, p2, p3 ); | |
2367 | wl->release(); | |
2368 | } else { | |
2369 | (*action)( service, p0, p1, p2, p3 ); | |
2370 | } | |
0b4e3aa0 A |
2371 | } |
2372 | ||
0a7de745 A |
2373 | bool |
2374 | IOService::requestTerminate( IOService * provider, IOOptionBits options ) | |
1c79356b | 2375 | { |
0a7de745 | 2376 | bool ok; |
1c79356b | 2377 | |
0a7de745 A |
2378 | // if its our only provider |
2379 | ok = isParent( provider, gIOServicePlane, true); | |
0b4e3aa0 | 2380 | |
0a7de745 A |
2381 | // -- compat |
2382 | if (ok) { | |
2383 | provider->terminateClient( this, options | kIOServiceRecursing ); | |
2384 | ok = (0 != (kIOServiceInactiveState & __state[0])); | |
2385 | } | |
2386 | // -- | |
1c79356b | 2387 | |
0a7de745 | 2388 | return ok; |
1c79356b A |
2389 | } |
2390 | ||
0a7de745 A |
2391 | bool |
2392 | IOService::terminatePhase1( IOOptionBits options ) | |
1c79356b | 2393 | { |
0a7de745 A |
2394 | IOService * victim; |
2395 | IOService * client; | |
cb323159 | 2396 | IOService * rematchProvider; |
0a7de745 A |
2397 | OSIterator * iter; |
2398 | OSArray * makeInactive; | |
2399 | OSArray * waitingInactive; | |
cb323159 | 2400 | IOOptionBits callerOptions; |
0a7de745 A |
2401 | int waitResult = THREAD_AWAKENED; |
2402 | bool wait; | |
2403 | bool ok; | |
2404 | bool didInactive; | |
2405 | bool startPhase2 = false; | |
1c79356b | 2406 | |
0a7de745 | 2407 | TLOG("%s[0x%qx]::terminatePhase1(%08llx)\n", getName(), getRegistryEntryID(), (long long)options); |
b0d623f7 | 2408 | |
cb323159 A |
2409 | callerOptions = options; |
2410 | rematchProvider = NULL; | |
0a7de745 A |
2411 | uint64_t regID = getRegistryEntryID(); |
2412 | IOServiceTrace( | |
2413 | IOSERVICE_TERMINATE_PHASE1, | |
2414 | (uintptr_t) regID, | |
2415 | (uintptr_t) (regID >> 32), | |
2416 | (uintptr_t) this, | |
2417 | (uintptr_t) options); | |
2418 | ||
2419 | // -- compat | |
2420 | if (options & kIOServiceRecursing) { | |
2421 | lockForArbitration(); | |
2422 | if (0 == (kIOServiceInactiveState & __state[0])) { | |
2423 | __state[0] |= kIOServiceInactiveState; | |
2424 | __state[1] |= kIOServiceRecursing | kIOServiceTermPhase1State; | |
2425 | } | |
2426 | unlockForArbitration(); | |
6d2010ae | 2427 | |
0a7de745 A |
2428 | return true; |
2429 | } | |
2430 | // -- | |
3e170ce0 | 2431 | |
0a7de745 A |
2432 | makeInactive = OSArray::withCapacity( 16 ); |
2433 | waitingInactive = OSArray::withCapacity( 16 ); | |
2434 | if (!makeInactive || !waitingInactive) { | |
2435 | return false; | |
2436 | } | |
2437 | ||
2438 | victim = this; | |
3e170ce0 | 2439 | victim->retain(); |
3e170ce0 | 2440 | |
0a7de745 A |
2441 | while (victim) { |
2442 | didInactive = victim->lockForArbitration( true ); | |
2443 | if (didInactive) { | |
2444 | uint64_t regID1 = victim->getRegistryEntryID(); | |
2445 | IOServiceTrace(IOSERVICE_TERM_SET_INACTIVE, | |
2446 | (uintptr_t) regID1, | |
2447 | (uintptr_t) (regID1 >> 32), | |
2448 | (uintptr_t) victim->__state[1], | |
2449 | (uintptr_t) 0); | |
2450 | ||
2451 | enum { kRP1 = kIOServiceRecursing | kIOServiceTermPhase1State }; | |
2452 | didInactive = (kRP1 == (victim->__state[1] & kRP1)) | |
2453 | || (0 == (victim->__state[0] & kIOServiceInactiveState)); | |
2454 | ||
2455 | if (!didInactive) { | |
2456 | // a multiply attached IOService can be visited twice | |
2457 | if (-1U == waitingInactive->getNextIndexOfObject(victim, 0)) { | |
2458 | do{ | |
2459 | IOLockLock(gIOServiceBusyLock); | |
2460 | wait = (victim->__state[1] & kIOServiceTermPhase1State); | |
2461 | if (wait) { | |
2462 | TLOG("%s[0x%qx]::waitPhase1(%s[0x%qx])\n", | |
2463 | getName(), getRegistryEntryID(), victim->getName(), victim->getRegistryEntryID()); | |
2464 | victim->__state[1] |= kIOServiceTerm1WaiterState; | |
2465 | victim->unlockForArbitration(); | |
2466 | assert_wait((event_t)&victim->__state[1], THREAD_UNINT); | |
2467 | } | |
2468 | IOLockUnlock(gIOServiceBusyLock); | |
2469 | if (wait) { | |
2470 | waitResult = thread_block(THREAD_CONTINUE_NULL); | |
2471 | TLOG("%s[0x%qx]::did waitPhase1(%s[0x%qx])\n", | |
2472 | getName(), getRegistryEntryID(), victim->getName(), victim->getRegistryEntryID()); | |
2473 | victim->lockForArbitration(); | |
2474 | } | |
2475 | }while (wait && (waitResult != THREAD_TIMED_OUT)); | |
2476 | } | |
2477 | } else { | |
2478 | victim->__state[0] |= kIOServiceInactiveState; | |
2479 | victim->__state[0] &= ~(kIOServiceRegisteredState | kIOServiceMatchedState | |
2480 | | kIOServiceFirstPublishState | kIOServiceFirstMatchState); | |
2481 | victim->__state[1] &= ~kIOServiceRecursing; | |
2482 | victim->__state[1] |= kIOServiceTermPhase1State; | |
2483 | waitingInactive->headQ(victim); | |
2484 | if (victim == this) { | |
2485 | if (kIOServiceTerminateNeedWillTerminate & options) { | |
2486 | victim->__state[1] |= kIOServiceNeedWillTerminate; | |
2487 | } | |
2488 | } | |
2489 | victim->_adjustBusy( 1 ); | |
cb323159 A |
2490 | |
2491 | if ((options & kIOServiceTerminateWithRematch) && (victim == this)) { | |
2492 | OSObject * obj; | |
2493 | OSObject * rematchProps; | |
2494 | OSNumber * num; | |
2495 | uint32_t count; | |
2496 | ||
2497 | rematchProvider = getProvider(); | |
2498 | if (rematchProvider) { | |
2499 | obj = rematchProvider->copyProperty(gIORematchCountKey); | |
2500 | num = OSDynamicCast(OSNumber, obj); | |
2501 | count = 0; | |
2502 | if (num) { | |
2503 | count = num->unsigned32BitValue(); | |
2504 | count++; | |
2505 | } | |
2506 | num = OSNumber::withNumber(count, 32); | |
2507 | rematchProvider->setProperty(gIORematchCountKey, num); | |
2508 | rematchProps = copyProperty(gIOMatchedPersonalityKey); | |
2509 | rematchProvider->setProperty(gIORematchPersonalityKey, rematchProps); | |
2510 | OSSafeReleaseNULL(num); | |
2511 | OSSafeReleaseNULL(rematchProps); | |
2512 | OSSafeReleaseNULL(obj); | |
2513 | } | |
2514 | } | |
0a7de745 A |
2515 | } |
2516 | victim->unlockForArbitration(); | |
2517 | } | |
2518 | if (victim == this) { | |
cb323159 | 2519 | options &= ~kIOServiceTerminateWithRematch; |
0a7de745 A |
2520 | startPhase2 = didInactive; |
2521 | } | |
2522 | if (didInactive) { | |
2523 | OSArray * notifiers; | |
2524 | notifiers = victim->copyNotifiers(gIOTerminatedNotification, 0, 0xffffffff); | |
2525 | victim->invokeNotifiers(¬ifiers); | |
2526 | ||
2527 | IOUserClient::destroyUserReferences( victim ); | |
2528 | ||
2529 | iter = victim->getClientIterator(); | |
2530 | if (iter) { | |
2531 | while ((client = (IOService *) iter->getNextObject())) { | |
2532 | TLOG("%s[0x%qx]::requestTerminate(%s[0x%qx], %08llx)\n", | |
2533 | client->getName(), client->getRegistryEntryID(), | |
2534 | victim->getName(), victim->getRegistryEntryID(), (long long)options); | |
2535 | ok = client->requestTerminate( victim, options ); | |
2536 | TLOG("%s[0x%qx]::requestTerminate(%s[0x%qx], ok = %d)\n", | |
2537 | client->getName(), client->getRegistryEntryID(), | |
2538 | victim->getName(), victim->getRegistryEntryID(), ok); | |
2539 | ||
2540 | uint64_t regID1 = client->getRegistryEntryID(); | |
2541 | uint64_t regID2 = victim->getRegistryEntryID(); | |
2542 | IOServiceTrace( | |
2543 | (ok ? IOSERVICE_TERMINATE_REQUEST_OK | |
2544 | : IOSERVICE_TERMINATE_REQUEST_FAIL), | |
2545 | (uintptr_t) regID1, | |
2546 | (uintptr_t) (regID1 >> 32), | |
2547 | (uintptr_t) regID2, | |
2548 | (uintptr_t) (regID2 >> 32)); | |
2549 | ||
2550 | if (ok) { | |
2551 | makeInactive->setObject( client ); | |
2552 | } | |
2553 | } | |
2554 | iter->release(); | |
2555 | } | |
2556 | } | |
2557 | victim->release(); | |
2558 | victim = (IOService *) makeInactive->getObject(0); | |
2559 | if (victim) { | |
2560 | victim->retain(); | |
2561 | makeInactive->removeObject(0); | |
2562 | } | |
2563 | } | |
2564 | makeInactive->release(); | |
2565 | ||
2566 | while ((victim = (IOService *) waitingInactive->getObject(0))) { | |
2567 | victim->retain(); | |
2568 | waitingInactive->removeObject(0); | |
2569 | ||
2570 | victim->lockForArbitration(); | |
2571 | victim->__state[1] &= ~kIOServiceTermPhase1State; | |
2572 | if (kIOServiceTerm1WaiterState & victim->__state[1]) { | |
2573 | victim->__state[1] &= ~kIOServiceTerm1WaiterState; | |
2574 | TLOG("%s[0x%qx]::wakePhase1\n", victim->getName(), victim->getRegistryEntryID()); | |
2575 | IOLockLock( gIOServiceBusyLock ); | |
2576 | thread_wakeup((event_t) &victim->__state[1]); | |
2577 | IOLockUnlock( gIOServiceBusyLock ); | |
2578 | } | |
2579 | victim->unlockForArbitration(); | |
2580 | victim->release(); | |
2581 | } | |
2582 | waitingInactive->release(); | |
2583 | ||
2584 | if (startPhase2) { | |
2585 | retain(); | |
2586 | lockForArbitration(); | |
2587 | scheduleTerminatePhase2(options); | |
2588 | unlockForArbitration(); | |
2589 | release(); | |
2590 | } | |
fe8ab488 | 2591 | |
cb323159 A |
2592 | if (rematchProvider) { |
2593 | DKLOG(DKS " rematching after dext crash\n", DKN(rematchProvider)); | |
2594 | rematchProvider->registerService(); | |
2595 | } | |
2596 | ||
0a7de745 | 2597 | return true; |
1c79356b A |
2598 | } |
2599 | ||
0a7de745 A |
2600 | void |
2601 | IOService::setTerminateDefer(IOService * provider, bool defer) | |
39236c6e | 2602 | { |
0a7de745 A |
2603 | lockForArbitration(); |
2604 | if (defer) { | |
2605 | __state[1] |= kIOServiceStartState; | |
2606 | } else { | |
2607 | __state[1] &= ~kIOServiceStartState; | |
2608 | } | |
2609 | unlockForArbitration(); | |
39236c6e | 2610 | |
0a7de745 A |
2611 | if (provider && !defer) { |
2612 | provider->lockForArbitration(); | |
2613 | provider->scheduleTerminatePhase2(); | |
2614 | provider->unlockForArbitration(); | |
2615 | } | |
39236c6e A |
2616 | } |
2617 | ||
5c9f4661 | 2618 | // Must call this while holding gJobsLock |
0a7de745 A |
2619 | void |
2620 | IOService::waitToBecomeTerminateThread(void) | |
2621 | { | |
2622 | IOLockAssert(gJobsLock, kIOLockAssertOwned); | |
2623 | bool wait; | |
2624 | do { | |
2625 | wait = (gIOTerminateThread != THREAD_NULL); | |
2626 | if (wait) { | |
2627 | IOLockSleep(gJobsLock, &gIOTerminateThread, THREAD_UNINT); | |
2628 | } | |
2629 | } while (wait); | |
2630 | gIOTerminateThread = current_thread(); | |
5c9f4661 A |
2631 | } |
2632 | ||
fe8ab488 | 2633 | // call with lockForArbitration |
0a7de745 A |
2634 | void |
2635 | IOService::scheduleTerminatePhase2( IOOptionBits options ) | |
2636 | { | |
2637 | AbsoluteTime deadline; | |
2638 | uint64_t regID1; | |
2639 | int waitResult = THREAD_AWAKENED; | |
2640 | bool wait = false, haveDeadline = false; | |
2641 | ||
2642 | if (!(__state[0] & kIOServiceInactiveState)) { | |
2643 | return; | |
2644 | } | |
2645 | ||
2646 | regID1 = getRegistryEntryID(); | |
2647 | IOServiceTrace( | |
2648 | IOSERVICE_TERM_SCHED_PHASE2, | |
2649 | (uintptr_t) regID1, | |
2650 | (uintptr_t) (regID1 >> 32), | |
2651 | (uintptr_t) __state[1], | |
2652 | (uintptr_t) options); | |
2653 | ||
2654 | if (__state[1] & kIOServiceTermPhase1State) { | |
2655 | return; | |
2656 | } | |
2657 | ||
2658 | retain(); | |
2659 | unlockForArbitration(); | |
2660 | options |= kIOServiceRequired; | |
2661 | IOLockLock( gJobsLock ); | |
2662 | ||
2663 | if ((options & kIOServiceSynchronous) | |
2664 | && (current_thread() != gIOTerminateThread)) { | |
2665 | waitToBecomeTerminateThread(); | |
2666 | gIOTerminatePhase2List->setObject( this ); | |
2667 | gIOTerminateWork++; | |
2668 | ||
2669 | do { | |
2670 | while (gIOTerminateWork) { | |
2671 | terminateWorker( options ); | |
2672 | } | |
2673 | wait = (0 != (__state[1] & kIOServiceBusyStateMask)); | |
2674 | if (wait) { | |
2675 | /* wait for the victim to go non-busy */ | |
2676 | if (!haveDeadline) { | |
2677 | clock_interval_to_deadline( 15, kSecondScale, &deadline ); | |
2678 | haveDeadline = true; | |
2679 | } | |
2680 | /* let others do work while we wait */ | |
cb323159 | 2681 | gIOTerminateThread = NULL; |
0a7de745 A |
2682 | IOLockWakeup( gJobsLock, (event_t) &gIOTerminateThread, /* one-thread */ false); |
2683 | waitResult = IOLockSleepDeadline( gJobsLock, &gIOTerminateWork, | |
2684 | deadline, THREAD_UNINT ); | |
2685 | if (__improbable(waitResult == THREAD_TIMED_OUT)) { | |
cb323159 A |
2686 | IOLog("%s[0x%qx]::terminate(kIOServiceSynchronous): THREAD_TIMED_OUT. " |
2687 | "Attempting to auto-resolve your deadlock. PLEASE FIX!\n", getName(), getRegistryEntryID()); | |
0a7de745 A |
2688 | } |
2689 | waitToBecomeTerminateThread(); | |
2690 | } | |
2691 | } while (gIOTerminateWork || (wait && (waitResult != THREAD_TIMED_OUT))); | |
2692 | ||
cb323159 | 2693 | gIOTerminateThread = NULL; |
0a7de745 A |
2694 | IOLockWakeup( gJobsLock, (event_t) &gIOTerminateThread, /* one-thread */ false); |
2695 | } else { | |
2696 | // ! kIOServiceSynchronous | |
2697 | ||
2698 | gIOTerminatePhase2List->setObject( this ); | |
2699 | if (0 == gIOTerminateWork++) { | |
2700 | assert(gIOTerminateWorkerThread); | |
2701 | IOLockWakeup(gJobsLock, (event_t)&gIOTerminateWork, /* one-thread */ false ); | |
2702 | } | |
2703 | } | |
2704 | ||
2705 | IOLockUnlock( gJobsLock ); | |
2706 | lockForArbitration(); | |
2707 | release(); | |
1c79356b A |
2708 | } |
2709 | ||
5c9f4661 | 2710 | __attribute__((__noreturn__)) |
0a7de745 A |
2711 | void |
2712 | IOService::terminateThread( void * arg, wait_result_t waitResult ) | |
2713 | { | |
2714 | // IOLockSleep re-acquires the lock on wakeup, so we only need to do this once | |
2715 | IOLockLock(gJobsLock); | |
2716 | while (true) { | |
2717 | if (gIOTerminateThread != gIOTerminateWorkerThread) { | |
2718 | waitToBecomeTerminateThread(); | |
2719 | } | |
1c79356b | 2720 | |
0a7de745 | 2721 | while (gIOTerminateWork) { |
f427ee49 | 2722 | terminateWorker((IOOptionBits)(uintptr_t)arg ); |
0a7de745 | 2723 | } |
1c79356b | 2724 | |
cb323159 | 2725 | gIOTerminateThread = NULL; |
0a7de745 A |
2726 | IOLockWakeup( gJobsLock, (event_t) &gIOTerminateThread, /* one-thread */ false); |
2727 | IOLockSleep(gJobsLock, &gIOTerminateWork, THREAD_UNINT); | |
2728 | } | |
0b4e3aa0 | 2729 | } |
1c79356b | 2730 | |
0a7de745 A |
2731 | void |
2732 | IOService::scheduleStop( IOService * provider ) | |
0b4e3aa0 | 2733 | { |
0a7de745 A |
2734 | uint64_t regID1 = getRegistryEntryID(); |
2735 | uint64_t regID2 = provider->getRegistryEntryID(); | |
fe8ab488 | 2736 | |
0a7de745 A |
2737 | TLOG("%s[0x%qx]::scheduleStop(%s[0x%qx])\n", getName(), regID1, provider->getName(), regID2); |
2738 | IOServiceTrace( | |
2739 | IOSERVICE_TERMINATE_SCHEDULE_STOP, | |
2740 | (uintptr_t) regID1, | |
2741 | (uintptr_t) (regID1 >> 32), | |
2742 | (uintptr_t) regID2, | |
2743 | (uintptr_t) (regID2 >> 32)); | |
b0d623f7 | 2744 | |
0a7de745 A |
2745 | IOLockLock( gJobsLock ); |
2746 | gIOStopList->tailQ( this ); | |
2747 | gIOStopProviderList->tailQ( provider ); | |
0b4e3aa0 | 2748 | |
0a7de745 A |
2749 | if (0 == gIOTerminateWork++) { |
2750 | assert(gIOTerminateWorkerThread); | |
2751 | IOLockWakeup(gJobsLock, (event_t)&gIOTerminateWork, /* one-thread */ false ); | |
2752 | } | |
1c79356b | 2753 | |
0a7de745 | 2754 | IOLockUnlock( gJobsLock ); |
0b4e3aa0 | 2755 | } |
1c79356b | 2756 | |
0a7de745 A |
2757 | void |
2758 | IOService::scheduleFinalize(bool now) | |
0b4e3aa0 | 2759 | { |
0a7de745 | 2760 | uint64_t regID1 = getRegistryEntryID(); |
b0d623f7 | 2761 | |
0a7de745 A |
2762 | TLOG("%s[0x%qx]::scheduleFinalize\n", getName(), regID1); |
2763 | IOServiceTrace( | |
2764 | IOSERVICE_TERMINATE_SCHEDULE_FINALIZE, | |
2765 | (uintptr_t) regID1, | |
2766 | (uintptr_t) (regID1 >> 32), | |
2767 | 0, 0); | |
2768 | ||
2769 | if (now || IOUserClient::finalizeUserReferences(this)) { | |
2770 | IOLockLock( gJobsLock ); | |
2771 | gIOFinalizeList->tailQ(this); | |
2772 | if (0 == gIOTerminateWork++) { | |
2773 | assert(gIOTerminateWorkerThread); | |
2774 | IOLockWakeup(gJobsLock, (event_t)&gIOTerminateWork, /* one-thread */ false ); | |
2775 | } | |
2776 | IOLockUnlock( gJobsLock ); | |
490019cf | 2777 | } |
0b4e3aa0 | 2778 | } |
1c79356b | 2779 | |
0a7de745 A |
2780 | bool |
2781 | IOService::willTerminate( IOService * provider, IOOptionBits options ) | |
0b4e3aa0 | 2782 | { |
cb323159 A |
2783 | if (reserved->uvars) { |
2784 | IOUserServer::serviceWillTerminate(this, provider, options); | |
2785 | } | |
0a7de745 | 2786 | return true; |
0b4e3aa0 | 2787 | } |
1c79356b | 2788 | |
0a7de745 A |
2789 | bool |
2790 | IOService::didTerminate( IOService * provider, IOOptionBits options, bool * defer ) | |
0b4e3aa0 | 2791 | { |
cb323159 A |
2792 | if (reserved->uvars) { |
2793 | IOUserServer::serviceDidTerminate(this, provider, options, defer); | |
2794 | } | |
2795 | ||
0a7de745 A |
2796 | if (false == *defer) { |
2797 | if (lockForArbitration( true )) { | |
2798 | if (false == provider->handleIsOpen( this )) { | |
2799 | scheduleStop( provider ); | |
2800 | } | |
2801 | // -- compat | |
2802 | else { | |
2803 | message( kIOMessageServiceIsRequestingClose, provider, (void *)(uintptr_t) options ); | |
2804 | if (false == provider->handleIsOpen( this )) { | |
2805 | scheduleStop( provider ); | |
2806 | } | |
2807 | } | |
2808 | // -- | |
2809 | unlockForArbitration(); | |
2810 | } | |
2811 | } | |
2812 | ||
2813 | return true; | |
2814 | } | |
0b4e3aa0 | 2815 | |
0a7de745 A |
2816 | void |
2817 | IOService::actionWillTerminate( IOService * victim, IOOptionBits options, | |
2818 | OSArray * doPhase2List, | |
cb323159 A |
2819 | bool user, |
2820 | void *unused3 __unused) | |
0a7de745 A |
2821 | { |
2822 | OSIterator * iter; | |
2823 | IOService * client; | |
2824 | bool ok; | |
2825 | uint64_t regID1, regID2 = victim->getRegistryEntryID(); | |
1c79356b | 2826 | |
0a7de745 A |
2827 | iter = victim->getClientIterator(); |
2828 | if (iter) { | |
2829 | while ((client = (IOService *) iter->getNextObject())) { | |
cb323159 A |
2830 | if (user != (NULL != client->reserved->uvars)) { |
2831 | continue; | |
2832 | } | |
0a7de745 A |
2833 | regID1 = client->getRegistryEntryID(); |
2834 | TLOG("%s[0x%qx]::willTerminate(%s[0x%qx], %08llx)\n", | |
2835 | client->getName(), regID1, | |
2836 | victim->getName(), regID2, (long long)options); | |
2837 | IOServiceTrace( | |
2838 | IOSERVICE_TERMINATE_WILL, | |
2839 | (uintptr_t) regID1, | |
2840 | (uintptr_t) (regID1 >> 32), | |
2841 | (uintptr_t) regID2, | |
2842 | (uintptr_t) (regID2 >> 32)); | |
2843 | ||
2844 | ok = client->willTerminate( victim, options ); | |
2845 | doPhase2List->tailQ( client ); | |
2846 | } | |
2847 | iter->release(); | |
2848 | } | |
0b4e3aa0 | 2849 | } |
1c79356b | 2850 | |
0a7de745 A |
2851 | void |
2852 | IOService::actionDidTerminate( IOService * victim, IOOptionBits options, | |
2853 | void *unused1 __unused, void *unused2 __unused, | |
2854 | void *unused3 __unused ) | |
0b4e3aa0 | 2855 | { |
0a7de745 A |
2856 | OSIterator * iter; |
2857 | IOService * client; | |
2858 | bool defer; | |
2859 | uint64_t regID1, regID2 = victim->getRegistryEntryID(); | |
0b4e3aa0 | 2860 | |
0a7de745 | 2861 | victim->messageClients( kIOMessageServiceIsTerminated, (void *)(uintptr_t) options ); |
b0d623f7 | 2862 | |
0a7de745 A |
2863 | iter = victim->getClientIterator(); |
2864 | if (iter) { | |
2865 | while ((client = (IOService *) iter->getNextObject())) { | |
2866 | regID1 = client->getRegistryEntryID(); | |
2867 | TLOG("%s[0x%qx]::didTerminate(%s[0x%qx], %08llx)\n", | |
2868 | client->getName(), regID1, | |
2869 | victim->getName(), regID2, (long long)options); | |
2870 | defer = false; | |
2871 | client->didTerminate( victim, options, &defer ); | |
2872 | ||
2873 | IOServiceTrace( | |
2874 | (defer ? IOSERVICE_TERMINATE_DID_DEFER | |
2875 | : IOSERVICE_TERMINATE_DID), | |
2876 | (uintptr_t) regID1, | |
2877 | (uintptr_t) (regID1 >> 32), | |
2878 | (uintptr_t) regID2, | |
2879 | (uintptr_t) (regID2 >> 32)); | |
2880 | ||
2881 | TLOG("%s[0x%qx]::didTerminate(%s[0x%qx], defer %d)\n", | |
2882 | client->getName(), regID1, | |
2883 | victim->getName(), regID2, defer); | |
2884 | } | |
2885 | iter->release(); | |
2886 | } | |
2887 | } | |
2888 | ||
2889 | ||
2890 | void | |
2891 | IOService::actionWillStop( IOService * victim, IOOptionBits options, | |
2892 | void *unused1 __unused, void *unused2 __unused, | |
2893 | void *unused3 __unused ) | |
2894 | { | |
2895 | OSIterator * iter; | |
2896 | IOService * provider; | |
2897 | bool ok; | |
2898 | uint64_t regID1, regID2 = victim->getRegistryEntryID(); | |
b0d623f7 | 2899 | |
0a7de745 A |
2900 | iter = victim->getProviderIterator(); |
2901 | if (iter) { | |
2902 | while ((provider = (IOService *) iter->getNextObject())) { | |
2903 | regID1 = provider->getRegistryEntryID(); | |
2904 | TLOG("%s[0x%qx]::willTerminate(%s[0x%qx], %08llx)\n", | |
2905 | victim->getName(), regID2, | |
2906 | provider->getName(), regID1, (long long)options); | |
2907 | IOServiceTrace( | |
2908 | IOSERVICE_TERMINATE_WILL, | |
2909 | (uintptr_t) regID2, | |
2910 | (uintptr_t) (regID2 >> 32), | |
2911 | (uintptr_t) regID1, | |
2912 | (uintptr_t) (regID1 >> 32)); | |
2913 | ||
2914 | ok = victim->willTerminate( provider, options ); | |
2915 | } | |
2916 | iter->release(); | |
2917 | } | |
1c79356b A |
2918 | } |
2919 | ||
0a7de745 A |
2920 | void |
2921 | IOService::actionDidStop( IOService * victim, IOOptionBits options, | |
2922 | void *unused1 __unused, void *unused2 __unused, | |
2923 | void *unused3 __unused ) | |
1c79356b | 2924 | { |
0a7de745 A |
2925 | OSIterator * iter; |
2926 | IOService * provider; | |
2927 | bool defer = false; | |
2928 | uint64_t regID1, regID2 = victim->getRegistryEntryID(); | |
1c79356b | 2929 | |
0a7de745 A |
2930 | iter = victim->getProviderIterator(); |
2931 | if (iter) { | |
2932 | while ((provider = (IOService *) iter->getNextObject())) { | |
2933 | regID1 = provider->getRegistryEntryID(); | |
2934 | TLOG("%s[0x%qx]::didTerminate(%s[0x%qx], %08llx)\n", | |
2935 | victim->getName(), regID2, | |
2936 | provider->getName(), regID1, (long long)options); | |
2937 | victim->didTerminate( provider, options, &defer ); | |
1c79356b | 2938 | |
0a7de745 A |
2939 | IOServiceTrace( |
2940 | (defer ? IOSERVICE_TERMINATE_DID_DEFER | |
2941 | : IOSERVICE_TERMINATE_DID), | |
2942 | (uintptr_t) regID2, | |
2943 | (uintptr_t) (regID2 >> 32), | |
2944 | (uintptr_t) regID1, | |
2945 | (uintptr_t) (regID1 >> 32)); | |
2946 | ||
2947 | TLOG("%s[0x%qx]::didTerminate(%s[0x%qx], defer %d)\n", | |
2948 | victim->getName(), regID2, | |
2949 | provider->getName(), regID1, defer); | |
2950 | } | |
2951 | iter->release(); | |
2952 | } | |
2953 | } | |
fe8ab488 | 2954 | |
b0d623f7 | 2955 | |
0a7de745 A |
2956 | void |
2957 | IOService::actionFinalize( IOService * victim, IOOptionBits options, | |
2958 | void *unused1 __unused, void *unused2 __unused, | |
2959 | void *unused3 __unused ) | |
2960 | { | |
2961 | uint64_t regID1 = victim->getRegistryEntryID(); | |
2962 | TLOG("%s[0x%qx]::finalize(%08llx)\n", victim->getName(), regID1, (long long)options); | |
2963 | IOServiceTrace( | |
2964 | IOSERVICE_TERMINATE_FINALIZE, | |
2965 | (uintptr_t) regID1, | |
b0d623f7 | 2966 | (uintptr_t) (regID1 >> 32), |
0a7de745 A |
2967 | 0, 0); |
2968 | ||
2969 | victim->finalize( options ); | |
2970 | } | |
b0d623f7 | 2971 | |
0a7de745 A |
2972 | void |
2973 | IOService::actionStop( IOService * provider, IOService * client, | |
2974 | void *unused1 __unused, void *unused2 __unused, | |
2975 | void *unused3 __unused ) | |
0b4e3aa0 | 2976 | { |
0a7de745 A |
2977 | uint64_t regID1 = provider->getRegistryEntryID(); |
2978 | uint64_t regID2 = client->getRegistryEntryID(); | |
fe8ab488 | 2979 | |
0a7de745 A |
2980 | TLOG("%s[0x%qx]::stop(%s[0x%qx])\n", client->getName(), regID2, provider->getName(), regID1); |
2981 | IOServiceTrace( | |
2982 | IOSERVICE_TERMINATE_STOP, | |
3e170ce0 A |
2983 | (uintptr_t) regID1, |
2984 | (uintptr_t) (regID1 >> 32), | |
0a7de745 A |
2985 | (uintptr_t) regID2, |
2986 | (uintptr_t) (regID2 >> 32)); | |
3e170ce0 | 2987 | |
0a7de745 A |
2988 | client->stop( provider ); |
2989 | if (provider->isOpen( client )) { | |
2990 | provider->close( client ); | |
2991 | } | |
2992 | ||
2993 | TLOG("%s[0x%qx]::detach(%s[0x%qx])\n", client->getName(), regID2, provider->getName(), regID1); | |
2994 | client->detach( provider ); | |
2995 | } | |
2996 | ||
2997 | void | |
2998 | IOService::terminateWorker( IOOptionBits options ) | |
2999 | { | |
3000 | OSArray * doPhase2List; | |
3001 | OSArray * didPhase2List; | |
3002 | OSSet * freeList; | |
3003 | OSIterator * iter; | |
3004 | UInt32 workDone; | |
3005 | IOService * victim; | |
3006 | IOService * client; | |
3007 | IOService * provider; | |
3008 | unsigned int idx; | |
3009 | bool moreToDo; | |
3010 | bool doPhase2; | |
3011 | bool doPhase3; | |
3012 | ||
3013 | options |= kIOServiceRequired; | |
3014 | ||
3015 | doPhase2List = OSArray::withCapacity( 16 ); | |
3016 | didPhase2List = OSArray::withCapacity( 16 ); | |
3017 | freeList = OSSet::withCapacity( 16 ); | |
cb323159 | 3018 | if ((NULL == doPhase2List) || (NULL == didPhase2List) || (NULL == freeList)) { |
0a7de745 A |
3019 | return; |
3020 | } | |
3021 | ||
3022 | do { | |
3023 | workDone = gIOTerminateWork; | |
3024 | ||
3025 | while ((victim = (IOService *) gIOTerminatePhase2List->getObject(0))) { | |
3026 | victim->retain(); | |
3027 | gIOTerminatePhase2List->removeObject(0); | |
3028 | IOLockUnlock( gJobsLock ); | |
3e170ce0 A |
3029 | |
3030 | uint64_t regID1 = victim->getRegistryEntryID(); | |
3031 | IOServiceTrace( | |
0a7de745 A |
3032 | IOSERVICE_TERM_START_PHASE2, |
3033 | (uintptr_t) regID1, | |
3034 | (uintptr_t) (regID1 >> 32), | |
3035 | (uintptr_t) 0, | |
3036 | (uintptr_t) 0); | |
3037 | ||
3038 | while (victim) { | |
3039 | doPhase2 = victim->lockForArbitration( true ); | |
3040 | if (doPhase2) { | |
3041 | doPhase2 = (0 != (kIOServiceInactiveState & victim->__state[0])); | |
3042 | if (doPhase2) { | |
3043 | uint64_t regID1 = victim->getRegistryEntryID(); | |
3044 | IOServiceTrace( | |
3045 | IOSERVICE_TERM_TRY_PHASE2, | |
3046 | (uintptr_t) regID1, | |
3047 | (uintptr_t) (regID1 >> 32), | |
3048 | (uintptr_t) victim->__state[1], | |
3049 | (uintptr_t) 0); | |
3050 | ||
3051 | doPhase2 = (0 == (victim->__state[1] & | |
3052 | (kIOServiceTermPhase1State | |
3053 | | kIOServiceTermPhase2State | |
3054 | | kIOServiceConfigState))); | |
3055 | ||
3056 | if (doPhase2 && (iter = victim->getClientIterator())) { | |
3057 | while (doPhase2 && (client = (IOService *) iter->getNextObject())) { | |
3058 | doPhase2 = (0 == (client->__state[1] & kIOServiceStartState)); | |
3059 | if (!doPhase2) { | |
3060 | uint64_t regID1 = client->getRegistryEntryID(); | |
3061 | IOServiceTrace( | |
3062 | IOSERVICE_TERM_UC_DEFER, | |
3063 | (uintptr_t) regID1, | |
3064 | (uintptr_t) (regID1 >> 32), | |
3065 | (uintptr_t) client->__state[1], | |
3066 | (uintptr_t) 0); | |
3067 | TLOG("%s[0x%qx]::defer phase2(%s[0x%qx])\n", | |
3068 | victim->getName(), victim->getRegistryEntryID(), | |
3069 | client->getName(), client->getRegistryEntryID()); | |
3070 | } | |
3071 | } | |
3072 | iter->release(); | |
3073 | } | |
3074 | if (doPhase2) { | |
3075 | victim->__state[1] |= kIOServiceTermPhase2State; | |
3076 | } | |
3077 | } | |
3078 | victim->unlockForArbitration(); | |
3079 | } | |
3080 | if (doPhase2) { | |
3081 | if (kIOServiceNeedWillTerminate & victim->__state[1]) { | |
94ff46dc A |
3082 | if (NULL == victim->reserved->uvars) { |
3083 | _workLoopAction((IOWorkLoop::Action) &actionWillStop, | |
3084 | victim, (void *)(uintptr_t) options); | |
3085 | } else { | |
3086 | actionWillStop(victim, options, NULL, NULL, NULL); | |
3087 | } | |
0a7de745 | 3088 | } |
3e170ce0 | 3089 | |
0a7de745 A |
3090 | OSArray * notifiers; |
3091 | notifiers = victim->copyNotifiers(gIOWillTerminateNotification, 0, 0xffffffff); | |
3092 | victim->invokeNotifiers(¬ifiers); | |
39236c6e | 3093 | |
0a7de745 | 3094 | _workLoopAction((IOWorkLoop::Action) &actionWillTerminate, |
cb323159 A |
3095 | victim, |
3096 | (void *)(uintptr_t) options, | |
3097 | (void *)(uintptr_t) doPhase2List, | |
3098 | (void *)(uintptr_t) false); | |
3099 | ||
3100 | actionWillTerminate( | |
3101 | victim, options, doPhase2List, true, NULL); | |
0a7de745 A |
3102 | |
3103 | didPhase2List->headQ( victim ); | |
3104 | } | |
3105 | victim->release(); | |
3106 | victim = (IOService *) doPhase2List->getObject(0); | |
3107 | if (victim) { | |
3108 | victim->retain(); | |
3109 | doPhase2List->removeObject(0); | |
3110 | } | |
3111 | } | |
3112 | ||
3113 | while ((victim = (IOService *) didPhase2List->getObject(0))) { | |
3114 | bool scheduleFinalize = false; | |
3115 | if (victim->lockForArbitration( true )) { | |
3116 | victim->__state[1] |= kIOServiceTermPhase3State; | |
cb323159 | 3117 | scheduleFinalize = (NULL == victim->getClient()); |
0a7de745 A |
3118 | victim->unlockForArbitration(); |
3119 | } | |
3120 | _workLoopAction((IOWorkLoop::Action) &actionDidTerminate, | |
3121 | victim, (void *)(uintptr_t) options ); | |
3122 | if (kIOServiceNeedWillTerminate & victim->__state[1]) { | |
3123 | _workLoopAction((IOWorkLoop::Action) &actionDidStop, | |
3124 | victim, (void *)(uintptr_t) options, NULL ); | |
3125 | } | |
3126 | // no clients - will go to finalize | |
3127 | if (scheduleFinalize) { | |
3128 | victim->scheduleFinalize(false); | |
3129 | } | |
3130 | didPhase2List->removeObject(0); | |
3131 | } | |
3132 | IOLockLock( gJobsLock ); | |
3133 | } | |
3134 | ||
3135 | // phase 3 | |
3136 | do { | |
3137 | doPhase3 = false; | |
3138 | // finalize leaves | |
3139 | while ((victim = (IOService *) gIOFinalizeList->getObject(0))) { | |
3140 | bool sendFinal = false; | |
3141 | IOLockUnlock( gJobsLock ); | |
3142 | if (victim->lockForArbitration(true)) { | |
3143 | sendFinal = (0 == (victim->__state[1] & kIOServiceFinalized)); | |
3144 | if (sendFinal) { | |
3145 | victim->__state[1] |= kIOServiceFinalized; | |
3146 | } | |
3147 | victim->unlockForArbitration(); | |
3148 | } | |
3149 | if (sendFinal) { | |
3150 | _workLoopAction((IOWorkLoop::Action) &actionFinalize, | |
3151 | victim, (void *)(uintptr_t) options ); | |
3152 | } | |
3153 | IOLockLock( gJobsLock ); | |
3154 | // hold off free | |
3155 | freeList->setObject( victim ); | |
3156 | // safe if finalize list is append only | |
3157 | gIOFinalizeList->removeObject(0); | |
3158 | } | |
3159 | ||
3160 | for (idx = 0; | |
3161 | (!doPhase3) && (client = (IOService *) gIOStopList->getObject(idx));) { | |
3162 | provider = (IOService *) gIOStopProviderList->getObject(idx); | |
3163 | assert( provider ); | |
3164 | ||
3165 | uint64_t regID1 = provider->getRegistryEntryID(); | |
3166 | uint64_t regID2 = client->getRegistryEntryID(); | |
3167 | ||
3168 | if (!provider->isChild( client, gIOServicePlane )) { | |
3169 | // may be multiply queued - nop it | |
3170 | TLOG("%s[0x%qx]::nop stop(%s[0x%qx])\n", client->getName(), regID2, provider->getName(), regID1); | |
3171 | IOServiceTrace( | |
3172 | IOSERVICE_TERMINATE_STOP_NOP, | |
3173 | (uintptr_t) regID1, | |
3174 | (uintptr_t) (regID1 >> 32), | |
3175 | (uintptr_t) regID2, | |
3176 | (uintptr_t) (regID2 >> 32)); | |
3177 | } else { | |
3178 | // a terminated client is not ready for stop if it has clients, skip it | |
3179 | bool deferStop = (0 != (kIOServiceInactiveState & client->__state[0])); | |
3180 | IOLockUnlock( gJobsLock ); | |
3181 | if (deferStop && client->lockForArbitration(true)) { | |
3182 | deferStop = (0 == (client->__state[1] & kIOServiceFinalized)); | |
3183 | //deferStop = (!deferStop && (0 != client->getClient())); | |
3184 | //deferStop = (0 != client->getClient()); | |
3185 | client->unlockForArbitration(); | |
3186 | if (deferStop) { | |
3187 | TLOG("%s[0x%qx]::defer stop()\n", client->getName(), regID2); | |
3188 | IOServiceTrace(IOSERVICE_TERMINATE_STOP_DEFER, | |
3189 | (uintptr_t) regID1, | |
3190 | (uintptr_t) (regID1 >> 32), | |
3191 | (uintptr_t) regID2, | |
3192 | (uintptr_t) (regID2 >> 32)); | |
3193 | ||
3194 | idx++; | |
3195 | IOLockLock( gJobsLock ); | |
3196 | continue; | |
3197 | } | |
3198 | } | |
3199 | _workLoopAction((IOWorkLoop::Action) &actionStop, | |
3200 | provider, (void *) client ); | |
3201 | IOLockLock( gJobsLock ); | |
3202 | // check the finalize list now | |
3203 | doPhase3 = true; | |
3204 | } | |
3205 | // hold off free | |
3206 | freeList->setObject( client ); | |
3207 | freeList->setObject( provider ); | |
3208 | ||
3209 | // safe if stop list is append only | |
3210 | gIOStopList->removeObject( idx ); | |
3211 | gIOStopProviderList->removeObject( idx ); | |
3212 | idx = 0; | |
3213 | } | |
3214 | } while (doPhase3); | |
3215 | ||
3216 | gIOTerminateWork -= workDone; | |
3217 | moreToDo = (gIOTerminateWork != 0); | |
3218 | ||
3219 | if (!moreToDo) { | |
3220 | TLOG("iokit terminate done, %d stops remain\n", gIOStopList->getCount()); | |
3221 | IOServiceTrace( | |
3222 | IOSERVICE_TERMINATE_DONE, | |
3223 | (uintptr_t) gIOStopList->getCount(), 0, 0, 0); | |
3224 | } | |
3225 | } while (moreToDo); | |
3226 | ||
3227 | IOLockUnlock( gJobsLock ); | |
3228 | ||
3229 | freeList->release(); | |
3230 | doPhase2List->release(); | |
3231 | didPhase2List->release(); | |
3232 | ||
3233 | IOLockLock( gJobsLock ); | |
3234 | } | |
3235 | ||
3236 | bool | |
3237 | IOService::finalize( IOOptionBits options ) | |
3238 | { | |
3239 | OSIterator * iter; | |
3240 | IOService * provider; | |
3241 | uint64_t regID1, regID2 = getRegistryEntryID(); | |
3242 | ||
3243 | iter = getProviderIterator(); | |
3244 | assert( iter ); | |
3245 | ||
3246 | if (iter) { | |
3247 | while ((provider = (IOService *) iter->getNextObject())) { | |
3248 | // -- compat | |
3249 | if (0 == (__state[1] & kIOServiceTermPhase3State)) { | |
3250 | /* we come down here on programmatic terminate */ | |
3251 | ||
3252 | regID1 = provider->getRegistryEntryID(); | |
3253 | TLOG("%s[0x%qx]::stop1(%s[0x%qx])\n", getName(), regID2, provider->getName(), regID1); | |
3254 | IOServiceTrace( | |
3255 | IOSERVICE_TERMINATE_STOP, | |
3e170ce0 A |
3256 | (uintptr_t) regID1, |
3257 | (uintptr_t) (regID1 >> 32), | |
0a7de745 A |
3258 | (uintptr_t) regID2, |
3259 | (uintptr_t) (regID2 >> 32)); | |
3260 | ||
3261 | stop( provider ); | |
3262 | if (provider->isOpen( this )) { | |
3263 | provider->close( this ); | |
3264 | } | |
3265 | detach( provider ); | |
3266 | } else { | |
3267 | //-- | |
3268 | if (provider->lockForArbitration( true )) { | |
3269 | if (0 == (provider->__state[1] & kIOServiceTermPhase3State)) { | |
3270 | scheduleStop( provider ); | |
3271 | } | |
3272 | provider->unlockForArbitration(); | |
3e170ce0 | 3273 | } |
39236c6e | 3274 | } |
0a7de745 A |
3275 | } |
3276 | iter->release(); | |
3277 | } | |
3278 | ||
3279 | return true; | |
1c79356b A |
3280 | } |
3281 | ||
55e303ae A |
3282 | #undef tailQ |
3283 | #undef headQ | |
0b4e3aa0 A |
3284 | |
3285 | /* | |
3286 | * Terminate | |
3287 | */ | |
3288 | ||
0a7de745 A |
3289 | void |
3290 | IOService::doServiceTerminate( IOOptionBits options ) | |
0b4e3aa0 A |
3291 | { |
3292 | } | |
3293 | ||
3294 | // a method in case someone needs to override it | |
0a7de745 A |
3295 | bool |
3296 | IOService::terminateClient( IOService * client, IOOptionBits options ) | |
0b4e3aa0 | 3297 | { |
0a7de745 | 3298 | bool ok; |
0b4e3aa0 | 3299 | |
0a7de745 A |
3300 | if (client->isParent( this, gIOServicePlane, true)) { |
3301 | // we are the clients only provider | |
3302 | ok = client->terminate( options ); | |
3303 | } else { | |
3304 | ok = true; | |
3305 | } | |
0b4e3aa0 | 3306 | |
0a7de745 | 3307 | return ok; |
0b4e3aa0 A |
3308 | } |
3309 | ||
0a7de745 A |
3310 | bool |
3311 | IOService::terminate( IOOptionBits options ) | |
0b4e3aa0 | 3312 | { |
0a7de745 | 3313 | options |= kIOServiceTerminate; |
0b4e3aa0 | 3314 | |
0a7de745 | 3315 | return terminatePhase1( options ); |
0b4e3aa0 A |
3316 | } |
3317 | ||
3318 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ | |
3319 | ||
1c79356b A |
3320 | /* |
3321 | * Open & close | |
3322 | */ | |
3323 | ||
0a7de745 A |
3324 | struct ServiceOpenMessageContext { |
3325 | IOService * service; | |
3326 | UInt32 type; | |
3327 | IOService * excludeClient; | |
3328 | IOOptionBits options; | |
0b4e3aa0 A |
3329 | }; |
3330 | ||
0a7de745 A |
3331 | static void |
3332 | serviceOpenMessageApplier( OSObject * object, void * ctx ) | |
0b4e3aa0 | 3333 | { |
0a7de745 | 3334 | ServiceOpenMessageContext * context = (ServiceOpenMessageContext *) ctx; |
0b4e3aa0 | 3335 | |
0a7de745 A |
3336 | if (object != context->excludeClient) { |
3337 | context->service->messageClient( context->type, object, (void *)(uintptr_t) context->options ); | |
3338 | } | |
0b4e3aa0 A |
3339 | } |
3340 | ||
0a7de745 A |
3341 | bool |
3342 | IOService::open( IOService * forClient, | |
3343 | IOOptionBits options, | |
3344 | void * arg ) | |
1c79356b | 3345 | { |
0a7de745 A |
3346 | bool ok; |
3347 | ServiceOpenMessageContext context; | |
0b4e3aa0 | 3348 | |
0a7de745 A |
3349 | context.service = this; |
3350 | context.type = kIOMessageServiceIsAttemptingOpen; | |
3351 | context.excludeClient = forClient; | |
3352 | context.options = options; | |
0b4e3aa0 | 3353 | |
0a7de745 A |
3354 | applyToInterested( gIOGeneralInterest, |
3355 | &serviceOpenMessageApplier, &context ); | |
1c79356b | 3356 | |
0a7de745 A |
3357 | if (false == lockForArbitration(false)) { |
3358 | return false; | |
3359 | } | |
1c79356b | 3360 | |
0a7de745 A |
3361 | ok = (0 == (__state[0] & kIOServiceInactiveState)); |
3362 | if (ok) { | |
3363 | ok = handleOpen( forClient, options, arg ); | |
3364 | } | |
1c79356b | 3365 | |
cb323159 A |
3366 | if (ok && forClient && forClient->reserved->uvars && forClient->reserved->uvars->userServer) { |
3367 | forClient->reserved->uvars->userServer->serviceOpen(this, forClient); | |
3368 | } | |
3369 | ||
0a7de745 | 3370 | unlockForArbitration(); |
1c79356b | 3371 | |
0a7de745 | 3372 | return ok; |
1c79356b A |
3373 | } |
3374 | ||
0a7de745 A |
3375 | void |
3376 | IOService::close( IOService * forClient, | |
3377 | IOOptionBits options ) | |
1c79356b | 3378 | { |
0a7de745 A |
3379 | bool wasClosed; |
3380 | bool last = false; | |
1c79356b | 3381 | |
0a7de745 | 3382 | lockForArbitration(); |
1c79356b | 3383 | |
0a7de745 A |
3384 | wasClosed = handleIsOpen( forClient ); |
3385 | if (wasClosed) { | |
3386 | handleClose( forClient, options ); | |
3387 | last = (__state[1] & kIOServiceTermPhase3State); | |
cb323159 A |
3388 | |
3389 | if (forClient && forClient->reserved->uvars && forClient->reserved->uvars->userServer) { | |
3390 | forClient->reserved->uvars->userServer->serviceClose(this, forClient); | |
3391 | } | |
0a7de745 | 3392 | } |
1c79356b | 3393 | |
0a7de745 | 3394 | unlockForArbitration(); |
1c79356b | 3395 | |
0a7de745 A |
3396 | if (last) { |
3397 | forClient->scheduleStop( this ); | |
3398 | } else if (wasClosed) { | |
3399 | ServiceOpenMessageContext context; | |
1c79356b | 3400 | |
0a7de745 A |
3401 | context.service = this; |
3402 | context.type = kIOMessageServiceWasClosed; | |
3403 | context.excludeClient = forClient; | |
3404 | context.options = options; | |
1c79356b | 3405 | |
0a7de745 A |
3406 | applyToInterested( gIOGeneralInterest, |
3407 | &serviceOpenMessageApplier, &context ); | |
3408 | } | |
1c79356b A |
3409 | } |
3410 | ||
0a7de745 A |
3411 | bool |
3412 | IOService::isOpen( const IOService * forClient ) const | |
1c79356b | 3413 | { |
0a7de745 A |
3414 | IOService * self = (IOService *) this; |
3415 | bool ok; | |
1c79356b | 3416 | |
0a7de745 | 3417 | self->lockForArbitration(); |
1c79356b | 3418 | |
0a7de745 | 3419 | ok = handleIsOpen( forClient ); |
1c79356b | 3420 | |
0a7de745 | 3421 | self->unlockForArbitration(); |
1c79356b | 3422 | |
0a7de745 | 3423 | return ok; |
1c79356b A |
3424 | } |
3425 | ||
0a7de745 A |
3426 | bool |
3427 | IOService::handleOpen( IOService * forClient, | |
3428 | IOOptionBits options, | |
3429 | void * arg ) | |
1c79356b | 3430 | { |
0a7de745 | 3431 | bool ok; |
1c79356b | 3432 | |
cb323159 | 3433 | ok = (NULL == __owner); |
0a7de745 A |
3434 | if (ok) { |
3435 | __owner = forClient; | |
3436 | } else if (options & kIOServiceSeize) { | |
3437 | ok = (kIOReturnSuccess == messageClient( kIOMessageServiceIsRequestingClose, | |
3438 | __owner, (void *)(uintptr_t) options )); | |
cb323159 | 3439 | if (ok && (NULL == __owner)) { |
0a7de745 A |
3440 | __owner = forClient; |
3441 | } else { | |
3442 | ok = false; | |
3443 | } | |
3444 | } | |
3445 | return ok; | |
1c79356b A |
3446 | } |
3447 | ||
0a7de745 A |
3448 | void |
3449 | IOService::handleClose( IOService * forClient, | |
3450 | IOOptionBits options ) | |
1c79356b | 3451 | { |
0a7de745 | 3452 | if (__owner == forClient) { |
cb323159 | 3453 | __owner = NULL; |
0a7de745 | 3454 | } |
1c79356b A |
3455 | } |
3456 | ||
0a7de745 A |
3457 | bool |
3458 | IOService::handleIsOpen( const IOService * forClient ) const | |
1c79356b | 3459 | { |
0a7de745 A |
3460 | if (forClient) { |
3461 | return __owner == forClient; | |
3462 | } else { | |
3463 | return __owner != forClient; | |
3464 | } | |
1c79356b A |
3465 | } |
3466 | ||
3467 | /* | |
3468 | * Probing & starting | |
3469 | */ | |
0a7de745 A |
3470 | static SInt32 |
3471 | IONotifyOrdering( const OSMetaClassBase * inObj1, const OSMetaClassBase * inObj2, void * ref ) | |
1c79356b | 3472 | { |
0a7de745 A |
3473 | const _IOServiceNotifier * obj1 = (const _IOServiceNotifier *) inObj1; |
3474 | const _IOServiceNotifier * obj2 = (const _IOServiceNotifier *) inObj2; | |
3475 | SInt32 val1; | |
3476 | SInt32 val2; | |
1c79356b | 3477 | |
0a7de745 A |
3478 | val1 = 0; |
3479 | val2 = 0; | |
0a7de745 A |
3480 | if (obj1) { |
3481 | val1 = obj1->priority; | |
3482 | } | |
0a7de745 A |
3483 | if (obj2) { |
3484 | val2 = obj2->priority; | |
3485 | } | |
f427ee49 A |
3486 | if (val1 > val2) { |
3487 | return 1; | |
3488 | } | |
3489 | if (val1 < val2) { | |
3490 | return -1; | |
3491 | } | |
3492 | return 0; | |
0a7de745 A |
3493 | } |
3494 | ||
3495 | static SInt32 | |
3496 | IOServiceObjectOrder( const OSObject * entry, void * ref) | |
3497 | { | |
3498 | OSDictionary * dict; | |
3499 | IOService * service; | |
3500 | _IOServiceNotifier * notify; | |
3501 | OSSymbol * key = (OSSymbol *) ref; | |
3502 | OSNumber * offset; | |
3503 | OSObject * prop; | |
3504 | SInt32 result; | |
3505 | ||
cb323159 | 3506 | prop = NULL; |
0a7de745 A |
3507 | result = kIODefaultProbeScore; |
3508 | if ((dict = OSDynamicCast( OSDictionary, entry))) { | |
3509 | offset = OSDynamicCast(OSNumber, dict->getObject( key )); | |
3510 | } else if ((notify = OSDynamicCast( _IOServiceNotifier, entry))) { | |
3511 | return notify->priority; | |
3512 | } else if ((service = OSDynamicCast( IOService, entry))) { | |
3513 | prop = service->copyProperty(key); | |
3514 | offset = OSDynamicCast(OSNumber, prop); | |
3515 | } else { | |
3516 | assert( false ); | |
cb323159 | 3517 | offset = NULL; |
0a7de745 | 3518 | } |
1c79356b | 3519 | |
0a7de745 A |
3520 | if (offset) { |
3521 | result = offset->unsigned32BitValue(); | |
3522 | } | |
5ba3f43e | 3523 | |
0a7de745 | 3524 | OSSafeReleaseNULL(prop); |
5ba3f43e | 3525 | |
0a7de745 | 3526 | return result; |
1c79356b A |
3527 | } |
3528 | ||
0a7de745 A |
3529 | SInt32 |
3530 | IOServiceOrdering( const OSMetaClassBase * inObj1, const OSMetaClassBase * inObj2, void * ref ) | |
1c79356b | 3531 | { |
0a7de745 A |
3532 | const OSObject * obj1 = (const OSObject *) inObj1; |
3533 | const OSObject * obj2 = (const OSObject *) inObj2; | |
3534 | SInt32 val1; | |
3535 | SInt32 val2; | |
1c79356b | 3536 | |
0a7de745 A |
3537 | val1 = 0; |
3538 | val2 = 0; | |
1c79356b | 3539 | |
0a7de745 A |
3540 | if (obj1) { |
3541 | val1 = IOServiceObjectOrder( obj1, ref ); | |
3542 | } | |
1c79356b | 3543 | |
0a7de745 A |
3544 | if (obj2) { |
3545 | val2 = IOServiceObjectOrder( obj2, ref ); | |
3546 | } | |
1c79356b | 3547 | |
0a7de745 | 3548 | return val1 - val2; |
1c79356b A |
3549 | } |
3550 | ||
0a7de745 A |
3551 | IOService * |
3552 | IOService::copyClientWithCategory( const OSSymbol * category ) | |
1c79356b | 3553 | { |
cb323159 | 3554 | IOService * service = NULL; |
0a7de745 A |
3555 | OSIterator * iter; |
3556 | const OSSymbol * nextCat; | |
1c79356b | 3557 | |
0a7de745 A |
3558 | iter = getClientIterator(); |
3559 | if (iter) { | |
3560 | while ((service = (IOService *) iter->getNextObject())) { | |
3561 | if (kIOServiceInactiveState & service->__state[0]) { | |
3562 | continue; | |
3563 | } | |
3564 | nextCat = (const OSSymbol *) OSDynamicCast( OSSymbol, | |
3565 | service->getProperty( gIOMatchCategoryKey )); | |
3566 | if (category == nextCat) { | |
3567 | service->retain(); | |
3568 | break; | |
3569 | } | |
3570 | } | |
3571 | iter->release(); | |
1c79356b | 3572 | } |
0a7de745 | 3573 | return service; |
1c79356b A |
3574 | } |
3575 | ||
0a7de745 A |
3576 | IOService * |
3577 | IOService::getClientWithCategory( const OSSymbol * category ) | |
b0d623f7 | 3578 | { |
0a7de745 A |
3579 | IOService * |
3580 | service = copyClientWithCategory(category); | |
3581 | if (service) { | |
3582 | service->release(); | |
3583 | } | |
3584 | return service; | |
b0d623f7 A |
3585 | } |
3586 | ||
0a7de745 A |
3587 | bool |
3588 | IOService::invokeNotifier( _IOServiceNotifier * notify ) | |
1c79356b | 3589 | { |
0a7de745 A |
3590 | _IOServiceNotifierInvocation invocation; |
3591 | bool willNotify; | |
3592 | bool ret = true; | |
3593 | invocation.thread = current_thread(); | |
1c79356b | 3594 | |
5ba3f43e | 3595 | #if DEBUG_NOTIFIER_LOCKED |
0a7de745 A |
3596 | uint32_t count; |
3597 | if ((count = isLockedForArbitration(0))) { | |
3598 | IOLog("[%s, 0x%x]\n", notify->type->getCStringNoCopy(), count); | |
3599 | panic("[%s, 0x%x]\n", notify->type->getCStringNoCopy(), count); | |
3600 | } | |
5ba3f43e A |
3601 | #endif /* DEBUG_NOTIFIER_LOCKED */ |
3602 | ||
0a7de745 A |
3603 | LOCKWRITENOTIFY(); |
3604 | willNotify = (0 != (kIOServiceNotifyEnable & notify->state)); | |
1c79356b | 3605 | |
0a7de745 A |
3606 | if (willNotify) { |
3607 | queue_enter( ¬ify->handlerInvocations, &invocation, | |
3608 | _IOServiceNotifierInvocation *, link ); | |
3609 | } | |
3610 | UNLOCKNOTIFY(); | |
1c79356b | 3611 | |
0a7de745 A |
3612 | if (willNotify) { |
3613 | ret = (*notify->handler)(notify->target, notify->ref, this, notify); | |
1c79356b | 3614 | |
0a7de745 A |
3615 | LOCKWRITENOTIFY(); |
3616 | queue_remove( ¬ify->handlerInvocations, &invocation, | |
3617 | _IOServiceNotifierInvocation *, link ); | |
3618 | if (kIOServiceNotifyWaiter & notify->state) { | |
3619 | notify->state &= ~kIOServiceNotifyWaiter; | |
3620 | WAKEUPNOTIFY( notify ); | |
3621 | } | |
3622 | UNLOCKNOTIFY(); | |
3623 | } | |
1c79356b | 3624 | |
0a7de745 | 3625 | return ret; |
1c79356b A |
3626 | } |
3627 | ||
0a7de745 | 3628 | bool |
cb323159 | 3629 | IOService::invokeNotifiers(OSArray * willSend[]) |
5ba3f43e | 3630 | { |
0a7de745 A |
3631 | OSArray * array; |
3632 | _IOServiceNotifier * notify; | |
3633 | bool ret = true; | |
5ba3f43e | 3634 | |
0a7de745 A |
3635 | array = *willSend; |
3636 | if (!array) { | |
3637 | return true; | |
3638 | } | |
cb323159 | 3639 | *willSend = NULL; |
5ba3f43e | 3640 | |
0a7de745 A |
3641 | for (unsigned int idx = 0; |
3642 | (notify = (_IOServiceNotifier *) array->getObject(idx)); | |
3643 | idx++) { | |
3644 | ret &= invokeNotifier(notify); | |
3645 | } | |
3646 | array->release(); | |
5ba3f43e | 3647 | |
0a7de745 | 3648 | return ret; |
5ba3f43e A |
3649 | } |
3650 | ||
1c79356b A |
3651 | /* |
3652 | * Alloc and probe matching classes, | |
3653 | * called on the provider instance | |
3654 | */ | |
3655 | ||
0a7de745 A |
3656 | void |
3657 | IOService::probeCandidates( OSOrderedSet * matches ) | |
3658 | { | |
cb323159 | 3659 | OSDictionary * match = NULL; |
0a7de745 A |
3660 | OSSymbol * symbol; |
3661 | IOService * inst; | |
3662 | IOService * newInst; | |
3663 | OSDictionary * props; | |
3664 | SInt32 score; | |
3665 | OSNumber * newPri; | |
cb323159 | 3666 | OSOrderedSet * familyMatches = NULL; |
0a7de745 | 3667 | OSOrderedSet * startList; |
cb323159 A |
3668 | OSSet * kexts = NULL; |
3669 | OSObject * kextRef; | |
3670 | ||
3671 | OSDictionary * startDict = NULL; | |
0a7de745 A |
3672 | const OSSymbol * category; |
3673 | OSIterator * iter; | |
cb323159 A |
3674 | _IOServiceNotifier * notify; |
3675 | OSObject * nextMatch = NULL; | |
0a7de745 A |
3676 | bool started; |
3677 | bool needReloc = false; | |
cb323159 | 3678 | bool matchDeferred = false; |
1c79356b | 3679 | #if IOMATCHDEBUG |
0a7de745 | 3680 | SInt64 debugFlags; |
1c79356b | 3681 | #endif |
cb323159 A |
3682 | IOService * client = NULL; |
3683 | OSObject * prop1; | |
3684 | OSObject * prop2; | |
3685 | OSDictionary * rematchPersonality; | |
3686 | OSNumber * num; | |
3687 | uint32_t count; | |
3688 | uint32_t dextCount; | |
3689 | bool isDext; | |
3690 | bool categoryConsumed; | |
3691 | ||
3692 | prop2 = NULL; | |
3693 | count = 0; | |
3694 | prop1 = copyProperty(gIORematchPersonalityKey); | |
3695 | rematchPersonality = OSDynamicCast(OSDictionary, prop1); | |
3696 | if (rematchPersonality) { | |
3697 | prop2 = copyProperty(gIORematchCountKey); | |
3698 | num = OSDynamicCast(OSNumber, prop2); | |
3699 | if (num) { | |
3700 | count = num->unsigned32BitValue(); | |
3701 | } | |
3702 | OSSafeReleaseNULL(prop2); | |
3703 | } | |
3704 | dextCount = 0; | |
1c79356b | 3705 | |
0a7de745 | 3706 | assert( matches ); |
cb323159 A |
3707 | while (!needReloc |
3708 | && (nextMatch = matches->getFirstObject())) { | |
0a7de745 A |
3709 | nextMatch->retain(); |
3710 | matches->removeObject(nextMatch); | |
1c79356b | 3711 | |
0a7de745 A |
3712 | if ((notify = OSDynamicCast( _IOServiceNotifier, nextMatch ))) { |
3713 | if (0 == (__state[0] & kIOServiceInactiveState)) { | |
3714 | invokeNotifier( notify ); | |
3715 | } | |
3716 | nextMatch->release(); | |
cb323159 | 3717 | nextMatch = NULL; |
0a7de745 A |
3718 | continue; |
3719 | } else if (!(match = OSDynamicCast( OSDictionary, nextMatch ))) { | |
3720 | nextMatch->release(); | |
cb323159 | 3721 | nextMatch = NULL; |
0a7de745 A |
3722 | continue; |
3723 | } | |
1c79356b | 3724 | |
cb323159 | 3725 | props = NULL; |
1c79356b | 3726 | #if IOMATCHDEBUG |
0a7de745 | 3727 | debugFlags = getDebugFlags( match ); |
1c79356b A |
3728 | #endif |
3729 | ||
0a7de745 | 3730 | do { |
cb323159 A |
3731 | isDext = (NULL != match->getObject(gIOUserServerNameKey)); |
3732 | if (isDext && !(kIODKEnable & gIODKDebug)) { | |
3733 | continue; | |
3734 | } | |
3735 | ||
0a7de745 A |
3736 | category = OSDynamicCast( OSSymbol, |
3737 | match->getObject( gIOMatchCategoryKey )); | |
cb323159 | 3738 | if (NULL == category) { |
0a7de745 A |
3739 | category = gIODefaultMatchCategoryKey; |
3740 | } | |
cb323159 | 3741 | client = copyClientWithCategory(category); |
0a7de745 | 3742 | |
cb323159 A |
3743 | categoryConsumed = (client != NULL); |
3744 | if (categoryConsumed) { | |
1c79356b | 3745 | #if IOMATCHDEBUG |
0a7de745 A |
3746 | if ((debugFlags & kIOLogMatch) && (this != gIOResources)) { |
3747 | LOG("%s: match category %s exists\n", getName(), | |
3748 | category->getCStringNoCopy()); | |
3749 | } | |
1c79356b | 3750 | #endif |
cb323159 A |
3751 | OSSafeReleaseNULL(client); |
3752 | if (!isDext) { | |
3753 | break; | |
3754 | } | |
0a7de745 | 3755 | } |
1c79356b | 3756 | |
0a7de745 | 3757 | // create a copy now in case its modified during matching |
cb323159 A |
3758 | props = OSDictionary::withDictionary(match, match->getCount()); |
3759 | if (NULL == props) { | |
3760 | break; | |
0a7de745 A |
3761 | } |
3762 | props->setCapacityIncrement(1); | |
1c79356b | 3763 | |
0a7de745 A |
3764 | // check the nub matches |
3765 | if (false == matchPassive(props, kIOServiceChangesOK | kIOServiceClassDone)) { | |
cb323159 A |
3766 | break; |
3767 | } | |
3768 | if (isDext) { | |
3769 | dextCount++; | |
3770 | if (categoryConsumed) { | |
3771 | break; | |
3772 | } | |
3773 | } | |
3774 | ||
3775 | if (rematchPersonality) { | |
3776 | bool personalityMatch = match->isEqualTo(rematchPersonality); | |
3777 | if (count > gIODextRelaunchMax) { | |
3778 | personalityMatch = !personalityMatch; | |
3779 | } | |
3780 | if (!personalityMatch) { | |
3781 | break; | |
3782 | } | |
0a7de745 | 3783 | } |
1c79356b | 3784 | |
0a7de745 | 3785 | // Check to see if driver reloc has been loaded. |
cb323159 | 3786 | needReloc = (false == gIOCatalogue->isModuleLoaded( match, &kextRef )); |
0a7de745 | 3787 | if (needReloc) { |
1c79356b | 3788 | #if IOMATCHDEBUG |
0a7de745 A |
3789 | if (debugFlags & kIOLogCatalogue) { |
3790 | LOG("%s: stalling for module\n", getName()); | |
3791 | } | |
1c79356b | 3792 | #endif |
0a7de745 A |
3793 | // If reloc hasn't been loaded, exit; |
3794 | // reprobing will occur after reloc has been loaded. | |
cb323159 A |
3795 | break; |
3796 | } | |
3797 | if (kextRef) { | |
3798 | if (NULL == kexts) { | |
3799 | kexts = OSSet::withCapacity(1); | |
3800 | } | |
3801 | if (kexts) { | |
3802 | kexts->setObject(kextRef); | |
3803 | kextRef->release(); | |
3804 | } | |
3805 | } | |
3806 | if (isDext) { | |
3807 | // copy saved for rematchng | |
3808 | props->setObject(gIOMatchedPersonalityKey, match); | |
0a7de745 | 3809 | } |
0a7de745 | 3810 | // reorder on family matchPropertyTable score. |
cb323159 | 3811 | if (NULL == familyMatches) { |
0a7de745 A |
3812 | familyMatches = OSOrderedSet::withCapacity( 1, |
3813 | IOServiceOrdering, (void *) gIOProbeScoreKey ); | |
3814 | } | |
3815 | if (familyMatches) { | |
3816 | familyMatches->setObject( props ); | |
3817 | } | |
3818 | } while (false); | |
3819 | ||
cb323159 A |
3820 | OSSafeReleaseNULL(nextMatch); |
3821 | OSSafeReleaseNULL(props); | |
0a7de745 A |
3822 | } |
3823 | matches->release(); | |
cb323159 | 3824 | matches = NULL; |
0a7de745 A |
3825 | |
3826 | if (familyMatches) { | |
3827 | while (!needReloc | |
3828 | && (props = (OSDictionary *) familyMatches->getFirstObject())) { | |
3829 | props->retain(); | |
3830 | familyMatches->removeObject( props ); | |
3831 | ||
cb323159 A |
3832 | inst = NULL; |
3833 | newInst = NULL; | |
1c79356b | 3834 | #if IOMATCHDEBUG |
0a7de745 | 3835 | debugFlags = getDebugFlags( props ); |
1c79356b | 3836 | #endif |
0a7de745 A |
3837 | do { |
3838 | symbol = OSDynamicCast( OSSymbol, | |
3839 | props->getObject( gIOClassKey)); | |
3840 | if (!symbol) { | |
3841 | continue; | |
3842 | } | |
3843 | ||
3844 | //IOLog("%s alloc (symbol %p props %p)\n", symbol->getCStringNoCopy(), IOSERVICE_OBFUSCATE(symbol), IOSERVICE_OBFUSCATE(props)); | |
3845 | ||
3846 | // alloc the driver instance | |
3847 | inst = (IOService *) OSMetaClass::allocClassWithName( symbol); | |
3848 | ||
3849 | if (!inst || !OSDynamicCast(IOService, inst)) { | |
3850 | IOLog("Couldn't alloc class \"%s\"\n", | |
3851 | symbol->getCStringNoCopy()); | |
3852 | continue; | |
3853 | } | |
3854 | ||
3855 | // init driver instance | |
3856 | if (!(inst->init( props ))) { | |
1c79356b | 3857 | #if IOMATCHDEBUG |
0a7de745 A |
3858 | if (debugFlags & kIOLogStart) { |
3859 | IOLog("%s::init fails\n", symbol->getCStringNoCopy()); | |
3860 | } | |
1c79356b | 3861 | #endif |
0a7de745 A |
3862 | continue; |
3863 | } | |
3864 | if (__state[1] & kIOServiceSynchronousState) { | |
3865 | inst->__state[1] |= kIOServiceSynchronousState; | |
3866 | } | |
3867 | ||
3868 | // give the driver the default match category if not specified | |
3869 | category = OSDynamicCast( OSSymbol, | |
3870 | props->getObject( gIOMatchCategoryKey )); | |
cb323159 | 3871 | if (NULL == category) { |
0a7de745 A |
3872 | category = gIODefaultMatchCategoryKey; |
3873 | } | |
3874 | inst->setProperty( gIOMatchCategoryKey, (OSObject *) category ); | |
3875 | // attach driver instance | |
3876 | if (!(inst->attach( this ))) { | |
3877 | continue; | |
3878 | } | |
1c79356b | 3879 | |
0a7de745 A |
3880 | // pass in score from property table |
3881 | score = familyMatches->orderObject( props ); | |
3882 | ||
3883 | // & probe the new driver instance | |
1c79356b | 3884 | #if IOMATCHDEBUG |
0a7de745 A |
3885 | if (debugFlags & kIOLogProbe) { |
3886 | LOG("%s::probe(%s)\n", | |
3887 | inst->getMetaClass()->getClassName(), getName()); | |
3888 | } | |
1c79356b | 3889 | #endif |
0a7de745 A |
3890 | |
3891 | newInst = inst->probe( this, &score ); | |
3892 | inst->detach( this ); | |
cb323159 | 3893 | if (NULL == newInst) { |
1c79356b | 3894 | #if IOMATCHDEBUG |
0a7de745 A |
3895 | if (debugFlags & kIOLogProbe) { |
3896 | IOLog("%s::probe fails\n", symbol->getCStringNoCopy()); | |
3897 | } | |
1c79356b | 3898 | #endif |
0a7de745 A |
3899 | continue; |
3900 | } | |
3901 | ||
3902 | // save the score | |
3903 | newPri = OSNumber::withNumber( score, 32 ); | |
3904 | if (newPri) { | |
3905 | newInst->setProperty( gIOProbeScoreKey, newPri ); | |
3906 | newPri->release(); | |
3907 | } | |
3908 | ||
3909 | // add to start list for the match category | |
cb323159 | 3910 | if (NULL == startDict) { |
0a7de745 A |
3911 | startDict = OSDictionary::withCapacity( 1 ); |
3912 | } | |
3913 | assert( startDict ); | |
3914 | startList = (OSOrderedSet *) | |
3915 | startDict->getObject( category ); | |
cb323159 | 3916 | if (NULL == startList) { |
0a7de745 A |
3917 | startList = OSOrderedSet::withCapacity( 1, |
3918 | IOServiceOrdering, (void *) gIOProbeScoreKey ); | |
3919 | if (startDict && startList) { | |
3920 | startDict->setObject( category, startList ); | |
3921 | startList->release(); | |
3922 | } | |
3923 | } | |
3924 | assert( startList ); | |
3925 | if (startList) { | |
3926 | startList->setObject( newInst ); | |
3927 | } | |
3928 | } while (false); | |
1c79356b | 3929 | |
0a7de745 A |
3930 | props->release(); |
3931 | if (inst) { | |
3932 | inst->release(); | |
3933 | } | |
3934 | } | |
3935 | familyMatches->release(); | |
cb323159 | 3936 | familyMatches = NULL; |
0a7de745 | 3937 | } |
0b4e3aa0 | 3938 | |
0a7de745 | 3939 | // start the best (until success) of each category |
b0d623f7 | 3940 | |
0a7de745 A |
3941 | iter = OSCollectionIterator::withCollection( startDict ); |
3942 | if (iter) { | |
3943 | while ((category = (const OSSymbol *) iter->getNextObject())) { | |
3944 | startList = (OSOrderedSet *) startDict->getObject( category ); | |
3945 | assert( startList ); | |
3946 | if (!startList) { | |
3947 | continue; | |
3948 | } | |
b0d623f7 | 3949 | |
0a7de745 A |
3950 | started = false; |
3951 | while (true // (!started) | |
cb323159 | 3952 | && !matchDeferred |
0a7de745 A |
3953 | && (inst = (IOService *)startList->getFirstObject())) { |
3954 | inst->retain(); | |
3955 | startList->removeObject(inst); | |
b0d623f7 | 3956 | |
0a7de745 A |
3957 | #if IOMATCHDEBUG |
3958 | debugFlags = getDebugFlags( inst ); | |
3959 | ||
3960 | if (debugFlags & kIOLogStart) { | |
3961 | if (started) { | |
3962 | LOG( "match category exists, skipping " ); | |
3963 | } | |
3964 | LOG( "%s::start(%s) <%d>\n", inst->getName(), | |
3965 | getName(), inst->getRetainCount()); | |
3966 | } | |
3967 | #endif | |
3968 | if (false == started) { | |
cb323159 A |
3969 | #if !NO_KEXTD |
3970 | IOLockLock(gJobsLock); | |
3971 | matchDeferred = (gIOMatchDeferList | |
f427ee49 | 3972 | && (kOSBooleanTrue == inst->getProperty(gIOMatchDeferKey) || gInUserspaceReboot)); |
cb323159 A |
3973 | if (matchDeferred && (-1U == gIOMatchDeferList->getNextIndexOfObject(this, 0))) { |
3974 | gIOMatchDeferList->setObject(this); | |
3975 | } | |
3976 | IOLockUnlock(gJobsLock); | |
3977 | if (matchDeferred) { | |
3978 | symbol = OSDynamicCast(OSSymbol, inst->getProperty(gIOClassKey)); | |
3979 | IOLog("%s(0x%qx): matching deferred by %s\n", | |
3980 | getName(), getRegistryEntryID(), | |
3981 | symbol ? symbol->getCStringNoCopy() : ""); | |
f427ee49 | 3982 | // rematching will occur after the IOKit daemon loads all plists |
cb323159 A |
3983 | } |
3984 | #endif | |
3985 | if (!matchDeferred) { | |
3986 | started = startCandidate( inst ); | |
0a7de745 | 3987 | #if IOMATCHDEBUG |
cb323159 A |
3988 | if ((debugFlags & kIOLogStart) && (false == started)) { |
3989 | LOG( "%s::start(%s) <%d> failed\n", inst->getName(), getName(), | |
3990 | inst->getRetainCount()); | |
3991 | } | |
0a7de745 | 3992 | #endif |
cb323159 A |
3993 | } |
3994 | } | |
0a7de745 A |
3995 | inst->release(); |
3996 | } | |
3997 | } | |
3998 | iter->release(); | |
b0d623f7 | 3999 | } |
0b4e3aa0 | 4000 | |
cb323159 A |
4001 | OSSafeReleaseNULL(prop1); |
4002 | ||
4003 | if (dextCount) { | |
4004 | num = OSNumber::withNumber(dextCount, 32); | |
4005 | setProperty(gIODEXTMatchCountKey, num); | |
4006 | OSSafeReleaseNULL(num); | |
4007 | } else if (rematchPersonality) { | |
4008 | removeProperty(gIODEXTMatchCountKey); | |
4009 | } | |
4010 | ||
4011 | // now that instances are created, drop the refs on any kexts allowing unload | |
4012 | if (kexts) { | |
4013 | OSKext::dropMatchingReferences(kexts); | |
4014 | OSSafeReleaseNULL(kexts); | |
4015 | } | |
b0d623f7 | 4016 | |
0a7de745 A |
4017 | // adjust the busy count by +1 if matching is stalled for a module, |
4018 | // or -1 if a previously stalled matching is complete. | |
4019 | lockForArbitration(); | |
4020 | SInt32 adjBusy = 0; | |
4021 | uint64_t regID = getRegistryEntryID(); | |
4022 | ||
4023 | if (needReloc) { | |
4024 | adjBusy = (__state[1] & kIOServiceModuleStallState) ? 0 : 1; | |
4025 | if (adjBusy) { | |
4026 | IOServiceTrace( | |
4027 | IOSERVICE_MODULESTALL, | |
4028 | (uintptr_t) regID, | |
4029 | (uintptr_t) (regID >> 32), | |
4030 | (uintptr_t) this, | |
4031 | 0); | |
b0d623f7 | 4032 | |
0a7de745 A |
4033 | __state[1] |= kIOServiceModuleStallState; |
4034 | } | |
4035 | } else if (__state[1] & kIOServiceModuleStallState) { | |
4036 | IOServiceTrace( | |
4037 | IOSERVICE_MODULEUNSTALL, | |
4038 | (uintptr_t) regID, | |
4039 | (uintptr_t) (regID >> 32), | |
4040 | (uintptr_t) this, | |
4041 | 0); | |
4042 | ||
4043 | __state[1] &= ~kIOServiceModuleStallState; | |
4044 | adjBusy = -1; | |
4045 | } | |
4046 | if (adjBusy) { | |
4047 | _adjustBusy( adjBusy ); | |
4048 | } | |
4049 | unlockForArbitration(); | |
0b4e3aa0 | 4050 | |
0a7de745 A |
4051 | if (startDict) { |
4052 | startDict->release(); | |
4053 | } | |
1c79356b A |
4054 | } |
4055 | ||
cb323159 A |
4056 | /* |
4057 | * Wait for a IOUserServer to check in | |
4058 | */ | |
4059 | ||
4060 | static | |
4061 | __attribute__((noinline, not_tail_called)) | |
4062 | IOService * | |
f427ee49 | 4063 | __WAITING_FOR_USER_SERVER__(OSDictionary * matching, IOUserServerCheckInToken * token) |
cb323159 A |
4064 | { |
4065 | IOService * server; | |
f427ee49 | 4066 | server = IOService::waitForMatchingServiceWithToken(matching, kIOUserServerCheckInTimeoutSecs * NSEC_PER_SEC, token); |
cb323159 A |
4067 | return server; |
4068 | } | |
4069 | ||
4070 | void | |
4071 | IOService::willShutdown() | |
4072 | { | |
f427ee49 | 4073 | gIOKitWillTerminate = true; |
cb323159 A |
4074 | #if !NO_KEXTD |
4075 | getPlatform()->waitQuiet(30 * NSEC_PER_SEC); | |
4076 | #endif | |
4077 | OSKext::willShutdown(); | |
4078 | } | |
4079 | ||
f427ee49 A |
4080 | void |
4081 | IOService::userSpaceWillReboot() | |
4082 | { | |
4083 | IOLockLock(gJobsLock); | |
4084 | #if !NO_KEXTD | |
4085 | // Recreate the defer list if it does not exist | |
4086 | if (!gIOMatchDeferList) { | |
4087 | gIOMatchDeferList = OSArray::withCapacity( 16 ); | |
4088 | } | |
4089 | #endif | |
4090 | gInUserspaceReboot = true; | |
4091 | IOLockUnlock(gJobsLock); | |
4092 | } | |
4093 | ||
4094 | void | |
4095 | IOService::userSpaceDidReboot() | |
4096 | { | |
4097 | IOLockLock(gJobsLock); | |
4098 | gInUserspaceReboot = false; | |
4099 | IOLockUnlock(gJobsLock); | |
4100 | } | |
4101 | ||
cb323159 A |
4102 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ |
4103 | ||
4104 | void | |
4105 | IOServicePH::init(IOPMrootDomain * root) | |
4106 | { | |
4107 | fUserServers = OSArray::withCapacity(4); | |
4108 | fMatchingWork = OSArray::withCapacity(4); | |
4109 | ||
4110 | assert(fUserServers && fMatchingWork); | |
4111 | ||
4112 | fRootNotifier = root->registerInterest( | |
4113 | gIOPriorityPowerStateInterest, &IOServicePH::systemPowerChange, NULL, NULL); | |
4114 | ||
4115 | assert(fRootNotifier); | |
4116 | } | |
4117 | ||
4118 | void | |
4119 | IOServicePH::lock() | |
4120 | { | |
4121 | IOLockLock(gJobsLock); | |
4122 | } | |
4123 | ||
4124 | void | |
4125 | IOServicePH::unlock() | |
4126 | { | |
4127 | IOLockUnlock(gJobsLock); | |
4128 | } | |
4129 | ||
4130 | void | |
4131 | IOServicePH::serverAdd(IOUserServer * server) | |
4132 | { | |
4133 | uint32_t idx; | |
4134 | ||
4135 | lock(); | |
4136 | idx = fUserServers->getNextIndexOfObject(server, 0); | |
4137 | if (idx == -1U) { | |
4138 | fUserServers->setObject(server); | |
4139 | } | |
4140 | unlock(); | |
4141 | } | |
4142 | ||
4143 | void | |
4144 | IOServicePH::serverRemove(IOUserServer * server) | |
4145 | { | |
4146 | uint32_t idx; | |
4147 | ||
4148 | lock(); | |
4149 | idx = fUserServers->getNextIndexOfObject(server, 0); | |
4150 | if (idx != -1U) { | |
4151 | fUserServers->removeObject(idx); | |
4152 | } | |
4153 | unlock(); | |
4154 | } | |
4155 | ||
4156 | void | |
4157 | IOServicePH::serverAck(IOUserServer * server) | |
4158 | { | |
4159 | uint32_t idx; | |
4160 | IOService * ackTo; | |
4161 | uint32_t ackToRef; | |
4162 | ||
4163 | ackTo = NULL; | |
4164 | lock(); | |
4165 | if (server && fUserServersWait) { | |
4166 | idx = fUserServersWait->getNextIndexOfObject(server, 0); | |
4167 | if (idx != -1U) { | |
4168 | fUserServersWait->removeObject(idx); | |
4169 | if (0 == fUserServersWait->getCount()) { | |
4170 | OSSafeReleaseNULL(fUserServersWait); | |
4171 | } | |
4172 | } | |
4173 | } | |
4174 | if (!fUserServersWait && !fMatchingWork->getCount()) { | |
4175 | ackTo = fSystemPowerAckTo; | |
4176 | ackToRef = fSystemPowerAckRef; | |
4177 | fSystemPowerAckTo = NULL; | |
4178 | } | |
4179 | unlock(); | |
4180 | ||
4181 | if (ackTo) { | |
4182 | DKLOG("allowPowerChange\n"); | |
4183 | ackTo->allowPowerChange((uintptr_t) ackToRef); | |
4184 | } | |
4185 | } | |
4186 | ||
4187 | bool | |
4188 | IOServicePH::matchingStart(IOService * service) | |
4189 | { | |
4190 | uint32_t idx; | |
4191 | bool ok; | |
4192 | ||
4193 | lock(); | |
4194 | ok = !fSystemOff; | |
4195 | if (ok) { | |
4196 | idx = fMatchingWork->getNextIndexOfObject(service, 0); | |
4197 | if (idx == -1U) { | |
4198 | fMatchingWork->setObject(service); | |
4199 | } | |
4200 | } else { | |
4201 | if (!fMatchingDelayed) { | |
4202 | fMatchingDelayed = OSArray::withObjects((const OSObject **) &service, 1, 1); | |
4203 | } else { | |
4204 | idx = fMatchingDelayed->getNextIndexOfObject(service, 0); | |
4205 | if (idx == -1U) { | |
4206 | fMatchingDelayed->setObject(service); | |
4207 | } | |
4208 | } | |
4209 | } | |
4210 | unlock(); | |
4211 | ||
4212 | return ok; | |
4213 | } | |
4214 | ||
4215 | void | |
4216 | IOServicePH::matchingEnd(IOService * service) | |
4217 | { | |
4218 | uint32_t idx; | |
4219 | OSArray * notifyServers; | |
4220 | OSArray * deferredMatches; | |
4221 | ||
4222 | notifyServers = NULL; | |
4223 | deferredMatches = NULL; | |
4224 | ||
4225 | lock(); | |
4226 | ||
4227 | if (service) { | |
4228 | idx = fMatchingWork->getNextIndexOfObject(service, 0); | |
4229 | if (idx != -1U) { | |
4230 | fMatchingWork->removeObject(idx); | |
4231 | } | |
4232 | } | |
4233 | ||
4234 | ||
4235 | if ((fUserServerOff != fSystemOff) && fUserServers->getCount()) { | |
4236 | if (fSystemOff) { | |
4237 | if (0 == fMatchingWork->getCount()) { | |
4238 | fUserServersWait = OSArray::withArray(fUserServers); | |
4239 | notifyServers = OSArray::withArray(fUserServers); | |
4240 | fUserServerOff = fSystemOff; | |
4241 | } | |
4242 | } else { | |
4243 | notifyServers = OSArray::withArray(fUserServers); | |
4244 | fUserServerOff = fSystemOff; | |
4245 | } | |
4246 | } | |
4247 | ||
4248 | if (!fSystemOff && fMatchingDelayed) { | |
4249 | deferredMatches = fMatchingDelayed; | |
4250 | fMatchingDelayed = NULL; | |
4251 | } | |
4252 | ||
4253 | unlock(); | |
4254 | ||
4255 | if (notifyServers) { | |
4256 | notifyServers->iterateObjects(^bool (OSObject * obj) { | |
4257 | IOUserServer * us; | |
4258 | us = (typeof(us))obj; | |
4259 | us->systemPower(fSystemOff); | |
4260 | return false; | |
4261 | }); | |
4262 | OSSafeReleaseNULL(notifyServers); | |
4263 | } | |
4264 | ||
4265 | if (deferredMatches) { | |
4266 | DKLOG("sleep deferred rematching count %d\n", deferredMatches->getCount()); | |
4267 | deferredMatches->iterateObjects(^bool (OSObject * obj) | |
4268 | { | |
4269 | ((IOService *)obj)->startMatching(kIOServiceAsynchronous); | |
4270 | return false; | |
4271 | }); | |
4272 | deferredMatches->release(); | |
4273 | } | |
4274 | ||
4275 | serverAck(NULL); | |
4276 | } | |
4277 | ||
f427ee49 A |
4278 | bool |
4279 | IOServicePH::serverSlept(void) | |
4280 | { | |
4281 | bool ret; | |
4282 | ||
4283 | lock(); | |
4284 | ret = (kIOMessageSystemWillSleep == sSystemPower) | |
4285 | || (kIOMessageSystemPagingOff == sSystemPower); | |
4286 | unlock(); | |
4287 | ||
4288 | return ret; | |
4289 | } | |
4290 | ||
cb323159 A |
4291 | IOReturn |
4292 | IOServicePH::systemPowerChange( | |
4293 | void * target, | |
4294 | void * refCon, | |
4295 | UInt32 messageType, IOService * service, | |
4296 | void * messageArgument, vm_size_t argSize) | |
4297 | { | |
4298 | IOReturn ret; | |
4299 | IOUserServer * us; | |
4300 | IOPMSystemCapabilityChangeParameters * params; | |
4301 | ||
4302 | us = NULL; | |
4303 | ||
4304 | switch (messageType) { | |
4305 | case kIOMessageSystemCapabilityChange: | |
4306 | ||
4307 | params = (typeof params)messageArgument; | |
4308 | ||
4309 | if (kIODKLogPM & gIODKDebug) { | |
4310 | IOLog("IOServicePH::kIOMessageSystemCapabilityChange: %s%s 0x%x->0x%x\n", | |
4311 | params->changeFlags & kIOPMSystemCapabilityWillChange ? "will" : "", | |
4312 | params->changeFlags & kIOPMSystemCapabilityDidChange ? "did" : "", | |
4313 | params->fromCapabilities, | |
4314 | params->toCapabilities); | |
4315 | } | |
4316 | ||
4317 | if ((params->changeFlags & kIOPMSystemCapabilityWillChange) && | |
4318 | (params->fromCapabilities & kIOPMSystemCapabilityCPU) && | |
4319 | ((params->toCapabilities & kIOPMSystemCapabilityCPU) == 0)) { | |
4320 | lock(); | |
4321 | fSystemOff = true; | |
4322 | fSystemPowerAckRef = params->notifyRef; | |
4323 | fSystemPowerAckTo = service; | |
4324 | unlock(); | |
4325 | ||
4326 | matchingEnd(NULL); | |
4327 | ||
4328 | params->maxWaitForReply = 60 * 1000 * 1000; | |
4329 | ret = kIOReturnSuccess; | |
4330 | } else if ((params->changeFlags & kIOPMSystemCapabilityWillChange) && | |
4331 | ((params->fromCapabilities & kIOPMSystemCapabilityCPU) == 0) && | |
4332 | (params->toCapabilities & kIOPMSystemCapabilityCPU)) { | |
4333 | lock(); | |
4334 | fSystemOff = false; | |
4335 | unlock(); | |
4336 | ||
4337 | matchingEnd(NULL); | |
4338 | ||
4339 | params->maxWaitForReply = 0; | |
4340 | ret = kIOReturnSuccess; | |
4341 | } else { | |
4342 | params->maxWaitForReply = 0; | |
4343 | ret = kIOReturnSuccess; | |
4344 | } | |
4345 | break; | |
4346 | ||
4347 | default: | |
4348 | ret = kIOReturnUnsupported; | |
4349 | break; | |
4350 | } | |
4351 | ||
4352 | return ret; | |
4353 | } | |
4354 | ||
4355 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ | |
4356 | ||
1c79356b A |
4357 | /* |
4358 | * Start a previously attached & probed instance, | |
4359 | * called on exporting object instance | |
4360 | */ | |
4361 | ||
0a7de745 A |
4362 | bool |
4363 | IOService::startCandidate( IOService * service ) | |
1c79356b | 4364 | { |
0a7de745 | 4365 | bool ok; |
cb323159 A |
4366 | OSObject * obj; |
4367 | OSObject * prop; | |
4368 | IOUserServer * userServer; | |
4369 | bool ph; | |
1c79356b | 4370 | |
cb323159 A |
4371 | userServer = NULL; |
4372 | obj = service->copyProperty(gIOUserServerNameKey); | |
1c79356b | 4373 | |
cb323159 A |
4374 | if (obj && (this == gIOResources)) { |
4375 | ok = false; | |
4376 | } else { | |
4377 | ok = service->attach( this ); | |
4378 | } | |
4379 | if (!ok) { | |
4380 | return false; | |
4381 | } | |
55e303ae | 4382 | |
cb323159 A |
4383 | if ((this != gIOResources) && (this != gIOUserResources)) { |
4384 | // stall for any nub resources | |
4385 | checkResources(); | |
4386 | // stall for any driver resources | |
4387 | service->checkResources(); | |
4388 | } | |
4389 | ph = false; | |
4390 | { | |
4391 | OSString * bundleID; | |
4392 | OSString * serverName; | |
4393 | OSString * str; | |
4394 | const OSSymbol * sym; | |
4395 | OSDictionary * matching; | |
4396 | IOService * server; | |
4397 | OSNumber * serverTag; | |
4398 | uint64_t entryID; | |
f427ee49 | 4399 | IOUserServerCheckInToken * token; |
cb323159 A |
4400 | |
4401 | if ((serverName = OSDynamicCast(OSString, obj))) { | |
4402 | obj = service->copyProperty(gIOModuleIdentifierKey); | |
4403 | bundleID = OSDynamicCast(OSString, obj); | |
4404 | entryID = service->getRegistryEntryID(); | |
4405 | serverTag = OSNumber::withNumber(entryID, 64); | |
f427ee49 | 4406 | token = NULL; |
cb323159 | 4407 | |
f427ee49 | 4408 | if (gIOKitWillTerminate) { |
cb323159 A |
4409 | DKLOG("%s disabled in shutdown\n", serverName->getCStringNoCopy()); |
4410 | service->detach(this); | |
4411 | OSSafeReleaseNULL(obj); | |
4412 | return false; | |
4413 | } | |
1c79356b | 4414 | |
cb323159 A |
4415 | ph = IOServicePH::matchingStart(this); |
4416 | if (!ph) { | |
4417 | DKLOG("%s deferred in sleep\n", serverName->getCStringNoCopy()); | |
4418 | service->detach(this); | |
4419 | OSSafeReleaseNULL(obj); | |
4420 | return false; | |
4421 | } | |
4422 | ||
4423 | prop = service->copyProperty(gIOUserClassKey); | |
4424 | str = OSDynamicCast(OSString, prop); | |
4425 | if (str) { | |
4426 | service->setName(str); | |
4427 | } | |
4428 | OSSafeReleaseNULL(prop); | |
0b4e3aa0 | 4429 | |
cb323159 | 4430 | if (!(kIODKDisableDextLaunch & gIODKDebug)) { |
f427ee49 A |
4431 | OSKext::requestDaemonLaunch(bundleID, serverName, serverTag, &token); |
4432 | } | |
4433 | if (!token) { | |
4434 | DKLOG("%s failed to create check in token\n", serverName->getCStringNoCopy()); | |
4435 | service->detach(this); | |
4436 | OSSafeReleaseNULL(obj); | |
4437 | return false; | |
cb323159 A |
4438 | } |
4439 | sym = OSSymbol::withString(serverName); | |
4440 | matching = serviceMatching(gIOUserServerClassKey); | |
4441 | propertyMatching(gIOUserServerNameKey, sym, matching); | |
4442 | if (!(kIODKDisableDextTag & gIODKDebug)) { | |
4443 | propertyMatching(gIOUserServerTagKey, serverTag, matching); | |
4444 | } | |
0a7de745 | 4445 | |
f427ee49 | 4446 | server = __WAITING_FOR_USER_SERVER__(matching, token); |
cb323159 A |
4447 | matching->release(); |
4448 | OSSafeReleaseNULL(serverTag); | |
4449 | OSSafeReleaseNULL(serverName); | |
4450 | ||
4451 | userServer = OSDynamicCast(IOUserServer, server); | |
4452 | if (!userServer) { | |
f427ee49 | 4453 | token->release(); |
cb323159 A |
4454 | service->detach(this); |
4455 | IOServicePH::matchingEnd(this); | |
f427ee49 | 4456 | OSSafeReleaseNULL(obj); |
cb323159 | 4457 | DKLOG(DKS " user server timeout\n", DKN(service)); |
f427ee49 A |
4458 | #if DEVELOPMENT || DEBUG |
4459 | driverkit_checkin_timed_out = mach_absolute_time(); | |
4460 | #endif | |
cb323159 A |
4461 | return false; |
4462 | } | |
0a7de745 | 4463 | |
f427ee49 A |
4464 | if (!(kIODKDisableCheckInTokenVerification & gIODKDebug)) { |
4465 | if (!userServer->serviceMatchesCheckInToken(token)) { | |
4466 | token->release(); | |
cb323159 A |
4467 | service->detach(this); |
4468 | IOServicePH::matchingEnd(this); | |
f427ee49 A |
4469 | OSSafeReleaseNULL(obj); |
4470 | userServer->exit("Check In Token verification failed"); | |
cb323159 A |
4471 | userServer->release(); |
4472 | return false; | |
0a7de745 A |
4473 | } |
4474 | } | |
f427ee49 A |
4475 | token->release(); |
4476 | ||
cb323159 A |
4477 | OSKext *kext = OSKext::lookupKextWithIdentifier(bundleID); |
4478 | if (!kext) { | |
4479 | const char *name = bundleID->getCStringNoCopy(); | |
4480 | IOLog("%s Could not find OSKext for %s\n", __func__, name); | |
4481 | goto skip_log; | |
4482 | } | |
4483 | ||
4484 | /* | |
4485 | * Used for logging | |
4486 | */ | |
4487 | userServer->setTaskLoadTag(kext); | |
4488 | userServer->setDriverKitUUID(kext); | |
4489 | OSKext::OSKextLogDriverKitInfoLoad(kext); | |
4490 | skip_log: | |
4491 | OSSafeReleaseNULL(bundleID); | |
4492 | OSSafeReleaseNULL(kext); | |
4493 | ||
4494 | if (!(kIODKDisableEntitlementChecking & gIODKDebug)) { | |
4495 | if (!userServer->checkEntitlements(this, service)) { | |
4496 | service->detach(this); | |
4497 | IOServicePH::matchingEnd(this); | |
4498 | userServer->exit("Entitlements check failed"); | |
4499 | userServer->release(); | |
4500 | return false; | |
4501 | } | |
4502 | } | |
4503 | ||
4504 | userServer->serviceAttach(service, this); | |
0a7de745 | 4505 | } |
cb323159 A |
4506 | } |
4507 | ||
4508 | AbsoluteTime startTime; | |
4509 | AbsoluteTime endTime; | |
4510 | UInt64 nano; | |
4511 | ||
4512 | if (kIOLogStart & gIOKitDebug) { | |
4513 | clock_get_uptime(&startTime); | |
4514 | } | |
4515 | ||
4516 | ok = service->start(this); | |
4517 | ||
4518 | if (kIOLogStart & gIOKitDebug) { | |
4519 | clock_get_uptime(&endTime); | |
4520 | ||
4521 | if (CMP_ABSOLUTETIME(&endTime, &startTime) > 0) { | |
4522 | SUB_ABSOLUTETIME(&endTime, &startTime); | |
4523 | absolutetime_to_nanoseconds(endTime, &nano); | |
4524 | if (nano > 500000000ULL) { | |
4525 | IOLog("%s::start took %ld ms\n", service->getName(), (long)(UInt32)(nano / 1000000ULL)); | |
4526 | } | |
0a7de745 | 4527 | } |
55e303ae | 4528 | } |
cb323159 A |
4529 | if (userServer) { |
4530 | userServer->serviceStarted(service, this, ok); | |
4531 | userServer->release(); | |
4532 | } | |
f427ee49 A |
4533 | |
4534 | if (ok) { | |
4535 | IOInstallServiceSleepPlatformActions(service); | |
4536 | } | |
4537 | ||
cb323159 A |
4538 | if (!ok) { |
4539 | service->detach( this ); | |
4540 | } | |
4541 | ||
4542 | if (ph) { | |
4543 | IOServicePH::matchingEnd(this); | |
4544 | } | |
4545 | ||
0a7de745 | 4546 | return ok; |
1c79356b A |
4547 | } |
4548 | ||
0a7de745 A |
4549 | void |
4550 | IOService::publishResource( const char * key, OSObject * value ) | |
1c79356b | 4551 | { |
0a7de745 | 4552 | const OSSymbol * sym; |
1c79356b | 4553 | |
0a7de745 A |
4554 | if ((sym = OSSymbol::withCString( key))) { |
4555 | publishResource( sym, value); | |
4556 | sym->release(); | |
4557 | } | |
1c79356b A |
4558 | } |
4559 | ||
0a7de745 A |
4560 | void |
4561 | IOService::publishResource( const OSSymbol * key, OSObject * value ) | |
1c79356b | 4562 | { |
cb323159 | 4563 | if (NULL == value) { |
0a7de745 A |
4564 | value = (OSObject *) gIOServiceKey; |
4565 | } | |
1c79356b | 4566 | |
0a7de745 | 4567 | gIOResources->setProperty( key, value); |
1c79356b | 4568 | |
0a7de745 A |
4569 | if (IORecursiveLockHaveLock( gNotificationLock)) { |
4570 | return; | |
4571 | } | |
55e303ae | 4572 | |
0a7de745 A |
4573 | gIOResourceGenerationCount++; |
4574 | gIOResources->registerService(); | |
1c79356b A |
4575 | } |
4576 | ||
cb323159 A |
4577 | void |
4578 | IOService::publishUserResource( const OSSymbol * key, OSObject * value ) | |
4579 | { | |
4580 | if (NULL == value) { | |
4581 | value = (OSObject *) gIOServiceKey; | |
4582 | } | |
4583 | ||
4584 | gIOUserResources->setProperty( key, value); | |
4585 | ||
4586 | if (IORecursiveLockHaveLock( gNotificationLock)) { | |
4587 | return; | |
4588 | } | |
4589 | ||
4590 | gIOResourceGenerationCount++; | |
4591 | gIOUserResources->registerService(); | |
4592 | } | |
4593 | ||
0a7de745 A |
4594 | bool |
4595 | IOService::addNeededResource( const char * key ) | |
1c79356b | 4596 | { |
0a7de745 A |
4597 | OSObject * resourcesProp; |
4598 | OSSet * set; | |
4599 | OSString * newKey; | |
4600 | bool ret; | |
1c79356b | 4601 | |
0a7de745 A |
4602 | resourcesProp = copyProperty( gIOResourceMatchKey ); |
4603 | if (!resourcesProp) { | |
4604 | return false; | |
4605 | } | |
1c79356b | 4606 | |
0a7de745 A |
4607 | newKey = OSString::withCString( key ); |
4608 | if (!newKey) { | |
4609 | resourcesProp->release(); | |
4610 | return false; | |
4611 | } | |
1c79356b | 4612 | |
0a7de745 A |
4613 | set = OSDynamicCast( OSSet, resourcesProp ); |
4614 | if (!set) { | |
4615 | set = OSSet::withCapacity( 1 ); | |
4616 | if (set) { | |
4617 | set->setObject( resourcesProp ); | |
4618 | } | |
4619 | } else { | |
4620 | set->retain(); | |
4621 | } | |
1c79356b | 4622 | |
0a7de745 A |
4623 | set->setObject( newKey ); |
4624 | newKey->release(); | |
4625 | ret = setProperty( gIOResourceMatchKey, set ); | |
4626 | set->release(); | |
4627 | resourcesProp->release(); | |
1c79356b | 4628 | |
0a7de745 | 4629 | return ret; |
1c79356b A |
4630 | } |
4631 | ||
0a7de745 A |
4632 | bool |
4633 | IOService::checkResource( OSObject * matching ) | |
1c79356b | 4634 | { |
0a7de745 A |
4635 | OSString * str; |
4636 | OSDictionary * table; | |
1c79356b | 4637 | |
0a7de745 A |
4638 | if ((str = OSDynamicCast( OSString, matching ))) { |
4639 | if (gIOResources->getProperty( str )) { | |
4640 | return true; | |
4641 | } | |
4642 | } | |
1c79356b | 4643 | |
0a7de745 A |
4644 | if (str) { |
4645 | table = resourceMatching( str ); | |
4646 | } else if ((table = OSDynamicCast( OSDictionary, matching ))) { | |
4647 | table->retain(); | |
4648 | } else { | |
4649 | IOLog("%s: Can't match using: %s\n", getName(), | |
4650 | matching->getMetaClass()->getClassName()); | |
4651 | /* false would stall forever */ | |
4652 | return true; | |
4653 | } | |
1c79356b | 4654 | |
0a7de745 A |
4655 | if (gIOKitDebug & kIOLogConfig) { |
4656 | LOG("config(%p): stalling %s\n", IOSERVICE_OBFUSCATE(IOThreadSelf()), getName()); | |
4657 | } | |
1c79356b | 4658 | |
0a7de745 | 4659 | waitForService( table ); |
1c79356b | 4660 | |
0a7de745 A |
4661 | if (gIOKitDebug & kIOLogConfig) { |
4662 | LOG("config(%p): waking\n", IOSERVICE_OBFUSCATE(IOThreadSelf())); | |
4663 | } | |
1c79356b | 4664 | |
0a7de745 | 4665 | return true; |
1c79356b A |
4666 | } |
4667 | ||
0a7de745 A |
4668 | bool |
4669 | IOService::checkResources( void ) | |
1c79356b | 4670 | { |
0a7de745 A |
4671 | OSObject * resourcesProp; |
4672 | OSSet * set; | |
f427ee49 | 4673 | OSObject * obj; |
0a7de745 A |
4674 | OSIterator * iter; |
4675 | bool ok; | |
1c79356b | 4676 | |
0a7de745 | 4677 | resourcesProp = copyProperty( gIOResourceMatchKey ); |
cb323159 | 4678 | if (NULL == resourcesProp) { |
0a7de745 A |
4679 | return true; |
4680 | } | |
1c79356b | 4681 | |
0a7de745 A |
4682 | if ((set = OSDynamicCast( OSSet, resourcesProp ))) { |
4683 | iter = OSCollectionIterator::withCollection( set ); | |
cb323159 | 4684 | ok = (NULL != iter); |
f427ee49 A |
4685 | while (ok && (obj = iter->getNextObject())) { |
4686 | ok = checkResource( obj ); | |
0a7de745 A |
4687 | } |
4688 | if (iter) { | |
4689 | iter->release(); | |
4690 | } | |
4691 | } else { | |
4692 | ok = checkResource( resourcesProp ); | |
4693 | } | |
1c79356b | 4694 | |
0a7de745 | 4695 | OSSafeReleaseNULL(resourcesProp); |
5ba3f43e | 4696 | |
0a7de745 | 4697 | return ok; |
1c79356b A |
4698 | } |
4699 | ||
4700 | ||
0a7de745 | 4701 | void |
f427ee49 | 4702 | _IOConfigThread::configThread( const char * name ) |
1c79356b | 4703 | { |
0a7de745 | 4704 | _IOConfigThread * inst; |
1c79356b | 4705 | |
0a7de745 A |
4706 | do { |
4707 | if (!(inst = new _IOConfigThread)) { | |
4708 | continue; | |
4709 | } | |
4710 | if (!inst->init()) { | |
4711 | continue; | |
4712 | } | |
cb323159 A |
4713 | thread_t thread; |
4714 | if (KERN_SUCCESS != kernel_thread_start(&_IOConfigThread::main, inst, &thread)) { | |
0a7de745 A |
4715 | continue; |
4716 | } | |
1c79356b | 4717 | |
cb323159 | 4718 | char threadName[MAXTHREADNAMESIZE]; |
f427ee49 | 4719 | snprintf(threadName, sizeof(threadName), "IOConfigThread_'%s'", name); |
cb323159 A |
4720 | thread_set_thread_name(thread, threadName); |
4721 | thread_deallocate(thread); | |
4722 | ||
0a7de745 A |
4723 | return; |
4724 | } while (false); | |
1c79356b | 4725 | |
0a7de745 A |
4726 | if (inst) { |
4727 | inst->release(); | |
4728 | } | |
1c79356b | 4729 | |
0a7de745 | 4730 | return; |
1c79356b A |
4731 | } |
4732 | ||
0a7de745 A |
4733 | void |
4734 | IOService::doServiceMatch( IOOptionBits options ) | |
1c79356b | 4735 | { |
0a7de745 A |
4736 | _IOServiceNotifier * notify; |
4737 | OSIterator * iter; | |
4738 | OSOrderedSet * matches; | |
cb323159 | 4739 | OSArray * resourceKeys = NULL; |
0a7de745 A |
4740 | SInt32 catalogGeneration; |
4741 | bool keepGuessing = true; | |
4742 | bool reRegistered = true; | |
4743 | bool didRegister; | |
cb323159 | 4744 | OSArray * notifiers[2] = {NULL}; |
1c79356b A |
4745 | |
4746 | // job->nub->deliverNotification( gIOPublishNotification, | |
0a7de745 A |
4747 | // kIOServiceRegisteredState, 0xffffffff ); |
4748 | ||
4749 | while (keepGuessing) { | |
4750 | matches = gIOCatalogue->findDrivers( this, &catalogGeneration ); | |
4751 | // the matches list should always be created by findDrivers() | |
4752 | if (matches) { | |
4753 | lockForArbitration(); | |
4754 | if (0 == (__state[0] & kIOServiceFirstPublishState)) { | |
4755 | getMetaClass()->addInstance(this); | |
4756 | notifiers[0] = copyNotifiers(gIOFirstPublishNotification, | |
4757 | kIOServiceFirstPublishState, 0xffffffff ); | |
4758 | } | |
4759 | LOCKREADNOTIFY(); | |
4760 | __state[1] &= ~kIOServiceNeedConfigState; | |
4761 | __state[1] |= kIOServiceConfigState | kIOServiceConfigRunning; | |
4762 | didRegister = (0 == (kIOServiceRegisteredState & __state[0])); | |
4763 | __state[0] |= kIOServiceRegisteredState; | |
4764 | ||
4765 | keepGuessing &= (0 == (__state[0] & kIOServiceInactiveState)); | |
4766 | if (reRegistered && keepGuessing) { | |
4767 | iter = OSCollectionIterator::withCollection((OSOrderedSet *) | |
4768 | gNotifications->getObject( gIOPublishNotification )); | |
4769 | if (iter) { | |
4770 | while ((notify = (_IOServiceNotifier *) | |
4771 | iter->getNextObject())) { | |
4772 | if (matchPassive(notify->matching, 0) | |
4773 | && (kIOServiceNotifyEnable & notify->state)) { | |
4774 | matches->setObject( notify ); | |
4775 | } | |
4776 | } | |
4777 | iter->release(); | |
4778 | } | |
4779 | } | |
4780 | ||
4781 | UNLOCKNOTIFY(); | |
4782 | unlockForArbitration(); | |
4783 | invokeNotifiers(¬ifiers[0]); | |
4784 | ||
4785 | if (keepGuessing && matches->getCount() && (kIOReturnSuccess == getResources())) { | |
cb323159 | 4786 | if ((this == gIOResources) || (this == gIOUserResources)) { |
0a7de745 A |
4787 | if (resourceKeys) { |
4788 | resourceKeys->release(); | |
4789 | } | |
4790 | resourceKeys = copyPropertyKeys(); | |
4791 | } | |
4792 | probeCandidates( matches ); | |
4793 | } else { | |
4794 | matches->release(); | |
4795 | } | |
4796 | } | |
4797 | ||
4798 | lockForArbitration(); | |
4799 | reRegistered = (0 != (__state[1] & kIOServiceNeedConfigState)); | |
4800 | keepGuessing = | |
4801 | (reRegistered || (catalogGeneration != | |
4802 | gIOCatalogue->getGenerationCount())) | |
4803 | && (0 == (__state[0] & kIOServiceInactiveState)); | |
4804 | ||
4805 | if (keepGuessing) { | |
4806 | unlockForArbitration(); | |
4807 | } | |
4808 | } | |
4809 | ||
4810 | if ((0 == (__state[0] & kIOServiceInactiveState)) | |
4811 | && (0 == (__state[1] & kIOServiceModuleStallState))) { | |
4812 | if (resourceKeys) { | |
4813 | setProperty(gIOResourceMatchedKey, resourceKeys); | |
4814 | } | |
4815 | ||
4816 | notifiers[0] = copyNotifiers(gIOMatchedNotification, | |
4817 | kIOServiceMatchedState, 0xffffffff); | |
4818 | if (0 == (__state[0] & kIOServiceFirstMatchState)) { | |
4819 | notifiers[1] = copyNotifiers(gIOFirstMatchNotification, | |
4820 | kIOServiceFirstMatchState, 0xffffffff); | |
4821 | } | |
4822 | } | |
b0d623f7 | 4823 | |
0a7de745 A |
4824 | __state[1] &= ~kIOServiceConfigRunning; |
4825 | unlockForArbitration(); | |
4826 | ||
4827 | if (resourceKeys) { | |
4828 | resourceKeys->release(); | |
4829 | } | |
4830 | ||
4831 | invokeNotifiers(¬ifiers[0]); | |
4832 | invokeNotifiers(¬ifiers[1]); | |
4833 | ||
4834 | lockForArbitration(); | |
4835 | __state[1] &= ~kIOServiceConfigState; | |
4836 | scheduleTerminatePhase2(); | |
4837 | ||
4838 | _adjustBusy( -1 ); | |
4839 | unlockForArbitration(); | |
4840 | } | |
4841 | ||
4842 | UInt32 | |
4843 | IOService::_adjustBusy( SInt32 delta ) | |
4844 | { | |
4845 | IOService * next; | |
4846 | UInt32 count; | |
4847 | UInt32 result; | |
4848 | bool wasQuiet, nowQuiet, needWake; | |
4849 | ||
4850 | next = this; | |
4851 | result = __state[1] & kIOServiceBusyStateMask; | |
4852 | ||
4853 | if (delta) { | |
4854 | do { | |
4855 | if (next != this) { | |
4856 | next->lockForArbitration(); | |
4857 | } | |
4858 | count = next->__state[1] & kIOServiceBusyStateMask; | |
4859 | wasQuiet = (0 == count); | |
4860 | if (((delta < 0) && wasQuiet) || ((delta > 0) && (kIOServiceBusyMax == count))) { | |
4861 | OSReportWithBacktrace("%s: bad busy count (%d,%d)\n", next->getName(), count, delta); | |
4862 | } else { | |
4863 | count += delta; | |
4864 | } | |
4865 | next->__state[1] = (next->__state[1] & ~kIOServiceBusyStateMask) | count; | |
4866 | nowQuiet = (0 == count); | |
4867 | needWake = (0 != (kIOServiceBusyWaiterState & next->__state[1])); | |
4868 | ||
4869 | if (needWake) { | |
4870 | next->__state[1] &= ~kIOServiceBusyWaiterState; | |
4871 | IOLockLock( gIOServiceBusyLock ); | |
4872 | thread_wakeup((event_t) next); | |
4873 | IOLockUnlock( gIOServiceBusyLock ); | |
4874 | } | |
4875 | if (next != this) { | |
4876 | next->unlockForArbitration(); | |
4877 | } | |
4878 | ||
4879 | if ((wasQuiet || nowQuiet)) { | |
4880 | uint64_t regID = next->getRegistryEntryID(); | |
4881 | IOServiceTrace( | |
4882 | ((wasQuiet /*nowBusy*/) ? IOSERVICE_BUSY : IOSERVICE_NONBUSY), | |
4883 | (uintptr_t) regID, | |
4884 | (uintptr_t) (regID >> 32), | |
4885 | (uintptr_t) next, | |
4886 | 0); | |
4887 | ||
4888 | if (wasQuiet) { | |
4889 | next->__timeBusy = mach_absolute_time(); | |
4890 | } else { | |
4891 | next->__accumBusy += mach_absolute_time() - next->__timeBusy; | |
4892 | next->__timeBusy = 0; | |
4893 | } | |
b0d623f7 | 4894 | |
0a7de745 | 4895 | MessageClientsContext context; |
c0fea474 | 4896 | |
0a7de745 A |
4897 | context.service = next; |
4898 | context.type = kIOMessageServiceBusyStateChange; | |
4899 | context.argument = (void *) wasQuiet; /*nowBusy*/ | |
4900 | context.argSize = 0; | |
c0fea474 | 4901 | |
0a7de745 A |
4902 | applyToInterestNotifiers( next, gIOBusyInterest, |
4903 | &messageClientsApplier, &context ); | |
1c79356b | 4904 | |
2d21ac55 | 4905 | #if !NO_KEXTD |
0a7de745 A |
4906 | if (nowQuiet && (next == gIOServiceRoot)) { |
4907 | OSKext::considerUnloads(); | |
4908 | IOServiceTrace(IOSERVICE_REGISTRY_QUIET, 0, 0, 0, 0); | |
4909 | } | |
2d21ac55 | 4910 | #endif |
0a7de745 A |
4911 | } |
4912 | ||
4913 | delta = nowQuiet ? -1 : +1; | |
4914 | } while ((wasQuiet || nowQuiet) && (next = next->getProvider())); | |
4915 | } | |
4916 | ||
4917 | return result; | |
4918 | } | |
4919 | ||
4920 | void | |
4921 | IOService::adjustBusy( SInt32 delta ) | |
4922 | { | |
4923 | lockForArbitration(); | |
4924 | _adjustBusy( delta ); | |
4925 | unlockForArbitration(); | |
4926 | } | |
4927 | ||
4928 | uint64_t | |
4929 | IOService::getAccumulatedBusyTime( void ) | |
4930 | { | |
4931 | uint64_t accumBusy = __accumBusy; | |
4932 | uint64_t timeBusy = __timeBusy; | |
4933 | uint64_t nano; | |
1c79356b | 4934 | |
0a7de745 A |
4935 | do{ |
4936 | accumBusy = __accumBusy; | |
4937 | timeBusy = __timeBusy; | |
4938 | if (timeBusy) { | |
4939 | accumBusy += mach_absolute_time() - timeBusy; | |
4940 | } | |
4941 | }while (timeBusy != __timeBusy); | |
4942 | ||
4943 | absolutetime_to_nanoseconds(*(AbsoluteTime *)&accumBusy, &nano); | |
4944 | ||
4945 | return nano; | |
4946 | } | |
1c79356b | 4947 | |
0a7de745 A |
4948 | UInt32 |
4949 | IOService::getBusyState( void ) | |
4950 | { | |
4951 | return __state[1] & kIOServiceBusyStateMask; | |
4952 | } | |
1c79356b | 4953 | |
0a7de745 A |
4954 | IOReturn |
4955 | IOService::waitForState( UInt32 mask, UInt32 value, | |
4956 | mach_timespec_t * timeout ) | |
4957 | { | |
4958 | panic("waitForState"); | |
4959 | return kIOReturnUnsupported; | |
0b4e3aa0 A |
4960 | } |
4961 | ||
0a7de745 A |
4962 | IOReturn |
4963 | IOService::waitForState( UInt32 mask, UInt32 value, | |
4964 | uint64_t timeout ) | |
0b4e3aa0 | 4965 | { |
0a7de745 A |
4966 | bool wait; |
4967 | int waitResult = THREAD_AWAKENED; | |
4968 | bool computeDeadline = true; | |
4969 | AbsoluteTime abstime; | |
4970 | ||
4971 | do { | |
4972 | lockForArbitration(); | |
4973 | IOLockLock( gIOServiceBusyLock ); | |
4974 | wait = (value != (__state[1] & mask)); | |
4975 | if (wait) { | |
4976 | __state[1] |= kIOServiceBusyWaiterState; | |
4977 | unlockForArbitration(); | |
4978 | if (timeout != UINT64_MAX) { | |
4979 | if (computeDeadline) { | |
4980 | AbsoluteTime nsinterval; | |
4981 | nanoseconds_to_absolutetime(timeout, &nsinterval ); | |
4982 | clock_absolutetime_interval_to_deadline(nsinterval, &abstime); | |
4983 | computeDeadline = false; | |
4984 | } | |
4985 | assert_wait_deadline((event_t)this, THREAD_UNINT, __OSAbsoluteTime(abstime)); | |
4986 | } else { | |
4987 | assert_wait((event_t)this, THREAD_UNINT ); | |
4988 | } | |
4989 | } else { | |
4990 | unlockForArbitration(); | |
4991 | } | |
4992 | IOLockUnlock( gIOServiceBusyLock ); | |
4993 | if (wait) { | |
4994 | waitResult = thread_block(THREAD_CONTINUE_NULL); | |
4995 | } | |
4996 | } while (wait && (waitResult != THREAD_TIMED_OUT)); | |
4997 | ||
4998 | if (waitResult == THREAD_TIMED_OUT) { | |
4999 | return kIOReturnTimeout; | |
5000 | } else { | |
5001 | return kIOReturnSuccess; | |
5002 | } | |
5003 | } | |
5004 | ||
5005 | IOReturn | |
5006 | IOService::waitQuiet( uint64_t timeout ) | |
5007 | { | |
5008 | IOReturn ret; | |
5009 | uint32_t loops; | |
5010 | char * string = NULL; | |
5011 | char * panicString = NULL; | |
5012 | size_t len; | |
5013 | size_t panicStringLen; | |
5014 | uint64_t time; | |
5015 | uint64_t nano; | |
f427ee49 A |
5016 | bool pendingRequests; |
5017 | bool dopanic = false; | |
0a7de745 | 5018 | |
cb323159 A |
5019 | #if KASAN |
5020 | /* | |
5021 | * On kasan kernels, everything takes longer, so double the number of | |
5022 | * timeout extensions. This should help with issues like 41259215 | |
5023 | * where WindowServer was timing out waiting for kextd to get all the | |
5024 | * kasan kexts loaded and started. | |
5025 | */ | |
5026 | enum { kTimeoutExtensions = 8 }; | |
f427ee49 A |
5027 | #define WITH_IOWAITQUIET_EXTENSIONS 1 |
5028 | #elif XNU_TARGET_OS_OSX && defined(__arm64__) | |
5029 | enum { kTimeoutExtensions = 1 }; | |
5030 | #define WITH_IOWAITQUIET_EXTENSIONS 0 | |
cb323159 | 5031 | #else |
0a7de745 | 5032 | enum { kTimeoutExtensions = 4 }; |
f427ee49 | 5033 | #define WITH_IOWAITQUIET_EXTENSIONS 1 |
cb323159 | 5034 | #endif |
0a7de745 A |
5035 | |
5036 | time = mach_absolute_time(); | |
f427ee49 | 5037 | pendingRequests = false; |
0a7de745 A |
5038 | for (loops = 0; loops < kTimeoutExtensions; loops++) { |
5039 | ret = waitForState( kIOServiceBusyStateMask, 0, timeout ); | |
5040 | ||
5041 | if (loops && (kIOReturnSuccess == ret)) { | |
5042 | time = mach_absolute_time() - time; | |
5043 | absolutetime_to_nanoseconds(*(AbsoluteTime *)&time, &nano); | |
5044 | IOLog("busy extended ok[%d], (%llds, %llds)\n", | |
5045 | loops, timeout / 1000000000ULL, nano / 1000000000ULL); | |
5046 | break; | |
5047 | } else if (kIOReturnTimeout != ret) { | |
5048 | break; | |
f427ee49 | 5049 | } else if (timeout < (41ull * NSEC_PER_SEC)) { |
0a7de745 A |
5050 | break; |
5051 | } | |
5052 | ||
5053 | { | |
5054 | IORegistryIterator * iter; | |
5055 | OSOrderedSet * set; | |
5056 | OSOrderedSet * leaves; | |
5057 | IOService * next; | |
5058 | IOService * nextParent; | |
5059 | char * s; | |
5060 | size_t l; | |
5061 | ||
5062 | len = 256; | |
5063 | panicStringLen = 256; | |
5064 | if (!string) { | |
5065 | string = IONew(char, len); | |
5066 | } | |
5067 | if (!panicString) { | |
5068 | panicString = IONew(char, panicStringLen); | |
5069 | } | |
5070 | set = NULL; | |
f427ee49 | 5071 | pendingRequests = OSKext::pendingIOKitDaemonRequests(); |
0a7de745 A |
5072 | iter = IORegistryIterator::iterateOver(this, gIOServicePlane, kIORegistryIterateRecursively); |
5073 | leaves = OSOrderedSet::withCapacity(4); | |
5074 | if (iter) { | |
5075 | set = iter->iterateAll(); | |
5076 | } | |
5077 | if (string && panicString && leaves && set) { | |
5078 | string[0] = panicString[0] = 0; | |
5079 | set->setObject(this); | |
5080 | while ((next = (IOService *) set->getLastObject())) { | |
5081 | if (next->getBusyState()) { | |
5082 | if (kIOServiceModuleStallState & next->__state[1]) { | |
f427ee49 | 5083 | pendingRequests = true; |
0a7de745 A |
5084 | } |
5085 | leaves->setObject(next); | |
5086 | nextParent = next; | |
5087 | while ((nextParent = nextParent->getProvider())) { | |
5088 | set->removeObject(nextParent); | |
5089 | leaves->removeObject(nextParent); | |
5090 | } | |
5091 | } | |
5092 | set->removeObject(next); | |
5093 | } | |
5094 | s = string; | |
5095 | while ((next = (IOService *) leaves->getLastObject())) { | |
5096 | l = snprintf(s, len, "%s'%s'", ((s == string) ? "" : ", "), next->getName()); | |
5097 | if (l >= len) { | |
5098 | break; | |
5099 | } | |
5100 | s += l; | |
5101 | len -= l; | |
5102 | leaves->removeObject(next); | |
5103 | } | |
5104 | } | |
5105 | OSSafeReleaseNULL(leaves); | |
5106 | OSSafeReleaseNULL(set); | |
5107 | OSSafeReleaseNULL(iter); | |
5108 | } | |
5109 | ||
f427ee49 A |
5110 | dopanic = (kIOWaitQuietPanics & gIOKitDebug); |
5111 | #if WITH_IOWAITQUIET_EXTENSIONS | |
5112 | dopanic = (dopanic && (loops >= (kTimeoutExtensions - 1))); | |
5113 | #endif | |
0a7de745 A |
5114 | snprintf(panicString, panicStringLen, |
5115 | "%s[%d], (%llds): %s", | |
f427ee49 | 5116 | pendingRequests ? "IOKit Daemon (" kIOKitDaemonName ") stall" : "busy timeout", |
0a7de745 A |
5117 | loops, timeout / 1000000000ULL, |
5118 | string ? string : ""); | |
5119 | IOLog("%s\n", panicString); | |
5120 | if (dopanic) { | |
5121 | panic("%s", panicString); | |
5122 | } else if (!loops) { | |
5123 | getPMRootDomain()->startSpinDump(1); | |
5124 | } | |
5125 | } | |
5126 | ||
5127 | if (string) { | |
5128 | IODelete(string, char, 256); | |
5129 | } | |
5130 | if (panicString) { | |
5131 | IODelete(panicString, char, panicStringLen); | |
5132 | } | |
5133 | ||
5134 | return ret; | |
1c79356b A |
5135 | } |
5136 | ||
0a7de745 A |
5137 | IOReturn |
5138 | IOService::waitQuiet( mach_timespec_t * timeout ) | |
b0d623f7 | 5139 | { |
0a7de745 A |
5140 | uint64_t timeoutNS; |
5141 | ||
5142 | if (timeout) { | |
5143 | timeoutNS = timeout->tv_sec; | |
5144 | timeoutNS *= kSecondScale; | |
5145 | timeoutNS += timeout->tv_nsec; | |
5146 | } else { | |
5147 | timeoutNS = UINT64_MAX; | |
5148 | } | |
b0d623f7 | 5149 | |
0a7de745 A |
5150 | return waitQuiet(timeoutNS); |
5151 | } | |
b0d623f7 | 5152 | |
0a7de745 A |
5153 | bool |
5154 | IOService::serializeProperties( OSSerialize * s ) const | |
1c79356b A |
5155 | { |
5156 | #if 0 | |
0a7de745 A |
5157 | ((IOService *)this)->setProperty(((IOService *)this)->__state, |
5158 | sizeof(__state), "__state"); | |
1c79356b | 5159 | #endif |
0a7de745 | 5160 | return super::serializeProperties(s); |
1c79356b A |
5161 | } |
5162 | ||
f427ee49 A |
5163 | void |
5164 | IOService::resetRematchProperties() | |
5165 | { | |
5166 | removeProperty(gIORematchCountKey); | |
5167 | removeProperty(gIORematchPersonalityKey); | |
5168 | } | |
5169 | ||
1c79356b | 5170 | |
0a7de745 A |
5171 | void |
5172 | _IOConfigThread::main(void * arg, wait_result_t result) | |
1c79356b | 5173 | { |
0a7de745 A |
5174 | _IOConfigThread * self = (_IOConfigThread *) arg; |
5175 | _IOServiceJob * job; | |
5176 | IOService * nub; | |
5177 | bool alive = true; | |
5178 | kern_return_t kr; | |
5179 | thread_precedence_policy_data_t precedence = { -1 }; | |
2d21ac55 | 5180 | |
0a7de745 A |
5181 | kr = thread_policy_set(current_thread(), |
5182 | THREAD_PRECEDENCE_POLICY, | |
5183 | (thread_policy_t) &precedence, | |
5184 | THREAD_PRECEDENCE_POLICY_COUNT); | |
5185 | if (KERN_SUCCESS != kr) { | |
5186 | IOLog("thread_policy_set(%d)\n", kr); | |
5187 | } | |
1c79356b | 5188 | |
0a7de745 | 5189 | do { |
0b4e3aa0 | 5190 | // randomDelay(); |
1c79356b | 5191 | |
0a7de745 | 5192 | semaphore_wait( gJobsSemaphore ); |
1c79356b | 5193 | |
0a7de745 A |
5194 | IOTakeLock( gJobsLock ); |
5195 | job = (_IOServiceJob *) gJobs->getFirstObject(); | |
5196 | job->retain(); | |
5197 | gJobs->removeObject(job); | |
5198 | if (job) { | |
5199 | gOutstandingJobs--; | |
1c79356b | 5200 | // gNumConfigThreads--; // we're out of service |
0a7de745 A |
5201 | gNumWaitingThreads--; // we're out of service |
5202 | } | |
5203 | IOUnlock( gJobsLock ); | |
1c79356b | 5204 | |
0a7de745 A |
5205 | if (job) { |
5206 | nub = job->nub; | |
1c79356b | 5207 | |
0a7de745 A |
5208 | if (gIOKitDebug & kIOLogConfig) { |
5209 | LOG("config(%p): starting on %s, %d\n", | |
5210 | IOSERVICE_OBFUSCATE(IOThreadSelf()), job->nub->getName(), job->type); | |
5211 | } | |
1c79356b | 5212 | |
0a7de745 A |
5213 | switch (job->type) { |
5214 | case kMatchNubJob: | |
5215 | nub->doServiceMatch( job->options ); | |
5216 | break; | |
1c79356b | 5217 | |
0a7de745 A |
5218 | default: |
5219 | LOG("config(%p): strange type (%d)\n", | |
5220 | IOSERVICE_OBFUSCATE(IOThreadSelf()), job->type ); | |
5221 | break; | |
5222 | } | |
1c79356b | 5223 | |
0a7de745 A |
5224 | nub->release(); |
5225 | job->release(); | |
1c79356b | 5226 | |
0a7de745 A |
5227 | IOTakeLock( gJobsLock ); |
5228 | alive = (gOutstandingJobs > gNumWaitingThreads); | |
5229 | if (alive) { | |
5230 | gNumWaitingThreads++; // back in service | |
5231 | } | |
1c79356b | 5232 | // gNumConfigThreads++; |
0a7de745 A |
5233 | else { |
5234 | if (0 == --gNumConfigThreads) { | |
0b4e3aa0 | 5235 | // IOLog("MATCH IDLE\n"); |
0a7de745 A |
5236 | IOLockWakeup( gJobsLock, (event_t) &gNumConfigThreads, /* one-thread */ false ); |
5237 | } | |
5238 | } | |
5239 | IOUnlock( gJobsLock ); | |
5240 | } | |
5241 | } while (alive); | |
5242 | ||
5243 | if (gIOKitDebug & kIOLogConfig) { | |
5244 | LOG("config(%p): terminating\n", IOSERVICE_OBFUSCATE(IOThreadSelf())); | |
5245 | } | |
5246 | ||
5247 | self->release(); | |
5248 | } | |
5249 | ||
5250 | IOReturn | |
5251 | IOService::waitMatchIdle( UInt32 msToWait ) | |
5252 | { | |
5253 | bool wait; | |
5254 | int waitResult = THREAD_AWAKENED; | |
5255 | bool computeDeadline = true; | |
5256 | AbsoluteTime deadline; | |
5257 | ||
5258 | IOLockLock( gJobsLock ); | |
5259 | do { | |
5260 | wait = (0 != gNumConfigThreads); | |
5261 | if (wait) { | |
5262 | if (msToWait) { | |
5263 | if (computeDeadline) { | |
5264 | clock_interval_to_deadline( | |
5265 | msToWait, kMillisecondScale, &deadline ); | |
5266 | computeDeadline = false; | |
5267 | } | |
5268 | waitResult = IOLockSleepDeadline( gJobsLock, &gNumConfigThreads, | |
5269 | deadline, THREAD_UNINT ); | |
5270 | } else { | |
5271 | waitResult = IOLockSleep( gJobsLock, &gNumConfigThreads, | |
5272 | THREAD_UNINT ); | |
5273 | } | |
5274 | } | |
5275 | } while (wait && (waitResult != THREAD_TIMED_OUT)); | |
9bccf70c | 5276 | IOLockUnlock( gJobsLock ); |
0b4e3aa0 | 5277 | |
0a7de745 A |
5278 | if (waitResult == THREAD_TIMED_OUT) { |
5279 | return kIOReturnTimeout; | |
5280 | } else { | |
5281 | return kIOReturnSuccess; | |
5282 | } | |
0b4e3aa0 A |
5283 | } |
5284 | ||
0a7de745 A |
5285 | void |
5286 | IOService::cpusRunning(void) | |
3e170ce0 | 5287 | { |
0a7de745 | 5288 | gCPUsRunning = true; |
3e170ce0 A |
5289 | } |
5290 | ||
0a7de745 A |
5291 | void |
5292 | _IOServiceJob::pingConfig( _IOServiceJob * job ) | |
1c79356b | 5293 | { |
0a7de745 A |
5294 | int count; |
5295 | bool create; | |
f427ee49 | 5296 | IOService * nub; |
1c79356b | 5297 | |
0a7de745 | 5298 | assert( job ); |
f427ee49 | 5299 | nub = job->nub; |
1c79356b | 5300 | |
0a7de745 | 5301 | IOTakeLock( gJobsLock ); |
1c79356b | 5302 | |
0a7de745 | 5303 | gOutstandingJobs++; |
f427ee49 A |
5304 | if (nub == gIOResources) { |
5305 | gJobs->setFirstObject( job ); | |
5306 | } else { | |
5307 | gJobs->setLastObject( job ); | |
5308 | } | |
1c79356b | 5309 | |
0a7de745 | 5310 | count = gNumWaitingThreads; |
1c79356b A |
5311 | // if( gNumConfigThreads) count++;// assume we're called from a config thread |
5312 | ||
0a7de745 | 5313 | create = ((gOutstandingJobs > count) |
f427ee49 A |
5314 | && ((gNumConfigThreads < gMaxConfigThreads) |
5315 | || (nub == gIOResources) | |
0a7de745 A |
5316 | || !gCPUsRunning)); |
5317 | if (create) { | |
5318 | gNumConfigThreads++; | |
5319 | gNumWaitingThreads++; | |
f427ee49 A |
5320 | if (gNumConfigThreads > gHighNumConfigThreads) { |
5321 | gHighNumConfigThreads = gNumConfigThreads; | |
5322 | } | |
0a7de745 | 5323 | } |
1c79356b | 5324 | |
0a7de745 | 5325 | IOUnlock( gJobsLock ); |
1c79356b | 5326 | |
0a7de745 | 5327 | job->release(); |
1c79356b | 5328 | |
0a7de745 A |
5329 | if (create) { |
5330 | if (gIOKitDebug & kIOLogConfig) { | |
5331 | LOG("config(%d): creating\n", gNumConfigThreads - 1); | |
5332 | } | |
f427ee49 | 5333 | _IOConfigThread::configThread(nub->getName()); |
0a7de745 | 5334 | } |
1c79356b | 5335 | |
0a7de745 | 5336 | semaphore_signal( gJobsSemaphore ); |
1c79356b A |
5337 | } |
5338 | ||
0a7de745 A |
5339 | struct IOServiceMatchContext { |
5340 | OSDictionary * table; | |
5341 | OSObject * result; | |
5342 | uint32_t options; | |
5343 | uint32_t state; | |
5344 | uint32_t count; | |
5345 | uint32_t done; | |
316670eb A |
5346 | }; |
5347 | ||
0a7de745 A |
5348 | bool |
5349 | IOService::instanceMatch(const OSObject * entry, void * context) | |
1c79356b | 5350 | { |
0a7de745 A |
5351 | IOServiceMatchContext * ctx = (typeof(ctx))context; |
5352 | IOService * service = (typeof(service))entry; | |
5353 | OSDictionary * table = ctx->table; | |
5354 | uint32_t options = ctx->options; | |
5355 | uint32_t state = ctx->state; | |
5356 | uint32_t done; | |
5357 | bool match; | |
1c79356b | 5358 | |
0a7de745 A |
5359 | done = 0; |
5360 | do{ | |
5361 | match = ((state == (state & service->__state[0])) | |
5362 | && (0 == (service->__state[0] & kIOServiceInactiveState))); | |
5363 | if (!match) { | |
5364 | break; | |
5365 | } | |
0a7de745 | 5366 | match = service->matchInternal(table, options, &done); |
f427ee49 A |
5367 | if (match) { |
5368 | ctx->count += table->getCount(); | |
5369 | ctx->done += done; | |
5370 | } | |
0a7de745 A |
5371 | }while (false); |
5372 | if (!match) { | |
5373 | return false; | |
5374 | } | |
316670eb | 5375 | |
0a7de745 | 5376 | if ((kIONotifyOnce & options) && (ctx->done == ctx->count)) { |
b0d623f7 | 5377 | service->retain(); |
0a7de745 A |
5378 | ctx->result = service; |
5379 | return true; | |
5380 | } else if (!ctx->result) { | |
5381 | ctx->result = OSSet::withObjects((const OSObject **) &service, 1, 1); | |
5382 | } else { | |
5383 | ((OSSet *)ctx->result)->setObject(service); | |
316670eb | 5384 | } |
0a7de745 A |
5385 | return false; |
5386 | } | |
5387 | ||
5388 | // internal - call with gNotificationLock | |
5389 | OSObject * | |
5390 | IOService::copyExistingServices( OSDictionary * matching, | |
5391 | IOOptionBits inState, IOOptionBits options ) | |
5392 | { | |
cb323159 | 5393 | OSObject * current = NULL; |
0a7de745 A |
5394 | OSIterator * iter; |
5395 | IOService * service; | |
5396 | OSObject * obj; | |
5397 | OSString * str; | |
5398 | ||
5399 | if (!matching) { | |
cb323159 | 5400 | return NULL; |
316670eb A |
5401 | } |
5402 | ||
0a7de745 A |
5403 | #if MATCH_DEBUG |
5404 | OSSerialize * s = OSSerialize::withCapacity(128); | |
5405 | matching->serialize(s); | |
5406 | #endif | |
316670eb | 5407 | |
0a7de745 A |
5408 | if ((obj = matching->getObject(gIOProviderClassKey)) |
5409 | && gIOResourcesKey | |
5410 | && gIOResourcesKey->isEqualTo(obj) | |
5411 | && (service = gIOResources)) { | |
5412 | if ((inState == (service->__state[0] & inState)) | |
5413 | && (0 == (service->__state[0] & kIOServiceInactiveState)) | |
5414 | && service->matchPassive(matching, options)) { | |
5415 | if (options & kIONotifyOnce) { | |
5416 | service->retain(); | |
5417 | current = service; | |
5418 | } else { | |
5419 | current = OSSet::withObjects((const OSObject **) &service, 1, 1 ); | |
5420 | } | |
5421 | } | |
5422 | } else { | |
5423 | IOServiceMatchContext ctx; | |
f427ee49 A |
5424 | |
5425 | options |= kIOServiceClassDone; | |
0a7de745 A |
5426 | ctx.table = matching; |
5427 | ctx.state = inState; | |
5428 | ctx.count = 0; | |
5429 | ctx.done = 0; | |
5430 | ctx.options = options; | |
cb323159 | 5431 | ctx.result = NULL; |
0a7de745 A |
5432 | |
5433 | if ((str = OSDynamicCast(OSString, obj))) { | |
5434 | const OSSymbol * sym = OSSymbol::withString(str); | |
5435 | OSMetaClass::applyToInstancesOfClassName(sym, instanceMatch, &ctx); | |
5436 | sym->release(); | |
5437 | } else { | |
5438 | IOService::gMetaClass.applyToInstances(instanceMatch, &ctx); | |
5439 | } | |
316670eb | 5440 | |
f427ee49 A |
5441 | if (((!(options & kIONotifyOnce) || !ctx.result)) |
5442 | && matching->getObject(gIOCompatibilityMatchKey)) { | |
5443 | IOServiceCompatibility::gMetaClass.applyToInstances(instanceMatch, &ctx); | |
5444 | } | |
316670eb | 5445 | |
0a7de745 | 5446 | current = ctx.result; |
f427ee49 | 5447 | options |= kIOServiceInternalDone; |
0a7de745 | 5448 | if (current && (ctx.done != ctx.count)) { |
f427ee49 | 5449 | OSSet * source = OSDynamicCast(OSSet, current); |
cb323159 | 5450 | current = NULL; |
0a7de745 A |
5451 | while ((service = (IOService *) source->getAnyObject())) { |
5452 | if (service->matchPassive(matching, options)) { | |
5453 | if (options & kIONotifyOnce) { | |
5454 | service->retain(); | |
5455 | current = service; | |
5456 | break; | |
5457 | } | |
5458 | if (current) { | |
5459 | ((OSSet *)current)->setObject( service ); | |
5460 | } else { | |
5461 | current = OSSet::withObjects( | |
5462 | (const OSObject **) &service, 1, 1 ); | |
5463 | } | |
5464 | } | |
5465 | source->removeObject(service); | |
91447636 | 5466 | } |
0a7de745 | 5467 | source->release(); |
91447636 | 5468 | } |
91447636 | 5469 | } |
316670eb | 5470 | |
0a7de745 | 5471 | #if MATCH_DEBUG |
316670eb | 5472 | { |
0a7de745 A |
5473 | OSObject * _current = 0; |
5474 | ||
5475 | iter = IORegistryIterator::iterateOver( gIOServicePlane, | |
5476 | kIORegistryIterateRecursively ); | |
5477 | if (iter) { | |
5478 | do { | |
5479 | iter->reset(); | |
5480 | while ((service = (IOService *) iter->getNextObject())) { | |
5481 | if ((inState == (service->__state[0] & inState)) | |
5482 | && (0 == (service->__state[0] & kIOServiceInactiveState)) | |
5483 | && service->matchPassive(matching, 0)) { | |
5484 | if (options & kIONotifyOnce) { | |
5485 | service->retain(); | |
5486 | _current = service; | |
5487 | break; | |
5488 | } | |
5489 | if (_current) { | |
5490 | ((OSSet *)_current)->setObject( service ); | |
5491 | } else { | |
5492 | _current = OSSet::withObjects( | |
5493 | (const OSObject **) &service, 1, 1 ); | |
5494 | } | |
5495 | } | |
5496 | } | |
5497 | } while (!service && !iter->isValid()); | |
5498 | iter->release(); | |
5499 | } | |
5500 | ||
0a7de745 A |
5501 | if (((current != 0) != (_current != 0)) |
5502 | || (current && _current && !current->isEqualTo(_current))) { | |
5503 | OSSerialize * s1 = OSSerialize::withCapacity(128); | |
5504 | OSSerialize * s2 = OSSerialize::withCapacity(128); | |
5505 | current->serialize(s1); | |
5506 | _current->serialize(s2); | |
5507 | kprintf("**mismatch** %p %p\n%s\n%s\n%s\n", IOSERVICE_OBFUSCATE(current), | |
5508 | IOSERVICE_OBFUSCATE(_current), s->text(), s1->text(), s2->text()); | |
5509 | s1->release(); | |
5510 | s2->release(); | |
5511 | } | |
316670eb | 5512 | |
0a7de745 A |
5513 | if (_current) { |
5514 | _current->release(); | |
5515 | } | |
5516 | } | |
316670eb | 5517 | |
0a7de745 | 5518 | s->release(); |
316670eb | 5519 | #endif |
1c79356b | 5520 | |
0a7de745 A |
5521 | if (current && (0 == (options & (kIONotifyOnce | kIOServiceExistingSet)))) { |
5522 | iter = OSCollectionIterator::withCollection((OSSet *)current ); | |
5523 | current->release(); | |
5524 | current = iter; | |
5525 | } | |
1c79356b | 5526 | |
0a7de745 | 5527 | return current; |
1c79356b A |
5528 | } |
5529 | ||
5530 | // public version | |
0a7de745 A |
5531 | OSIterator * |
5532 | IOService::getMatchingServices( OSDictionary * matching ) | |
1c79356b | 5533 | { |
0a7de745 A |
5534 | OSIterator * iter; |
5535 | ||
5536 | // is a lock even needed? | |
5537 | LOCKWRITENOTIFY(); | |
1c79356b | 5538 | |
0a7de745 A |
5539 | iter = (OSIterator *) copyExistingServices( matching, |
5540 | kIOServiceMatchedState ); | |
1c79356b | 5541 | |
0a7de745 | 5542 | UNLOCKNOTIFY(); |
1c79356b | 5543 | |
0a7de745 | 5544 | return iter; |
1c79356b A |
5545 | } |
5546 | ||
0a7de745 A |
5547 | IOService * |
5548 | IOService::copyMatchingService( OSDictionary * matching ) | |
316670eb | 5549 | { |
0a7de745 | 5550 | IOService * service; |
316670eb | 5551 | |
0a7de745 A |
5552 | // is a lock even needed? |
5553 | LOCKWRITENOTIFY(); | |
316670eb | 5554 | |
0a7de745 A |
5555 | service = (IOService *) copyExistingServices( matching, |
5556 | kIOServiceMatchedState, kIONotifyOnce ); | |
316670eb | 5557 | |
0a7de745 A |
5558 | UNLOCKNOTIFY(); |
5559 | ||
5560 | return service; | |
316670eb A |
5561 | } |
5562 | ||
0a7de745 A |
5563 | struct _IOServiceMatchingNotificationHandlerRef { |
5564 | IOServiceNotificationHandler handler; | |
5565 | void * ref; | |
b0d623f7 A |
5566 | }; |
5567 | ||
0a7de745 A |
5568 | static bool |
5569 | _IOServiceMatchingNotificationHandler( void * target, void * refCon, | |
5570 | IOService * newService, | |
5571 | IONotifier * notifier ) | |
b0d623f7 | 5572 | { |
0a7de745 | 5573 | return (*((_IOServiceNotifier *) notifier)->compatHandler)(target, refCon, newService); |
b0d623f7 | 5574 | } |
1c79356b A |
5575 | |
5576 | // internal - call with gNotificationLock | |
0a7de745 A |
5577 | IONotifier * |
5578 | IOService::setNotification( | |
5579 | const OSSymbol * type, OSDictionary * matching, | |
5580 | IOServiceMatchingNotificationHandler handler, void * target, void * ref, | |
5581 | SInt32 priority ) | |
1c79356b | 5582 | { |
cb323159 | 5583 | _IOServiceNotifier * notify = NULL; |
0a7de745 | 5584 | OSOrderedSet * set; |
1c79356b | 5585 | |
0a7de745 | 5586 | if (!matching) { |
cb323159 | 5587 | return NULL; |
b0d623f7 | 5588 | } |
1c79356b | 5589 | |
0a7de745 A |
5590 | notify = new _IOServiceNotifier; |
5591 | if (notify && !notify->init()) { | |
5592 | notify->release(); | |
cb323159 | 5593 | notify = NULL; |
0a7de745 | 5594 | } |
1c79356b | 5595 | |
0a7de745 A |
5596 | if (notify) { |
5597 | notify->handler = handler; | |
5598 | notify->target = target; | |
5599 | notify->type = type; | |
5600 | notify->matching = matching; | |
5601 | matching->retain(); | |
5602 | if (handler == &_IOServiceMatchingNotificationHandler) { | |
5603 | notify->compatHandler = ((_IOServiceMatchingNotificationHandlerRef *)ref)->handler; | |
5604 | notify->ref = ((_IOServiceMatchingNotificationHandlerRef *)ref)->ref; | |
5605 | } else { | |
5606 | notify->ref = ref; | |
5607 | } | |
5608 | notify->priority = priority; | |
5609 | notify->state = kIOServiceNotifyEnable; | |
5610 | queue_init( ¬ify->handlerInvocations ); | |
5611 | ||
5612 | ////// queue | |
5613 | ||
cb323159 | 5614 | if (NULL == (set = (OSOrderedSet *) gNotifications->getObject( type ))) { |
0a7de745 | 5615 | set = OSOrderedSet::withCapacity( 1, |
cb323159 | 5616 | IONotifyOrdering, NULL ); |
0a7de745 A |
5617 | if (set) { |
5618 | gNotifications->setObject( type, set ); | |
5619 | set->release(); | |
5620 | } | |
5621 | } | |
5622 | notify->whence = set; | |
5623 | if (set) { | |
5624 | set->setObject( notify ); | |
5625 | } | |
5626 | } | |
1c79356b | 5627 | |
0a7de745 | 5628 | return notify; |
1c79356b A |
5629 | } |
5630 | ||
5631 | // internal - call with gNotificationLock | |
0a7de745 A |
5632 | IONotifier * |
5633 | IOService::doInstallNotification( | |
5634 | const OSSymbol * type, OSDictionary * matching, | |
5635 | IOServiceMatchingNotificationHandler handler, | |
5636 | void * target, void * ref, | |
5637 | SInt32 priority, OSIterator ** existing ) | |
5638 | { | |
5639 | OSIterator * exist; | |
5640 | IONotifier * notify; | |
5641 | IOOptionBits inState; | |
5642 | ||
5643 | if (!matching) { | |
cb323159 | 5644 | return NULL; |
0a7de745 | 5645 | } |
39037602 | 5646 | |
0a7de745 A |
5647 | if (type == gIOPublishNotification) { |
5648 | inState = kIOServiceRegisteredState; | |
5649 | } else if (type == gIOFirstPublishNotification) { | |
5650 | inState = kIOServiceFirstPublishState; | |
5651 | } else if (type == gIOMatchedNotification) { | |
5652 | inState = kIOServiceMatchedState; | |
5653 | } else if (type == gIOFirstMatchNotification) { | |
5654 | inState = kIOServiceFirstMatchState; | |
5655 | } else if ((type == gIOTerminatedNotification) || (type == gIOWillTerminateNotification)) { | |
5656 | inState = 0; | |
5657 | } else { | |
cb323159 | 5658 | return NULL; |
0a7de745 | 5659 | } |
39037602 | 5660 | |
0a7de745 | 5661 | notify = setNotification( type, matching, handler, target, ref, priority ); |
1c79356b | 5662 | |
0a7de745 A |
5663 | if (inState) { |
5664 | // get the current set | |
5665 | exist = (OSIterator *) copyExistingServices( matching, inState ); | |
5666 | } else { | |
cb323159 | 5667 | exist = NULL; |
0a7de745 | 5668 | } |
1c79356b | 5669 | |
0a7de745 | 5670 | *existing = exist; |
1c79356b | 5671 | |
0a7de745 | 5672 | return notify; |
1c79356b A |
5673 | } |
5674 | ||
b0d623f7 | 5675 | #if !defined(__LP64__) |
0a7de745 A |
5676 | IONotifier * |
5677 | IOService::installNotification(const OSSymbol * type, OSDictionary * matching, | |
5678 | IOServiceNotificationHandler handler, | |
5679 | void * target, void * refCon, | |
5680 | SInt32 priority, OSIterator ** existing ) | |
5681 | { | |
5682 | IONotifier * result; | |
5683 | _IOServiceMatchingNotificationHandlerRef ref; | |
5684 | ref.handler = handler; | |
5685 | ref.ref = refCon; | |
5686 | ||
5687 | result = (_IOServiceNotifier *) installNotification( type, matching, | |
5688 | &_IOServiceMatchingNotificationHandler, | |
5689 | target, &ref, priority, existing ); | |
5690 | if (result) { | |
5691 | matching->release(); | |
5692 | } | |
b0d623f7 | 5693 | |
0a7de745 | 5694 | return result; |
b0d623f7 | 5695 | } |
f427ee49 | 5696 | |
b0d623f7 A |
5697 | #endif /* !defined(__LP64__) */ |
5698 | ||
1c79356b | 5699 | |
0a7de745 A |
5700 | IONotifier * |
5701 | IOService::installNotification( | |
5702 | const OSSymbol * type, OSDictionary * matching, | |
5703 | IOServiceMatchingNotificationHandler handler, | |
5704 | void * target, void * ref, | |
5705 | SInt32 priority, OSIterator ** existing ) | |
1c79356b | 5706 | { |
0a7de745 | 5707 | IONotifier * notify; |
1c79356b | 5708 | |
0a7de745 | 5709 | LOCKWRITENOTIFY(); |
1c79356b | 5710 | |
0a7de745 A |
5711 | notify = doInstallNotification( type, matching, handler, target, ref, |
5712 | priority, existing ); | |
1c79356b | 5713 | |
0a7de745 A |
5714 | // in case handler remove()s |
5715 | if (notify) { | |
5716 | notify->retain(); | |
5717 | } | |
39037602 | 5718 | |
0a7de745 | 5719 | UNLOCKNOTIFY(); |
1c79356b | 5720 | |
0a7de745 | 5721 | return notify; |
1c79356b A |
5722 | } |
5723 | ||
0a7de745 A |
5724 | IONotifier * |
5725 | IOService::addNotification( | |
5726 | const OSSymbol * type, OSDictionary * matching, | |
5727 | IOServiceNotificationHandler handler, | |
5728 | void * target, void * refCon, | |
5729 | SInt32 priority ) | |
b0d623f7 | 5730 | { |
0a7de745 A |
5731 | IONotifier * result; |
5732 | _IOServiceMatchingNotificationHandlerRef ref; | |
b0d623f7 | 5733 | |
0a7de745 A |
5734 | ref.handler = handler; |
5735 | ref.ref = refCon; | |
5736 | ||
5737 | result = addMatchingNotification(type, matching, &_IOServiceMatchingNotificationHandler, | |
5738 | target, &ref, priority); | |
5739 | ||
5740 | if (result) { | |
5741 | matching->release(); | |
5742 | } | |
b0d623f7 | 5743 | |
0a7de745 | 5744 | return result; |
b0d623f7 A |
5745 | } |
5746 | ||
0a7de745 A |
5747 | IONotifier * |
5748 | IOService::addMatchingNotification( | |
5749 | const OSSymbol * type, OSDictionary * matching, | |
5750 | IOServiceMatchingNotificationHandler handler, | |
5751 | void * target, void * ref, | |
5752 | SInt32 priority ) | |
1c79356b | 5753 | { |
0a7de745 A |
5754 | OSIterator * existing = NULL; |
5755 | IONotifier * ret; | |
5756 | _IOServiceNotifier * notify; | |
5757 | IOService * next; | |
1c79356b | 5758 | |
0a7de745 A |
5759 | ret = notify = (_IOServiceNotifier *) installNotification( type, matching, |
5760 | handler, target, ref, priority, &existing ); | |
5761 | if (!ret) { | |
cb323159 | 5762 | return NULL; |
0a7de745 | 5763 | } |
1c79356b | 5764 | |
0a7de745 A |
5765 | // send notifications for existing set |
5766 | if (existing) { | |
5767 | while ((next = (IOService *) existing->getNextObject())) { | |
5768 | if (0 == (next->__state[0] & kIOServiceInactiveState)) { | |
5769 | next->invokeNotifier( notify ); | |
5770 | } | |
5771 | } | |
5772 | existing->release(); | |
1c79356b | 5773 | } |
1c79356b | 5774 | |
0a7de745 | 5775 | LOCKWRITENOTIFY(); |
cb323159 | 5776 | bool removed = (NULL == notify->whence); |
0a7de745 A |
5777 | notify->release(); |
5778 | if (removed) { | |
5779 | ret = gIOServiceNullNotifier; | |
5780 | } | |
5781 | UNLOCKNOTIFY(); | |
39037602 | 5782 | |
0a7de745 | 5783 | return ret; |
1c79356b A |
5784 | } |
5785 | ||
d9a64523 A |
5786 | static bool |
5787 | IOServiceMatchingNotificationHandlerToBlock( void * target __unused, void * refCon, | |
0a7de745 A |
5788 | IOService * newService, |
5789 | IONotifier * notifier ) | |
d9a64523 | 5790 | { |
0a7de745 | 5791 | return ((IOServiceMatchingNotificationHandlerBlock) refCon)(newService, notifier); |
d9a64523 A |
5792 | } |
5793 | ||
0a7de745 A |
5794 | IONotifier * |
5795 | IOService::addMatchingNotification( | |
5796 | const OSSymbol * type, OSDictionary * matching, | |
5797 | SInt32 priority, | |
5798 | IOServiceMatchingNotificationHandlerBlock handler) | |
d9a64523 | 5799 | { |
0a7de745 A |
5800 | IONotifier * notify; |
5801 | void * block; | |
d9a64523 | 5802 | |
0a7de745 A |
5803 | block = Block_copy(handler); |
5804 | if (!block) { | |
5805 | return NULL; | |
5806 | } | |
d9a64523 | 5807 | |
0a7de745 A |
5808 | notify = addMatchingNotification(type, matching, |
5809 | &IOServiceMatchingNotificationHandlerToBlock, NULL, block, priority); | |
d9a64523 | 5810 | |
0a7de745 A |
5811 | if (!notify) { |
5812 | Block_release(block); | |
5813 | } | |
d9a64523 | 5814 | |
0a7de745 | 5815 | return notify; |
d9a64523 A |
5816 | } |
5817 | ||
f427ee49 A |
5818 | void |
5819 | IOService::userServerCheckInTokenNotificationHandler( | |
5820 | __unused IOUserServerCheckInToken *token, | |
5821 | void *ref) | |
5822 | { | |
5823 | LOCKWRITENOTIFY(); | |
5824 | WAKEUPNOTIFY(ref); | |
5825 | UNLOCKNOTIFY(); | |
5826 | } | |
d9a64523 | 5827 | |
0a7de745 A |
5828 | bool |
5829 | IOService::syncNotificationHandler( | |
5830 | void * /* target */, void * ref, | |
5831 | IOService * newService, | |
5832 | IONotifier * notifier ) | |
1c79356b | 5833 | { |
0a7de745 A |
5834 | LOCKWRITENOTIFY(); |
5835 | if (!*((IOService **) ref)) { | |
5836 | newService->retain(); | |
5837 | (*(IOService **) ref) = newService; | |
5838 | WAKEUPNOTIFY(ref); | |
5839 | } | |
5840 | UNLOCKNOTIFY(); | |
1c79356b | 5841 | |
0a7de745 | 5842 | return false; |
1c79356b A |
5843 | } |
5844 | ||
0a7de745 | 5845 | IOService * |
f427ee49 A |
5846 | IOService::waitForMatchingServiceWithToken( OSDictionary * matching, |
5847 | uint64_t timeout, | |
5848 | IOUserServerCheckInToken * checkInToken) | |
1c79356b | 5849 | { |
cb323159 | 5850 | IONotifier * notify = NULL; |
0a7de745 A |
5851 | // priority doesn't help us much since we need a thread wakeup |
5852 | SInt32 priority = 0; | |
5853 | IOService * result; | |
1c79356b | 5854 | |
0a7de745 | 5855 | if (!matching) { |
cb323159 | 5856 | return NULL; |
b0d623f7 | 5857 | } |
b0d623f7 | 5858 | |
0a7de745 | 5859 | result = NULL; |
1c79356b | 5860 | |
f427ee49 A |
5861 | #if DEBUG || DEVELOPMENT |
5862 | char currentName[MAXTHREADNAMESIZE]; | |
5863 | char newName[MAXTHREADNAMESIZE]; | |
5864 | OSObject * obj; | |
5865 | OSString * str; | |
5866 | OSDictionary * dict; | |
5867 | ||
5868 | currentName[0] = '\0'; | |
5869 | if (thread_has_thread_name(current_thread())) { | |
5870 | dict = matching; | |
5871 | obj = matching->getObject(gIOPropertyMatchKey); | |
5872 | if ((dict = OSDynamicCast(OSDictionary, obj))) { | |
5873 | OSObject * result __block = NULL; | |
5874 | dict->iterateObjects(^bool (const OSSymbol * sym, OSObject * value) { | |
5875 | result = __DECONST(OSObject *, sym); | |
5876 | return true; | |
5877 | }); | |
5878 | obj = result; | |
5879 | } | |
5880 | if (!obj) { | |
5881 | obj = matching->getObject(gIOResourceMatchKey); | |
5882 | } | |
5883 | if (!obj) { | |
5884 | obj = matching->getObject(gIONameMatchKey); | |
5885 | } | |
5886 | if (!obj) { | |
5887 | obj = matching->getObject(gIOProviderClassKey); | |
5888 | } | |
5889 | if ((str = OSDynamicCast(OSString, obj))) { | |
5890 | thread_get_thread_name(current_thread(), currentName); | |
5891 | snprintf(newName, sizeof(newName), "Waiting_'%s'", str->getCStringNoCopy()); | |
5892 | thread_set_thread_name(current_thread(), newName); | |
5893 | } | |
5894 | } | |
5895 | #endif /* DEBUG || DEVELOPMENT */ | |
5896 | ||
0a7de745 A |
5897 | LOCKWRITENOTIFY(); |
5898 | do{ | |
f427ee49 A |
5899 | if (checkInToken) { |
5900 | checkInToken->setNoSendersNotification(&IOService::userServerCheckInTokenNotificationHandler, | |
5901 | &result); | |
5902 | } | |
0a7de745 A |
5903 | result = (IOService *) copyExistingServices( matching, |
5904 | kIOServiceMatchedState, kIONotifyOnce ); | |
5905 | if (result) { | |
5906 | break; | |
5907 | } | |
5908 | notify = IOService::setNotification( gIOMatchedNotification, matching, | |
cb323159 | 5909 | &IOService::syncNotificationHandler, (void *) NULL, |
0a7de745 A |
5910 | &result, priority ); |
5911 | if (!notify) { | |
5912 | break; | |
5913 | } | |
5914 | if (UINT64_MAX != timeout) { | |
5915 | AbsoluteTime deadline; | |
5916 | nanoseconds_to_absolutetime(timeout, &deadline); | |
5917 | clock_absolutetime_interval_to_deadline(deadline, &deadline); | |
5918 | SLEEPNOTIFYTO(&result, deadline); | |
5919 | } else { | |
5920 | SLEEPNOTIFY(&result); | |
5921 | } | |
5922 | }while (false); | |
5923 | ||
5924 | UNLOCKNOTIFY(); | |
b0d623f7 | 5925 | |
f427ee49 A |
5926 | #if DEBUG || DEVELOPMENT |
5927 | if (currentName[0]) { | |
5928 | thread_set_thread_name(current_thread(), currentName); | |
5929 | } | |
5930 | #endif /* DEBUG || DEVELOPMENT */ | |
5931 | ||
0a7de745 A |
5932 | if (notify) { |
5933 | notify->remove(); // dequeues | |
5934 | } | |
f427ee49 A |
5935 | |
5936 | if (checkInToken) { | |
5937 | checkInToken->clearNotification(); | |
5938 | } | |
5939 | ||
0a7de745 | 5940 | return result; |
b0d623f7 A |
5941 | } |
5942 | ||
f427ee49 A |
5943 | IOService * |
5944 | IOService::waitForMatchingService( OSDictionary * matching, | |
5945 | uint64_t timeout) | |
5946 | { | |
5947 | return IOService::waitForMatchingServiceWithToken(matching, timeout, NULL); | |
5948 | } | |
5949 | ||
0a7de745 A |
5950 | IOService * |
5951 | IOService::waitForService( OSDictionary * matching, | |
5952 | mach_timespec_t * timeout ) | |
b0d623f7 | 5953 | { |
0a7de745 A |
5954 | IOService * result; |
5955 | uint64_t timeoutNS; | |
b0d623f7 | 5956 | |
0a7de745 A |
5957 | if (timeout) { |
5958 | timeoutNS = timeout->tv_sec; | |
5959 | timeoutNS *= kSecondScale; | |
5960 | timeoutNS += timeout->tv_nsec; | |
5961 | } else { | |
5962 | timeoutNS = UINT64_MAX; | |
5963 | } | |
b0d623f7 | 5964 | |
0a7de745 | 5965 | result = waitForMatchingService(matching, timeoutNS); |
1c79356b | 5966 | |
0a7de745 A |
5967 | matching->release(); |
5968 | if (result) { | |
5969 | result->release(); | |
5970 | } | |
b0d623f7 | 5971 | |
0a7de745 | 5972 | return result; |
1c79356b A |
5973 | } |
5974 | ||
cb323159 | 5975 | __dead2 |
0a7de745 A |
5976 | void |
5977 | IOService::deliverNotification( const OSSymbol * type, | |
5978 | IOOptionBits orNewState, IOOptionBits andNewState ) | |
5ba3f43e | 5979 | { |
0a7de745 | 5980 | panic("deliverNotification"); |
5ba3f43e A |
5981 | } |
5982 | ||
0a7de745 A |
5983 | OSArray * |
5984 | IOService::copyNotifiers(const OSSymbol * type, | |
5985 | IOOptionBits orNewState, IOOptionBits andNewState ) | |
1c79356b | 5986 | { |
0a7de745 A |
5987 | _IOServiceNotifier * notify; |
5988 | OSIterator * iter; | |
cb323159 | 5989 | OSArray * willSend = NULL; |
1c79356b | 5990 | |
0a7de745 | 5991 | lockForArbitration(); |
1c79356b | 5992 | |
0a7de745 A |
5993 | if ((0 == (__state[0] & kIOServiceInactiveState)) |
5994 | || (type == gIOTerminatedNotification) | |
5995 | || (type == gIOWillTerminateNotification)) { | |
5996 | LOCKREADNOTIFY(); | |
5997 | ||
5998 | iter = OSCollectionIterator::withCollection((OSOrderedSet *) | |
5999 | gNotifications->getObject( type )); | |
6000 | ||
6001 | if (iter) { | |
6002 | while ((notify = (_IOServiceNotifier *) iter->getNextObject())) { | |
6003 | if (matchPassive(notify->matching, 0) | |
6004 | && (kIOServiceNotifyEnable & notify->state)) { | |
cb323159 | 6005 | if (NULL == willSend) { |
0a7de745 A |
6006 | willSend = OSArray::withCapacity(8); |
6007 | } | |
6008 | if (willSend) { | |
6009 | willSend->setObject( notify ); | |
6010 | } | |
6011 | } | |
6012 | } | |
6013 | iter->release(); | |
6014 | } | |
6015 | __state[0] = (__state[0] | orNewState) & andNewState; | |
6016 | UNLOCKNOTIFY(); | |
6017 | } | |
5ba3f43e | 6018 | |
0a7de745 | 6019 | unlockForArbitration(); |
5ba3f43e | 6020 | |
0a7de745 | 6021 | return willSend; |
1c79356b A |
6022 | } |
6023 | ||
0a7de745 A |
6024 | IOOptionBits |
6025 | IOService::getState( void ) const | |
1c79356b | 6026 | { |
0a7de745 | 6027 | return __state[0]; |
1c79356b A |
6028 | } |
6029 | ||
6030 | /* | |
6031 | * Helpers to make matching objects for simple cases | |
6032 | */ | |
6033 | ||
0a7de745 A |
6034 | OSDictionary * |
6035 | IOService::serviceMatching( const OSString * name, | |
6036 | OSDictionary * table ) | |
1c79356b | 6037 | { |
0a7de745 | 6038 | const OSString * str; |
316670eb | 6039 | |
0a7de745 A |
6040 | str = OSSymbol::withString(name); |
6041 | if (!str) { | |
cb323159 | 6042 | return NULL; |
0a7de745 | 6043 | } |
316670eb | 6044 | |
0a7de745 A |
6045 | if (!table) { |
6046 | table = OSDictionary::withCapacity( 2 ); | |
6047 | } | |
6048 | if (table) { | |
6049 | table->setObject(gIOProviderClassKey, (OSObject *)str ); | |
6050 | } | |
6051 | str->release(); | |
1c79356b | 6052 | |
0a7de745 | 6053 | return table; |
1c79356b A |
6054 | } |
6055 | ||
0a7de745 A |
6056 | OSDictionary * |
6057 | IOService::serviceMatching( const char * name, | |
6058 | OSDictionary * table ) | |
1c79356b | 6059 | { |
0a7de745 | 6060 | const OSString * str; |
1c79356b | 6061 | |
0a7de745 A |
6062 | str = OSSymbol::withCString( name ); |
6063 | if (!str) { | |
cb323159 | 6064 | return NULL; |
0a7de745 | 6065 | } |
1c79356b | 6066 | |
0a7de745 A |
6067 | table = serviceMatching( str, table ); |
6068 | str->release(); | |
6069 | return table; | |
1c79356b A |
6070 | } |
6071 | ||
0a7de745 A |
6072 | OSDictionary * |
6073 | IOService::nameMatching( const OSString * name, | |
6074 | OSDictionary * table ) | |
1c79356b | 6075 | { |
0a7de745 A |
6076 | if (!table) { |
6077 | table = OSDictionary::withCapacity( 2 ); | |
6078 | } | |
6079 | if (table) { | |
6080 | table->setObject( gIONameMatchKey, (OSObject *)name ); | |
6081 | } | |
1c79356b | 6082 | |
0a7de745 | 6083 | return table; |
1c79356b A |
6084 | } |
6085 | ||
0a7de745 A |
6086 | OSDictionary * |
6087 | IOService::nameMatching( const char * name, | |
6088 | OSDictionary * table ) | |
1c79356b | 6089 | { |
0a7de745 | 6090 | const OSString * str; |
1c79356b | 6091 | |
0a7de745 A |
6092 | str = OSSymbol::withCString( name ); |
6093 | if (!str) { | |
cb323159 | 6094 | return NULL; |
0a7de745 | 6095 | } |
1c79356b | 6096 | |
0a7de745 A |
6097 | table = nameMatching( str, table ); |
6098 | str->release(); | |
6099 | return table; | |
1c79356b A |
6100 | } |
6101 | ||
0a7de745 A |
6102 | OSDictionary * |
6103 | IOService::resourceMatching( const OSString * str, | |
6104 | OSDictionary * table ) | |
1c79356b | 6105 | { |
0a7de745 A |
6106 | table = serviceMatching( gIOResourcesKey, table ); |
6107 | if (table) { | |
6108 | table->setObject( gIOResourceMatchKey, (OSObject *) str ); | |
6109 | } | |
1c79356b | 6110 | |
0a7de745 | 6111 | return table; |
1c79356b A |
6112 | } |
6113 | ||
0a7de745 A |
6114 | OSDictionary * |
6115 | IOService::resourceMatching( const char * name, | |
6116 | OSDictionary * table ) | |
1c79356b | 6117 | { |
0a7de745 | 6118 | const OSSymbol * str; |
1c79356b | 6119 | |
0a7de745 A |
6120 | str = OSSymbol::withCString( name ); |
6121 | if (!str) { | |
cb323159 | 6122 | return NULL; |
0a7de745 | 6123 | } |
1c79356b | 6124 | |
0a7de745 A |
6125 | table = resourceMatching( str, table ); |
6126 | str->release(); | |
1c79356b | 6127 | |
0a7de745 | 6128 | return table; |
1c79356b A |
6129 | } |
6130 | ||
0a7de745 A |
6131 | OSDictionary * |
6132 | IOService::propertyMatching( const OSSymbol * key, const OSObject * value, | |
6133 | OSDictionary * table ) | |
2d21ac55 | 6134 | { |
0a7de745 | 6135 | OSDictionary * properties; |
2d21ac55 | 6136 | |
0a7de745 A |
6137 | properties = OSDictionary::withCapacity( 2 ); |
6138 | if (!properties) { | |
cb323159 | 6139 | return NULL; |
0a7de745 A |
6140 | } |
6141 | properties->setObject( key, value ); | |
2d21ac55 | 6142 | |
0a7de745 A |
6143 | if (!table) { |
6144 | table = OSDictionary::withCapacity( 2 ); | |
6145 | } | |
6146 | if (table) { | |
6147 | table->setObject( gIOPropertyMatchKey, properties ); | |
6148 | } | |
2d21ac55 | 6149 | |
0a7de745 | 6150 | properties->release(); |
2d21ac55 | 6151 | |
0a7de745 | 6152 | return table; |
2d21ac55 A |
6153 | } |
6154 | ||
0a7de745 A |
6155 | OSDictionary * |
6156 | IOService::registryEntryIDMatching( uint64_t entryID, | |
6157 | OSDictionary * table ) | |
b0d623f7 | 6158 | { |
0a7de745 | 6159 | OSNumber * num; |
b0d623f7 | 6160 | |
0a7de745 A |
6161 | num = OSNumber::withNumber( entryID, 64 ); |
6162 | if (!num) { | |
cb323159 | 6163 | return NULL; |
0a7de745 | 6164 | } |
b0d623f7 | 6165 | |
0a7de745 A |
6166 | if (!table) { |
6167 | table = OSDictionary::withCapacity( 2 ); | |
6168 | } | |
6169 | if (table) { | |
6170 | table->setObject( gIORegistryEntryIDKey, num ); | |
6171 | } | |
b0d623f7 | 6172 | |
0a7de745 A |
6173 | if (num) { |
6174 | num->release(); | |
6175 | } | |
6176 | ||
6177 | return table; | |
b0d623f7 A |
6178 | } |
6179 | ||
6180 | ||
1c79356b A |
6181 | /* |
6182 | * _IOServiceNotifier | |
6183 | */ | |
6184 | ||
6185 | // wait for all threads, other than the current one, | |
6186 | // to exit the handler | |
6187 | ||
0a7de745 A |
6188 | void |
6189 | _IOServiceNotifier::wait() | |
1c79356b | 6190 | { |
0a7de745 A |
6191 | _IOServiceNotifierInvocation * next; |
6192 | bool doWait; | |
1c79356b | 6193 | |
0a7de745 A |
6194 | do { |
6195 | doWait = false; | |
6196 | queue_iterate( &handlerInvocations, next, | |
6197 | _IOServiceNotifierInvocation *, link) { | |
6198 | if (next->thread != current_thread()) { | |
6199 | doWait = true; | |
6200 | break; | |
6201 | } | |
6202 | } | |
6203 | if (doWait) { | |
6204 | state |= kIOServiceNotifyWaiter; | |
6205 | SLEEPNOTIFY(this); | |
6206 | } | |
6207 | } while (doWait); | |
1c79356b A |
6208 | } |
6209 | ||
0a7de745 A |
6210 | void |
6211 | _IOServiceNotifier::free() | |
1c79356b | 6212 | { |
0a7de745 | 6213 | assert( queue_empty( &handlerInvocations )); |
d9a64523 | 6214 | |
0a7de745 A |
6215 | if (handler == &IOServiceMatchingNotificationHandlerToBlock) { |
6216 | Block_release(ref); | |
6217 | } | |
d9a64523 | 6218 | |
0a7de745 | 6219 | OSObject::free(); |
1c79356b A |
6220 | } |
6221 | ||
0a7de745 A |
6222 | void |
6223 | _IOServiceNotifier::remove() | |
1c79356b | 6224 | { |
0a7de745 A |
6225 | LOCKWRITENOTIFY(); |
6226 | ||
6227 | if (whence) { | |
6228 | whence->removeObject((OSObject *) this ); | |
cb323159 | 6229 | whence = NULL; |
0a7de745 A |
6230 | } |
6231 | if (matching) { | |
6232 | matching->release(); | |
cb323159 | 6233 | matching = NULL; |
0a7de745 | 6234 | } |
1c79356b | 6235 | |
0a7de745 | 6236 | state &= ~kIOServiceNotifyEnable; |
1c79356b | 6237 | |
0a7de745 | 6238 | wait(); |
1c79356b | 6239 | |
0a7de745 | 6240 | UNLOCKNOTIFY(); |
1c79356b | 6241 | |
0a7de745 | 6242 | release(); |
1c79356b A |
6243 | } |
6244 | ||
0a7de745 A |
6245 | bool |
6246 | _IOServiceNotifier::disable() | |
1c79356b | 6247 | { |
0a7de745 | 6248 | bool ret; |
1c79356b | 6249 | |
0a7de745 | 6250 | LOCKWRITENOTIFY(); |
1c79356b | 6251 | |
0a7de745 A |
6252 | ret = (0 != (kIOServiceNotifyEnable & state)); |
6253 | state &= ~kIOServiceNotifyEnable; | |
6254 | if (ret) { | |
6255 | wait(); | |
6256 | } | |
1c79356b | 6257 | |
0a7de745 | 6258 | UNLOCKNOTIFY(); |
1c79356b | 6259 | |
0a7de745 | 6260 | return ret; |
1c79356b A |
6261 | } |
6262 | ||
0a7de745 A |
6263 | void |
6264 | _IOServiceNotifier::enable( bool was ) | |
1c79356b | 6265 | { |
0a7de745 A |
6266 | LOCKWRITENOTIFY(); |
6267 | if (was) { | |
6268 | state |= kIOServiceNotifyEnable; | |
6269 | } else { | |
6270 | state &= ~kIOServiceNotifyEnable; | |
6271 | } | |
6272 | UNLOCKNOTIFY(); | |
1c79356b A |
6273 | } |
6274 | ||
39037602 A |
6275 | |
6276 | /* | |
6277 | * _IOServiceNullNotifier | |
6278 | */ | |
6279 | ||
0a7de745 A |
6280 | void |
6281 | _IOServiceNullNotifier::taggedRetain(const void *tag) const | |
6282 | { | |
6283 | } | |
6284 | void | |
6285 | _IOServiceNullNotifier::taggedRelease(const void *tag, const int when) const | |
6286 | { | |
6287 | } | |
6288 | void | |
6289 | _IOServiceNullNotifier::free() | |
6290 | { | |
6291 | } | |
6292 | void | |
6293 | _IOServiceNullNotifier::wait() | |
6294 | { | |
6295 | } | |
6296 | void | |
6297 | _IOServiceNullNotifier::remove() | |
6298 | { | |
6299 | } | |
6300 | void | |
6301 | _IOServiceNullNotifier::enable(bool was) | |
6302 | { | |
6303 | } | |
6304 | bool | |
6305 | _IOServiceNullNotifier::disable() | |
6306 | { | |
6307 | return false; | |
6308 | } | |
39037602 | 6309 | |
1c79356b A |
6310 | /* |
6311 | * IOResources | |
6312 | */ | |
6313 | ||
0a7de745 A |
6314 | IOService * |
6315 | IOResources::resources( void ) | |
1c79356b | 6316 | { |
0a7de745 | 6317 | IOResources * inst; |
1c79356b | 6318 | |
0a7de745 A |
6319 | inst = new IOResources; |
6320 | if (inst && !inst->init()) { | |
6321 | inst->release(); | |
cb323159 | 6322 | inst = NULL; |
0a7de745 | 6323 | } |
1c79356b | 6324 | |
0a7de745 | 6325 | return inst; |
1c79356b A |
6326 | } |
6327 | ||
0a7de745 A |
6328 | bool |
6329 | IOResources::init( OSDictionary * dictionary ) | |
6d2010ae | 6330 | { |
0a7de745 A |
6331 | // Do super init first |
6332 | if (!IOService::init()) { | |
6333 | return false; | |
6334 | } | |
6d2010ae | 6335 | |
0a7de745 A |
6336 | // Allow PAL layer to publish a value |
6337 | const char *property_name; | |
6338 | int property_value; | |
6d2010ae | 6339 | |
0a7de745 | 6340 | pal_get_resource_property( &property_name, &property_value ); |
6d2010ae | 6341 | |
0a7de745 A |
6342 | if (property_name) { |
6343 | OSNumber *num; | |
6344 | const OSSymbol * sym; | |
6d2010ae | 6345 | |
cb323159 A |
6346 | if ((num = OSNumber::withNumber(property_value, 32)) != NULL) { |
6347 | if ((sym = OSSymbol::withCString( property_name)) != NULL) { | |
0a7de745 A |
6348 | this->setProperty( sym, num ); |
6349 | sym->release(); | |
6350 | } | |
6351 | num->release(); | |
6352 | } | |
6d2010ae | 6353 | } |
6d2010ae | 6354 | |
0a7de745 | 6355 | return true; |
6d2010ae A |
6356 | } |
6357 | ||
0a7de745 A |
6358 | IOReturn |
6359 | IOResources::newUserClient(task_t owningTask, void * securityID, | |
6360 | UInt32 type, OSDictionary * properties, | |
6361 | IOUserClient ** handler) | |
3e170ce0 | 6362 | { |
0a7de745 | 6363 | return kIOReturnUnsupported; |
3e170ce0 A |
6364 | } |
6365 | ||
0a7de745 A |
6366 | IOWorkLoop * |
6367 | IOResources::getWorkLoop() const | |
1c79356b | 6368 | { |
0a7de745 A |
6369 | // If we are the resource root |
6370 | // then use the platform's workloop | |
6371 | if (this == (IOResources *) gIOResources) { | |
6372 | return getPlatform()->getWorkLoop(); | |
6373 | } else { | |
6374 | return IOService::getWorkLoop(); | |
6375 | } | |
1c79356b A |
6376 | } |
6377 | ||
cb323159 A |
6378 | static bool |
6379 | IOResourcesMatchPropertyTable(IOService * resources, OSDictionary * table) | |
1c79356b | 6380 | { |
0a7de745 A |
6381 | OSObject * prop; |
6382 | OSString * str; | |
6383 | OSSet * set; | |
6384 | OSIterator * iter; | |
6385 | OSObject * obj; | |
6386 | OSArray * keys; | |
6387 | bool ok = true; | |
1c79356b | 6388 | |
0a7de745 A |
6389 | prop = table->getObject( gIOResourceMatchKey ); |
6390 | str = OSDynamicCast( OSString, prop ); | |
6391 | if (str) { | |
cb323159 | 6392 | ok = (NULL != resources->getProperty( str )); |
0a7de745 A |
6393 | } else if ((set = OSDynamicCast( OSSet, prop))) { |
6394 | iter = OSCollectionIterator::withCollection( set ); | |
cb323159 | 6395 | ok = (iter != NULL); |
0a7de745 | 6396 | while (ok && (str = OSDynamicCast( OSString, iter->getNextObject()))) { |
cb323159 | 6397 | ok = (NULL != resources->getProperty( str )); |
0a7de745 | 6398 | } |
1c79356b | 6399 | |
0a7de745 A |
6400 | if (iter) { |
6401 | iter->release(); | |
6402 | } | |
6403 | } else if ((prop = table->getObject(gIOResourceMatchedKey))) { | |
cb323159 | 6404 | obj = resources->copyProperty(gIOResourceMatchedKey); |
0a7de745 A |
6405 | keys = OSDynamicCast(OSArray, obj); |
6406 | ok = false; | |
6407 | if (keys) { | |
6408 | // assuming OSSymbol | |
6409 | ok = ((-1U) != keys->getNextIndexOfObject(prop, 0)); | |
6410 | } | |
6411 | OSSafeReleaseNULL(obj); | |
6412 | } | |
1c79356b | 6413 | |
0a7de745 | 6414 | return ok; |
1c79356b A |
6415 | } |
6416 | ||
cb323159 A |
6417 | bool |
6418 | IOResources::matchPropertyTable( OSDictionary * table ) | |
6419 | { | |
6420 | return IOResourcesMatchPropertyTable(this, table); | |
6421 | } | |
6422 | ||
6423 | /* | |
6424 | * IOUserResources | |
6425 | */ | |
6426 | ||
6427 | IOService * | |
6428 | IOUserResources::resources( void ) | |
6429 | { | |
6430 | IOUserResources * inst; | |
6431 | ||
6432 | inst = OSTypeAlloc(IOUserResources); | |
6433 | if (inst && !inst->init()) { | |
6434 | inst->release(); | |
6435 | inst = NULL; | |
6436 | } | |
6437 | ||
6438 | return inst; | |
6439 | } | |
6440 | ||
6441 | bool | |
6442 | IOUserResources::init( OSDictionary * dictionary ) | |
6443 | { | |
6444 | // Do super init first | |
6445 | if (!IOService::init()) { | |
6446 | return false; | |
6447 | } | |
6448 | return true; | |
6449 | } | |
6450 | ||
6451 | IOReturn | |
6452 | IOUserResources::newUserClient(task_t owningTask, void * securityID, | |
6453 | UInt32 type, OSDictionary * properties, | |
6454 | IOUserClient ** handler) | |
6455 | { | |
6456 | return kIOReturnUnsupported; | |
6457 | } | |
6458 | ||
6459 | IOWorkLoop * | |
6460 | IOUserResources::getWorkLoop() const | |
6461 | { | |
6462 | return getPlatform()->getWorkLoop(); | |
6463 | } | |
6464 | ||
6465 | bool | |
6466 | IOUserResources::matchPropertyTable( OSDictionary * table ) | |
6467 | { | |
6468 | return IOResourcesMatchPropertyTable(this, table); | |
6469 | } | |
6470 | ||
6471 | // -- | |
6472 | ||
0a7de745 A |
6473 | void |
6474 | IOService::consoleLockTimer(thread_call_param_t p0, thread_call_param_t p1) | |
6d2010ae | 6475 | { |
0a7de745 | 6476 | IOService::updateConsoleUsers(NULL, 0); |
6d2010ae A |
6477 | } |
6478 | ||
0a7de745 | 6479 | void |
f427ee49 | 6480 | IOService::updateConsoleUsers(OSArray * consoleUsers, IOMessage systemMessage, bool afterUserspaceReboot) |
6d2010ae | 6481 | { |
0a7de745 A |
6482 | IORegistryEntry * regEntry; |
6483 | OSObject * locked = kOSBooleanFalse; | |
6484 | uint32_t idx; | |
6485 | bool publish; | |
6486 | OSDictionary * user; | |
0a7de745 A |
6487 | clock_sec_t now = 0; |
6488 | clock_usec_t microsecs; | |
6d2010ae | 6489 | |
0a7de745 | 6490 | regEntry = IORegistryEntry::getRegistryRoot(); |
6d2010ae | 6491 | |
0a7de745 A |
6492 | if (!gIOChosenEntry) { |
6493 | gIOChosenEntry = IORegistryEntry::fromPath("/chosen", gIODTPlane); | |
6494 | } | |
316670eb | 6495 | |
0a7de745 | 6496 | IOLockLock(gIOConsoleUsersLock); |
6d2010ae | 6497 | |
0a7de745 A |
6498 | if (systemMessage) { |
6499 | sSystemPower = systemMessage; | |
316670eb | 6500 | #if HIBERNATION |
0a7de745 A |
6501 | if (kIOMessageSystemHasPoweredOn == systemMessage) { |
6502 | uint32_t lockState = IOHibernateWasScreenLocked(); | |
6503 | switch (lockState) { | |
6504 | case 0: | |
6505 | break; | |
6506 | case kIOScreenLockLocked: | |
6507 | case kIOScreenLockFileVaultDialog: | |
6508 | gIOConsoleBooterLockState = kOSBooleanTrue; | |
6509 | break; | |
6510 | case kIOScreenLockNoLock: | |
cb323159 | 6511 | gIOConsoleBooterLockState = NULL; |
0a7de745 A |
6512 | break; |
6513 | case kIOScreenLockUnlocked: | |
6514 | default: | |
6515 | gIOConsoleBooterLockState = kOSBooleanFalse; | |
6516 | break; | |
6517 | } | |
6518 | } | |
316670eb | 6519 | #endif /* HIBERNATION */ |
0a7de745 | 6520 | } |
316670eb | 6521 | |
0a7de745 | 6522 | if (consoleUsers) { |
cb323159 | 6523 | OSNumber * num = NULL; |
0a7de745 | 6524 | bool loginLocked = true; |
d190cdc3 | 6525 | |
0a7de745 A |
6526 | gIOConsoleLoggedIn = false; |
6527 | for (idx = 0; | |
6528 | (user = OSDynamicCast(OSDictionary, consoleUsers->getObject(idx))); | |
6529 | idx++) { | |
6530 | gIOConsoleLoggedIn |= ((kOSBooleanTrue == user->getObject(gIOConsoleSessionOnConsoleKey)) | |
6531 | && (kOSBooleanTrue == user->getObject(gIOConsoleSessionLoginDoneKey))); | |
d190cdc3 | 6532 | |
0a7de745 A |
6533 | loginLocked &= (kOSBooleanTrue == user->getObject(gIOConsoleSessionScreenIsLockedKey)); |
6534 | if (!num) { | |
6535 | num = OSDynamicCast(OSNumber, user->getObject(gIOConsoleSessionScreenLockedTimeKey)); | |
6536 | } | |
6537 | } | |
d190cdc3 | 6538 | #if HIBERNATION |
f427ee49 | 6539 | if (!loginLocked || afterUserspaceReboot) { |
cb323159 | 6540 | gIOConsoleBooterLockState = NULL; |
0a7de745 A |
6541 | } |
6542 | IOLog("IOConsoleUsers: time(%d) %ld->%d, lin %d, llk %d, \n", | |
cb323159 | 6543 | (num != NULL), gIOConsoleLockTime, (num ? num->unsigned32BitValue() : 0), |
0a7de745 | 6544 | gIOConsoleLoggedIn, loginLocked); |
d190cdc3 | 6545 | #endif /* HIBERNATION */ |
0a7de745 A |
6546 | gIOConsoleLockTime = num ? num->unsigned32BitValue() : 0; |
6547 | } | |
6548 | ||
6549 | if (!gIOConsoleLoggedIn | |
6550 | || (kIOMessageSystemWillSleep == sSystemPower) | |
6551 | || (kIOMessageSystemPagingOff == sSystemPower)) { | |
f427ee49 A |
6552 | if (afterUserspaceReboot) { |
6553 | // set "locked" to false after a user space reboot | |
6554 | // because the reboot happens directly after a user | |
6555 | // logs into the machine via fvunlock mode. | |
6556 | locked = kOSBooleanFalse; | |
6557 | } else { | |
6558 | locked = kOSBooleanTrue; | |
6559 | } | |
0a7de745 | 6560 | } |
d190cdc3 | 6561 | #if HIBERNATION |
0a7de745 A |
6562 | else if (gIOConsoleBooterLockState) { |
6563 | locked = gIOConsoleBooterLockState; | |
ebb1b9f4 | 6564 | } |
0a7de745 A |
6565 | #endif /* HIBERNATION */ |
6566 | else if (gIOConsoleLockTime) { | |
6567 | clock_get_calendar_microtime(&now, µsecs); | |
6568 | if (gIOConsoleLockTime > now) { | |
6569 | AbsoluteTime deadline; | |
f427ee49 A |
6570 | clock_sec_t interval; |
6571 | uint32_t interval32; | |
6572 | ||
6573 | interval = (gIOConsoleLockTime - now); | |
6574 | interval32 = (uint32_t) interval; | |
6575 | if (interval32 != interval) { | |
6576 | interval32 = UINT_MAX; | |
6577 | } | |
6578 | clock_interval_to_deadline(interval32, kSecondScale, &deadline); | |
0a7de745 A |
6579 | thread_call_enter_delayed(gIOConsoleLockCallout, deadline); |
6580 | } else { | |
6581 | locked = kOSBooleanTrue; | |
6582 | } | |
6d2010ae | 6583 | } |
6d2010ae | 6584 | |
0a7de745 A |
6585 | publish = (consoleUsers || (locked != regEntry->getProperty(gIOConsoleLockedKey))); |
6586 | if (publish) { | |
6587 | regEntry->setProperty(gIOConsoleLockedKey, locked); | |
6588 | if (consoleUsers) { | |
6589 | regEntry->setProperty(gIOConsoleUsersKey, consoleUsers); | |
6590 | } | |
6591 | OSIncrementAtomic( &gIOConsoleUsersSeed ); | |
6d2010ae | 6592 | } |
6d2010ae | 6593 | |
316670eb | 6594 | #if HIBERNATION |
0a7de745 A |
6595 | if (gIOChosenEntry) { |
6596 | if (locked == kOSBooleanTrue) { | |
6597 | gIOScreenLockState = kIOScreenLockLocked; | |
6598 | } else if (gIOConsoleLockTime) { | |
6599 | gIOScreenLockState = kIOScreenLockUnlocked; | |
6600 | } else { | |
6601 | gIOScreenLockState = kIOScreenLockNoLock; | |
6602 | } | |
6603 | gIOChosenEntry->setProperty(kIOScreenLockStateKey, &gIOScreenLockState, sizeof(gIOScreenLockState)); | |
6604 | ||
6605 | IOLog("IOConsoleUsers: gIOScreenLockState %d, hs %d, bs %d, now %ld, sm 0x%x\n", | |
cb323159 | 6606 | gIOScreenLockState, gIOHibernateState, (gIOConsoleBooterLockState != NULL), now, systemMessage); |
0a7de745 | 6607 | } |
316670eb A |
6608 | #endif /* HIBERNATION */ |
6609 | ||
0a7de745 A |
6610 | IOLockUnlock(gIOConsoleUsersLock); |
6611 | ||
6612 | if (publish) { | |
6613 | publishResource( gIOConsoleUsersSeedKey, gIOConsoleUsersSeedValue ); | |
6d2010ae | 6614 | |
0a7de745 | 6615 | MessageClientsContext context; |
6d2010ae | 6616 | |
0a7de745 A |
6617 | context.service = getServiceRoot(); |
6618 | context.type = kIOMessageConsoleSecurityChange; | |
6619 | context.argument = (void *) regEntry; | |
6620 | context.argSize = 0; | |
6621 | ||
6622 | applyToInterestNotifiers(getServiceRoot(), gIOConsoleSecurityInterest, | |
6623 | &messageClientsApplier, &context ); | |
6624 | } | |
6d2010ae A |
6625 | } |
6626 | ||
0a7de745 A |
6627 | IOReturn |
6628 | IOResources::setProperties( OSObject * properties ) | |
9bccf70c | 6629 | { |
0a7de745 A |
6630 | IOReturn err; |
6631 | const OSSymbol * key; | |
6632 | OSDictionary * dict; | |
6633 | OSCollectionIterator * iter; | |
9bccf70c | 6634 | |
f427ee49 A |
6635 | if (!IOTaskHasEntitlement(current_task(), kIOResourcesSetPropertyKey)) { |
6636 | err = IOUserClient::clientHasPrivilege(current_task(), kIOClientPrivilegeAdministrator); | |
6637 | if (kIOReturnSuccess != err) { | |
6638 | return err; | |
6639 | } | |
0a7de745 | 6640 | } |
9bccf70c | 6641 | |
0a7de745 | 6642 | dict = OSDynamicCast(OSDictionary, properties); |
cb323159 | 6643 | if (NULL == dict) { |
0a7de745 A |
6644 | return kIOReturnBadArgument; |
6645 | } | |
9bccf70c | 6646 | |
0a7de745 | 6647 | iter = OSCollectionIterator::withCollection( dict); |
cb323159 | 6648 | if (NULL == iter) { |
0a7de745 | 6649 | return kIOReturnBadArgument; |
55e303ae A |
6650 | } |
6651 | ||
0a7de745 A |
6652 | while ((key = OSDynamicCast(OSSymbol, iter->getNextObject()))) { |
6653 | if (gIOConsoleUsersKey == key) { | |
6654 | do{ | |
6655 | OSArray * consoleUsers; | |
6656 | consoleUsers = OSDynamicCast(OSArray, dict->getObject(key)); | |
6657 | if (!consoleUsers) { | |
6658 | continue; | |
6659 | } | |
6660 | IOService::updateConsoleUsers(consoleUsers, 0); | |
6661 | }while (false); | |
6662 | } | |
6663 | ||
6664 | publishResource( key, dict->getObject(key)); | |
6665 | } | |
9bccf70c | 6666 | |
0a7de745 | 6667 | iter->release(); |
9bccf70c | 6668 | |
0a7de745 | 6669 | return kIOReturnSuccess; |
9bccf70c A |
6670 | } |
6671 | ||
1c79356b A |
6672 | /* |
6673 | * Helpers for matching dictionaries. | |
6674 | * Keys existing in matching are checked in properties. | |
6675 | * Keys may be a string or OSCollection of IOStrings | |
6676 | */ | |
6677 | ||
0a7de745 A |
6678 | bool |
6679 | IOService::compareProperty( OSDictionary * matching, | |
6680 | const char * key ) | |
1c79356b | 6681 | { |
0a7de745 A |
6682 | OSObject * value; |
6683 | OSObject * prop; | |
6684 | bool ok; | |
1c79356b | 6685 | |
0a7de745 A |
6686 | value = matching->getObject( key ); |
6687 | if (value) { | |
6688 | prop = copyProperty(key); | |
6689 | ok = value->isEqualTo(prop); | |
6690 | if (prop) { | |
6691 | prop->release(); | |
6692 | } | |
6693 | } else { | |
6694 | ok = true; | |
6695 | } | |
1c79356b | 6696 | |
0a7de745 | 6697 | return ok; |
1c79356b A |
6698 | } |
6699 | ||
6700 | ||
0a7de745 A |
6701 | bool |
6702 | IOService::compareProperty( OSDictionary * matching, | |
6703 | const OSString * key ) | |
1c79356b | 6704 | { |
0a7de745 A |
6705 | OSObject * value; |
6706 | OSObject * prop; | |
6707 | bool ok; | |
1c79356b | 6708 | |
0a7de745 A |
6709 | value = matching->getObject( key ); |
6710 | if (value) { | |
6711 | prop = copyProperty(key); | |
6712 | ok = value->isEqualTo(prop); | |
6713 | if (prop) { | |
6714 | prop->release(); | |
6715 | } | |
6716 | } else { | |
6717 | ok = true; | |
6718 | } | |
1c79356b | 6719 | |
0a7de745 | 6720 | return ok; |
1c79356b A |
6721 | } |
6722 | ||
f427ee49 A |
6723 | #ifndef __clang_analyzer__ |
6724 | // Implementation of this function is hidden from the static analyzer. | |
6725 | // The analyzer was worried about this function's confusing contract over | |
6726 | // the 'keys' parameter. The contract is to either release it or not release it | |
6727 | // depending on whether 'matching' is non-null. Such contracts are discouraged | |
6728 | // but changing it now would break compatibility. | |
0a7de745 A |
6729 | bool |
6730 | IOService::compareProperties( OSDictionary * matching, | |
6731 | OSCollection * keys ) | |
1c79356b | 6732 | { |
0a7de745 A |
6733 | OSCollectionIterator * iter; |
6734 | const OSString * key; | |
6735 | bool ok = true; | |
1c79356b | 6736 | |
0a7de745 A |
6737 | if (!matching || !keys) { |
6738 | return false; | |
6739 | } | |
1c79356b | 6740 | |
0a7de745 | 6741 | iter = OSCollectionIterator::withCollection( keys ); |
1c79356b | 6742 | |
0a7de745 A |
6743 | if (iter) { |
6744 | while (ok && (key = OSDynamicCast( OSString, iter->getNextObject()))) { | |
6745 | ok = compareProperty( matching, key ); | |
6746 | } | |
1c79356b | 6747 | |
0a7de745 A |
6748 | iter->release(); |
6749 | } | |
6750 | keys->release(); // !! consume a ref !! | |
1c79356b | 6751 | |
0a7de745 | 6752 | return ok; |
1c79356b | 6753 | } |
f427ee49 | 6754 | #endif // __clang_analyzer__ |
1c79356b A |
6755 | |
6756 | /* Helper to add a location matching dict to the table */ | |
6757 | ||
0a7de745 A |
6758 | OSDictionary * |
6759 | IOService::addLocation( OSDictionary * table ) | |
1c79356b | 6760 | { |
0a7de745 | 6761 | OSDictionary * dict; |
1c79356b | 6762 | |
0a7de745 | 6763 | if (!table) { |
cb323159 | 6764 | return NULL; |
0a7de745 | 6765 | } |
1c79356b | 6766 | |
0a7de745 A |
6767 | dict = OSDictionary::withCapacity( 1 ); |
6768 | if (dict) { | |
cb323159 | 6769 | bool ok = table->setObject( gIOLocationMatchKey, dict ); |
0a7de745 | 6770 | dict->release(); |
cb323159 A |
6771 | if (!ok) { |
6772 | dict = NULL; | |
6773 | } | |
0a7de745 | 6774 | } |
1c79356b | 6775 | |
0a7de745 | 6776 | return dict; |
1c79356b A |
6777 | } |
6778 | ||
6779 | /* | |
6780 | * Go looking for a provider to match a location dict. | |
6781 | */ | |
6782 | ||
0a7de745 A |
6783 | IOService * |
6784 | IOService::matchLocation( IOService * /* client */ ) | |
1c79356b | 6785 | { |
0a7de745 | 6786 | IOService * parent; |
1c79356b | 6787 | |
0a7de745 | 6788 | parent = getProvider(); |
1c79356b | 6789 | |
0a7de745 A |
6790 | if (parent) { |
6791 | parent = parent->matchLocation( this ); | |
6792 | } | |
1c79356b | 6793 | |
0a7de745 | 6794 | return parent; |
1c79356b A |
6795 | } |
6796 | ||
0a7de745 A |
6797 | bool |
6798 | IOService::matchInternal(OSDictionary * table, uint32_t options, uint32_t * did) | |
1c79356b | 6799 | { |
0a7de745 A |
6800 | OSString * matched; |
6801 | OSObject * obj; | |
6802 | OSString * str; | |
f427ee49 | 6803 | OSDictionary * matchProps; |
0a7de745 A |
6804 | IORegistryEntry * entry; |
6805 | OSNumber * num; | |
6806 | bool match = true; | |
6807 | bool changesOK = (0 != (kIOServiceChangesOK & options)); | |
6808 | uint32_t count; | |
6809 | uint32_t done; | |
1c79356b | 6810 | |
0a7de745 A |
6811 | do{ |
6812 | count = table->getCount(); | |
6813 | done = 0; | |
f427ee49 A |
6814 | matchProps = NULL; |
6815 | ||
6816 | if (table->getObject(gIOCompatibilityMatchKey)) { | |
6817 | done++; | |
6818 | obj = copyProperty(gIOCompatibilityPropertiesKey); | |
6819 | matchProps = OSDynamicCast(OSDictionary, obj); | |
6820 | if (!matchProps) { | |
6821 | OSSafeReleaseNULL(obj); | |
6822 | } | |
6823 | } | |
4bd07ac2 | 6824 | |
0a7de745 A |
6825 | str = OSDynamicCast(OSString, table->getObject(gIOProviderClassKey)); |
6826 | if (str) { | |
6827 | done++; | |
f427ee49 A |
6828 | if (matchProps && (obj = matchProps->getObject(gIOClassKey))) { |
6829 | match = str->isEqualTo(obj); | |
6830 | } else { | |
6831 | match = ((kIOServiceClassDone & options) || (NULL != metaCast(str))); | |
6832 | } | |
6833 | ||
316670eb | 6834 | #if MATCH_DEBUG |
0a7de745 A |
6835 | match = (0 != metaCast( str )); |
6836 | if ((kIOServiceClassDone & options) && !match) { | |
6837 | panic("classDone"); | |
6838 | } | |
316670eb | 6839 | #endif |
0a7de745 A |
6840 | if ((!match) || (done == count)) { |
6841 | break; | |
6842 | } | |
6843 | } | |
1c79356b | 6844 | |
0a7de745 A |
6845 | obj = table->getObject( gIONameMatchKey ); |
6846 | if (obj) { | |
6847 | done++; | |
cb323159 | 6848 | match = compareNames( obj, changesOK ? &matched : NULL ); |
0a7de745 A |
6849 | if (!match) { |
6850 | break; | |
6851 | } | |
6852 | if (changesOK && matched) { | |
6853 | // leave a hint as to which name matched | |
6854 | table->setObject( gIONameMatchedKey, matched ); | |
6855 | matched->release(); | |
6856 | } | |
6857 | if (done == count) { | |
6858 | break; | |
6859 | } | |
6860 | } | |
1c79356b | 6861 | |
0a7de745 A |
6862 | str = OSDynamicCast( OSString, table->getObject( gIOLocationMatchKey )); |
6863 | if (str) { | |
6864 | const OSSymbol * sym; | |
6865 | done++; | |
6866 | match = false; | |
6867 | sym = copyLocation(); | |
6868 | if (sym) { | |
6869 | match = sym->isEqualTo( str ); | |
6870 | sym->release(); | |
6871 | } | |
6872 | if ((!match) || (done == count)) { | |
6873 | break; | |
6874 | } | |
6875 | } | |
9bccf70c | 6876 | |
0a7de745 A |
6877 | obj = table->getObject( gIOPropertyMatchKey ); |
6878 | if (obj) { | |
0a7de745 A |
6879 | OSDictionary * nextDict; |
6880 | OSIterator * iter; | |
6881 | done++; | |
6882 | match = false; | |
f427ee49 A |
6883 | if (!matchProps) { |
6884 | matchProps = dictionaryWithProperties(); | |
6885 | } | |
6886 | if (matchProps) { | |
0a7de745 A |
6887 | nextDict = OSDynamicCast( OSDictionary, obj); |
6888 | if (nextDict) { | |
cb323159 | 6889 | iter = NULL; |
0a7de745 A |
6890 | } else { |
6891 | iter = OSCollectionIterator::withCollection( | |
6892 | OSDynamicCast(OSCollection, obj)); | |
6893 | } | |
6894 | ||
6895 | while (nextDict | |
cb323159 | 6896 | || (iter && (NULL != (nextDict = OSDynamicCast(OSDictionary, |
0a7de745 | 6897 | iter->getNextObject()))))) { |
f427ee49 | 6898 | match = matchProps->isEqualTo( nextDict, nextDict); |
0a7de745 A |
6899 | if (match) { |
6900 | break; | |
6901 | } | |
cb323159 | 6902 | nextDict = NULL; |
0a7de745 | 6903 | } |
0a7de745 A |
6904 | if (iter) { |
6905 | iter->release(); | |
6906 | } | |
6907 | } | |
6908 | if ((!match) || (done == count)) { | |
6909 | break; | |
6910 | } | |
316670eb | 6911 | } |
9bccf70c | 6912 | |
0a7de745 A |
6913 | obj = table->getObject( gIOPropertyExistsMatchKey ); |
6914 | if (obj) { | |
0a7de745 A |
6915 | OSString * nextKey; |
6916 | OSIterator * iter; | |
6917 | done++; | |
6918 | match = false; | |
f427ee49 A |
6919 | if (!matchProps) { |
6920 | matchProps = dictionaryWithProperties(); | |
6921 | } | |
6922 | if (matchProps) { | |
0a7de745 A |
6923 | nextKey = OSDynamicCast( OSString, obj); |
6924 | if (nextKey) { | |
cb323159 | 6925 | iter = NULL; |
0a7de745 A |
6926 | } else { |
6927 | iter = OSCollectionIterator::withCollection( | |
6928 | OSDynamicCast(OSCollection, obj)); | |
6929 | } | |
6930 | ||
6931 | while (nextKey | |
cb323159 | 6932 | || (iter && (NULL != (nextKey = OSDynamicCast(OSString, |
0a7de745 | 6933 | iter->getNextObject()))))) { |
f427ee49 | 6934 | match = (NULL != matchProps->getObject(nextKey)); |
0a7de745 A |
6935 | if (match) { |
6936 | break; | |
6937 | } | |
cb323159 | 6938 | nextKey = NULL; |
0a7de745 | 6939 | } |
0a7de745 A |
6940 | if (iter) { |
6941 | iter->release(); | |
6942 | } | |
6943 | } | |
6944 | if ((!match) || (done == count)) { | |
6945 | break; | |
6946 | } | |
39037602 | 6947 | } |
39037602 | 6948 | |
0a7de745 A |
6949 | str = OSDynamicCast( OSString, table->getObject( gIOPathMatchKey )); |
6950 | if (str) { | |
6951 | done++; | |
6952 | entry = IORegistryEntry::fromPath( str->getCStringNoCopy()); | |
6953 | match = (this == entry); | |
6954 | if (entry) { | |
6955 | entry->release(); | |
6956 | } | |
f427ee49 A |
6957 | if (!match && matchProps && (obj = matchProps->getObject(gIOPathKey))) { |
6958 | match = str->isEqualTo(obj); | |
6959 | } | |
0a7de745 A |
6960 | if ((!match) || (done == count)) { |
6961 | break; | |
6962 | } | |
6963 | } | |
9bccf70c | 6964 | |
0a7de745 A |
6965 | num = OSDynamicCast( OSNumber, table->getObject( gIORegistryEntryIDKey )); |
6966 | if (num) { | |
6967 | done++; | |
6968 | match = (getRegistryEntryID() == num->unsigned64BitValue()); | |
6969 | if ((!match) || (done == count)) { | |
6970 | break; | |
6971 | } | |
6972 | } | |
1c79356b | 6973 | |
0a7de745 A |
6974 | num = OSDynamicCast( OSNumber, table->getObject( gIOMatchedServiceCountKey )); |
6975 | if (num) { | |
6976 | OSIterator * iter; | |
cb323159 | 6977 | IOService * service = NULL; |
0a7de745 A |
6978 | UInt32 serviceCount = 0; |
6979 | ||
6980 | done++; | |
6981 | iter = getClientIterator(); | |
6982 | if (iter) { | |
6983 | while ((service = (IOService *) iter->getNextObject())) { | |
6984 | if (kIOServiceInactiveState & service->__state[0]) { | |
6985 | continue; | |
6986 | } | |
cb323159 | 6987 | if (NULL == service->getProperty( gIOMatchCategoryKey )) { |
0a7de745 A |
6988 | continue; |
6989 | } | |
6990 | ++serviceCount; | |
6991 | } | |
6992 | iter->release(); | |
6993 | } | |
6994 | match = (serviceCount == num->unsigned32BitValue()); | |
6995 | if ((!match) || (done == count)) { | |
6996 | break; | |
6997 | } | |
316670eb | 6998 | } |
0a7de745 A |
6999 | |
7000 | #define propMatch(key) \ | |
7001 | obj = table->getObject(key); \ | |
7002 | if (obj) \ | |
7003 | { \ | |
7004 | OSObject * prop; \ | |
7005 | done++; \ | |
7006 | prop = copyProperty(key); \ | |
7007 | match = obj->isEqualTo(prop); \ | |
7008 | if (prop) prop->release(); \ | |
7009 | if ((!match) || (done == count)) break; \ | |
7010 | } | |
7011 | propMatch(gIOBSDNameKey) | |
7012 | propMatch(gIOBSDMajorKey) | |
7013 | propMatch(gIOBSDMinorKey) | |
7014 | propMatch(gIOBSDUnitKey) | |
316670eb | 7015 | #undef propMatch |
0a7de745 | 7016 | }while (false); |
1c79356b | 7017 | |
f427ee49 A |
7018 | OSSafeReleaseNULL(matchProps); |
7019 | ||
0a7de745 A |
7020 | if (did) { |
7021 | *did = done; | |
7022 | } | |
7023 | return match; | |
316670eb | 7024 | } |
9bccf70c | 7025 | |
0a7de745 A |
7026 | bool |
7027 | IOService::passiveMatch( OSDictionary * table, bool changesOK ) | |
316670eb | 7028 | { |
0a7de745 | 7029 | return matchPassive(table, changesOK ? kIOServiceChangesOK : 0); |
316670eb | 7030 | } |
b0d623f7 | 7031 | |
0a7de745 A |
7032 | bool |
7033 | IOService::matchPassive(OSDictionary * table, uint32_t options) | |
316670eb | 7034 | { |
0a7de745 A |
7035 | IOService * where; |
7036 | OSDictionary * nextTable; | |
7037 | SInt32 score; | |
7038 | OSNumber * newPri; | |
7039 | bool match = true; | |
7040 | bool matchParent = false; | |
7041 | uint32_t count; | |
7042 | uint32_t done; | |
9bccf70c | 7043 | |
0a7de745 | 7044 | assert( table ); |
9bccf70c | 7045 | |
f427ee49 | 7046 | #if defined(XNU_TARGET_OS_OSX) |
0a7de745 A |
7047 | OSArray* aliasServiceRegIds = NULL; |
7048 | IOService* foundAlternateService = NULL; | |
f427ee49 | 7049 | #endif /* defined(XNU_TARGET_OS_OSX) */ |
490019cf | 7050 | |
39037602 | 7051 | #if MATCH_DEBUG |
0a7de745 | 7052 | OSDictionary * root = table; |
316670eb | 7053 | #endif |
1c79356b | 7054 | |
0a7de745 A |
7055 | where = this; |
7056 | do{ | |
7057 | do{ | |
7058 | count = table->getCount(); | |
7059 | if (!(kIOServiceInternalDone & options)) { | |
7060 | match = where->matchInternal(table, options, &done); | |
7061 | // don't call family if we've done all the entries in the table | |
7062 | if ((!match) || (done == count)) { | |
7063 | break; | |
7064 | } | |
7065 | } | |
7066 | ||
7067 | // pass in score from property table | |
7068 | score = IOServiceObjectOrder( table, (void *) gIOProbeScoreKey); | |
7069 | ||
7070 | // do family specific matching | |
7071 | match = where->matchPropertyTable( table, &score ); | |
7072 | ||
7073 | if (!match) { | |
1c79356b | 7074 | #if IOMATCHDEBUG |
0a7de745 A |
7075 | if (kIOLogMatch & getDebugFlags( table )) { |
7076 | LOG("%s: family specific matching fails\n", where->getName()); | |
7077 | } | |
1c79356b | 7078 | #endif |
0a7de745 A |
7079 | break; |
7080 | } | |
7081 | ||
7082 | if (kIOServiceChangesOK & options) { | |
7083 | // save the score | |
7084 | newPri = OSNumber::withNumber( score, 32 ); | |
7085 | if (newPri) { | |
7086 | table->setObject( gIOProbeScoreKey, newPri ); | |
7087 | newPri->release(); | |
7088 | } | |
7089 | } | |
7090 | ||
7091 | options = 0; | |
7092 | matchParent = false; | |
7093 | ||
7094 | nextTable = OSDynamicCast(OSDictionary, | |
7095 | table->getObject( gIOParentMatchKey )); | |
7096 | if (nextTable) { | |
7097 | // look for a matching entry anywhere up to root | |
7098 | match = false; | |
7099 | matchParent = true; | |
7100 | table = nextTable; | |
7101 | break; | |
7102 | } | |
7103 | ||
7104 | table = OSDynamicCast(OSDictionary, | |
7105 | table->getObject( gIOLocationMatchKey )); | |
7106 | if (table) { | |
7107 | // look for a matching entry at matchLocation() | |
7108 | match = false; | |
7109 | where = where->getProvider(); | |
7110 | if (where && (where = where->matchLocation(where))) { | |
7111 | continue; | |
7112 | } | |
7113 | } | |
7114 | break; | |
7115 | }while (true); | |
7116 | ||
7117 | if (match == true) { | |
7118 | break; | |
7119 | } | |
7120 | ||
7121 | if (matchParent == true) { | |
f427ee49 | 7122 | #if defined(XNU_TARGET_OS_OSX) |
0a7de745 A |
7123 | // check if service has an alias to search its other "parents" if a parent match isn't found |
7124 | OSObject * prop = where->copyProperty(gIOServiceLegacyMatchingRegistryIDKey); | |
7125 | OSNumber * alternateRegistryID = OSDynamicCast(OSNumber, prop); | |
7126 | if (alternateRegistryID != NULL) { | |
7127 | if (aliasServiceRegIds == NULL) { | |
7128 | aliasServiceRegIds = OSArray::withCapacity(sizeof(alternateRegistryID)); | |
7129 | } | |
7130 | aliasServiceRegIds->setObject(alternateRegistryID); | |
7131 | } | |
7132 | OSSafeReleaseNULL(prop); | |
f427ee49 | 7133 | #endif /* defined(XNU_TARGET_OS_OSX) */ |
0a7de745 A |
7134 | } else { |
7135 | break; | |
7136 | } | |
490019cf | 7137 | |
0a7de745 | 7138 | where = where->getProvider(); |
f427ee49 | 7139 | #if defined(XNU_TARGET_OS_OSX) |
0a7de745 A |
7140 | if (where == NULL) { |
7141 | // there were no matching parent services, check to see if there are aliased services that have a matching parent | |
7142 | if (aliasServiceRegIds != NULL) { | |
7143 | unsigned int numAliasedServices = aliasServiceRegIds->getCount(); | |
7144 | if (numAliasedServices != 0) { | |
7145 | OSNumber* alternateRegistryID = OSDynamicCast(OSNumber, aliasServiceRegIds->getObject(numAliasedServices - 1)); | |
7146 | if (alternateRegistryID != NULL) { | |
7147 | OSDictionary* alternateMatchingDict = IOService::registryEntryIDMatching(alternateRegistryID->unsigned64BitValue()); | |
7148 | aliasServiceRegIds->removeObject(numAliasedServices - 1); | |
7149 | if (alternateMatchingDict != NULL) { | |
7150 | OSSafeReleaseNULL(foundAlternateService); | |
7151 | foundAlternateService = IOService::copyMatchingService(alternateMatchingDict); | |
7152 | alternateMatchingDict->release(); | |
7153 | if (foundAlternateService != NULL) { | |
7154 | where = foundAlternateService; | |
7155 | } | |
7156 | } | |
7157 | } | |
7158 | } | |
7159 | } | |
7160 | } | |
f427ee49 | 7161 | #endif /* defined(XNU_TARGET_OS_OSX) */ |
0a7de745 | 7162 | }while (where != NULL); |
490019cf | 7163 | |
f427ee49 | 7164 | #if defined(XNU_TARGET_OS_OSX) |
0a7de745 A |
7165 | OSSafeReleaseNULL(foundAlternateService); |
7166 | OSSafeReleaseNULL(aliasServiceRegIds); | |
f427ee49 | 7167 | #endif /* defined(XNU_TARGET_OS_OSX) */ |
9bccf70c | 7168 | |
316670eb | 7169 | #if MATCH_DEBUG |
0a7de745 A |
7170 | if (where != this) { |
7171 | OSSerialize * s = OSSerialize::withCapacity(128); | |
7172 | root->serialize(s); | |
7173 | kprintf("parent match 0x%llx, %d,\n%s\n", getRegistryEntryID(), match, s->text()); | |
7174 | s->release(); | |
7175 | } | |
316670eb | 7176 | #endif |
1c79356b | 7177 | |
0a7de745 | 7178 | return match; |
1c79356b A |
7179 | } |
7180 | ||
7181 | ||
0a7de745 A |
7182 | IOReturn |
7183 | IOService::newUserClient( task_t owningTask, void * securityID, | |
7184 | UInt32 type, OSDictionary * properties, | |
7185 | IOUserClient ** handler ) | |
1c79356b | 7186 | { |
cb323159 | 7187 | const OSSymbol *userClientClass = NULL; |
0a7de745 A |
7188 | IOUserClient *client; |
7189 | OSObject *prop; | |
7190 | OSObject *temp; | |
1c79356b | 7191 | |
cb323159 A |
7192 | if (reserved && reserved->uvars && reserved->uvars->userServer) { |
7193 | return reserved->uvars->userServer->serviceNewUserClient(this, owningTask, securityID, type, properties, handler); | |
7194 | } | |
7195 | ||
0a7de745 A |
7196 | if (kIOReturnSuccess == newUserClient( owningTask, securityID, type, handler )) { |
7197 | return kIOReturnSuccess; | |
7198 | } | |
0c530ab8 | 7199 | |
0a7de745 A |
7200 | // First try my own properties for a user client class name |
7201 | prop = copyProperty(gIOUserClientClassKey); | |
7202 | if (prop) { | |
7203 | if (OSDynamicCast(OSSymbol, prop)) { | |
7204 | userClientClass = (const OSSymbol *) prop; | |
7205 | } else if (OSDynamicCast(OSString, prop)) { | |
7206 | userClientClass = OSSymbol::withString((OSString *) prop); | |
7207 | if (userClientClass) { | |
7208 | setProperty(gIOUserClientClassKey, | |
7209 | (OSObject *) userClientClass); | |
7210 | } | |
7211 | } | |
1c79356b | 7212 | } |
1c79356b | 7213 | |
0a7de745 A |
7214 | // Didn't find one so lets just bomb out now without further ado. |
7215 | if (!userClientClass) { | |
7216 | OSSafeReleaseNULL(prop); | |
7217 | return kIOReturnUnsupported; | |
7218 | } | |
1c79356b | 7219 | |
0a7de745 A |
7220 | // This reference is consumed by the IOServiceOpen call |
7221 | temp = OSMetaClass::allocClassWithName(userClientClass); | |
7222 | OSSafeReleaseNULL(prop); | |
7223 | if (!temp) { | |
7224 | return kIOReturnNoMemory; | |
7225 | } | |
1c79356b | 7226 | |
0a7de745 A |
7227 | if (OSDynamicCast(IOUserClient, temp)) { |
7228 | client = (IOUserClient *) temp; | |
7229 | } else { | |
7230 | temp->release(); | |
7231 | return kIOReturnUnsupported; | |
7232 | } | |
1c79356b | 7233 | |
0a7de745 A |
7234 | if (!client->initWithTask(owningTask, securityID, type, properties)) { |
7235 | client->release(); | |
7236 | return kIOReturnBadArgument; | |
7237 | } | |
1c79356b | 7238 | |
0a7de745 A |
7239 | if (!client->attach(this)) { |
7240 | client->release(); | |
7241 | return kIOReturnUnsupported; | |
7242 | } | |
1c79356b | 7243 | |
0a7de745 A |
7244 | if (!client->start(this)) { |
7245 | client->detach(this); | |
7246 | client->release(); | |
7247 | return kIOReturnUnsupported; | |
7248 | } | |
1c79356b | 7249 | |
0a7de745 A |
7250 | *handler = client; |
7251 | return kIOReturnSuccess; | |
1c79356b A |
7252 | } |
7253 | ||
f427ee49 A |
7254 | IOReturn |
7255 | IOService::newUserClient( task_t owningTask, void * securityID, | |
7256 | UInt32 type, OSDictionary * properties, | |
7257 | OSSharedPtr<IOUserClient>& handler ) | |
7258 | { | |
7259 | IOUserClient* handlerRaw = NULL; | |
7260 | IOReturn result = newUserClient(owningTask, securityID, type, properties, &handlerRaw); | |
7261 | handler.reset(handlerRaw, OSNoRetain); | |
7262 | return result; | |
7263 | } | |
7264 | ||
0a7de745 A |
7265 | IOReturn |
7266 | IOService::newUserClient( task_t owningTask, void * securityID, | |
7267 | UInt32 type, IOUserClient ** handler ) | |
1c79356b | 7268 | { |
0a7de745 | 7269 | return kIOReturnUnsupported; |
1c79356b A |
7270 | } |
7271 | ||
f427ee49 A |
7272 | IOReturn |
7273 | IOService::newUserClient( task_t owningTask, void * securityID, | |
7274 | UInt32 type, OSSharedPtr<IOUserClient>& handler ) | |
7275 | { | |
7276 | IOUserClient* handlerRaw = nullptr; | |
7277 | IOReturn result = IOService::newUserClient(owningTask, securityID, type, &handlerRaw); | |
7278 | handler.reset(handlerRaw, OSNoRetain); | |
7279 | return result; | |
7280 | } | |
7281 | ||
7282 | ||
0a7de745 A |
7283 | IOReturn |
7284 | IOService::requestProbe( IOOptionBits options ) | |
1c79356b | 7285 | { |
0a7de745 | 7286 | return kIOReturnUnsupported; |
1c79356b A |
7287 | } |
7288 | ||
f427ee49 A |
7289 | bool |
7290 | IOService::hasUserServer() const | |
7291 | { | |
7292 | return reserved && reserved->uvars && reserved->uvars->userServer; | |
7293 | } | |
7294 | ||
1c79356b A |
7295 | /* |
7296 | * Convert an IOReturn to text. Subclasses which add additional | |
0a7de745 | 7297 | * IOReturn's should override this method and call |
1c79356b A |
7298 | * super::stringFromReturn if the desired value is not found. |
7299 | */ | |
7300 | ||
0a7de745 A |
7301 | const char * |
7302 | IOService::stringFromReturn( IOReturn rtn ) | |
7303 | { | |
7304 | static const IONamedValue IOReturn_values[] = { | |
7305 | {kIOReturnSuccess, "success" }, | |
7306 | {kIOReturnError, "general error" }, | |
7307 | {kIOReturnNoMemory, "memory allocation error" }, | |
7308 | {kIOReturnNoResources, "resource shortage" }, | |
7309 | {kIOReturnIPCError, "Mach IPC failure" }, | |
7310 | {kIOReturnNoDevice, "no such device" }, | |
7311 | {kIOReturnNotPrivileged, "privilege violation" }, | |
7312 | {kIOReturnBadArgument, "invalid argument" }, | |
7313 | {kIOReturnLockedRead, "device is read locked" }, | |
7314 | {kIOReturnLockedWrite, "device is write locked" }, | |
7315 | {kIOReturnExclusiveAccess, "device is exclusive access" }, | |
7316 | {kIOReturnBadMessageID, "bad IPC message ID" }, | |
7317 | {kIOReturnUnsupported, "unsupported function" }, | |
7318 | {kIOReturnVMError, "virtual memory error" }, | |
7319 | {kIOReturnInternalError, "internal driver error" }, | |
7320 | {kIOReturnIOError, "I/O error" }, | |
7321 | {kIOReturnCannotLock, "cannot acquire lock" }, | |
7322 | {kIOReturnNotOpen, "device is not open" }, | |
7323 | {kIOReturnNotReadable, "device is not readable" }, | |
7324 | {kIOReturnNotWritable, "device is not writeable" }, | |
7325 | {kIOReturnNotAligned, "alignment error" }, | |
7326 | {kIOReturnBadMedia, "media error" }, | |
7327 | {kIOReturnStillOpen, "device is still open" }, | |
7328 | {kIOReturnRLDError, "rld failure" }, | |
7329 | {kIOReturnDMAError, "DMA failure" }, | |
7330 | {kIOReturnBusy, "device is busy" }, | |
7331 | {kIOReturnTimeout, "I/O timeout" }, | |
7332 | {kIOReturnOffline, "device is offline" }, | |
7333 | {kIOReturnNotReady, "device is not ready" }, | |
7334 | {kIOReturnNotAttached, "device/channel is not attached" }, | |
7335 | {kIOReturnNoChannels, "no DMA channels available" }, | |
7336 | {kIOReturnNoSpace, "no space for data" }, | |
7337 | {kIOReturnPortExists, "device port already exists" }, | |
7338 | {kIOReturnCannotWire, "cannot wire physical memory" }, | |
7339 | {kIOReturnNoInterrupt, "no interrupt attached" }, | |
7340 | {kIOReturnNoFrames, "no DMA frames enqueued" }, | |
7341 | {kIOReturnMessageTooLarge, "message is too large" }, | |
7342 | {kIOReturnNotPermitted, "operation is not permitted" }, | |
7343 | {kIOReturnNoPower, "device is without power" }, | |
7344 | {kIOReturnNoMedia, "media is not present" }, | |
7345 | {kIOReturnUnformattedMedia, "media is not formatted" }, | |
7346 | {kIOReturnUnsupportedMode, "unsupported mode" }, | |
7347 | {kIOReturnUnderrun, "data underrun" }, | |
7348 | {kIOReturnOverrun, "data overrun" }, | |
7349 | {kIOReturnDeviceError, "device error" }, | |
7350 | {kIOReturnNoCompletion, "no completion routine" }, | |
7351 | {kIOReturnAborted, "operation was aborted" }, | |
7352 | {kIOReturnNoBandwidth, "bus bandwidth would be exceeded" }, | |
7353 | {kIOReturnNotResponding, "device is not responding" }, | |
7354 | {kIOReturnInvalid, "unanticipated driver error" }, | |
7355 | {0, NULL } | |
7356 | }; | |
7357 | ||
7358 | return IOFindNameForValue(rtn, IOReturn_values); | |
1c79356b A |
7359 | } |
7360 | ||
7361 | /* | |
7362 | * Convert an IOReturn to an errno. | |
7363 | */ | |
0a7de745 A |
7364 | int |
7365 | IOService::errnoFromReturn( IOReturn rtn ) | |
7366 | { | |
7367 | if (unix_err(err_get_code(rtn)) == rtn) { | |
7368 | return err_get_code(rtn); | |
7369 | } | |
7370 | ||
7371 | switch (rtn) { | |
7372 | // (obvious match) | |
7373 | case kIOReturnSuccess: | |
7374 | return 0; | |
7375 | case kIOReturnNoMemory: | |
7376 | return ENOMEM; | |
7377 | case kIOReturnNoDevice: | |
7378 | return ENXIO; | |
7379 | case kIOReturnVMError: | |
7380 | return EFAULT; | |
7381 | case kIOReturnNotPermitted: | |
7382 | return EPERM; | |
7383 | case kIOReturnNotPrivileged: | |
7384 | return EACCES; | |
7385 | case kIOReturnIOError: | |
7386 | return EIO; | |
7387 | case kIOReturnNotWritable: | |
7388 | return EROFS; | |
7389 | case kIOReturnBadArgument: | |
7390 | return EINVAL; | |
7391 | case kIOReturnUnsupported: | |
7392 | return ENOTSUP; | |
7393 | case kIOReturnBusy: | |
7394 | return EBUSY; | |
7395 | case kIOReturnNoPower: | |
7396 | return EPWROFF; | |
7397 | case kIOReturnDeviceError: | |
7398 | return EDEVERR; | |
7399 | case kIOReturnTimeout: | |
7400 | return ETIMEDOUT; | |
7401 | case kIOReturnMessageTooLarge: | |
7402 | return EMSGSIZE; | |
7403 | case kIOReturnNoSpace: | |
7404 | return ENOSPC; | |
7405 | case kIOReturnCannotLock: | |
7406 | return ENOLCK; | |
7407 | ||
7408 | // (best match) | |
7409 | case kIOReturnBadMessageID: | |
7410 | case kIOReturnNoCompletion: | |
7411 | case kIOReturnNotAligned: | |
7412 | return EINVAL; | |
7413 | case kIOReturnNotReady: | |
7414 | return EBUSY; | |
7415 | case kIOReturnRLDError: | |
7416 | return EBADMACHO; | |
7417 | case kIOReturnPortExists: | |
7418 | case kIOReturnStillOpen: | |
7419 | return EEXIST; | |
7420 | case kIOReturnExclusiveAccess: | |
7421 | case kIOReturnLockedRead: | |
7422 | case kIOReturnLockedWrite: | |
7423 | case kIOReturnNotOpen: | |
7424 | case kIOReturnNotReadable: | |
7425 | return EACCES; | |
7426 | case kIOReturnCannotWire: | |
7427 | case kIOReturnNoResources: | |
7428 | return ENOMEM; | |
7429 | case kIOReturnAborted: | |
7430 | case kIOReturnOffline: | |
7431 | case kIOReturnNotResponding: | |
7432 | return EBUSY; | |
7433 | case kIOReturnBadMedia: | |
7434 | case kIOReturnNoMedia: | |
7435 | case kIOReturnNotAttached: | |
7436 | case kIOReturnUnformattedMedia: | |
7437 | return ENXIO; // (media error) | |
7438 | case kIOReturnDMAError: | |
7439 | case kIOReturnOverrun: | |
7440 | case kIOReturnUnderrun: | |
7441 | return EIO; // (transfer error) | |
7442 | case kIOReturnNoBandwidth: | |
7443 | case kIOReturnNoChannels: | |
7444 | case kIOReturnNoFrames: | |
7445 | case kIOReturnNoInterrupt: | |
7446 | return EIO; // (hardware error) | |
7447 | case kIOReturnError: | |
7448 | case kIOReturnInternalError: | |
7449 | case kIOReturnInvalid: | |
7450 | return EIO; // (generic error) | |
7451 | case kIOReturnIPCError: | |
7452 | return EIO; // (ipc error) | |
7453 | default: | |
7454 | return EIO; // (all other errors) | |
7455 | } | |
7456 | } | |
7457 | ||
7458 | IOReturn | |
7459 | IOService::message( UInt32 type, IOService * provider, | |
7460 | void * argument ) | |
7461 | { | |
7462 | /* | |
7463 | * Generic entry point for calls from the provider. A return value of | |
7464 | * kIOReturnSuccess indicates that the message was received, and where | |
7465 | * applicable, that it was successful. | |
7466 | */ | |
7467 | ||
7468 | return kIOReturnUnsupported; | |
1c79356b A |
7469 | } |
7470 | ||
7471 | /* | |
7472 | * Device memory | |
7473 | */ | |
7474 | ||
0a7de745 A |
7475 | IOItemCount |
7476 | IOService::getDeviceMemoryCount( void ) | |
1c79356b | 7477 | { |
0a7de745 A |
7478 | OSArray * array; |
7479 | IOItemCount count; | |
1c79356b | 7480 | |
0a7de745 A |
7481 | array = OSDynamicCast( OSArray, getProperty( gIODeviceMemoryKey)); |
7482 | if (array) { | |
7483 | count = array->getCount(); | |
7484 | } else { | |
7485 | count = 0; | |
7486 | } | |
1c79356b | 7487 | |
0a7de745 | 7488 | return count; |
1c79356b A |
7489 | } |
7490 | ||
0a7de745 A |
7491 | IODeviceMemory * |
7492 | IOService::getDeviceMemoryWithIndex( unsigned int index ) | |
1c79356b | 7493 | { |
0a7de745 A |
7494 | OSArray * array; |
7495 | IODeviceMemory * range; | |
1c79356b | 7496 | |
0a7de745 A |
7497 | array = OSDynamicCast( OSArray, getProperty( gIODeviceMemoryKey)); |
7498 | if (array) { | |
7499 | range = (IODeviceMemory *) array->getObject( index ); | |
7500 | } else { | |
cb323159 | 7501 | range = NULL; |
0a7de745 | 7502 | } |
1c79356b | 7503 | |
0a7de745 | 7504 | return range; |
1c79356b A |
7505 | } |
7506 | ||
0a7de745 A |
7507 | IOMemoryMap * |
7508 | IOService::mapDeviceMemoryWithIndex( unsigned int index, | |
7509 | IOOptionBits options ) | |
1c79356b | 7510 | { |
0a7de745 A |
7511 | IODeviceMemory * range; |
7512 | IOMemoryMap * map; | |
1c79356b | 7513 | |
0a7de745 A |
7514 | range = getDeviceMemoryWithIndex( index ); |
7515 | if (range) { | |
7516 | map = range->map( options ); | |
7517 | } else { | |
cb323159 | 7518 | map = NULL; |
0a7de745 | 7519 | } |
1c79356b | 7520 | |
0a7de745 | 7521 | return map; |
1c79356b A |
7522 | } |
7523 | ||
0a7de745 A |
7524 | OSArray * |
7525 | IOService::getDeviceMemory( void ) | |
1c79356b | 7526 | { |
0a7de745 | 7527 | return OSDynamicCast( OSArray, getProperty( gIODeviceMemoryKey)); |
1c79356b A |
7528 | } |
7529 | ||
7530 | ||
0a7de745 A |
7531 | void |
7532 | IOService::setDeviceMemory( OSArray * array ) | |
1c79356b | 7533 | { |
0a7de745 | 7534 | setProperty( gIODeviceMemoryKey, array); |
1c79356b A |
7535 | } |
7536 | ||
593a1d5f A |
7537 | static void |
7538 | requireMaxCpuDelay(IOService * service, UInt32 ns, UInt32 delayType) | |
0c530ab8 | 7539 | { |
0a7de745 A |
7540 | static const UInt kNoReplace = -1U; // Must be an illegal index |
7541 | UInt replace = kNoReplace; | |
7542 | bool setCpuDelay = false; | |
7543 | ||
7544 | IORecursiveLockLock(sCpuDelayLock); | |
7545 | ||
7546 | UInt count = sCpuDelayData->getLength() / sizeof(CpuDelayEntry); | |
7547 | CpuDelayEntry *entries = (CpuDelayEntry *) sCpuDelayData->getBytesNoCopy(); | |
7548 | IOService * holder = NULL; | |
7549 | ||
7550 | if (ns) { | |
7551 | const CpuDelayEntry ne = {service, ns, delayType}; | |
7552 | holder = service; | |
7553 | // Set maximum delay. | |
7554 | for (UInt i = 0; i < count; i++) { | |
7555 | IOService *thisService = entries[i].fService; | |
7556 | bool sameType = (delayType == entries[i].fDelayType); | |
7557 | if ((service == thisService) && sameType) { | |
7558 | replace = i; | |
7559 | } else if (!thisService) { | |
7560 | if (kNoReplace == replace) { | |
7561 | replace = i; | |
7562 | } | |
7563 | } else if (sameType) { | |
7564 | const UInt32 thisMax = entries[i].fMaxDelay; | |
7565 | if (thisMax < ns) { | |
7566 | ns = thisMax; | |
7567 | holder = thisService; | |
7568 | } | |
7569 | } | |
7570 | } | |
7571 | ||
7572 | setCpuDelay = true; | |
7573 | if (kNoReplace == replace) { | |
7574 | sCpuDelayData->appendBytes(&ne, sizeof(ne)); | |
7575 | } else { | |
7576 | entries[replace] = ne; | |
7577 | } | |
7578 | } else { | |
7579 | ns = -1U; // Set to max unsigned, i.e. no restriction | |
7580 | ||
7581 | for (UInt i = 0; i < count; i++) { | |
7582 | // Clear a maximum delay. | |
7583 | IOService *thisService = entries[i].fService; | |
7584 | if (thisService && (delayType == entries[i].fDelayType)) { | |
7585 | UInt32 thisMax = entries[i].fMaxDelay; | |
7586 | if (service == thisService) { | |
7587 | replace = i; | |
7588 | } else if (thisMax < ns) { | |
7589 | ns = thisMax; | |
7590 | holder = thisService; | |
7591 | } | |
7592 | } | |
7593 | } | |
7594 | ||
7595 | // Check if entry found | |
7596 | if (kNoReplace != replace) { | |
cb323159 | 7597 | entries[replace].fService = NULL; // Null the entry |
0a7de745 A |
7598 | setCpuDelay = true; |
7599 | } | |
7600 | } | |
7601 | ||
7602 | if (setCpuDelay) { | |
7603 | if (holder && debug_boot_arg) { | |
7604 | strlcpy(sCPULatencyHolderName[delayType], holder->getName(), sizeof(sCPULatencyHolderName[delayType])); | |
7605 | } | |
7606 | ||
7607 | // Must be safe to call from locked context | |
7608 | if (delayType == kCpuDelayBusStall) { | |
f427ee49 | 7609 | #if defined(__x86_64__) |
0a7de745 | 7610 | ml_set_maxbusdelay(ns); |
f427ee49 A |
7611 | #endif /* defined(__x86_64__) */ |
7612 | } | |
7613 | #if defined(__x86_64__) | |
7614 | else if (delayType == kCpuDelayInterrupt) { | |
0a7de745 A |
7615 | ml_set_maxintdelay(ns); |
7616 | } | |
f427ee49 | 7617 | #endif /* defined(__x86_64__) */ |
0a7de745 A |
7618 | sCPULatencyHolder[delayType]->setValue(holder ? holder->getRegistryEntryID() : 0); |
7619 | sCPULatencySet[delayType]->setValue(ns); | |
7620 | ||
7621 | OSArray * handlers = sCpuLatencyHandlers[delayType]; | |
7622 | IOService * target; | |
7623 | if (handlers) { | |
7624 | for (unsigned int idx = 0; | |
593a1d5f | 7625 | (target = (IOService *) handlers->getObject(idx)); |
0a7de745 A |
7626 | idx++) { |
7627 | target->callPlatformFunction(sCPULatencyFunctionName[delayType], false, | |
7628 | (void *) (uintptr_t) ns, holder, | |
7629 | NULL, NULL); | |
7630 | } | |
7631 | } | |
0c530ab8 A |
7632 | } |
7633 | ||
0a7de745 | 7634 | IORecursiveLockUnlock(sCpuDelayLock); |
593a1d5f A |
7635 | } |
7636 | ||
7637 | static IOReturn | |
7638 | setLatencyHandler(UInt32 delayType, IOService * target, bool enable) | |
7639 | { | |
0a7de745 A |
7640 | IOReturn result = kIOReturnNotFound; |
7641 | OSArray * array; | |
7642 | unsigned int idx; | |
7643 | ||
7644 | IORecursiveLockLock(sCpuDelayLock); | |
7645 | ||
7646 | do{ | |
7647 | if (enable && !sCpuLatencyHandlers[delayType]) { | |
7648 | sCpuLatencyHandlers[delayType] = OSArray::withCapacity(4); | |
7649 | } | |
7650 | array = sCpuLatencyHandlers[delayType]; | |
7651 | if (!array) { | |
7652 | break; | |
7653 | } | |
7654 | idx = array->getNextIndexOfObject(target, 0); | |
7655 | if (!enable) { | |
7656 | if (-1U != idx) { | |
7657 | array->removeObject(idx); | |
7658 | result = kIOReturnSuccess; | |
7659 | } | |
7660 | } else { | |
7661 | if (-1U != idx) { | |
7662 | result = kIOReturnExclusiveAccess; | |
7663 | break; | |
7664 | } | |
7665 | array->setObject(target); | |
7666 | ||
7667 | UInt count = sCpuDelayData->getLength() / sizeof(CpuDelayEntry); | |
7668 | CpuDelayEntry *entries = (CpuDelayEntry *) sCpuDelayData->getBytesNoCopy(); | |
7669 | UInt32 ns = -1U; // Set to max unsigned, i.e. no restriction | |
7670 | IOService * holder = NULL; | |
7671 | ||
7672 | for (UInt i = 0; i < count; i++) { | |
7673 | if (entries[i].fService | |
7674 | && (delayType == entries[i].fDelayType) | |
7675 | && (entries[i].fMaxDelay < ns)) { | |
7676 | ns = entries[i].fMaxDelay; | |
7677 | holder = entries[i].fService; | |
7678 | } | |
7679 | } | |
7680 | target->callPlatformFunction(sCPULatencyFunctionName[delayType], false, | |
7681 | (void *) (uintptr_t) ns, holder, | |
7682 | NULL, NULL); | |
7683 | result = kIOReturnSuccess; | |
7684 | } | |
7685 | }while (false); | |
7686 | ||
7687 | IORecursiveLockUnlock(sCpuDelayLock); | |
7688 | ||
7689 | return result; | |
593a1d5f A |
7690 | } |
7691 | ||
f427ee49 A |
7692 | IOReturn |
7693 | IOService::requireMaxBusStall(UInt32 ns) | |
7694 | { | |
7695 | #if !defined(__x86_64__) | |
7696 | switch (ns) { | |
7697 | case kIOMaxBusStall40usec: | |
7698 | case kIOMaxBusStall30usec: | |
7699 | case kIOMaxBusStall25usec: | |
7700 | case kIOMaxBusStall20usec: | |
7701 | case kIOMaxBusStall10usec: | |
7702 | case kIOMaxBusStall5usec: | |
7703 | case kIOMaxBusStallNone: | |
7704 | break; | |
7705 | default: | |
7706 | return kIOReturnBadArgument; | |
7707 | } | |
7708 | #endif /* !defined(__x86_64__) */ | |
0a7de745 | 7709 | requireMaxCpuDelay(this, ns, kCpuDelayBusStall); |
f427ee49 | 7710 | return kIOReturnSuccess; |
0c530ab8 A |
7711 | } |
7712 | ||
f427ee49 A |
7713 | IOReturn |
7714 | IOService::requireMaxInterruptDelay(uint32_t ns) | |
b0d623f7 | 7715 | { |
f427ee49 | 7716 | #if defined(__x86_64__) |
0a7de745 | 7717 | requireMaxCpuDelay(this, ns, kCpuDelayInterrupt); |
f427ee49 A |
7718 | return kIOReturnSuccess; |
7719 | #else /* defined(__x86_64__) */ | |
7720 | return kIOReturnUnsupported; | |
7721 | #endif /* defined(__x86_64__) */ | |
b0d623f7 A |
7722 | } |
7723 | ||
1c79356b A |
7724 | /* |
7725 | * Device interrupts | |
7726 | */ | |
7727 | ||
0a7de745 A |
7728 | IOReturn |
7729 | IOService::resolveInterrupt(IOService *nub, int source) | |
7730 | { | |
7731 | IOInterruptController *interruptController; | |
7732 | OSArray *array; | |
7733 | OSData *data; | |
7734 | OSSymbol *interruptControllerName; | |
f427ee49 | 7735 | unsigned int numSources; |
0a7de745 A |
7736 | IOInterruptSource *interruptSources; |
7737 | ||
7738 | // Get the parents list from the nub. | |
7739 | array = OSDynamicCast(OSArray, nub->getProperty(gIOInterruptControllersKey)); | |
cb323159 | 7740 | if (array == NULL) { |
0a7de745 A |
7741 | return kIOReturnNoResources; |
7742 | } | |
7743 | ||
7744 | // Allocate space for the IOInterruptSources if needed... then return early. | |
cb323159 | 7745 | if (nub->_interruptSources == NULL) { |
0a7de745 A |
7746 | numSources = array->getCount(); |
7747 | interruptSources = (IOInterruptSource *)IOMalloc( | |
7748 | numSources * sizeofAllIOInterruptSource); | |
cb323159 | 7749 | if (interruptSources == NULL) { |
0a7de745 A |
7750 | return kIOReturnNoMemory; |
7751 | } | |
7752 | ||
7753 | bzero(interruptSources, numSources * sizeofAllIOInterruptSource); | |
7754 | ||
7755 | nub->_numInterruptSources = numSources; | |
7756 | nub->_interruptSources = interruptSources; | |
7757 | return kIOReturnSuccess; | |
7758 | } | |
7759 | ||
7760 | interruptControllerName = OSDynamicCast(OSSymbol, array->getObject(source)); | |
cb323159 | 7761 | if (interruptControllerName == NULL) { |
0a7de745 A |
7762 | return kIOReturnNoResources; |
7763 | } | |
7764 | ||
7765 | interruptController = getPlatform()->lookUpInterruptController(interruptControllerName); | |
cb323159 | 7766 | if (interruptController == NULL) { |
0a7de745 A |
7767 | return kIOReturnNoResources; |
7768 | } | |
7769 | ||
7770 | // Get the interrupt numbers from the nub. | |
7771 | array = OSDynamicCast(OSArray, nub->getProperty(gIOInterruptSpecifiersKey)); | |
cb323159 | 7772 | if (array == NULL) { |
0a7de745 A |
7773 | return kIOReturnNoResources; |
7774 | } | |
7775 | data = OSDynamicCast(OSData, array->getObject(source)); | |
cb323159 | 7776 | if (data == NULL) { |
0a7de745 A |
7777 | return kIOReturnNoResources; |
7778 | } | |
7779 | ||
7780 | // Set the interruptController and interruptSource in the nub's table. | |
7781 | interruptSources = nub->_interruptSources; | |
7782 | interruptSources[source].interruptController = interruptController; | |
7783 | interruptSources[source].vectorData = data; | |
7784 | ||
7785 | return kIOReturnSuccess; | |
1c79356b A |
7786 | } |
7787 | ||
0a7de745 A |
7788 | IOReturn |
7789 | IOService::lookupInterrupt(int source, bool resolve, IOInterruptController **interruptController) | |
1c79356b | 7790 | { |
0a7de745 A |
7791 | IOReturn ret; |
7792 | ||
7793 | /* Make sure the _interruptSources are set */ | |
cb323159 | 7794 | if (_interruptSources == NULL) { |
0a7de745 A |
7795 | ret = resolveInterrupt(this, source); |
7796 | if (ret != kIOReturnSuccess) { | |
7797 | return ret; | |
7798 | } | |
7799 | } | |
7800 | ||
7801 | /* Make sure the local source number is valid */ | |
7802 | if ((source < 0) || (source >= _numInterruptSources)) { | |
7803 | return kIOReturnNoInterrupt; | |
7804 | } | |
7805 | ||
7806 | /* Look up the contoller for the local source */ | |
7807 | *interruptController = _interruptSources[source].interruptController; | |
7808 | ||
7809 | if (*interruptController == NULL) { | |
7810 | if (!resolve) { | |
7811 | return kIOReturnNoInterrupt; | |
7812 | } | |
7813 | ||
7814 | /* Try to resolve the interrupt */ | |
7815 | ret = resolveInterrupt(this, source); | |
7816 | if (ret != kIOReturnSuccess) { | |
7817 | return ret; | |
7818 | } | |
7819 | ||
7820 | *interruptController = _interruptSources[source].interruptController; | |
7821 | } | |
7822 | ||
7823 | return kIOReturnSuccess; | |
7824 | } | |
7825 | ||
7826 | IOReturn | |
7827 | IOService::registerInterrupt(int source, OSObject *target, | |
7828 | IOInterruptAction handler, | |
7829 | void *refCon) | |
7830 | { | |
7831 | IOInterruptController *interruptController; | |
7832 | IOReturn ret; | |
7833 | ||
7834 | ret = lookupInterrupt(source, true, &interruptController); | |
7835 | if (ret != kIOReturnSuccess) { | |
7836 | return ret; | |
7837 | } | |
7838 | ||
7839 | /* Register the source */ | |
7840 | return interruptController->registerInterrupt(this, source, target, | |
7841 | (IOInterruptHandler)handler, | |
7842 | refCon); | |
1c79356b A |
7843 | } |
7844 | ||
0a7de745 A |
7845 | static void |
7846 | IOServiceInterruptActionToBlock( OSObject * target, void * refCon, | |
7847 | IOService * nub, int source ) | |
39236c6e | 7848 | { |
0a7de745 A |
7849 | ((IOInterruptActionBlock)(refCon))(nub, source); |
7850 | } | |
7851 | ||
7852 | IOReturn | |
7853 | IOService::registerInterruptBlock(int source, OSObject *target, | |
7854 | IOInterruptActionBlock handler) | |
7855 | { | |
7856 | IOReturn ret; | |
7857 | void * block; | |
7858 | ||
7859 | block = Block_copy(handler); | |
7860 | if (!block) { | |
7861 | return kIOReturnNoMemory; | |
7862 | } | |
7863 | ||
7864 | ret = registerInterrupt(source, target, &IOServiceInterruptActionToBlock, block); | |
7865 | if (kIOReturnSuccess != ret) { | |
7866 | Block_release(block); | |
7867 | return ret; | |
7868 | } | |
7869 | _interruptSourcesPrivate(this)[source].vectorBlock = block; | |
39236c6e | 7870 | |
0a7de745 A |
7871 | return ret; |
7872 | } | |
39037602 | 7873 | |
0a7de745 A |
7874 | IOReturn |
7875 | IOService::unregisterInterrupt(int source) | |
7876 | { | |
7877 | IOReturn ret; | |
7878 | IOInterruptController *interruptController; | |
7879 | void *block; | |
7880 | ||
7881 | ret = lookupInterrupt(source, false, &interruptController); | |
7882 | if (ret != kIOReturnSuccess) { | |
7883 | return ret; | |
7884 | } | |
39037602 | 7885 | |
0a7de745 A |
7886 | /* Unregister the source */ |
7887 | block = _interruptSourcesPrivate(this)[source].vectorBlock; | |
7888 | ret = interruptController->unregisterInterrupt(this, source); | |
7889 | if ((kIOReturnSuccess == ret) && (block = _interruptSourcesPrivate(this)[source].vectorBlock)) { | |
7890 | _interruptSourcesPrivate(this)[source].vectorBlock = NULL; | |
7891 | Block_release(block); | |
7892 | } | |
fe8ab488 | 7893 | |
0a7de745 | 7894 | return ret; |
39236c6e A |
7895 | } |
7896 | ||
0a7de745 A |
7897 | IOReturn |
7898 | IOService::addInterruptStatistics(IOInterruptAccountingData * statistics, int source) | |
39236c6e | 7899 | { |
0a7de745 A |
7900 | IOReportLegend * legend = NULL; |
7901 | IOInterruptAccountingData * oldValue = NULL; | |
7902 | IOInterruptAccountingReporter * newArray = NULL; | |
7903 | char subgroupName[64]; | |
7904 | int newArraySize = 0; | |
7905 | int i = 0; | |
7906 | ||
7907 | if (source < 0) { | |
7908 | return kIOReturnBadArgument; | |
7909 | } | |
7910 | ||
7911 | /* | |
7912 | * We support statistics on a maximum of 256 interrupts per nub; if a nub | |
7913 | * has more than 256 interrupt specifiers associated with it, and tries | |
7914 | * to register a high interrupt index with interrupt accounting, panic. | |
7915 | * Having more than 256 interrupts associated with a single nub is | |
7916 | * probably a sign that something fishy is going on. | |
7917 | */ | |
7918 | if (source > IA_INDEX_MAX) { | |
7919 | panic("addInterruptStatistics called for an excessively large index (%d)", source); | |
7920 | } | |
7921 | ||
7922 | /* | |
7923 | * TODO: This is ugly (wrapping a lock around an allocation). I'm only | |
7924 | * leaving it as is because the likelihood of contention where we are | |
7925 | * actually growing the array is minimal (we would realistically need | |
7926 | * to be starting a driver for the first time, with an IOReporting | |
7927 | * client already in place). Nonetheless, cleanup that can be done | |
7928 | * to adhere to best practices; it'll make the code more complicated, | |
7929 | * unfortunately. | |
7930 | */ | |
7931 | IOLockLock(reserved->interruptStatisticsLock); | |
7932 | ||
7933 | /* | |
7934 | * Lazily allocate the statistics array. | |
7935 | */ | |
7936 | if (!reserved->interruptStatisticsArray) { | |
7937 | reserved->interruptStatisticsArray = IONew(IOInterruptAccountingReporter, 1); | |
7938 | assert(reserved->interruptStatisticsArray); | |
7939 | reserved->interruptStatisticsArrayCount = 1; | |
7940 | bzero(reserved->interruptStatisticsArray, sizeof(*reserved->interruptStatisticsArray)); | |
7941 | } | |
39236c6e | 7942 | |
0a7de745 A |
7943 | if (source >= reserved->interruptStatisticsArrayCount) { |
7944 | /* | |
7945 | * We're still within the range of supported indices, but we are out | |
7946 | * of space in the current array. Do a nasty realloc (because | |
7947 | * IORealloc isn't a thing) here. We'll double the size with each | |
7948 | * reallocation. | |
7949 | * | |
7950 | * Yes, the "next power of 2" could be more efficient; but this will | |
7951 | * be invoked incredibly rarely. Who cares. | |
7952 | */ | |
7953 | newArraySize = (reserved->interruptStatisticsArrayCount << 1); | |
7954 | ||
7955 | while (newArraySize <= source) { | |
7956 | newArraySize = (newArraySize << 1); | |
7957 | } | |
7958 | newArray = IONew(IOInterruptAccountingReporter, newArraySize); | |
7959 | ||
7960 | assert(newArray); | |
7961 | ||
7962 | /* | |
7963 | * TODO: This even zeroes the memory it is about to overwrite. | |
7964 | * Shameful; fix it. Not particularly high impact, however. | |
7965 | */ | |
7966 | bzero(newArray, newArraySize * sizeof(*newArray)); | |
7967 | memcpy(newArray, reserved->interruptStatisticsArray, reserved->interruptStatisticsArrayCount * sizeof(*newArray)); | |
7968 | IODelete(reserved->interruptStatisticsArray, IOInterruptAccountingReporter, reserved->interruptStatisticsArrayCount); | |
7969 | reserved->interruptStatisticsArray = newArray; | |
7970 | reserved->interruptStatisticsArrayCount = newArraySize; | |
7971 | } | |
7972 | ||
7973 | if (!reserved->interruptStatisticsArray[source].reporter) { | |
7974 | /* | |
7975 | * We don't have a reporter associated with this index yet, so we | |
7976 | * need to create one. | |
7977 | */ | |
7978 | /* | |
7979 | * TODO: Some statistics do in fact have common units (time); should this be | |
7980 | * split into separate reporters to communicate this? | |
7981 | */ | |
7982 | reserved->interruptStatisticsArray[source].reporter = IOSimpleReporter::with(this, kIOReportCategoryPower, kIOReportUnitNone); | |
7983 | ||
7984 | /* | |
7985 | * Each statistic is given an identifier based on the interrupt index (which | |
7986 | * should be unique relative to any single nub) and the statistic involved. | |
7987 | * We should now have a sane (small and positive) index, so start | |
7988 | * constructing the channels for statistics. | |
7989 | */ | |
7990 | for (i = 0; i < IA_NUM_INTERRUPT_ACCOUNTING_STATISTICS; i++) { | |
7991 | /* | |
7992 | * TODO: Currently, this does not add channels for disabled statistics. | |
7993 | * Will this be confusing for clients? If so, we should just add the | |
7994 | * channels; we can avoid updating the channels even if they exist. | |
7995 | */ | |
7996 | if (IA_GET_STATISTIC_ENABLED(i)) { | |
7997 | reserved->interruptStatisticsArray[source].reporter->addChannel(IA_GET_CHANNEL_ID(source, i), kInterruptAccountingStatisticNameArray[i]); | |
7998 | } | |
7999 | } | |
8000 | ||
8001 | /* | |
8002 | * We now need to add the legend for this reporter to the registry. | |
8003 | */ | |
8004 | OSObject * prop = copyProperty(kIOReportLegendKey); | |
8005 | legend = IOReportLegend::with(OSDynamicCast(OSArray, prop)); | |
8006 | OSSafeReleaseNULL(prop); | |
8007 | ||
8008 | /* | |
8009 | * Note that while we compose the subgroup name, we do not need to | |
8010 | * manage its lifecycle (the reporter will handle this). | |
8011 | */ | |
8012 | snprintf(subgroupName, sizeof(subgroupName), "%s %d", getName(), source); | |
8013 | subgroupName[sizeof(subgroupName) - 1] = 0; | |
8014 | legend->addReporterLegend(reserved->interruptStatisticsArray[source].reporter, kInterruptAccountingGroupName, subgroupName); | |
8015 | setProperty(kIOReportLegendKey, legend->getLegend()); | |
8016 | legend->release(); | |
8017 | ||
8018 | /* | |
8019 | * TODO: Is this a good idea? Probably not; my assumption is it opts | |
8020 | * all entities who register interrupts into public disclosure of all | |
8021 | * IOReporting channels. Unfortunately, this appears to be as fine | |
8022 | * grain as it gets. | |
8023 | */ | |
8024 | setProperty(kIOReportLegendPublicKey, true); | |
8025 | } | |
39236c6e | 8026 | |
0a7de745 A |
8027 | /* |
8028 | * Don't stomp existing entries. If we are about to, panic; this | |
8029 | * probably means we failed to tear down our old interrupt source | |
8030 | * correctly. | |
8031 | */ | |
8032 | oldValue = reserved->interruptStatisticsArray[source].statistics; | |
fe8ab488 | 8033 | |
0a7de745 A |
8034 | if (oldValue) { |
8035 | panic("addInterruptStatistics call for index %d would have clobbered existing statistics", source); | |
8036 | } | |
8037 | ||
8038 | reserved->interruptStatisticsArray[source].statistics = statistics; | |
fe8ab488 | 8039 | |
0a7de745 A |
8040 | /* |
8041 | * Inherit the reporter values for each statistic. The target may | |
8042 | * be torn down as part of the runtime of the service (especially | |
8043 | * for sleep/wake), so we inherit in order to avoid having values | |
8044 | * reset for no apparent reason. Our statistics are ultimately | |
8045 | * tied to the index and the sevice, not to an individual target, | |
8046 | * so we should maintain them accordingly. | |
8047 | */ | |
8048 | interruptAccountingDataInheritChannels(reserved->interruptStatisticsArray[source].statistics, reserved->interruptStatisticsArray[source].reporter); | |
fe8ab488 | 8049 | |
0a7de745 | 8050 | IOLockUnlock(reserved->interruptStatisticsLock); |
fe8ab488 | 8051 | |
0a7de745 | 8052 | return kIOReturnSuccess; |
39236c6e A |
8053 | } |
8054 | ||
0a7de745 A |
8055 | IOReturn |
8056 | IOService::removeInterruptStatistics(int source) | |
8057 | { | |
8058 | IOInterruptAccountingData * value = NULL; | |
8059 | ||
8060 | if (source < 0) { | |
8061 | return kIOReturnBadArgument; | |
8062 | } | |
8063 | ||
8064 | IOLockLock(reserved->interruptStatisticsLock); | |
8065 | ||
8066 | /* | |
8067 | * We dynamically grow the statistics array, so an excessively | |
8068 | * large index value has NEVER been registered. This either | |
8069 | * means our cap on the array size is too small (unlikely), or | |
8070 | * that we have been passed a corrupt index (this must be passed | |
8071 | * the plain index into the interrupt specifier list). | |
8072 | */ | |
8073 | if (source >= reserved->interruptStatisticsArrayCount) { | |
8074 | panic("removeInterruptStatistics called for index %d, which was never registered", source); | |
8075 | } | |
8076 | ||
8077 | assert(reserved->interruptStatisticsArray); | |
8078 | ||
8079 | /* | |
8080 | * If there is no existing entry, we are most likely trying to | |
8081 | * free an interrupt owner twice, or we have corrupted the | |
8082 | * index value. | |
8083 | */ | |
8084 | value = reserved->interruptStatisticsArray[source].statistics; | |
8085 | ||
8086 | if (!value) { | |
8087 | panic("removeInterruptStatistics called for empty index %d", source); | |
8088 | } | |
8089 | ||
8090 | /* | |
8091 | * We update the statistics, so that any delta with the reporter | |
8092 | * state is not lost. | |
8093 | */ | |
8094 | interruptAccountingDataUpdateChannels(reserved->interruptStatisticsArray[source].statistics, reserved->interruptStatisticsArray[source].reporter); | |
8095 | reserved->interruptStatisticsArray[source].statistics = NULL; | |
8096 | IOLockUnlock(reserved->interruptStatisticsLock); | |
8097 | ||
8098 | return kIOReturnSuccess; | |
fe8ab488 A |
8099 | } |
8100 | ||
0a7de745 A |
8101 | IOReturn |
8102 | IOService::getInterruptType(int source, int *interruptType) | |
fe8ab488 | 8103 | { |
0a7de745 A |
8104 | IOInterruptController *interruptController; |
8105 | IOReturn ret; | |
fe8ab488 | 8106 | |
0a7de745 A |
8107 | ret = lookupInterrupt(source, true, &interruptController); |
8108 | if (ret != kIOReturnSuccess) { | |
8109 | return ret; | |
8110 | } | |
fe8ab488 | 8111 | |
0a7de745 A |
8112 | /* Return the type */ |
8113 | return interruptController->getInterruptType(this, source, interruptType); | |
8114 | } | |
fe8ab488 | 8115 | |
0a7de745 A |
8116 | IOReturn |
8117 | IOService::enableInterrupt(int source) | |
8118 | { | |
8119 | IOInterruptController *interruptController; | |
8120 | IOReturn ret; | |
fe8ab488 | 8121 | |
0a7de745 A |
8122 | ret = lookupInterrupt(source, false, &interruptController); |
8123 | if (ret != kIOReturnSuccess) { | |
8124 | return ret; | |
8125 | } | |
8126 | ||
8127 | /* Enable the source */ | |
8128 | return interruptController->enableInterrupt(this, source); | |
8129 | } | |
8130 | ||
8131 | IOReturn | |
8132 | IOService::disableInterrupt(int source) | |
8133 | { | |
8134 | IOInterruptController *interruptController; | |
8135 | IOReturn ret; | |
8136 | ||
8137 | ret = lookupInterrupt(source, false, &interruptController); | |
8138 | if (ret != kIOReturnSuccess) { | |
8139 | return ret; | |
8140 | } | |
8141 | ||
8142 | /* Disable the source */ | |
8143 | return interruptController->disableInterrupt(this, source); | |
8144 | } | |
8145 | ||
8146 | IOReturn | |
8147 | IOService::causeInterrupt(int source) | |
8148 | { | |
8149 | IOInterruptController *interruptController; | |
8150 | IOReturn ret; | |
8151 | ||
8152 | ret = lookupInterrupt(source, false, &interruptController); | |
8153 | if (ret != kIOReturnSuccess) { | |
8154 | return ret; | |
8155 | } | |
8156 | ||
8157 | /* Cause an interrupt for the source */ | |
8158 | return interruptController->causeInterrupt(this, source); | |
8159 | } | |
8160 | ||
8161 | IOReturn | |
8162 | IOService::configureReport(IOReportChannelList *channelList, | |
8163 | IOReportConfigureAction action, | |
8164 | void *result, | |
8165 | void *destination) | |
8166 | { | |
8167 | unsigned cnt; | |
8168 | ||
8169 | for (cnt = 0; cnt < channelList->nchannels; cnt++) { | |
8170 | if (channelList->channels[cnt].channel_id == kPMPowerStatesChID) { | |
8171 | if (pwrMgt) { | |
8172 | configurePowerStatesReport(action, result); | |
8173 | } else { | |
8174 | return kIOReturnUnsupported; | |
8175 | } | |
8176 | } else if (channelList->channels[cnt].channel_id == kPMCurrStateChID) { | |
8177 | if (pwrMgt) { | |
8178 | configureSimplePowerReport(action, result); | |
8179 | } else { | |
8180 | return kIOReturnUnsupported; | |
8181 | } | |
8182 | } | |
8183 | } | |
8184 | ||
8185 | IOLockLock(reserved->interruptStatisticsLock); | |
8186 | ||
8187 | /* The array count is signed (because the interrupt indices are signed), hence the cast */ | |
8188 | for (cnt = 0; cnt < (unsigned) reserved->interruptStatisticsArrayCount; cnt++) { | |
8189 | if (reserved->interruptStatisticsArray[cnt].reporter) { | |
8190 | /* | |
8191 | * If the reporter is currently associated with the statistics | |
8192 | * for an event source, we may need to update the reporter. | |
8193 | */ | |
8194 | if (reserved->interruptStatisticsArray[cnt].statistics) { | |
8195 | interruptAccountingDataUpdateChannels(reserved->interruptStatisticsArray[cnt].statistics, reserved->interruptStatisticsArray[cnt].reporter); | |
8196 | } | |
8197 | ||
8198 | reserved->interruptStatisticsArray[cnt].reporter->configureReport(channelList, action, result, destination); | |
8199 | } | |
8200 | } | |
8201 | ||
8202 | IOLockUnlock(reserved->interruptStatisticsLock); | |
8203 | ||
8204 | return kIOReturnSuccess; | |
8205 | } | |
8206 | ||
8207 | IOReturn | |
8208 | IOService::updateReport(IOReportChannelList *channelList, | |
8209 | IOReportUpdateAction action, | |
8210 | void *result, | |
8211 | void *destination) | |
8212 | { | |
8213 | unsigned cnt; | |
8214 | ||
8215 | for (cnt = 0; cnt < channelList->nchannels; cnt++) { | |
8216 | if (channelList->channels[cnt].channel_id == kPMPowerStatesChID) { | |
8217 | if (pwrMgt) { | |
8218 | updatePowerStatesReport(action, result, destination); | |
8219 | } else { | |
8220 | return kIOReturnUnsupported; | |
8221 | } | |
8222 | } else if (channelList->channels[cnt].channel_id == kPMCurrStateChID) { | |
8223 | if (pwrMgt) { | |
8224 | updateSimplePowerReport(action, result, destination); | |
8225 | } else { | |
8226 | return kIOReturnUnsupported; | |
8227 | } | |
8228 | } | |
8229 | } | |
8230 | ||
8231 | IOLockLock(reserved->interruptStatisticsLock); | |
8232 | ||
8233 | /* The array count is signed (because the interrupt indices are signed), hence the cast */ | |
8234 | for (cnt = 0; cnt < (unsigned) reserved->interruptStatisticsArrayCount; cnt++) { | |
8235 | if (reserved->interruptStatisticsArray[cnt].reporter) { | |
8236 | /* | |
8237 | * If the reporter is currently associated with the statistics | |
8238 | * for an event source, we need to update the reporter. | |
8239 | */ | |
8240 | if (reserved->interruptStatisticsArray[cnt].statistics) { | |
8241 | interruptAccountingDataUpdateChannels(reserved->interruptStatisticsArray[cnt].statistics, reserved->interruptStatisticsArray[cnt].reporter); | |
8242 | } | |
8243 | ||
8244 | reserved->interruptStatisticsArray[cnt].reporter->updateReport(channelList, action, result, destination); | |
8245 | } | |
8246 | } | |
8247 | ||
8248 | IOLockUnlock(reserved->interruptStatisticsLock); | |
8249 | ||
8250 | return kIOReturnSuccess; | |
8251 | } | |
8252 | ||
8253 | uint64_t | |
8254 | IOService::getAuthorizationID( void ) | |
8255 | { | |
8256 | return reserved->authorizationID; | |
8257 | } | |
8258 | ||
8259 | IOReturn | |
8260 | IOService::setAuthorizationID( uint64_t authorizationID ) | |
8261 | { | |
8262 | OSObject * entitlement; | |
8263 | IOReturn status; | |
8264 | ||
8265 | entitlement = IOUserClient::copyClientEntitlement( current_task(), "com.apple.private.iokit.IOServiceSetAuthorizationID" ); | |
8266 | ||
8267 | if (entitlement) { | |
8268 | if (entitlement == kOSBooleanTrue) { | |
8269 | reserved->authorizationID = authorizationID; | |
8270 | ||
8271 | status = kIOReturnSuccess; | |
8272 | } else { | |
8273 | status = kIOReturnNotPrivileged; | |
8274 | } | |
8275 | ||
8276 | entitlement->release(); | |
8277 | } else { | |
8278 | status = kIOReturnNotPrivileged; | |
8279 | } | |
fe8ab488 | 8280 | |
0a7de745 | 8281 | return status; |
fe8ab488 A |
8282 | } |
8283 | ||
cb323159 A |
8284 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ |
8285 | ||
8286 | ||
b0d623f7 | 8287 | #if __LP64__ |
f427ee49 A |
8288 | OSMetaClassDefineReservedUsedX86(IOService, 0); |
8289 | OSMetaClassDefineReservedUsedX86(IOService, 1); | |
b0d623f7 A |
8290 | OSMetaClassDefineReservedUnused(IOService, 2); |
8291 | OSMetaClassDefineReservedUnused(IOService, 3); | |
8292 | OSMetaClassDefineReservedUnused(IOService, 4); | |
8293 | OSMetaClassDefineReservedUnused(IOService, 5); | |
39236c6e A |
8294 | OSMetaClassDefineReservedUnused(IOService, 6); |
8295 | OSMetaClassDefineReservedUnused(IOService, 7); | |
b0d623f7 | 8296 | #else |
f427ee49 A |
8297 | OSMetaClassDefineReservedUsedX86(IOService, 0); |
8298 | OSMetaClassDefineReservedUsedX86(IOService, 1); | |
8299 | OSMetaClassDefineReservedUsedX86(IOService, 2); | |
8300 | OSMetaClassDefineReservedUsedX86(IOService, 3); | |
8301 | OSMetaClassDefineReservedUsedX86(IOService, 4); | |
8302 | OSMetaClassDefineReservedUsedX86(IOService, 5); | |
8303 | OSMetaClassDefineReservedUsedX86(IOService, 6); | |
8304 | OSMetaClassDefineReservedUsedX86(IOService, 7); | |
b0d623f7 | 8305 | #endif |
1c79356b A |
8306 | OSMetaClassDefineReservedUnused(IOService, 8); |
8307 | OSMetaClassDefineReservedUnused(IOService, 9); | |
8308 | OSMetaClassDefineReservedUnused(IOService, 10); | |
8309 | OSMetaClassDefineReservedUnused(IOService, 11); | |
8310 | OSMetaClassDefineReservedUnused(IOService, 12); | |
8311 | OSMetaClassDefineReservedUnused(IOService, 13); | |
8312 | OSMetaClassDefineReservedUnused(IOService, 14); | |
8313 | OSMetaClassDefineReservedUnused(IOService, 15); | |
8314 | OSMetaClassDefineReservedUnused(IOService, 16); | |
8315 | OSMetaClassDefineReservedUnused(IOService, 17); | |
8316 | OSMetaClassDefineReservedUnused(IOService, 18); | |
8317 | OSMetaClassDefineReservedUnused(IOService, 19); | |
8318 | OSMetaClassDefineReservedUnused(IOService, 20); | |
8319 | OSMetaClassDefineReservedUnused(IOService, 21); | |
8320 | OSMetaClassDefineReservedUnused(IOService, 22); | |
8321 | OSMetaClassDefineReservedUnused(IOService, 23); | |
8322 | OSMetaClassDefineReservedUnused(IOService, 24); | |
8323 | OSMetaClassDefineReservedUnused(IOService, 25); | |
8324 | OSMetaClassDefineReservedUnused(IOService, 26); | |
8325 | OSMetaClassDefineReservedUnused(IOService, 27); | |
8326 | OSMetaClassDefineReservedUnused(IOService, 28); | |
8327 | OSMetaClassDefineReservedUnused(IOService, 29); | |
8328 | OSMetaClassDefineReservedUnused(IOService, 30); | |
8329 | OSMetaClassDefineReservedUnused(IOService, 31); | |
8330 | OSMetaClassDefineReservedUnused(IOService, 32); | |
8331 | OSMetaClassDefineReservedUnused(IOService, 33); | |
8332 | OSMetaClassDefineReservedUnused(IOService, 34); | |
8333 | OSMetaClassDefineReservedUnused(IOService, 35); | |
8334 | OSMetaClassDefineReservedUnused(IOService, 36); | |
8335 | OSMetaClassDefineReservedUnused(IOService, 37); | |
8336 | OSMetaClassDefineReservedUnused(IOService, 38); | |
8337 | OSMetaClassDefineReservedUnused(IOService, 39); | |
8338 | OSMetaClassDefineReservedUnused(IOService, 40); | |
8339 | OSMetaClassDefineReservedUnused(IOService, 41); | |
8340 | OSMetaClassDefineReservedUnused(IOService, 42); | |
8341 | OSMetaClassDefineReservedUnused(IOService, 43); | |
8342 | OSMetaClassDefineReservedUnused(IOService, 44); | |
8343 | OSMetaClassDefineReservedUnused(IOService, 45); | |
8344 | OSMetaClassDefineReservedUnused(IOService, 46); | |
8345 | OSMetaClassDefineReservedUnused(IOService, 47); |