1 /* 2 * Copyright (c) 1998-2021 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 #include <libkern/c++/OSAllocation.h> 38 39 extern "C" { 40 #include <libkern/amfi/amfi.h> 41 #include <sys/codesign.h> 42 #include <sys/code_signing.h> 43 #include <vm/pmap.h> 44 #include <vm/vm_map.h> 45 #include <pexpert/pexpert.h> 46 #include <kern/clock.h> 47 #if CONFIG_KDP_INTERACTIVE_DEBUGGING 48 #include <kern/debug.h> 49 #endif 50 #include <mach/machine.h> 51 #include <uuid/uuid.h> 52 #include <sys/vnode_internal.h> 53 #include <sys/mount.h> 54 #include <corecrypto/ccsha2.h> 55 56 // how long to wait for matching root device, secs 57 #if DEBUG 58 #define ROOTDEVICETIMEOUT 120 59 #else 60 #define ROOTDEVICETIMEOUT 60 61 #endif 62 63 extern dev_t mdevadd(int devid, uint64_t base, unsigned int size, int phys); 64 extern dev_t mdevlookup(int devid); 65 extern void mdevremoveall(void); 66 extern int mdevgetrange(int devid, uint64_t *base, uint64_t *size); 67 extern void di_root_ramfile(IORegistryEntry * entry); 68 extern int IODTGetDefault(const char *key, void *infoAddr, unsigned int infoSize); 69 extern boolean_t cpuid_vmm_present(void); 70 71 #define ROUNDUP(a, b) (((a) + ((b) - 1)) & (~((b) - 1))) 72 73 #define IOPOLLED_COREFILE (CONFIG_KDP_INTERACTIVE_DEBUGGING) 74 75 #if defined(XNU_TARGET_OS_BRIDGE) 76 #define kIOCoreDumpPath "/private/var/internal/kernelcore" 77 #elif defined(XNU_TARGET_OS_OSX) 78 #define kIOCoreDumpPath "/System/Volumes/VM/kernelcore" 79 #else 80 #define kIOCoreDumpPath "/private/var/vm/kernelcore" 81 #endif 82 83 #define SYSTEM_NVRAM_PREFIX "40A0DDD2-77F8-4392-B4A3-1E7304206516:" 84 85 #if CONFIG_KDP_INTERACTIVE_DEBUGGING 86 /* 87 * Touched by IOFindBSDRoot() if a RAMDisk is used for the root device. 88 */ 89 extern uint64_t kdp_core_ramdisk_addr; 90 extern uint64_t kdp_core_ramdisk_size; 91 92 /* 93 * A callback to indicate that the polled-mode corefile is now available. 94 */ 95 extern kern_return_t kdp_core_polled_io_polled_file_available(IOCoreFileAccessCallback access_data, void *access_context, void *recipient_context); 96 97 /* 98 * A callback to indicate that the polled-mode corefile is no longer available. 99 */ 100 extern kern_return_t kdp_core_polled_io_polled_file_unavailable(void); 101 #endif 102 103 #if IOPOLLED_COREFILE 104 static void IOOpenPolledCoreFile(thread_call_param_t __unused, thread_call_param_t corefilename); 105 106 thread_call_t corefile_open_call = NULL; 107 #endif 108 109 kern_return_t 110 IOKitBSDInit( void ) 111 { 112 IOService::publishResource("IOBSD"); 113 114 #if IOPOLLED_COREFILE 115 corefile_open_call = thread_call_allocate_with_options(IOOpenPolledCoreFile, NULL, THREAD_CALL_PRIORITY_KERNEL, THREAD_CALL_OPTIONS_ONCE); 116 #endif 117 118 return kIOReturnSuccess; 119 } 120 121 void 122 IOServicePublishResource( const char * property, boolean_t value ) 123 { 124 if (value) { 125 IOService::publishResource( property, kOSBooleanTrue ); 126 } else { 127 IOService::getResourceService()->removeProperty( property ); 128 } 129 } 130 131 boolean_t 132 IOServiceWaitForMatchingResource( const char * property, uint64_t timeout ) 133 { 134 OSDictionary * dict = NULL; 135 IOService * match = NULL; 136 boolean_t found = false; 137 138 do { 139 dict = IOService::resourceMatching( property ); 140 if (!dict) { 141 continue; 142 } 143 match = IOService::waitForMatchingService( dict, timeout ); 144 if (match) { 145 found = true; 146 } 147 } while (false); 148 149 if (dict) { 150 dict->release(); 151 } 152 if (match) { 153 match->release(); 154 } 155 156 return found; 157 } 158 159 boolean_t 160 IOCatalogueMatchingDriversPresent( const char * property ) 161 { 162 OSDictionary * dict = NULL; 163 OSOrderedSet * set = NULL; 164 SInt32 generationCount = 0; 165 boolean_t found = false; 166 167 do { 168 dict = OSDictionary::withCapacity(1); 169 if (!dict) { 170 continue; 171 } 172 dict->setObject( property, kOSBooleanTrue ); 173 set = gIOCatalogue->findDrivers( dict, &generationCount ); 174 if (set && (set->getCount() > 0)) { 175 found = true; 176 } 177 } while (false); 178 179 if (dict) { 180 dict->release(); 181 } 182 if (set) { 183 set->release(); 184 } 185 186 return found; 187 } 188 189 OSDictionary * 190 IOBSDNameMatching( const char * name ) 191 { 192 OSDictionary * dict; 193 const OSSymbol * str = NULL; 194 195 do { 196 dict = IOService::serviceMatching( gIOServiceKey ); 197 if (!dict) { 198 continue; 199 } 200 str = OSSymbol::withCString( name ); 201 if (!str) { 202 continue; 203 } 204 dict->setObject( kIOBSDNameKey, (OSObject *) str ); 205 str->release(); 206 207 return dict; 208 } while (false); 209 210 if (dict) { 211 dict->release(); 212 } 213 if (str) { 214 str->release(); 215 } 216 217 return NULL; 218 } 219 220 OSDictionary * 221 IOUUIDMatching( void ) 222 { 223 return IOService::resourceMatching( "boot-uuid-media" ); 224 } 225 226 OSDictionary * 227 IONetworkNamePrefixMatching( const char * prefix ) 228 { 229 OSDictionary * matching; 230 OSDictionary * propDict = NULL; 231 const OSSymbol * str = NULL; 232 char networkType[128]; 233 234 do { 235 matching = IOService::serviceMatching( "IONetworkInterface" ); 236 if (matching == NULL) { 237 continue; 238 } 239 240 propDict = OSDictionary::withCapacity(1); 241 if (propDict == NULL) { 242 continue; 243 } 244 245 str = OSSymbol::withCString( prefix ); 246 if (str == NULL) { 247 continue; 248 } 249 250 propDict->setObject( "IOInterfaceNamePrefix", (OSObject *) str ); 251 str->release(); 252 str = NULL; 253 254 // see if we're contrained to netroot off of specific network type 255 if (PE_parse_boot_argn( "network-type", networkType, 128 )) { 256 str = OSSymbol::withCString( networkType ); 257 if (str) { 258 propDict->setObject( "IONetworkRootType", str); 259 str->release(); 260 str = NULL; 261 } 262 } 263 264 if (matching->setObject( gIOPropertyMatchKey, 265 (OSObject *) propDict ) != true) { 266 continue; 267 } 268 269 propDict->release(); 270 propDict = NULL; 271 272 return matching; 273 } while (false); 274 275 if (matching) { 276 matching->release(); 277 } 278 if (propDict) { 279 propDict->release(); 280 } 281 if (str) { 282 str->release(); 283 } 284 285 return NULL; 286 } 287 288 static bool 289 IORegisterNetworkInterface( IOService * netif ) 290 { 291 // A network interface is typically named and registered 292 // with BSD after receiving a request from a user space 293 // "namer". However, for cases when the system needs to 294 // root from the network, this registration task must be 295 // done inside the kernel and completed before the root 296 // device is handed to BSD. 297 298 IOService * stack; 299 OSNumber * zero = NULL; 300 OSString * path = NULL; 301 OSDictionary * dict = NULL; 302 OSDataAllocation<char> pathBuf; 303 int len; 304 enum { kMaxPathLen = 512 }; 305 306 do { 307 stack = IOService::waitForService( 308 IOService::serviceMatching("IONetworkStack")); 309 if (stack == NULL) { 310 break; 311 } 312 313 dict = OSDictionary::withCapacity(3); 314 if (dict == NULL) { 315 break; 316 } 317 318 zero = OSNumber::withNumber((UInt64) 0, 32); 319 if (zero == NULL) { 320 break; 321 } 322 323 pathBuf = OSDataAllocation<char>( kMaxPathLen, OSAllocateMemory ); 324 if (!pathBuf) { 325 break; 326 } 327 328 len = kMaxPathLen; 329 if (netif->getPath( pathBuf.data(), &len, gIOServicePlane ) 330 == false) { 331 break; 332 } 333 334 path = OSString::withCStringNoCopy(pathBuf.data()); 335 if (path == NULL) { 336 break; 337 } 338 339 dict->setObject( "IOInterfaceUnit", zero ); 340 dict->setObject( kIOPathMatchKey, path ); 341 342 stack->setProperties( dict ); 343 }while (false); 344 345 if (zero) { 346 zero->release(); 347 } 348 if (path) { 349 path->release(); 350 } 351 if (dict) { 352 dict->release(); 353 } 354 355 return netif->getProperty( kIOBSDNameKey ) != NULL; 356 } 357 358 OSDictionary * 359 IOOFPathMatching( const char * path, char * buf, int maxLen ) 360 { 361 OSDictionary * matching = NULL; 362 OSString * str; 363 char * comp; 364 int len; 365 366 do { 367 len = ((int) strlen( kIODeviceTreePlane ":" )); 368 maxLen -= len; 369 if (maxLen <= 0) { 370 continue; 371 } 372 373 strlcpy( buf, kIODeviceTreePlane ":", len + 1 ); 374 comp = buf + len; 375 376 len = ((int) strnlen( path, INT_MAX )); 377 maxLen -= len; 378 if (maxLen <= 0) { 379 continue; 380 } 381 strlcpy( comp, path, len + 1 ); 382 383 matching = OSDictionary::withCapacity( 1 ); 384 if (!matching) { 385 continue; 386 } 387 388 str = OSString::withCString( buf ); 389 if (!str) { 390 continue; 391 } 392 matching->setObject( kIOPathMatchKey, str ); 393 str->release(); 394 395 return matching; 396 } while (false); 397 398 if (matching) { 399 matching->release(); 400 } 401 402 return NULL; 403 } 404 405 static int didRam = 0; 406 enum { kMaxPathBuf = 512, kMaxBootVar = 128 }; 407 408 bool 409 IOGetBootUUID(char *uuid) 410 { 411 IORegistryEntry *entry; 412 OSData *uuid_data = NULL; 413 bool result = false; 414 415 if ((entry = IORegistryEntry::fromPath("/chosen", gIODTPlane))) { 416 uuid_data = (OSData *)entry->getProperty("boot-uuid"); 417 if (uuid_data) { 418 unsigned int length = uuid_data->getLength(); 419 if (length <= sizeof(uuid_string_t)) { 420 /* ensure caller's buffer is fully initialized: */ 421 bzero(uuid, sizeof(uuid_string_t)); 422 /* copy the content of uuid_data->getBytesNoCopy() into uuid */ 423 memcpy(uuid, uuid_data->getBytesNoCopy(), length); 424 /* guarantee nul-termination: */ 425 uuid[sizeof(uuid_string_t) - 1] = '\0'; 426 result = true; 427 } else { 428 uuid = NULL; 429 } 430 } 431 OSSafeReleaseNULL(entry); 432 } 433 return result; 434 } 435 436 bool 437 IOGetApfsPrebootUUID(char *uuid) 438 { 439 IORegistryEntry *entry; 440 OSData *uuid_data = NULL; 441 bool result = false; 442 443 if ((entry = IORegistryEntry::fromPath("/chosen", gIODTPlane))) { 444 uuid_data = (OSData *)entry->getProperty("apfs-preboot-uuid"); 445 446 if (uuid_data) { 447 unsigned int length = uuid_data->getLength(); 448 if (length <= sizeof(uuid_string_t)) { 449 /* ensure caller's buffer is fully initialized: */ 450 bzero(uuid, sizeof(uuid_string_t)); 451 /* copy the content of uuid_data->getBytesNoCopy() into uuid */ 452 memcpy(uuid, uuid_data->getBytesNoCopy(), length); 453 /* guarantee nul-termination: */ 454 uuid[sizeof(uuid_string_t) - 1] = '\0'; 455 result = true; 456 } else { 457 uuid = NULL; 458 } 459 } 460 OSSafeReleaseNULL(entry); 461 } 462 return result; 463 } 464 465 bool 466 IOGetAssociatedApfsVolgroupUUID(char *uuid) 467 { 468 IORegistryEntry *entry; 469 OSData *uuid_data = NULL; 470 bool result = false; 471 472 if ((entry = IORegistryEntry::fromPath("/chosen", gIODTPlane))) { 473 uuid_data = (OSData *)entry->getProperty("associated-volume-group"); 474 475 if (uuid_data) { 476 unsigned int length = uuid_data->getLength(); 477 478 if (length <= sizeof(uuid_string_t)) { 479 /* ensure caller's buffer is fully initialized: */ 480 bzero(uuid, sizeof(uuid_string_t)); 481 /* copy the content of uuid_data->getBytesNoCopy() into uuid */ 482 memcpy(uuid, uuid_data->getBytesNoCopy(), length); 483 /* guarantee nul-termination: */ 484 uuid[sizeof(uuid_string_t) - 1] = '\0'; 485 result = true; 486 } else { 487 uuid = NULL; 488 } 489 } 490 OSSafeReleaseNULL(entry); 491 } 492 return result; 493 } 494 495 bool 496 IOGetBootObjectsPath(char *path_prefix) 497 { 498 IORegistryEntry *entry; 499 OSData *path_prefix_data = NULL; 500 bool result = false; 501 502 if ((entry = IORegistryEntry::fromPath("/chosen", gIODTPlane))) { 503 path_prefix_data = (OSData *)entry->getProperty("boot-objects-path"); 504 505 if (path_prefix_data) { 506 unsigned int length = path_prefix_data->getLength(); 507 508 if (length <= MAXPATHLEN) { 509 /* ensure caller's buffer is fully initialized: */ 510 bzero(path_prefix, MAXPATHLEN); 511 /* copy the content of path_prefix_data->getBytesNoCopy() into path_prefix */ 512 memcpy(path_prefix, path_prefix_data->getBytesNoCopy(), length); 513 /* guarantee nul-termination: */ 514 path_prefix[MAXPATHLEN - 1] = '\0'; 515 result = true; 516 } else { 517 path_prefix = NULL; 518 } 519 } 520 OSSafeReleaseNULL(entry); 521 } 522 return result; 523 } 524 525 526 bool 527 IOGetBootManifestHash(char *hash_data, size_t *hash_data_size) 528 { 529 IORegistryEntry *entry = NULL; 530 OSData *manifest_hash_data = NULL; 531 bool result = false; 532 533 if ((entry = IORegistryEntry::fromPath("/chosen", gIODTPlane))) { 534 manifest_hash_data = (OSData *)entry->getProperty("boot-manifest-hash"); 535 if (manifest_hash_data) { 536 unsigned int length = manifest_hash_data->getLength(); 537 /* hashed with SHA2-384 or SHA1, the boot manifest hash should be 48 Bytes or less */ 538 if ((length <= CCSHA384_OUTPUT_SIZE) && (*hash_data_size >= CCSHA384_OUTPUT_SIZE)) { 539 /* ensure caller's buffer is fully initialized: */ 540 bzero(hash_data, CCSHA384_OUTPUT_SIZE); 541 /* copy the content of manifest_hash_data->getBytesNoCopy() into hash_data */ 542 memcpy(hash_data, manifest_hash_data->getBytesNoCopy(), length); 543 *hash_data_size = length; 544 result = true; 545 } else { 546 hash_data = NULL; 547 *hash_data_size = 0; 548 } 549 } 550 OSSafeReleaseNULL(entry); 551 } 552 553 return result; 554 } 555 556 /* 557 * Set NVRAM to boot into the right flavor of Recovery, 558 * optionally passing a UUID of a volume that failed to boot. 559 * If `reboot` is true, reboot immediately. 560 * 561 * Returns true if `mode` was understood, false otherwise. 562 * (Does not return if `reboot` is true.) 563 */ 564 boolean_t 565 IOSetRecoveryBoot(bsd_bootfail_mode_t mode, uuid_t volume_uuid, boolean_t reboot) 566 { 567 IODTNVRAM *nvram = NULL; 568 const OSSymbol *boot_command_sym = NULL; 569 OSString *boot_command_recover = NULL; 570 571 if (mode == BSD_BOOTFAIL_SEAL_BROKEN) { 572 const char *boot_mode = "ssv-seal-broken"; 573 uuid_string_t volume_uuid_str; 574 575 // Set `recovery-broken-seal-uuid = <volume_uuid>`. 576 if (volume_uuid) { 577 uuid_unparse_upper(volume_uuid, volume_uuid_str); 578 579 if (!PEWriteNVRAMProperty(SYSTEM_NVRAM_PREFIX "recovery-broken-seal-uuid", 580 volume_uuid_str, sizeof(uuid_string_t))) { 581 IOLog("Failed to write recovery-broken-seal-uuid to NVRAM.\n"); 582 } 583 } 584 585 // Set `recovery-boot-mode = ssv-seal-broken`. 586 if (!PEWriteNVRAMProperty(SYSTEM_NVRAM_PREFIX "recovery-boot-mode", boot_mode, 587 (const unsigned int) strlen(boot_mode))) { 588 IOLog("Failed to write recovery-boot-mode to NVRAM.\n"); 589 } 590 } else if (mode == BSD_BOOTFAIL_MEDIA_MISSING) { 591 const char *boot_picker_reason = "missing-boot-media"; 592 593 // Set `boot-picker-bringup-reason = missing-boot-media`. 594 if (!PEWriteNVRAMProperty(SYSTEM_NVRAM_PREFIX "boot-picker-bringup-reason", 595 boot_picker_reason, (const unsigned int) strlen(boot_picker_reason))) { 596 IOLog("Failed to write boot-picker-bringup-reason to NVRAM.\n"); 597 } 598 599 // Set `boot-command = recover-system`. 600 601 // Construct an OSSymbol and an OSString to be the (key, value) pair 602 // we write to NVRAM. Unfortunately, since our value must be an OSString 603 // instead of an OSData, we cannot use PEWriteNVRAMProperty() here. 604 boot_command_sym = OSSymbol::withCStringNoCopy(SYSTEM_NVRAM_PREFIX "boot-command"); 605 boot_command_recover = OSString::withCStringNoCopy("recover-system"); 606 if (boot_command_sym == NULL || boot_command_recover == NULL) { 607 IOLog("Failed to create boot-command strings.\n"); 608 goto do_reboot; 609 } 610 611 // Wait for NVRAM to be readable... 612 nvram = OSDynamicCast(IODTNVRAM, IOService::waitForService( 613 IOService::serviceMatching("IODTNVRAM"))); 614 if (nvram == NULL) { 615 IOLog("Failed to acquire IODTNVRAM object.\n"); 616 goto do_reboot; 617 } 618 619 // Wait for NVRAM to be writable... 620 if (!IOServiceWaitForMatchingResource("IONVRAM", UINT64_MAX)) { 621 IOLog("Failed to wait for IONVRAM service.\n"); 622 // attempt the work anyway... 623 } 624 625 // Write the new boot-command to NVRAM, and sync if successful. 626 if (!nvram->setProperty(boot_command_sym, boot_command_recover)) { 627 IOLog("Failed to save new boot-command to NVRAM.\n"); 628 } else { 629 nvram->sync(); 630 } 631 } else { 632 IOLog("Unknown mode: %d\n", mode); 633 return false; 634 } 635 636 // Clean up and reboot! 637 do_reboot: 638 if (boot_command_recover != NULL) { 639 boot_command_recover->release(); 640 } 641 642 if (boot_command_sym != NULL) { 643 boot_command_sym->release(); 644 } 645 646 if (reboot) { 647 IOLog("\nAbout to reboot into Recovery!\n"); 648 (void)PEHaltRestart(kPERestartCPU); 649 } 650 651 return true; 652 } 653 654 kern_return_t 655 IOFindBSDRoot( char * rootName, unsigned int rootNameSize, 656 dev_t * root, u_int32_t * oflags ) 657 { 658 mach_timespec_t t; 659 IOService * service; 660 IORegistryEntry * regEntry; 661 OSDictionary * matching = NULL; 662 OSString * iostr; 663 OSNumber * off; 664 OSData * data = NULL; 665 666 UInt32 flags = 0; 667 int mnr, mjr; 668 const char * mediaProperty = NULL; 669 char * rdBootVar; 670 OSDataAllocation<char> str; 671 const char * look = NULL; 672 int len; 673 bool debugInfoPrintedOnce = false; 674 bool needNetworkKexts = false; 675 const char * uuidStr = NULL; 676 677 static int mountAttempts = 0; 678 679 int xchar, dchar; 680 681 // stall here for anyone matching on the IOBSD resource to finish (filesystems) 682 matching = IOService::serviceMatching(gIOResourcesKey); 683 assert(matching); 684 matching->setObject(gIOResourceMatchedKey, gIOBSDKey); 685 686 if ((service = IOService::waitForMatchingService(matching, 30ULL * kSecondScale))) { 687 OSSafeReleaseNULL(service); 688 } else { 689 IOLog("!BSD\n"); 690 } 691 matching->release(); 692 matching = NULL; 693 694 if (mountAttempts++) { 695 IOLog("mount(%d) failed\n", mountAttempts); 696 IOSleep( 5 * 1000 ); 697 } 698 699 str = OSDataAllocation<char>( kMaxPathBuf + kMaxBootVar, OSAllocateMemory ); 700 if (!str) { 701 return kIOReturnNoMemory; 702 } 703 rdBootVar = str.data() + kMaxPathBuf; 704 705 if (!PE_parse_boot_argn("rd", rdBootVar, kMaxBootVar ) 706 && !PE_parse_boot_argn("rootdev", rdBootVar, kMaxBootVar )) { 707 rdBootVar[0] = 0; 708 } 709 710 if ((regEntry = IORegistryEntry::fromPath( "/chosen", gIODTPlane ))) { 711 do { 712 di_root_ramfile(regEntry); 713 OSObject* unserializedContainer = NULL; 714 data = OSDynamicCast(OSData, regEntry->getProperty( "root-matching" )); 715 if (data) { 716 unserializedContainer = OSUnserializeXML((char *)data->getBytesNoCopy()); 717 matching = OSDynamicCast(OSDictionary, unserializedContainer); 718 if (matching) { 719 continue; 720 } 721 } 722 OSSafeReleaseNULL(unserializedContainer); 723 724 data = (OSData *) regEntry->getProperty( "boot-uuid" ); 725 if (data) { 726 uuidStr = (const char*)data->getBytesNoCopy(); 727 OSString *uuidString = OSString::withCString( uuidStr ); 728 729 // match the boot-args boot-uuid processing below 730 if (uuidString) { 731 IOLog("rooting via boot-uuid from /chosen: %s\n", uuidStr); 732 IOService::publishResource( "boot-uuid", uuidString ); 733 uuidString->release(); 734 matching = IOUUIDMatching(); 735 mediaProperty = "boot-uuid-media"; 736 continue; 737 } else { 738 uuidStr = NULL; 739 } 740 } 741 } while (false); 742 OSSafeReleaseNULL(regEntry); 743 } 744 745 // 746 // See if we have a RAMDisk property in /chosen/memory-map. If so, make it into a device. 747 // It will become /dev/mdx, where x is 0-f. 748 // 749 750 if (!didRam) { /* Have we already build this ram disk? */ 751 didRam = 1; /* Remember we did this */ 752 if ((regEntry = IORegistryEntry::fromPath( "/chosen/memory-map", gIODTPlane ))) { /* Find the map node */ 753 data = (OSData *)regEntry->getProperty("RAMDisk"); /* Find the ram disk, if there */ 754 if (data) { /* We found one */ 755 uintptr_t *ramdParms; 756 ramdParms = (uintptr_t *)data->getBytesNoCopy(); /* Point to the ram disk base and size */ 757 #if __LP64__ 758 #define MAX_PHYS_RAM (((uint64_t)UINT_MAX) << 12) 759 if (ramdParms[1] > MAX_PHYS_RAM) { 760 panic("ramdisk params"); 761 } 762 #endif /* __LP64__ */ 763 (void)mdevadd(-1, ml_static_ptovirt(ramdParms[0]) >> 12, (unsigned int) (ramdParms[1] >> 12), 0); /* Initialize it and pass back the device number */ 764 } 765 regEntry->release(); /* Toss the entry */ 766 } 767 } 768 769 // 770 // Now check if we are trying to root on a memory device 771 // 772 773 if ((rdBootVar[0] == 'm') && (rdBootVar[1] == 'd') && (rdBootVar[3] == 0)) { 774 dchar = xchar = rdBootVar[2]; /* Get the actual device */ 775 if ((xchar >= '0') && (xchar <= '9')) { 776 xchar = xchar - '0'; /* If digit, convert */ 777 } else { 778 xchar = xchar & ~' '; /* Fold to upper case */ 779 if ((xchar >= 'A') && (xchar <= 'F')) { /* Is this a valid digit? */ 780 xchar = (xchar & 0xF) + 9; /* Convert the hex digit */ 781 dchar = dchar | ' '; /* Fold to lower case */ 782 } else { 783 xchar = -1; /* Show bogus */ 784 } 785 } 786 if (xchar >= 0) { /* Do we have a valid memory device name? */ 787 OSSafeReleaseNULL(matching); 788 *root = mdevlookup(xchar); /* Find the device number */ 789 if (*root >= 0) { /* Did we find one? */ 790 rootName[0] = 'm'; /* Build root name */ 791 rootName[1] = 'd'; /* Build root name */ 792 rootName[2] = (char) dchar; /* Build root name */ 793 rootName[3] = 0; /* Build root name */ 794 IOLog("BSD root: %s, major %d, minor %d\n", rootName, major(*root), minor(*root)); 795 *oflags = 0; /* Show that this is not network */ 796 797 #if CONFIG_KDP_INTERACTIVE_DEBUGGING 798 /* retrieve final ramdisk range and initialize KDP variables */ 799 if (mdevgetrange(xchar, &kdp_core_ramdisk_addr, &kdp_core_ramdisk_size) != 0) { 800 IOLog("Unable to retrieve range for root memory device %d\n", xchar); 801 kdp_core_ramdisk_addr = 0; 802 kdp_core_ramdisk_size = 0; 803 } 804 #endif 805 806 goto iofrootx; /* Join common exit... */ 807 } 808 panic("IOFindBSDRoot: specified root memory device, %s, has not been configured", rdBootVar); /* Not there */ 809 } 810 } 811 812 if ((!matching) && rdBootVar[0]) { 813 // by BSD name 814 look = rdBootVar; 815 if (look[0] == '*') { 816 look++; 817 } 818 819 if (strncmp( look, "en", strlen( "en" )) == 0) { 820 matching = IONetworkNamePrefixMatching( "en" ); 821 needNetworkKexts = true; 822 } else if (strncmp( look, "uuid", strlen( "uuid" )) == 0) { 823 OSDataAllocation<char> uuid( kMaxBootVar, OSAllocateMemory ); 824 825 if (uuid) { 826 OSString *uuidString; 827 828 if (!PE_parse_boot_argn( "boot-uuid", uuid.data(), kMaxBootVar )) { 829 panic( "rd=uuid but no boot-uuid=<value> specified" ); 830 } 831 uuidString = OSString::withCString(uuid.data()); 832 if (uuidString) { 833 IOService::publishResource( "boot-uuid", uuidString ); 834 uuidString->release(); 835 IOLog("\nWaiting for boot volume with UUID %s\n", uuid.data()); 836 matching = IOUUIDMatching(); 837 mediaProperty = "boot-uuid-media"; 838 } 839 } 840 } else { 841 matching = IOBSDNameMatching( look ); 842 } 843 } 844 845 if (!matching) { 846 OSString * astring; 847 // Match any HFS media 848 849 matching = IOService::serviceMatching( "IOMedia" ); 850 assert(matching); 851 astring = OSString::withCStringNoCopy("Apple_HFS"); 852 if (astring) { 853 matching->setObject("Content", astring); 854 astring->release(); 855 } 856 } 857 858 if (gIOKitDebug & kIOWaitQuietBeforeRoot) { 859 IOLog( "Waiting for matching to complete\n" ); 860 IOService::getPlatform()->waitQuiet(); 861 } 862 863 if (matching) { 864 OSSerialize * s = OSSerialize::withCapacity( 5 ); 865 866 if (matching->serialize( s )) { 867 IOLog( "Waiting on %s\n", s->text()); 868 } 869 s->release(); 870 } 871 872 char namep[8]; 873 if (needNetworkKexts 874 || PE_parse_boot_argn("-s", namep, sizeof(namep))) { 875 IOService::startDeferredMatches(); 876 } 877 878 do { 879 t.tv_sec = ROOTDEVICETIMEOUT; 880 t.tv_nsec = 0; 881 matching->retain(); 882 service = IOService::waitForService( matching, &t ); 883 if ((!service) || (mountAttempts == 10)) { 884 #if !XNU_TARGET_OS_OSX || !defined(__arm64__) 885 PE_display_icon( 0, "noroot"); 886 IOLog( "Still waiting for root device\n" ); 887 #endif 888 889 if (!debugInfoPrintedOnce) { 890 debugInfoPrintedOnce = true; 891 if (gIOKitDebug & kIOLogDTree) { 892 IOLog("\nDT plane:\n"); 893 IOPrintPlane( gIODTPlane ); 894 } 895 if (gIOKitDebug & kIOLogServiceTree) { 896 IOLog("\nService plane:\n"); 897 IOPrintPlane( gIOServicePlane ); 898 } 899 if (gIOKitDebug & kIOLogMemory) { 900 IOPrintMemory(); 901 } 902 } 903 904 #if XNU_TARGET_OS_OSX && defined(__arm64__) 905 // The disk isn't found - have the user pick from System Recovery. 906 (void)IOSetRecoveryBoot(BSD_BOOTFAIL_MEDIA_MISSING, NULL, true); 907 #elif XNU_TARGET_OS_IOS 908 panic("Failed to mount root device"); 909 #endif 910 } 911 } while (!service); 912 913 OSSafeReleaseNULL(matching); 914 915 if (service && mediaProperty) { 916 service = (IOService *)service->getProperty(mediaProperty); 917 } 918 919 mjr = 0; 920 mnr = 0; 921 922 // If the IOService we matched to is a subclass of IONetworkInterface, 923 // then make sure it has been registered with BSD and has a BSD name 924 // assigned. 925 926 if (service 927 && service->metaCast( "IONetworkInterface" ) 928 && !IORegisterNetworkInterface( service )) { 929 service = NULL; 930 } 931 932 if (service) { 933 len = kMaxPathBuf; 934 service->getPath( str.data(), &len, gIOServicePlane ); 935 IOLog("Got boot device = %s\n", str.data()); 936 937 iostr = (OSString *) service->getProperty( kIOBSDNameKey ); 938 if (iostr) { 939 strlcpy( rootName, iostr->getCStringNoCopy(), rootNameSize ); 940 } 941 off = (OSNumber *) service->getProperty( kIOBSDMajorKey ); 942 if (off) { 943 mjr = off->unsigned32BitValue(); 944 } 945 off = (OSNumber *) service->getProperty( kIOBSDMinorKey ); 946 if (off) { 947 mnr = off->unsigned32BitValue(); 948 } 949 950 if (service->metaCast( "IONetworkInterface" )) { 951 flags |= 1; 952 } 953 } else { 954 IOLog( "Wait for root failed\n" ); 955 strlcpy( rootName, "en0", rootNameSize ); 956 flags |= 1; 957 } 958 959 IOLog( "BSD root: %s", rootName ); 960 if (mjr) { 961 IOLog(", major %d, minor %d\n", mjr, mnr ); 962 } else { 963 IOLog("\n"); 964 } 965 966 *root = makedev( mjr, mnr ); 967 *oflags = flags; 968 969 iofrootx: 970 971 IOService::setRootMedia(service); 972 973 if ((gIOKitDebug & (kIOLogDTree | kIOLogServiceTree | kIOLogMemory)) && !debugInfoPrintedOnce) { 974 IOService::getPlatform()->waitQuiet(); 975 if (gIOKitDebug & kIOLogDTree) { 976 IOLog("\nDT plane:\n"); 977 IOPrintPlane( gIODTPlane ); 978 } 979 if (gIOKitDebug & kIOLogServiceTree) { 980 IOLog("\nService plane:\n"); 981 IOPrintPlane( gIOServicePlane ); 982 } 983 if (gIOKitDebug & kIOLogMemory) { 984 IOPrintMemory(); 985 } 986 } 987 988 return kIOReturnSuccess; 989 } 990 991 void 992 IOSetImageBoot(void) 993 { 994 // this will unhide all IOMedia, without waiting for kernelmanagement to start 995 IOService::setRootMedia(NULL); 996 } 997 998 bool 999 IORamDiskBSDRoot(void) 1000 { 1001 char rdBootVar[kMaxBootVar]; 1002 if (PE_parse_boot_argn("rd", rdBootVar, kMaxBootVar ) 1003 || PE_parse_boot_argn("rootdev", rdBootVar, kMaxBootVar )) { 1004 if ((rdBootVar[0] == 'm') && (rdBootVar[1] == 'd') && (rdBootVar[3] == 0)) { 1005 return true; 1006 } 1007 } 1008 return false; 1009 } 1010 1011 void 1012 IOSecureBSDRoot(const char * rootName) 1013 { 1014 #if CONFIG_SECURE_BSD_ROOT 1015 IOReturn result; 1016 IOPlatformExpert *pe; 1017 OSDictionary *matching; 1018 const OSSymbol *functionName = OSSymbol::withCStringNoCopy("SecureRootName"); 1019 1020 matching = IOService::serviceMatching("IOPlatformExpert"); 1021 assert(matching); 1022 pe = (IOPlatformExpert *) IOService::waitForMatchingService(matching, 30ULL * kSecondScale); 1023 matching->release(); 1024 assert(pe); 1025 // Returns kIOReturnNotPrivileged is the root device is not secure. 1026 // Returns kIOReturnUnsupported if "SecureRootName" is not implemented. 1027 result = pe->callPlatformFunction(functionName, false, (void *)rootName, (void *)NULL, (void *)NULL, (void *)NULL); 1028 functionName->release(); 1029 OSSafeReleaseNULL(pe); 1030 1031 if (result == kIOReturnNotPrivileged) { 1032 mdevremoveall(); 1033 } 1034 1035 #endif // CONFIG_SECURE_BSD_ROOT 1036 } 1037 1038 void * 1039 IOBSDRegistryEntryForDeviceTree(char * path) 1040 { 1041 return IORegistryEntry::fromPath(path, gIODTPlane); 1042 } 1043 1044 void 1045 IOBSDRegistryEntryRelease(void * entry) 1046 { 1047 IORegistryEntry * regEntry = (IORegistryEntry *)entry; 1048 1049 if (regEntry) { 1050 regEntry->release(); 1051 } 1052 return; 1053 } 1054 1055 const void * 1056 IOBSDRegistryEntryGetData(void * entry, char * property_name, 1057 int * packet_length) 1058 { 1059 OSData * data; 1060 IORegistryEntry * regEntry = (IORegistryEntry *)entry; 1061 1062 data = (OSData *) regEntry->getProperty(property_name); 1063 if (data) { 1064 *packet_length = data->getLength(); 1065 return data->getBytesNoCopy(); 1066 } 1067 return NULL; 1068 } 1069 1070 kern_return_t 1071 IOBSDGetPlatformUUID( uuid_t uuid, mach_timespec_t timeout ) 1072 { 1073 IOService * resources; 1074 OSString * string; 1075 1076 resources = IOService::waitForService( IOService::resourceMatching( kIOPlatformUUIDKey ), (timeout.tv_sec || timeout.tv_nsec) ? &timeout : NULL ); 1077 if (resources == NULL) { 1078 return KERN_OPERATION_TIMED_OUT; 1079 } 1080 1081 string = (OSString *) IOService::getPlatform()->getProvider()->getProperty( kIOPlatformUUIDKey ); 1082 if (string == NULL) { 1083 return KERN_NOT_SUPPORTED; 1084 } 1085 1086 uuid_parse( string->getCStringNoCopy(), uuid ); 1087 1088 return KERN_SUCCESS; 1089 } 1090 } /* extern "C" */ 1091 1092 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 1093 1094 #include <sys/conf.h> 1095 #include <sys/lock.h> 1096 #include <sys/vnode.h> 1097 #include <sys/vnode_if.h> 1098 #include <sys/vnode_internal.h> 1099 #include <sys/fcntl.h> 1100 #include <sys/fsctl.h> 1101 #include <sys/mount.h> 1102 #include <IOKit/IOPolledInterface.h> 1103 #include <IOKit/IOBufferMemoryDescriptor.h> 1104 1105 // see HFSIOC_VOLUME_STATUS in APFS/HFS 1106 #define HFS_IOCTL_VOLUME_STATUS _IOR('h', 24, u_int32_t) 1107 1108 LCK_GRP_DECLARE(gIOPolledCoreFileGrp, "polled_corefile"); 1109 LCK_MTX_DECLARE(gIOPolledCoreFileMtx, &gIOPolledCoreFileGrp); 1110 1111 IOPolledFileIOVars * gIOPolledCoreFileVars; 1112 kern_return_t gIOPolledCoreFileOpenRet = kIOReturnNotReady; 1113 IOPolledCoreFileMode_t gIOPolledCoreFileMode = kIOPolledCoreFileModeNotInitialized; 1114 1115 #if IOPOLLED_COREFILE 1116 1117 #if defined(XNU_TARGET_OS_BRIDGE) 1118 // On bridgeOS allocate a 150MB corefile and leave 150MB free 1119 #define kIOCoreDumpSize 150ULL*1024ULL*1024ULL 1120 #define kIOCoreDumpFreeSize 150ULL*1024ULL*1024ULL 1121 1122 #elif !defined(XNU_TARGET_OS_OSX) /* defined(XNU_TARGET_OS_BRIDGE) */ 1123 // On embedded devices with >3GB DRAM we allocate a 500MB corefile 1124 // otherwise allocate a 350MB corefile. Leave 350 MB free 1125 1126 #define kIOCoreDumpMinSize 350ULL*1024ULL*1024ULL 1127 #define kIOCoreDumpLargeSize 500ULL*1024ULL*1024ULL 1128 1129 #define kIOCoreDumpFreeSize 350ULL*1024ULL*1024ULL 1130 1131 #else /* defined(XNU_TARGET_OS_BRIDGE) */ 1132 // on macOS devices allocate a corefile sized at 1GB / 32GB of DRAM, 1133 // fallback to a 1GB corefile and leave at least 1GB free 1134 #define kIOCoreDumpMinSize 1024ULL*1024ULL*1024ULL 1135 #define kIOCoreDumpIncrementalSize 1024ULL*1024ULL*1024ULL 1136 1137 #define kIOCoreDumpFreeSize 1024ULL*1024ULL*1024ULL 1138 1139 // on older macOS devices we allocate a 1MB file at boot 1140 // to store a panic time stackshot 1141 #define kIOStackshotFileSize 1024ULL*1024ULL 1142 1143 #endif /* defined(XNU_TARGET_OS_BRIDGE) */ 1144 1145 static IOPolledCoreFileMode_t 1146 GetCoreFileMode() 1147 { 1148 if (on_device_corefile_enabled()) { 1149 return kIOPolledCoreFileModeCoredump; 1150 } else if (panic_stackshot_to_disk_enabled()) { 1151 return kIOPolledCoreFileModeStackshot; 1152 } else { 1153 return kIOPolledCoreFileModeDisabled; 1154 } 1155 } 1156 1157 static void 1158 IOCoreFileGetSize(uint64_t *ideal_size, uint64_t *fallback_size, uint64_t *free_space_to_leave, IOPolledCoreFileMode_t mode) 1159 { 1160 unsigned int requested_corefile_size = 0; 1161 1162 *ideal_size = *fallback_size = *free_space_to_leave = 0; 1163 1164 // If a custom size was requested, override the ideal and requested sizes 1165 if (PE_parse_boot_argn("corefile_size_mb", &requested_corefile_size, 1166 sizeof(requested_corefile_size))) { 1167 IOLog("Boot-args specify %d MB kernel corefile\n", requested_corefile_size); 1168 1169 *ideal_size = *fallback_size = (requested_corefile_size * 1024ULL * 1024ULL); 1170 return; 1171 } 1172 1173 unsigned int status_flags = 0; 1174 int error = VNOP_IOCTL(rootvnode, HFS_IOCTL_VOLUME_STATUS, (caddr_t)&status_flags, 0, 1175 vfs_context_kernel()); 1176 if (!error) { 1177 if (status_flags & (VQ_VERYLOWDISK | VQ_LOWDISK | VQ_NEARLOWDISK)) { 1178 IOLog("Volume is low on space. Not allocating kernel corefile.\n"); 1179 return; 1180 } 1181 } else { 1182 IOLog("Couldn't retrieve volume status. Error %d\n", error); 1183 } 1184 1185 #if defined(XNU_TARGET_OS_BRIDGE) 1186 #pragma unused(mode) 1187 *ideal_size = *fallback_size = kIOCoreDumpSize; 1188 *free_space_to_leave = kIOCoreDumpFreeSize; 1189 #elif !defined(XNU_TARGET_OS_OSX) /* defined(XNU_TARGET_OS_BRIDGE) */ 1190 #pragma unused(mode) 1191 *ideal_size = *fallback_size = kIOCoreDumpMinSize; 1192 1193 if (max_mem > (3 * 1024ULL * 1024ULL * 1024ULL)) { 1194 *ideal_size = kIOCoreDumpLargeSize; 1195 } 1196 1197 *free_space_to_leave = kIOCoreDumpFreeSize; 1198 #else /* defined(XNU_TARGET_OS_BRIDGE) */ 1199 if (mode == kIOPolledCoreFileModeCoredump) { 1200 *ideal_size = *fallback_size = kIOCoreDumpMinSize; 1201 if (kIOCoreDumpIncrementalSize != 0 && max_mem > (32 * 1024ULL * 1024ULL * 1024ULL)) { 1202 *ideal_size = ((ROUNDUP(max_mem, (32 * 1024ULL * 1024ULL * 1024ULL)) / (32 * 1024ULL * 1024ULL * 1024ULL)) * kIOCoreDumpIncrementalSize); 1203 } 1204 *free_space_to_leave = kIOCoreDumpFreeSize; 1205 } else if (mode == kIOPolledCoreFileModeStackshot) { 1206 *ideal_size = *fallback_size = *free_space_to_leave = kIOStackshotFileSize; 1207 } 1208 #endif /* defined(XNU_TARGET_OS_BRIDGE) */ 1209 1210 return; 1211 } 1212 1213 static IOReturn 1214 IOAccessCoreFileData(void *context, boolean_t write, uint64_t offset, int length, void *buffer) 1215 { 1216 errno_t vnode_error = 0; 1217 vfs_context_t vfs_context; 1218 vnode_t vnode_ptr = (vnode_t) context; 1219 1220 vfs_context = vfs_context_kernel(); 1221 vnode_error = vn_rdwr(write ? UIO_WRITE : UIO_READ, vnode_ptr, (caddr_t)buffer, length, offset, 1222 UIO_SYSSPACE, IO_SWAP_DISPATCH | IO_SYNC | IO_NOCACHE | IO_UNIT, vfs_context_ucred(vfs_context), NULL, vfs_context_proc(vfs_context)); 1223 1224 if (vnode_error) { 1225 IOLog("Failed to %s the corefile. Error %d\n", write ? "write to" : "read from", vnode_error); 1226 return kIOReturnError; 1227 } 1228 1229 return kIOReturnSuccess; 1230 } 1231 1232 static void 1233 IOOpenPolledCoreFile(thread_call_param_t __unused, thread_call_param_t corefilename) 1234 { 1235 assert(corefilename != NULL); 1236 1237 IOReturn err; 1238 char *filename = (char *) corefilename; 1239 uint64_t corefile_size_bytes = 0, corefile_fallback_size_bytes = 0, free_space_to_leave_bytes = 0; 1240 IOPolledCoreFileMode_t mode_to_init = GetCoreFileMode(); 1241 1242 if (gIOPolledCoreFileVars) { 1243 return; 1244 } 1245 if (!IOPolledInterface::gMetaClass.getInstanceCount()) { 1246 return; 1247 } 1248 1249 if (gIOPolledCoreFileMode == kIOPolledCoreFileModeUnlinked) { 1250 return; 1251 } 1252 1253 if (mode_to_init == kIOPolledCoreFileModeDisabled) { 1254 gIOPolledCoreFileMode = kIOPolledCoreFileModeDisabled; 1255 return; 1256 } 1257 1258 // We'll overwrite this once we open the file, we update this to mark that we have made 1259 // it past initialization 1260 gIOPolledCoreFileMode = kIOPolledCoreFileModeClosed; 1261 1262 IOCoreFileGetSize(&corefile_size_bytes, &corefile_fallback_size_bytes, &free_space_to_leave_bytes, mode_to_init); 1263 1264 if (corefile_size_bytes == 0 && corefile_fallback_size_bytes == 0) { 1265 gIOPolledCoreFileMode = kIOPolledCoreFileModeUnlinked; 1266 return; 1267 } 1268 1269 do { 1270 err = IOPolledFileOpen(filename, kIOPolledFileCreate, corefile_size_bytes, free_space_to_leave_bytes, 1271 NULL, 0, &gIOPolledCoreFileVars, NULL, NULL, NULL); 1272 if (kIOReturnSuccess == err) { 1273 break; 1274 } else if (kIOReturnNoSpace == err) { 1275 IOLog("Failed to open corefile of size %llu MB (low disk space)", 1276 (corefile_size_bytes / (1024ULL * 1024ULL))); 1277 if (corefile_size_bytes == corefile_fallback_size_bytes) { 1278 gIOPolledCoreFileOpenRet = err; 1279 return; 1280 } 1281 } else { 1282 IOLog("Failed to open corefile of size %llu MB (returned error 0x%x)\n", 1283 (corefile_size_bytes / (1024ULL * 1024ULL)), err); 1284 gIOPolledCoreFileOpenRet = err; 1285 return; 1286 } 1287 1288 err = IOPolledFileOpen(filename, kIOPolledFileCreate, corefile_fallback_size_bytes, free_space_to_leave_bytes, 1289 NULL, 0, &gIOPolledCoreFileVars, NULL, NULL, NULL); 1290 if (kIOReturnSuccess != err) { 1291 IOLog("Failed to open corefile of size %llu MB (returned error 0x%x)\n", 1292 (corefile_fallback_size_bytes / (1024ULL * 1024ULL)), err); 1293 gIOPolledCoreFileOpenRet = err; 1294 return; 1295 } 1296 } while (false); 1297 1298 gIOPolledCoreFileOpenRet = IOPolledFilePollersSetup(gIOPolledCoreFileVars, kIOPolledPreflightCoreDumpState); 1299 if (kIOReturnSuccess != gIOPolledCoreFileOpenRet) { 1300 IOPolledFileClose(&gIOPolledCoreFileVars, 0, NULL, 0, 0, 0, false); 1301 IOLog("IOPolledFilePollersSetup for corefile failed with error: 0x%x\n", err); 1302 } else { 1303 IOLog("Opened corefile of size %llu MB\n", (corefile_size_bytes / (1024ULL * 1024ULL))); 1304 gIOPolledCoreFileMode = mode_to_init; 1305 } 1306 1307 // Provide the "polled file available" callback with a temporary way to read from the file 1308 (void) IOProvideCoreFileAccess(kdp_core_polled_io_polled_file_available, NULL); 1309 1310 return; 1311 } 1312 1313 kern_return_t 1314 IOProvideCoreFileAccess(IOCoreFileAccessRecipient recipient, void *recipient_context) 1315 { 1316 kern_return_t error = kIOReturnSuccess; 1317 errno_t vnode_error = 0; 1318 vfs_context_t vfs_context; 1319 vnode_t vnode_ptr; 1320 1321 if (!recipient) { 1322 return kIOReturnBadArgument; 1323 } 1324 1325 if (kIOReturnSuccess != gIOPolledCoreFileOpenRet) { 1326 return kIOReturnNotReady; 1327 } 1328 1329 // Open the kernel corefile 1330 vfs_context = vfs_context_kernel(); 1331 vnode_error = vnode_open(kIOCoreDumpPath, (FREAD | FWRITE | O_NOFOLLOW), 0600, 0, &vnode_ptr, vfs_context); 1332 if (vnode_error) { 1333 IOLog("Failed to open the corefile. Error %d\n", vnode_error); 1334 return kIOReturnError; 1335 } 1336 1337 // Call the recipient function 1338 error = recipient(IOAccessCoreFileData, (void *)vnode_ptr, recipient_context); 1339 1340 // Close the kernel corefile 1341 vnode_close(vnode_ptr, FREAD | FWRITE, vfs_context); 1342 1343 return error; 1344 } 1345 1346 static void 1347 IOClosePolledCoreFile(void) 1348 { 1349 // Notify kdp core that the corefile is no longer available 1350 (void) kdp_core_polled_io_polled_file_unavailable(); 1351 1352 gIOPolledCoreFileOpenRet = kIOReturnNotOpen; 1353 gIOPolledCoreFileMode = kIOPolledCoreFileModeClosed; 1354 IOPolledFilePollersClose(gIOPolledCoreFileVars, kIOPolledPostflightCoreDumpState); 1355 IOPolledFileClose(&gIOPolledCoreFileVars, 0, NULL, 0, 0, 0, false); 1356 } 1357 1358 static void 1359 IOUnlinkPolledCoreFile(void) 1360 { 1361 // Notify kdp core that the corefile is no longer available 1362 (void) kdp_core_polled_io_polled_file_unavailable(); 1363 1364 gIOPolledCoreFileOpenRet = kIOReturnNotOpen; 1365 gIOPolledCoreFileMode = kIOPolledCoreFileModeUnlinked; 1366 IOPolledFilePollersClose(gIOPolledCoreFileVars, kIOPolledPostflightCoreDumpState); 1367 IOPolledFileClose(&gIOPolledCoreFileVars, 0, NULL, 0, 0, 0, true); 1368 } 1369 1370 #endif /* IOPOLLED_COREFILE */ 1371 1372 extern "C" void 1373 IOBSDMountChange(struct mount * mp, uint32_t op) 1374 { 1375 #if IOPOLLED_COREFILE 1376 uint64_t flags; 1377 char path[128]; 1378 int pathLen; 1379 vnode_t vn; 1380 int result; 1381 1382 lck_mtx_lock(&gIOPolledCoreFileMtx); 1383 1384 switch (op) { 1385 case kIOMountChangeMount: 1386 case kIOMountChangeDidResize: 1387 1388 if (gIOPolledCoreFileVars) { 1389 break; 1390 } 1391 flags = vfs_flags(mp); 1392 if (MNT_RDONLY & flags) { 1393 break; 1394 } 1395 if (!(MNT_LOCAL & flags)) { 1396 break; 1397 } 1398 1399 vn = vfs_vnodecovered(mp); 1400 if (!vn) { 1401 break; 1402 } 1403 pathLen = sizeof(path); 1404 result = vn_getpath(vn, &path[0], &pathLen); 1405 vnode_put(vn); 1406 if (0 != result) { 1407 break; 1408 } 1409 if (!pathLen) { 1410 break; 1411 } 1412 #if defined(XNU_TARGET_OS_BRIDGE) 1413 // on bridgeOS systems we put the core in /private/var/internal. We don't 1414 // want to match with /private/var because /private/var/internal is often mounted 1415 // over /private/var 1416 if ((pathLen - 1) < (int) strlen("/private/var/internal")) { 1417 break; 1418 } 1419 #endif 1420 if (0 != strncmp(path, kIOCoreDumpPath, pathLen - 1)) { 1421 break; 1422 } 1423 1424 thread_call_enter1(corefile_open_call, (void *) kIOCoreDumpPath); 1425 break; 1426 1427 case kIOMountChangeUnmount: 1428 case kIOMountChangeWillResize: 1429 if (gIOPolledCoreFileVars && (mp == kern_file_mount(gIOPolledCoreFileVars->fileRef))) { 1430 thread_call_cancel_wait(corefile_open_call); 1431 IOClosePolledCoreFile(); 1432 } 1433 break; 1434 } 1435 1436 lck_mtx_unlock(&gIOPolledCoreFileMtx); 1437 #endif /* IOPOLLED_COREFILE */ 1438 } 1439 1440 extern "C" void 1441 IOBSDLowSpaceUnlinkKernelCore(void) 1442 { 1443 #if IOPOLLED_COREFILE 1444 lck_mtx_lock(&gIOPolledCoreFileMtx); 1445 if (gIOPolledCoreFileVars) { 1446 thread_call_cancel_wait(corefile_open_call); 1447 IOUnlinkPolledCoreFile(); 1448 } 1449 lck_mtx_unlock(&gIOPolledCoreFileMtx); 1450 #endif 1451 } 1452 1453 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 1454 1455 static char* 1456 copyOSStringAsCString(OSString *string) 1457 { 1458 size_t string_length = 0; 1459 char *c_string = NULL; 1460 1461 if (string == NULL) { 1462 return NULL; 1463 } 1464 string_length = string->getLength() + 1; 1465 1466 /* Allocate kernel data memory for the string */ 1467 c_string = (char*)kalloc_data(string_length, (zalloc_flags_t)(Z_ZERO | Z_WAITOK | Z_NOFAIL)); 1468 assert(c_string != NULL); 1469 1470 /* Copy in the string */ 1471 strlcpy(c_string, string->getCStringNoCopy(), string_length); 1472 1473 return c_string; 1474 } 1475 1476 extern "C" OS_ALWAYS_INLINE boolean_t 1477 IOCurrentTaskHasStringEntitlement(const char *entitlement, const char *value) 1478 { 1479 return IOTaskHasStringEntitlement(NULL, entitlement, value); 1480 } 1481 1482 extern "C" boolean_t 1483 IOTaskHasStringEntitlement(task_t task, const char *entitlement, const char *value) 1484 { 1485 if (task == NULL) { 1486 task = current_task(); 1487 } 1488 1489 /* Validate input arguments */ 1490 if (task == kernel_task || entitlement == NULL || value == NULL) { 1491 return false; 1492 } 1493 proc_t proc = (proc_t)get_bsdtask_info(task); 1494 1495 kern_return_t ret = amfi->OSEntitlements.queryEntitlementStringWithProc( 1496 proc, 1497 entitlement, 1498 value); 1499 1500 if (ret == KERN_SUCCESS) { 1501 return true; 1502 } 1503 1504 return false; 1505 } 1506 1507 extern "C" OS_ALWAYS_INLINE boolean_t 1508 IOCurrentTaskHasEntitlement(const char *entitlement) 1509 { 1510 return IOTaskHasEntitlement(NULL, entitlement); 1511 } 1512 1513 extern "C" boolean_t 1514 IOTaskHasEntitlement(task_t task, const char *entitlement) 1515 { 1516 if (task == NULL) { 1517 task = current_task(); 1518 } 1519 1520 /* Validate input arguments */ 1521 if (task == kernel_task || entitlement == NULL) { 1522 return false; 1523 } 1524 proc_t proc = (proc_t)get_bsdtask_info(task); 1525 1526 kern_return_t ret = amfi->OSEntitlements.queryEntitlementBooleanWithProc( 1527 proc, 1528 entitlement); 1529 1530 if (ret == KERN_SUCCESS) { 1531 return true; 1532 } 1533 1534 return false; 1535 } 1536 1537 extern "C" OS_ALWAYS_INLINE char* 1538 IOCurrentTaskGetEntitlement(const char *entitlement) 1539 { 1540 return IOTaskGetEntitlement(NULL, entitlement); 1541 } 1542 1543 extern "C" char* 1544 IOTaskGetEntitlement(task_t task, const char *entitlement) 1545 { 1546 void *entitlement_object = NULL; 1547 char *return_value = NULL; 1548 1549 if (task == NULL) { 1550 task = current_task(); 1551 } 1552 1553 /* Validate input arguments */ 1554 if (task == kernel_task || entitlement == NULL) { 1555 return NULL; 1556 } 1557 proc_t proc = (proc_t)get_bsdtask_info(task); 1558 1559 kern_return_t ret = amfi->OSEntitlements.copyEntitlementAsOSObjectWithProc( 1560 proc, 1561 entitlement, 1562 &entitlement_object); 1563 1564 if (ret != KERN_SUCCESS) { 1565 return NULL; 1566 } 1567 assert(entitlement_object != NULL); 1568 1569 OSObject *os_object = (OSObject*)entitlement_object; 1570 OSString *os_string = OSDynamicCast(OSString, os_object); 1571 1572 /* Get a C string version of the OSString */ 1573 return_value = copyOSStringAsCString(os_string); 1574 1575 /* Free the OSObject which was given to us */ 1576 OSSafeReleaseNULL(os_object); 1577 1578 return return_value; 1579 } 1580 1581 extern "C" boolean_t 1582 IOVnodeHasEntitlement(vnode_t vnode, int64_t off, const char *entitlement) 1583 { 1584 OSObject * obj; 1585 off_t offset = (off_t)off; 1586 1587 obj = IOUserClient::copyClientEntitlementVnode(vnode, offset, entitlement); 1588 if (!obj) { 1589 return false; 1590 } 1591 obj->release(); 1592 return obj != kOSBooleanFalse; 1593 } 1594 1595 extern "C" char * 1596 IOVnodeGetEntitlement(vnode_t vnode, int64_t off, const char *entitlement) 1597 { 1598 OSObject *obj = NULL; 1599 OSString *str = NULL; 1600 size_t len; 1601 char *value = NULL; 1602 off_t offset = (off_t)off; 1603 1604 obj = IOUserClient::copyClientEntitlementVnode(vnode, offset, entitlement); 1605 if (obj != NULL) { 1606 str = OSDynamicCast(OSString, obj); 1607 if (str != NULL) { 1608 len = str->getLength() + 1; 1609 value = (char *)kalloc_data(len, Z_WAITOK); 1610 strlcpy(value, str->getCStringNoCopy(), len); 1611 } 1612 obj->release(); 1613 } 1614 return value; 1615 } 1616