1/* 2 * Copyright (c) 1998-2000 Apple Computer, Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 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. 11 * 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 14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 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. 19 * 20 * @APPLE_LICENSE_HEADER_END@ 21 */ 22#include <IOKit/IOBSD.h> 23#include <IOKit/IOLib.h> 24#include <IOKit/IOService.h> 25#include <IOKit/IODeviceTreeSupport.h> 26#include <IOKit/IOKitKeys.h> 27#include <IOKit/IOPlatformExpert.h> 28 29extern "C" { 30 31#include <pexpert/pexpert.h> 32#include <kern/clock.h> 33 34// how long to wait for matching root device, secs 35#define ROOTDEVICETIMEOUT 60 36 37extern dev_t mdevadd(int devid, ppnum_t base, unsigned int size, int phys); 38extern dev_t mdevlookup(int devid); 39 40kern_return_t 41IOKitBSDInit( void ) 42{ 43 IOService::publishResource("IOBSD"); 44 45 return( kIOReturnSuccess ); 46} 47 48OSDictionary * IOBSDNameMatching( const char * name ) 49{ 50 OSDictionary * dict; 51 const OSSymbol * str = 0; 52 53 do { 54 55 dict = IOService::serviceMatching( gIOServiceKey ); 56 if( !dict) 57 continue; 58 str = OSSymbol::withCString( name ); 59 if( !str) 60 continue; 61 dict->setObject( kIOBSDNameKey, (OSObject *) str ); 62 str->release(); 63 64 return( dict ); 65 66 } while( false ); 67 68 if( dict) 69 dict->release(); 70 if( str) 71 str->release(); 72 73 return( 0 ); 74} 75 76OSDictionary * IOUUIDMatching( void ) 77{ 78 return IOService::resourceMatching( "boot-uuid-media" ); 79} 80 81 82OSDictionary * IOCDMatching( void ) 83{ 84 OSDictionary * dict; 85 const OSSymbol * str; 86 87 dict = IOService::serviceMatching( "IOMedia" ); 88 if( dict == 0 ) { 89 IOLog("Unable to find IOMedia\n"); 90 return 0; 91 } 92 93 str = OSSymbol::withCString( "CD_ROM_Mode_1" ); 94 if( str == 0 ) { 95 dict->release(); 96 return 0; 97 } 98 99 dict->setObject( "Content Hint", (OSObject *)str ); 100 str->release(); 101 return( dict ); 102} 103 104OSDictionary * IONetworkMatching( const char * path, 105 char * buf, int maxLen ) 106{ 107 OSDictionary * matching = 0; 108 OSDictionary * dict; 109 OSString * str; 110 char * comp; 111 const char * skip; 112 int len; 113 114 do { 115 116 len = strlen( kIODeviceTreePlane ":" ); 117 maxLen -= len; 118 if( maxLen < 0) 119 continue; 120 121 strcpy( buf, kIODeviceTreePlane ":" ); 122 comp = buf + len; 123 124 // remove parameters following ':' from the path 125 skip = strchr( path, ':'); 126 if( !skip) 127 continue; 128 129 len = skip - path; 130 maxLen -= len; 131 if( maxLen < 0) 132 continue; 133 strncpy( comp, path, len ); 134 comp[ len ] = 0; 135 136 matching = IOService::serviceMatching( "IONetworkInterface" ); 137 if( !matching) 138 continue; 139 dict = IOService::addLocation( matching ); 140 if( !dict) 141 continue; 142 143 str = OSString::withCString( buf ); 144 if( !str) 145 continue; 146 dict->setObject( kIOPathMatchKey, str ); 147 str->release(); 148 149 return( matching ); 150 151 } while( false ); 152 153 if( matching) 154 matching->release(); 155 156 return( 0 ); 157} 158 159OSDictionary * IONetworkNamePrefixMatching( const char * prefix ) 160{ 161 OSDictionary * matching; 162 OSDictionary * propDict = 0; 163 const OSSymbol * str = 0; 164 165 do { 166 matching = IOService::serviceMatching( "IONetworkInterface" ); 167 if ( matching == 0 ) 168 continue; 169 170 propDict = OSDictionary::withCapacity(1); 171 if ( propDict == 0 ) 172 continue; 173 174 str = OSSymbol::withCString( prefix ); 175 if ( str == 0 ) 176 continue; 177 178 propDict->setObject( "IOInterfaceNamePrefix", (OSObject *) str ); 179 str->release(); 180 str = 0; 181 182 if ( matching->setObject( gIOPropertyMatchKey, 183 (OSObject *) propDict ) != true ) 184 continue; 185 186 propDict->release(); 187 propDict = 0; 188 189 return( matching ); 190 191 } while ( false ); 192 193 if ( matching ) matching->release(); 194 if ( propDict ) propDict->release(); 195 if ( str ) str->release(); 196 197 return( 0 ); 198} 199 200static bool IORegisterNetworkInterface( IOService * netif ) 201{ 202 // A network interface is typically named and registered 203 // with BSD after receiving a request from a user space 204 // "namer". However, for cases when the system needs to 205 // root from the network, this registration task must be 206 // done inside the kernel and completed before the root 207 // device is handed to BSD. 208 209 IOService * stack; 210 OSNumber * zero = 0; 211 OSString * path = 0; 212 OSDictionary * dict = 0; 213 char * pathBuf = 0; 214 int len; 215 enum { kMaxPathLen = 512 }; 216 217 do { 218 stack = IOService::waitForService( 219 IOService::serviceMatching("IONetworkStack") ); 220 if ( stack == 0 ) break; 221 222 dict = OSDictionary::withCapacity(3); 223 if ( dict == 0 ) break; 224 225 zero = OSNumber::withNumber((UInt64) 0, 32); 226 if ( zero == 0 ) break; 227 228 pathBuf = (char *) IOMalloc( kMaxPathLen ); 229 if ( pathBuf == 0 ) break; 230 231 len = kMaxPathLen; 232 if ( netif->getPath( pathBuf, &len, gIOServicePlane ) 233 == false ) break; 234 235 path = OSString::withCStringNoCopy( pathBuf ); 236 if ( path == 0 ) break; 237 238 dict->setObject( "IOInterfaceUnit", zero ); 239 dict->setObject( kIOPathMatchKey, path ); 240 241 stack->setProperties( dict ); 242 } 243 while ( false ); 244 245 if ( zero ) zero->release(); 246 if ( path ) path->release(); 247 if ( dict ) dict->release(); 248 if ( pathBuf ) IOFree(pathBuf, kMaxPathLen); 249 250 return ( netif->getProperty( kIOBSDNameKey ) != 0 ); 251} 252 253OSDictionary * IODiskMatching( const char * path, char * buf, int maxLen ) 254{ 255 const char * look; 256 const char * alias; 257 char * comp; 258 long unit = -1; 259 long partition = -1; 260 long lun = -1; 261 char c; 262 263 // scan the tail of the path for "@unit:partition" 264 do { 265 // Have to get the full path to the controller - an alias may 266 // tell us next to nothing, like "hd:8" 267 alias = IORegistryEntry::dealiasPath( &path, gIODTPlane ); 268 269 look = path + strlen( path); 270 c = ':'; 271 while( look != path) { 272 if( *(--look) == c) { 273 if( c == ':') { 274 partition = strtol( look + 1, 0, 0 ); 275 c = '@'; 276 } else if( c == '@') { 277 unit = strtol( look + 1, &comp, 16 ); 278 279 if( *comp == ',') { 280 lun = strtol( comp + 1, 0, 16 ); 281 } 282 283 c = '/'; 284 } else if( c == '/') { 285 c = 0; 286 break; 287 } 288 } 289 290 if( alias && (look == path)) { 291 path = alias; 292 look = path + strlen( path); 293 alias = 0; 294 } 295 } 296 if( c || unit == -1 || partition == -1) 297 continue; 298 299 maxLen -= strlen( "{" kIOPathMatchKey "='" kIODeviceTreePlane ":" ); 300 maxLen -= ( alias ? strlen( alias ) : 0 ) + (look - path); 301 maxLen -= strlen( "/@hhhhhhhh,hhhhhhhh:dddddddddd';}" ); 302 303 if( maxLen > 0) { 304 sprintf( buf, "{" kIOPathMatchKey "='" kIODeviceTreePlane ":" ); 305 comp = buf + strlen( buf ); 306 307 if( alias) { 308 strcpy( comp, alias ); 309 comp += strlen( alias ); 310 } 311 312 if ( (look - path)) { 313 strncpy( comp, path, look - path); 314 comp += look - path; 315 } 316 317 if ( lun != -1 ) 318 { 319 sprintf ( comp, "/@%lx,%lx:%ld';}", unit, lun, partition ); 320 } 321 else 322 { 323 sprintf( comp, "/@%lx:%ld';}", unit, partition ); 324 } 325 } else 326 continue; 327 328 return( OSDynamicCast(OSDictionary, OSUnserialize( buf, 0 )) ); 329 330 } while( false ); 331 332 return( 0 ); 333} 334 335OSDictionary * IOOFPathMatching( const char * path, char * buf, int maxLen ) 336{ 337 OSDictionary * matching; 338 OSString * str; 339 char * comp; 340 int len; 341 342 /* need to look up path, get device type, 343 call matching help based on device type */ 344 345 matching = IODiskMatching( path, buf, maxLen ); 346 if( matching) 347 return( matching ); 348 349 do { 350 351 len = strlen( kIODeviceTreePlane ":" ); 352 maxLen -= len; 353 if( maxLen < 0) 354 continue; 355 356 strcpy( buf, kIODeviceTreePlane ":" ); 357 comp = buf + len; 358 359 len = strlen( path ); 360 maxLen -= len; 361 if( maxLen < 0) 362 continue; 363 strncpy( comp, path, len ); 364 comp[ len ] = 0; 365 366 matching = OSDictionary::withCapacity( 1 ); 367 if( !matching) 368 continue; 369 370 str = OSString::withCString( buf ); 371 if( !str) 372 continue; 373 matching->setObject( kIOPathMatchKey, str ); 374 str->release(); 375 376 return( matching ); 377 378 } while( false ); 379 380 if( matching) 381 matching->release(); 382 383 return( 0 ); 384} 385 386IOService * IOFindMatchingChild( IOService * service ) 387{ 388 // find a matching child service 389 IOService * child = 0; 390 OSIterator * iter = service->getClientIterator(); 391 if ( iter ) { 392 while( ( child = (IOService *) iter->getNextObject() ) ) { 393 OSDictionary * dict = OSDictionary::withCapacity( 1 ); 394 if( dict == 0 ) { 395 iter->release(); 396 return 0; 397 } 398 const OSSymbol * str = OSSymbol::withCString( "Apple_HFS" ); 399 if( str == 0 ) { 400 dict->release(); 401 iter->release(); 402 return 0; 403 } 404 dict->setObject( "Content", (OSObject *)str ); 405 str->release(); 406 if ( child->compareProperty( dict, "Content" ) ) { 407 dict->release(); 408 break; 409 } 410 dict->release(); 411 IOService * subchild = IOFindMatchingChild( child ); 412 if ( subchild ) { 413 child = subchild; 414 break; 415 } 416 } 417 iter->release(); 418 } 419 return child; 420} 421 422static int didRam = 0; 423 424kern_return_t IOFindBSDRoot( char * rootName, 425 dev_t * root, u_int32_t * oflags ) 426{ 427 mach_timespec_t t; 428 IOService * service; 429 IORegistryEntry * regEntry; 430 OSDictionary * matching = 0; 431 OSString * iostr; 432 OSNumber * off; 433 OSData * data = 0; 434 UInt32 *ramdParms = 0; 435 436 UInt32 flags = 0; 437 int minor, major; 438 bool findHFSChild = false; 439 char * mediaProperty = 0; 440 char * rdBootVar; 441 enum { kMaxPathBuf = 512, kMaxBootVar = 128 }; 442 char * str; 443 const char * look = 0; 444 int len; 445 bool forceNet = false; 446 bool debugInfoPrintedOnce = false; 447 const char * uuidStr = NULL; 448 449 static int mountAttempts = 0; 450 451 int xchar, dchar; 452 453 454 if( mountAttempts++) 455 IOSleep( 5 * 1000 ); 456 457 str = (char *) IOMalloc( kMaxPathBuf + kMaxBootVar ); 458 if( !str) 459 return( kIOReturnNoMemory ); 460 rdBootVar = str + kMaxPathBuf; 461 462 if (!PE_parse_boot_arg("rd", rdBootVar ) 463 && !PE_parse_boot_arg("rootdev", rdBootVar )) 464 rdBootVar[0] = 0; 465 466 do { 467 if( (regEntry = IORegistryEntry::fromPath( "/chosen", gIODTPlane ))) { 468 data = (OSData *) regEntry->getProperty( "boot-uuid" ); 469 if( data) { 470 uuidStr = (const char*)data->getBytesNoCopy(); 471 OSString *uuidString = OSString::withCString( uuidStr ); 472 473 // match the boot-args boot-uuid processing below 474 if( uuidString) { 475 IOLog("rooting via boot-uuid from /chosen: %s\n", uuidStr); 476 IOService::publishResource( "boot-uuid", uuidString ); 477 uuidString->release(); 478 matching = IOUUIDMatching(); 479 mediaProperty = "boot-uuid-media"; 480 regEntry->release(); 481 continue; 482 } else { 483 uuidStr = NULL; 484 } 485 } 486 487 // else try for an OF Path 488 data = (OSData *) regEntry->getProperty( "rootpath" ); 489 regEntry->release(); 490 if( data) continue; 491 } 492 if( (regEntry = IORegistryEntry::fromPath( "/options", gIODTPlane ))) { 493 data = (OSData *) regEntry->getProperty( "boot-file" ); 494 regEntry->release(); 495 if( data) continue; 496 } 497 } while( false ); 498 499 if( data && !uuidStr) 500 look = (const char *) data->getBytesNoCopy(); 501 502 if( rdBootVar[0] == '*') { 503 look = rdBootVar + 1; 504 forceNet = false; 505 } else { 506 if( (regEntry = IORegistryEntry::fromPath( "/", gIODTPlane ))) { 507 forceNet = (0 != regEntry->getProperty( "net-boot" )); 508 regEntry->release(); 509 } 510 } 511 512 513 514// 515// See if we have a RAMDisk property in /chosen/memory-map. If so, make it into a device. 516// It will become /dev/mdx, where x is 0-f. 517// 518 519 if(!didRam) { /* Have we already build this ram disk? */ 520 didRam = 1; /* Remember we did this */ 521 if((regEntry = IORegistryEntry::fromPath( "/chosen/memory-map", gIODTPlane ))) { /* Find the map node */ 522 data = (OSData *)regEntry->getProperty("RAMDisk"); /* Find the ram disk, if there */ 523 if(data) { /* We found one */ 524 525 ramdParms = (UInt32 *)data->getBytesNoCopy(); /* Point to the ram disk base and size */ 526 (void)mdevadd(-1, ramdParms[0] >> 12, ramdParms[1] >> 12, 0); /* Initialize it and pass back the device number */ 527 } 528 regEntry->release(); /* Toss the entry */ 529 } 530 } 531 532// 533// Now check if we are trying to root on a memory device 534// 535 536 if((rdBootVar[0] == 'm') && (rdBootVar[1] == 'd') && (rdBootVar[3] == 0)) { 537 dchar = xchar = rdBootVar[2]; /* Get the actual device */ 538 if((xchar >= '0') && (xchar <= '9')) xchar = xchar - '0'; /* If digit, convert */ 539 else { 540 xchar = xchar & ~' '; /* Fold to upper case */ 541 if((xchar >= 'A') && (xchar <= 'F')) { /* Is this a valid digit? */ 542 xchar = (xchar & 0xF) + 9; /* Convert the hex digit */ 543 dchar = dchar | ' '; /* Fold to lower case */ 544 } 545 else xchar = -1; /* Show bogus */ 546 } 547 if(xchar >= 0) { /* Do we have a valid memory device name? */ 548 *root = mdevlookup(xchar); /* Find the device number */ 549 if(*root >= 0) { /* Did we find one? */ 550 551 rootName[0] = 'm'; /* Build root name */ 552 rootName[1] = 'd'; /* Build root name */ 553 rootName[2] = dchar; /* Build root name */ 554 rootName[3] = 0; /* Build root name */ 555 IOLog("BSD root: %s, major %d, minor %d\n", rootName, major(*root), minor(*root)); 556 *oflags = 0; /* Show that this is not network */ 557 goto iofrootx; /* Join common exit... */ 558 } 559 panic("IOFindBSDRoot: specified root memory device, %s, has not been configured\n", rdBootVar); /* Not there */ 560 } 561 } 562 563 if( look) { 564 // from OpenFirmware path 565 IOLog("From path: \"%s\", ", look); 566 567 if( forceNet || (0 == strncmp( look, "enet", strlen( "enet" ))) ) { 568 matching = IONetworkMatching( look, str, kMaxPathBuf ); 569 } else { 570 matching = IODiskMatching( look, str, kMaxPathBuf ); 571 } 572 } 573 574 if( (!matching) && rdBootVar[0] ) { 575 // by BSD name 576 look = rdBootVar; 577 if( look[0] == '*') 578 look++; 579 580 if ( strncmp( look, "en", strlen( "en" )) == 0 ) { 581 matching = IONetworkNamePrefixMatching( "en" ); 582 } else if ( strncmp( look, "cdrom", strlen( "cdrom" )) == 0 ) { 583 matching = IOCDMatching(); 584 findHFSChild = true; 585 } else if ( strncmp( look, "uuid", strlen( "uuid" )) == 0 ) { 586 char *uuid; 587 OSString *uuidString; 588 589 uuid = (char *)IOMalloc( kMaxBootVar ); 590 591 if ( uuid ) { 592 if (!PE_parse_boot_arg( "boot-uuid", uuid )) { 593 panic( "rd=uuid but no boot-uuid=<value> specified" ); 594 } 595 uuidString = OSString::withCString( uuid ); 596 if ( uuidString ) { 597 IOService::publishResource( "boot-uuid", uuidString ); 598 uuidString->release(); 599 IOLog( "\nWaiting for boot volume with UUID %s\n", uuid ); 600 matching = IOUUIDMatching(); 601 mediaProperty = "boot-uuid-media"; 602 } 603 IOFree( uuid, kMaxBootVar ); 604 } 605 } else { 606 matching = IOBSDNameMatching( look ); 607 } 608 } 609 610 if( !matching) { 611 OSString * astring; 612 // any HFS 613 matching = IOService::serviceMatching( "IOMedia" ); 614 astring = OSString::withCStringNoCopy("Apple_HFS"); 615 if ( astring ) { 616 matching->setObject("Content", astring); 617 astring->release(); 618 } 619 } 620 621 if( true && matching) { 622 OSSerialize * s = OSSerialize::withCapacity( 5 ); 623 624 if( matching->serialize( s )) { 625 IOLog( "Waiting on %s\n", s->text() ); 626 s->release(); 627 } 628 } 629 630 do { 631 t.tv_sec = ROOTDEVICETIMEOUT; 632 t.tv_nsec = 0; 633 matching->retain(); 634 service = IOService::waitForService( matching, &t ); 635 if( (!service) || (mountAttempts == 10)) { 636 PE_display_icon( 0, "noroot"); 637 IOLog( "Still waiting for root device\n" ); 638 639 if( !debugInfoPrintedOnce) { 640 debugInfoPrintedOnce = true; 641 if( gIOKitDebug & kIOLogDTree) { 642 IOLog("\nDT plane:\n"); 643 IOPrintPlane( gIODTPlane ); 644 } 645 if( gIOKitDebug & kIOLogServiceTree) { 646 IOLog("\nService plane:\n"); 647 IOPrintPlane( gIOServicePlane ); 648 } 649 if( gIOKitDebug & kIOLogMemory) 650 IOPrintMemory(); 651 } 652 } 653 } while( !service); 654 matching->release(); 655 656 if ( service && findHFSChild ) { 657 bool waiting = true; 658 // wait for children services to finish registering 659 while ( waiting ) { 660 t.tv_sec = ROOTDEVICETIMEOUT; 661 t.tv_nsec = 0; 662 if ( service->waitQuiet( &t ) == kIOReturnSuccess ) { 663 waiting = false; 664 } else { 665 IOLog( "Waiting for child registration\n" ); 666 } 667 } 668 // look for a subservice with an Apple_HFS child 669 IOService * subservice = IOFindMatchingChild( service ); 670 if ( subservice ) service = subservice; 671 } else if ( service && mediaProperty ) { 672 service = service->getProperty(mediaProperty); 673 } 674 675 major = 0; 676 minor = 0; 677 678 // If the IOService we matched to is a subclass of IONetworkInterface, 679 // then make sure it has been registered with BSD and has a BSD name 680 // assigned. 681 682 if ( service 683 && service->metaCast( "IONetworkInterface" ) 684 && !IORegisterNetworkInterface( service ) ) 685 { 686 service = 0; 687 } 688 689 if( service) { 690 691 len = kMaxPathBuf; 692 service->getPath( str, &len, gIOServicePlane ); 693 IOLog( "Got boot device = %s\n", str ); 694 695 iostr = (OSString *) service->getProperty( kIOBSDNameKey ); 696 if( iostr) 697 strcpy( rootName, iostr->getCStringNoCopy() ); 698 off = (OSNumber *) service->getProperty( kIOBSDMajorKey ); 699 if( off) 700 major = off->unsigned32BitValue(); 701 off = (OSNumber *) service->getProperty( kIOBSDMinorKey ); 702 if( off) 703 minor = off->unsigned32BitValue(); 704 705 if( service->metaCast( "IONetworkInterface" )) 706 flags |= 1; 707 708 } else { 709 710 IOLog( "Wait for root failed\n" ); 711 strcpy( rootName, "en0"); 712 flags |= 1; 713 } 714 715 IOLog( "BSD root: %s", rootName ); 716 if( major) 717 IOLog(", major %d, minor %d\n", major, minor ); 718 else 719 IOLog("\n"); 720 721 *root = makedev( major, minor ); 722 *oflags = flags; 723 724 IOFree( str, kMaxPathBuf + kMaxBootVar ); 725 726iofrootx: 727 if( (gIOKitDebug & (kIOLogDTree | kIOLogServiceTree | kIOLogMemory)) && !debugInfoPrintedOnce) { 728 729 IOService::getPlatform()->waitQuiet(); 730 if( gIOKitDebug & kIOLogDTree) { 731 IOLog("\nDT plane:\n"); 732 IOPrintPlane( gIODTPlane ); 733 } 734 if( gIOKitDebug & kIOLogServiceTree) { 735 IOLog("\nService plane:\n"); 736 IOPrintPlane( gIOServicePlane ); 737 } 738 if( gIOKitDebug & kIOLogMemory) 739 IOPrintMemory(); 740 } 741 742 return( kIOReturnSuccess ); 743} 744 745void * 746IOBSDRegistryEntryForDeviceTree(char * path) 747{ 748 return (IORegistryEntry::fromPath(path, gIODTPlane)); 749} 750 751void 752IOBSDRegistryEntryRelease(void * entry) 753{ 754 IORegistryEntry * regEntry = (IORegistryEntry *)entry; 755 756 if (regEntry) 757 regEntry->release(); 758 return; 759} 760 761const void * 762IOBSDRegistryEntryGetData(void * entry, char * property_name, 763 int * packet_length) 764{ 765 OSData * data; 766 IORegistryEntry * regEntry = (IORegistryEntry *)entry; 767 768 data = (OSData *) regEntry->getProperty(property_name); 769 if (data) { 770 *packet_length = data->getLength(); 771 return (data->getBytesNoCopy()); 772 } 773 return (NULL); 774} 775 776} /* extern "C" */ 777

