2 * Copyright (c) 1998-2008 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@
28 #include <IOKit/IOBSD.h>
29 #include <IOKit/IOLib.h>
30 #include <IOKit/IOService.h>
31 #include <IOKit/IODeviceTreeSupport.h>
32 #include <IOKit/IOKitKeys.h>
33 #include <IOKit/IOPlatformExpert.h>
37 #include <pexpert/pexpert.h>
38 #include <kern/clock.h>
39 #include <uuid/uuid.h>
41 // how long to wait for matching root device, secs
43 #define ROOTDEVICETIMEOUT 120
45 #define ROOTDEVICETIMEOUT 60
48 extern dev_t
mdevadd(int devid
, ppnum_t base
, unsigned int size
, int phys
);
49 extern dev_t
mdevlookup(int devid
);
50 extern void mdevremoveall(void);
55 IOService::publishResource("IOBSD");
57 return( kIOReturnSuccess
);
60 OSDictionary
* IOBSDNameMatching( const char * name
)
63 const OSSymbol
* str
= 0;
67 dict
= IOService::serviceMatching( gIOServiceKey
);
70 str
= OSSymbol::withCString( name
);
73 dict
->setObject( kIOBSDNameKey
, (OSObject
*) str
);
88 OSDictionary
* IOUUIDMatching( void )
90 return IOService::resourceMatching( "boot-uuid-media" );
94 OSDictionary
* IOCDMatching( void )
99 dict
= IOService::serviceMatching( "IOMedia" );
101 IOLog("Unable to find IOMedia\n");
105 str
= OSSymbol::withCString( "CD_ROM_Mode_1" );
111 dict
->setObject( "Content Hint", (OSObject
*)str
);
116 OSDictionary
* IONetworkMatching( const char * path
,
117 char * buf
, int maxLen
)
119 OSDictionary
* matching
= 0;
128 len
= strlen( kIODeviceTreePlane
":" );
133 strlcpy( buf
, kIODeviceTreePlane
":", len
+ 1 );
136 // remove parameters following ':' from the path
137 skip
= strchr( path
, ':');
145 strlcpy( comp
, path
, len
+ 1 );
147 matching
= IOService::serviceMatching( "IONetworkInterface" );
150 dict
= IOService::addLocation( matching
);
154 str
= OSString::withCString( buf
);
157 dict
->setObject( kIOPathMatchKey
, str
);
170 OSDictionary
* IONetworkNamePrefixMatching( const char * prefix
)
172 OSDictionary
* matching
;
173 OSDictionary
* propDict
= 0;
174 const OSSymbol
* str
= 0;
175 char networkType
[128];
178 matching
= IOService::serviceMatching( "IONetworkInterface" );
182 propDict
= OSDictionary::withCapacity(1);
186 str
= OSSymbol::withCString( prefix
);
190 propDict
->setObject( "IOInterfaceNamePrefix", (OSObject
*) str
);
194 // see if we're contrained to netroot off of specific network type
195 if(PE_parse_boot_argn( "network-type", networkType
, 128 ))
197 str
= OSSymbol::withCString( networkType
);
200 propDict
->setObject( "IONetworkRootType", str
);
206 if ( matching
->setObject( gIOPropertyMatchKey
,
207 (OSObject
*) propDict
) != true )
217 if ( matching
) matching
->release();
218 if ( propDict
) propDict
->release();
219 if ( str
) str
->release();
224 static bool IORegisterNetworkInterface( IOService
* netif
)
226 // A network interface is typically named and registered
227 // with BSD after receiving a request from a user space
228 // "namer". However, for cases when the system needs to
229 // root from the network, this registration task must be
230 // done inside the kernel and completed before the root
231 // device is handed to BSD.
236 OSDictionary
* dict
= 0;
239 enum { kMaxPathLen
= 512 };
242 stack
= IOService::waitForService(
243 IOService::serviceMatching("IONetworkStack") );
244 if ( stack
== 0 ) break;
246 dict
= OSDictionary::withCapacity(3);
247 if ( dict
== 0 ) break;
249 zero
= OSNumber::withNumber((UInt64
) 0, 32);
250 if ( zero
== 0 ) break;
252 pathBuf
= (char *) IOMalloc( kMaxPathLen
);
253 if ( pathBuf
== 0 ) break;
256 if ( netif
->getPath( pathBuf
, &len
, gIOServicePlane
)
259 path
= OSString::withCStringNoCopy( pathBuf
);
260 if ( path
== 0 ) break;
262 dict
->setObject( "IOInterfaceUnit", zero
);
263 dict
->setObject( kIOPathMatchKey
, path
);
265 stack
->setProperties( dict
);
269 if ( zero
) zero
->release();
270 if ( path
) path
->release();
271 if ( dict
) dict
->release();
272 if ( pathBuf
) IOFree(pathBuf
, kMaxPathLen
);
274 return ( netif
->getProperty( kIOBSDNameKey
) != 0 );
277 OSDictionary
* IODiskMatching( const char * path
, char * buf
, int maxLen
)
288 // scan the tail of the path for "@unit:partition"
290 // Have to get the full path to the controller - an alias may
291 // tell us next to nothing, like "hd:8"
292 alias
= IORegistryEntry::dealiasPath( &path
, gIODTPlane
);
294 look
= path
+ strlen( path
);
296 while( look
!= path
) {
297 if( *(--look
) == c
) {
299 partition
= strtol( look
+ 1, 0, 0 );
301 } else if( c
== '@') {
302 unit
= strtol( look
+ 1, &comp
, 16 );
305 lun
= strtol( comp
+ 1, 0, 16 );
309 } else if( c
== '/') {
315 if( alias
&& (look
== path
)) {
317 look
= path
+ strlen( path
);
321 if( c
|| unit
== -1 || partition
== -1)
324 len
= strlen( "{" kIOPathMatchKey
"='" kIODeviceTreePlane
":" );
329 snprintf( buf
, len
+ 1, "{" kIOPathMatchKey
"='" kIODeviceTreePlane
":" );
333 len
= strlen( alias
);
338 strlcpy( comp
, alias
, len
+ 1 );
342 if ( (look
- path
)) {
348 strlcpy( comp
, path
, len
+ 1 );
354 len
= strlen( "/@hhhhhhhh,hhhhhhhh:dddddddddd';}" );
359 snprintf( comp
, len
+ 1, "/@%lx,%lx:%ld';}", unit
, lun
, partition
);
363 len
= strlen( "/@hhhhhhhh:dddddddddd';}" );
368 snprintf( comp
, len
+ 1, "/@%lx:%ld';}", unit
, partition
);
371 return( OSDynamicCast(OSDictionary
, OSUnserialize( buf
, 0 )) );
378 OSDictionary
* IOOFPathMatching( const char * path
, char * buf
, int maxLen
)
380 OSDictionary
* matching
;
385 /* need to look up path, get device type,
386 call matching help based on device type */
388 matching
= IODiskMatching( path
, buf
, maxLen
);
394 len
= strlen( kIODeviceTreePlane
":" );
399 strlcpy( buf
, kIODeviceTreePlane
":", len
+ 1 );
402 len
= strlen( path
);
406 strlcpy( comp
, path
, len
+ 1 );
408 matching
= OSDictionary::withCapacity( 1 );
412 str
= OSString::withCString( buf
);
415 matching
->setObject( kIOPathMatchKey
, str
);
428 IOService
* IOFindMatchingChild( IOService
* service
)
430 // find a matching child service
431 IOService
* child
= 0;
432 OSIterator
* iter
= service
->getClientIterator();
434 while( ( child
= (IOService
*) iter
->getNextObject() ) ) {
435 OSDictionary
* dict
= OSDictionary::withCapacity( 1 );
440 const OSSymbol
* str
= OSSymbol::withCString( "Apple_HFS" );
446 dict
->setObject( "Content", (OSObject
*)str
);
448 if ( child
->compareProperty( dict
, "Content" ) ) {
453 IOService
* subchild
= IOFindMatchingChild( child
);
464 static int didRam
= 0;
466 kern_return_t
IOFindBSDRoot( char * rootName
, unsigned int rootNameSize
,
467 dev_t
* root
, u_int32_t
* oflags
)
471 IORegistryEntry
* regEntry
;
472 OSDictionary
* matching
= 0;
476 UInt32
*ramdParms
= 0;
480 bool findHFSChild
= false;
481 char * mediaProperty
= 0;
483 enum { kMaxPathBuf
= 512, kMaxBootVar
= 128 };
485 const char * look
= 0;
487 bool forceNet
= false;
488 bool debugInfoPrintedOnce
= false;
489 const char * uuidStr
= NULL
;
491 static int mountAttempts
= 0;
499 str
= (char *) IOMalloc( kMaxPathBuf
+ kMaxBootVar
);
501 return( kIOReturnNoMemory
);
502 rdBootVar
= str
+ kMaxPathBuf
;
504 if (!PE_parse_boot_argn("rd", rdBootVar
, kMaxBootVar
)
505 && !PE_parse_boot_argn("rootdev", rdBootVar
, kMaxBootVar
))
509 if( (regEntry
= IORegistryEntry::fromPath( "/chosen", gIODTPlane
))) {
510 data
= OSDynamicCast(OSData
, regEntry
->getProperty( "root-matching" ));
512 matching
= OSDynamicCast(OSDictionary
, OSUnserializeXML((char *)data
->getBytesNoCopy()));
518 data
= (OSData
*) regEntry
->getProperty( "boot-uuid" );
520 uuidStr
= (const char*)data
->getBytesNoCopy();
521 OSString
*uuidString
= OSString::withCString( uuidStr
);
523 // match the boot-args boot-uuid processing below
525 IOLog("rooting via boot-uuid from /chosen: %s\n", uuidStr
);
526 IOService::publishResource( "boot-uuid", uuidString
);
527 uuidString
->release();
528 matching
= IOUUIDMatching();
529 mediaProperty
= "boot-uuid-media";
537 // else try for an OF Path
538 data
= (OSData
*) regEntry
->getProperty( "rootpath" );
542 if( (regEntry
= IORegistryEntry::fromPath( "/options", gIODTPlane
))) {
543 data
= (OSData
*) regEntry
->getProperty( "boot-file" );
549 if( data
&& !uuidStr
)
550 look
= (const char *) data
->getBytesNoCopy();
552 if( rdBootVar
[0] == '*') {
553 look
= rdBootVar
+ 1;
556 if( (regEntry
= IORegistryEntry::fromPath( "/", gIODTPlane
))) {
557 forceNet
= (0 != regEntry
->getProperty( "net-boot" ));
565 // See if we have a RAMDisk property in /chosen/memory-map. If so, make it into a device.
566 // It will become /dev/mdx, where x is 0-f.
569 if(!didRam
) { /* Have we already build this ram disk? */
570 didRam
= 1; /* Remember we did this */
571 if((regEntry
= IORegistryEntry::fromPath( "/chosen/memory-map", gIODTPlane
))) { /* Find the map node */
572 data
= (OSData
*)regEntry
->getProperty("RAMDisk"); /* Find the ram disk, if there */
573 if(data
) { /* We found one */
575 ramdParms
= (UInt32
*)data
->getBytesNoCopy(); /* Point to the ram disk base and size */
576 (void)mdevadd(-1, ml_static_ptovirt(ramdParms
[0]) >> 12, ramdParms
[1] >> 12, 0); /* Initialize it and pass back the device number */
578 regEntry
->release(); /* Toss the entry */
583 // Now check if we are trying to root on a memory device
586 if((rdBootVar
[0] == 'm') && (rdBootVar
[1] == 'd') && (rdBootVar
[3] == 0)) {
587 dchar
= xchar
= rdBootVar
[2]; /* Get the actual device */
588 if((xchar
>= '0') && (xchar
<= '9')) xchar
= xchar
- '0'; /* If digit, convert */
590 xchar
= xchar
& ~' '; /* Fold to upper case */
591 if((xchar
>= 'A') && (xchar
<= 'F')) { /* Is this a valid digit? */
592 xchar
= (xchar
& 0xF) + 9; /* Convert the hex digit */
593 dchar
= dchar
| ' '; /* Fold to lower case */
595 else xchar
= -1; /* Show bogus */
597 if(xchar
>= 0) { /* Do we have a valid memory device name? */
598 *root
= mdevlookup(xchar
); /* Find the device number */
599 if(*root
>= 0) { /* Did we find one? */
601 rootName
[0] = 'm'; /* Build root name */
602 rootName
[1] = 'd'; /* Build root name */
603 rootName
[2] = dchar
; /* Build root name */
604 rootName
[3] = 0; /* Build root name */
605 IOLog("BSD root: %s, major %d, minor %d\n", rootName
, major(*root
), minor(*root
));
606 *oflags
= 0; /* Show that this is not network */
607 goto iofrootx
; /* Join common exit... */
609 panic("IOFindBSDRoot: specified root memory device, %s, has not been configured\n", rdBootVar
); /* Not there */
614 // from OpenFirmware path
615 IOLog("From path: \"%s\", ", look
);
618 if( forceNet
|| (0 == strncmp( look
, "enet", strlen( "enet" ))) ) {
619 matching
= IONetworkMatching( look
, str
, kMaxPathBuf
);
621 matching
= IODiskMatching( look
, str
, kMaxPathBuf
);
626 if( (!matching
) && rdBootVar
[0] ) {
632 if ( strncmp( look
, "en", strlen( "en" )) == 0 ) {
633 matching
= IONetworkNamePrefixMatching( "en" );
634 } else if ( strncmp( look
, "cdrom", strlen( "cdrom" )) == 0 ) {
635 matching
= IOCDMatching();
637 } else if ( strncmp( look
, "uuid", strlen( "uuid" )) == 0 ) {
639 OSString
*uuidString
;
641 uuid
= (char *)IOMalloc( kMaxBootVar
);
644 if (!PE_parse_boot_argn( "boot-uuid", uuid
, kMaxBootVar
)) {
645 panic( "rd=uuid but no boot-uuid=<value> specified" );
647 uuidString
= OSString::withCString( uuid
);
649 IOService::publishResource( "boot-uuid", uuidString
);
650 uuidString
->release();
651 IOLog( "\nWaiting for boot volume with UUID %s\n", uuid
);
652 matching
= IOUUIDMatching();
653 mediaProperty
= "boot-uuid-media";
655 IOFree( uuid
, kMaxBootVar
);
658 matching
= IOBSDNameMatching( look
);
664 // Match any HFS media
666 matching
= IOService::serviceMatching( "IOMedia" );
667 astring
= OSString::withCStringNoCopy("Apple_HFS");
669 matching
->setObject("Content", astring
);
674 if( true && matching
) {
675 OSSerialize
* s
= OSSerialize::withCapacity( 5 );
677 if( matching
->serialize( s
)) {
678 IOLog( "Waiting on %s\n", s
->text() );
684 t
.tv_sec
= ROOTDEVICETIMEOUT
;
687 service
= IOService::waitForService( matching
, &t
);
688 if( (!service
) || (mountAttempts
== 10)) {
689 PE_display_icon( 0, "noroot");
690 IOLog( "Still waiting for root device\n" );
692 if( !debugInfoPrintedOnce
) {
693 debugInfoPrintedOnce
= true;
694 if( gIOKitDebug
& kIOLogDTree
) {
695 IOLog("\nDT plane:\n");
696 IOPrintPlane( gIODTPlane
);
698 if( gIOKitDebug
& kIOLogServiceTree
) {
699 IOLog("\nService plane:\n");
700 IOPrintPlane( gIOServicePlane
);
702 if( gIOKitDebug
& kIOLogMemory
)
709 if ( service
&& findHFSChild
) {
713 // wait for children services to finish registering
715 timeoutNS
= ROOTDEVICETIMEOUT
;
716 timeoutNS
*= kSecondScale
;
718 if ( (service
->waitQuiet(timeoutNS
) ) == kIOReturnSuccess
) {
721 IOLog( "Waiting for child registration\n" );
724 // look for a subservice with an Apple_HFS child
725 IOService
* subservice
= IOFindMatchingChild( service
);
726 if ( subservice
) service
= subservice
;
727 } else if ( service
&& mediaProperty
) {
728 service
= (IOService
*)service
->getProperty(mediaProperty
);
734 // If the IOService we matched to is a subclass of IONetworkInterface,
735 // then make sure it has been registered with BSD and has a BSD name
739 && service
->metaCast( "IONetworkInterface" )
740 && !IORegisterNetworkInterface( service
) )
748 service
->getPath( str
, &len
, gIOServicePlane
);
749 IOLog( "Got boot device = %s\n", str
);
751 iostr
= (OSString
*) service
->getProperty( kIOBSDNameKey
);
753 strlcpy( rootName
, iostr
->getCStringNoCopy(), rootNameSize
);
754 off
= (OSNumber
*) service
->getProperty( kIOBSDMajorKey
);
756 mjr
= off
->unsigned32BitValue();
757 off
= (OSNumber
*) service
->getProperty( kIOBSDMinorKey
);
759 mnr
= off
->unsigned32BitValue();
761 if( service
->metaCast( "IONetworkInterface" ))
766 IOLog( "Wait for root failed\n" );
767 strlcpy( rootName
, "en0", rootNameSize
);
771 IOLog( "BSD root: %s", rootName
);
773 IOLog(", major %d, minor %d\n", mjr
, mnr
);
777 *root
= makedev( mjr
, mnr
);
780 IOFree( str
, kMaxPathBuf
+ kMaxBootVar
);
783 if( (gIOKitDebug
& (kIOLogDTree
| kIOLogServiceTree
| kIOLogMemory
)) && !debugInfoPrintedOnce
) {
785 IOService::getPlatform()->waitQuiet();
786 if( gIOKitDebug
& kIOLogDTree
) {
787 IOLog("\nDT plane:\n");
788 IOPrintPlane( gIODTPlane
);
790 if( gIOKitDebug
& kIOLogServiceTree
) {
791 IOLog("\nService plane:\n");
792 IOPrintPlane( gIOServicePlane
);
794 if( gIOKitDebug
& kIOLogMemory
)
798 return( kIOReturnSuccess
);
801 void IOSecureBSDRoot(const char * rootName
)
805 IOPlatformExpert
*pe
;
806 const OSSymbol
*functionName
= OSSymbol::withCStringNoCopy("SecureRootName");
808 while ((pe
= IOService::getPlatform()) == 0) IOSleep(1 * 1000);
810 // Returns kIOReturnNotPrivileged is the root device is not secure.
811 // Returns kIOReturnUnsupported if "SecureRootName" is not implemented.
812 result
= pe
->callPlatformFunction(functionName
, false, (void *)rootName
, (void *)0, (void *)0, (void *)0);
814 functionName
->release();
816 if (result
== kIOReturnNotPrivileged
) mdevremoveall();
821 IOBSDRegistryEntryForDeviceTree(char * path
)
823 return (IORegistryEntry::fromPath(path
, gIODTPlane
));
827 IOBSDRegistryEntryRelease(void * entry
)
829 IORegistryEntry
* regEntry
= (IORegistryEntry
*)entry
;
837 IOBSDRegistryEntryGetData(void * entry
, char * property_name
,
841 IORegistryEntry
* regEntry
= (IORegistryEntry
*)entry
;
843 data
= (OSData
*) regEntry
->getProperty(property_name
);
845 *packet_length
= data
->getLength();
846 return (data
->getBytesNoCopy());
851 kern_return_t
IOBSDGetPlatformUUID( uuid_t uuid
, mach_timespec_t timeout
)
853 IOService
* resources
;
856 resources
= IOService::waitForService( IOService::resourceMatching( kIOPlatformUUIDKey
), &timeout
);
857 if ( resources
== 0 ) return KERN_OPERATION_TIMED_OUT
;
859 string
= ( OSString
* ) IOService::getPlatform( )->getProvider( )->getProperty( kIOPlatformUUIDKey
);
860 if ( string
== 0 ) return KERN_NOT_SUPPORTED
;
862 uuid_parse( string
->getCStringNoCopy( ), uuid
);
867 kern_return_t
IOBSDGetPlatformSerialNumber( char *serial_number_str
, u_int32_t len
)
869 OSDictionary
* platform_dict
;
876 serial_number_str
[0] = '\0';
878 platform_dict
= IOService::serviceMatching( "IOPlatformExpertDevice" );
879 if (platform_dict
== NULL
) {
880 return KERN_NOT_SUPPORTED
;
883 platform
= IOService::waitForService( platform_dict
);
885 string
= ( OSString
* ) platform
->getProperty( kIOPlatformSerialNumberKey
);
887 return KERN_NOT_SUPPORTED
;
889 strlcpy( serial_number_str
, string
->getCStringNoCopy( ), len
);
896 dev_t
IOBSDGetMediaWithUUID( const char *uuid_cstring
, char *bsd_name
, int bsd_name_len
, int timeout
)
899 OSDictionary
*dictionary
;
900 OSString
*uuid_string
;
902 if (bsd_name_len
< 1) {
907 dictionary
= IOService::serviceMatching( "IOMedia" );
909 uuid_string
= OSString::withCString( uuid_cstring
);
912 mach_timespec_t tv
= { timeout
, 0 }; // wait up to "timeout" seconds for the device
914 dictionary
->setObject( "UUID", uuid_string
);
915 dictionary
->retain();
916 service
= IOService::waitForService( dictionary
, &tv
);
918 OSNumber
*dev_major
= (OSNumber
*) service
->getProperty( kIOBSDMajorKey
);
919 OSNumber
*dev_minor
= (OSNumber
*) service
->getProperty( kIOBSDMinorKey
);
920 OSString
*iostr
= (OSString
*) service
->getProperty( kIOBSDNameKey
);
923 strlcpy( bsd_name
, iostr
->getCStringNoCopy(), bsd_name_len
);
925 if ( dev_major
&& dev_minor
)
926 dev
= makedev( dev_major
->unsigned32BitValue(), dev_minor
->unsigned32BitValue() );
928 uuid_string
->release();
930 dictionary
->release();
937 void IOBSDIterateMediaWithContent(const char *content_uuid_cstring
, int (*func
)(const char *bsd_dev_name
, const char *uuid_str
, void *arg
), void *arg
)
939 OSDictionary
*dictionary
;
940 OSString
*content_uuid_string
;
942 dictionary
= IOService::serviceMatching( "IOMedia" );
944 content_uuid_string
= OSString::withCString( content_uuid_cstring
);
945 if( content_uuid_string
) {
949 dictionary
->setObject( "Content", content_uuid_string
);
950 dictionary
->retain();
952 iter
= IOService::getMatchingServices(dictionary
);
953 while (iter
&& (service
= (IOService
*)iter
->getNextObject())) {
955 OSString
*iostr
= (OSString
*) service
->getProperty( kIOBSDNameKey
);
956 OSString
*uuidstr
= (OSString
*) service
->getProperty( "UUID" );
961 uuid
= uuidstr
->getCStringNoCopy();
963 uuid
= "00000000-0000-0000-0000-000000000000";
967 if (func
&& func(iostr
->getCStringNoCopy(), uuid
, arg
) == 0) {
976 content_uuid_string
->release();
978 dictionary
->release();
983 int IOBSDIsMediaEjectable( const char *cdev_name
)
986 OSDictionary
*dictionary
;
989 if (strncmp(cdev_name
, "/dev/", 5) == 0) {
993 dictionary
= IOService::serviceMatching( "IOMedia" );
995 dev_name
= OSString::withCString( cdev_name
);
998 mach_timespec_t tv
= { 5, 0 }; // wait up to "timeout" seconds for the device
1000 dictionary
->setObject( kIOBSDNameKey
, dev_name
);
1001 dictionary
->retain();
1002 service
= IOService::waitForService( dictionary
, &tv
);
1004 OSBoolean
*ejectable
= (OSBoolean
*) service
->getProperty( "Ejectable" );
1007 ret
= (int)ejectable
->getValue();
1011 dev_name
->release();
1013 dictionary
->release();