2 * Copyright (c) 1998-2011 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/IOCatalogue.h>
32 #include <IOKit/IODeviceTreeSupport.h>
33 #include <IOKit/IOKitKeys.h>
34 #include <IOKit/IOPlatformExpert.h>
38 #include <pexpert/pexpert.h>
39 #include <kern/clock.h>
40 #include <uuid/uuid.h>
41 #include <sys/vnode_internal.h>
43 // how long to wait for matching root device, secs
45 #define ROOTDEVICETIMEOUT 120
47 #define ROOTDEVICETIMEOUT 60
50 extern dev_t
mdevadd(int devid
, uint64_t base
, unsigned int size
, int phys
);
51 extern dev_t
mdevlookup(int devid
);
52 extern void mdevremoveall(void);
53 extern void di_root_ramfile(IORegistryEntry
* entry
);
58 IOService::publishResource("IOBSD");
60 return( kIOReturnSuccess
);
64 IOServicePublishResource( const char * property
, boolean_t value
)
67 IOService::publishResource( property
, kOSBooleanTrue
);
69 IOService::getResourceService()->removeProperty( property
);
73 IOServiceWaitForMatchingResource( const char * property
, uint64_t timeout
)
75 OSDictionary
* dict
= 0;
76 IOService
* match
= 0;
77 boolean_t found
= false;
81 dict
= IOService::resourceMatching( property
);
84 match
= IOService::waitForMatchingService( dict
, timeout
);
99 IOCatalogueMatchingDriversPresent( const char * property
)
101 OSDictionary
* dict
= 0;
102 OSOrderedSet
* set
= 0;
103 SInt32 generationCount
= 0;
104 boolean_t found
= false;
108 dict
= OSDictionary::withCapacity(1);
111 dict
->setObject( property
, kOSBooleanTrue
);
112 set
= gIOCatalogue
->findDrivers( dict
, &generationCount
);
113 if ( set
&& (set
->getCount() > 0))
126 OSDictionary
* IOBSDNameMatching( const char * name
)
129 const OSSymbol
* str
= 0;
133 dict
= IOService::serviceMatching( gIOServiceKey
);
136 str
= OSSymbol::withCString( name
);
139 dict
->setObject( kIOBSDNameKey
, (OSObject
*) str
);
154 OSDictionary
* IOUUIDMatching( void )
156 return IOService::resourceMatching( "boot-uuid-media" );
159 OSDictionary
* IONetworkNamePrefixMatching( const char * prefix
)
161 OSDictionary
* matching
;
162 OSDictionary
* propDict
= 0;
163 const OSSymbol
* str
= 0;
164 char networkType
[128];
167 matching
= IOService::serviceMatching( "IONetworkInterface" );
171 propDict
= OSDictionary::withCapacity(1);
175 str
= OSSymbol::withCString( prefix
);
179 propDict
->setObject( "IOInterfaceNamePrefix", (OSObject
*) str
);
183 // see if we're contrained to netroot off of specific network type
184 if(PE_parse_boot_argn( "network-type", networkType
, 128 ))
186 str
= OSSymbol::withCString( networkType
);
189 propDict
->setObject( "IONetworkRootType", str
);
195 if ( matching
->setObject( gIOPropertyMatchKey
,
196 (OSObject
*) propDict
) != true )
206 if ( matching
) matching
->release();
207 if ( propDict
) propDict
->release();
208 if ( str
) str
->release();
213 static bool IORegisterNetworkInterface( IOService
* netif
)
215 // A network interface is typically named and registered
216 // with BSD after receiving a request from a user space
217 // "namer". However, for cases when the system needs to
218 // root from the network, this registration task must be
219 // done inside the kernel and completed before the root
220 // device is handed to BSD.
225 OSDictionary
* dict
= 0;
228 enum { kMaxPathLen
= 512 };
231 stack
= IOService::waitForService(
232 IOService::serviceMatching("IONetworkStack") );
233 if ( stack
== 0 ) break;
235 dict
= OSDictionary::withCapacity(3);
236 if ( dict
== 0 ) break;
238 zero
= OSNumber::withNumber((UInt64
) 0, 32);
239 if ( zero
== 0 ) break;
241 pathBuf
= (char *) IOMalloc( kMaxPathLen
);
242 if ( pathBuf
== 0 ) break;
245 if ( netif
->getPath( pathBuf
, &len
, gIOServicePlane
)
248 path
= OSString::withCStringNoCopy( pathBuf
);
249 if ( path
== 0 ) break;
251 dict
->setObject( "IOInterfaceUnit", zero
);
252 dict
->setObject( kIOPathMatchKey
, path
);
254 stack
->setProperties( dict
);
258 if ( zero
) zero
->release();
259 if ( path
) path
->release();
260 if ( dict
) dict
->release();
261 if ( pathBuf
) IOFree(pathBuf
, kMaxPathLen
);
263 return ( netif
->getProperty( kIOBSDNameKey
) != 0 );
266 OSDictionary
* IOOFPathMatching( const char * path
, char * buf
, int maxLen
)
268 OSDictionary
* matching
= NULL
;
275 len
= strlen( kIODeviceTreePlane
":" );
280 strlcpy( buf
, kIODeviceTreePlane
":", len
+ 1 );
283 len
= strlen( path
);
287 strlcpy( comp
, path
, len
+ 1 );
289 matching
= OSDictionary::withCapacity( 1 );
293 str
= OSString::withCString( buf
);
296 matching
->setObject( kIOPathMatchKey
, str
);
309 static int didRam
= 0;
310 enum { kMaxPathBuf
= 512, kMaxBootVar
= 128 };
312 kern_return_t
IOFindBSDRoot( char * rootName
, unsigned int rootNameSize
,
313 dev_t
* root
, u_int32_t
* oflags
)
317 IORegistryEntry
* regEntry
;
318 OSDictionary
* matching
= 0;
325 const char * mediaProperty
= 0;
328 const char * look
= 0;
330 bool debugInfoPrintedOnce
= false;
331 const char * uuidStr
= NULL
;
333 static int mountAttempts
= 0;
341 str
= (char *) IOMalloc( kMaxPathBuf
+ kMaxBootVar
);
343 return( kIOReturnNoMemory
);
344 rdBootVar
= str
+ kMaxPathBuf
;
346 if (!PE_parse_boot_argn("rd", rdBootVar
, kMaxBootVar
)
347 && !PE_parse_boot_argn("rootdev", rdBootVar
, kMaxBootVar
))
351 if( (regEntry
= IORegistryEntry::fromPath( "/chosen", gIODTPlane
))) {
352 di_root_ramfile(regEntry
);
353 data
= OSDynamicCast(OSData
, regEntry
->getProperty( "root-matching" ));
355 matching
= OSDynamicCast(OSDictionary
, OSUnserializeXML((char *)data
->getBytesNoCopy()));
361 data
= (OSData
*) regEntry
->getProperty( "boot-uuid" );
363 uuidStr
= (const char*)data
->getBytesNoCopy();
364 OSString
*uuidString
= OSString::withCString( uuidStr
);
366 // match the boot-args boot-uuid processing below
368 IOLog("rooting via boot-uuid from /chosen: %s\n", uuidStr
);
369 IOService::publishResource( "boot-uuid", uuidString
);
370 uuidString
->release();
371 matching
= IOUUIDMatching();
372 mediaProperty
= "boot-uuid-media";
384 // See if we have a RAMDisk property in /chosen/memory-map. If so, make it into a device.
385 // It will become /dev/mdx, where x is 0-f.
388 if(!didRam
) { /* Have we already build this ram disk? */
389 didRam
= 1; /* Remember we did this */
390 if((regEntry
= IORegistryEntry::fromPath( "/chosen/memory-map", gIODTPlane
))) { /* Find the map node */
391 data
= (OSData
*)regEntry
->getProperty("RAMDisk"); /* Find the ram disk, if there */
392 if(data
) { /* We found one */
393 uintptr_t *ramdParms
;
394 ramdParms
= (uintptr_t *)data
->getBytesNoCopy(); /* Point to the ram disk base and size */
395 (void)mdevadd(-1, ml_static_ptovirt(ramdParms
[0]) >> 12, ramdParms
[1] >> 12, 0); /* Initialize it and pass back the device number */
397 regEntry
->release(); /* Toss the entry */
402 // Now check if we are trying to root on a memory device
405 if((rdBootVar
[0] == 'm') && (rdBootVar
[1] == 'd') && (rdBootVar
[3] == 0)) {
406 dchar
= xchar
= rdBootVar
[2]; /* Get the actual device */
407 if((xchar
>= '0') && (xchar
<= '9')) xchar
= xchar
- '0'; /* If digit, convert */
409 xchar
= xchar
& ~' '; /* Fold to upper case */
410 if((xchar
>= 'A') && (xchar
<= 'F')) { /* Is this a valid digit? */
411 xchar
= (xchar
& 0xF) + 9; /* Convert the hex digit */
412 dchar
= dchar
| ' '; /* Fold to lower case */
414 else xchar
= -1; /* Show bogus */
416 if(xchar
>= 0) { /* Do we have a valid memory device name? */
417 *root
= mdevlookup(xchar
); /* Find the device number */
418 if(*root
>= 0) { /* Did we find one? */
420 rootName
[0] = 'm'; /* Build root name */
421 rootName
[1] = 'd'; /* Build root name */
422 rootName
[2] = dchar
; /* Build root name */
423 rootName
[3] = 0; /* Build root name */
424 IOLog("BSD root: %s, major %d, minor %d\n", rootName
, major(*root
), minor(*root
));
425 *oflags
= 0; /* Show that this is not network */
426 goto iofrootx
; /* Join common exit... */
428 panic("IOFindBSDRoot: specified root memory device, %s, has not been configured\n", rdBootVar
); /* Not there */
432 if( (!matching
) && rdBootVar
[0] ) {
438 if ( strncmp( look
, "en", strlen( "en" )) == 0 ) {
439 matching
= IONetworkNamePrefixMatching( "en" );
440 } else if ( strncmp( look
, "uuid", strlen( "uuid" )) == 0 ) {
442 OSString
*uuidString
;
444 uuid
= (char *)IOMalloc( kMaxBootVar
);
447 if (!PE_parse_boot_argn( "boot-uuid", uuid
, kMaxBootVar
)) {
448 panic( "rd=uuid but no boot-uuid=<value> specified" );
450 uuidString
= OSString::withCString( uuid
);
452 IOService::publishResource( "boot-uuid", uuidString
);
453 uuidString
->release();
454 IOLog( "\nWaiting for boot volume with UUID %s\n", uuid
);
455 matching
= IOUUIDMatching();
456 mediaProperty
= "boot-uuid-media";
458 IOFree( uuid
, kMaxBootVar
);
461 matching
= IOBSDNameMatching( look
);
467 // Match any HFS media
469 matching
= IOService::serviceMatching( "IOMedia" );
470 astring
= OSString::withCStringNoCopy("Apple_HFS");
472 matching
->setObject("Content", astring
);
477 if( gIOKitDebug
& kIOWaitQuietBeforeRoot
) {
478 IOLog( "Waiting for matching to complete\n" );
479 IOService::getPlatform()->waitQuiet();
482 if( true && matching
) {
483 OSSerialize
* s
= OSSerialize::withCapacity( 5 );
485 if( matching
->serialize( s
)) {
486 IOLog( "Waiting on %s\n", s
->text() );
492 t
.tv_sec
= ROOTDEVICETIMEOUT
;
495 service
= IOService::waitForService( matching
, &t
);
496 if( (!service
) || (mountAttempts
== 10)) {
497 PE_display_icon( 0, "noroot");
498 IOLog( "Still waiting for root device\n" );
500 if( !debugInfoPrintedOnce
) {
501 debugInfoPrintedOnce
= true;
502 if( gIOKitDebug
& kIOLogDTree
) {
503 IOLog("\nDT plane:\n");
504 IOPrintPlane( gIODTPlane
);
506 if( gIOKitDebug
& kIOLogServiceTree
) {
507 IOLog("\nService plane:\n");
508 IOPrintPlane( gIOServicePlane
);
510 if( gIOKitDebug
& kIOLogMemory
)
517 if ( service
&& mediaProperty
) {
518 service
= (IOService
*)service
->getProperty(mediaProperty
);
524 // If the IOService we matched to is a subclass of IONetworkInterface,
525 // then make sure it has been registered with BSD and has a BSD name
529 && service
->metaCast( "IONetworkInterface" )
530 && !IORegisterNetworkInterface( service
) )
538 service
->getPath( str
, &len
, gIOServicePlane
);
539 IOLog( "Got boot device = %s\n", str
);
541 iostr
= (OSString
*) service
->getProperty( kIOBSDNameKey
);
543 strlcpy( rootName
, iostr
->getCStringNoCopy(), rootNameSize
);
544 off
= (OSNumber
*) service
->getProperty( kIOBSDMajorKey
);
546 mjr
= off
->unsigned32BitValue();
547 off
= (OSNumber
*) service
->getProperty( kIOBSDMinorKey
);
549 mnr
= off
->unsigned32BitValue();
551 if( service
->metaCast( "IONetworkInterface" ))
556 IOLog( "Wait for root failed\n" );
557 strlcpy( rootName
, "en0", rootNameSize
);
561 IOLog( "BSD root: %s", rootName
);
563 IOLog(", major %d, minor %d\n", mjr
, mnr
);
567 *root
= makedev( mjr
, mnr
);
570 IOFree( str
, kMaxPathBuf
+ kMaxBootVar
);
573 if( (gIOKitDebug
& (kIOLogDTree
| kIOLogServiceTree
| kIOLogMemory
)) && !debugInfoPrintedOnce
) {
575 IOService::getPlatform()->waitQuiet();
576 if( gIOKitDebug
& kIOLogDTree
) {
577 IOLog("\nDT plane:\n");
578 IOPrintPlane( gIODTPlane
);
580 if( gIOKitDebug
& kIOLogServiceTree
) {
581 IOLog("\nService plane:\n");
582 IOPrintPlane( gIOServicePlane
);
584 if( gIOKitDebug
& kIOLogMemory
)
588 return( kIOReturnSuccess
);
591 bool IORamDiskBSDRoot(void)
593 char rdBootVar
[kMaxBootVar
];
594 if (PE_parse_boot_argn("rd", rdBootVar
, kMaxBootVar
)
595 || PE_parse_boot_argn("rootdev", rdBootVar
, kMaxBootVar
)) {
596 if((rdBootVar
[0] == 'm') && (rdBootVar
[1] == 'd') && (rdBootVar
[3] == 0)) {
603 void IOSecureBSDRoot(const char * rootName
)
608 IOBSDRegistryEntryForDeviceTree(char * path
)
610 return (IORegistryEntry::fromPath(path
, gIODTPlane
));
614 IOBSDRegistryEntryRelease(void * entry
)
616 IORegistryEntry
* regEntry
= (IORegistryEntry
*)entry
;
624 IOBSDRegistryEntryGetData(void * entry
, char * property_name
,
628 IORegistryEntry
* regEntry
= (IORegistryEntry
*)entry
;
630 data
= (OSData
*) regEntry
->getProperty(property_name
);
632 *packet_length
= data
->getLength();
633 return (data
->getBytesNoCopy());
638 kern_return_t
IOBSDGetPlatformUUID( uuid_t uuid
, mach_timespec_t timeout
)
640 IOService
* resources
;
643 resources
= IOService::waitForService( IOService::resourceMatching( kIOPlatformUUIDKey
), ( timeout
.tv_sec
|| timeout
.tv_nsec
) ? &timeout
: 0 );
644 if ( resources
== 0 ) return KERN_OPERATION_TIMED_OUT
;
646 string
= ( OSString
* ) IOService::getPlatform( )->getProvider( )->getProperty( kIOPlatformUUIDKey
);
647 if ( string
== 0 ) return KERN_NOT_SUPPORTED
;
649 uuid_parse( string
->getCStringNoCopy( ), uuid
);
654 kern_return_t
IOBSDGetPlatformSerialNumber( char *serial_number_str
, u_int32_t len
)
656 OSDictionary
* platform_dict
;
663 serial_number_str
[0] = '\0';
665 platform_dict
= IOService::serviceMatching( "IOPlatformExpertDevice" );
666 if (platform_dict
== NULL
) {
667 return KERN_NOT_SUPPORTED
;
670 platform
= IOService::waitForService( platform_dict
);
672 string
= ( OSString
* ) platform
->getProperty( kIOPlatformSerialNumberKey
);
674 return KERN_NOT_SUPPORTED
;
676 strlcpy( serial_number_str
, string
->getCStringNoCopy( ), len
);
683 void IOBSDIterateMediaWithContent(const char *content_uuid_cstring
, int (*func
)(const char *bsd_dev_name
, const char *uuid_str
, void *arg
), void *arg
)
685 OSDictionary
*dictionary
;
686 OSString
*content_uuid_string
;
688 dictionary
= IOService::serviceMatching( "IOMedia" );
690 content_uuid_string
= OSString::withCString( content_uuid_cstring
);
691 if( content_uuid_string
) {
695 dictionary
->setObject( "Content", content_uuid_string
);
696 dictionary
->retain();
698 iter
= IOService::getMatchingServices(dictionary
);
699 while (iter
&& (service
= (IOService
*)iter
->getNextObject())) {
701 OSString
*iostr
= (OSString
*) service
->getProperty( kIOBSDNameKey
);
702 OSString
*uuidstr
= (OSString
*) service
->getProperty( "UUID" );
707 uuid
= uuidstr
->getCStringNoCopy();
709 uuid
= "00000000-0000-0000-0000-000000000000";
713 if (func
&& func(iostr
->getCStringNoCopy(), uuid
, arg
) == 0) {
722 content_uuid_string
->release();
724 dictionary
->release();
729 int IOBSDIsMediaEjectable( const char *cdev_name
)
732 OSDictionary
*dictionary
;
735 if (strncmp(cdev_name
, "/dev/", 5) == 0) {
739 dictionary
= IOService::serviceMatching( "IOMedia" );
741 dev_name
= OSString::withCString( cdev_name
);
744 mach_timespec_t tv
= { 5, 0 }; // wait up to "timeout" seconds for the device
746 dictionary
->setObject( kIOBSDNameKey
, dev_name
);
747 dictionary
->retain();
748 service
= IOService::waitForService( dictionary
, &tv
);
750 OSBoolean
*ejectable
= (OSBoolean
*) service
->getProperty( "Ejectable" );
753 ret
= (int)ejectable
->getValue();
759 dictionary
->release();