2 * Copyright (c) 1998-2006 Apple Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
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.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
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
24 * limitations under the License.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
29 * Copyright (c) 1998 Apple Inc. All rights reserved.
35 * NOTICE: This file was modified by SPARTA, Inc. in 2005 to introduce
36 * support for mandatory and extensible security protections. This notice
37 * is included in support of clause 2.2 (b) of the Apple Public License,
42 #include <machine/machine_routines.h>
43 #include <libkern/kernel_mach_header.h>
44 #include <kern/host.h>
45 #include <security/mac_data.h>
48 #include <libkern/c++/OSContainers.h>
49 #include <libkern/c++/OSUnserialize.h>
50 #include <libkern/c++/OSKext.h>
51 #include <libkern/OSKextLibPrivate.h>
53 #include <IOKit/IODeviceTreeSupport.h>
54 #include <IOKit/IOService.h>
55 #include <IOKit/IOCatalogue.h>
57 #include <IOKit/IOLib.h>
58 #include <IOKit/assert.h>
61 #pragma mark Internal Declarations
63 /*********************************************************************
64 *********************************************************************/
68 IOCatalogue
* gIOCatalogue
;
69 const OSSymbol
* gIOClassKey
;
70 const OSSymbol
* gIOProbeScoreKey
;
71 const OSSymbol
* gIOModuleIdentifierKey
;
72 IOLock
* gIOCatalogLock
;
75 #pragma mark Utility functions
77 /*********************************************************************
78 * Add a new personality to the set if it has a unique IOResourceMatchKey value.
79 * XXX -- svail: This should be optimized.
80 * esb - There doesn't seem like any reason to do this - it causes problems
81 * esb - when there are more than one loadable driver matching on the same provider class
82 *********************************************************************/
84 AddNewImports(OSOrderedSet
* set
, OSDictionary
* dict
)
90 #pragma mark IOCatalogue class implementation
92 /*********************************************************************
93 *********************************************************************/
95 #define super OSObject
96 OSDefineMetaClassAndStructors(IOCatalogue
, OSObject
)
98 /*********************************************************************
99 *********************************************************************/
100 void IOCatalogue::initialize(void)
103 OSString
* errorString
;
106 extern const char * gIOKernelConfigTables
;
108 array
= OSDynamicCast(OSArray
, OSUnserialize(gIOKernelConfigTables
, &errorString
));
109 if (!array
&& errorString
) {
110 IOLog("KernelConfigTables syntax error: %s\n",
111 errorString
->getCStringNoCopy());
112 errorString
->release();
115 gIOClassKey
= OSSymbol::withCStringNoCopy( kIOClassKey
);
116 gIOProbeScoreKey
= OSSymbol::withCStringNoCopy( kIOProbeScoreKey
);
117 gIOModuleIdentifierKey
= OSSymbol::withCStringNoCopy( kCFBundleIdentifierKey
);
119 assert( array
&& gIOClassKey
&& gIOProbeScoreKey
120 && gIOModuleIdentifierKey
);
122 gIOCatalogue
= new IOCatalogue
;
123 assert(gIOCatalogue
);
124 rc
= gIOCatalogue
->init(array
);
129 /*********************************************************************
130 * Initialize the IOCatalog object.
131 *********************************************************************/
132 bool IOCatalogue::init(OSArray
* initArray
)
136 if ( !super::init() )
143 kernelTables
= OSCollectionIterator::withCollection( array
);
145 gIOCatalogLock
= IOLockAlloc();
147 lock
= gIOCatalogLock
;
150 #endif /* __i386__ */
152 kernelTables
->reset();
153 while( (dict
= (OSDictionary
*) kernelTables
->getNextObject())) {
154 OSKext::uniquePersonalityProperties(dict
);
155 if( 0 == dict
->getObject( gIOClassKey
))
156 IOLog("Missing or bad \"%s\" key\n",
157 gIOClassKey
->getCStringNoCopy());
161 AbsoluteTime deadline
;
162 clock_interval_to_deadline( 1000, kMillisecondScale
);
163 thread_call_func_delayed( ping
, this, deadline
);
169 /*********************************************************************
170 * Release all resources used by IOCatalogue and deallocate.
171 * This will probably never be called.
172 *********************************************************************/
173 void IOCatalogue::free( void )
179 kernelTables
->release();
184 /*********************************************************************
185 *********************************************************************/
188 static int hackLimit
;
189 enum { kDriversPerIter
= 4 };
192 IOCatalogue::ping(thread_call_param_t arg
, thread_call_param_t
)
194 IOCatalogue
* self
= (IOCatalogue
*) arg
;
196 OSDictionary
* table
;
199 set
= OSOrderedSet::withCapacity( 1 );
201 IOLockLock( &self
->lock
);
203 for( newLimit
= 0; newLimit
< kDriversPerIter
; newLimit
++) {
204 table
= (OSDictionary
*) self
->array
->getObject(
205 hackLimit
+ newLimit
);
207 set
->setLastObject( table
);
209 OSSymbol
* sym
= (OSSymbol
*) table
->getObject(gIOClassKey
);
210 kprintf("enabling %s\n", sym
->getCStringNoCopy());
218 IOService::catalogNewDrivers( set
);
220 hackLimit
+= newLimit
;
223 IOLockUnlock( &self
->lock
);
225 if( kDriversPerIter
== newLimit
) {
226 AbsoluteTime deadline
;
227 clock_interval_to_deadline(500, kMillisecondScale
);
228 thread_call_func_delayed(ping
, this, deadline
);
233 /*********************************************************************
234 *********************************************************************/
236 IOCatalogue::findDrivers(
238 SInt32
* generationCount
)
240 OSDictionary
* nextTable
;
244 set
= OSOrderedSet::withCapacity( 1, IOServiceOrdering
,
245 (void *)gIOProbeScoreKey
);
250 kernelTables
->reset();
255 while( (nextTable
= (OSDictionary
*) kernelTables
->getNextObject())) {
257 if( hackIndex
++ > hackLimit
)
260 imports
= OSDynamicCast( OSString
,
261 nextTable
->getObject( gIOProviderClassKey
));
262 if( imports
&& service
->metaCast( imports
))
263 set
->setObject( nextTable
);
266 *generationCount
= getGenerationCount();
273 /*********************************************************************
274 * Is personality already in the catalog?
275 *********************************************************************/
277 IOCatalogue::findDrivers(
278 OSDictionary
* matching
,
279 SInt32
* generationCount
)
284 OSKext::uniquePersonalityProperties(matching
);
286 set
= OSOrderedSet::withCapacity( 1, IOServiceOrdering
,
287 (void *)gIOProbeScoreKey
);
290 kernelTables
->reset();
291 while ( (dict
= (OSDictionary
*) kernelTables
->getNextObject()) ) {
293 /* This comparison must be done with only the keys in the
294 * "matching" dict to enable general searches.
296 if ( dict
->isEqualTo(matching
, matching
) )
297 set
->setObject(dict
);
299 *generationCount
= getGenerationCount();
305 /*********************************************************************
306 * Add driver config tables to catalog and start matching process.
308 * Important that existing personalities are kept (not replaced)
309 * if duplicates found. Personalities can come from OSKext objects
310 * or from userland kext library. We want to minimize distinct
311 * copies between OSKext & IOCatalogue.
313 * xxx - userlib used to refuse to send personalities with IOKitDebug
314 * xxx - during safe boot. That would be better implemented here.
315 *********************************************************************/
316 bool IOCatalogue::addDrivers(
321 OSCollectionIterator
* iter
= NULL
; // must release
322 OSOrderedSet
* set
= NULL
; // must release
323 OSObject
* object
= NULL
; // do not release
324 OSArray
* persons
= NULL
; // do not release
326 persons
= OSDynamicCast(OSArray
, drivers
);
331 set
= OSOrderedSet::withCapacity( 10, IOServiceOrdering
,
332 (void *)gIOProbeScoreKey
);
337 iter
= OSCollectionIterator::withCollection(persons
);
342 /* Start with success; clear it on an error.
347 while ( (object
= iter
->getNextObject()) ) {
349 // xxx Deleted OSBundleModuleDemand check; will handle in other ways for SL
351 OSDictionary
* personality
= OSDynamicCast(OSDictionary
, object
);
356 IOLog("IOCatalogue::addDrivers() encountered non-dictionary; bailing.\n");
361 OSKext::uniquePersonalityProperties(personality
);
363 // Add driver personality to catalogue.
364 count
= array
->getCount();
366 OSDictionary
* driver
;
368 // Be sure not to double up on personalities.
369 driver
= (OSDictionary
*)array
->getObject(count
);
371 /* Unlike in other functions, this comparison must be exact!
372 * The catalogue must be able to contain personalities that
373 * are proper supersets of others.
374 * Do not compare just the properties present in one driver
375 * pesonality or the other.
377 if (personality
->isEqualTo(driver
)) {
386 result
= array
->setObject(personality
);
391 AddNewImports(set
, personality
);
393 // Start device matching.
394 if (result
&& doNubMatching
&& (set
->getCount() > 0)) {
395 IOService::catalogNewDrivers(set
);
401 if (set
) set
->release();
402 if (iter
) iter
->release();
407 /*********************************************************************
408 * Remove drivers from the catalog which match the
409 * properties in the matching dictionary.
410 *********************************************************************/
412 IOCatalogue::removeDrivers(
413 OSDictionary
* matching
,
416 OSCollectionIterator
* tables
;
424 set
= OSOrderedSet::withCapacity(10,
426 (void *)gIOProbeScoreKey
);
430 arrayCopy
= OSArray::withCapacity(100);
436 tables
= OSCollectionIterator::withCollection(arrayCopy
);
437 arrayCopy
->release();
443 OSKext::uniquePersonalityProperties( matching
);
446 kernelTables
->reset();
447 arrayCopy
->merge(array
);
448 array
->flushCollection();
450 while ( (dict
= (OSDictionary
*)tables
->getNextObject()) ) {
452 /* This comparison must be done with only the keys in the
453 * "matching" dict to enable general searches.
455 if ( dict
->isEqualTo(matching
, matching
) ) {
456 AddNewImports( set
, dict
);
460 array
->setObject(dict
);
462 // Start device matching.
463 if ( doNubMatching
&& (set
->getCount() > 0) ) {
464 IOService::catalogNewDrivers(set
);
475 // Return the generation count.
476 SInt32
IOCatalogue::getGenerationCount(void) const
478 return( generation
);
481 bool IOCatalogue::isModuleLoaded(OSString
* moduleName
) const
483 return isModuleLoaded(moduleName
->getCStringNoCopy());
486 bool IOCatalogue::isModuleLoaded(const char * moduleName
) const
489 ret
= OSKext::loadKextWithIdentifier(moduleName
);
490 if (kOSKextReturnDeferred
== ret
) {
491 // a request has been queued but the module isn't necessarily
492 // loaded yet, so stall.
495 // module is present or never will be
499 // Check to see if module has been loaded already.
500 bool IOCatalogue::isModuleLoaded(OSDictionary
* driver
) const
502 OSString
* moduleName
= NULL
;
503 OSString
* publisherName
= NULL
;
508 /* The personalities of codeless kexts often contain the bundle ID of the
509 * kext they reference, and not the bundle ID of the codeless kext itself.
510 * The prelinked kernel needs to know the bundle ID of the codeless kext
511 * so it can include these personalities, so OSKext stores that bundle ID
512 * in the IOPersonalityPublisher key, and we record it as requested here.
514 publisherName
= OSDynamicCast(OSString
,
515 driver
->getObject(kIOPersonalityPublisherKey
));
516 OSKext::recordIdentifierRequest(publisherName
);
518 moduleName
= OSDynamicCast(OSString
, driver
->getObject(gIOModuleIdentifierKey
));
520 return isModuleLoaded(moduleName
);
522 /* If a personality doesn't hold the "CFBundleIdentifier" key
523 * it is assumed to be an "in-kernel" driver.
528 /* This function is called after a module has been loaded.
529 * Is invoked from user client call, ultimately from IOKitLib's
530 * IOCatalogueModuleLoaded(). Sent from kextd.
532 void IOCatalogue::moduleHasLoaded(OSString
* moduleName
)
536 dict
= OSDictionary::withCapacity(2);
537 dict
->setObject(gIOModuleIdentifierKey
, moduleName
);
541 (void) OSKext::considerRebuildOfPrelinkedKernel(moduleName
);
544 void IOCatalogue::moduleHasLoaded(const char * moduleName
)
548 name
= OSString::withCString(moduleName
);
549 moduleHasLoaded(name
);
553 // xxx - return is really OSReturn/kern_return_t
554 IOReturn
IOCatalogue::unloadModule(OSString
* moduleName
) const
556 return OSKext::removeKextWithIdentifier(moduleName
->getCStringNoCopy());
559 static IOReturn
_terminateDrivers(OSDictionary
* matching
)
567 return kIOReturnBadArgument
;
569 ret
= kIOReturnSuccess
;
571 iter
= IORegistryIterator::iterateOver(gIOServicePlane
,
572 kIORegistryIterateRecursively
);
574 return kIOReturnNoMemory
;
576 OSKext::uniquePersonalityProperties( matching
);
578 // terminate instances.
581 while( (service
= (IOService
*)iter
->getNextObject()) ) {
582 dict
= service
->getPropertyTable();
586 /* Terminate only for personalities that match the matching dictionary.
587 * This comparison must be done with only the keys in the
588 * "matching" dict to enable general matching.
590 if ( !dict
->isEqualTo(matching
, matching
) )
593 if ( !service
->terminate(kIOServiceRequired
|kIOServiceSynchronous
) ) {
594 ret
= kIOReturnUnsupported
;
598 } while( !service
&& !iter
->isValid());
604 static IOReturn
_removeDrivers( OSArray
* array
, OSDictionary
* matching
)
606 OSCollectionIterator
* tables
;
609 IOReturn ret
= kIOReturnSuccess
;
611 // remove configs from catalog.
613 arrayCopy
= OSArray::withCapacity(100);
615 return kIOReturnNoMemory
;
617 tables
= OSCollectionIterator::withCollection(arrayCopy
);
618 arrayCopy
->release();
620 return kIOReturnNoMemory
;
622 arrayCopy
->merge(array
);
623 array
->flushCollection();
625 while ( (dict
= (OSDictionary
*)tables
->getNextObject()) ) {
627 /* Remove from the catalogue's array any personalities
628 * that match the matching dictionary.
629 * This comparison must be done with only the keys in the
630 * "matching" dict to enable general matching.
632 if ( dict
->isEqualTo(matching
, matching
) )
635 array
->setObject(dict
);
643 IOReturn
IOCatalogue::terminateDrivers(OSDictionary
* matching
)
647 ret
= _terminateDrivers(matching
);
649 if (kIOReturnSuccess
== ret
)
650 ret
= _removeDrivers(array
, matching
);
651 kernelTables
->reset();
657 IOReturn
IOCatalogue::terminateDriversForModule(
658 OSString
* moduleName
,
663 bool isLoaded
= false;
665 /* Check first if the kext currently has any linkage dependents;
666 * in such a case the unload would fail so let's not terminate any
667 * IOServices (since doing so typically results in a panic when there
668 * are loaded dependencies). Note that we aren't locking the kext here
669 * so it might lose or gain dependents by the time we call unloadModule();
670 * I think that's ok, our unload can fail if a kext comes in on top of
671 * this one even after we've torn down IOService objects. Conversely,
672 * if we fail the unload here and then lose a library, the autounload
673 * thread will get us in short order.
675 if (OSKext::isKextWithIdentifierLoaded(moduleName
->getCStringNoCopy())) {
679 if (!OSKext::canUnloadKextWithIdentifier(moduleName
,
680 /* checkClasses */ false)) {
681 ret
= kOSKextReturnInUse
;
685 dict
= OSDictionary::withCapacity(1);
687 ret
= kIOReturnNoMemory
;
691 dict
->setObject(gIOModuleIdentifierKey
, moduleName
);
693 ret
= _terminateDrivers(dict
);
695 /* No goto between IOLock calls!
698 if (kIOReturnSuccess
== ret
) {
699 ret
= _removeDrivers(array
, dict
);
701 kernelTables
->reset();
703 // Unload the module itself.
704 if (unload
&& isLoaded
&& ret
== kIOReturnSuccess
) {
705 ret
= unloadModule(moduleName
);
716 IOReturn
IOCatalogue::terminateDriversForModule(
717 const char * moduleName
,
723 name
= OSString::withCString(moduleName
);
725 return kIOReturnNoMemory
;
727 ret
= terminateDriversForModule(name
, unload
);
733 bool IOCatalogue::startMatching( OSDictionary
* matching
)
741 set
= OSOrderedSet::withCapacity(10, IOServiceOrdering
,
742 (void *)gIOProbeScoreKey
);
747 kernelTables
->reset();
749 while ( (dict
= (OSDictionary
*)kernelTables
->getNextObject()) ) {
751 /* This comparison must be done with only the keys in the
752 * "matching" dict to enable general matching.
754 if ( dict
->isEqualTo(matching
, matching
) )
755 AddNewImports(set
, dict
);
757 // Start device matching.
758 if ( set
->getCount() > 0 ) {
759 IOService::catalogNewDrivers(set
);
770 void IOCatalogue::reset(void)
772 IOCatalogue::resetAndAddDrivers(/* no drivers; true reset */ NULL
,
773 /* doMatching */ false);
777 bool IOCatalogue::resetAndAddDrivers(OSArray
* drivers
, bool doNubMatching
)
780 OSArray
* newPersonalities
= NULL
; // do not release
781 OSCollectionIterator
* newPIterator
= NULL
; // must release
782 OSOrderedSet
* matchSet
= NULL
; // must release
783 OSArray
* oldPersonalities
= NULL
; // must release
784 OSArray
* kernelPersonalities
= NULL
; // must release
785 OSString
* errorString
= NULL
; // must release
786 OSObject
* object
= NULL
; // do not release
787 OSDictionary
* thisNewPersonality
= NULL
; // do not release
790 extern const char * gIOKernelConfigTables
;
793 newPersonalities
= OSDynamicCast(OSArray
, drivers
);
794 if (!newPersonalities
) {
798 newPIterator
= OSCollectionIterator::withCollection(newPersonalities
);
803 matchSet
= OSOrderedSet::withCapacity(10, IOServiceOrdering
,
804 (void *)gIOProbeScoreKey
);
810 /* Read personalities for the built-in kernel driver classes.
811 * We don't have many any more.
813 kernelPersonalities
= OSDynamicCast(OSArray
,
814 OSUnserialize(gIOKernelConfigTables
, &errorString
));
815 if (!kernelPersonalities
&& errorString
) {
816 IOLog("KernelConfigTables syntax error: %s\n",
817 errorString
->getCStringNoCopy());
821 /* Now copy the current array of personalities so we can reuse them
822 * if the new list contains any duplicates. This saves on memory
825 oldPersonalities
= OSDynamicCast(OSArray
, array
->copyCollection());
826 if (!oldPersonalities
) {
832 IOLog("Resetting IOCatalogue.\n");
834 /* No goto finish from here to unlock.
838 array
->flushCollection();
840 /* Add back the kernel personalities and remove them from the old
841 * array so we don't try to match on them again. Go forward through
842 * the arrays as this causes the least iteration since kernel personalities
843 * should always be first.
845 count
= kernelPersonalities
->getCount();
846 for (i
= 0; i
< count
; i
++) {
848 /* Static cast here, as the data is coming from within the kernel image.
850 OSDictionary
* thisNewPersonality
= (OSDictionary
*)
851 kernelPersonalities
->getObject(i
);
852 array
->setObject(thisNewPersonality
);
854 signed int oldPCount
= oldPersonalities
->getCount();
855 for (signed int oldPIndex
= 0; oldPIndex
< oldPCount
; oldPIndex
++) {
856 if (thisNewPersonality
->isEqualTo(oldPersonalities
->getObject(oldPIndex
))) {
857 oldPersonalities
->removeObject(oldPIndex
);
863 /* Now add the new set of personalities passed in, using existing
864 * copies if we had them in kernel memory already.
867 OSDictionary
* thisOldPersonality
= NULL
; // do not release
869 while ( (object
= newPIterator
->getNextObject()) ) {
871 thisNewPersonality
= OSDynamicCast(OSDictionary
, object
);
872 if (!thisNewPersonality
) {
873 IOLog("IOCatalogue::resetAndAddDrivers() encountered non-dictionary; bailing.\n");
878 /* Convert common OSString property values to OSSymbols.
880 OSKext::uniquePersonalityProperties(thisNewPersonality
);
882 /* Add driver personality to catalogue, but if we had a copy already
883 * use that instead so we don't have multiple copies from OSKext instances.
885 count
= oldPersonalities
->getCount();
886 thisOldPersonality
= NULL
;
889 thisOldPersonality
= (OSDictionary
*)oldPersonalities
->getObject(count
);
891 /* Unlike in other functions, this comparison must be exact!
892 * The catalogue must be able to contain personalities that
893 * are proper supersets of others.
894 * Do not compare just the properties present in one driver
895 * pesonality or the other.
897 if (thisNewPersonality
->isEqualTo(thisOldPersonality
)) {
902 /* If we found a dup, add the *original* back to the catalogue,
903 * remove it from our bookkeeping list, and continue.
904 * Don't worry about matching on personalities we already had.
907 array
->setObject(thisOldPersonality
);
908 oldPersonalities
->removeObject(count
);
912 /* Otherwise add the new personality and mark it for matching.
914 array
->setObject(thisNewPersonality
);
915 AddNewImports(matchSet
, thisNewPersonality
);
919 * Now, go through remaining old personalities, which have effectively
920 * been removed, and add them to the match set as necessary.
922 count
= oldPersonalities
->getCount();
925 /* Static cast here is ok as these dictionaries were already in the catalogue.
927 thisOldPersonality
= (OSDictionary
*)oldPersonalities
->getObject(count
);
928 AddNewImports(matchSet
, thisOldPersonality
);
931 /* Finally, start device matching on all new & removed personalities.
933 if (result
&& doNubMatching
&& (matchSet
->getCount() > 0)) {
934 IOService::catalogNewDrivers(matchSet
);
942 if (newPIterator
) newPIterator
->release();
943 if (matchSet
) matchSet
->release();
944 if (oldPersonalities
) oldPersonalities
->release();
945 if (kernelPersonalities
) kernelPersonalities
->release();
946 if (errorString
) errorString
->release();
951 bool IOCatalogue::serialize(OSSerialize
* s
) const
956 return super::serialize(s
);
959 bool IOCatalogue::serializeData(IOOptionBits kind
, OSSerialize
* s
) const
961 kern_return_t kr
= kIOReturnSuccess
;
965 case kIOCatalogGetContents
:
966 if (!array
->serialize(s
))
967 kr
= kIOReturnNoMemory
;
970 case kIOCatalogGetModuleDemandList
:
971 kr
= KERN_NOT_SUPPORTED
;
974 case kIOCatalogGetCacheMissList
:
975 kr
= KERN_NOT_SUPPORTED
;
978 case kIOCatalogGetROMMkextList
:
979 kr
= KERN_NOT_SUPPORTED
;
983 kr
= kIOReturnBadArgument
;
992 #pragma mark Obsolete Kext Loading Stuff
994 /*********************************************************************
995 **********************************************************************
996 *** BINARY COMPATIBILITY SECTION ***
997 **********************************************************************
998 **********************************************************************
999 * These functions are no longer used are necessary for C++ binary
1000 * compatibility on i386.
1001 **********************************************************************/
1004 bool IOCatalogue::recordStartupExtensions(void)
1007 bool IOCatalogue::addExtensionsFromArchive(OSData
* mkext
)
1008 { return KERN_NOT_SUPPORTED
; }
1010 kern_return_t
IOCatalogue::removeKernelLinker(void)
1011 { return KERN_NOT_SUPPORTED
; }
1013 #endif /* __i386__ */