xref: /xnu-8796.101.5/pexpert/arm/pe_init.c (revision aca3beaa3dfbd42498b42c5e5ce20a938e6554e5)
1 /*
2  * Copyright (c) 2000-2017 Apple Inc. All rights reserved.
3  *
4  *    arm platform expert initialization.
5  */
6 #include <sys/types.h>
7 #include <sys/kdebug.h>
8 #include <mach/vm_param.h>
9 #include <pexpert/protos.h>
10 #include <pexpert/pexpert.h>
11 #include <pexpert/boot.h>
12 #include <pexpert/device_tree.h>
13 #include <pexpert/pe_images.h>
14 #include <kern/sched_prim.h>
15 #include <kern/socd_client.h>
16 #include <machine/atomic.h>
17 #include <machine/machine_routines.h>
18 #include <arm/caches_internal.h>
19 #include <kern/debug.h>
20 #include <libkern/section_keywords.h>
21 #include <os/overflow.h>
22 
23 #include <pexpert/arm64/board_config.h>
24 
25 
26 /* extern references */
27 extern void     pe_identify_machine(boot_args *bootArgs);
28 
29 /* static references */
30 static void     pe_prepare_images(void);
31 
32 /* private globals */
33 SECURITY_READ_ONLY_LATE(PE_state_t) PE_state;
34 
35 #define FW_VERS_LEN 128
36 
37 char iBoot_version[FW_VERS_LEN];
38 #if defined(TARGET_OS_OSX) && defined(__arm64__)
39 char iBoot_Stage_2_version[FW_VERS_LEN];
40 #endif /* defined(TARGET_OS_OSX) && defined(__arm64__) */
41 
42 /*
43  * This variable is only modified once, when the BSP starts executing. We put it in __DATA_CONST
44  * as page protections on kernel text early in startup are read-write. The kernel is
45  * locked down later in start-up, said mappings become RO and thus this
46  * variable becomes immutable.
47  *
48  * See osfmk/arm/arm_vm_init.c for more information.
49  */
50 SECURITY_READ_ONLY_LATE(volatile uint32_t) debug_enabled = FALSE;
51 
52 uint8_t         gPlatformECID[8];
53 uint32_t        gPlatformMemoryID;
54 static boolean_t vc_progress_initialized = FALSE;
55 uint64_t    last_hwaccess_thread = 0;
56 char     gTargetTypeBuffer[16];
57 char     gModelTypeBuffer[32];
58 
59 /* Clock Frequency Info */
60 clock_frequency_info_t gPEClockFrequencyInfo;
61 
62 vm_offset_t gPanicBase = 0;
63 unsigned int gPanicSize;
64 struct embedded_panic_header *panic_info = NULL;
65 
66 #if (DEVELOPMENT || DEBUG) && defined(XNU_TARGET_OS_BRIDGE)
67 /*
68  * On DEVELOPMENT bridgeOS, we map the x86 panic region
69  * so we can include this data in bridgeOS corefiles
70  */
71 uint64_t macos_panic_base = 0;
72 unsigned int macos_panic_size = 0;
73 
74 struct macos_panic_header *mac_panic_header = NULL;
75 #endif
76 
77 /* Maximum size of panic log excluding headers, in bytes */
78 static unsigned int panic_text_len;
79 
80 /* Whether a console is standing by for panic logging */
81 static boolean_t panic_console_available = FALSE;
82 
83 /* socd trace ram attributes */
84 static SECURITY_READ_ONLY_LATE(vm_offset_t) socd_trace_ram_base = 0;
85 static SECURITY_READ_ONLY_LATE(vm_size_t) socd_trace_ram_size = 0;
86 
87 extern uint32_t crc32(uint32_t crc, const void *buf, size_t size);
88 
89 void PE_slide_devicetree(vm_offset_t);
90 
91 static void
check_for_panic_log(void)92 check_for_panic_log(void)
93 {
94 #ifdef PLATFORM_PANIC_LOG_PADDR
95 	gPanicBase = ml_io_map_wcomb(PLATFORM_PANIC_LOG_PADDR, PLATFORM_PANIC_LOG_SIZE);
96 	panic_text_len = PLATFORM_PANIC_LOG_SIZE - sizeof(struct embedded_panic_header);
97 	gPanicSize = PLATFORM_PANIC_LOG_SIZE;
98 #else
99 	DTEntry entry, chosen;
100 	unsigned int size;
101 	uintptr_t const *reg_prop;
102 	uint32_t const *panic_region_length;
103 
104 	/*
105 	 * DT properties for the panic region are populated by UpdateDeviceTree() in iBoot:
106 	 *
107 	 * chosen {
108 	 *   embedded-panic-log-size = <0x00080000>;
109 	 *   [a bunch of other stuff]
110 	 * };
111 	 *
112 	 * pram {
113 	 *   reg = <0x00000008_fbc48000 0x00000000_000b4000>;
114 	 * };
115 	 *
116 	 * reg[0] is the physical address
117 	 * reg[1] is the size of iBoot's kMemoryRegion_Panic (not used)
118 	 * embedded-panic-log-size is the maximum amount of data to store in the buffer
119 	 */
120 	if (kSuccess != SecureDTLookupEntry(0, "pram", &entry)) {
121 		return;
122 	}
123 
124 	if (kSuccess != SecureDTGetProperty(entry, "reg", (void const **)&reg_prop, &size)) {
125 		return;
126 	}
127 
128 	if (kSuccess != SecureDTLookupEntry(0, "/chosen", &chosen)) {
129 		return;
130 	}
131 
132 	if (kSuccess != SecureDTGetProperty(chosen, "embedded-panic-log-size", (void const **) &panic_region_length, &size)) {
133 		return;
134 	}
135 
136 	gPanicBase = ml_io_map_wcomb(reg_prop[0], panic_region_length[0]);
137 
138 	/* Deduct the size of the panic header from the panic region size */
139 	panic_text_len = panic_region_length[0] - sizeof(struct embedded_panic_header);
140 	gPanicSize = panic_region_length[0];
141 
142 #if DEVELOPMENT && defined(XNU_TARGET_OS_BRIDGE)
143 	if (PE_consistent_debug_enabled()) {
144 		uint64_t macos_panic_physbase = 0;
145 		uint64_t macos_panic_physlen = 0;
146 		/* Populate the macOS panic region data if it's present in consistent debug */
147 		if (PE_consistent_debug_lookup_entry(kDbgIdMacOSPanicRegion, &macos_panic_physbase, &macos_panic_physlen)) {
148 			macos_panic_base = ml_io_map_with_prot(macos_panic_physbase, macos_panic_physlen, VM_PROT_READ);
149 			mac_panic_header = (struct macos_panic_header *) ((void *) macos_panic_base);
150 			macos_panic_size = macos_panic_physlen;
151 		}
152 	}
153 #endif /* DEVELOPMENT && defined(XNU_TARGET_OS_BRIDGE) */
154 
155 #endif
156 	panic_info = (struct embedded_panic_header *)gPanicBase;
157 
158 	/* Check if a shared memory console is running in the panic buffer */
159 	if (panic_info->eph_magic == 'SHMC') {
160 		panic_console_available = TRUE;
161 		return;
162 	}
163 
164 	/* Check if there's a boot profile in the panic buffer */
165 	if (panic_info->eph_magic == 'BTRC') {
166 		return;
167 	}
168 
169 	/*
170 	 * Check to see if a panic (FUNK) is in VRAM from the last time
171 	 */
172 	if (panic_info->eph_magic == EMBEDDED_PANIC_MAGIC) {
173 		printf("iBoot didn't extract panic log from previous session crash, this is bad\n");
174 	}
175 
176 	/* Clear panic region */
177 	bzero((void *)gPanicBase, gPanicSize);
178 }
179 
180 int
PE_initialize_console(PE_Video * info,int op)181 PE_initialize_console(PE_Video * info, int op)
182 {
183 	static int last_console = -1;
184 
185 	if (info && (info != &PE_state.video)) {
186 		info->v_scale = PE_state.video.v_scale;
187 	}
188 
189 	switch (op) {
190 	case kPEDisableScreen:
191 		initialize_screen(info, op);
192 		last_console = switch_to_serial_console();
193 		kprintf("kPEDisableScreen %d\n", last_console);
194 		break;
195 
196 	case kPEEnableScreen:
197 		initialize_screen(info, op);
198 		if (info) {
199 			PE_state.video = *info;
200 		}
201 		kprintf("kPEEnableScreen %d\n", last_console);
202 		if (last_console != -1) {
203 			switch_to_old_console(last_console);
204 		}
205 		break;
206 
207 	case kPEReleaseScreen:
208 		/*
209 		 * we don't show the progress indicator on boot, but want to
210 		 * show it afterwards.
211 		 */
212 		if (!vc_progress_initialized) {
213 			default_progress.dx = 0;
214 			default_progress.dy = 0;
215 			vc_progress_initialize(&default_progress,
216 			    default_progress_data1x,
217 			    default_progress_data2x,
218 			    default_progress_data3x,
219 			    (unsigned char *) appleClut8);
220 			vc_progress_initialized = TRUE;
221 		}
222 		initialize_screen(info, op);
223 		break;
224 
225 	default:
226 		initialize_screen(info, op);
227 		break;
228 	}
229 
230 	return 0;
231 }
232 
233 void
PE_init_iokit(void)234 PE_init_iokit(void)
235 {
236 	DTEntry         entry;
237 	unsigned int    size, scale;
238 	unsigned long   display_size;
239 	void const * const *map;
240 	unsigned int    show_progress;
241 	int             *delta, image_size, flip;
242 	uint32_t        start_time_value = 0;
243 	uint32_t        debug_wait_start_value = 0;
244 	uint32_t        load_kernel_start_value = 0;
245 	uint32_t        populate_registry_time_value = 0;
246 
247 	PE_init_printf(TRUE);
248 
249 	printf("iBoot version: %s\n", iBoot_version);
250 #if defined(TARGET_OS_OSX) && defined(__arm64__)
251 	printf("iBoot Stage 2 version: %s\n", iBoot_Stage_2_version);
252 #endif /* defined(TARGET_OS_OSX) && defined(__arm64__) */
253 
254 	if (kSuccess == SecureDTLookupEntry(0, "/chosen/memory-map", &entry)) {
255 		boot_progress_element const *bootPict;
256 
257 		if (kSuccess == SecureDTGetProperty(entry, "BootCLUT", (void const **) &map, &size)) {
258 			bcopy(map[0], appleClut8, sizeof(appleClut8));
259 		}
260 
261 		if (kSuccess == SecureDTGetProperty(entry, "Pict-FailedBoot", (void const **) &map, &size)) {
262 			bootPict = (boot_progress_element const *) map[0];
263 			default_noroot.width = bootPict->width;
264 			default_noroot.height = bootPict->height;
265 			default_noroot.dx = 0;
266 			default_noroot.dy = bootPict->yOffset;
267 			default_noroot_data = &bootPict->data[0];
268 		}
269 	}
270 
271 	pe_prepare_images();
272 
273 	scale = PE_state.video.v_scale;
274 	flip = 1;
275 
276 #if defined(XNU_TARGET_OS_OSX)
277 	int notused;
278 	show_progress = TRUE;
279 	if (PE_parse_boot_argn("-restore", &notused, sizeof(notused))) {
280 		show_progress = FALSE;
281 	}
282 	if (PE_parse_boot_argn("-noprogress", &notused, sizeof(notused))) {
283 		show_progress = FALSE;
284 	}
285 #else
286 	show_progress = FALSE;
287 	PE_parse_boot_argn("-progress", &show_progress, sizeof(show_progress));
288 #endif /* XNU_TARGET_OS_OSX */
289 	if (show_progress) {
290 		/* Rotation: 0:normal, 1:right 90, 2:left 180, 3:left 90 */
291 		switch (PE_state.video.v_rotate) {
292 		case 2:
293 			flip = -1;
294 			OS_FALLTHROUGH;
295 		case 0:
296 			display_size = PE_state.video.v_height;
297 			image_size = default_progress.height;
298 			delta = &default_progress.dy;
299 			break;
300 		case 1:
301 			flip = -1;
302 			OS_FALLTHROUGH;
303 		case 3:
304 		default:
305 			display_size = PE_state.video.v_width;
306 			image_size = default_progress.width;
307 			delta = &default_progress.dx;
308 		}
309 		assert(*delta >= 0);
310 		while (((unsigned)(*delta + image_size)) >= (display_size / 2)) {
311 			*delta -= 50 * scale;
312 			assert(*delta >= 0);
313 		}
314 		*delta *= flip;
315 
316 		/* Check for DT-defined progress y delta */
317 		PE_get_default("progress-dy", &default_progress.dy, sizeof(default_progress.dy));
318 
319 		vc_progress_initialize(&default_progress,
320 		    default_progress_data1x,
321 		    default_progress_data2x,
322 		    default_progress_data3x,
323 		    (unsigned char *) appleClut8);
324 		vc_progress_initialized = TRUE;
325 	}
326 
327 	if (kdebug_enable && kdebug_debugid_enabled(IOKDBG_CODE(DBG_BOOTER, 0))) {
328 		/* Trace iBoot-provided timing information. */
329 		if (kSuccess == SecureDTLookupEntry(0, "/chosen/iBoot", &entry)) {
330 			uint32_t const * value_ptr;
331 
332 			if (kSuccess == SecureDTGetProperty(entry, "start-time", (void const **)&value_ptr, &size)) {
333 				if (size == sizeof(start_time_value)) {
334 					start_time_value = *value_ptr;
335 				}
336 			}
337 
338 			if (kSuccess == SecureDTGetProperty(entry, "debug-wait-start", (void const **)&value_ptr, &size)) {
339 				if (size == sizeof(debug_wait_start_value)) {
340 					debug_wait_start_value = *value_ptr;
341 				}
342 			}
343 
344 			if (kSuccess == SecureDTGetProperty(entry, "load-kernel-start", (void const **)&value_ptr, &size)) {
345 				if (size == sizeof(load_kernel_start_value)) {
346 					load_kernel_start_value = *value_ptr;
347 				}
348 			}
349 
350 			if (kSuccess == SecureDTGetProperty(entry, "populate-registry-time", (void const **)&value_ptr, &size)) {
351 				if (size == sizeof(populate_registry_time_value)) {
352 					populate_registry_time_value = *value_ptr;
353 				}
354 			}
355 		}
356 
357 		KDBG_RELEASE(IOKDBG_CODE(DBG_BOOTER, 0), start_time_value, debug_wait_start_value, load_kernel_start_value, populate_registry_time_value);
358 	}
359 
360 	InitIOKit(PE_state.deviceTreeHead);
361 	ConfigureIOKit();
362 }
363 
364 void
PE_lockdown_iokit(void)365 PE_lockdown_iokit(void)
366 {
367 	/*
368 	 * On arm/arm64 platforms, and especially those that employ KTRR/CTRR,
369 	 * machine_lockdown() is treated as a hard security checkpoint, such that
370 	 * code which executes prior to lockdown must be minimized and limited only to
371 	 * trusted parts of the kernel and specially-entitled kexts.  We therefore
372 	 * cannot start the general-purpose IOKit matching process until after lockdown,
373 	 * as it may involve execution of untrusted/non-entitled kext code.
374 	 * Furthermore, such kext code may process attacker controlled data (e.g.
375 	 * network packets), which dramatically increases the potential attack surface
376 	 * against a kernel which has not yet enabled the full set of available
377 	 * hardware protections.
378 	 */
379 	StartIOKitMatching();
380 }
381 
382 void
PE_slide_devicetree(vm_offset_t slide)383 PE_slide_devicetree(vm_offset_t slide)
384 {
385 	assert(PE_state.initialized);
386 	PE_state.deviceTreeHead = (void *)((uintptr_t)PE_state.deviceTreeHead + slide);
387 	SecureDTInit(PE_state.deviceTreeHead, PE_state.deviceTreeSize);
388 }
389 
390 void
PE_init_platform(boolean_t vm_initialized,void * args)391 PE_init_platform(boolean_t vm_initialized, void *args)
392 {
393 	DTEntry         entry;
394 	unsigned int    size;
395 	void * const    *prop;
396 	boot_args      *boot_args_ptr = (boot_args *) args;
397 
398 	if (PE_state.initialized == FALSE) {
399 		PE_state.initialized = TRUE;
400 		PE_state.bootArgs = boot_args_ptr;
401 		PE_state.deviceTreeHead = boot_args_ptr->deviceTreeP;
402 		PE_state.deviceTreeSize = boot_args_ptr->deviceTreeLength;
403 		PE_state.video.v_baseAddr = boot_args_ptr->Video.v_baseAddr;
404 		PE_state.video.v_rowBytes = boot_args_ptr->Video.v_rowBytes;
405 		PE_state.video.v_width = boot_args_ptr->Video.v_width;
406 		PE_state.video.v_height = boot_args_ptr->Video.v_height;
407 		PE_state.video.v_depth = (boot_args_ptr->Video.v_depth >> kBootVideoDepthDepthShift) & kBootVideoDepthMask;
408 		PE_state.video.v_rotate = (
409 			((boot_args_ptr->Video.v_depth >> kBootVideoDepthRotateShift) & kBootVideoDepthMask) +    // rotation
410 			((boot_args_ptr->Video.v_depth >> kBootVideoDepthBootRotateShift)  & kBootVideoDepthMask) // add extra boot rotation
411 			) % 4;
412 		PE_state.video.v_scale = ((boot_args_ptr->Video.v_depth >> kBootVideoDepthScaleShift) & kBootVideoDepthMask) + 1;
413 		PE_state.video.v_display = boot_args_ptr->Video.v_display;
414 		strlcpy(PE_state.video.v_pixelFormat, "BBBBBBBBGGGGGGGGRRRRRRRR", sizeof(PE_state.video.v_pixelFormat));
415 	}
416 	if (!vm_initialized) {
417 		/*
418 		 * Setup the Device Tree routines
419 		 * so the console can be found and the right I/O space
420 		 * can be used..
421 		 */
422 		SecureDTInit(PE_state.deviceTreeHead, PE_state.deviceTreeSize);
423 		pe_identify_machine(boot_args_ptr);
424 	} else {
425 		pe_arm_init_interrupts(args);
426 		pe_arm_init_debug(args);
427 	}
428 
429 	if (!vm_initialized) {
430 		if (kSuccess == (SecureDTFindEntry("name", "device-tree", &entry))) {
431 			if (kSuccess == SecureDTGetProperty(entry, "target-type",
432 			    (void const **)&prop, &size)) {
433 				if (size > sizeof(gTargetTypeBuffer)) {
434 					size = sizeof(gTargetTypeBuffer);
435 				}
436 				bcopy(prop, gTargetTypeBuffer, size);
437 				gTargetTypeBuffer[size - 1] = '\0';
438 			}
439 		}
440 		if (kSuccess == (SecureDTFindEntry("name", "device-tree", &entry))) {
441 			if (kSuccess == SecureDTGetProperty(entry, "model",
442 			    (void const **)&prop, &size)) {
443 				if (size > sizeof(gModelTypeBuffer)) {
444 					size = sizeof(gModelTypeBuffer);
445 				}
446 				bcopy(prop, gModelTypeBuffer, size);
447 				gModelTypeBuffer[size - 1] = '\0';
448 			}
449 		}
450 		if (kSuccess == SecureDTLookupEntry(NULL, "/chosen", &entry)) {
451 			if (kSuccess == SecureDTGetProperty(entry, "debug-enabled",
452 			    (void const **) &prop, &size)) {
453 				/*
454 				 * We purposefully modify a constified variable as
455 				 * it will get locked down by a trusted monitor or
456 				 * via page table mappings. We don't want people easily
457 				 * modifying this variable...
458 				 */
459 #pragma clang diagnostic push
460 #pragma clang diagnostic ignored "-Wcast-qual"
461 				boolean_t *modify_debug_enabled = (boolean_t *) &debug_enabled;
462 				if (size > sizeof(uint32_t)) {
463 					size = sizeof(uint32_t);
464 				}
465 				bcopy(prop, modify_debug_enabled, size);
466 #pragma clang diagnostic pop
467 			}
468 			if (kSuccess == SecureDTGetProperty(entry, "firmware-version", (void const **) &prop, &size)) {
469 				if (size > sizeof(iBoot_version)) {
470 					size = sizeof(iBoot_version);
471 				}
472 				bcopy(prop, iBoot_version, size);
473 				iBoot_version[size - 1] = '\0';
474 			}
475 #if defined(TARGET_OS_OSX) && defined(__arm64__)
476 			if (kSuccess == SecureDTGetProperty(entry, "system-firmware-version", (void const **) &prop, &size)) {
477 				if (size > sizeof(iBoot_Stage_2_version)) {
478 					size = sizeof(iBoot_Stage_2_version);
479 				}
480 				bcopy(prop, iBoot_Stage_2_version, size);
481 				iBoot_Stage_2_version[size - 1] = '\0';
482 			}
483 #endif /* defined(TARGET_OS_OSX) && defined(__arm64__) */
484 			if (kSuccess == SecureDTGetProperty(entry, "unique-chip-id",
485 			    (void const **) &prop, &size)) {
486 				if (size > sizeof(gPlatformECID)) {
487 					size = sizeof(gPlatformECID);
488 				}
489 				bcopy(prop, gPlatformECID, size);
490 			}
491 			if (kSuccess == SecureDTGetProperty(entry, "dram-vendor-id",
492 			    (void const **) &prop, &size)) {
493 				if (size > sizeof(gPlatformMemoryID)) {
494 					size = sizeof(gPlatformMemoryID);
495 				}
496 				bcopy(prop, &gPlatformMemoryID, size);
497 			}
498 		}
499 		pe_init_debug();
500 	}
501 }
502 
503 void
PE_create_console(void)504 PE_create_console(void)
505 {
506 	/*
507 	 * Check the head of VRAM for a panic log saved on last panic.
508 	 * Do this before the VRAM is trashed.
509 	 */
510 	check_for_panic_log();
511 
512 	if (PE_state.video.v_display) {
513 		PE_initialize_console(&PE_state.video, kPEGraphicsMode);
514 	} else {
515 		PE_initialize_console(&PE_state.video, kPETextMode);
516 	}
517 }
518 
519 int
PE_current_console(PE_Video * info)520 PE_current_console(PE_Video * info)
521 {
522 	*info = PE_state.video;
523 	return 0;
524 }
525 
526 void
PE_display_icon(__unused unsigned int flags,__unused const char * name)527 PE_display_icon(__unused unsigned int flags, __unused const char *name)
528 {
529 	if (default_noroot_data) {
530 		vc_display_icon(&default_noroot, default_noroot_data);
531 	}
532 }
533 
534 extern          boolean_t
PE_get_hotkey(__unused unsigned char key)535 PE_get_hotkey(__unused unsigned char key)
536 {
537 	return FALSE;
538 }
539 
540 static timebase_callback_func gTimebaseCallback;
541 
542 void
PE_register_timebase_callback(timebase_callback_func callback)543 PE_register_timebase_callback(timebase_callback_func callback)
544 {
545 	gTimebaseCallback = callback;
546 
547 	PE_call_timebase_callback();
548 }
549 
550 void
PE_call_timebase_callback(void)551 PE_call_timebase_callback(void)
552 {
553 	struct timebase_freq_t timebase_freq;
554 
555 	timebase_freq.timebase_num = gPEClockFrequencyInfo.timebase_frequency_hz;
556 	timebase_freq.timebase_den = 1;
557 
558 	if (gTimebaseCallback) {
559 		gTimebaseCallback(&timebase_freq);
560 	}
561 }
562 
563 /*
564  * The default PE_poll_input handler.
565  */
566 int
PE_stub_poll_input(__unused unsigned int options,char * c)567 PE_stub_poll_input(__unused unsigned int options, char *c)
568 {
569 	*c = (char)uart_getc();
570 	return 0; /* 0 for success, 1 for unsupported */
571 }
572 
573 /*
574  * This routine will return 1 if you are running on a device with a variant
575  * of iBoot that allows debugging. This is typically not the case on production
576  * fused parts (even when running development variants of iBoot).
577  *
578  * The routine takes an optional argument of the flags passed to debug="" so
579  * kexts don't have to parse the boot arg themselves.
580  */
581 uint32_t
PE_i_can_has_debugger(uint32_t * debug_flags)582 PE_i_can_has_debugger(uint32_t *debug_flags)
583 {
584 	if (debug_flags) {
585 #if DEVELOPMENT || DEBUG
586 		assert(startup_phase >= STARTUP_SUB_TUNABLES);
587 #endif
588 		if (debug_enabled) {
589 			*debug_flags = debug_boot_arg;
590 		} else {
591 			*debug_flags = 0;
592 		}
593 	}
594 	return debug_enabled;
595 }
596 
597 /*
598  * This routine returns TRUE if the device is configured
599  * with panic debugging enabled.
600  */
601 boolean_t
PE_panic_debugging_enabled()602 PE_panic_debugging_enabled()
603 {
604 	return panicDebugging;
605 }
606 
607 void
PE_update_panic_crc(unsigned char * buf,unsigned int * size)608 PE_update_panic_crc(unsigned char *buf, unsigned int *size)
609 {
610 	if (!panic_info || !size) {
611 		return;
612 	}
613 
614 	if (!buf) {
615 		*size = panic_text_len;
616 		return;
617 	}
618 
619 	if (*size == 0) {
620 		return;
621 	}
622 
623 	*size = *size > panic_text_len ? panic_text_len : *size;
624 	if (panic_info->eph_magic != EMBEDDED_PANIC_MAGIC) {
625 		// rdar://88696402 (PanicTest: test case for MAGIC check in PE_update_panic_crc)
626 		printf("Error!! Current Magic 0x%X, expected value 0x%x", panic_info->eph_magic, EMBEDDED_PANIC_MAGIC);
627 	}
628 
629 	/* CRC everything after the CRC itself - starting with the panic header version */
630 	panic_info->eph_crc = crc32(0L, &panic_info->eph_version, (panic_text_len +
631 	    sizeof(struct embedded_panic_header) - offsetof(struct embedded_panic_header, eph_version)));
632 }
633 
634 uint32_t
PE_get_offset_into_panic_region(char * location)635 PE_get_offset_into_panic_region(char *location)
636 {
637 	assert(gPanicBase != 0);
638 	assert(location >= (char *) gPanicBase);
639 	assert((unsigned int)(location - gPanicBase) < gPanicSize);
640 
641 	return (uint32_t)(uintptr_t)(location - gPanicBase);
642 }
643 
644 void
PE_init_panicheader()645 PE_init_panicheader()
646 {
647 	if (!panic_info) {
648 		return;
649 	}
650 
651 	bzero(panic_info, sizeof(struct embedded_panic_header));
652 
653 	/*
654 	 * The panic log begins immediately after the panic header -- debugger synchronization and other functions
655 	 * may log into this region before we've become the exclusive panicking CPU and initialize the header here.
656 	 */
657 	panic_info->eph_panic_log_offset = debug_buf_base ? PE_get_offset_into_panic_region(debug_buf_base) : 0;
658 
659 	panic_info->eph_magic = EMBEDDED_PANIC_MAGIC;
660 	panic_info->eph_version = EMBEDDED_PANIC_HEADER_CURRENT_VERSION;
661 
662 	return;
663 }
664 
665 /*
666  * Tries to update the panic header to keep it consistent on nested panics.
667  *
668  * NOTE: The purpose of this function is NOT to detect/correct corruption in the panic region,
669  *       it is to update the panic header to make it consistent when we nest panics.
670  */
671 void
PE_update_panicheader_nestedpanic()672 PE_update_panicheader_nestedpanic()
673 {
674 	/*
675 	 * if the panic header pointer is bogus (e.g. someone stomped on it) then bail.
676 	 */
677 	if (!panic_info) {
678 		/* if this happens in development then blow up bigly */
679 		assert(panic_info);
680 		return;
681 	}
682 
683 	/*
684 	 * If the panic log offset is not set, re-init the panic header
685 	 *
686 	 * note that this should not be possible unless someone stomped on the panic header to zero it out, since by the time
687 	 * we reach this location *someone* should have appended something to the log..
688 	 */
689 	if (panic_info->eph_panic_log_offset == 0) {
690 		PE_init_panicheader();
691 		panic_info->eph_panic_flags |= EMBEDDED_PANIC_HEADER_FLAG_NESTED_PANIC;
692 		return;
693 	}
694 
695 	panic_info->eph_panic_flags |= EMBEDDED_PANIC_HEADER_FLAG_NESTED_PANIC;
696 
697 	/*
698 	 * If the panic log length is not set, set the end to
699 	 * the current location of the debug_buf_ptr to close it.
700 	 */
701 	if (panic_info->eph_panic_log_len == 0) {
702 		panic_info->eph_panic_log_len = PE_get_offset_into_panic_region(debug_buf_ptr);
703 
704 		/* indicative of corruption in the panic region, consumer beware */
705 		if ((panic_info->eph_other_log_offset == 0) &&
706 		    (panic_info->eph_other_log_len == 0)) {
707 			panic_info->eph_panic_flags |= EMBEDDED_PANIC_HEADER_FLAG_INCOHERENT_PANICLOG;
708 		}
709 	}
710 
711 	/* likely indicative of corruption in the panic region, consumer beware */
712 	if (((panic_info->eph_stackshot_offset == 0) && (panic_info->eph_stackshot_len == 0)) || ((panic_info->eph_stackshot_offset != 0) && (panic_info->eph_stackshot_len != 0))) {
713 		panic_info->eph_panic_flags |= EMBEDDED_PANIC_HEADER_FLAG_INCOHERENT_PANICLOG;
714 	}
715 
716 	/*
717 	 * If we haven't set up the other log yet, set the beginning of the other log
718 	 * to the current location of the debug_buf_ptr
719 	 */
720 	if (panic_info->eph_other_log_offset == 0) {
721 		panic_info->eph_other_log_offset = PE_get_offset_into_panic_region(debug_buf_ptr);
722 
723 		/* indicative of corruption in the panic region, consumer beware */
724 		if (panic_info->eph_other_log_len == 0) {
725 			panic_info->eph_panic_flags |= EMBEDDED_PANIC_HEADER_FLAG_INCOHERENT_PANICLOG;
726 		}
727 	}
728 
729 	return;
730 }
731 
732 boolean_t
PE_reboot_on_panic(void)733 PE_reboot_on_panic(void)
734 {
735 	uint32_t debug_flags;
736 
737 	if (PE_i_can_has_debugger(&debug_flags)
738 	    && (debug_flags & DB_NMI)) {
739 		/* kernel debugging is active */
740 		return FALSE;
741 	} else {
742 		return TRUE;
743 	}
744 }
745 
746 void
PE_sync_panic_buffers(void)747 PE_sync_panic_buffers(void)
748 {
749 	/*
750 	 * rdar://problem/26453070:
751 	 * The iBoot panic region is write-combined on arm64.  We must flush dirty lines
752 	 * from L1/L2 as late as possible before reset, with no further reads of the panic
753 	 * region between the flush and the reset.  Some targets have an additional memcache (L3),
754 	 * and a read may bring dirty lines out of L3 and back into L1/L2, causing the lines to
755 	 * be discarded on reset.  If we can make sure the lines are flushed to L3/DRAM,
756 	 * the platform reset handler will flush any L3.
757 	 */
758 	if (gPanicBase) {
759 		CleanPoC_DcacheRegion_Force(gPanicBase, gPanicSize);
760 	}
761 }
762 
763 static void
pe_prepare_images(void)764 pe_prepare_images(void)
765 {
766 	if ((1 & PE_state.video.v_rotate) != 0) {
767 		// Only square square images with radial symmetry are supported
768 		// No need to actually rotate the data
769 
770 		// Swap the dx and dy offsets
771 		uint32_t tmp = default_progress.dx;
772 		default_progress.dx = default_progress.dy;
773 		default_progress.dy = tmp;
774 	}
775 #if 0
776 	uint32_t cnt, cnt2, cnt3, cnt4;
777 	uint32_t tmp, width, height;
778 	uint8_t  data, *new_data;
779 	const uint8_t *old_data;
780 
781 	width  = default_progress.width;
782 	height = default_progress.height * default_progress.count;
783 
784 	// Scale images if the UI is being scaled
785 	if (PE_state.video.v_scale > 1) {
786 		new_data = kalloc(width * height * scale * scale);
787 		if (new_data != 0) {
788 			old_data = default_progress_data;
789 			default_progress_data = new_data;
790 			for (cnt = 0; cnt < height; cnt++) {
791 				for (cnt2 = 0; cnt2 < width; cnt2++) {
792 					data = *(old_data++);
793 					for (cnt3 = 0; cnt3 < scale; cnt3++) {
794 						for (cnt4 = 0; cnt4 < scale; cnt4++) {
795 							new_data[width * scale * cnt3 + cnt4] = data;
796 						}
797 					}
798 					new_data += scale;
799 				}
800 				new_data += width * scale * (scale - 1);
801 			}
802 			default_progress.width  *= scale;
803 			default_progress.height *= scale;
804 			default_progress.dx     *= scale;
805 			default_progress.dy     *= scale;
806 		}
807 	}
808 #endif
809 }
810 
811 void
PE_mark_hwaccess(uint64_t thread)812 PE_mark_hwaccess(uint64_t thread)
813 {
814 	last_hwaccess_thread = thread;
815 	__builtin_arm_dmb(DMB_ISH);
816 }
817 
818 __startup_func
819 vm_size_t
PE_init_socd_client(void)820 PE_init_socd_client(void)
821 {
822 	DTEntry entry;
823 	uintptr_t const *reg_prop;
824 	unsigned int size;
825 
826 	if (kSuccess != SecureDTLookupEntry(0, "socd-trace-ram", &entry)) {
827 		return 0;
828 	}
829 
830 	if (kSuccess != SecureDTGetProperty(entry, "reg", (void const **)&reg_prop, &size)) {
831 		return 0;
832 	}
833 
834 	socd_trace_ram_base = ml_io_map(reg_prop[0], (vm_size_t)reg_prop[1]);
835 	socd_trace_ram_size = (vm_size_t)reg_prop[1];
836 
837 	return socd_trace_ram_size;
838 }
839 
840 /*
841  * PE_write_socd_client_buffer solves two problems:
842  * 1. Prevents accidentally trusting a value read from socd client buffer. socd client buffer is considered untrusted.
843  * 2. Ensures only 4 byte store instructions are used. On some platforms, socd client buffer is backed up
844  *    by a SRAM that must be written to only 4 bytes at a time.
845  */
846 void
PE_write_socd_client_buffer(vm_offset_t offset,const void * buff,vm_size_t size)847 PE_write_socd_client_buffer(vm_offset_t offset, const void *buff, vm_size_t size)
848 {
849 	volatile uint32_t *dst = (volatile uint32_t *)(socd_trace_ram_base + offset);
850 	vm_size_t len = size / sizeof(dst[0]);
851 
852 	assert(offset + size <= socd_trace_ram_size);
853 
854 	/* Perform 4 byte aligned accesses */
855 	if ((offset % 4 != 0) || (size % 4 != 0)) {
856 		panic("unaligned acccess to socd trace ram");
857 	}
858 
859 	for (vm_size_t i = 0; i < len; i++) {
860 		dst[i] = ((const uint32_t *)buff)[i];
861 	}
862 }
863