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
IOKitBSDInit(void)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
IOServicePublishResource(const char * property,boolean_t value)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
IOServiceWaitForMatchingResource(const char * property,uint64_t timeout)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
IOCatalogueMatchingDriversPresent(const char * property)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 *
IOBSDNameMatching(const char * name)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 *
IOUUIDMatching(void)221 IOUUIDMatching( void )
222 {
223 return IOService::resourceMatching( "boot-uuid-media" );
224 }
225
226 OSDictionary *
IONetworkNamePrefixMatching(const char * prefix)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
IORegisterNetworkInterface(IOService * netif)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 *
IOOFPathMatching(const char * path,char * buf,int maxLen)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
IOGetBootUUID(char * uuid)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
IOGetApfsPrebootUUID(char * uuid)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
IOGetAssociatedApfsVolgroupUUID(char * uuid)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
IOGetBootObjectsPath(char * path_prefix)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
IOGetBootManifestHash(char * hash_data,size_t * hash_data_size)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
IOSetRecoveryBoot(bsd_bootfail_mode_t mode,uuid_t volume_uuid,boolean_t reboot)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
IOFindBSDRoot(char * rootName,unsigned int rootNameSize,dev_t * root,u_int32_t * oflags)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
IOSetImageBoot(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
IORamDiskBSDRoot(void)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
IOSecureBSDRoot(const char * rootName)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 *
IOBSDRegistryEntryForDeviceTree(char * path)1039 IOBSDRegistryEntryForDeviceTree(char * path)
1040 {
1041 return IORegistryEntry::fromPath(path, gIODTPlane);
1042 }
1043
1044 void
IOBSDRegistryEntryRelease(void * entry)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 *
IOBSDRegistryEntryGetData(void * entry,char * property_name,int * packet_length)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
IOBSDGetPlatformUUID(uuid_t uuid,mach_timespec_t timeout)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
GetCoreFileMode()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
IOCoreFileGetSize(uint64_t * ideal_size,uint64_t * fallback_size,uint64_t * free_space_to_leave,IOPolledCoreFileMode_t mode)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
IOAccessCoreFileData(void * context,boolean_t write,uint64_t offset,int length,void * buffer)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
IOOpenPolledCoreFile(thread_call_param_t __unused,thread_call_param_t corefilename)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
IOProvideCoreFileAccess(IOCoreFileAccessRecipient recipient,void * recipient_context)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
IOClosePolledCoreFile(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
IOUnlinkPolledCoreFile(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
IOBSDMountChange(struct mount * mp,uint32_t op)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
IOBSDLowSpaceUnlinkKernelCore(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*
copyOSStringAsCString(OSString * string)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
IOCurrentTaskHasStringEntitlement(const char * entitlement,const char * value)1477 IOCurrentTaskHasStringEntitlement(const char *entitlement, const char *value)
1478 {
1479 return IOTaskHasStringEntitlement(NULL, entitlement, value);
1480 }
1481
1482 extern "C" boolean_t
IOTaskHasStringEntitlement(task_t task,const char * entitlement,const char * value)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
IOCurrentTaskHasEntitlement(const char * entitlement)1508 IOCurrentTaskHasEntitlement(const char *entitlement)
1509 {
1510 return IOTaskHasEntitlement(NULL, entitlement);
1511 }
1512
1513 extern "C" boolean_t
IOTaskHasEntitlement(task_t task,const char * entitlement)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*
IOCurrentTaskGetEntitlement(const char * entitlement)1538 IOCurrentTaskGetEntitlement(const char *entitlement)
1539 {
1540 return IOTaskGetEntitlement(NULL, entitlement);
1541 }
1542
1543 extern "C" char*
IOTaskGetEntitlement(task_t task,const char * entitlement)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
IOVnodeHasEntitlement(vnode_t vnode,int64_t off,const char * entitlement)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 *
IOVnodeGetEntitlement(vnode_t vnode,int64_t off,const char * entitlement)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