2 * Copyright (c) 1998-2011 Apple Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
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.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
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.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
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>
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>
46 // how long to wait for matching root device, secs
48 #define ROOTDEVICETIMEOUT 120
50 #define ROOTDEVICETIMEOUT 60
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 extern int IODTGetDefault(const char *key
, void *infoAddr
, unsigned int infoSize
);
59 extern boolean_t
cpuid_vmm_present(void);
61 #define ROUNDUP(a, b) (((a) + ((b) - 1)) & (~((b) - 1)))
63 #define IOPOLLED_COREFILE (CONFIG_KDP_INTERACTIVE_DEBUGGING)
65 #if defined(XNU_TARGET_OS_BRIDGE)
66 #define kIOCoreDumpPath "/private/var/internal/kernelcore"
67 #elif defined(XNU_TARGET_OS_OSX)
68 #define kIOCoreDumpPath "/System/Volumes/VM/kernelcore"
70 #define kIOCoreDumpPath "/private/var/vm/kernelcore"
73 #define SYSTEM_NVRAM_PREFIX "40A0DDD2-77F8-4392-B4A3-1E7304206516:"
75 #if CONFIG_KDP_INTERACTIVE_DEBUGGING
77 * Touched by IOFindBSDRoot() if a RAMDisk is used for the root device.
79 extern uint64_t kdp_core_ramdisk_addr
;
80 extern uint64_t kdp_core_ramdisk_size
;
84 static void IOOpenPolledCoreFile(thread_call_param_t __unused
, thread_call_param_t corefilename
);
86 thread_call_t corefile_open_call
= NULL
;
92 IOService::publishResource("IOBSD");
95 corefile_open_call
= thread_call_allocate_with_options(IOOpenPolledCoreFile
, NULL
, THREAD_CALL_PRIORITY_KERNEL
, THREAD_CALL_OPTIONS_ONCE
);
98 return kIOReturnSuccess
;
102 IOServicePublishResource( const char * property
, boolean_t value
)
105 IOService::publishResource( property
, kOSBooleanTrue
);
107 IOService::getResourceService()->removeProperty( property
);
112 IOServiceWaitForMatchingResource( const char * property
, uint64_t timeout
)
114 OSDictionary
* dict
= NULL
;
115 IOService
* match
= NULL
;
116 boolean_t found
= false;
119 dict
= IOService::resourceMatching( property
);
123 match
= IOService::waitForMatchingService( dict
, timeout
);
140 IOCatalogueMatchingDriversPresent( const char * property
)
142 OSDictionary
* dict
= NULL
;
143 OSOrderedSet
* set
= NULL
;
144 SInt32 generationCount
= 0;
145 boolean_t found
= false;
148 dict
= OSDictionary::withCapacity(1);
152 dict
->setObject( property
, kOSBooleanTrue
);
153 set
= gIOCatalogue
->findDrivers( dict
, &generationCount
);
154 if (set
&& (set
->getCount() > 0)) {
170 IOBSDNameMatching( const char * name
)
173 const OSSymbol
* str
= NULL
;
176 dict
= IOService::serviceMatching( gIOServiceKey
);
180 str
= OSSymbol::withCString( name
);
184 dict
->setObject( kIOBSDNameKey
, (OSObject
*) str
);
201 IOUUIDMatching( void )
203 return IOService::resourceMatching( "boot-uuid-media" );
207 IONetworkNamePrefixMatching( const char * prefix
)
209 OSDictionary
* matching
;
210 OSDictionary
* propDict
= NULL
;
211 const OSSymbol
* str
= NULL
;
212 char networkType
[128];
215 matching
= IOService::serviceMatching( "IONetworkInterface" );
216 if (matching
== NULL
) {
220 propDict
= OSDictionary::withCapacity(1);
221 if (propDict
== NULL
) {
225 str
= OSSymbol::withCString( prefix
);
230 propDict
->setObject( "IOInterfaceNamePrefix", (OSObject
*) str
);
234 // see if we're contrained to netroot off of specific network type
235 if (PE_parse_boot_argn( "network-type", networkType
, 128 )) {
236 str
= OSSymbol::withCString( networkType
);
238 propDict
->setObject( "IONetworkRootType", str
);
244 if (matching
->setObject( gIOPropertyMatchKey
,
245 (OSObject
*) propDict
) != true) {
269 IORegisterNetworkInterface( IOService
* netif
)
271 // A network interface is typically named and registered
272 // with BSD after receiving a request from a user space
273 // "namer". However, for cases when the system needs to
274 // root from the network, this registration task must be
275 // done inside the kernel and completed before the root
276 // device is handed to BSD.
279 OSNumber
* zero
= NULL
;
280 OSString
* path
= NULL
;
281 OSDictionary
* dict
= NULL
;
282 char * pathBuf
= NULL
;
284 enum { kMaxPathLen
= 512 };
287 stack
= IOService::waitForService(
288 IOService::serviceMatching("IONetworkStack"));
293 dict
= OSDictionary::withCapacity(3);
298 zero
= OSNumber::withNumber((UInt64
) 0, 32);
303 pathBuf
= (char *) IOMalloc( kMaxPathLen
);
304 if (pathBuf
== NULL
) {
309 if (netif
->getPath( pathBuf
, &len
, gIOServicePlane
)
314 path
= OSString::withCStringNoCopy( pathBuf
);
319 dict
->setObject( "IOInterfaceUnit", zero
);
320 dict
->setObject( kIOPathMatchKey
, path
);
322 stack
->setProperties( dict
);
335 IOFree(pathBuf
, kMaxPathLen
);
338 return netif
->getProperty( kIOBSDNameKey
) != NULL
;
342 IOOFPathMatching( const char * path
, char * buf
, int maxLen
)
344 OSDictionary
* matching
= NULL
;
350 len
= ((int) strlen( kIODeviceTreePlane
":" ));
356 strlcpy( buf
, kIODeviceTreePlane
":", len
+ 1 );
359 len
= ((int) strnlen( path
, INT_MAX
));
364 strlcpy( comp
, path
, len
+ 1 );
366 matching
= OSDictionary::withCapacity( 1 );
371 str
= OSString::withCString( buf
);
375 matching
->setObject( kIOPathMatchKey
, str
);
388 static int didRam
= 0;
389 enum { kMaxPathBuf
= 512, kMaxBootVar
= 128 };
394 IORegistryEntry
*entry
;
396 if ((entry
= IORegistryEntry::fromPath("/chosen", gIODTPlane
))) {
397 OSData
*uuid_data
= (OSData
*)entry
->getProperty("boot-uuid");
399 return (const char*)uuid_data
->getBytesNoCopy();
407 IOGetApfsPrebootUUID(void)
409 IORegistryEntry
*entry
;
411 if ((entry
= IORegistryEntry::fromPath("/chosen", gIODTPlane
))) {
412 OSData
*uuid_data
= (OSData
*)entry
->getProperty("apfs-preboot-uuid");
414 return (const char*)uuid_data
->getBytesNoCopy();
422 IOGetAssociatedApfsVolgroupUUID(void)
424 IORegistryEntry
*entry
;
426 if ((entry
= IORegistryEntry::fromPath("/chosen", gIODTPlane
))) {
427 OSData
*uuid_data
= (OSData
*)entry
->getProperty("associated-volume-group");
429 return (const char*)uuid_data
->getBytesNoCopy();
437 IOGetBootObjectsPath(void)
439 IORegistryEntry
*entry
;
441 if ((entry
= IORegistryEntry::fromPath("/chosen", gIODTPlane
))) {
442 OSData
*path_prefix_data
= (OSData
*)entry
->getProperty("boot-objects-path");
443 if (path_prefix_data
) {
444 return (const char *)path_prefix_data
->getBytesNoCopy();
452 * Set NVRAM to boot into the right flavor of Recovery,
453 * optionally passing a UUID of a volume that failed to boot.
454 * If `reboot` is true, reboot immediately.
456 * Returns true if `mode` was understood, false otherwise.
457 * (Does not return if `reboot` is true.)
460 IOSetRecoveryBoot(bsd_bootfail_mode_t mode
, uuid_t volume_uuid
, boolean_t reboot
)
462 IODTNVRAM
*nvram
= NULL
;
463 const OSSymbol
*boot_command_sym
= NULL
;
464 OSString
*boot_command_recover
= NULL
;
466 if (mode
== BSD_BOOTFAIL_SEAL_BROKEN
) {
467 const char *boot_mode
= "ssv-seal-broken";
468 uuid_string_t volume_uuid_str
;
470 // Set `recovery-broken-seal-uuid = <volume_uuid>`.
472 uuid_unparse_upper(volume_uuid
, volume_uuid_str
);
474 if (!PEWriteNVRAMProperty(SYSTEM_NVRAM_PREFIX
"recovery-broken-seal-uuid",
475 volume_uuid_str
, sizeof(uuid_string_t
))) {
476 IOLog("Failed to write recovery-broken-seal-uuid to NVRAM.\n");
480 // Set `recovery-boot-mode = ssv-seal-broken`.
481 if (!PEWriteNVRAMProperty(SYSTEM_NVRAM_PREFIX
"recovery-boot-mode", boot_mode
,
482 (const unsigned int) strlen(boot_mode
))) {
483 IOLog("Failed to write recovery-boot-mode to NVRAM.\n");
485 } else if (mode
== BSD_BOOTFAIL_MEDIA_MISSING
) {
486 const char *boot_picker_reason
= "missing-boot-media";
488 // Set `boot-picker-bringup-reason = missing-boot-media`.
489 if (!PEWriteNVRAMProperty(SYSTEM_NVRAM_PREFIX
"boot-picker-bringup-reason",
490 boot_picker_reason
, (const unsigned int) strlen(boot_picker_reason
))) {
491 IOLog("Failed to write boot-picker-bringup-reason to NVRAM.\n");
494 // Set `boot-command = recover`.
496 // Construct an OSSymbol and an OSString to be the (key, value) pair
497 // we write to NVRAM. Unfortunately, since our value must be an OSString
498 // instead of an OSData, we cannot use PEWriteNVRAMProperty() here.
499 boot_command_sym
= OSSymbol::withCStringNoCopy(SYSTEM_NVRAM_PREFIX
"boot-command");
500 boot_command_recover
= OSString::withCStringNoCopy("recover");
501 if (boot_command_sym
== NULL
|| boot_command_recover
== NULL
) {
502 IOLog("Failed to create boot-command strings.\n");
506 // Wait for NVRAM to be readable...
507 nvram
= OSDynamicCast(IODTNVRAM
, IOService::waitForService(
508 IOService::serviceMatching("IODTNVRAM")));
510 IOLog("Failed to acquire IODTNVRAM object.\n");
514 // Wait for NVRAM to be writable...
515 if (!IOServiceWaitForMatchingResource("IONVRAM", UINT64_MAX
)) {
516 IOLog("Failed to wait for IONVRAM service.\n");
517 // attempt the work anyway...
520 // Write the new boot-command to NVRAM, and sync if successful.
521 if (!nvram
->setProperty(boot_command_sym
, boot_command_recover
)) {
522 IOLog("Failed to save new boot-command to NVRAM.\n");
527 IOLog("Unknown mode: %d\n", mode
);
531 // Clean up and reboot!
533 if (boot_command_recover
!= NULL
) {
534 boot_command_recover
->release();
537 if (boot_command_sym
!= NULL
) {
538 boot_command_sym
->release();
542 IOLog("\nAbout to reboot into Recovery!\n");
543 (void)PEHaltRestart(kPERestartCPU
);
550 IOGetVMMPresent(void)
552 int hv_vmm_present
= 0;
554 #if defined(__arm64__)
555 if (IODTGetDefault("vmm-present", &hv_vmm_present
, sizeof(hv_vmm_present
)) < 0) {
559 if (hv_vmm_present
!= 0) {
562 #elif defined(__x86_64__)
563 hv_vmm_present
= cpuid_vmm_present();
566 return hv_vmm_present
;
570 IOFindBSDRoot( char * rootName
, unsigned int rootNameSize
,
571 dev_t
* root
, u_int32_t
* oflags
)
575 IORegistryEntry
* regEntry
;
576 OSDictionary
* matching
= NULL
;
579 OSData
* data
= NULL
;
583 const char * mediaProperty
= NULL
;
586 const char * look
= NULL
;
588 bool debugInfoPrintedOnce
= false;
589 bool needNetworkKexts
= false;
590 const char * uuidStr
= NULL
;
592 static int mountAttempts
= 0;
596 // stall here for anyone matching on the IOBSD resource to finish (filesystems)
597 matching
= IOService::serviceMatching(gIOResourcesKey
);
599 matching
->setObject(gIOResourceMatchedKey
, gIOBSDKey
);
601 if ((service
= IOService::waitForMatchingService(matching
, 30ULL * kSecondScale
))) {
609 if (mountAttempts
++) {
610 IOLog("mount(%d) failed\n", mountAttempts
);
614 str
= (char *) IOMalloc( kMaxPathBuf
+ kMaxBootVar
);
616 return kIOReturnNoMemory
;
618 rdBootVar
= str
+ kMaxPathBuf
;
620 if (!PE_parse_boot_argn("rd", rdBootVar
, kMaxBootVar
)
621 && !PE_parse_boot_argn("rootdev", rdBootVar
, kMaxBootVar
)) {
626 if ((regEntry
= IORegistryEntry::fromPath( "/chosen", gIODTPlane
))) {
627 di_root_ramfile(regEntry
);
628 data
= OSDynamicCast(OSData
, regEntry
->getProperty( "root-matching" ));
630 matching
= OSDynamicCast(OSDictionary
, OSUnserializeXML((char *)data
->getBytesNoCopy()));
636 data
= (OSData
*) regEntry
->getProperty( "boot-uuid" );
638 uuidStr
= (const char*)data
->getBytesNoCopy();
639 OSString
*uuidString
= OSString::withCString( uuidStr
);
641 // match the boot-args boot-uuid processing below
643 IOLog("rooting via boot-uuid from /chosen: %s\n", uuidStr
);
644 IOService::publishResource( "boot-uuid", uuidString
);
645 uuidString
->release();
646 matching
= IOUUIDMatching();
647 mediaProperty
= "boot-uuid-media";
659 // See if we have a RAMDisk property in /chosen/memory-map. If so, make it into a device.
660 // It will become /dev/mdx, where x is 0-f.
663 if (!didRam
) { /* Have we already build this ram disk? */
664 didRam
= 1; /* Remember we did this */
665 if ((regEntry
= IORegistryEntry::fromPath( "/chosen/memory-map", gIODTPlane
))) { /* Find the map node */
666 data
= (OSData
*)regEntry
->getProperty("RAMDisk"); /* Find the ram disk, if there */
667 if (data
) { /* We found one */
668 uintptr_t *ramdParms
;
669 ramdParms
= (uintptr_t *)data
->getBytesNoCopy(); /* Point to the ram disk base and size */
671 #define MAX_PHYS_RAM (((uint64_t)UINT_MAX) << 12)
672 if (ramdParms
[1] > MAX_PHYS_RAM
) {
673 panic("ramdisk params");
675 #endif /* __LP64__ */
676 (void)mdevadd(-1, ml_static_ptovirt(ramdParms
[0]) >> 12, (unsigned int) (ramdParms
[1] >> 12), 0); /* Initialize it and pass back the device number */
678 regEntry
->release(); /* Toss the entry */
683 // Now check if we are trying to root on a memory device
686 if ((rdBootVar
[0] == 'm') && (rdBootVar
[1] == 'd') && (rdBootVar
[3] == 0)) {
687 dchar
= xchar
= rdBootVar
[2]; /* Get the actual device */
688 if ((xchar
>= '0') && (xchar
<= '9')) {
689 xchar
= xchar
- '0'; /* If digit, convert */
691 xchar
= xchar
& ~' '; /* Fold to upper case */
692 if ((xchar
>= 'A') && (xchar
<= 'F')) { /* Is this a valid digit? */
693 xchar
= (xchar
& 0xF) + 9; /* Convert the hex digit */
694 dchar
= dchar
| ' '; /* Fold to lower case */
696 xchar
= -1; /* Show bogus */
699 if (xchar
>= 0) { /* Do we have a valid memory device name? */
700 *root
= mdevlookup(xchar
); /* Find the device number */
701 if (*root
>= 0) { /* Did we find one? */
702 rootName
[0] = 'm'; /* Build root name */
703 rootName
[1] = 'd'; /* Build root name */
704 rootName
[2] = (char) dchar
; /* Build root name */
705 rootName
[3] = 0; /* Build root name */
706 IOLog("BSD root: %s, major %d, minor %d\n", rootName
, major(*root
), minor(*root
));
707 *oflags
= 0; /* Show that this is not network */
709 #if CONFIG_KDP_INTERACTIVE_DEBUGGING
710 /* retrieve final ramdisk range and initialize KDP variables */
711 if (mdevgetrange(xchar
, &kdp_core_ramdisk_addr
, &kdp_core_ramdisk_size
) != 0) {
712 IOLog("Unable to retrieve range for root memory device %d\n", xchar
);
713 kdp_core_ramdisk_addr
= 0;
714 kdp_core_ramdisk_size
= 0;
718 goto iofrootx
; /* Join common exit... */
720 panic("IOFindBSDRoot: specified root memory device, %s, has not been configured\n", rdBootVar
); /* Not there */
724 if ((!matching
) && rdBootVar
[0]) {
727 if (look
[0] == '*') {
731 if (strncmp( look
, "en", strlen( "en" )) == 0) {
732 matching
= IONetworkNamePrefixMatching( "en" );
733 needNetworkKexts
= true;
734 } else if (strncmp( look
, "uuid", strlen( "uuid" )) == 0) {
736 OSString
*uuidString
;
738 uuid
= (char *)IOMalloc( kMaxBootVar
);
741 if (!PE_parse_boot_argn( "boot-uuid", uuid
, kMaxBootVar
)) {
742 panic( "rd=uuid but no boot-uuid=<value> specified" );
744 uuidString
= OSString::withCString( uuid
);
746 IOService::publishResource( "boot-uuid", uuidString
);
747 uuidString
->release();
748 IOLog( "\nWaiting for boot volume with UUID %s\n", uuid
);
749 matching
= IOUUIDMatching();
750 mediaProperty
= "boot-uuid-media";
752 IOFree( uuid
, kMaxBootVar
);
755 matching
= IOBSDNameMatching( look
);
761 // Match any HFS media
763 matching
= IOService::serviceMatching( "IOMedia" );
764 astring
= OSString::withCStringNoCopy("Apple_HFS");
766 matching
->setObject("Content", astring
);
771 if (gIOKitDebug
& kIOWaitQuietBeforeRoot
) {
772 IOLog( "Waiting for matching to complete\n" );
773 IOService::getPlatform()->waitQuiet();
776 if (true && matching
) {
777 OSSerialize
* s
= OSSerialize::withCapacity( 5 );
779 if (matching
->serialize( s
)) {
780 IOLog( "Waiting on %s\n", s
->text());
787 || PE_parse_boot_argn("-s", namep
, sizeof(namep
))) {
788 IOService::startDeferredMatches();
792 t
.tv_sec
= ROOTDEVICETIMEOUT
;
795 service
= IOService::waitForService( matching
, &t
);
796 if ((!service
) || (mountAttempts
== 10)) {
797 #if !XNU_TARGET_OS_OSX || !defined(__arm64__)
798 PE_display_icon( 0, "noroot");
799 IOLog( "Still waiting for root device\n" );
802 if (!debugInfoPrintedOnce
) {
803 debugInfoPrintedOnce
= true;
804 if (gIOKitDebug
& kIOLogDTree
) {
805 IOLog("\nDT plane:\n");
806 IOPrintPlane( gIODTPlane
);
808 if (gIOKitDebug
& kIOLogServiceTree
) {
809 IOLog("\nService plane:\n");
810 IOPrintPlane( gIOServicePlane
);
812 if (gIOKitDebug
& kIOLogMemory
) {
817 #if XNU_TARGET_OS_OSX && defined(__arm64__)
818 // The disk isn't found - have the user pick from recoveryOS+.
819 (void)IOSetRecoveryBoot(BSD_BOOTFAIL_MEDIA_MISSING
, NULL
, true);
825 if (service
&& mediaProperty
) {
826 service
= (IOService
*)service
->getProperty(mediaProperty
);
832 // If the IOService we matched to is a subclass of IONetworkInterface,
833 // then make sure it has been registered with BSD and has a BSD name
837 && service
->metaCast( "IONetworkInterface" )
838 && !IORegisterNetworkInterface( service
)) {
844 service
->getPath( str
, &len
, gIOServicePlane
);
845 IOLog( "Got boot device = %s\n", str
);
847 iostr
= (OSString
*) service
->getProperty( kIOBSDNameKey
);
849 strlcpy( rootName
, iostr
->getCStringNoCopy(), rootNameSize
);
851 off
= (OSNumber
*) service
->getProperty( kIOBSDMajorKey
);
853 mjr
= off
->unsigned32BitValue();
855 off
= (OSNumber
*) service
->getProperty( kIOBSDMinorKey
);
857 mnr
= off
->unsigned32BitValue();
860 if (service
->metaCast( "IONetworkInterface" )) {
864 IOLog( "Wait for root failed\n" );
865 strlcpy( rootName
, "en0", rootNameSize
);
869 IOLog( "BSD root: %s", rootName
);
871 IOLog(", major %d, minor %d\n", mjr
, mnr
);
876 *root
= makedev( mjr
, mnr
);
879 IOFree( str
, kMaxPathBuf
+ kMaxBootVar
);
882 if ((gIOKitDebug
& (kIOLogDTree
| kIOLogServiceTree
| kIOLogMemory
)) && !debugInfoPrintedOnce
) {
883 IOService::getPlatform()->waitQuiet();
884 if (gIOKitDebug
& kIOLogDTree
) {
885 IOLog("\nDT plane:\n");
886 IOPrintPlane( gIODTPlane
);
888 if (gIOKitDebug
& kIOLogServiceTree
) {
889 IOLog("\nService plane:\n");
890 IOPrintPlane( gIOServicePlane
);
892 if (gIOKitDebug
& kIOLogMemory
) {
897 return kIOReturnSuccess
;
901 IORamDiskBSDRoot(void)
903 char rdBootVar
[kMaxBootVar
];
904 if (PE_parse_boot_argn("rd", rdBootVar
, kMaxBootVar
)
905 || PE_parse_boot_argn("rootdev", rdBootVar
, kMaxBootVar
)) {
906 if ((rdBootVar
[0] == 'm') && (rdBootVar
[1] == 'd') && (rdBootVar
[3] == 0)) {
914 IOSecureBSDRoot(const char * rootName
)
916 #if CONFIG_SECURE_BSD_ROOT
918 IOPlatformExpert
*pe
;
919 OSDictionary
*matching
;
920 const OSSymbol
*functionName
= OSSymbol::withCStringNoCopy("SecureRootName");
922 matching
= IOService::serviceMatching("IOPlatformExpert");
924 pe
= (IOPlatformExpert
*) IOService::waitForMatchingService(matching
, 30ULL * kSecondScale
);
927 // Returns kIOReturnNotPrivileged is the root device is not secure.
928 // Returns kIOReturnUnsupported if "SecureRootName" is not implemented.
929 result
= pe
->callPlatformFunction(functionName
, false, (void *)rootName
, (void *)NULL
, (void *)NULL
, (void *)NULL
);
930 functionName
->release();
931 OSSafeReleaseNULL(pe
);
933 if (result
== kIOReturnNotPrivileged
) {
937 #endif // CONFIG_SECURE_BSD_ROOT
941 IOBSDRegistryEntryForDeviceTree(char * path
)
943 return IORegistryEntry::fromPath(path
, gIODTPlane
);
947 IOBSDRegistryEntryRelease(void * entry
)
949 IORegistryEntry
* regEntry
= (IORegistryEntry
*)entry
;
958 IOBSDRegistryEntryGetData(void * entry
, char * property_name
,
962 IORegistryEntry
* regEntry
= (IORegistryEntry
*)entry
;
964 data
= (OSData
*) regEntry
->getProperty(property_name
);
966 *packet_length
= data
->getLength();
967 return data
->getBytesNoCopy();
973 IOBSDGetPlatformUUID( uuid_t uuid
, mach_timespec_t timeout
)
975 IOService
* resources
;
978 resources
= IOService::waitForService( IOService::resourceMatching( kIOPlatformUUIDKey
), (timeout
.tv_sec
|| timeout
.tv_nsec
) ? &timeout
: NULL
);
979 if (resources
== NULL
) {
980 return KERN_OPERATION_TIMED_OUT
;
983 string
= (OSString
*) IOService::getPlatform()->getProvider()->getProperty( kIOPlatformUUIDKey
);
984 if (string
== NULL
) {
985 return KERN_NOT_SUPPORTED
;
988 uuid_parse( string
->getCStringNoCopy(), uuid
);
994 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
996 #include <sys/conf.h>
997 #include <sys/vnode.h>
998 #include <sys/vnode_internal.h>
999 #include <sys/fcntl.h>
1000 #include <IOKit/IOPolledInterface.h>
1001 #include <IOKit/IOBufferMemoryDescriptor.h>
1003 IOPolledFileIOVars
* gIOPolledCoreFileVars
;
1004 kern_return_t gIOPolledCoreFileOpenRet
= kIOReturnNotReady
;
1005 IOPolledCoreFileMode_t gIOPolledCoreFileMode
= kIOPolledCoreFileModeNotInitialized
;
1007 #if IOPOLLED_COREFILE
1009 #if defined(XNU_TARGET_OS_BRIDGE)
1010 // On bridgeOS allocate a 150MB corefile and leave 150MB free
1011 #define kIOCoreDumpSize 150ULL*1024ULL*1024ULL
1012 #define kIOCoreDumpFreeSize 150ULL*1024ULL*1024ULL
1014 #elif !defined(XNU_TARGET_OS_OSX) /* defined(XNU_TARGET_OS_BRIDGE) */
1015 // On embedded devices with >3GB DRAM we allocate a 500MB corefile
1016 // otherwise allocate a 350MB corefile. Leave 350 MB free
1018 #define kIOCoreDumpMinSize 350ULL*1024ULL*1024ULL
1019 #define kIOCoreDumpLargeSize 500ULL*1024ULL*1024ULL
1021 #define kIOCoreDumpFreeSize 350ULL*1024ULL*1024ULL
1023 #else /* defined(XNU_TARGET_OS_BRIDGE) */
1024 // on macOS devices allocate a corefile sized at 1GB / 32GB of DRAM,
1025 // fallback to a 1GB corefile and leave at least 1GB free
1026 #define kIOCoreDumpMinSize 1024ULL*1024ULL*1024ULL
1027 #define kIOCoreDumpIncrementalSize 1024ULL*1024ULL*1024ULL
1029 #define kIOCoreDumpFreeSize 1024ULL*1024ULL*1024ULL
1031 // on older macOS devices we allocate a 1MB file at boot
1032 // to store a panic time stackshot
1033 #define kIOStackshotFileSize 1024ULL*1024ULL
1035 #endif /* defined(XNU_TARGET_OS_BRIDGE) */
1037 static IOPolledCoreFileMode_t
1040 if (on_device_corefile_enabled()) {
1041 return kIOPolledCoreFileModeCoredump
;
1042 } else if (panic_stackshot_to_disk_enabled()) {
1043 return kIOPolledCoreFileModeStackshot
;
1045 return kIOPolledCoreFileModeDisabled
;
1050 IOCoreFileGetSize(uint64_t *ideal_size
, uint64_t *fallback_size
, uint64_t *free_space_to_leave
, IOPolledCoreFileMode_t mode
)
1052 unsigned int requested_corefile_size
= 0;
1054 *ideal_size
= *fallback_size
= *free_space_to_leave
= 0;
1056 #if defined(XNU_TARGET_OS_BRIDGE)
1057 #pragma unused(mode)
1058 *ideal_size
= *fallback_size
= kIOCoreDumpSize
;
1059 *free_space_to_leave
= kIOCoreDumpFreeSize
;
1060 #elif !defined(XNU_TARGET_OS_OSX) /* defined(XNU_TARGET_OS_BRIDGE) */
1061 #pragma unused(mode)
1062 *ideal_size
= *fallback_size
= kIOCoreDumpMinSize
;
1064 if (max_mem
> (3 * 1024ULL * 1024ULL * 1024ULL)) {
1065 *ideal_size
= kIOCoreDumpLargeSize
;
1068 *free_space_to_leave
= kIOCoreDumpFreeSize
;
1069 #else /* defined(XNU_TARGET_OS_BRIDGE) */
1070 if (mode
== kIOPolledCoreFileModeCoredump
) {
1071 *ideal_size
= *fallback_size
= kIOCoreDumpMinSize
;
1072 if (kIOCoreDumpIncrementalSize
!= 0 && max_mem
> (32 * 1024ULL * 1024ULL * 1024ULL)) {
1073 *ideal_size
= ((ROUNDUP(max_mem
, (32 * 1024ULL * 1024ULL * 1024ULL)) / (32 * 1024ULL * 1024ULL * 1024ULL)) * kIOCoreDumpIncrementalSize
);
1075 *free_space_to_leave
= kIOCoreDumpFreeSize
;
1076 } else if (mode
== kIOPolledCoreFileModeStackshot
) {
1077 *ideal_size
= *fallback_size
= *free_space_to_leave
= kIOStackshotFileSize
;
1079 #endif /* defined(XNU_TARGET_OS_BRIDGE) */
1080 // If a custom size was requested, override the ideal and requested sizes
1081 if (PE_parse_boot_argn("corefile_size_mb", &requested_corefile_size
, sizeof(requested_corefile_size
))) {
1082 IOLog("Boot-args specify %d MB kernel corefile\n", requested_corefile_size
);
1084 *ideal_size
= *fallback_size
= (requested_corefile_size
* 1024ULL * 1024ULL);
1091 IOOpenPolledCoreFile(thread_call_param_t __unused
, thread_call_param_t corefilename
)
1093 assert(corefilename
!= NULL
);
1096 char *filename
= (char *) corefilename
;
1097 uint64_t corefile_size_bytes
= 0, corefile_fallback_size_bytes
= 0, free_space_to_leave_bytes
= 0;
1098 IOPolledCoreFileMode_t mode_to_init
= GetCoreFileMode();
1100 if (gIOPolledCoreFileVars
) {
1103 if (!IOPolledInterface::gMetaClass
.getInstanceCount()) {
1107 if (mode_to_init
== kIOPolledCoreFileModeDisabled
) {
1108 gIOPolledCoreFileMode
= kIOPolledCoreFileModeDisabled
;
1112 // We'll overwrite this once we open the file, we update this to mark that we have made
1113 // it past initialization
1114 gIOPolledCoreFileMode
= kIOPolledCoreFileModeClosed
;
1116 IOCoreFileGetSize(&corefile_size_bytes
, &corefile_fallback_size_bytes
, &free_space_to_leave_bytes
, mode_to_init
);
1119 err
= IOPolledFileOpen(filename
, kIOPolledFileCreate
, corefile_size_bytes
, free_space_to_leave_bytes
,
1120 NULL
, 0, &gIOPolledCoreFileVars
, NULL
, NULL
, NULL
);
1121 if (kIOReturnSuccess
== err
) {
1123 } else if (kIOReturnNoSpace
== err
) {
1124 IOLog("Failed to open corefile of size %llu MB (low disk space)",
1125 (corefile_size_bytes
/ (1024ULL * 1024ULL)));
1126 if (corefile_size_bytes
== corefile_fallback_size_bytes
) {
1127 gIOPolledCoreFileOpenRet
= err
;
1131 IOLog("Failed to open corefile of size %llu MB (returned error 0x%x)\n",
1132 (corefile_size_bytes
/ (1024ULL * 1024ULL)), err
);
1133 gIOPolledCoreFileOpenRet
= err
;
1137 err
= IOPolledFileOpen(filename
, kIOPolledFileCreate
, corefile_fallback_size_bytes
, free_space_to_leave_bytes
,
1138 NULL
, 0, &gIOPolledCoreFileVars
, NULL
, NULL
, NULL
);
1139 if (kIOReturnSuccess
!= err
) {
1140 IOLog("Failed to open corefile of size %llu MB (returned error 0x%x)\n",
1141 (corefile_fallback_size_bytes
/ (1024ULL * 1024ULL)), err
);
1142 gIOPolledCoreFileOpenRet
= err
;
1147 gIOPolledCoreFileOpenRet
= IOPolledFilePollersSetup(gIOPolledCoreFileVars
, kIOPolledPreflightCoreDumpState
);
1148 if (kIOReturnSuccess
!= gIOPolledCoreFileOpenRet
) {
1149 IOPolledFileClose(&gIOPolledCoreFileVars
, 0, NULL
, 0, 0, 0);
1150 IOLog("IOPolledFilePollersSetup for corefile failed with error: 0x%x\n", err
);
1152 IOLog("Opened corefile of size %llu MB\n", (corefile_size_bytes
/ (1024ULL * 1024ULL)));
1153 gIOPolledCoreFileMode
= mode_to_init
;
1160 IOClosePolledCoreFile(void)
1162 gIOPolledCoreFileOpenRet
= kIOReturnNotOpen
;
1163 gIOPolledCoreFileMode
= kIOPolledCoreFileModeClosed
;
1164 IOPolledFilePollersClose(gIOPolledCoreFileVars
, kIOPolledPostflightCoreDumpState
);
1165 IOPolledFileClose(&gIOPolledCoreFileVars
, 0, NULL
, 0, 0, 0);
1168 #endif /* IOPOLLED_COREFILE */
1171 IOBSDMountChange(struct mount
* mp
, uint32_t op
)
1173 #if IOPOLLED_COREFILE
1181 case kIOMountChangeMount
:
1182 case kIOMountChangeDidResize
:
1184 if (gIOPolledCoreFileVars
) {
1187 flags
= vfs_flags(mp
);
1188 if (MNT_RDONLY
& flags
) {
1191 if (!(MNT_LOCAL
& flags
)) {
1195 vn
= vfs_vnodecovered(mp
);
1199 pathLen
= sizeof(path
);
1200 result
= vn_getpath(vn
, &path
[0], &pathLen
);
1208 #if defined(XNU_TARGET_OS_BRIDGE)
1209 // on bridgeOS systems we put the core in /private/var/internal. We don't
1210 // want to match with /private/var because /private/var/internal is often mounted
1211 // over /private/var
1212 if ((pathLen
- 1) < (int) strlen("/private/var/internal")) {
1216 if (0 != strncmp(path
, kIOCoreDumpPath
, pathLen
- 1)) {
1220 thread_call_enter1(corefile_open_call
, (void *) kIOCoreDumpPath
);
1223 case kIOMountChangeUnmount
:
1224 case kIOMountChangeWillResize
:
1225 if (gIOPolledCoreFileVars
&& (mp
== kern_file_mount(gIOPolledCoreFileVars
->fileRef
))) {
1226 thread_call_cancel_wait(corefile_open_call
);
1227 IOClosePolledCoreFile();
1231 #endif /* IOPOLLED_COREFILE */
1234 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1236 extern "C" boolean_t
1237 IOTaskHasEntitlement(task_t task
, const char * entitlement
)
1240 obj
= IOUserClient::copyClientEntitlement(task
, entitlement
);
1245 return obj
!= kOSBooleanFalse
;
1248 extern "C" boolean_t
1249 IOVnodeHasEntitlement(vnode_t vnode
, int64_t off
, const char *entitlement
)
1252 off_t offset
= (off_t
)off
;
1254 obj
= IOUserClient::copyClientEntitlementVnode(vnode
, offset
, entitlement
);
1259 return obj
!= kOSBooleanFalse
;
1263 IOVnodeGetEntitlement(vnode_t vnode
, int64_t off
, const char *entitlement
)
1265 OSObject
*obj
= NULL
;
1266 OSString
*str
= NULL
;
1269 off_t offset
= (off_t
)off
;
1271 obj
= IOUserClient::copyClientEntitlementVnode(vnode
, offset
, entitlement
);
1273 str
= OSDynamicCast(OSString
, obj
);
1275 len
= str
->getLength() + 1;
1276 value
= (char *)kheap_alloc(KHEAP_DATA_BUFFERS
, len
, Z_WAITOK
);
1277 strlcpy(value
, str
->getCStringNoCopy(), len
);