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