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