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