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 29 #include <sys/disklabel.h> 30 31 extern "C" { 32 33 #include <pexpert/pexpert.h> 34 #include <kern/clock.h> 35 36 // how long to wait for matching root device, secs 37 #define ROOTDEVICETIMEOUT 60 38 39 40 kern_return_t 41 IOKitBSDInit( void ) 42 { 43 IOLog("IOKitBSDInit\n"); 44 45 IOService::publishResource("IOBSD"); 46 47 return( kIOReturnSuccess ); 48 } 49 50 OSDictionary * IOBSDNameMatching( const char * name ) 51 { 52 OSDictionary * dict; 53 const OSSymbol * str = 0; 54 55 do { 56 57 dict = IOService::serviceMatching( gIOServiceKey ); 58 if( !dict) 59 continue; 60 str = OSSymbol::withCString( name ); 61 if( !str) 62 continue; 63 dict->setObject( kIOBSDNameKey, (OSObject *) str ); 64 str->release(); 65 66 return( dict ); 67 68 } while( false ); 69 70 if( dict) 71 dict->release(); 72 if( str) 73 str->release(); 74 75 return( 0 ); 76 } 77 78 OSDictionary * IOCDMatching( const char * name ) 79 { 80 OSDictionary * dict; 81 const OSSymbol * str; 82 83 dict = IOService::serviceMatching( "IOMedia" ); 84 if( dict == 0 ) { 85 IOLog("Unable to find IOMedia\n"); 86 return 0; 87 } 88 89 str = OSSymbol::withCString( "CD_ROM_Mode_1" ); 90 if( str == 0 ) { 91 dict->release(); 92 return 0; 93 } 94 95 dict->setObject( "Content", (OSObject *)str ); 96 str->release(); 97 return( dict ); 98 } 99 100 OSDictionary * IONetworkMatching( const char * path, 101 char * buf, int maxLen ) 102 { 103 OSDictionary * matching = 0; 104 OSDictionary * dict; 105 OSString * str; 106 char * comp; 107 const char * skip; 108 int len; 109 110 do { 111 112 len = strlen( kIODeviceTreePlane ":" ); 113 maxLen -= len; 114 if( maxLen < 0) 115 continue; 116 117 strcpy( buf, kIODeviceTreePlane ":" ); 118 comp = buf + len; 119 120 // remove parameters following ':' from the path 121 skip = strchr( path, ':'); 122 if( !skip) 123 continue; 124 125 len = skip - path; 126 maxLen -= len; 127 if( maxLen < 0) 128 continue; 129 strncpy( comp, path, len ); 130 comp[ len ] = 0; 131 132 matching = IOService::serviceMatching( "IONetworkInterface" ); 133 if( !matching) 134 continue; 135 dict = IOService::addLocation( matching ); 136 if( !dict) 137 continue; 138 139 str = OSString::withCString( buf ); 140 if( !str) 141 continue; 142 dict->setObject( kIOPathMatchKey, str ); 143 str->release(); 144 145 return( matching ); 146 147 } while( false ); 148 149 if( matching) 150 matching->release(); 151 152 return( 0 ); 153 } 154 155 OSDictionary * IONetworkNamePrefixMatching( const char * prefix ) 156 { 157 OSDictionary * matching; 158 OSDictionary * propDict = 0; 159 const OSSymbol * str = 0; 160 161 do { 162 matching = IOService::serviceMatching( "IONetworkInterface" ); 163 if ( matching == 0 ) 164 continue; 165 166 propDict = OSDictionary::withCapacity(1); 167 if ( propDict == 0 ) 168 continue; 169 170 str = OSSymbol::withCString( prefix ); 171 if ( str == 0 ) 172 continue; 173 174 propDict->setObject( "IOInterfaceNamePrefix", (OSObject *) str ); 175 str->release(); 176 str = 0; 177 178 if ( matching->setObject( gIOPropertyMatchKey, 179 (OSObject *) propDict ) != true ) 180 continue; 181 182 propDict->release(); 183 propDict = 0; 184 185 return( matching ); 186 187 } while ( false ); 188 189 if ( matching ) matching->release(); 190 if ( propDict ) propDict->release(); 191 if ( str ) str->release(); 192 193 return( 0 ); 194 } 195 196 static bool IORegisterNetworkInterface( IOService * netif ) 197 { 198 // A network interface is typically named and registered 199 // with BSD after receiving a request from a user space 200 // "namer". However, for cases when the system needs to 201 // root from the network, this registration task must be 202 // done inside the kernel and completed before the root 203 // device is handed to BSD. 204 205 IOService * stack; 206 OSNumber * zero = 0; 207 OSString * path = 0; 208 OSDictionary * dict = 0; 209 char * pathBuf = 0; 210 int len; 211 enum { kMaxPathLen = 512 }; 212 213 do { 214 stack = IOService::waitForService( 215 IOService::serviceMatching("IONetworkStack") ); 216 if ( stack == 0 ) break; 217 218 dict = OSDictionary::withCapacity(3); 219 if ( dict == 0 ) break; 220 221 zero = OSNumber::withNumber((UInt64) 0, 32); 222 if ( zero == 0 ) break; 223 224 pathBuf = (char *) IOMalloc( kMaxPathLen ); 225 if ( pathBuf == 0 ) break; 226 227 len = kMaxPathLen; 228 if ( netif->getPath( pathBuf, &len, gIOServicePlane ) 229 == false ) break; 230 231 path = OSString::withCStringNoCopy( pathBuf ); 232 if ( path == 0 ) break; 233 234 dict->setObject( "IOInterfaceUnit", zero ); 235 dict->setObject( kIOPathMatchKey, path ); 236 237 stack->setProperties( dict ); 238 } 239 while ( false ); 240 241 if ( zero ) zero->release(); 242 if ( path ) path->release(); 243 if ( dict ) dict->release(); 244 if ( pathBuf ) IOFree(pathBuf, kMaxPathLen); 245 246 return ( netif->getProperty( kIOBSDNameKey ) != 0 ); 247 } 248 249 OSDictionary * IODiskMatching( const char * path, char * buf, int maxLen ) 250 { 251 const char * look; 252 const char * alias; 253 char * comp; 254 long unit = -1; 255 long partition = -1; 256 char c; 257 258 // scan the tail of the path for "@unit:partition" 259 do { 260 // Have to get the full path to the controller - an alias may 261 // tell us next to nothing, like "hd:8" 262 alias = IORegistryEntry::dealiasPath( &path, gIODTPlane ); 263 264 look = path + strlen( path); 265 c = ':'; 266 while( look != path) { 267 if( *(--look) == c) { 268 if( c == ':') { 269 partition = strtol( look + 1, 0, 0 ); 270 c = '@'; 271 } else if( c == '@') { 272 unit = strtol( look + 1, 0, 16 ); 273 c = '/'; 274 } else if( c == '/') { 275 c = 0; 276 break; 277 } 278 } 279 280 if( alias && (look == path)) { 281 path = alias; 282 look = path + strlen( path); 283 alias = 0; 284 } 285 } 286 if( c || unit == -1 || partition == -1) 287 continue; 288 289 maxLen -= strlen( "{" kIOPathMatchKey "='" kIODeviceTreePlane ":" ); 290 maxLen -= ( alias ? strlen( alias ) : 0 ) + (look - path); 291 maxLen -= strlen( "/@hhhhhhhh:dddddddddd';}" ); 292 293 if( maxLen > 0) { 294 sprintf( buf, "{" kIOPathMatchKey "='" kIODeviceTreePlane ":" ); 295 comp = buf + strlen( buf ); 296 297 if( alias) { 298 strcpy( comp, alias ); 299 comp += strlen( alias ); 300 } 301 302 if ( (look - path)) { 303 strncpy( comp, path, look - path); 304 comp += look - path; 305 } 306 307 sprintf( comp, "/@%lx:%ld';}", unit, partition ); 308 } else 309 continue; 310 311 return( OSDynamicCast(OSDictionary, OSUnserialize( buf, 0 )) ); 312 313 } while( false ); 314 315 return( 0 ); 316 } 317 318 OSDictionary * IOOFPathMatching( const char * path, char * buf, int maxLen ) 319 { 320 /* need to look up path, get device type, 321 call matching help based on device type */ 322 323 return( IODiskMatching( path, buf, maxLen )); 324 325 } 326 327 kern_return_t IOFindBSDRoot( char * rootName, 328 dev_t * root, u_int32_t * oflags ) 329 { 330 mach_timespec_t t; 331 IOService * service; 332 IORegistryEntry * regEntry; 333 OSDictionary * matching = 0; 334 OSString * iostr; 335 OSNumber * off; 336 OSData * data = 0; 337 338 UInt32 flags = 0; 339 int minor, major; 340 char * rdBootVar; 341 enum { kMaxPathBuf = 512, kMaxBootVar = 128 }; 342 char * str; 343 const char * look = 0; 344 int len; 345 bool forceNet = false; 346 bool debugInfoPrintedOnce = false; 347 348 static int mountAttempts = 0; 349 350 if( mountAttempts++) 351 IOSleep( 5 * 1000 ); 352 353 str = (char *) IOMalloc( kMaxPathBuf + kMaxBootVar ); 354 if( !str) 355 return( kIOReturnNoMemory ); 356 rdBootVar = str + kMaxPathBuf; 357 358 if (!PE_parse_boot_arg("rd", rdBootVar ) 359 && !PE_parse_boot_arg("rootdev", rdBootVar )) 360 rdBootVar[0] = 0; 361 362 do { 363 if( (regEntry = IORegistryEntry::fromPath( "/chosen", gIODTPlane ))) { 364 data = (OSData *) regEntry->getProperty( "rootpath" ); 365 regEntry->release(); 366 if( data) 367 continue; 368 } 369 if( (regEntry = IORegistryEntry::fromPath( "/options", gIODTPlane ))) { 370 data = (OSData *) regEntry->getProperty( "boot-file" ); 371 regEntry->release(); 372 if( data) 373 continue; 374 } 375 } while( false ); 376 377 if( data) 378 look = (const char *) data->getBytesNoCopy(); 379 380 if( rdBootVar[0] == '*') { 381 look = rdBootVar + 1; 382 forceNet = false; 383 } else { 384 if( (regEntry = IORegistryEntry::fromPath( "/", gIODTPlane ))) { 385 forceNet = (0 != regEntry->getProperty( "net-boot" )); 386 regEntry->release(); 387 } 388 } 389 390 if( look) { 391 // from OpenFirmware path 392 IOLog("From path: \"%s\", ", look); 393 394 if( forceNet || (0 == strncmp( look, "enet", strlen( "enet" ))) ) { 395 matching = IONetworkMatching( look, str, kMaxPathBuf ); 396 } else { 397 matching = IODiskMatching( look, str, kMaxPathBuf ); 398 } 399 } 400 401 if( (!matching) && rdBootVar[0] ) { 402 // by BSD name 403 look = rdBootVar; 404 if( look[0] == '*') 405 look++; 406 407 if ( strncmp( look, "en", strlen( "en" )) == 0 ) { 408 matching = IONetworkNamePrefixMatching( "en" ); 409 } else if ( strncmp( look, "cdrom", strlen( "cdrom" )) == 0 ) { 410 matching = IOCDMatching( look ); 411 } else { 412 matching = IOBSDNameMatching( look ); 413 } 414 } 415 416 if( !matching) { 417 OSString * astring; 418 // any UFS 419 matching = IOService::serviceMatching( "IOMedia" ); 420 astring = OSString::withCStringNoCopy("Apple_UFS"); 421 if ( astring ) { 422 matching->setObject("Content", astring); 423 astring->release(); 424 } 425 } 426 427 if( true && matching) { 428 OSSerialize * s = OSSerialize::withCapacity( 5 ); 429 430 if( matching->serialize( s )) { 431 IOLog( "Waiting on %s\n", s->text() ); 432 s->release(); 433 } 434 } 435 436 do { 437 t.tv_sec = ROOTDEVICETIMEOUT; 438 t.tv_nsec = 0; 439 matching->retain(); 440 service = IOService::waitForService( matching, &t ); 441 if( (!service) || (mountAttempts == 10)) { 442 PE_display_icon( 0, "noroot"); 443 IOLog( "Still waiting for root device\n" ); 444 445 if( !debugInfoPrintedOnce) { 446 debugInfoPrintedOnce = true; 447 if( gIOKitDebug & kIOLogDTree) { 448 IOLog("\nDT plane:\n"); 449 IOPrintPlane( gIODTPlane ); 450 } 451 if( gIOKitDebug & kIOLogServiceTree) { 452 IOLog("\nService plane:\n"); 453 IOPrintPlane( gIOServicePlane ); 454 } 455 if( gIOKitDebug & kIOLogMemory) 456 IOPrintMemory(); 457 } 458 } 459 } while( !service); 460 matching->release(); 461 462 major = 0; 463 minor = 0; 464 465 // If the IOService we matched to is a subclass of IONetworkInterface, 466 // then make sure it has been registered with BSD and has a BSD name 467 // assigned. 468 469 if ( service 470 && service->metaCast( "IONetworkInterface" ) 471 && !IORegisterNetworkInterface( service ) ) 472 { 473 service = 0; 474 } 475 476 if( service) { 477 478 len = kMaxPathBuf; 479 service->getPath( str, &len, gIOServicePlane ); 480 IOLog( "Got boot device = %s\n", str ); 481 482 iostr = (OSString *) service->getProperty( kIOBSDNameKey ); 483 if( iostr) 484 strcpy( rootName, iostr->getCStringNoCopy() ); 485 off = (OSNumber *) service->getProperty( kIOBSDMajorKey ); 486 if( off) 487 major = off->unsigned32BitValue(); 488 off = (OSNumber *) service->getProperty( kIOBSDMinorKey ); 489 if( off) 490 minor = off->unsigned32BitValue(); 491 492 if( service->metaCast( "IONetworkInterface" )) 493 flags |= 1; 494 495 } else { 496 497 IOLog( "Wait for root failed\n" ); 498 strcpy( rootName, "en0"); 499 flags |= 1; 500 } 501 502 IOLog( "BSD root: %s", rootName ); 503 if( major) 504 IOLog(", major %d, minor %d\n", major, minor ); 505 else 506 IOLog("\n"); 507 508 *root = makedev( major, minor ); 509 *oflags = flags; 510 511 IOFree( str, kMaxPathBuf + kMaxBootVar ); 512 513 if( (gIOKitDebug & (kIOLogDTree | kIOLogServiceTree | kIOLogMemory)) && !debugInfoPrintedOnce) { 514 515 IOService::getPlatform()->waitQuiet(); 516 if( gIOKitDebug & kIOLogDTree) { 517 IOLog("\nDT plane:\n"); 518 IOPrintPlane( gIODTPlane ); 519 } 520 if( gIOKitDebug & kIOLogServiceTree) { 521 IOLog("\nService plane:\n"); 522 IOPrintPlane( gIOServicePlane ); 523 } 524 if( gIOKitDebug & kIOLogMemory) 525 IOPrintMemory(); 526 } 527 528 return( kIOReturnSuccess ); 529 } 530 531 } /* extern "C" */ 532