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/IONVRAM.h> 35 #include <IOKit/IOPlatformExpert.h> 36 #include <IOKit/IOUserClient.h> 37 38 extern "C" { 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 extern dev_t mdevadd(int devid, uint64_t base, unsigned int size, int phys); 54 extern dev_t mdevlookup(int devid); 55 extern void mdevremoveall(void); 56 extern int mdevgetrange(int devid, uint64_t *base, uint64_t *size); 57 extern void di_root_ramfile(IORegistryEntry * entry); 58 59 #define ROUNDUP(a, b) (((a) + ((b) - 1)) & (~((b) - 1))) 60 61 #define IOPOLLED_COREFILE (CONFIG_KDP_INTERACTIVE_DEBUGGING) 62 63 #if defined(XNU_TARGET_OS_BRIDGE) 64 #define kIOCoreDumpPath "/private/var/internal/kernelcore" 65 #elif defined(XNU_TARGET_OS_OSX) 66 #define kIOCoreDumpPath "/System/Volumes/VM/kernelcore" 67 #else 68 #define kIOCoreDumpPath "/private/var/vm/kernelcore" 69 #endif 70 71 #define SYSTEM_NVRAM_PREFIX "40A0DDD2-77F8-4392-B4A3-1E7304206516:" 72 73 #if CONFIG_KDP_INTERACTIVE_DEBUGGING 74 /* 75 * Touched by IOFindBSDRoot() if a RAMDisk is used for the root device. 76 */ 77 extern uint64_t kdp_core_ramdisk_addr; 78 extern uint64_t kdp_core_ramdisk_size; 79 #endif 80 81 #if IOPOLLED_COREFILE 82 static void IOOpenPolledCoreFile(thread_call_param_t __unused, thread_call_param_t corefilename); 83 84 thread_call_t corefile_open_call = NULL; 85 #endif 86 87 kern_return_t 88 IOKitBSDInit( void ) 89 { 90 IOService::publishResource("IOBSD"); 91 92 #if IOPOLLED_COREFILE 93 corefile_open_call = thread_call_allocate_with_options(IOOpenPolledCoreFile, NULL, THREAD_CALL_PRIORITY_KERNEL, THREAD_CALL_OPTIONS_ONCE); 94 #endif 95 96 return kIOReturnSuccess; 97 } 98 99 void 100 IOServicePublishResource( const char * property, boolean_t value ) 101 { 102 if (value) { 103 IOService::publishResource( property, kOSBooleanTrue ); 104 } else { 105 IOService::getResourceService()->removeProperty( property ); 106 } 107 } 108 109 boolean_t 110 IOServiceWaitForMatchingResource( const char * property, uint64_t timeout ) 111 { 112 OSDictionary * dict = NULL; 113 IOService * match = NULL; 114 boolean_t found = false; 115 116 do { 117 dict = IOService::resourceMatching( property ); 118 if (!dict) { 119 continue; 120 } 121 match = IOService::waitForMatchingService( dict, timeout ); 122 if (match) { 123 found = true; 124 } 125 } while (false); 126 127 if (dict) { 128 dict->release(); 129 } 130 if (match) { 131 match->release(); 132 } 133 134 return found; 135 } 136 137 boolean_t 138 IOCatalogueMatchingDriversPresent( const char * property ) 139 { 140 OSDictionary * dict = NULL; 141 OSOrderedSet * set = NULL; 142 SInt32 generationCount = 0; 143 boolean_t found = false; 144 145 do { 146 dict = OSDictionary::withCapacity(1); 147 if (!dict) { 148 continue; 149 } 150 dict->setObject( property, kOSBooleanTrue ); 151 set = gIOCatalogue->findDrivers( dict, &generationCount ); 152 if (set && (set->getCount() > 0)) { 153 found = true; 154 } 155 } while (false); 156 157 if (dict) { 158 dict->release(); 159 } 160 if (set) { 161 set->release(); 162 } 163 164 return found; 165 } 166 167 OSDictionary * 168 IOBSDNameMatching( const char * name ) 169 { 170 OSDictionary * dict; 171 const OSSymbol * str = NULL; 172 173 do { 174 dict = IOService::serviceMatching( gIOServiceKey ); 175 if (!dict) { 176 continue; 177 } 178 str = OSSymbol::withCString( name ); 179 if (!str) { 180 continue; 181 } 182 dict->setObject( kIOBSDNameKey, (OSObject *) str ); 183 str->release(); 184 185 return dict; 186 } while (false); 187 188 if (dict) { 189 dict->release(); 190 } 191 if (str) { 192 str->release(); 193 } 194 195 return NULL; 196 } 197 198 OSDictionary * 199 IOUUIDMatching( void ) 200 { 201 return IOService::resourceMatching( "boot-uuid-media" ); 202 } 203 204 OSDictionary * 205 IONetworkNamePrefixMatching( const char * prefix ) 206 { 207 OSDictionary * matching; 208 OSDictionary * propDict = NULL; 209 const OSSymbol * str = NULL; 210 char networkType[128]; 211 212 do { 213 matching = IOService::serviceMatching( "IONetworkInterface" ); 214 if (matching == NULL) { 215 continue; 216 } 217 218 propDict = OSDictionary::withCapacity(1); 219 if (propDict == NULL) { 220 continue; 221 } 222 223 str = OSSymbol::withCString( prefix ); 224 if (str == NULL) { 225 continue; 226 } 227 228 propDict->setObject( "IOInterfaceNamePrefix", (OSObject *) str ); 229 str->release(); 230 str = NULL; 231 232 // see if we're contrained to netroot off of specific network type 233 if (PE_parse_boot_argn( "network-type", networkType, 128 )) { 234 str = OSSymbol::withCString( networkType ); 235 if (str) { 236 propDict->setObject( "IONetworkRootType", str); 237 str->release(); 238 str = NULL; 239 } 240 } 241 242 if (matching->setObject( gIOPropertyMatchKey, 243 (OSObject *) propDict ) != true) { 244 continue; 245 } 246 247 propDict->release(); 248 propDict = NULL; 249 250 return matching; 251 } while (false); 252 253 if (matching) { 254 matching->release(); 255 } 256 if (propDict) { 257 propDict->release(); 258 } 259 if (str) { 260 str->release(); 261 } 262 263 return NULL; 264 } 265 266 static bool 267 IORegisterNetworkInterface( IOService * netif ) 268 { 269 // A network interface is typically named and registered 270 // with BSD after receiving a request from a user space 271 // "namer". However, for cases when the system needs to 272 // root from the network, this registration task must be 273 // done inside the kernel and completed before the root 274 // device is handed to BSD. 275 276 IOService * stack; 277 OSNumber * zero = NULL; 278 OSString * path = NULL; 279 OSDictionary * dict = NULL; 280 char * pathBuf = NULL; 281 int len; 282 enum { kMaxPathLen = 512 }; 283 284 do { 285 stack = IOService::waitForService( 286 IOService::serviceMatching("IONetworkStack")); 287 if (stack == NULL) { 288 break; 289 } 290 291 dict = OSDictionary::withCapacity(3); 292 if (dict == NULL) { 293 break; 294 } 295 296 zero = OSNumber::withNumber((UInt64) 0, 32); 297 if (zero == NULL) { 298 break; 299 } 300 301 pathBuf = (char *) IOMalloc( kMaxPathLen ); 302 if (pathBuf == NULL) { 303 break; 304 } 305 306 len = kMaxPathLen; 307 if (netif->getPath( pathBuf, &len, gIOServicePlane ) 308 == false) { 309 break; 310 } 311 312 path = OSString::withCStringNoCopy( pathBuf ); 313 if (path == NULL) { 314 break; 315 } 316 317 dict->setObject( "IOInterfaceUnit", zero ); 318 dict->setObject( kIOPathMatchKey, path ); 319 320 stack->setProperties( dict ); 321 }while (false); 322 323 if (zero) { 324 zero->release(); 325 } 326 if (path) { 327 path->release(); 328 } 329 if (dict) { 330 dict->release(); 331 } 332 if (pathBuf) { 333 IOFree(pathBuf, kMaxPathLen); 334 } 335 336 return netif->getProperty( kIOBSDNameKey ) != NULL; 337 } 338 339 OSDictionary * 340 IOOFPathMatching( const char * path, char * buf, int maxLen ) 341 { 342 OSDictionary * matching = NULL; 343 OSString * str; 344 char * comp; 345 int len; 346 347 do { 348 len = ((int) strlen( kIODeviceTreePlane ":" )); 349 maxLen -= len; 350 if (maxLen <= 0) { 351 continue; 352 } 353 354 strlcpy( buf, kIODeviceTreePlane ":", len + 1 ); 355 comp = buf + len; 356 357 len = ((int) strnlen( path, INT_MAX )); 358 maxLen -= len; 359 if (maxLen <= 0) { 360 continue; 361 } 362 strlcpy( comp, path, len + 1 ); 363 364 matching = OSDictionary::withCapacity( 1 ); 365 if (!matching) { 366 continue; 367 } 368 369 str = OSString::withCString( buf ); 370 if (!str) { 371 continue; 372 } 373 matching->setObject( kIOPathMatchKey, str ); 374 str->release(); 375 376 return matching; 377 } while (false); 378 379 if (matching) { 380 matching->release(); 381 } 382 383 return NULL; 384 } 385 386 static int didRam = 0; 387 enum { kMaxPathBuf = 512, kMaxBootVar = 128 }; 388 389 const char* 390 IOGetBootUUID(void) 391 { 392 IORegistryEntry *entry; 393 394 if ((entry = IORegistryEntry::fromPath("/chosen", gIODTPlane))) { 395 OSData *uuid_data = (OSData *)entry->getProperty("boot-uuid"); 396 if (uuid_data) { 397 return (const char*)uuid_data->getBytesNoCopy(); 398 } 399 } 400 401 return NULL; 402 } 403 404 const char * 405 IOGetApfsPrebootUUID(void) 406 { 407 IORegistryEntry *entry; 408 409 if ((entry = IORegistryEntry::fromPath("/chosen", gIODTPlane))) { 410 OSData *uuid_data = (OSData *)entry->getProperty("apfs-preboot-uuid"); 411 if (uuid_data) { 412 return (const char*)uuid_data->getBytesNoCopy(); 413 } 414 } 415 416 return NULL; 417 } 418 419 const char * 420 IOGetAssociatedApfsVolgroupUUID(void) 421 { 422 IORegistryEntry *entry; 423 424 if ((entry = IORegistryEntry::fromPath("/chosen", gIODTPlane))) { 425 OSData *uuid_data = (OSData *)entry->getProperty("associated-volume-group"); 426 if (uuid_data) { 427 return (const char*)uuid_data->getBytesNoCopy(); 428 } 429 } 430 431 return NULL; 432 } 433 434 const char * 435 IOGetBootObjectsPath(void) 436 { 437 IORegistryEntry *entry; 438 439 if ((entry = IORegistryEntry::fromPath("/chosen", gIODTPlane))) { 440 OSData *path_prefix_data = (OSData *)entry->getProperty("boot-objects-path"); 441 if (path_prefix_data) { 442 return (const char *)path_prefix_data->getBytesNoCopy(); 443 } 444 } 445 446 return NULL; 447 } 448 449 /* 450 * Set NVRAM to boot into the right flavor of Recovery, 451 * optionally passing a UUID of a volume that failed to boot. 452 * If `reboot` is true, reboot immediately. 453 * 454 * Returns true if `mode` was understood, false otherwise. 455 * (Does not return if `reboot` is true.) 456 */ 457 boolean_t 458 IOSetRecoveryBoot(bsd_bootfail_mode_t mode, uuid_t volume_uuid, boolean_t reboot) 459 { 460 IODTNVRAM *nvram = NULL; 461 const OSSymbol *boot_command_sym = NULL; 462 OSString *boot_command_recover = NULL; 463 464 if (mode == BSD_BOOTFAIL_SEAL_BROKEN) { 465 const char *boot_mode = "ssv-seal-broken"; 466 uuid_string_t volume_uuid_str; 467 468 // Set `recovery-broken-seal-uuid = <volume_uuid>`. 469 if (volume_uuid) { 470 uuid_unparse_upper(volume_uuid, volume_uuid_str); 471 472 if (!PEWriteNVRAMProperty(SYSTEM_NVRAM_PREFIX "recovery-broken-seal-uuid", 473 volume_uuid_str, sizeof(uuid_string_t))) { 474 IOLog("Failed to write recovery-broken-seal-uuid to NVRAM.\n"); 475 } 476 } 477 478 // Set `recovery-boot-mode = ssv-seal-broken`. 479 if (!PEWriteNVRAMProperty(SYSTEM_NVRAM_PREFIX "recovery-boot-mode", boot_mode, 480 (const unsigned int) strlen(boot_mode))) { 481 IOLog("Failed to write recovery-boot-mode to NVRAM.\n"); 482 } 483 } else if (mode == BSD_BOOTFAIL_MEDIA_MISSING) { 484 const char *boot_picker_reason = "missing-boot-media"; 485 486 // Set `boot-picker-bringup-reason = missing-boot-media`. 487 if (!PEWriteNVRAMProperty(SYSTEM_NVRAM_PREFIX "boot-picker-bringup-reason", 488 boot_picker_reason, (const unsigned int) strlen(boot_picker_reason))) { 489 IOLog("Failed to write boot-picker-bringup-reason to NVRAM.\n"); 490 } 491 492 // Set `boot-command = recover`. 493 494 // Construct an OSSymbol and an OSString to be the (key, value) pair 495 // we write to NVRAM. Unfortunately, since our value must be an OSString 496 // instead of an OSData, we cannot use PEWriteNVRAMProperty() here. 497 boot_command_sym = OSSymbol::withCStringNoCopy(SYSTEM_NVRAM_PREFIX "boot-command"); 498 boot_command_recover = OSString::withCStringNoCopy("recover"); 499 if (boot_command_sym == NULL || boot_command_recover == NULL) { 500 IOLog("Failed to create boot-command strings.\n"); 501 goto do_reboot; 502 } 503 504 // Wait for NVRAM to be readable... 505 nvram = OSDynamicCast(IODTNVRAM, IOService::waitForService( 506 IOService::serviceMatching("IODTNVRAM"))); 507 if (nvram == NULL) { 508 IOLog("Failed to acquire IODTNVRAM object.\n"); 509 goto do_reboot; 510 } 511 512 // Wait for NVRAM to be writable... 513 if (!IOServiceWaitForMatchingResource("IONVRAM", UINT64_MAX)) { 514 IOLog("Failed to wait for IONVRAM service.\n"); 515 // attempt the work anyway... 516 } 517 518 // Write the new boot-command to NVRAM, and sync if successful. 519 if (!nvram->setProperty(boot_command_sym, boot_command_recover)) { 520 IOLog("Failed to save new boot-command to NVRAM.\n"); 521 } else { 522 nvram->sync(); 523 } 524 } else { 525 IOLog("Unknown mode: %d\n", mode); 526 return false; 527 } 528 529 // Clean up and reboot! 530 do_reboot: 531 if (nvram != NULL) { 532 nvram->release(); 533 } 534 535 if (boot_command_recover != NULL) { 536 boot_command_recover->release(); 537 } 538 539 if (boot_command_sym != NULL) { 540 boot_command_sym->release(); 541 } 542 543 if (reboot) { 544 IOLog("\nAbout to reboot into Recovery!\n"); 545 (void)PEHaltRestart(kPERestartCPU); 546 } 547 548 return true; 549 } 550 551 kern_return_t 552 IOFindBSDRoot( char * rootName, unsigned int rootNameSize, 553 dev_t * root, u_int32_t * oflags ) 554 { 555 mach_timespec_t t; 556 IOService * service; 557 IORegistryEntry * regEntry; 558 OSDictionary * matching = NULL; 559 OSString * iostr; 560 OSNumber * off; 561 OSData * data = NULL; 562 563 UInt32 flags = 0; 564 int mnr, mjr; 565 const char * mediaProperty = NULL; 566 char * rdBootVar; 567 char * str; 568 const char * look = NULL; 569 int len; 570 bool debugInfoPrintedOnce = false; 571 bool needNetworkKexts = false; 572 const char * uuidStr = NULL; 573 574 static int mountAttempts = 0; 575 576 int xchar, dchar; 577 578 // stall here for anyone matching on the IOBSD resource to finish (filesystems) 579 matching = IOService::serviceMatching(gIOResourcesKey); 580 assert(matching); 581 matching->setObject(gIOResourceMatchedKey, gIOBSDKey); 582 583 if ((service = IOService::waitForMatchingService(matching, 30ULL * kSecondScale))) { 584 service->release(); 585 } else { 586 IOLog("!BSD\n"); 587 } 588 matching->release(); 589 matching = NULL; 590 591 if (mountAttempts++) { 592 IOLog("mount(%d) failed\n", mountAttempts); 593 IOSleep( 5 * 1000 ); 594 } 595 596 str = (char *) IOMalloc( kMaxPathBuf + kMaxBootVar ); 597 if (!str) { 598 return kIOReturnNoMemory; 599 } 600 rdBootVar = str + kMaxPathBuf; 601 602 if (!PE_parse_boot_argn("rd", rdBootVar, kMaxBootVar ) 603 && !PE_parse_boot_argn("rootdev", rdBootVar, kMaxBootVar )) { 604 rdBootVar[0] = 0; 605 } 606 607 do { 608 if ((regEntry = IORegistryEntry::fromPath( "/chosen", gIODTPlane ))) { 609 di_root_ramfile(regEntry); 610 data = OSDynamicCast(OSData, regEntry->getProperty( "root-matching" )); 611 if (data) { 612 matching = OSDynamicCast(OSDictionary, OSUnserializeXML((char *)data->getBytesNoCopy())); 613 if (matching) { 614 continue; 615 } 616 } 617 618 data = (OSData *) regEntry->getProperty( "boot-uuid" ); 619 if (data) { 620 uuidStr = (const char*)data->getBytesNoCopy(); 621 OSString *uuidString = OSString::withCString( uuidStr ); 622 623 // match the boot-args boot-uuid processing below 624 if (uuidString) { 625 IOLog("rooting via boot-uuid from /chosen: %s\n", uuidStr); 626 IOService::publishResource( "boot-uuid", uuidString ); 627 uuidString->release(); 628 matching = IOUUIDMatching(); 629 mediaProperty = "boot-uuid-media"; 630 regEntry->release(); 631 continue; 632 } else { 633 uuidStr = NULL; 634 } 635 } 636 regEntry->release(); 637 } 638 } while (false); 639 640 // 641 // See if we have a RAMDisk property in /chosen/memory-map. If so, make it into a device. 642 // It will become /dev/mdx, where x is 0-f. 643 // 644 645 if (!didRam) { /* Have we already build this ram disk? */ 646 didRam = 1; /* Remember we did this */ 647 if ((regEntry = IORegistryEntry::fromPath( "/chosen/memory-map", gIODTPlane ))) { /* Find the map node */ 648 data = (OSData *)regEntry->getProperty("RAMDisk"); /* Find the ram disk, if there */ 649 if (data) { /* We found one */ 650 uintptr_t *ramdParms; 651 ramdParms = (uintptr_t *)data->getBytesNoCopy(); /* Point to the ram disk base and size */ 652 #if __LP64__ 653 #define MAX_PHYS_RAM (((uint64_t)UINT_MAX) << 12) 654 if (ramdParms[1] > MAX_PHYS_RAM) { 655 panic("ramdisk params"); 656 } 657 #endif /* __LP64__ */ 658 (void)mdevadd(-1, ml_static_ptovirt(ramdParms[0]) >> 12, (unsigned int) (ramdParms[1] >> 12), 0); /* Initialize it and pass back the device number */ 659 } 660 regEntry->release(); /* Toss the entry */ 661 } 662 } 663 664 // 665 // Now check if we are trying to root on a memory device 666 // 667 668 if ((rdBootVar[0] == 'm') && (rdBootVar[1] == 'd') && (rdBootVar[3] == 0)) { 669 dchar = xchar = rdBootVar[2]; /* Get the actual device */ 670 if ((xchar >= '0') && (xchar <= '9')) { 671 xchar = xchar - '0'; /* If digit, convert */ 672 } else { 673 xchar = xchar & ~' '; /* Fold to upper case */ 674 if ((xchar >= 'A') && (xchar <= 'F')) { /* Is this a valid digit? */ 675 xchar = (xchar & 0xF) + 9; /* Convert the hex digit */ 676 dchar = dchar | ' '; /* Fold to lower case */ 677 } else { 678 xchar = -1; /* Show bogus */ 679 } 680 } 681 if (xchar >= 0) { /* Do we have a valid memory device name? */ 682 *root = mdevlookup(xchar); /* Find the device number */ 683 if (*root >= 0) { /* Did we find one? */ 684 rootName[0] = 'm'; /* Build root name */ 685 rootName[1] = 'd'; /* Build root name */ 686 rootName[2] = (char) dchar; /* Build root name */ 687 rootName[3] = 0; /* Build root name */ 688 IOLog("BSD root: %s, major %d, minor %d\n", rootName, major(*root), minor(*root)); 689 *oflags = 0; /* Show that this is not network */ 690 691 #if CONFIG_KDP_INTERACTIVE_DEBUGGING 692 /* retrieve final ramdisk range and initialize KDP variables */ 693 if (mdevgetrange(xchar, &kdp_core_ramdisk_addr, &kdp_core_ramdisk_size) != 0) { 694 IOLog("Unable to retrieve range for root memory device %d\n", xchar); 695 kdp_core_ramdisk_addr = 0; 696 kdp_core_ramdisk_size = 0; 697 } 698 #endif 699 700 goto iofrootx; /* Join common exit... */ 701 } 702 panic("IOFindBSDRoot: specified root memory device, %s, has not been configured\n", rdBootVar); /* Not there */ 703 } 704 } 705 706 if ((!matching) && rdBootVar[0]) { 707 // by BSD name 708 look = rdBootVar; 709 if (look[0] == '*') { 710 look++; 711 } 712 713 if (strncmp( look, "en", strlen( "en" )) == 0) { 714 matching = IONetworkNamePrefixMatching( "en" ); 715 needNetworkKexts = true; 716 } else if (strncmp( look, "uuid", strlen( "uuid" )) == 0) { 717 char *uuid; 718 OSString *uuidString; 719 720 uuid = (char *)IOMalloc( kMaxBootVar ); 721 722 if (uuid) { 723 if (!PE_parse_boot_argn( "boot-uuid", uuid, kMaxBootVar )) { 724 panic( "rd=uuid but no boot-uuid=<value> specified" ); 725 } 726 uuidString = OSString::withCString( uuid ); 727 if (uuidString) { 728 IOService::publishResource( "boot-uuid", uuidString ); 729 uuidString->release(); 730 IOLog( "\nWaiting for boot volume with UUID %s\n", uuid ); 731 matching = IOUUIDMatching(); 732 mediaProperty = "boot-uuid-media"; 733 } 734 IOFree( uuid, kMaxBootVar ); 735 } 736 } else { 737 matching = IOBSDNameMatching( look ); 738 } 739 } 740 741 if (!matching) { 742 OSString * astring; 743 // Match any HFS media 744 745 matching = IOService::serviceMatching( "IOMedia" ); 746 astring = OSString::withCStringNoCopy("Apple_HFS"); 747 if (astring) { 748 matching->setObject("Content", astring); 749 astring->release(); 750 } 751 } 752 753 if (gIOKitDebug & kIOWaitQuietBeforeRoot) { 754 IOLog( "Waiting for matching to complete\n" ); 755 IOService::getPlatform()->waitQuiet(); 756 } 757 758 if (true && matching) { 759 OSSerialize * s = OSSerialize::withCapacity( 5 ); 760 761 if (matching->serialize( s )) { 762 IOLog( "Waiting on %s\n", s->text()); 763 s->release(); 764 } 765 } 766 767 char namep[8]; 768 if (needNetworkKexts 769 || PE_parse_boot_argn("-s", namep, sizeof(namep))) { 770 IOService::startDeferredMatches(); 771 } 772 773 do { 774 t.tv_sec = ROOTDEVICETIMEOUT; 775 t.tv_nsec = 0; 776 matching->retain(); 777 service = IOService::waitForService( matching, &t ); 778 if ((!service) || (mountAttempts == 10)) { 779 #if !XNU_TARGET_OS_OSX || !defined(__arm64__) 780 PE_display_icon( 0, "noroot"); 781 IOLog( "Still waiting for root device\n" ); 782 #endif 783 784 if (!debugInfoPrintedOnce) { 785 debugInfoPrintedOnce = true; 786 if (gIOKitDebug & kIOLogDTree) { 787 IOLog("\nDT plane:\n"); 788 IOPrintPlane( gIODTPlane ); 789 } 790 if (gIOKitDebug & kIOLogServiceTree) { 791 IOLog("\nService plane:\n"); 792 IOPrintPlane( gIOServicePlane ); 793 } 794 if (gIOKitDebug & kIOLogMemory) { 795 IOPrintMemory(); 796 } 797 } 798 799 #if XNU_TARGET_OS_OSX && defined(__arm64__) 800 // The disk isn't found - have the user pick from recoveryOS+. 801 (void)IOSetRecoveryBoot(BSD_BOOTFAIL_MEDIA_MISSING, NULL, true); 802 #endif 803 } 804 } while (!service); 805 matching->release(); 806 807 if (service && mediaProperty) { 808 service = (IOService *)service->getProperty(mediaProperty); 809 } 810 811 mjr = 0; 812 mnr = 0; 813 814 // If the IOService we matched to is a subclass of IONetworkInterface, 815 // then make sure it has been registered with BSD and has a BSD name 816 // assigned. 817 818 if (service 819 && service->metaCast( "IONetworkInterface" ) 820 && !IORegisterNetworkInterface( service )) { 821 service = NULL; 822 } 823 824 if (service) { 825 len = kMaxPathBuf; 826 service->getPath( str, &len, gIOServicePlane ); 827 IOLog( "Got boot device = %s\n", str ); 828 829 iostr = (OSString *) service->getProperty( kIOBSDNameKey ); 830 if (iostr) { 831 strlcpy( rootName, iostr->getCStringNoCopy(), rootNameSize ); 832 } 833 off = (OSNumber *) service->getProperty( kIOBSDMajorKey ); 834 if (off) { 835 mjr = off->unsigned32BitValue(); 836 } 837 off = (OSNumber *) service->getProperty( kIOBSDMinorKey ); 838 if (off) { 839 mnr = off->unsigned32BitValue(); 840 } 841 842 if (service->metaCast( "IONetworkInterface" )) { 843 flags |= 1; 844 } 845 } else { 846 IOLog( "Wait for root failed\n" ); 847 strlcpy( rootName, "en0", rootNameSize ); 848 flags |= 1; 849 } 850 851 IOLog( "BSD root: %s", rootName ); 852 if (mjr) { 853 IOLog(", major %d, minor %d\n", mjr, mnr ); 854 } else { 855 IOLog("\n"); 856 } 857 858 *root = makedev( mjr, mnr ); 859 *oflags = flags; 860 861 IOFree( str, kMaxPathBuf + kMaxBootVar ); 862 863 iofrootx: 864 if ((gIOKitDebug & (kIOLogDTree | kIOLogServiceTree | kIOLogMemory)) && !debugInfoPrintedOnce) { 865 IOService::getPlatform()->waitQuiet(); 866 if (gIOKitDebug & kIOLogDTree) { 867 IOLog("\nDT plane:\n"); 868 IOPrintPlane( gIODTPlane ); 869 } 870 if (gIOKitDebug & kIOLogServiceTree) { 871 IOLog("\nService plane:\n"); 872 IOPrintPlane( gIOServicePlane ); 873 } 874 if (gIOKitDebug & kIOLogMemory) { 875 IOPrintMemory(); 876 } 877 } 878 879 return kIOReturnSuccess; 880 } 881 882 bool 883 IORamDiskBSDRoot(void) 884 { 885 char rdBootVar[kMaxBootVar]; 886 if (PE_parse_boot_argn("rd", rdBootVar, kMaxBootVar ) 887 || PE_parse_boot_argn("rootdev", rdBootVar, kMaxBootVar )) { 888 if ((rdBootVar[0] == 'm') && (rdBootVar[1] == 'd') && (rdBootVar[3] == 0)) { 889 return true; 890 } 891 } 892 return false; 893 } 894 895 void 896 IOSecureBSDRoot(const char * rootName) 897 { 898 #if CONFIG_SECURE_BSD_ROOT 899 IOReturn result; 900 IOPlatformExpert *pe; 901 OSDictionary *matching; 902 const OSSymbol *functionName = OSSymbol::withCStringNoCopy("SecureRootName"); 903 904 matching = IOService::serviceMatching("IOPlatformExpert"); 905 assert(matching); 906 pe = (IOPlatformExpert *) IOService::waitForMatchingService(matching, 30ULL * kSecondScale); 907 matching->release(); 908 assert(pe); 909 // Returns kIOReturnNotPrivileged is the root device is not secure. 910 // Returns kIOReturnUnsupported if "SecureRootName" is not implemented. 911 result = pe->callPlatformFunction(functionName, false, (void *)rootName, (void *)NULL, (void *)NULL, (void *)NULL); 912 functionName->release(); 913 OSSafeReleaseNULL(pe); 914 915 if (result == kIOReturnNotPrivileged) { 916 mdevremoveall(); 917 } 918 919 #endif // CONFIG_SECURE_BSD_ROOT 920 } 921 922 void * 923 IOBSDRegistryEntryForDeviceTree(char * path) 924 { 925 return IORegistryEntry::fromPath(path, gIODTPlane); 926 } 927 928 void 929 IOBSDRegistryEntryRelease(void * entry) 930 { 931 IORegistryEntry * regEntry = (IORegistryEntry *)entry; 932 933 if (regEntry) { 934 regEntry->release(); 935 } 936 return; 937 } 938 939 const void * 940 IOBSDRegistryEntryGetData(void * entry, char * property_name, 941 int * packet_length) 942 { 943 OSData * data; 944 IORegistryEntry * regEntry = (IORegistryEntry *)entry; 945 946 data = (OSData *) regEntry->getProperty(property_name); 947 if (data) { 948 *packet_length = data->getLength(); 949 return data->getBytesNoCopy(); 950 } 951 return NULL; 952 } 953 954 kern_return_t 955 IOBSDGetPlatformUUID( uuid_t uuid, mach_timespec_t timeout ) 956 { 957 IOService * resources; 958 OSString * string; 959 960 resources = IOService::waitForService( IOService::resourceMatching( kIOPlatformUUIDKey ), (timeout.tv_sec || timeout.tv_nsec) ? &timeout : NULL ); 961 if (resources == NULL) { 962 return KERN_OPERATION_TIMED_OUT; 963 } 964 965 string = (OSString *) IOService::getPlatform()->getProvider()->getProperty( kIOPlatformUUIDKey ); 966 if (string == NULL) { 967 return KERN_NOT_SUPPORTED; 968 } 969 970 uuid_parse( string->getCStringNoCopy(), uuid ); 971 972 return KERN_SUCCESS; 973 } 974 } /* extern "C" */ 975 976 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 977 978 #include <sys/conf.h> 979 #include <sys/vnode.h> 980 #include <sys/vnode_internal.h> 981 #include <sys/fcntl.h> 982 #include <IOKit/IOPolledInterface.h> 983 #include <IOKit/IOBufferMemoryDescriptor.h> 984 985 IOPolledFileIOVars * gIOPolledCoreFileVars; 986 kern_return_t gIOPolledCoreFileOpenRet = kIOReturnNotReady; 987 IOPolledCoreFileMode_t gIOPolledCoreFileMode = kIOPolledCoreFileModeNotInitialized; 988 989 #if IOPOLLED_COREFILE 990 991 #if defined(XNU_TARGET_OS_BRIDGE) 992 // On bridgeOS allocate a 150MB corefile and leave 150MB free 993 #define kIOCoreDumpSize 150ULL*1024ULL*1024ULL 994 #define kIOCoreDumpFreeSize 150ULL*1024ULL*1024ULL 995 996 #elif !defined(XNU_TARGET_OS_OSX) /* defined(XNU_TARGET_OS_BRIDGE) */ 997 // On embedded devices with >3GB DRAM we allocate a 500MB corefile 998 // otherwise allocate a 350MB corefile. Leave 350 MB free 999 1000 #define kIOCoreDumpMinSize 350ULL*1024ULL*1024ULL 1001 #define kIOCoreDumpLargeSize 500ULL*1024ULL*1024ULL 1002 1003 #define kIOCoreDumpFreeSize 350ULL*1024ULL*1024ULL 1004 1005 #else /* defined(XNU_TARGET_OS_BRIDGE) */ 1006 // on macOS devices allocate a corefile sized at 1GB / 32GB of DRAM, 1007 // fallback to a 1GB corefile and leave at least 1GB free 1008 #define kIOCoreDumpMinSize 1024ULL*1024ULL*1024ULL 1009 #define kIOCoreDumpIncrementalSize 1024ULL*1024ULL*1024ULL 1010 1011 #define kIOCoreDumpFreeSize 1024ULL*1024ULL*1024ULL 1012 1013 // on older macOS devices we allocate a 1MB file at boot 1014 // to store a panic time stackshot 1015 #define kIOStackshotFileSize 1024ULL*1024ULL 1016 1017 #endif /* defined(XNU_TARGET_OS_BRIDGE) */ 1018 1019 static IOPolledCoreFileMode_t 1020 GetCoreFileMode() 1021 { 1022 if (on_device_corefile_enabled()) { 1023 return kIOPolledCoreFileModeCoredump; 1024 } else if (panic_stackshot_to_disk_enabled()) { 1025 return kIOPolledCoreFileModeStackshot; 1026 } else { 1027 return kIOPolledCoreFileModeDisabled; 1028 } 1029 } 1030 1031 static void 1032 IOCoreFileGetSize(uint64_t *ideal_size, uint64_t *fallback_size, uint64_t *free_space_to_leave, IOPolledCoreFileMode_t mode) 1033 { 1034 unsigned int requested_corefile_size = 0; 1035 1036 *ideal_size = *fallback_size = *free_space_to_leave = 0; 1037 1038 #if defined(XNU_TARGET_OS_BRIDGE) 1039 #pragma unused(mode) 1040 *ideal_size = *fallback_size = kIOCoreDumpSize; 1041 *free_space_to_leave = kIOCoreDumpFreeSize; 1042 #elif !defined(XNU_TARGET_OS_OSX) /* defined(XNU_TARGET_OS_BRIDGE) */ 1043 #pragma unused(mode) 1044 *ideal_size = *fallback_size = kIOCoreDumpMinSize; 1045 1046 if (max_mem > (3 * 1024ULL * 1024ULL * 1024ULL)) { 1047 *ideal_size = kIOCoreDumpLargeSize; 1048 } 1049 1050 *free_space_to_leave = kIOCoreDumpFreeSize; 1051 #else /* defined(XNU_TARGET_OS_BRIDGE) */ 1052 if (mode == kIOPolledCoreFileModeCoredump) { 1053 *ideal_size = *fallback_size = kIOCoreDumpMinSize; 1054 if (kIOCoreDumpIncrementalSize != 0 && max_mem > (32 * 1024ULL * 1024ULL * 1024ULL)) { 1055 *ideal_size = ((ROUNDUP(max_mem, (32 * 1024ULL * 1024ULL * 1024ULL)) / (32 * 1024ULL * 1024ULL * 1024ULL)) * kIOCoreDumpIncrementalSize); 1056 } 1057 *free_space_to_leave = kIOCoreDumpFreeSize; 1058 } else if (mode == kIOPolledCoreFileModeStackshot) { 1059 *ideal_size = *fallback_size = *free_space_to_leave = kIOStackshotFileSize; 1060 } 1061 #endif /* defined(XNU_TARGET_OS_BRIDGE) */ 1062 // If a custom size was requested, override the ideal and requested sizes 1063 if (PE_parse_boot_argn("corefile_size_mb", &requested_corefile_size, sizeof(requested_corefile_size))) { 1064 IOLog("Boot-args specify %d MB kernel corefile\n", requested_corefile_size); 1065 1066 *ideal_size = *fallback_size = (requested_corefile_size * 1024ULL * 1024ULL); 1067 } 1068 1069 return; 1070 } 1071 1072 static void 1073 IOOpenPolledCoreFile(thread_call_param_t __unused, thread_call_param_t corefilename) 1074 { 1075 assert(corefilename != NULL); 1076 1077 IOReturn err; 1078 char *filename = (char *) corefilename; 1079 uint64_t corefile_size_bytes = 0, corefile_fallback_size_bytes = 0, free_space_to_leave_bytes = 0; 1080 IOPolledCoreFileMode_t mode_to_init = GetCoreFileMode(); 1081 1082 if (gIOPolledCoreFileVars) { 1083 return; 1084 } 1085 if (!IOPolledInterface::gMetaClass.getInstanceCount()) { 1086 return; 1087 } 1088 1089 if (mode_to_init == kIOPolledCoreFileModeDisabled) { 1090 gIOPolledCoreFileMode = kIOPolledCoreFileModeDisabled; 1091 return; 1092 } 1093 1094 // We'll overwrite this once we open the file, we update this to mark that we have made 1095 // it past initialization 1096 gIOPolledCoreFileMode = kIOPolledCoreFileModeClosed; 1097 1098 IOCoreFileGetSize(&corefile_size_bytes, &corefile_fallback_size_bytes, &free_space_to_leave_bytes, mode_to_init); 1099 1100 do { 1101 err = IOPolledFileOpen(filename, kIOPolledFileCreate, corefile_size_bytes, free_space_to_leave_bytes, 1102 NULL, 0, &gIOPolledCoreFileVars, NULL, NULL, NULL); 1103 if (kIOReturnSuccess == err) { 1104 break; 1105 } else if (kIOReturnNoSpace == err) { 1106 IOLog("Failed to open corefile of size %llu MB (low disk space)", 1107 (corefile_size_bytes / (1024ULL * 1024ULL))); 1108 if (corefile_size_bytes == corefile_fallback_size_bytes) { 1109 gIOPolledCoreFileOpenRet = err; 1110 return; 1111 } 1112 } else { 1113 IOLog("Failed to open corefile of size %llu MB (returned error 0x%x)\n", 1114 (corefile_size_bytes / (1024ULL * 1024ULL)), err); 1115 gIOPolledCoreFileOpenRet = err; 1116 return; 1117 } 1118 1119 err = IOPolledFileOpen(filename, kIOPolledFileCreate, corefile_fallback_size_bytes, free_space_to_leave_bytes, 1120 NULL, 0, &gIOPolledCoreFileVars, NULL, NULL, NULL); 1121 if (kIOReturnSuccess != err) { 1122 IOLog("Failed to open corefile of size %llu MB (returned error 0x%x)\n", 1123 (corefile_fallback_size_bytes / (1024ULL * 1024ULL)), err); 1124 gIOPolledCoreFileOpenRet = err; 1125 return; 1126 } 1127 } while (false); 1128 1129 gIOPolledCoreFileOpenRet = IOPolledFilePollersSetup(gIOPolledCoreFileVars, kIOPolledPreflightCoreDumpState); 1130 if (kIOReturnSuccess != gIOPolledCoreFileOpenRet) { 1131 IOPolledFileClose(&gIOPolledCoreFileVars, 0, NULL, 0, 0, 0); 1132 IOLog("IOPolledFilePollersSetup for corefile failed with error: 0x%x\n", err); 1133 } else { 1134 IOLog("Opened corefile of size %llu MB\n", (corefile_size_bytes / (1024ULL * 1024ULL))); 1135 gIOPolledCoreFileMode = mode_to_init; 1136 } 1137 1138 return; 1139 } 1140 1141 static void 1142 IOClosePolledCoreFile(void) 1143 { 1144 gIOPolledCoreFileOpenRet = kIOReturnNotOpen; 1145 gIOPolledCoreFileMode = kIOPolledCoreFileModeClosed; 1146 IOPolledFilePollersClose(gIOPolledCoreFileVars, kIOPolledPostflightCoreDumpState); 1147 IOPolledFileClose(&gIOPolledCoreFileVars, 0, NULL, 0, 0, 0); 1148 } 1149 1150 #endif /* IOPOLLED_COREFILE */ 1151 1152 extern "C" void 1153 IOBSDMountChange(struct mount * mp, uint32_t op) 1154 { 1155 #if IOPOLLED_COREFILE 1156 uint64_t flags; 1157 char path[128]; 1158 int pathLen; 1159 vnode_t vn; 1160 int result; 1161 1162 switch (op) { 1163 case kIOMountChangeMount: 1164 case kIOMountChangeDidResize: 1165 1166 if (gIOPolledCoreFileVars) { 1167 break; 1168 } 1169 flags = vfs_flags(mp); 1170 if (MNT_RDONLY & flags) { 1171 break; 1172 } 1173 if (!(MNT_LOCAL & flags)) { 1174 break; 1175 } 1176 1177 vn = vfs_vnodecovered(mp); 1178 if (!vn) { 1179 break; 1180 } 1181 pathLen = sizeof(path); 1182 result = vn_getpath(vn, &path[0], &pathLen); 1183 vnode_put(vn); 1184 if (0 != result) { 1185 break; 1186 } 1187 if (!pathLen) { 1188 break; 1189 } 1190 #if defined(XNU_TARGET_OS_BRIDGE) 1191 // on bridgeOS systems we put the core in /private/var/internal. We don't 1192 // want to match with /private/var because /private/var/internal is often mounted 1193 // over /private/var 1194 if ((pathLen - 1) < (int) strlen("/private/var/internal")) { 1195 break; 1196 } 1197 #endif 1198 if (0 != strncmp(path, kIOCoreDumpPath, pathLen - 1)) { 1199 break; 1200 } 1201 1202 thread_call_enter1(corefile_open_call, (void *) kIOCoreDumpPath); 1203 break; 1204 1205 case kIOMountChangeUnmount: 1206 case kIOMountChangeWillResize: 1207 if (gIOPolledCoreFileVars && (mp == kern_file_mount(gIOPolledCoreFileVars->fileRef))) { 1208 thread_call_cancel_wait(corefile_open_call); 1209 IOClosePolledCoreFile(); 1210 } 1211 break; 1212 } 1213 #endif /* IOPOLLED_COREFILE */ 1214 } 1215 1216 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 1217 1218 extern "C" boolean_t 1219 IOTaskHasEntitlement(task_t task, const char * entitlement) 1220 { 1221 OSObject * obj; 1222 obj = IOUserClient::copyClientEntitlement(task, entitlement); 1223 if (!obj) { 1224 return false; 1225 } 1226 obj->release(); 1227 return obj != kOSBooleanFalse; 1228 } 1229 1230 extern "C" boolean_t 1231 IOVnodeHasEntitlement(vnode_t vnode, int64_t off, const char *entitlement) 1232 { 1233 OSObject * obj; 1234 off_t offset = (off_t)off; 1235 1236 obj = IOUserClient::copyClientEntitlementVnode(vnode, offset, entitlement); 1237 if (!obj) { 1238 return false; 1239 } 1240 obj->release(); 1241 return obj != kOSBooleanFalse; 1242 } 1243 1244 extern "C" char * 1245 IOVnodeGetEntitlement(vnode_t vnode, int64_t off, const char *entitlement) 1246 { 1247 OSObject *obj = NULL; 1248 OSString *str = NULL; 1249 size_t len; 1250 char *value = NULL; 1251 off_t offset = (off_t)off; 1252 1253 obj = IOUserClient::copyClientEntitlementVnode(vnode, offset, entitlement); 1254 if (obj != NULL) { 1255 str = OSDynamicCast(OSString, obj); 1256 if (str != NULL) { 1257 len = str->getLength() + 1; 1258 value = (char *)kheap_alloc(KHEAP_DATA_BUFFERS, len, Z_WAITOK); 1259 strlcpy(value, str->getCStringNoCopy(), len); 1260 } 1261 obj->release(); 1262 } 1263 return value; 1264 } 1265