| 1 | /* |
| 2 | * Copyright (c) 1998-2011 Apple Inc. All rights reserved. |
| 3 | * |
| 4 | * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ |
| 5 | * |
| 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. |
| 14 | * |
| 15 | * Please obtain a copy of the License at |
| 16 | * http://www.opensource.apple.com/apsl/ and read it before using this file. |
| 17 | * |
| 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. |
| 25 | * |
| 26 | * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ |
| 27 | */ |
| 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> |
| 35 | #include <IOKit/IOUserClient.h> |
| 36 | |
| 37 | extern "C" { |
| 38 | |
| 39 | #include <pexpert/pexpert.h> |
| 40 | #include <kern/clock.h> |
| 41 | #include <mach/machine.h> |
| 42 | #include <uuid/uuid.h> |
| 43 | #include <sys/vnode_internal.h> |
| 44 | #include <sys/mount.h> |
| 45 | |
| 46 | // how long to wait for matching root device, secs |
| 47 | #if DEBUG |
| 48 | #define ROOTDEVICETIMEOUT 120 |
| 49 | #else |
| 50 | #define ROOTDEVICETIMEOUT 60 |
| 51 | #endif |
| 52 | |
| 53 | int panic_on_exception_triage = 0; |
| 54 | |
| 55 | extern dev_t mdevadd(int devid, uint64_t base, unsigned int size, int phys); |
| 56 | extern dev_t mdevlookup(int devid); |
| 57 | extern void mdevremoveall(void); |
| 58 | extern int mdevgetrange(int devid, uint64_t *base, uint64_t *size); |
| 59 | extern void di_root_ramfile(IORegistryEntry * entry); |
| 60 | |
| 61 | #if CONFIG_EMBEDDED |
| 62 | #define IOPOLLED_COREFILE (CONFIG_KDP_INTERACTIVE_DEBUGGING) |
| 63 | |
| 64 | #if defined(XNU_TARGET_OS_BRIDGE) |
| 65 | |
| 66 | #define kIOCoreDumpSize 150ULL*1024ULL*1024ULL |
| 67 | // leave free space on volume: |
| 68 | #define kIOCoreDumpFreeSize 150ULL*1024ULL*1024ULL |
| 69 | #define kIOCoreDumpPath "/private/var/internal/kernelcore" |
| 70 | |
| 71 | #else /* defined(XNU_TARGET_OS_BRIDGE) */ |
| 72 | #define kIOCoreDumpMinSize 350ULL*1024ULL*1024ULL |
| 73 | #define kIOCoreDumpLargeSize 500ULL*1024ULL*1024ULL |
| 74 | // leave free space on volume: |
| 75 | #define kIOCoreDumpFreeSize 350ULL*1024ULL*1024ULL |
| 76 | #define kIOCoreDumpPath "/private/var/vm/kernelcore" |
| 77 | |
| 78 | #endif /* defined(XNU_TARGET_OS_BRIDGE) */ |
| 79 | |
| 80 | #elif DEVELOPMENT /* CONFIG_EMBEDDED */ |
| 81 | #define IOPOLLED_COREFILE 1 |
| 82 | // no sizing |
| 83 | #define kIOCoreDumpSize 0ULL |
| 84 | #define kIOCoreDumpFreeSize 0ULL |
| 85 | #else /* CONFIG_EMBEDDED */ |
| 86 | #define IOPOLLED_COREFILE 0 |
| 87 | #endif /* CONFIG_EMBEDDED */ |
| 88 | |
| 89 | |
| 90 | #if IOPOLLED_COREFILE |
| 91 | static bool |
| 92 | NewKernelCoreMedia(void * target, void * refCon, |
| 93 | IOService * newService, |
| 94 | IONotifier * notifier); |
| 95 | #endif /* IOPOLLED_COREFILE */ |
| 96 | |
| 97 | #if CONFIG_KDP_INTERACTIVE_DEBUGGING |
| 98 | /* |
| 99 | * Touched by IOFindBSDRoot() if a RAMDisk is used for the root device. |
| 100 | */ |
| 101 | extern uint64_t kdp_core_ramdisk_addr; |
| 102 | extern uint64_t kdp_core_ramdisk_size; |
| 103 | #endif |
| 104 | |
| 105 | kern_return_t |
| 106 | IOKitBSDInit( void ) |
| 107 | { |
| 108 | IOService::publishResource("IOBSD" ); |
| 109 | |
| 110 | return( kIOReturnSuccess ); |
| 111 | } |
| 112 | |
| 113 | void |
| 114 | IOServicePublishResource( const char * property, boolean_t value ) |
| 115 | { |
| 116 | if ( value) |
| 117 | IOService::publishResource( property, kOSBooleanTrue ); |
| 118 | else |
| 119 | IOService::getResourceService()->removeProperty( property ); |
| 120 | } |
| 121 | |
| 122 | boolean_t |
| 123 | IOServiceWaitForMatchingResource( const char * property, uint64_t timeout ) |
| 124 | { |
| 125 | OSDictionary * dict = 0; |
| 126 | IOService * match = 0; |
| 127 | boolean_t found = false; |
| 128 | |
| 129 | do { |
| 130 | |
| 131 | dict = IOService::resourceMatching( property ); |
| 132 | if( !dict) |
| 133 | continue; |
| 134 | match = IOService::waitForMatchingService( dict, timeout ); |
| 135 | if ( match) |
| 136 | found = true; |
| 137 | |
| 138 | } while( false ); |
| 139 | |
| 140 | if( dict) |
| 141 | dict->release(); |
| 142 | if( match) |
| 143 | match->release(); |
| 144 | |
| 145 | return( found ); |
| 146 | } |
| 147 | |
| 148 | boolean_t |
| 149 | IOCatalogueMatchingDriversPresent( const char * property ) |
| 150 | { |
| 151 | OSDictionary * dict = 0; |
| 152 | OSOrderedSet * set = 0; |
| 153 | SInt32 generationCount = 0; |
| 154 | boolean_t found = false; |
| 155 | |
| 156 | do { |
| 157 | |
| 158 | dict = OSDictionary::withCapacity(1); |
| 159 | if( !dict) |
| 160 | continue; |
| 161 | dict->setObject( property, kOSBooleanTrue ); |
| 162 | set = gIOCatalogue->findDrivers( dict, &generationCount ); |
| 163 | if ( set && (set->getCount() > 0)) |
| 164 | found = true; |
| 165 | |
| 166 | } while( false ); |
| 167 | |
| 168 | if( dict) |
| 169 | dict->release(); |
| 170 | if( set) |
| 171 | set->release(); |
| 172 | |
| 173 | return( found ); |
| 174 | } |
| 175 | |
| 176 | OSDictionary * IOBSDNameMatching( const char * name ) |
| 177 | { |
| 178 | OSDictionary * dict; |
| 179 | const OSSymbol * str = 0; |
| 180 | |
| 181 | do { |
| 182 | |
| 183 | dict = IOService::serviceMatching( gIOServiceKey ); |
| 184 | if( !dict) |
| 185 | continue; |
| 186 | str = OSSymbol::withCString( name ); |
| 187 | if( !str) |
| 188 | continue; |
| 189 | dict->setObject( kIOBSDNameKey, (OSObject *) str ); |
| 190 | str->release(); |
| 191 | |
| 192 | return( dict ); |
| 193 | |
| 194 | } while( false ); |
| 195 | |
| 196 | if( dict) |
| 197 | dict->release(); |
| 198 | if( str) |
| 199 | str->release(); |
| 200 | |
| 201 | return( 0 ); |
| 202 | } |
| 203 | |
| 204 | OSDictionary * IOUUIDMatching( void ) |
| 205 | { |
| 206 | return IOService::resourceMatching( "boot-uuid-media" ); |
| 207 | } |
| 208 | |
| 209 | OSDictionary * IONetworkNamePrefixMatching( const char * prefix ) |
| 210 | { |
| 211 | OSDictionary * matching; |
| 212 | OSDictionary * propDict = 0; |
| 213 | const OSSymbol * str = 0; |
| 214 | char networkType[128]; |
| 215 | |
| 216 | do { |
| 217 | matching = IOService::serviceMatching( "IONetworkInterface" ); |
| 218 | if ( matching == 0 ) |
| 219 | continue; |
| 220 | |
| 221 | propDict = OSDictionary::withCapacity(1); |
| 222 | if ( propDict == 0 ) |
| 223 | continue; |
| 224 | |
| 225 | str = OSSymbol::withCString( prefix ); |
| 226 | if ( str == 0 ) |
| 227 | continue; |
| 228 | |
| 229 | propDict->setObject( "IOInterfaceNamePrefix" , (OSObject *) str ); |
| 230 | str->release(); |
| 231 | str = 0; |
| 232 | |
| 233 | // see if we're contrained to netroot off of specific network type |
| 234 | if(PE_parse_boot_argn( "network-type" , networkType, 128 )) |
| 235 | { |
| 236 | str = OSSymbol::withCString( networkType ); |
| 237 | if(str) |
| 238 | { |
| 239 | propDict->setObject( "IONetworkRootType" , str); |
| 240 | str->release(); |
| 241 | str = 0; |
| 242 | } |
| 243 | } |
| 244 | |
| 245 | if ( matching->setObject( gIOPropertyMatchKey, |
| 246 | (OSObject *) propDict ) != true ) |
| 247 | continue; |
| 248 | |
| 249 | propDict->release(); |
| 250 | propDict = 0; |
| 251 | |
| 252 | return( matching ); |
| 253 | |
| 254 | } while ( false ); |
| 255 | |
| 256 | if ( matching ) matching->release(); |
| 257 | if ( propDict ) propDict->release(); |
| 258 | if ( str ) str->release(); |
| 259 | |
| 260 | return( 0 ); |
| 261 | } |
| 262 | |
| 263 | static bool IORegisterNetworkInterface( IOService * netif ) |
| 264 | { |
| 265 | // A network interface is typically named and registered |
| 266 | // with BSD after receiving a request from a user space |
| 267 | // "namer". However, for cases when the system needs to |
| 268 | // root from the network, this registration task must be |
| 269 | // done inside the kernel and completed before the root |
| 270 | // device is handed to BSD. |
| 271 | |
| 272 | IOService * stack; |
| 273 | OSNumber * zero = 0; |
| 274 | OSString * path = 0; |
| 275 | OSDictionary * dict = 0; |
| 276 | char * pathBuf = 0; |
| 277 | int len; |
| 278 | enum { kMaxPathLen = 512 }; |
| 279 | |
| 280 | do { |
| 281 | stack = IOService::waitForService( |
| 282 | IOService::serviceMatching("IONetworkStack" ) ); |
| 283 | if ( stack == 0 ) break; |
| 284 | |
| 285 | dict = OSDictionary::withCapacity(3); |
| 286 | if ( dict == 0 ) break; |
| 287 | |
| 288 | zero = OSNumber::withNumber((UInt64) 0, 32); |
| 289 | if ( zero == 0 ) break; |
| 290 | |
| 291 | pathBuf = (char *) IOMalloc( kMaxPathLen ); |
| 292 | if ( pathBuf == 0 ) break; |
| 293 | |
| 294 | len = kMaxPathLen; |
| 295 | if ( netif->getPath( pathBuf, &len, gIOServicePlane ) |
| 296 | == false ) break; |
| 297 | |
| 298 | path = OSString::withCStringNoCopy( pathBuf ); |
| 299 | if ( path == 0 ) break; |
| 300 | |
| 301 | dict->setObject( "IOInterfaceUnit" , zero ); |
| 302 | dict->setObject( kIOPathMatchKey, path ); |
| 303 | |
| 304 | stack->setProperties( dict ); |
| 305 | } |
| 306 | while ( false ); |
| 307 | |
| 308 | if ( zero ) zero->release(); |
| 309 | if ( path ) path->release(); |
| 310 | if ( dict ) dict->release(); |
| 311 | if ( pathBuf ) IOFree(pathBuf, kMaxPathLen); |
| 312 | |
| 313 | return ( netif->getProperty( kIOBSDNameKey ) != 0 ); |
| 314 | } |
| 315 | |
| 316 | OSDictionary * IOOFPathMatching( const char * path, char * buf, int maxLen ) |
| 317 | { |
| 318 | OSDictionary * matching = NULL; |
| 319 | OSString * str; |
| 320 | char * comp; |
| 321 | int len; |
| 322 | |
| 323 | do { |
| 324 | |
| 325 | len = strlen( kIODeviceTreePlane ":" ); |
| 326 | maxLen -= len; |
| 327 | if( maxLen <= 0) |
| 328 | continue; |
| 329 | |
| 330 | strlcpy( buf, kIODeviceTreePlane ":" , len + 1 ); |
| 331 | comp = buf + len; |
| 332 | |
| 333 | len = strlen( path ); |
| 334 | maxLen -= len; |
| 335 | if( maxLen <= 0) |
| 336 | continue; |
| 337 | strlcpy( comp, path, len + 1 ); |
| 338 | |
| 339 | matching = OSDictionary::withCapacity( 1 ); |
| 340 | if( !matching) |
| 341 | continue; |
| 342 | |
| 343 | str = OSString::withCString( buf ); |
| 344 | if( !str) |
| 345 | continue; |
| 346 | matching->setObject( kIOPathMatchKey, str ); |
| 347 | str->release(); |
| 348 | |
| 349 | return( matching ); |
| 350 | |
| 351 | } while( false ); |
| 352 | |
| 353 | if( matching) |
| 354 | matching->release(); |
| 355 | |
| 356 | return( 0 ); |
| 357 | } |
| 358 | |
| 359 | static int didRam = 0; |
| 360 | enum { kMaxPathBuf = 512, kMaxBootVar = 128 }; |
| 361 | |
| 362 | kern_return_t IOFindBSDRoot( char * rootName, unsigned int rootNameSize, |
| 363 | dev_t * root, u_int32_t * oflags ) |
| 364 | { |
| 365 | mach_timespec_t t; |
| 366 | IOService * service; |
| 367 | IORegistryEntry * regEntry; |
| 368 | OSDictionary * matching = 0; |
| 369 | OSString * iostr; |
| 370 | OSNumber * off; |
| 371 | OSData * data = 0; |
| 372 | |
| 373 | UInt32 flags = 0; |
| 374 | int mnr, mjr; |
| 375 | const char * mediaProperty = 0; |
| 376 | char * rdBootVar; |
| 377 | char * str; |
| 378 | const char * look = 0; |
| 379 | int len; |
| 380 | bool debugInfoPrintedOnce = false; |
| 381 | const char * uuidStr = NULL; |
| 382 | |
| 383 | static int mountAttempts = 0; |
| 384 | |
| 385 | int xchar, dchar; |
| 386 | |
| 387 | // stall here for anyone matching on the IOBSD resource to finish (filesystems) |
| 388 | matching = IOService::serviceMatching(gIOResourcesKey); |
| 389 | assert(matching); |
| 390 | matching->setObject(gIOResourceMatchedKey, gIOBSDKey); |
| 391 | |
| 392 | if ((service = IOService::waitForMatchingService(matching, 30ULL * kSecondScale))) { |
| 393 | service->release(); |
| 394 | } else { |
| 395 | IOLog("!BSD\n" ); |
| 396 | } |
| 397 | matching->release(); |
| 398 | matching = NULL; |
| 399 | |
| 400 | if( mountAttempts++) |
| 401 | { |
| 402 | IOLog("mount(%d) failed\n" , mountAttempts); |
| 403 | IOSleep( 5 * 1000 ); |
| 404 | } |
| 405 | |
| 406 | str = (char *) IOMalloc( kMaxPathBuf + kMaxBootVar ); |
| 407 | if( !str) |
| 408 | return( kIOReturnNoMemory ); |
| 409 | rdBootVar = str + kMaxPathBuf; |
| 410 | |
| 411 | if (!PE_parse_boot_argn("rd" , rdBootVar, kMaxBootVar ) |
| 412 | && !PE_parse_boot_argn("rootdev" , rdBootVar, kMaxBootVar )) |
| 413 | rdBootVar[0] = 0; |
| 414 | |
| 415 | do { |
| 416 | if( (regEntry = IORegistryEntry::fromPath( "/chosen" , gIODTPlane ))) { |
| 417 | di_root_ramfile(regEntry); |
| 418 | data = OSDynamicCast(OSData, regEntry->getProperty( "root-matching" )); |
| 419 | if (data) { |
| 420 | matching = OSDynamicCast(OSDictionary, OSUnserializeXML((char *)data->getBytesNoCopy())); |
| 421 | if (matching) { |
| 422 | continue; |
| 423 | } |
| 424 | } |
| 425 | |
| 426 | data = (OSData *) regEntry->getProperty( "boot-uuid" ); |
| 427 | if( data) { |
| 428 | uuidStr = (const char*)data->getBytesNoCopy(); |
| 429 | OSString *uuidString = OSString::withCString( uuidStr ); |
| 430 | |
| 431 | // match the boot-args boot-uuid processing below |
| 432 | if( uuidString) { |
| 433 | IOLog("rooting via boot-uuid from /chosen: %s\n" , uuidStr); |
| 434 | IOService::publishResource( "boot-uuid" , uuidString ); |
| 435 | uuidString->release(); |
| 436 | matching = IOUUIDMatching(); |
| 437 | mediaProperty = "boot-uuid-media" ; |
| 438 | regEntry->release(); |
| 439 | continue; |
| 440 | } else { |
| 441 | uuidStr = NULL; |
| 442 | } |
| 443 | } |
| 444 | regEntry->release(); |
| 445 | } |
| 446 | } while( false ); |
| 447 | |
| 448 | // |
| 449 | // See if we have a RAMDisk property in /chosen/memory-map. If so, make it into a device. |
| 450 | // It will become /dev/mdx, where x is 0-f. |
| 451 | // |
| 452 | |
| 453 | if(!didRam) { /* Have we already build this ram disk? */ |
| 454 | didRam = 1; /* Remember we did this */ |
| 455 | if((regEntry = IORegistryEntry::fromPath( "/chosen/memory-map" , gIODTPlane ))) { /* Find the map node */ |
| 456 | data = (OSData *)regEntry->getProperty("RAMDisk" ); /* Find the ram disk, if there */ |
| 457 | if(data) { /* We found one */ |
| 458 | uintptr_t *ramdParms; |
| 459 | ramdParms = (uintptr_t *)data->getBytesNoCopy(); /* Point to the ram disk base and size */ |
| 460 | (void)mdevadd(-1, ml_static_ptovirt(ramdParms[0]) >> 12, ramdParms[1] >> 12, 0); /* Initialize it and pass back the device number */ |
| 461 | } |
| 462 | regEntry->release(); /* Toss the entry */ |
| 463 | } |
| 464 | } |
| 465 | |
| 466 | // |
| 467 | // Now check if we are trying to root on a memory device |
| 468 | // |
| 469 | |
| 470 | if((rdBootVar[0] == 'm') && (rdBootVar[1] == 'd') && (rdBootVar[3] == 0)) { |
| 471 | dchar = xchar = rdBootVar[2]; /* Get the actual device */ |
| 472 | if((xchar >= '0') && (xchar <= '9')) xchar = xchar - '0'; /* If digit, convert */ |
| 473 | else { |
| 474 | xchar = xchar & ~' '; /* Fold to upper case */ |
| 475 | if((xchar >= 'A') && (xchar <= 'F')) { /* Is this a valid digit? */ |
| 476 | xchar = (xchar & 0xF) + 9; /* Convert the hex digit */ |
| 477 | dchar = dchar | ' '; /* Fold to lower case */ |
| 478 | } |
| 479 | else xchar = -1; /* Show bogus */ |
| 480 | } |
| 481 | if(xchar >= 0) { /* Do we have a valid memory device name? */ |
| 482 | *root = mdevlookup(xchar); /* Find the device number */ |
| 483 | if(*root >= 0) { /* Did we find one? */ |
| 484 | rootName[0] = 'm'; /* Build root name */ |
| 485 | rootName[1] = 'd'; /* Build root name */ |
| 486 | rootName[2] = dchar; /* Build root name */ |
| 487 | rootName[3] = 0; /* Build root name */ |
| 488 | IOLog("BSD root: %s, major %d, minor %d\n" , rootName, major(*root), minor(*root)); |
| 489 | *oflags = 0; /* Show that this is not network */ |
| 490 | |
| 491 | #if CONFIG_KDP_INTERACTIVE_DEBUGGING |
| 492 | /* retrieve final ramdisk range and initialize KDP variables */ |
| 493 | if (mdevgetrange(xchar, &kdp_core_ramdisk_addr, &kdp_core_ramdisk_size) != 0) { |
| 494 | IOLog("Unable to retrieve range for root memory device %d\n" , xchar); |
| 495 | kdp_core_ramdisk_addr = 0; |
| 496 | kdp_core_ramdisk_size = 0; |
| 497 | } |
| 498 | #endif |
| 499 | |
| 500 | goto iofrootx; /* Join common exit... */ |
| 501 | } |
| 502 | panic("IOFindBSDRoot: specified root memory device, %s, has not been configured\n" , rdBootVar); /* Not there */ |
| 503 | } |
| 504 | } |
| 505 | |
| 506 | if( (!matching) && rdBootVar[0] ) { |
| 507 | // by BSD name |
| 508 | look = rdBootVar; |
| 509 | if( look[0] == '*') |
| 510 | look++; |
| 511 | |
| 512 | if ( strncmp( look, "en" , strlen( "en" )) == 0 ) { |
| 513 | matching = IONetworkNamePrefixMatching( "en" ); |
| 514 | } else if ( strncmp( look, "uuid" , strlen( "uuid" )) == 0 ) { |
| 515 | char *uuid; |
| 516 | OSString *uuidString; |
| 517 | |
| 518 | uuid = (char *)IOMalloc( kMaxBootVar ); |
| 519 | |
| 520 | if ( uuid ) { |
| 521 | if (!PE_parse_boot_argn( "boot-uuid" , uuid, kMaxBootVar )) { |
| 522 | panic( "rd=uuid but no boot-uuid=<value> specified" ); |
| 523 | } |
| 524 | uuidString = OSString::withCString( uuid ); |
| 525 | if ( uuidString ) { |
| 526 | IOService::publishResource( "boot-uuid" , uuidString ); |
| 527 | uuidString->release(); |
| 528 | IOLog( "\nWaiting for boot volume with UUID %s\n" , uuid ); |
| 529 | matching = IOUUIDMatching(); |
| 530 | mediaProperty = "boot-uuid-media" ; |
| 531 | } |
| 532 | IOFree( uuid, kMaxBootVar ); |
| 533 | } |
| 534 | } else { |
| 535 | matching = IOBSDNameMatching( look ); |
| 536 | } |
| 537 | } |
| 538 | |
| 539 | if( !matching) { |
| 540 | OSString * astring; |
| 541 | // Match any HFS media |
| 542 | |
| 543 | matching = IOService::serviceMatching( "IOMedia" ); |
| 544 | astring = OSString::withCStringNoCopy("Apple_HFS" ); |
| 545 | if ( astring ) { |
| 546 | matching->setObject("Content" , astring); |
| 547 | astring->release(); |
| 548 | } |
| 549 | } |
| 550 | |
| 551 | if( gIOKitDebug & kIOWaitQuietBeforeRoot ) { |
| 552 | IOLog( "Waiting for matching to complete\n" ); |
| 553 | IOService::getPlatform()->waitQuiet(); |
| 554 | } |
| 555 | |
| 556 | if( true && matching) { |
| 557 | OSSerialize * s = OSSerialize::withCapacity( 5 ); |
| 558 | |
| 559 | if( matching->serialize( s )) { |
| 560 | IOLog( "Waiting on %s\n" , s->text() ); |
| 561 | s->release(); |
| 562 | } |
| 563 | } |
| 564 | |
| 565 | do { |
| 566 | t.tv_sec = ROOTDEVICETIMEOUT; |
| 567 | t.tv_nsec = 0; |
| 568 | matching->retain(); |
| 569 | service = IOService::waitForService( matching, &t ); |
| 570 | if( (!service) || (mountAttempts == 10)) { |
| 571 | PE_display_icon( 0, "noroot" ); |
| 572 | IOLog( "Still waiting for root device\n" ); |
| 573 | |
| 574 | if( !debugInfoPrintedOnce) { |
| 575 | debugInfoPrintedOnce = true; |
| 576 | if( gIOKitDebug & kIOLogDTree) { |
| 577 | IOLog("\nDT plane:\n" ); |
| 578 | IOPrintPlane( gIODTPlane ); |
| 579 | } |
| 580 | if( gIOKitDebug & kIOLogServiceTree) { |
| 581 | IOLog("\nService plane:\n" ); |
| 582 | IOPrintPlane( gIOServicePlane ); |
| 583 | } |
| 584 | if( gIOKitDebug & kIOLogMemory) |
| 585 | IOPrintMemory(); |
| 586 | } |
| 587 | } |
| 588 | } while( !service); |
| 589 | matching->release(); |
| 590 | |
| 591 | if ( service && mediaProperty ) { |
| 592 | service = (IOService *)service->getProperty(mediaProperty); |
| 593 | } |
| 594 | |
| 595 | mjr = 0; |
| 596 | mnr = 0; |
| 597 | |
| 598 | // If the IOService we matched to is a subclass of IONetworkInterface, |
| 599 | // then make sure it has been registered with BSD and has a BSD name |
| 600 | // assigned. |
| 601 | |
| 602 | if ( service |
| 603 | && service->metaCast( "IONetworkInterface" ) |
| 604 | && !IORegisterNetworkInterface( service ) ) |
| 605 | { |
| 606 | service = 0; |
| 607 | } |
| 608 | |
| 609 | if( service) { |
| 610 | |
| 611 | len = kMaxPathBuf; |
| 612 | service->getPath( str, &len, gIOServicePlane ); |
| 613 | IOLog( "Got boot device = %s\n" , str ); |
| 614 | |
| 615 | iostr = (OSString *) service->getProperty( kIOBSDNameKey ); |
| 616 | if( iostr) |
| 617 | strlcpy( rootName, iostr->getCStringNoCopy(), rootNameSize ); |
| 618 | off = (OSNumber *) service->getProperty( kIOBSDMajorKey ); |
| 619 | if( off) |
| 620 | mjr = off->unsigned32BitValue(); |
| 621 | off = (OSNumber *) service->getProperty( kIOBSDMinorKey ); |
| 622 | if( off) |
| 623 | mnr = off->unsigned32BitValue(); |
| 624 | |
| 625 | if( service->metaCast( "IONetworkInterface" )) |
| 626 | flags |= 1; |
| 627 | |
| 628 | } else { |
| 629 | |
| 630 | IOLog( "Wait for root failed\n" ); |
| 631 | strlcpy( rootName, "en0" , rootNameSize ); |
| 632 | flags |= 1; |
| 633 | } |
| 634 | |
| 635 | IOLog( "BSD root: %s" , rootName ); |
| 636 | if( mjr) |
| 637 | IOLog(", major %d, minor %d\n" , mjr, mnr ); |
| 638 | else |
| 639 | IOLog("\n" ); |
| 640 | |
| 641 | *root = makedev( mjr, mnr ); |
| 642 | *oflags = flags; |
| 643 | |
| 644 | IOFree( str, kMaxPathBuf + kMaxBootVar ); |
| 645 | |
| 646 | iofrootx: |
| 647 | if( (gIOKitDebug & (kIOLogDTree | kIOLogServiceTree | kIOLogMemory)) && !debugInfoPrintedOnce) { |
| 648 | |
| 649 | IOService::getPlatform()->waitQuiet(); |
| 650 | if( gIOKitDebug & kIOLogDTree) { |
| 651 | IOLog("\nDT plane:\n" ); |
| 652 | IOPrintPlane( gIODTPlane ); |
| 653 | } |
| 654 | if( gIOKitDebug & kIOLogServiceTree) { |
| 655 | IOLog("\nService plane:\n" ); |
| 656 | IOPrintPlane( gIOServicePlane ); |
| 657 | } |
| 658 | if( gIOKitDebug & kIOLogMemory) |
| 659 | IOPrintMemory(); |
| 660 | } |
| 661 | |
| 662 | return( kIOReturnSuccess ); |
| 663 | } |
| 664 | |
| 665 | bool IORamDiskBSDRoot(void) |
| 666 | { |
| 667 | char rdBootVar[kMaxBootVar]; |
| 668 | if (PE_parse_boot_argn("rd" , rdBootVar, kMaxBootVar ) |
| 669 | || PE_parse_boot_argn("rootdev" , rdBootVar, kMaxBootVar )) { |
| 670 | if((rdBootVar[0] == 'm') && (rdBootVar[1] == 'd') && (rdBootVar[3] == 0)) { |
| 671 | return true; |
| 672 | } |
| 673 | } |
| 674 | return false; |
| 675 | } |
| 676 | |
| 677 | void IOSecureBSDRoot(const char * rootName) |
| 678 | { |
| 679 | #if CONFIG_EMBEDDED |
| 680 | int tmpInt; |
| 681 | IOReturn result; |
| 682 | IOPlatformExpert *pe; |
| 683 | OSDictionary *matching; |
| 684 | const OSSymbol *functionName = OSSymbol::withCStringNoCopy("SecureRootName" ); |
| 685 | |
| 686 | matching = IOService::serviceMatching("IOPlatformExpert" ); |
| 687 | assert(matching); |
| 688 | pe = (IOPlatformExpert *) IOService::waitForMatchingService(matching, 30ULL * kSecondScale); |
| 689 | matching->release(); |
| 690 | assert(pe); |
| 691 | // Returns kIOReturnNotPrivileged is the root device is not secure. |
| 692 | // Returns kIOReturnUnsupported if "SecureRootName" is not implemented. |
| 693 | result = pe->callPlatformFunction(functionName, false, (void *)rootName, (void *)0, (void *)0, (void *)0); |
| 694 | functionName->release(); |
| 695 | OSSafeReleaseNULL(pe); |
| 696 | |
| 697 | if (result == kIOReturnNotPrivileged) { |
| 698 | mdevremoveall(); |
| 699 | } else if (result == kIOReturnSuccess) { |
| 700 | // If we are booting with a secure root, and we have the right |
| 701 | // boot-arg, we will want to panic on exception triage. This |
| 702 | // behavior is intended as a debug aid (we can look at why an |
| 703 | // exception occured in the kernel debugger). |
| 704 | if (PE_parse_boot_argn("-panic_on_exception_triage" , &tmpInt, sizeof(tmpInt))) { |
| 705 | panic_on_exception_triage = 1; |
| 706 | } |
| 707 | } |
| 708 | |
| 709 | #endif // CONFIG_EMBEDDED |
| 710 | } |
| 711 | |
| 712 | void * |
| 713 | IOBSDRegistryEntryForDeviceTree(char * path) |
| 714 | { |
| 715 | return (IORegistryEntry::fromPath(path, gIODTPlane)); |
| 716 | } |
| 717 | |
| 718 | void |
| 719 | IOBSDRegistryEntryRelease(void * entry) |
| 720 | { |
| 721 | IORegistryEntry * regEntry = (IORegistryEntry *)entry; |
| 722 | |
| 723 | if (regEntry) |
| 724 | regEntry->release(); |
| 725 | return; |
| 726 | } |
| 727 | |
| 728 | const void * |
| 729 | IOBSDRegistryEntryGetData(void * entry, char * property_name, |
| 730 | int * packet_length) |
| 731 | { |
| 732 | OSData * data; |
| 733 | IORegistryEntry * regEntry = (IORegistryEntry *)entry; |
| 734 | |
| 735 | data = (OSData *) regEntry->getProperty(property_name); |
| 736 | if (data) { |
| 737 | *packet_length = data->getLength(); |
| 738 | return (data->getBytesNoCopy()); |
| 739 | } |
| 740 | return (NULL); |
| 741 | } |
| 742 | |
| 743 | kern_return_t IOBSDGetPlatformUUID( uuid_t uuid, mach_timespec_t timeout ) |
| 744 | { |
| 745 | IOService * resources; |
| 746 | OSString * string; |
| 747 | |
| 748 | resources = IOService::waitForService( IOService::resourceMatching( kIOPlatformUUIDKey ), ( timeout.tv_sec || timeout.tv_nsec ) ? &timeout : 0 ); |
| 749 | if ( resources == 0 ) return KERN_OPERATION_TIMED_OUT; |
| 750 | |
| 751 | string = ( OSString * ) IOService::getPlatform( )->getProvider( )->getProperty( kIOPlatformUUIDKey ); |
| 752 | if ( string == 0 ) return KERN_NOT_SUPPORTED; |
| 753 | |
| 754 | uuid_parse( string->getCStringNoCopy( ), uuid ); |
| 755 | |
| 756 | return KERN_SUCCESS; |
| 757 | } |
| 758 | |
| 759 | } /* extern "C" */ |
| 760 | |
| 761 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ |
| 762 | |
| 763 | #include <sys/conf.h> |
| 764 | #include <sys/vnode.h> |
| 765 | #include <sys/vnode_internal.h> |
| 766 | #include <sys/fcntl.h> |
| 767 | #include <IOKit/IOPolledInterface.h> |
| 768 | #include <IOKit/IOBufferMemoryDescriptor.h> |
| 769 | |
| 770 | IOPolledFileIOVars * gIOPolledCoreFileVars; |
| 771 | kern_return_t gIOPolledCoreFileOpenRet = kIOReturnNotReady; |
| 772 | #if IOPOLLED_COREFILE |
| 773 | |
| 774 | static IOReturn |
| 775 | IOOpenPolledCoreFile(const char * filename) |
| 776 | { |
| 777 | IOReturn err; |
| 778 | unsigned int debug; |
| 779 | uint64_t corefile_size_bytes = 0; |
| 780 | |
| 781 | if (gIOPolledCoreFileVars) return (kIOReturnBusy); |
| 782 | if (!IOPolledInterface::gMetaClass.getInstanceCount()) return (kIOReturnUnsupported); |
| 783 | |
| 784 | debug = 0; |
| 785 | PE_parse_boot_argn("debug" , &debug, sizeof (debug)); |
| 786 | if (DB_DISABLE_LOCAL_CORE & debug) return (kIOReturnUnsupported); |
| 787 | |
| 788 | #if CONFIG_EMBEDDED |
| 789 | unsigned int requested_corefile_size = 0; |
| 790 | if (PE_parse_boot_argn("corefile_size_mb" , &requested_corefile_size, sizeof(requested_corefile_size))) { |
| 791 | IOLog("Boot-args specify %d MB kernel corefile\n" , requested_corefile_size); |
| 792 | |
| 793 | corefile_size_bytes = (requested_corefile_size * 1024ULL * 1024ULL); |
| 794 | } |
| 795 | #endif |
| 796 | |
| 797 | |
| 798 | do { |
| 799 | #if defined(kIOCoreDumpLargeSize) |
| 800 | if (0 == corefile_size_bytes) |
| 801 | { |
| 802 | // If no custom size was requested and we're on a device with >3GB of DRAM, attempt |
| 803 | // to allocate a large corefile otherwise use a small file. |
| 804 | if (max_mem > (3 * 1024ULL * 1024ULL * 1024ULL)) |
| 805 | { |
| 806 | corefile_size_bytes = kIOCoreDumpLargeSize; |
| 807 | err = IOPolledFileOpen(filename, |
| 808 | kIOPolledFileCreate, |
| 809 | corefile_size_bytes, kIOCoreDumpFreeSize, |
| 810 | NULL, 0, |
| 811 | &gIOPolledCoreFileVars, NULL, NULL, 0); |
| 812 | if (kIOReturnSuccess == err) |
| 813 | { |
| 814 | break; |
| 815 | } |
| 816 | else if (kIOReturnNoSpace == err) |
| 817 | { |
| 818 | IOLog("Failed to open corefile of size %llu MB (low disk space)" , |
| 819 | (corefile_size_bytes / (1024ULL * 1024ULL))); |
| 820 | if (corefile_size_bytes == kIOCoreDumpMinSize) |
| 821 | { |
| 822 | gIOPolledCoreFileOpenRet = err; |
| 823 | return (err); |
| 824 | } |
| 825 | // Try to open a smaller corefile (set size and fall-through) |
| 826 | corefile_size_bytes = kIOCoreDumpMinSize; |
| 827 | } |
| 828 | else |
| 829 | { |
| 830 | IOLog("Failed to open corefile of size %llu MB (returned error 0x%x)\n" , |
| 831 | (corefile_size_bytes / (1024ULL * 1024ULL)), err); |
| 832 | gIOPolledCoreFileOpenRet = err; |
| 833 | return (err); |
| 834 | } |
| 835 | } |
| 836 | else |
| 837 | { |
| 838 | corefile_size_bytes = kIOCoreDumpMinSize; |
| 839 | } |
| 840 | } |
| 841 | #else /* defined(kIOCoreDumpLargeSize) */ |
| 842 | if (0 == corefile_size_bytes) |
| 843 | { |
| 844 | corefile_size_bytes = kIOCoreDumpSize; |
| 845 | } |
| 846 | #endif /* defined(kIOCoreDumpLargeSize) */ |
| 847 | err = IOPolledFileOpen(filename, |
| 848 | kIOPolledFileCreate, |
| 849 | corefile_size_bytes, kIOCoreDumpFreeSize, |
| 850 | NULL, 0, |
| 851 | &gIOPolledCoreFileVars, NULL, NULL, 0); |
| 852 | if (kIOReturnSuccess != err) |
| 853 | { |
| 854 | IOLog("Failed to open corefile of size %llu MB (returned error 0x%x)\n" , |
| 855 | (corefile_size_bytes / (1024ULL * 1024ULL)), err); |
| 856 | gIOPolledCoreFileOpenRet = err; |
| 857 | return (err); |
| 858 | } |
| 859 | } while (false); |
| 860 | |
| 861 | err = IOPolledFilePollersSetup(gIOPolledCoreFileVars, kIOPolledPreflightCoreDumpState); |
| 862 | if (kIOReturnSuccess != err) |
| 863 | { |
| 864 | IOPolledFileClose(&gIOPolledCoreFileVars, NULL, NULL, 0, 0, 0); |
| 865 | IOLog("IOPolledFilePollersSetup for corefile failed with error: 0x%x\n" , err); |
| 866 | gIOPolledCoreFileOpenRet = err; |
| 867 | } |
| 868 | else |
| 869 | { |
| 870 | IOLog("Opened corefile of size %llu MB\n" , (corefile_size_bytes / (1024ULL * 1024ULL))); |
| 871 | } |
| 872 | |
| 873 | return (err); |
| 874 | } |
| 875 | |
| 876 | static void |
| 877 | IOClosePolledCoreFile(void) |
| 878 | { |
| 879 | gIOPolledCoreFileOpenRet = kIOReturnNotOpen; |
| 880 | IOPolledFilePollersClose(gIOPolledCoreFileVars, kIOPolledPostflightCoreDumpState); |
| 881 | IOPolledFileClose(&gIOPolledCoreFileVars, NULL, NULL, 0, 0, 0); |
| 882 | } |
| 883 | |
| 884 | static thread_call_t gIOOpenPolledCoreFileTC; |
| 885 | static IONotifier * gIOPolledCoreFileNotifier; |
| 886 | static IONotifier * gIOPolledCoreFileInterestNotifier; |
| 887 | |
| 888 | static IOReturn |
| 889 | KernelCoreMediaInterest(void * target, void * refCon, |
| 890 | UInt32 messageType, IOService * provider, |
| 891 | void * messageArgument, vm_size_t argSize ) |
| 892 | { |
| 893 | if (kIOMessageServiceIsTerminated == messageType) |
| 894 | { |
| 895 | gIOPolledCoreFileInterestNotifier->remove(); |
| 896 | gIOPolledCoreFileInterestNotifier = 0; |
| 897 | IOClosePolledCoreFile(); |
| 898 | } |
| 899 | |
| 900 | return (kIOReturnSuccess); |
| 901 | } |
| 902 | |
| 903 | static void |
| 904 | OpenKernelCoreMedia(thread_call_param_t p0, thread_call_param_t p1) |
| 905 | { |
| 906 | IOService * newService; |
| 907 | OSString * string; |
| 908 | char filename[16]; |
| 909 | |
| 910 | newService = (IOService *) p1; |
| 911 | do |
| 912 | { |
| 913 | if (gIOPolledCoreFileVars) break; |
| 914 | string = OSDynamicCast(OSString, newService->getProperty(kIOBSDNameKey)); |
| 915 | if (!string) break; |
| 916 | snprintf(filename, sizeof(filename), "/dev/%s" , string->getCStringNoCopy()); |
| 917 | if (kIOReturnSuccess != IOOpenPolledCoreFile(filename)) break; |
| 918 | gIOPolledCoreFileInterestNotifier = newService->registerInterest( |
| 919 | gIOGeneralInterest, &KernelCoreMediaInterest, NULL, 0); |
| 920 | } |
| 921 | while (false); |
| 922 | |
| 923 | newService->release(); |
| 924 | } |
| 925 | |
| 926 | static bool |
| 927 | NewKernelCoreMedia(void * target, void * refCon, |
| 928 | IOService * newService, |
| 929 | IONotifier * notifier) |
| 930 | { |
| 931 | static volatile UInt32 onlyOneCorePartition = 0; |
| 932 | do |
| 933 | { |
| 934 | if (!OSCompareAndSwap(0, 1, &onlyOneCorePartition)) break; |
| 935 | if (gIOPolledCoreFileVars) break; |
| 936 | if (!gIOOpenPolledCoreFileTC) break; |
| 937 | newService = newService->getProvider(); |
| 938 | if (!newService) break; |
| 939 | newService->retain(); |
| 940 | thread_call_enter1(gIOOpenPolledCoreFileTC, newService); |
| 941 | } |
| 942 | while (false); |
| 943 | |
| 944 | return (false); |
| 945 | } |
| 946 | |
| 947 | #endif /* IOPOLLED_COREFILE */ |
| 948 | |
| 949 | extern "C" void |
| 950 | IOBSDMountChange(struct mount * mp, uint32_t op) |
| 951 | { |
| 952 | #if IOPOLLED_COREFILE |
| 953 | |
| 954 | OSDictionary * bsdMatching; |
| 955 | OSDictionary * mediaMatching; |
| 956 | OSString * string; |
| 957 | |
| 958 | if (!gIOPolledCoreFileNotifier) do |
| 959 | { |
| 960 | if (!gIOOpenPolledCoreFileTC) gIOOpenPolledCoreFileTC = thread_call_allocate(&OpenKernelCoreMedia, NULL); |
| 961 | bsdMatching = IOService::serviceMatching("IOMediaBSDClient" ); |
| 962 | if (!bsdMatching) break; |
| 963 | mediaMatching = IOService::serviceMatching("IOMedia" ); |
| 964 | string = OSString::withCStringNoCopy("5361644D-6163-11AA-AA11-00306543ECAC" ); |
| 965 | if (!string || !mediaMatching) break; |
| 966 | mediaMatching->setObject("Content" , string); |
| 967 | string->release(); |
| 968 | bsdMatching->setObject(gIOParentMatchKey, mediaMatching); |
| 969 | mediaMatching->release(); |
| 970 | |
| 971 | gIOPolledCoreFileNotifier = IOService::addMatchingNotification( |
| 972 | gIOFirstMatchNotification, bsdMatching, |
| 973 | &NewKernelCoreMedia, NULL, NULL, -1000); |
| 974 | } |
| 975 | while (false); |
| 976 | |
| 977 | #if CONFIG_EMBEDDED |
| 978 | uint64_t flags; |
| 979 | char path[128]; |
| 980 | int pathLen; |
| 981 | vnode_t vn; |
| 982 | int result; |
| 983 | |
| 984 | switch (op) |
| 985 | { |
| 986 | case kIOMountChangeMount: |
| 987 | case kIOMountChangeDidResize: |
| 988 | |
| 989 | if (gIOPolledCoreFileVars) break; |
| 990 | flags = vfs_flags(mp); |
| 991 | if (MNT_RDONLY & flags) break; |
| 992 | if (!(MNT_LOCAL & flags)) break; |
| 993 | |
| 994 | vn = vfs_vnodecovered(mp); |
| 995 | if (!vn) break; |
| 996 | pathLen = sizeof(path); |
| 997 | result = vn_getpath(vn, &path[0], &pathLen); |
| 998 | vnode_put(vn); |
| 999 | if (0 != result) break; |
| 1000 | if (!pathLen) break; |
| 1001 | #if defined(XNU_TARGET_OS_BRIDGE) |
| 1002 | // on bridgeOS systems we put the core in /private/var/internal. We don't |
| 1003 | // want to match with /private/var because /private/var/internal is often mounted |
| 1004 | // over /private/var |
| 1005 | if ((pathLen - 1) < (int) strlen("/private/var/internal" )) break; |
| 1006 | #endif |
| 1007 | if (0 != strncmp(path, kIOCoreDumpPath, pathLen - 1)) break; |
| 1008 | IOOpenPolledCoreFile(kIOCoreDumpPath); |
| 1009 | break; |
| 1010 | |
| 1011 | case kIOMountChangeUnmount: |
| 1012 | case kIOMountChangeWillResize: |
| 1013 | if (gIOPolledCoreFileVars && (mp == kern_file_mount(gIOPolledCoreFileVars->fileRef))) |
| 1014 | { |
| 1015 | IOClosePolledCoreFile(); |
| 1016 | } |
| 1017 | break; |
| 1018 | } |
| 1019 | #endif /* CONFIG_EMBEDDED */ |
| 1020 | #endif /* IOPOLLED_COREFILE */ |
| 1021 | } |
| 1022 | |
| 1023 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ |
| 1024 | |
| 1025 | extern "C" boolean_t |
| 1026 | IOTaskHasEntitlement(task_t task, const char * entitlement) |
| 1027 | { |
| 1028 | OSObject * obj; |
| 1029 | obj = IOUserClient::copyClientEntitlement(task, entitlement); |
| 1030 | if (!obj) return (false); |
| 1031 | obj->release(); |
| 1032 | return (obj != kOSBooleanFalse); |
| 1033 | } |
| 1034 | |
| 1035 | |