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