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