]> git.saurik.com Git - apple/xnu.git/blob - libsa/kext.cpp
xnu-792.6.70.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 LOG_DELAY(1);
170 result = false;
171 goto finish;
172 }
173 *caller_owns_code = true;
174 *code = (unsigned char *)driverCode->getBytesNoCopy();
175 if (code_size) {
176 *code_size = driverCode->getLength();
177 }
178 driverCode->release();
179 }
180 }
181 }
182 }
183
184 finish:
185
186 return result;
187 }
188
189
190 /*********************************************************************
191 *
192 *********************************************************************/
193 static
194 bool verifyCompatibility(OSString * extName, OSString * requiredVersion)
195 {
196 OSDictionary * extPlist; // don't release
197 OSString * extVersion; // don't release
198 OSString * extCompatVersion; // don't release
199 VERS_version ext_version;
200 VERS_version ext_compat_version;
201 VERS_version required_version;
202
203 if (!getKext(extName->getCStringNoCopy(), &extPlist, NULL, NULL, NULL)) {
204 return false;
205 }
206
207 extVersion = OSDynamicCast(OSString,
208 extPlist->getObject("CFBundleVersion"));
209 if (!extVersion) {
210 IOLog("verifyCompatibility(): "
211 "Extension \"%s\" has no \"CFBundleVersion\" property.\n",
212 extName->getCStringNoCopy());
213 return false;
214 }
215
216 extCompatVersion = OSDynamicCast(OSString,
217 extPlist->getObject("OSBundleCompatibleVersion"));
218 if (!extCompatVersion) {
219 IOLog("verifyCompatibility(): "
220 "Extension \"%s\" has no \"OSBundleCompatibleVersion\" property.\n",
221 extName->getCStringNoCopy());
222 return false;
223 }
224
225 required_version = VERS_parse_string(requiredVersion->getCStringNoCopy());
226 if (required_version < 0) {
227 IOLog("verifyCompatibility(): "
228 "Can't parse required version \"%s\" of dependency %s.\n",
229 requiredVersion->getCStringNoCopy(),
230 extName->getCStringNoCopy());
231 return false;
232 }
233 ext_version = VERS_parse_string(extVersion->getCStringNoCopy());
234 if (ext_version < 0) {
235 IOLog("verifyCompatibility(): "
236 "Can't parse version \"%s\" of dependency %s.\n",
237 extVersion->getCStringNoCopy(),
238 extName->getCStringNoCopy());
239 return false;
240 }
241 ext_compat_version = VERS_parse_string(extCompatVersion->getCStringNoCopy());
242 if (ext_compat_version < 0) {
243 IOLog("verifyCompatibility(): "
244 "Can't parse compatible version \"%s\" of dependency %s.\n",
245 extCompatVersion->getCStringNoCopy(),
246 extName->getCStringNoCopy());
247 return false;
248 }
249
250 if (required_version > ext_version || required_version < ext_compat_version) {
251 return false;
252 }
253
254 return true;
255 }
256
257 /*********************************************************************
258 *********************************************************************/
259 static
260 bool kextIsDependency(const char * kext_name, char * is_kernel) {
261 bool result = true;
262 OSDictionary * extensionsDict = 0; // don't release
263 OSDictionary * extDict = 0; // don't release
264 OSDictionary * extPlist = 0; // don't release
265 OSBoolean * isKernelResourceObj = 0; // don't release
266 OSData * driverCode = 0; // don't release
267 OSData * compressedCode = 0; // don't release
268
269 if (is_kernel) {
270 *is_kernel = false;
271 }
272
273 /* Get the dictionary of startup extensions.
274 * This is keyed by module name.
275 */
276 extensionsDict = getStartupExtensions();
277 if (!extensionsDict) {
278 IOLog("startup extensions dictionary is missing\n");
279 result = false;
280 goto finish;
281 }
282
283 /* Get the requested extension's dictionary entry and its property
284 * list, containing module dependencies.
285 */
286 extDict = OSDynamicCast(OSDictionary,
287 extensionsDict->getObject(kext_name));
288
289 if (!extDict) {
290 IOLog("extension \"%s\" cannot be found\n",
291 kext_name);
292 result = false;
293 goto finish;
294 }
295
296 extPlist = OSDynamicCast(OSDictionary, extDict->getObject("plist"));
297 if (!extPlist) {
298 IOLog("extension \"%s\" has no info dictionary\n",
299 kext_name);
300 result = false;
301 goto finish;
302 }
303
304 /* A kext that is a kernel component is still a dependency, as there
305 * are fake kmod entries for them.
306 */
307 isKernelResourceObj = OSDynamicCast(OSBoolean,
308 extPlist->getObject("OSKernelResource"));
309 if (isKernelResourceObj && isKernelResourceObj->isTrue()) {
310 if (is_kernel) {
311 *is_kernel = true;
312 }
313 }
314
315 driverCode = OSDynamicCast(OSData, extDict->getObject("code"));
316 compressedCode = OSDynamicCast(OSData,
317 extDict->getObject("compressedCode"));
318
319 if ((driverCode || compressedCode) && is_kernel && *is_kernel) {
320 *is_kernel = 2;
321 }
322
323 if (!driverCode && !compressedCode && !isKernelResourceObj) {
324 result = false;
325 goto finish;
326 }
327
328 finish:
329
330 return result;
331 }
332
333 /*********************************************************************
334 *********************************************************************/
335 static bool
336 figureDependenciesForKext(OSDictionary * kextPlist,
337 OSDictionary * dependencies,
338 OSString * trueParent,
339 Boolean skipKernelDependencies)
340 {
341 bool result = true;
342 bool hasDirectKernelDependency = false;
343 OSString * kextName = 0; // don't release
344 OSDictionary * libraries = 0; // don't release
345 OSCollectionIterator * keyIterator = 0; // must release
346 OSString * libraryName = 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 while ( (libraryName = OSDynamicCast(OSString,
371 keyIterator->getNextObject())) ) {
372
373 OSString * libraryVersion = OSDynamicCast(OSString,
374 libraries->getObject(libraryName));
375 if (!libraryVersion) {
376 // XXX: Add log message
377 result = false;
378 goto finish;
379 }
380 if (!verifyCompatibility(libraryName, libraryVersion)) {
381 result = false;
382 goto finish;
383 } else {
384 char is_kernel_component;
385
386 if (!kextIsDependency(libraryName->getCStringNoCopy(), &is_kernel_component))
387 is_kernel_component = false;
388
389 if (!skipKernelDependencies || !is_kernel_component) {
390 dependencies->setObject(libraryName,
391 trueParent ? trueParent : kextName);
392 }
393 if (!hasDirectKernelDependency && is_kernel_component) {
394 hasDirectKernelDependency = true;
395 }
396 }
397 }
398 if (!hasDirectKernelDependency) {
399 /* a kext without any kernel dependency is assumed dependent on 6.0 */
400 dependencies->setObject("com.apple.kernel.libkern",
401 trueParent ? trueParent : kextName);
402 IOLog("Extension \"%s\" has no kernel dependency.\n",
403 kextName->getCStringNoCopy());
404 }
405
406 finish:
407 if (keyIterator) keyIterator->release();
408 return result;
409 }
410
411 /*********************************************************************
412 *********************************************************************/
413 static
414 bool getVersionForKext(OSDictionary * kextPlist, char ** version)
415 {
416 OSString * kextName = 0; // don't release
417 OSString * kextVersion; // don't release
418
419 kextName = OSDynamicCast(OSString,
420 kextPlist->getObject("CFBundleIdentifier"));
421 if (!kextName) {
422 // XXX: Add log message
423 return false;
424 }
425
426 kextVersion = OSDynamicCast(OSString,
427 kextPlist->getObject("CFBundleVersion"));
428 if (!kextVersion) {
429 IOLog("getVersionForKext(): "
430 "Extension \"%s\" has no \"CFBundleVersion\" property.\n",
431 kextName->getCStringNoCopy());
432 return false;
433 }
434
435 if (version) {
436 *version = (char *)kextVersion->getCStringNoCopy();
437 }
438
439 return true;
440 }
441
442 /*********************************************************************
443 *********************************************************************/
444 static
445 bool add_dependencies_for_kmod(const char * kmod_name, dgraph_t * dgraph)
446 {
447 bool result = true;
448 OSDictionary * kextPlist = 0; // don't release
449 OSDictionary * workingDependencies = 0; // must release
450 OSDictionary * pendingDependencies = 0; // must release
451 OSDictionary * swapDict = 0; // don't release
452 OSString * dependentName = 0; // don't release
453 const char * dependent_name = 0; // don't free
454 OSString * libraryName = 0; // don't release
455 const char * library_name = 0; // don't free
456 OSCollectionIterator * dependencyIterator = 0; // must release
457 unsigned char * code = 0;
458 unsigned long code_length = 0;
459 bool code_is_kmem = false;
460 char * kmod_vers = 0; // from plist, don't free
461 char is_kernel_component = false;
462 dgraph_entry_t * dgraph_entry = 0; // don't free
463 dgraph_entry_t * dgraph_dependency = 0; // don't free
464 unsigned int graph_depth = 0;
465 bool kext_is_dependency = true;
466
467 if (!getKext(kmod_name, &kextPlist, &code, &code_length,
468 &code_is_kmem)) {
469 IOLog("can't find extension %s\n", kmod_name);
470 result = false;
471 goto finish;
472 }
473
474 if (!kextIsDependency(kmod_name, &is_kernel_component)) {
475 IOLog("extension %s is not loadable\n", kmod_name);
476 result = false;
477 goto finish;
478 }
479
480 if (!getVersionForKext(kextPlist, &kmod_vers)) {
481 IOLog("can't get version for extension %s\n", kmod_name);
482 result = false;
483 goto finish;
484 }
485
486 dgraph_entry = dgraph_add_dependent(dgraph, kmod_name,
487 code, code_length, code_is_kmem,
488 kmod_name, kmod_vers,
489 0 /* load_address not yet known */, is_kernel_component);
490 if (!dgraph_entry) {
491 IOLog("can't record %s in dependency graph\n", kmod_name);
492 result = false;
493 // kmem_alloc()ed code is freed in finish: block.
494 goto finish;
495 }
496
497 // pass ownership of code to kld patcher
498 if (code)
499 {
500 if (kload_map_entry(dgraph_entry) != kload_error_none) {
501 IOLog("can't map %s in preparation for loading\n", kmod_name);
502 result = false;
503 // kmem_alloc()ed code is freed in finish: block.
504 goto finish;
505 }
506 }
507 // clear local record of code
508 code = 0;
509 code_length = 0;
510 code_is_kmem = false;
511
512 workingDependencies = OSDictionary::withCapacity(5);
513 if (!workingDependencies) {
514 IOLog("memory allocation failure\n");
515 result = false;
516 goto finish;
517 }
518
519 pendingDependencies = OSDictionary::withCapacity(5);
520 if (!pendingDependencies) {
521 IOLog("memory allocation failure\n");
522 result = false;
523 goto finish;
524 }
525
526 if (!figureDependenciesForKext(kextPlist, workingDependencies, NULL, false)) {
527 IOLog("can't determine immediate dependencies for extension %s\n",
528 kmod_name);
529 result = false;
530 goto finish;
531 }
532
533 graph_depth = 0;
534 while (workingDependencies->getCount()) {
535 if (graph_depth > 255) {
536 IOLog("extension dependency graph ridiculously long, indicating a loop\n");
537 result = false;
538 goto finish;
539 }
540
541 if (dependencyIterator) {
542 dependencyIterator->release();
543 dependencyIterator = 0;
544 }
545
546 dependencyIterator = OSCollectionIterator::withCollection(
547 workingDependencies);
548 if (!dependencyIterator) {
549 IOLog("memory allocation failure\n");
550 result = false;
551 goto finish;
552 }
553
554 while ( (libraryName =
555 OSDynamicCast(OSString, dependencyIterator->getNextObject())) ) {
556
557 library_name = libraryName->getCStringNoCopy();
558
559 dependentName = OSDynamicCast(OSString,
560 workingDependencies->getObject(libraryName));
561
562 dependent_name = dependentName->getCStringNoCopy();
563
564 if (!getKext(library_name, &kextPlist, NULL, NULL, NULL)) {
565 IOLog("can't find extension %s\n", library_name);
566 result = false;
567 goto finish;
568 }
569
570 OSString * string;
571 if ((string = OSDynamicCast(OSString,
572 kextPlist->getObject("OSBundleSharedExecutableIdentifier"))))
573 {
574 library_name = string->getCStringNoCopy();
575 if (!getKext(library_name, &kextPlist, NULL, NULL, NULL)) {
576 IOLog("can't find extension %s\n", library_name);
577 result = false;
578 goto finish;
579 }
580 }
581
582 kext_is_dependency = kextIsDependency(library_name,
583 &is_kernel_component);
584
585 if (!kext_is_dependency) {
586
587 /* For binaryless kexts, add a new pending dependency from the
588 * original dependent onto the dependencies of the current,
589 * binaryless, dependency.
590 */
591 if (!figureDependenciesForKext(kextPlist, pendingDependencies,
592 dependentName, true)) {
593
594 IOLog("can't determine immediate dependencies for extension %s\n",
595 library_name);
596 result = false;
597 goto finish;
598 }
599 continue;
600 } else {
601 dgraph_entry = dgraph_find_dependent(dgraph, dependent_name);
602 if (!dgraph_entry) {
603 IOLog("internal error with dependency graph\n");
604 LOG_DELAY(1);
605 result = false;
606 goto finish;
607 }
608
609 if (!getVersionForKext(kextPlist, &kmod_vers)) {
610 IOLog("can't get version for extension %s\n", library_name);
611 result = false;
612 goto finish;
613 }
614
615 /* It's okay for code to be zero, as for a pseudokext
616 * representing a kernel component.
617 */
618 if (!getKext(library_name, NULL /* already got it */,
619 &code, &code_length, &code_is_kmem)) {
620 IOLog("can't find extension %s\n", library_name);
621 result = false;
622 goto finish;
623 }
624
625 dgraph_dependency = dgraph_add_dependency(dgraph, dgraph_entry,
626 library_name, code, code_length, code_is_kmem,
627 library_name, kmod_vers,
628 0 /* load_address not yet known */, is_kernel_component);
629
630 if (!dgraph_dependency) {
631 IOLog("can't record dependency %s -> %s\n", dependent_name,
632 library_name);
633 result = false;
634 // kmem_alloc()ed code is freed in finish: block.
635 goto finish;
636 }
637
638 // pass ownership of code to kld patcher
639 if (code) {
640 if (kload_map_entry(dgraph_dependency) != kload_error_none) {
641 IOLog("can't map %s in preparation for loading\n", library_name);
642 result = false;
643 // kmem_alloc()ed code is freed in finish: block.
644 goto finish;
645 }
646 }
647 // clear local record of code
648 code = 0;
649 code_length = 0;
650 code_is_kmem = false;
651 }
652
653 /* Now put the library's dependencies onto the pending set.
654 */
655 if (!figureDependenciesForKext(kextPlist, pendingDependencies,
656 NULL, false)) {
657
658 IOLog("can't determine immediate dependencies for extension %s\n",
659 library_name);
660 result = false;
661 goto finish;
662 }
663 }
664
665 dependencyIterator->release();
666 dependencyIterator = 0;
667
668 workingDependencies->flushCollection();
669 swapDict = workingDependencies;
670 workingDependencies = pendingDependencies;
671 pendingDependencies = swapDict;
672 graph_depth++;
673 }
674
675 finish:
676 if (code && code_is_kmem) {
677 kmem_free(kernel_map, (unsigned int)code, code_length);
678 }
679 if (workingDependencies) workingDependencies->release();
680 if (pendingDependencies) pendingDependencies->release();
681 if (dependencyIterator) dependencyIterator->release();
682 return result;
683 }
684
685 /*********************************************************************
686 * This is the function that IOCatalogue calls in order to load a kmod.
687 * It first checks whether the kmod is already loaded. If the kmod
688 * isn't loaded, this function builds a dependency list and calls
689 * load_kmod() repeatedly to guarantee that each dependency is in fact
690 * loaded.
691 *********************************************************************/
692 __private_extern__
693 kern_return_t load_kernel_extension(char * kmod_name)
694 {
695 kern_return_t result = KERN_SUCCESS;
696 kload_error load_result = kload_error_none;
697 dgraph_t dgraph;
698 bool free_dgraph = false;
699 kmod_info_t * kmod_info;
700
701 // Put this in for lots of messages about kext loading.
702 #if 0
703 kload_set_log_level(kload_log_level_load_details);
704 #endif
705
706 /* See if the kmod is already loaded.
707 */
708 if ((kmod_info = kmod_lookupbyname_locked(kmod_name))) {
709 kfree((vm_offset_t) kmod_info, sizeof(kmod_info_t));
710 return KERN_SUCCESS;
711 }
712
713 if (dgraph_init(&dgraph) != dgraph_valid) {
714 IOLog("Can't initialize dependency graph to load %s.\n",
715 kmod_name);
716 result = KERN_FAILURE;
717 goto finish;
718 }
719
720 free_dgraph = true;
721 if (!add_dependencies_for_kmod(kmod_name, &dgraph)) {
722 IOLog("Can't determine dependencies for %s.\n",
723 kmod_name);
724 result = KERN_FAILURE;
725 goto finish;
726 }
727
728 dgraph.root = dgraph_find_root(&dgraph);
729
730 if (!dgraph.root) {
731 IOLog("Dependency graph to load %s has no root.\n",
732 kmod_name);
733 result = KERN_FAILURE;
734 goto finish;
735 }
736
737 /* A kernel component is built in and need not be loaded.
738 */
739 if (dgraph.root->is_kernel_component) {
740 result = KERN_SUCCESS;
741 goto finish;
742 }
743
744 dgraph_establish_load_order(&dgraph);
745
746 load_result = kload_load_dgraph(&dgraph);
747 if (load_result != kload_error_none &&
748 load_result != kload_error_already_loaded) {
749
750 IOLog(VTYELLOW "Failed to load extension %s.\n" VTRESET, kmod_name);
751
752 result = KERN_FAILURE;
753 goto finish;
754 }
755
756 finish:
757
758 if (free_dgraph) {
759 dgraph_free(&dgraph, 0 /* don't free dgraph itself */);
760 }
761 return result;
762 }