]> git.saurik.com Git - apple/xnu.git/blame - libsa/kext.cpp
xnu-517.7.21.tar.gz
[apple/xnu.git] / libsa / kext.cpp
CommitLineData
55e303ae
A
1/*
2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
e5568f75
A
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.
55e303ae 11 *
e5568f75
A
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
55e303ae
A
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
e5568f75
A
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.
55e303ae
A
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
28extern "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
44extern "C" {
45extern kern_return_t
46kmod_create_internal(
47 kmod_info_t *info,
48 kmod_t *id);
49
50extern kern_return_t
51kmod_destroy_internal(kmod_t id);
52
53extern kern_return_t
54kmod_start_or_stop(
55 kmod_t id,
56 int start,
57 kmod_args_t *data,
58 mach_msg_type_number_t *dataCount);
59
60extern kern_return_t kmod_retain(kmod_t id);
61extern kern_return_t kmod_release(kmod_t id);
62
63extern void flush_dcache(vm_offset_t addr, unsigned cnt, int phys);
64extern 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*********************************************************************/
81static
82bool 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
184finish:
185
186 return result;
187}
188
189
190/*********************************************************************
191*
192*********************************************************************/
193static
194bool 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*********************************************************************/
259static
260bool 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
328finish:
329
330 return result;
331}
332
333/*********************************************************************
334*********************************************************************/
335static bool
336figureDependenciesForKext(OSDictionary * kextPlist,
337 OSDictionary * dependencies,
338 OSString * trueParent)
339{
340 bool result = true;
341 OSString * kextName = 0; // don't release
342 OSDictionary * libraries = 0; // don't release
343 OSCollectionIterator * keyIterator = 0; // must release
344 OSString * libraryName = 0; // don't release
345
346 kextName = OSDynamicCast(OSString,
347 kextPlist->getObject("CFBundleIdentifier"));
348 if (!kextName) {
349 // XXX: Add log message
350 result = false;
351 goto finish;
352 }
353
354 libraries = OSDynamicCast(OSDictionary,
355 kextPlist->getObject("OSBundleLibraries"));
356 if (!libraries) {
357 result = true;
358 goto finish;
359 }
360
361 keyIterator = OSCollectionIterator::withCollection(libraries);
362 if (!keyIterator) {
363 // XXX: Add log message
364 result = false;
365 goto finish;
366 }
367
368 while ( (libraryName = OSDynamicCast(OSString,
369 keyIterator->getNextObject())) ) {
370
371 OSString * libraryVersion = OSDynamicCast(OSString,
372 libraries->getObject(libraryName));
373 if (!libraryVersion) {
374 // XXX: Add log message
375 result = false;
376 goto finish;
377 }
378 if (!verifyCompatibility(libraryName, libraryVersion)) {
379 result = false;
380 goto finish;
381 } else {
382 dependencies->setObject(libraryName,
383 trueParent ? trueParent : kextName);
384 }
385 }
386
387finish:
388 if (keyIterator) keyIterator->release();
389 return result;
390}
391
392/*********************************************************************
393*********************************************************************/
394static
395bool getVersionForKext(OSDictionary * kextPlist, char ** version)
396{
397 OSString * kextName = 0; // don't release
398 OSString * kextVersion; // don't release
399
400 kextName = OSDynamicCast(OSString,
401 kextPlist->getObject("CFBundleIdentifier"));
402 if (!kextName) {
403 // XXX: Add log message
404 return false;
405 }
406
407 kextVersion = OSDynamicCast(OSString,
408 kextPlist->getObject("CFBundleVersion"));
409 if (!kextVersion) {
410 IOLog("getVersionForKext(): "
411 "Extension \"%s\" has no \"CFBundleVersion\" property.\n",
412 kextName->getCStringNoCopy());
413 return false;
414 }
415
416 if (version) {
417 *version = (char *)kextVersion->getCStringNoCopy();
418 }
419
420 return true;
421}
422
423/*********************************************************************
424*********************************************************************/
425static
426bool add_dependencies_for_kmod(const char * kmod_name, dgraph_t * dgraph)
427{
428 bool result = true;
429 OSDictionary * kextPlist = 0; // don't release
430 OSDictionary * workingDependencies = 0; // must release
431 OSDictionary * pendingDependencies = 0; // must release
432 OSDictionary * swapDict = 0; // don't release
433 OSString * dependentName = 0; // don't release
434 const char * dependent_name = 0; // don't free
435 OSString * libraryName = 0; // don't release
436 const char * library_name = 0; // don't free
437 OSCollectionIterator * dependencyIterator = 0; // must release
438 unsigned char * code = 0;
439 unsigned long code_length = 0;
440 bool code_is_kmem = false;
441 char * kmod_vers = 0; // from plist, don't free
442 char is_kernel_component = false;
443 dgraph_entry_t * dgraph_entry = 0; // don't free
444 dgraph_entry_t * dgraph_dependency = 0; // don't free
445 unsigned int graph_depth = 0;
446 bool kext_is_dependency = true;
447
448 if (!getKext(kmod_name, &kextPlist, &code, &code_length,
449 &code_is_kmem)) {
450 IOLog("can't find extension %s\n", kmod_name);
451 result = false;
452 goto finish;
453 }
454
455 if (!kextIsDependency(kmod_name, &is_kernel_component)) {
456 IOLog("extension %s is not loadable\n", kmod_name);
457 result = false;
458 goto finish;
459 }
460
461 if (!getVersionForKext(kextPlist, &kmod_vers)) {
462 IOLog("can't get version for extension %s\n", kmod_name);
463 result = false;
464 goto finish;
465 }
466
467 dgraph_entry = dgraph_add_dependent(dgraph, kmod_name,
468 code, code_length, code_is_kmem,
469 kmod_name, kmod_vers,
470 0 /* load_address not yet known */, is_kernel_component);
471 if (!dgraph_entry) {
472 IOLog("can't record %s in dependency graph\n", kmod_name);
473 result = false;
474 // kmem_alloc()ed code is freed in finish: block.
475 goto finish;
476 }
477
478 // pass ownership of code to kld patcher
479 if (code)
480 {
481 if (kload_map_entry(dgraph_entry) != kload_error_none) {
482 IOLog("can't map %s in preparation for loading\n", kmod_name);
483 result = false;
484 // kmem_alloc()ed code is freed in finish: block.
485 goto finish;
486 }
487 }
488 // clear local record of code
489 code = 0;
490 code_length = 0;
491 code_is_kmem = false;
492
493 workingDependencies = OSDictionary::withCapacity(5);
494 if (!workingDependencies) {
495 IOLog("memory allocation failure\n");
496 result = false;
497 goto finish;
498 }
499
500 pendingDependencies = OSDictionary::withCapacity(5);
501 if (!pendingDependencies) {
502 IOLog("memory allocation failure\n");
503 result = false;
504 goto finish;
505 }
506
507 if (!figureDependenciesForKext(kextPlist, workingDependencies, NULL)) {
508 IOLog("can't determine immediate dependencies for extension %s\n",
509 kmod_name);
510 result = false;
511 goto finish;
512 }
513
514 graph_depth = 0;
515 while (workingDependencies->getCount()) {
516 if (graph_depth > 255) {
517 IOLog("extension dependency graph ridiculously long, indicating a loop\n");
518 result = false;
519 goto finish;
520 }
521
522 if (dependencyIterator) {
523 dependencyIterator->release();
524 dependencyIterator = 0;
525 }
526
527 dependencyIterator = OSCollectionIterator::withCollection(
528 workingDependencies);
529 if (!dependencyIterator) {
530 IOLog("memory allocation failure\n");
531 result = false;
532 goto finish;
533 }
534
535 while ( (libraryName =
536 OSDynamicCast(OSString, dependencyIterator->getNextObject())) ) {
537
538 library_name = libraryName->getCStringNoCopy();
539
540 dependentName = OSDynamicCast(OSString,
541 workingDependencies->getObject(libraryName));
542
543 dependent_name = dependentName->getCStringNoCopy();
544
545 if (!getKext(library_name, &kextPlist, NULL, NULL, NULL)) {
546 IOLog("can't find extension %s\n", library_name);
547 result = false;
548 goto finish;
549 }
550
551 OSString * string;
552 if ((string = OSDynamicCast(OSString,
553 kextPlist->getObject("OSBundleSharedExecutableIdentifier"))))
554 {
555 library_name = string->getCStringNoCopy();
556 if (!getKext(library_name, &kextPlist, NULL, NULL, NULL)) {
557 IOLog("can't find extension %s\n", library_name);
558 result = false;
559 goto finish;
560 }
561 }
562
563 kext_is_dependency = kextIsDependency(library_name,
564 &is_kernel_component);
565
566 if (!kext_is_dependency) {
567
568 /* For binaryless kexts, add a new pending dependency from the
569 * original dependent onto the dependencies of the current,
570 * binaryless, dependency.
571 */
572 if (!figureDependenciesForKext(kextPlist, pendingDependencies,
573 dependentName)) {
574
575 IOLog("can't determine immediate dependencies for extension %s\n",
576 library_name);
577 result = false;
578 goto finish;
579 }
580 continue;
581 } else {
582 dgraph_entry = dgraph_find_dependent(dgraph, dependent_name);
583 if (!dgraph_entry) {
584 IOLog("internal error with dependency graph\n");
585 LOG_DELAY(1);
586 result = false;
587 goto finish;
588 }
589
590 if (!getVersionForKext(kextPlist, &kmod_vers)) {
591 IOLog("can't get version for extension %s\n", library_name);
592 result = false;
593 goto finish;
594 }
595
596 /* It's okay for code to be zero, as for a pseudokext
597 * representing a kernel component.
598 */
599 if (!getKext(library_name, NULL /* already got it */,
600 &code, &code_length, &code_is_kmem)) {
601 IOLog("can't find extension %s\n", library_name);
602 result = false;
603 goto finish;
604 }
605
606 dgraph_dependency = dgraph_add_dependency(dgraph, dgraph_entry,
607 library_name, code, code_length, code_is_kmem,
608 library_name, kmod_vers,
609 0 /* load_address not yet known */, is_kernel_component);
610
611 if (!dgraph_dependency) {
612 IOLog("can't record dependency %s -> %s\n", dependent_name,
613 library_name);
614 result = false;
615 // kmem_alloc()ed code is freed in finish: block.
616 goto finish;
617 }
618
619 // pass ownership of code to kld patcher
620 if (code) {
621 if (kload_map_entry(dgraph_dependency) != kload_error_none) {
622 IOLog("can't map %s in preparation for loading\n", library_name);
623 result = false;
624 // kmem_alloc()ed code is freed in finish: block.
625 goto finish;
626 }
627 }
628 // clear local record of code
629 code = 0;
630 code_length = 0;
631 code_is_kmem = false;
632 }
633
634 /* Now put the library's dependencies onto the pending set.
635 */
636 if (!figureDependenciesForKext(kextPlist, pendingDependencies,
637 NULL)) {
638
639 IOLog("can't determine immediate dependencies for extension %s\n",
640 library_name);
641 result = false;
642 goto finish;
643 }
644 }
645
646 dependencyIterator->release();
647 dependencyIterator = 0;
648
649 workingDependencies->flushCollection();
650 swapDict = workingDependencies;
651 workingDependencies = pendingDependencies;
652 pendingDependencies = swapDict;
653 graph_depth++;
654 }
655
656finish:
657 if (code && code_is_kmem) {
658 kmem_free(kernel_map, (unsigned int)code, code_length);
659 }
660 if (workingDependencies) workingDependencies->release();
661 if (pendingDependencies) pendingDependencies->release();
662 if (dependencyIterator) dependencyIterator->release();
663 return result;
664}
665
666/*********************************************************************
667* This is the function that IOCatalogue calls in order to load a kmod.
668* It first checks whether the kmod is already loaded. If the kmod
669* isn't loaded, this function builds a dependency list and calls
670* load_kmod() repeatedly to guarantee that each dependency is in fact
671* loaded.
672*********************************************************************/
673__private_extern__
674kern_return_t load_kernel_extension(char * kmod_name)
675{
676 kern_return_t result = KERN_SUCCESS;
677 kload_error load_result = kload_error_none;
678 dgraph_t dgraph;
679 bool free_dgraph = false;
680 kmod_info_t * kmod_info;
681
682// Put this in for lots of messages about kext loading.
683#if 0
684 kload_set_log_level(kload_log_level_load_details);
685#endif
686
687 /* See if the kmod is already loaded.
688 */
689 if ((kmod_info = kmod_lookupbyname_locked(kmod_name))) {
690 kfree((vm_offset_t) kmod_info, sizeof(kmod_info_t));
691 return KERN_SUCCESS;
692 }
693
694 if (dgraph_init(&dgraph) != dgraph_valid) {
695 IOLog("Can't initialize dependency graph to load %s.\n",
696 kmod_name);
697 result = KERN_FAILURE;
698 goto finish;
699 }
700
701 free_dgraph = true;
702 if (!add_dependencies_for_kmod(kmod_name, &dgraph)) {
703 IOLog("Can't determine dependencies for %s.\n",
704 kmod_name);
705 result = KERN_FAILURE;
706 goto finish;
707 }
708
709 dgraph.root = dgraph_find_root(&dgraph);
710
711 if (!dgraph.root) {
712 IOLog("Dependency graph to load %s has no root.\n",
713 kmod_name);
714 result = KERN_FAILURE;
715 goto finish;
716 }
717
718 /* A kernel component is built in and need not be loaded.
719 */
720 if (dgraph.root->is_kernel_component) {
721 result = KERN_SUCCESS;
722 goto finish;
723 }
724
725 dgraph_establish_load_order(&dgraph);
726
727 load_result = kload_load_dgraph(&dgraph);
728 if (load_result != kload_error_none &&
729 load_result != kload_error_already_loaded) {
730
731 IOLog(VTYELLOW "Failed to load extension %s.\n" VTRESET, kmod_name);
732
733 result = KERN_FAILURE;
734 goto finish;
735 }
736
737finish:
738
739 if (free_dgraph) {
740 dgraph_free(&dgraph, 0 /* don't free dgraph itself */);
741 }
742 return result;
743}