]> git.saurik.com Git - apple/xnu.git/blob - libsa/kext.cpp
xnu-792.10.96.tar.gz
[apple/xnu.git] / libsa / kext.cpp
1 /*
2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
11 *
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
18 * under the License.
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
22 #include <libkern/c++/OSContainers.h>
23 #include <IOKit/IOCatalogue.h>
24 #include <IOKit/IOLib.h>
25 #include <libsa/kext.h>
26 #include <libsa/catalogue.h>
27
28 extern "C" {
29 #include <mach-o/kld.h>
30 #include <libsa/vers_rsrc.h>
31 #include <libsa/stdlib.h>
32 #include <mach/kmod.h>
33 #include <vm/vm_kern.h>
34 #include <mach/kern_return.h>
35 #include <mach-o/fat.h>
36 #include <mach_loader.h>
37
38 #include "kld_patch.h"
39 #include "dgraph.h"
40 #include "load.h"
41 };
42
43
44 extern "C" {
45 extern kern_return_t
46 kmod_create_internal(
47 kmod_info_t *info,
48 kmod_t *id);
49
50 extern kern_return_t
51 kmod_destroy_internal(kmod_t id);
52
53 extern kern_return_t
54 kmod_start_or_stop(
55 kmod_t id,
56 int start,
57 kmod_args_t *data,
58 mach_msg_type_number_t *dataCount);
59
60 extern kern_return_t kmod_retain(kmod_t id);
61 extern kern_return_t kmod_release(kmod_t id);
62
63 extern void flush_dcache(vm_offset_t addr, unsigned cnt, int phys);
64 extern void invalidate_icache(vm_offset_t addr, unsigned cnt, int phys);
65 };
66
67 #define DEBUG
68 #ifdef DEBUG
69 #define LOG_DELAY(x) IODelay((x) * 1000000)
70 #define VTYELLOW "\033[33m"
71 #define VTRESET "\033[0m"
72 #else
73 #define LOG_DELAY(x)
74 #define VTYELLOW
75 #define VTRESET
76 #endif /* DEBUG */
77
78 /*********************************************************************
79 *
80 *********************************************************************/
81 static
82 bool getKext(
83 const char * bundleid,
84 OSDictionary ** plist,
85 unsigned char ** code,
86 unsigned long * code_size,
87 bool * caller_owns_code)
88 {
89 bool result = true;
90 OSDictionary * extensionsDict; // don't release
91 OSDictionary * extDict; // don't release
92 OSDictionary * extPlist; // don't release
93 unsigned long code_size_local;
94
95 /* Get the dictionary of startup extensions.
96 * This is keyed by module name.
97 */
98 extensionsDict = getStartupExtensions();
99 if (!extensionsDict) {
100 IOLog("startup extensions dictionary is missing\n");
101 result = false;
102 goto finish;
103 }
104
105 /* Get the requested extension's dictionary entry and its property
106 * list, containing module dependencies.
107 */
108 extDict = OSDynamicCast(OSDictionary,
109 extensionsDict->getObject(bundleid));
110
111 if (!extDict) {
112 IOLog("extension \"%s\" cannot be found\n",
113 bundleid);
114 result = false;
115 goto finish;
116 }
117
118 if (plist) {
119 extPlist = OSDynamicCast(OSDictionary, extDict->getObject("plist"));
120 if (!extPlist) {
121 IOLog("extension \"%s\" has no info dictionary\n",
122 bundleid);
123 result = false;
124 goto finish;
125 }
126 *plist = extPlist;
127 }
128
129 if (code) {
130
131 /* If asking for code, the caller must provide a return buffer
132 * for ownership!
133 */
134 if (!caller_owns_code) {
135 IOLog("getKext(): invalid usage (caller_owns_code not provided)\n");
136 result = false;
137 goto finish;
138 }
139
140 *code = 0;
141 if (code_size) {
142 *code_size = 0;
143 }
144 *caller_owns_code = false;
145
146 *code = (unsigned char *)kld_file_getaddr(bundleid,
147 (long *)&code_size_local);
148 if (*code) {
149 if (code_size) {
150 *code_size = code_size_local;
151 }
152 } else {
153 OSData * driverCode = 0; // release only if uncompressing!
154
155 driverCode = OSDynamicCast(OSData, extDict->getObject("code"));
156 if (driverCode) {
157 *code = (unsigned char *)driverCode->getBytesNoCopy();
158 if (code_size) {
159 *code_size = driverCode->getLength();
160 }
161 } else { // Look for compressed code and uncompress it
162 OSData * compressedCode = 0;
163 compressedCode = OSDynamicCast(OSData,
164 extDict->getObject("compressedCode"));
165 if (compressedCode) {
166 if (!uncompressModule(compressedCode, &driverCode)) {
167 IOLog("extension \"%s\": couldn't uncompress code\n",
168 bundleid);
169 result = false;
170 goto finish;
171 }
172 *caller_owns_code = true;
173 *code = (unsigned char *)driverCode->getBytesNoCopy();
174 if (code_size) {
175 *code_size = driverCode->getLength();
176 }
177 driverCode->release();
178 }
179 }
180 }
181 }
182
183 finish:
184
185 return result;
186 }
187
188
189 /*********************************************************************
190 *
191 *********************************************************************/
192 static
193 bool verifyCompatibility(OSString * extName, OSString * requiredVersion)
194 {
195 OSDictionary * extPlist; // don't release
196 OSString * extVersion; // don't release
197 OSString * extCompatVersion; // don't release
198 VERS_version ext_version;
199 VERS_version ext_compat_version;
200 VERS_version required_version;
201
202 if (!getKext(extName->getCStringNoCopy(), &extPlist, NULL, NULL, NULL)) {
203 return false;
204 }
205
206 extVersion = OSDynamicCast(OSString,
207 extPlist->getObject("CFBundleVersion"));
208 if (!extVersion) {
209 IOLog("verifyCompatibility(): "
210 "Extension \"%s\" has no \"CFBundleVersion\" property.\n",
211 extName->getCStringNoCopy());
212 return false;
213 }
214
215 extCompatVersion = OSDynamicCast(OSString,
216 extPlist->getObject("OSBundleCompatibleVersion"));
217 if (!extCompatVersion) {
218 IOLog("verifyCompatibility(): "
219 "Extension \"%s\" has no \"OSBundleCompatibleVersion\" property.\n",
220 extName->getCStringNoCopy());
221 return false;
222 }
223
224 required_version = VERS_parse_string(requiredVersion->getCStringNoCopy());
225 if (required_version < 0) {
226 IOLog("verifyCompatibility(): "
227 "Can't parse required version \"%s\" of dependency %s.\n",
228 requiredVersion->getCStringNoCopy(),
229 extName->getCStringNoCopy());
230 return false;
231 }
232 ext_version = VERS_parse_string(extVersion->getCStringNoCopy());
233 if (ext_version < 0) {
234 IOLog("verifyCompatibility(): "
235 "Can't parse version \"%s\" of dependency %s.\n",
236 extVersion->getCStringNoCopy(),
237 extName->getCStringNoCopy());
238 return false;
239 }
240 ext_compat_version = VERS_parse_string(extCompatVersion->getCStringNoCopy());
241 if (ext_compat_version < 0) {
242 IOLog("verifyCompatibility(): "
243 "Can't parse compatible version \"%s\" of dependency %s.\n",
244 extCompatVersion->getCStringNoCopy(),
245 extName->getCStringNoCopy());
246 return false;
247 }
248
249 if (required_version > ext_version || required_version < ext_compat_version) {
250 return false;
251 }
252
253 return true;
254 }
255
256 /*********************************************************************
257 *********************************************************************/
258 static
259 bool kextIsDependency(const char * kext_name, char * is_kernel) {
260 bool result = true;
261 OSDictionary * extensionsDict = 0; // don't release
262 OSDictionary * extDict = 0; // don't release
263 OSDictionary * extPlist = 0; // don't release
264 OSBoolean * isKernelResourceObj = 0; // don't release
265 OSData * driverCode = 0; // don't release
266 OSData * compressedCode = 0; // don't release
267
268 if (is_kernel) {
269 *is_kernel = false;
270 }
271
272 /* Get the dictionary of startup extensions.
273 * This is keyed by module name.
274 */
275 extensionsDict = getStartupExtensions();
276 if (!extensionsDict) {
277 IOLog("startup extensions dictionary is missing\n");
278 result = false;
279 goto finish;
280 }
281
282 /* Get the requested extension's dictionary entry and its property
283 * list, containing module dependencies.
284 */
285 extDict = OSDynamicCast(OSDictionary,
286 extensionsDict->getObject(kext_name));
287
288 if (!extDict) {
289 IOLog("extension \"%s\" cannot be found\n",
290 kext_name);
291 result = false;
292 goto finish;
293 }
294
295 extPlist = OSDynamicCast(OSDictionary, extDict->getObject("plist"));
296 if (!extPlist) {
297 IOLog("extension \"%s\" has no info dictionary\n",
298 kext_name);
299 result = false;
300 goto finish;
301 }
302
303 /* A kext that is a kernel component is still a dependency, as there
304 * are fake kmod entries for them.
305 */
306 isKernelResourceObj = OSDynamicCast(OSBoolean,
307 extPlist->getObject("OSKernelResource"));
308 if (isKernelResourceObj && isKernelResourceObj->isTrue()) {
309 if (is_kernel) {
310 *is_kernel = true;
311 }
312 }
313
314 driverCode = OSDynamicCast(OSData, extDict->getObject("code"));
315 compressedCode = OSDynamicCast(OSData,
316 extDict->getObject("compressedCode"));
317
318 if ((driverCode || compressedCode) && is_kernel && *is_kernel) {
319 *is_kernel = 2;
320 }
321
322 if (!driverCode && !compressedCode && !isKernelResourceObj) {
323 result = false;
324 goto finish;
325 }
326
327 finish:
328
329 return result;
330 }
331
332 /*********************************************************************
333 *********************************************************************/
334 static bool
335 addDependenciesForKext(OSDictionary * kextPlist,
336 OSArray * dependencyList,
337 OSString * trueParent,
338 Boolean skipKernelDependencies)
339 {
340 bool result = true;
341 bool hasDirectKernelDependency = false;
342 OSString * kextName = 0; // don't release
343 OSDictionary * libraries = 0; // don't release
344 OSCollectionIterator * keyIterator = 0; // must release
345 OSString * libraryName = 0; // don't release
346 OSString * dependentName = 0; // don't release
347
348 kextName = OSDynamicCast(OSString,
349 kextPlist->getObject("CFBundleIdentifier"));
350 if (!kextName) {
351 // XXX: Add log message
352 result = false;
353 goto finish;
354 }
355
356 libraries = OSDynamicCast(OSDictionary,
357 kextPlist->getObject("OSBundleLibraries"));
358 if (!libraries) {
359 result = true;
360 goto finish;
361 }
362
363 keyIterator = OSCollectionIterator::withCollection(libraries);
364 if (!keyIterator) {
365 // XXX: Add log message
366 result = false;
367 goto finish;
368 }
369
370 dependentName = trueParent ? trueParent : kextName;
371
372 while ( (libraryName = OSDynamicCast(OSString,
373 keyIterator->getNextObject())) ) {
374
375 OSString * libraryVersion = OSDynamicCast(OSString,
376 libraries->getObject(libraryName));
377 if (!libraryVersion) {
378 // XXX: Add log message
379 result = false;
380 goto finish;
381 }
382 if (!verifyCompatibility(libraryName, libraryVersion)) {
383 result = false;
384 goto finish;
385 } else {
386 char is_kernel_component;
387
388 if (!kextIsDependency(libraryName->getCStringNoCopy(),
389 &is_kernel_component)) {
390
391 is_kernel_component = false;
392 }
393
394 if (!skipKernelDependencies || !is_kernel_component) {
395 dependencyList->setObject(dependentName);
396 dependencyList->setObject(libraryName);
397 }
398 if (!hasDirectKernelDependency && is_kernel_component) {
399 hasDirectKernelDependency = true;
400 }
401 }
402 }
403 if (!hasDirectKernelDependency) {
404 const OSSymbol * kernelName = 0;
405
406 /* a kext without any kernel dependency is assumed dependent on 6.0 */
407 dependencyList->setObject(dependentName);
408
409 kernelName = OSSymbol::withCString("com.apple.kernel.libkern");
410 if (!kernelName) {
411 // XXX: Add log message
412 result = false;
413 goto finish;
414 }
415 dependencyList->setObject(kernelName);
416 kernelName->release();
417
418 IOLog("Extension \"%s\" has no kernel dependency.\n",
419 kextName->getCStringNoCopy());
420 }
421
422 finish:
423 if (keyIterator) keyIterator->release();
424 return result;
425 }
426
427 /*********************************************************************
428 *********************************************************************/
429 static
430 bool getVersionForKext(OSDictionary * kextPlist, char ** version)
431 {
432 OSString * kextName = 0; // don't release
433 OSString * kextVersion; // don't release
434
435 kextName = OSDynamicCast(OSString,
436 kextPlist->getObject("CFBundleIdentifier"));
437 if (!kextName) {
438 // XXX: Add log message
439 return false;
440 }
441
442 kextVersion = OSDynamicCast(OSString,
443 kextPlist->getObject("CFBundleVersion"));
444 if (!kextVersion) {
445 IOLog("getVersionForKext(): "
446 "Extension \"%s\" has no \"CFBundleVersion\" property.\n",
447 kextName->getCStringNoCopy());
448 return false;
449 }
450
451 if (version) {
452 *version = (char *)kextVersion->getCStringNoCopy();
453 }
454
455 return true;
456 }
457
458 /*********************************************************************
459 *********************************************************************/
460 static
461 bool add_dependencies_for_kmod(const char * kmod_name, dgraph_t * dgraph)
462 {
463 bool result = true;
464 OSDictionary * kextPlist = 0; // don't release
465 unsigned int index = 0;
466 OSArray * dependencyList = 0; // must release
467 unsigned char * code = 0;
468 unsigned long code_length = 0;
469 bool code_is_kmem = false;
470 char * kmod_vers = 0; // from plist, don't free
471 char is_kernel_component = false;
472 dgraph_entry_t * dgraph_entry = 0; // don't free
473 dgraph_entry_t * dgraph_dependency = 0; // don't free
474 bool kext_is_dependency = true;
475
476 /*****
477 * Set up the root kmod.
478 */
479 if (!getKext(kmod_name, &kextPlist, &code, &code_length,
480 &code_is_kmem)) {
481 IOLog("can't find extension %s\n", kmod_name);
482 result = false;
483 goto finish;
484 }
485
486 if (!kextIsDependency(kmod_name, &is_kernel_component)) {
487 IOLog("extension %s is not loadable\n", kmod_name);
488 result = false;
489 goto finish;
490 }
491
492 if (!getVersionForKext(kextPlist, &kmod_vers)) {
493 IOLog("can't get version for extension %s\n", kmod_name);
494 result = false;
495 goto finish;
496 }
497
498 dgraph_entry = dgraph_add_dependent(dgraph, kmod_name,
499 code, code_length, code_is_kmem,
500 kmod_name, kmod_vers,
501 0 /* load_address not yet known */, is_kernel_component);
502 if (!dgraph_entry) {
503 IOLog("can't record %s in dependency graph\n", kmod_name);
504 result = false;
505 // kmem_alloc()ed code is freed in finish: block.
506 goto finish;
507 }
508
509 // pass ownership of code to kld patcher
510 if (code) {
511 if (kload_map_entry(dgraph_entry) != kload_error_none) {
512 IOLog("can't map %s in preparation for loading\n", kmod_name);
513 result = false;
514 // kmem_alloc()ed code is freed in finish: block.
515 goto finish;
516 }
517 }
518 // clear local record of code
519 code = 0;
520 code_length = 0;
521 code_is_kmem = false;
522
523 /*****
524 * Now handle all the dependencies.
525 */
526 dependencyList = OSArray::withCapacity(5);
527 if (!dependencyList) {
528 IOLog("memory allocation failure\n");
529 result = false;
530 goto finish;
531 }
532
533 index = 0;
534 if (!addDependenciesForKext(kextPlist, dependencyList, NULL, false)) {
535 IOLog("can't determine immediate dependencies for extension %s\n",
536 kmod_name);
537 result = false;
538 goto finish;
539 }
540
541 /* IMPORTANT: loop condition gets list count every time through, as the
542 * array CAN change each iteration.
543 */
544 for (index = 0; index < dependencyList->getCount(); index += 2) {
545 OSString * dependentName = 0;
546 OSString * libraryName = 0;
547 const char * dependent_name = 0;
548 const char * library_name = 0;
549
550 /* 255 is an arbitrary limit. Multiplied by 2 because the dependency
551 * list is stocked with pairs (dependent -> dependency).
552 */
553 if (index > (2 * 255)) {
554 IOLog("extension dependency graph ridiculously long, indicating a loop\n");
555 result = false;
556 goto finish;
557 }
558
559 dependentName = OSDynamicCast(OSString,
560 dependencyList->getObject(index));
561 libraryName = OSDynamicCast(OSString,
562 dependencyList->getObject(index + 1));
563
564 if (!dependentName || !libraryName) {
565 IOLog("malformed dependency list\n");
566 result = false;
567 goto finish;
568 }
569
570 dependent_name = dependentName->getCStringNoCopy();
571 library_name = libraryName->getCStringNoCopy();
572
573 if (!getKext(library_name, &kextPlist, NULL, NULL, NULL)) {
574
575 IOLog("can't find extension %s\n", library_name);
576 result = false;
577 goto finish;
578 }
579
580 OSString * string = OSDynamicCast(OSString,
581 kextPlist->getObject("OSBundleSharedExecutableIdentifier"));
582 if (string) {
583 library_name = string->getCStringNoCopy();
584 if (!getKext(library_name, &kextPlist, NULL, NULL, NULL)) {
585 IOLog("can't find extension %s\n", library_name);
586 result = false;
587 goto finish;
588 }
589 }
590
591 kext_is_dependency = kextIsDependency(library_name,
592 &is_kernel_component);
593
594 if (kext_is_dependency) {
595 dgraph_entry = dgraph_find_dependent(dgraph, dependent_name);
596 if (!dgraph_entry) {
597 IOLog("internal error with dependency graph\n");
598 LOG_DELAY(1);
599 result = false;
600 goto finish;
601 }
602
603 if (!getVersionForKext(kextPlist, &kmod_vers)) {
604 IOLog("can't get version for extension %s\n", library_name);
605 result = false;
606 goto finish;
607 }
608
609 /* It's okay for code to be zero, as for a pseudokext
610 * representing a kernel component.
611 */
612 if (!getKext(library_name, NULL /* already got it */,
613 &code, &code_length, &code_is_kmem)) {
614 IOLog("can't find extension %s\n", library_name);
615 result = false;
616 goto finish;
617 }
618
619 dgraph_dependency = dgraph_add_dependency(dgraph, dgraph_entry,
620 library_name, code, code_length, code_is_kmem,
621 library_name, kmod_vers,
622 0 /* load_address not yet known */, is_kernel_component);
623
624 if (!dgraph_dependency) {
625 IOLog("can't record dependency %s -> %s\n", dependent_name,
626 library_name);
627 result = false;
628 // kmem_alloc()ed code is freed in finish: block.
629 goto finish;
630 }
631
632 // pass ownership of code to kld patcher
633 if (code) {
634 if (kload_map_entry(dgraph_dependency) != kload_error_none) {
635 IOLog("can't map %s in preparation for loading\n", library_name);
636 result = false;
637 // kmem_alloc()ed code is freed in finish: block.
638 goto finish;
639 }
640 }
641 // clear local record of code
642 code = 0;
643 code_length = 0;
644 code_is_kmem = false;
645 }
646
647 /* Now put the library's dependencies onto the pending set.
648 */
649 if (!addDependenciesForKext(kextPlist, dependencyList,
650 kext_is_dependency ? NULL : dependentName, !kext_is_dependency)) {
651
652 IOLog("can't determine immediate dependencies for extension %s\n",
653 library_name);
654 result = false;
655 goto finish;
656 }
657 }
658
659 finish:
660 if (code && code_is_kmem) {
661 kmem_free(kernel_map, (unsigned int)code, code_length);
662 }
663 if (dependencyList) dependencyList->release();
664
665 return result;
666 }
667
668 /*********************************************************************
669 * This is the function that IOCatalogue calls in order to load a kmod.
670 * It first checks whether the kmod is already loaded. If the kmod
671 * isn't loaded, this function builds a dependency list and calls
672 * load_kmod() repeatedly to guarantee that each dependency is in fact
673 * loaded.
674 *********************************************************************/
675 __private_extern__
676 kern_return_t load_kernel_extension(char * kmod_name)
677 {
678 kern_return_t result = KERN_SUCCESS;
679 kload_error load_result = kload_error_none;
680 dgraph_t dgraph;
681 bool free_dgraph = false;
682 kmod_info_t * kmod_info;
683
684 // Put this in for lots of messages about kext loading.
685 #if 0
686 kload_set_log_level(kload_log_level_load_details);
687 #endif
688
689 /* See if the kmod is already loaded.
690 */
691 if ((kmod_info = kmod_lookupbyname_locked(kmod_name))) {
692 kfree(kmod_info, sizeof(kmod_info_t));
693 return KERN_SUCCESS;
694 }
695
696 if (dgraph_init(&dgraph) != dgraph_valid) {
697 IOLog("Can't initialize dependency graph to load %s.\n",
698 kmod_name);
699 result = KERN_FAILURE;
700 goto finish;
701 }
702
703 free_dgraph = true;
704 if (!add_dependencies_for_kmod(kmod_name, &dgraph)) {
705 IOLog("Can't determine dependencies for %s.\n",
706 kmod_name);
707 result = KERN_FAILURE;
708 goto finish;
709 }
710
711 dgraph.root = dgraph_find_root(&dgraph);
712
713 if (!dgraph.root) {
714 IOLog("Dependency graph to load %s has no root.\n",
715 kmod_name);
716 result = KERN_FAILURE;
717 goto finish;
718 }
719
720 /* A kernel component is built in and need not be loaded.
721 */
722 if (dgraph.root->is_kernel_component) {
723 result = KERN_SUCCESS;
724 goto finish;
725 }
726
727 dgraph_establish_load_order(&dgraph);
728
729 load_result = kload_load_dgraph(&dgraph);
730 if (load_result != kload_error_none &&
731 load_result != kload_error_already_loaded) {
732
733 IOLog(VTYELLOW "Failed to load extension %s.\n" VTRESET, kmod_name);
734
735 result = KERN_FAILURE;
736 goto finish;
737 }
738
739 finish:
740
741 if (free_dgraph) {
742 dgraph_free(&dgraph, 0 /* don't free dgraph itself */);
743 }
744 return result;
745 }