]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | * Copyright (c) 2000-2012 Apple Inc. All rights reserved. | |
3 | * | |
4 | * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ | |
5 | * | |
6 | * This file contains Original Code and/or Modifications of Original Code | |
7 | * as defined in and that are subject to the Apple Public Source License | |
8 | * Version 2.0 (the 'License'). You may not use this file except in | |
9 | * compliance with the License. The rights granted to you under the License | |
10 | * may not be used to create, or enable the creation or redistribution of, | |
11 | * unlawful or unlicensed copies of an Apple operating system, or to | |
12 | * circumvent, violate, or enable the circumvention or violation of, any | |
13 | * terms of an Apple operating system software license agreement. | |
14 | * | |
15 | * Please obtain a copy of the License at | |
16 | * http://www.opensource.apple.com/apsl/ and read it before using this file. | |
17 | * | |
18 | * The Original Code and all software distributed under the License are | |
19 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
20 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
21 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
22 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. | |
23 | * Please see the License for the specific language governing rights and | |
24 | * limitations under the License. | |
25 | * | |
26 | * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ | |
27 | */ | |
28 | extern "C" { | |
29 | #include <mach/kmod.h> | |
30 | #include <libkern/kernel_mach_header.h> | |
31 | #include <libkern/prelink.h> | |
32 | #include <libkern/crypto/sha2.h> | |
33 | } | |
34 | ||
35 | #define IOKIT_ENABLE_SHARED_PTR | |
36 | ||
37 | #include <libkern/version.h> | |
38 | #include <libkern/c++/OSContainers.h> | |
39 | #include <libkern/OSKextLibPrivate.h> | |
40 | #include <libkern/c++/OSKext.h> | |
41 | #include <IOKit/IOLib.h> | |
42 | #include <IOKit/IOService.h> | |
43 | #include <IOKit/IODeviceTreeSupport.h> | |
44 | #include <IOKit/IOCatalogue.h> | |
45 | ||
46 | #if __x86_64__ | |
47 | #define KASLR_KEXT_DEBUG 0 | |
48 | #endif | |
49 | ||
50 | #if PRAGMA_MARK | |
51 | #pragma mark Bootstrap Declarations | |
52 | #endif | |
53 | /********************************************************************* | |
54 | * Bootstrap Declarations | |
55 | * | |
56 | * The ENTIRE point of the libsa/KLD segment is to isolate bootstrap | |
57 | * code from other parts of the kernel, so function symbols are not | |
58 | * exported; rather pointers to those functions are exported. | |
59 | * | |
60 | * xxx - need to think about locking for handling the 'weak' refs. | |
61 | * xxx - do export a non-KLD function that says you've called a | |
62 | * xxx - bootstrap function that has been removed. | |
63 | * | |
64 | * ALL call-ins to this segment of the kernel must be done through | |
65 | * exported pointers. The symbols themselves are private and not to | |
66 | * be linked against. | |
67 | *********************************************************************/ | |
68 | extern "C" { | |
69 | extern void (*record_startup_extensions_function)(void); | |
70 | extern void (*load_security_extensions_function)(void); | |
71 | }; | |
72 | ||
73 | static void bootstrapRecordStartupExtensions(void); | |
74 | static void bootstrapLoadSecurityExtensions(void); | |
75 | ||
76 | ||
77 | #if NO_KEXTD | |
78 | extern "C" bool IORamDiskBSDRoot(void); | |
79 | #endif | |
80 | ||
81 | #if PRAGMA_MARK | |
82 | #pragma mark Macros | |
83 | #endif | |
84 | /********************************************************************* | |
85 | * Macros | |
86 | *********************************************************************/ | |
87 | #define CONST_STRLEN(str) (sizeof(str) - 1) | |
88 | ||
89 | #if PRAGMA_MARK | |
90 | #pragma mark Kernel Component Kext Identifiers | |
91 | #endif | |
92 | /********************************************************************* | |
93 | * Kernel Component Kext Identifiers | |
94 | * | |
95 | * We could have each kernel resource kext automatically "load" as | |
96 | * it's created, but it's nicer to have them listed in kextstat in | |
97 | * the order of this list. We'll walk through this after setting up | |
98 | * all the boot kexts and have them load up. | |
99 | *********************************************************************/ | |
100 | static const char * sKernelComponentNames[] = { | |
101 | // The kexts for these IDs must have a version matching 'osrelease'. | |
102 | "com.apple.kernel", | |
103 | "com.apple.kpi.bsd", | |
104 | "com.apple.kpi.dsep", | |
105 | "com.apple.kpi.iokit", | |
106 | "com.apple.kpi.kasan", | |
107 | "com.apple.kpi.libkern", | |
108 | "com.apple.kpi.mach", | |
109 | "com.apple.kpi.private", | |
110 | "com.apple.kpi.unsupported", | |
111 | "com.apple.iokit.IONVRAMFamily", | |
112 | "com.apple.driver.AppleNMI", | |
113 | "com.apple.iokit.IOSystemManagementFamily", | |
114 | "com.apple.iokit.ApplePlatformFamily", | |
115 | NULL | |
116 | }; | |
117 | ||
118 | #if PRAGMA_MARK | |
119 | #pragma mark KLDBootstrap Class | |
120 | #endif | |
121 | /********************************************************************* | |
122 | * KLDBootstrap Class | |
123 | * | |
124 | * We use a C++ class here so that it can be a friend of OSKext and | |
125 | * get at private stuff. We can't hide the class itself, but we can | |
126 | * hide the instance through which we invoke the functions. | |
127 | *********************************************************************/ | |
128 | class KLDBootstrap { | |
129 | friend void bootstrapRecordStartupExtensions(void); | |
130 | friend void bootstrapLoadSecurityExtensions(void); | |
131 | ||
132 | private: | |
133 | void readStartupExtensions(void); | |
134 | ||
135 | void readPrelinkedExtensions(kernel_mach_header_t *mh, kc_kind_t type); | |
136 | void readBooterExtensions(void); | |
137 | ||
138 | OSReturn loadKernelComponentKexts(void); | |
139 | void loadKernelExternalComponents(void); | |
140 | void readBuiltinPersonalities(void); | |
141 | ||
142 | void loadSecurityExtensions(void); | |
143 | ||
144 | public: | |
145 | KLDBootstrap(void); | |
146 | ~KLDBootstrap(void); | |
147 | }; | |
148 | ||
149 | LIBKERN_ALWAYS_DESTROY static KLDBootstrap sBootstrapObject; | |
150 | ||
151 | /********************************************************************* | |
152 | * Set the function pointers for the entry points into the bootstrap | |
153 | * segment upon C++ static constructor invocation. | |
154 | *********************************************************************/ | |
155 | KLDBootstrap::KLDBootstrap(void) | |
156 | { | |
157 | if (this != &sBootstrapObject) { | |
158 | panic("Attempt to access bootstrap segment."); | |
159 | } | |
160 | record_startup_extensions_function = &bootstrapRecordStartupExtensions; | |
161 | load_security_extensions_function = &bootstrapLoadSecurityExtensions; | |
162 | } | |
163 | ||
164 | /********************************************************************* | |
165 | * Clear the function pointers for the entry points into the bootstrap | |
166 | * segment upon C++ static destructor invocation. | |
167 | *********************************************************************/ | |
168 | KLDBootstrap::~KLDBootstrap(void) | |
169 | { | |
170 | if (this != &sBootstrapObject) { | |
171 | panic("Attempt to access bootstrap segment."); | |
172 | } | |
173 | ||
174 | ||
175 | record_startup_extensions_function = NULL; | |
176 | load_security_extensions_function = NULL; | |
177 | } | |
178 | ||
179 | /********************************************************************* | |
180 | *********************************************************************/ | |
181 | void | |
182 | KLDBootstrap::readStartupExtensions(void) | |
183 | { | |
184 | kernel_section_t * prelinkInfoSect = NULL; // do not free | |
185 | ||
186 | OSKextLog(/* kext */ NULL, | |
187 | kOSKextLogProgressLevel | | |
188 | kOSKextLogGeneralFlag | kOSKextLogDirectoryScanFlag | | |
189 | kOSKextLogKextBookkeepingFlag, | |
190 | "Reading startup extensions."); | |
191 | ||
192 | kc_format_t kc_format; | |
193 | kernel_mach_header_t *mh = &_mh_execute_header; | |
194 | if (PE_get_primary_kc_format(&kc_format) && kc_format == KCFormatFileset) { | |
195 | mh = (kernel_mach_header_t *)PE_get_kc_header(KCKindPrimary); | |
196 | } | |
197 | ||
198 | /* If the prelink info segment has a nonzero size, we are prelinked | |
199 | * and won't have any individual kexts or mkexts to read. | |
200 | * Otherwise, we need to read kexts or the mkext from what the booter | |
201 | * has handed us. | |
202 | */ | |
203 | prelinkInfoSect = getsectbynamefromheader(mh, kPrelinkInfoSegment, kPrelinkInfoSection); | |
204 | if (prelinkInfoSect->size) { | |
205 | readPrelinkedExtensions(mh, KCKindPrimary); | |
206 | } else { | |
207 | readBooterExtensions(); | |
208 | } | |
209 | ||
210 | kernel_mach_header_t *akc_mh; | |
211 | akc_mh = (kernel_mach_header_t*)PE_get_kc_header(KCKindAuxiliary); | |
212 | if (akc_mh) { | |
213 | readPrelinkedExtensions(akc_mh, KCKindAuxiliary); | |
214 | } | |
215 | ||
216 | loadKernelComponentKexts(); | |
217 | loadKernelExternalComponents(); | |
218 | readBuiltinPersonalities(); | |
219 | OSKext::sendAllKextPersonalitiesToCatalog(true); | |
220 | ||
221 | return; | |
222 | } | |
223 | ||
224 | /********************************************************************* | |
225 | *********************************************************************/ | |
226 | void | |
227 | KLDBootstrap::readPrelinkedExtensions(kernel_mach_header_t *mh, kc_kind_t type) | |
228 | { | |
229 | bool ret; | |
230 | OSSharedPtr<OSData> loaded_kcUUID; | |
231 | OSSharedPtr<OSString> errorString; | |
232 | OSSharedPtr<OSObject> parsedXML; | |
233 | kernel_section_t *infoPlistSection = NULL; | |
234 | OSDictionary *infoDict = NULL; // do not release | |
235 | ||
236 | OSKextLog(/* kext */ NULL, | |
237 | kOSKextLogProgressLevel | | |
238 | kOSKextLogDirectoryScanFlag | kOSKextLogArchiveFlag, | |
239 | "Starting from prelinked kernel."); | |
240 | ||
241 | /* | |
242 | * The 'infoPlistSection' should contains an XML dictionary that | |
243 | * contains some meta data about the KC, and also describes each kext | |
244 | * included in the kext collection. Unserialize this dictionary and | |
245 | * then iterate over each kext. | |
246 | */ | |
247 | infoPlistSection = getsectbynamefromheader(mh, kPrelinkInfoSegment, kPrelinkInfoSection); | |
248 | parsedXML = OSUnserializeXML((const char *)infoPlistSection->addr, errorString); | |
249 | if (parsedXML) { | |
250 | infoDict = OSDynamicCast(OSDictionary, parsedXML.get()); | |
251 | } | |
252 | ||
253 | if (!infoDict) { | |
254 | const char *errorCString = "(unknown error)"; | |
255 | ||
256 | if (errorString && errorString->getCStringNoCopy()) { | |
257 | errorCString = errorString->getCStringNoCopy(); | |
258 | } else if (parsedXML) { | |
259 | errorCString = "not a dictionary"; | |
260 | } | |
261 | OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogArchiveFlag, | |
262 | "Error unserializing kext info plist section: %s.", errorCString); | |
263 | return; | |
264 | } | |
265 | ||
266 | /* Validate that the Kext Collection is prelinked to the loaded KC */ | |
267 | if (type == KCKindAuxiliary) { | |
268 | if (OSKext::validateKCFileSetUUID(infoDict, KCKindAuxiliary) != 0) { | |
269 | OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogArchiveFlag, | |
270 | "Early boot AuxKC doesn't appear to be linked against the loaded BootKC."); | |
271 | return; | |
272 | } | |
273 | ||
274 | /* | |
275 | * Defer further processing of the AuxKC, but keep the | |
276 | * processed info dictionary around so we can ml_static_free | |
277 | * the segment. | |
278 | */ | |
279 | if (!OSKext::registerDeferredKextCollection(mh, parsedXML, KCKindAuxiliary)) { | |
280 | OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogArchiveFlag, | |
281 | "Error deferring AuxKC kext processing: Kexts in this collection will be unusable."); | |
282 | } | |
283 | goto skip_adding_kexts; | |
284 | } | |
285 | ||
286 | /* | |
287 | * this function does all the heavy lifting of adding OSKext objects | |
288 | * and potentially sliding them if necessary | |
289 | */ | |
290 | ret = OSKext::addKextsFromKextCollection(mh, infoDict, | |
291 | kPrelinkTextSegment, loaded_kcUUID, (mh->filetype == MH_FILESET) ? type : KCKindUnknown); | |
292 | ||
293 | if (!ret) { | |
294 | OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogArchiveFlag, | |
295 | "Error loading kext info from prelinked primary KC"); | |
296 | return; | |
297 | } | |
298 | ||
299 | /* Copy in the kernelcache UUID */ | |
300 | if (!loaded_kcUUID) { | |
301 | OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogArchiveFlag, | |
302 | "WARNING: did not find UUID in %s KC!", (type == KCKindAuxiliary) ? "Aux" : "Primary"); | |
303 | } else if (type != KCKindAuxiliary) { | |
304 | kernelcache_uuid_valid = TRUE; | |
305 | memcpy((void *)&kernelcache_uuid, (const void *)loaded_kcUUID->getBytesNoCopy(), loaded_kcUUID->getLength()); | |
306 | uuid_unparse_upper(kernelcache_uuid, kernelcache_uuid_string); | |
307 | } else { | |
308 | auxkc_uuid_valid = TRUE; | |
309 | memcpy((void *)&auxkc_uuid, (const void *)loaded_kcUUID->getBytesNoCopy(), loaded_kcUUID->getLength()); | |
310 | uuid_unparse_upper(auxkc_uuid, auxkc_uuid_string); | |
311 | } | |
312 | ||
313 | skip_adding_kexts: | |
314 | #if CONFIG_KEXT_BASEMENT | |
315 | if (mh->filetype != MH_FILESET) { | |
316 | /* | |
317 | * On CONFIG_KEXT_BASEMENT systems which do _not_ boot the new | |
318 | * MH_FILESET kext collection, kexts are copied to their own | |
319 | * special VM region during OSKext init time, so we can free | |
320 | * the whole segment now. | |
321 | */ | |
322 | kernel_segment_command_t *prelinkTextSegment = NULL; | |
323 | prelinkTextSegment = getsegbyname(kPrelinkTextSegment); | |
324 | if (!prelinkTextSegment) { | |
325 | OSKextLog(/* kext */ NULL, | |
326 | kOSKextLogErrorLevel | kOSKextLogArchiveFlag, | |
327 | "Can't find prelinked kexts' text segment."); | |
328 | return; | |
329 | } | |
330 | ||
331 | ml_static_mfree((vm_offset_t)prelinkTextSegment->vmaddr, prelinkTextSegment->vmsize); | |
332 | } | |
333 | #endif /* CONFIG_KEXT_BASEMENT */ | |
334 | ||
335 | /* | |
336 | * Free the prelink info segment, we're done with it. | |
337 | */ | |
338 | kernel_segment_command_t *prelinkInfoSegment = NULL; | |
339 | prelinkInfoSegment = getsegbyname(kPrelinkInfoSegment); | |
340 | if (prelinkInfoSegment) { | |
341 | ml_static_mfree((vm_offset_t)prelinkInfoSegment->vmaddr, | |
342 | (vm_size_t)prelinkInfoSegment->vmsize); | |
343 | } | |
344 | ||
345 | return; | |
346 | } | |
347 | ||
348 | ||
349 | /********************************************************************* | |
350 | *********************************************************************/ | |
351 | #define BOOTER_KEXT_PREFIX "Driver-" | |
352 | ||
353 | typedef struct _DeviceTreeBuffer { | |
354 | uint32_t paddr; | |
355 | uint32_t length; | |
356 | } _DeviceTreeBuffer; | |
357 | ||
358 | void | |
359 | KLDBootstrap::readBooterExtensions(void) | |
360 | { | |
361 | OSSharedPtr<IORegistryEntry> booterMemoryMap; | |
362 | OSSharedPtr<OSDictionary> propertyDict; | |
363 | OSSharedPtr<OSCollectionIterator> keyIterator; | |
364 | OSString * deviceTreeName = NULL;// do not release | |
365 | ||
366 | const _DeviceTreeBuffer * deviceTreeBuffer = NULL;// do not free | |
367 | char * booterDataPtr = NULL;// do not free | |
368 | OSSharedPtr<OSData> booterData; | |
369 | OSSharedPtr<OSKext> aKext; | |
370 | ||
371 | OSKextLog(/* kext */ NULL, | |
372 | kOSKextLogProgressLevel | | |
373 | kOSKextLogDirectoryScanFlag | kOSKextLogKextBookkeepingFlag, | |
374 | "Reading startup extensions from booter memory."); | |
375 | ||
376 | booterMemoryMap = IORegistryEntry::fromPath( "/chosen/memory-map", gIODTPlane); | |
377 | ||
378 | if (!booterMemoryMap) { | |
379 | OSKextLog(/* kext */ NULL, | |
380 | kOSKextLogErrorLevel | | |
381 | kOSKextLogGeneralFlag | kOSKextLogDirectoryScanFlag, | |
382 | "Can't read booter memory map."); | |
383 | goto finish; | |
384 | } | |
385 | ||
386 | propertyDict = booterMemoryMap->dictionaryWithProperties(); | |
387 | if (!propertyDict) { | |
388 | OSKextLog(/* kext */ NULL, | |
389 | kOSKextLogErrorLevel | | |
390 | kOSKextLogDirectoryScanFlag, | |
391 | "Can't get property dictionary from memory map."); | |
392 | goto finish; | |
393 | } | |
394 | ||
395 | keyIterator = OSCollectionIterator::withCollection(propertyDict.get()); | |
396 | if (!keyIterator) { | |
397 | OSKextLog(/* kext */ NULL, | |
398 | kOSKextLogErrorLevel | | |
399 | kOSKextLogGeneralFlag, | |
400 | "Can't allocate iterator for driver images."); | |
401 | goto finish; | |
402 | } | |
403 | ||
404 | /* Create dictionary of excluded kexts | |
405 | */ | |
406 | #ifndef CONFIG_EMBEDDED | |
407 | OSKext::createExcludeListFromBooterData(propertyDict.get(), keyIterator.get()); | |
408 | #endif | |
409 | // !! reset the iterator, not the pointer | |
410 | keyIterator->reset(); | |
411 | ||
412 | while ((deviceTreeName = | |
413 | OSDynamicCast(OSString, keyIterator->getNextObject()))) { | |
414 | const char * devTreeNameCString = deviceTreeName->getCStringNoCopy(); | |
415 | OSData * deviceTreeEntry = OSDynamicCast(OSData, | |
416 | propertyDict->getObject(deviceTreeName)); | |
417 | ||
418 | /* If there is no entry for the name, we can't do much with it. */ | |
419 | if (!deviceTreeEntry) { | |
420 | continue; | |
421 | } | |
422 | ||
423 | /* Make sure it is a kext */ | |
424 | if (strncmp(devTreeNameCString, | |
425 | BOOTER_KEXT_PREFIX, | |
426 | CONST_STRLEN(BOOTER_KEXT_PREFIX))) { | |
427 | continue; | |
428 | } | |
429 | ||
430 | deviceTreeBuffer = (const _DeviceTreeBuffer *) | |
431 | deviceTreeEntry->getBytesNoCopy(0, sizeof(deviceTreeBuffer)); | |
432 | if (!deviceTreeBuffer) { | |
433 | /* We can't get to the data, so we can't do anything, | |
434 | * not even free it from physical memory (if it's there). | |
435 | */ | |
436 | OSKextLog(/* kext */ NULL, | |
437 | kOSKextLogErrorLevel | | |
438 | kOSKextLogDirectoryScanFlag, | |
439 | "Device tree entry %s has NULL pointer.", | |
440 | devTreeNameCString); | |
441 | goto finish; // xxx - continue, panic? | |
442 | } | |
443 | ||
444 | booterDataPtr = (char *)ml_static_ptovirt(deviceTreeBuffer->paddr); | |
445 | if (!booterDataPtr) { | |
446 | OSKextLog(/* kext */ NULL, | |
447 | kOSKextLogErrorLevel | | |
448 | kOSKextLogDirectoryScanFlag, | |
449 | "Can't get virtual address for device tree entry %s.", | |
450 | devTreeNameCString); | |
451 | goto finish; | |
452 | } | |
453 | ||
454 | /* Wrap the booter data buffer in an OSData and set a dealloc function | |
455 | * so it will take care of the physical memory when freed. Kexts will | |
456 | * retain the booterData for as long as they need it. Remove the entry | |
457 | * from the booter memory map after this is done. | |
458 | */ | |
459 | booterData = OSData::withBytesNoCopy(booterDataPtr, | |
460 | deviceTreeBuffer->length); | |
461 | if (!booterData) { | |
462 | OSKextLog(/* kext */ NULL, | |
463 | kOSKextLogErrorLevel | | |
464 | kOSKextLogGeneralFlag, | |
465 | "Error - Can't allocate OSData wrapper for device tree entry %s.", | |
466 | devTreeNameCString); | |
467 | goto finish; | |
468 | } | |
469 | booterData->setDeallocFunction(osdata_phys_free); | |
470 | ||
471 | /* Create the kext for the entry, then release it, because the | |
472 | * kext system keeps them around until explicitly removed. | |
473 | * Any creation/registration failures are already logged for us. | |
474 | */ | |
475 | OSSharedPtr<OSKext> newKext = OSKext::withBooterData(deviceTreeName, booterData.get()); | |
476 | ||
477 | booterMemoryMap->removeProperty(deviceTreeName); | |
478 | } /* while ( (deviceTreeName = OSDynamicCast(OSString, ...) ) ) */ | |
479 | ||
480 | finish: | |
481 | return; | |
482 | } | |
483 | ||
484 | /********************************************************************* | |
485 | *********************************************************************/ | |
486 | #define COM_APPLE "com.apple." | |
487 | ||
488 | void | |
489 | KLDBootstrap::loadSecurityExtensions(void) | |
490 | { | |
491 | OSSharedPtr<OSDictionary> extensionsDict; | |
492 | OSSharedPtr<OSCollectionIterator> keyIterator; | |
493 | OSString * bundleID = NULL;// don't release | |
494 | OSKext * theKext = NULL;// don't release | |
495 | ||
496 | OSKextLog(/* kext */ NULL, | |
497 | kOSKextLogStepLevel | | |
498 | kOSKextLogLoadFlag, | |
499 | "Loading security extensions."); | |
500 | ||
501 | extensionsDict = OSKext::copyKexts(); | |
502 | if (!extensionsDict) { | |
503 | return; | |
504 | } | |
505 | ||
506 | keyIterator = OSCollectionIterator::withCollection(extensionsDict.get()); | |
507 | if (!keyIterator) { | |
508 | OSKextLog(/* kext */ NULL, | |
509 | kOSKextLogErrorLevel | | |
510 | kOSKextLogGeneralFlag, | |
511 | "Failed to allocate iterator for security extensions."); | |
512 | goto finish; | |
513 | } | |
514 | ||
515 | while ((bundleID = OSDynamicCast(OSString, keyIterator->getNextObject()))) { | |
516 | const char * bundle_id = bundleID->getCStringNoCopy(); | |
517 | ||
518 | /* Skip extensions whose bundle IDs don't start with "com.apple.". | |
519 | */ | |
520 | if (!bundle_id || | |
521 | (strncmp(bundle_id, COM_APPLE, CONST_STRLEN(COM_APPLE)) != 0)) { | |
522 | continue; | |
523 | } | |
524 | ||
525 | theKext = OSDynamicCast(OSKext, extensionsDict->getObject(bundleID)); | |
526 | if (!theKext) { | |
527 | continue; | |
528 | } | |
529 | ||
530 | if (kOSBooleanTrue == theKext->getPropertyForHostArch(kAppleSecurityExtensionKey)) { | |
531 | OSKextLog(/* kext */ NULL, | |
532 | kOSKextLogStepLevel | | |
533 | kOSKextLogLoadFlag, | |
534 | "Loading security extension %s.", bundleID->getCStringNoCopy()); | |
535 | OSKext::loadKextWithIdentifier(bundleID->getCStringNoCopy(), | |
536 | /* allowDefer */ false); | |
537 | } | |
538 | } | |
539 | ||
540 | finish: | |
541 | return; | |
542 | } | |
543 | ||
544 | /********************************************************************* | |
545 | * We used to require that all listed kernel components load, but | |
546 | * nowadays we can get them from userland so we only try to load the | |
547 | * ones we have. If an error occurs later, such is life. | |
548 | * | |
549 | * Note that we look the kexts up first, so we can avoid spurious | |
550 | * (in this context, anyhow) log messages about kexts not being found. | |
551 | * | |
552 | * xxx - do we even need to do this any more? Check if the kernel | |
553 | * xxx - compoonents just load in the regular paths | |
554 | *********************************************************************/ | |
555 | OSReturn | |
556 | KLDBootstrap::loadKernelComponentKexts(void) | |
557 | { | |
558 | OSReturn result = kOSReturnSuccess;// optimistic | |
559 | OSSharedPtr<OSKext> theKext; | |
560 | const char ** kextIDPtr = NULL; // do not release | |
561 | ||
562 | for (kextIDPtr = &sKernelComponentNames[0]; *kextIDPtr; kextIDPtr++) { | |
563 | theKext = OSKext::lookupKextWithIdentifier(*kextIDPtr); | |
564 | ||
565 | if (theKext) { | |
566 | if (kOSReturnSuccess != OSKext::loadKextWithIdentifier( | |
567 | *kextIDPtr, /* allowDefer */ false)) { | |
568 | // xxx - check KextBookkeeping, might be redundant | |
569 | OSKextLog(/* kext */ NULL, | |
570 | kOSKextLogErrorLevel | | |
571 | kOSKextLogDirectoryScanFlag | kOSKextLogKextBookkeepingFlag, | |
572 | "Failed to initialize kernel component %s.", *kextIDPtr); | |
573 | result = kOSReturnError; | |
574 | } | |
575 | } | |
576 | } | |
577 | ||
578 | return result; | |
579 | } | |
580 | ||
581 | /********************************************************************* | |
582 | * Ensure that Kernel External Components are loaded early in boot, | |
583 | * before other kext personalities get sent to the IOCatalogue. These | |
584 | * kexts are treated specially because they may provide the implementation | |
585 | * for kernel-vended KPI, so they must register themselves before | |
586 | * general purpose IOKit probing begins. | |
587 | *********************************************************************/ | |
588 | ||
589 | #define COM_APPLE_KEC "com.apple.kec." | |
590 | ||
591 | void | |
592 | KLDBootstrap::loadKernelExternalComponents(void) | |
593 | { | |
594 | OSSharedPtr<OSDictionary> extensionsDict; | |
595 | OSSharedPtr<OSCollectionIterator> keyIterator; | |
596 | OSString * bundleID = NULL;// don't release | |
597 | OSKext * theKext = NULL;// don't release | |
598 | OSBoolean * isKernelExternalComponent = NULL;// don't release | |
599 | ||
600 | OSKextLog(/* kext */ NULL, | |
601 | kOSKextLogStepLevel | | |
602 | kOSKextLogLoadFlag, | |
603 | "Loading Kernel External Components."); | |
604 | ||
605 | extensionsDict = OSKext::copyKexts(); | |
606 | if (!extensionsDict) { | |
607 | return; | |
608 | } | |
609 | ||
610 | keyIterator = OSCollectionIterator::withCollection(extensionsDict.get()); | |
611 | if (!keyIterator) { | |
612 | OSKextLog(/* kext */ NULL, | |
613 | kOSKextLogErrorLevel | | |
614 | kOSKextLogGeneralFlag, | |
615 | "Failed to allocate iterator for Kernel External Components."); | |
616 | goto finish; | |
617 | } | |
618 | ||
619 | while ((bundleID = OSDynamicCast(OSString, keyIterator->getNextObject()))) { | |
620 | const char * bundle_id = bundleID->getCStringNoCopy(); | |
621 | ||
622 | /* Skip extensions whose bundle IDs don't start with "com.apple.kec.". | |
623 | */ | |
624 | if (!bundle_id || | |
625 | (strncmp(bundle_id, COM_APPLE_KEC, CONST_STRLEN(COM_APPLE_KEC)) != 0)) { | |
626 | continue; | |
627 | } | |
628 | ||
629 | theKext = OSDynamicCast(OSKext, extensionsDict->getObject(bundleID)); | |
630 | if (!theKext) { | |
631 | continue; | |
632 | } | |
633 | ||
634 | isKernelExternalComponent = OSDynamicCast(OSBoolean, | |
635 | theKext->getPropertyForHostArch(kAppleKernelExternalComponentKey)); | |
636 | if (isKernelExternalComponent && isKernelExternalComponent->isTrue()) { | |
637 | OSKextLog(/* kext */ NULL, | |
638 | kOSKextLogStepLevel | | |
639 | kOSKextLogLoadFlag, | |
640 | "Loading kernel external component %s.", bundleID->getCStringNoCopy()); | |
641 | OSKext::loadKextWithIdentifier(bundleID->getCStringNoCopy(), | |
642 | /* allowDefer */ false); | |
643 | } | |
644 | } | |
645 | ||
646 | finish: | |
647 | return; | |
648 | } | |
649 | ||
650 | /********************************************************************* | |
651 | *********************************************************************/ | |
652 | void | |
653 | KLDBootstrap::readBuiltinPersonalities(void) | |
654 | { | |
655 | OSSharedPtr<OSObject> parsedXML; | |
656 | OSArray * builtinExtensions = NULL;// do not release | |
657 | OSSharedPtr<OSArray> allPersonalities; | |
658 | OSSharedPtr<OSString> errorString; | |
659 | kernel_section_t * infosect = NULL;// do not free | |
660 | OSSharedPtr<OSCollectionIterator> personalitiesIterator; | |
661 | unsigned int count, i; | |
662 | ||
663 | OSKextLog(/* kext */ NULL, | |
664 | kOSKextLogStepLevel | | |
665 | kOSKextLogLoadFlag, | |
666 | "Reading built-in kernel personalities for I/O Kit drivers."); | |
667 | ||
668 | /* Look in the __BUILTIN __info segment for an array of Info.plist | |
669 | * entries. For each one, extract the personalities dictionary, add | |
670 | * it to our array, then push them all (without matching) to | |
671 | * the IOCatalogue. This can be used to augment the personalities | |
672 | * in gIOKernelConfigTables, especially when linking entire kexts into | |
673 | * the mach_kernel image. | |
674 | */ | |
675 | infosect = getsectbyname("__BUILTIN", "__info"); | |
676 | if (!infosect) { | |
677 | // this isn't fatal | |
678 | goto finish; | |
679 | } | |
680 | ||
681 | parsedXML = OSUnserializeXML((const char *) (uintptr_t)infosect->addr, | |
682 | errorString); | |
683 | if (parsedXML) { | |
684 | builtinExtensions = OSDynamicCast(OSArray, parsedXML.get()); | |
685 | } | |
686 | if (!builtinExtensions) { | |
687 | const char * errorCString = "(unknown error)"; | |
688 | ||
689 | if (errorString && errorString->getCStringNoCopy()) { | |
690 | errorCString = errorString->getCStringNoCopy(); | |
691 | } else if (parsedXML) { | |
692 | errorCString = "not an array"; | |
693 | } | |
694 | OSKextLog(/* kext */ NULL, | |
695 | kOSKextLogErrorLevel | | |
696 | kOSKextLogLoadFlag, | |
697 | "Error unserializing built-in personalities: %s.", errorCString); | |
698 | goto finish; | |
699 | } | |
700 | ||
701 | // estimate 3 personalities per Info.plist/kext | |
702 | count = builtinExtensions->getCount(); | |
703 | allPersonalities = OSArray::withCapacity(count * 3); | |
704 | ||
705 | for (i = 0; i < count; i++) { | |
706 | OSDictionary * infoDict = NULL;// do not release | |
707 | OSString * moduleName = NULL;// do not release | |
708 | OSDictionary * personalities;// do not release | |
709 | OSString * personalityName;// do not release | |
710 | ||
711 | infoDict = OSDynamicCast(OSDictionary, | |
712 | builtinExtensions->getObject(i)); | |
713 | if (!infoDict) { | |
714 | continue; | |
715 | } | |
716 | ||
717 | moduleName = OSDynamicCast(OSString, | |
718 | infoDict->getObject(kCFBundleIdentifierKey)); | |
719 | if (!moduleName) { | |
720 | continue; | |
721 | } | |
722 | ||
723 | OSKextLog(/* kext */ NULL, | |
724 | kOSKextLogStepLevel | | |
725 | kOSKextLogLoadFlag, | |
726 | "Adding personalities for built-in driver %s:", | |
727 | moduleName->getCStringNoCopy()); | |
728 | ||
729 | personalities = OSDynamicCast(OSDictionary, | |
730 | infoDict->getObject("IOKitPersonalities")); | |
731 | if (!personalities) { | |
732 | continue; | |
733 | } | |
734 | ||
735 | personalitiesIterator = OSCollectionIterator::withCollection(personalities); | |
736 | if (!personalitiesIterator) { | |
737 | continue; // xxx - well really, what can we do? should we panic? | |
738 | } | |
739 | ||
740 | while ((personalityName = OSDynamicCast(OSString, | |
741 | personalitiesIterator->getNextObject()))) { | |
742 | OSDictionary * personality = OSDynamicCast(OSDictionary, | |
743 | personalities->getObject(personalityName)); | |
744 | ||
745 | OSKextLog(/* kext */ NULL, | |
746 | kOSKextLogDetailLevel | | |
747 | kOSKextLogLoadFlag, | |
748 | "Adding built-in driver personality %s.", | |
749 | personalityName->getCStringNoCopy()); | |
750 | ||
751 | if (personality && !personality->getObject(kCFBundleIdentifierKey)) { | |
752 | personality->setObject(kCFBundleIdentifierKey, moduleName); | |
753 | } | |
754 | allPersonalities->setObject(personality); | |
755 | } | |
756 | } | |
757 | ||
758 | gIOCatalogue->addDrivers(allPersonalities.get(), false); | |
759 | ||
760 | finish: | |
761 | return; | |
762 | } | |
763 | ||
764 | #if PRAGMA_MARK | |
765 | #pragma mark Bootstrap Functions | |
766 | #endif | |
767 | /********************************************************************* | |
768 | * Bootstrap Functions | |
769 | *********************************************************************/ | |
770 | static void | |
771 | bootstrapRecordStartupExtensions(void) | |
772 | { | |
773 | sBootstrapObject.readStartupExtensions(); | |
774 | return; | |
775 | } | |
776 | ||
777 | static void | |
778 | bootstrapLoadSecurityExtensions(void) | |
779 | { | |
780 | sBootstrapObject.loadSecurityExtensions(); | |
781 | return; | |
782 | } | |
783 |