]> git.saurik.com Git - apple/xnu.git/blame - pexpert/arm/pe_init.c
xnu-4903.221.2.tar.gz
[apple/xnu.git] / pexpert / arm / pe_init.c
CommitLineData
5ba3f43e
A
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 <machine/machine_routines.h>
16#include <arm/caches_internal.h>
17#include <kern/debug.h>
18#include <libkern/section_keywords.h>
19
d9a64523
A
20#if defined __arm__
21#include <pexpert/arm/board_config.h>
22#elif defined __arm64__
23#include <pexpert/arm64/board_config.h>
24#endif
25
26
5ba3f43e
A
27/* extern references */
28extern void pe_identify_machine(boot_args *bootArgs);
29
30/* static references */
31static void pe_prepare_images(void);
32
33/* private globals */
d9a64523 34SECURITY_READ_ONLY_LATE(PE_state_t) PE_state;
5ba3f43e
A
35#define FW_VERS_LEN 128
36char firmware_version[FW_VERS_LEN];
37
38/*
39 * This variable is only modified once, when the BSP starts executing. We put it in __TEXT
40 * as page protections on kernel text early in startup are read-write. The kernel is
41 * locked down later in start-up, said mappings become RO and thus this
42 * variable becomes immutable.
43 *
44 * See osfmk/arm/arm_vm_init.c for more information.
45 */
46SECURITY_READ_ONLY_SPECIAL_SECTION(volatile uint32_t, "__TEXT,__const") debug_enabled = FALSE;
47
48uint8_t gPlatformECID[8];
49uint32_t gPlatformMemoryID;
50static boolean_t vc_progress_initialized = FALSE;
51uint64_t last_hwaccess_thread = 0;
52char gTargetTypeBuffer[8];
53char gModelTypeBuffer[32];
54
55/* Clock Frequency Info */
56clock_frequency_info_t gPEClockFrequencyInfo;
57
58vm_offset_t gPanicBase;
59unsigned int gPanicSize;
60struct embedded_panic_header *panic_info = NULL;
61
62/* Maximum size of panic log excluding headers, in bytes */
63static unsigned int panic_text_len;
64
65/* Whether a console is standing by for panic logging */
66static boolean_t panic_console_available = FALSE;
67
68extern uint32_t crc32(uint32_t crc, const void *buf, size_t size);
69
d9a64523
A
70void PE_slide_devicetree(vm_offset_t);
71
5ba3f43e
A
72static void
73check_for_panic_log(void)
74{
d9a64523
A
75#ifdef PLATFORM_PANIC_LOG_PADDR
76 gPanicBase = ml_io_map_wcomb(PLATFORM_PANIC_LOG_PADDR, PLATFORM_PANIC_LOG_SIZE);
77 panic_text_len = PLATFORM_PANIC_LOG_SIZE - sizeof(struct embedded_panic_header);
78 gPanicSize = PLATFORM_PANIC_LOG_SIZE;
79#else
5ba3f43e
A
80 DTEntry entry, chosen;
81 unsigned int size;
82 uintptr_t *reg_prop;
83 uint32_t *panic_region_length;
84
85 /*
86 * Find the vram node in the device tree
87 */
88 if (kSuccess != DTLookupEntry(0, "pram", &entry))
89 return;
90
91 if (kSuccess != DTGetProperty(entry, "reg", (void **)&reg_prop, &size))
92 return;
93
94 if (kSuccess != DTLookupEntry(0, "/chosen", &chosen))
95 return;
96
97 if (kSuccess != DTGetProperty(chosen, "embedded-panic-log-size", (void **) &panic_region_length, &size))
98 return;
99
100 /*
101 * Map the first page of VRAM into the kernel for use in case of
102 * panic
103 */
104 /* Note: map as normal memory. */
105 gPanicBase = ml_io_map_wcomb(reg_prop[0], panic_region_length[0]);
106
107 /* Deduct the size of the panic header from the panic region size */
108 panic_text_len = panic_region_length[0] - sizeof(struct embedded_panic_header);
109 gPanicSize = panic_region_length[0];
d9a64523 110#endif
5ba3f43e
A
111 panic_info = (struct embedded_panic_header *)gPanicBase;
112
113 /* Check if a shared memory console is running in the panic buffer */
114 if (panic_info->eph_magic == 'SHMC') {
115 panic_console_available = TRUE;
116 return;
117 }
118
119 /* Check if there's a boot profile in the panic buffer */
120 if (panic_info->eph_magic == 'BTRC') {
121 return;
122 }
123
124 /*
125 * Check to see if a panic (FUNK) is in VRAM from the last time
126 */
127 if (panic_info->eph_magic == EMBEDDED_PANIC_MAGIC) {
128 printf("iBoot didn't extract panic log from previous session crash, this is bad\n");
129 }
130
131 /* Clear panic region */
132 bzero((void *)gPanicBase, gPanicSize);
133}
134
135int
136PE_initialize_console(PE_Video * info, int op)
137{
138 static int last_console = -1;
139
140 if (info && (info != &PE_state.video)) info->v_scale = PE_state.video.v_scale;
141
142 switch (op) {
143
144 case kPEDisableScreen:
145 initialize_screen(info, op);
146 last_console = switch_to_serial_console();
147 kprintf("kPEDisableScreen %d\n", last_console);
148 break;
149
150 case kPEEnableScreen:
151 initialize_screen(info, op);
152 if (info)
153 PE_state.video = *info;
154 kprintf("kPEEnableScreen %d\n", last_console);
155 if (last_console != -1)
156 switch_to_old_console(last_console);
157 break;
158
159 case kPEReleaseScreen:
160 /*
161 * we don't show the progress indicator on boot, but want to
162 * show it afterwards.
163 */
164 if (!vc_progress_initialized) {
165 default_progress.dx = 0;
166 default_progress.dy = 0;
167 vc_progress_initialize(&default_progress,
168 default_progress_data1x,
169 default_progress_data2x,
170 default_progress_data3x,
171 (unsigned char *) appleClut8);
172 vc_progress_initialized = TRUE;
173 }
174 initialize_screen(info, op);
175 break;
176
177 default:
178 initialize_screen(info, op);
179 break;
180 }
181
182 return 0;
183}
184
185void
186PE_init_iokit(void)
187{
188 DTEntry entry;
189 unsigned int size, scale;
190 unsigned long display_size;
191 void **map;
192 unsigned int show_progress;
193 int *delta, image_size, flip;
194 uint32_t start_time_value = 0;
195 uint32_t debug_wait_start_value = 0;
196 uint32_t load_kernel_start_value = 0;
197 uint32_t populate_registry_time_value = 0;
198
199 PE_init_kprintf(TRUE);
200 PE_init_printf(TRUE);
201
202 printf("iBoot version: %s\n", firmware_version);
203
204 if (kSuccess == DTLookupEntry(0, "/chosen/memory-map", &entry)) {
205
206 boot_progress_element *bootPict;
207
208 if (kSuccess == DTGetProperty(entry, "BootCLUT", (void **) &map, &size))
209 bcopy(map[0], appleClut8, sizeof(appleClut8));
210
211 if (kSuccess == DTGetProperty(entry, "Pict-FailedBoot", (void **) &map, &size)) {
212
213 bootPict = (boot_progress_element *) map[0];
214 default_noroot.width = bootPict->width;
215 default_noroot.height = bootPict->height;
216 default_noroot.dx = 0;
217 default_noroot.dy = bootPict->yOffset;
218 default_noroot_data = &bootPict->data[0];
219 }
220 }
221
222 pe_prepare_images();
223
224 scale = PE_state.video.v_scale;
225 flip = 1;
226
227 if (PE_parse_boot_argn("-progress", &show_progress, sizeof (show_progress)) && show_progress) {
228 /* Rotation: 0:normal, 1:right 90, 2:left 180, 3:left 90 */
229 switch (PE_state.video.v_rotate) {
230 case 2:
231 flip = -1;
232 /* fall through */
233 case 0:
234 display_size = PE_state.video.v_height;
235 image_size = default_progress.height;
236 delta = &default_progress.dy;
237 break;
238 case 1:
239 flip = -1;
240 /* fall through */
241 case 3:
242 default:
243 display_size = PE_state.video.v_width;
244 image_size = default_progress.width;
245 delta = &default_progress.dx;
246 }
247 assert(*delta >= 0);
248 while (((unsigned)(*delta + image_size)) >= (display_size / 2)) {
249 *delta -= 50 * scale;
250 assert(*delta >= 0);
251 }
252 *delta *= flip;
253
254 /* Check for DT-defined progress y delta */
255 PE_get_default("progress-dy", &default_progress.dy, sizeof(default_progress.dy));
256
257 vc_progress_initialize(&default_progress,
258 default_progress_data1x,
259 default_progress_data2x,
260 default_progress_data3x,
261 (unsigned char *) appleClut8);
262 vc_progress_initialized = TRUE;
263 }
264
265 if (kdebug_enable && kdebug_debugid_enabled(IOKDBG_CODE(DBG_BOOTER, 0))) {
266 /* Trace iBoot-provided timing information. */
267 if (kSuccess == DTLookupEntry(0, "/chosen/iBoot", &entry)) {
268 uint32_t * value_ptr;
269
270 if (kSuccess == DTGetProperty(entry, "start-time", (void **)&value_ptr, &size)) {
271 if (size == sizeof(start_time_value))
272 start_time_value = *value_ptr;
273 }
274
275 if (kSuccess == DTGetProperty(entry, "debug-wait-start", (void **)&value_ptr, &size)) {
276 if (size == sizeof(debug_wait_start_value))
277 debug_wait_start_value = *value_ptr;
278 }
279
280 if (kSuccess == DTGetProperty(entry, "load-kernel-start", (void **)&value_ptr, &size)) {
281 if (size == sizeof(load_kernel_start_value))
282 load_kernel_start_value = *value_ptr;
283 }
284
285 if (kSuccess == DTGetProperty(entry, "populate-registry-time", (void **)&value_ptr, &size)) {
286 if (size == sizeof(populate_registry_time_value))
287 populate_registry_time_value = *value_ptr;
288 }
289 }
290
291 KDBG_RELEASE(IOKDBG_CODE(DBG_BOOTER, 0), start_time_value, debug_wait_start_value, load_kernel_start_value, populate_registry_time_value);
292 }
293
294 StartIOKit(PE_state.deviceTreeHead, PE_state.bootArgs, (void *) 0, (void *) 0);
295}
296
d9a64523
A
297void
298PE_slide_devicetree(vm_offset_t slide)
299{
300 assert(PE_state.initialized);
301 PE_state.deviceTreeHead += slide;
302 DTInit(PE_state.deviceTreeHead);
303}
304
5ba3f43e
A
305void
306PE_init_platform(boolean_t vm_initialized, void *args)
307{
308 DTEntry entry;
309 unsigned int size;
310 void **prop;
311 boot_args *boot_args_ptr = (boot_args *) args;
312
313 if (PE_state.initialized == FALSE) {
314 PE_state.initialized = TRUE;
315 PE_state.bootArgs = boot_args_ptr;
316 PE_state.deviceTreeHead = boot_args_ptr->deviceTreeP;
317 PE_state.video.v_baseAddr = boot_args_ptr->Video.v_baseAddr;
318 PE_state.video.v_rowBytes = boot_args_ptr->Video.v_rowBytes;
319 PE_state.video.v_width = boot_args_ptr->Video.v_width;
320 PE_state.video.v_height = boot_args_ptr->Video.v_height;
321 PE_state.video.v_depth = (boot_args_ptr->Video.v_depth >> kBootVideoDepthDepthShift) & kBootVideoDepthMask;
322 PE_state.video.v_rotate = (boot_args_ptr->Video.v_depth >> kBootVideoDepthRotateShift) & kBootVideoDepthMask;
323 PE_state.video.v_scale = ((boot_args_ptr->Video.v_depth >> kBootVideoDepthScaleShift) & kBootVideoDepthMask) + 1;
324 PE_state.video.v_display = boot_args_ptr->Video.v_display;
325 strlcpy(PE_state.video.v_pixelFormat, "BBBBBBBBGGGGGGGGRRRRRRRR", sizeof(PE_state.video.v_pixelFormat));
326 }
327 if (!vm_initialized) {
328 /*
329 * Setup the Device Tree routines
330 * so the console can be found and the right I/O space
331 * can be used..
332 */
333 DTInit(PE_state.deviceTreeHead);
334 pe_identify_machine(boot_args_ptr);
335 } else {
336 pe_arm_init_interrupts(args);
337 pe_arm_init_debug(args);
338 }
339
340 if (!vm_initialized) {
341 if (kSuccess == (DTFindEntry("name", "device-tree", &entry))) {
342 if (kSuccess == DTGetProperty(entry, "target-type",
343 (void **)&prop, &size)) {
344 if (size > sizeof(gTargetTypeBuffer))
345 size = sizeof(gTargetTypeBuffer);
346 bcopy(prop,gTargetTypeBuffer,size);
347 gTargetTypeBuffer[size-1]='\0';
348 }
349 }
350 if (kSuccess == (DTFindEntry("name", "device-tree", &entry))) {
351 if (kSuccess == DTGetProperty(entry, "model",
352 (void **)&prop, &size)) {
353 if (size > sizeof(gModelTypeBuffer))
354 size = sizeof(gModelTypeBuffer);
355 bcopy(prop,gModelTypeBuffer,size);
356 gModelTypeBuffer[size-1]='\0';
357 }
358 }
359 if (kSuccess == DTLookupEntry(NULL, "/chosen", &entry)) {
360 if (kSuccess == DTGetProperty(entry, "debug-enabled",
361 (void **) &prop, &size)) {
362 /*
363 * We purposefully modify a constified variable as
364 * it will get locked down by a trusted monitor or
365 * via page table mappings. We don't want people easily
366 * modifying this variable...
367 */
368#pragma clang diagnostic push
369#pragma clang diagnostic ignored "-Wcast-qual"
370 boolean_t *modify_debug_enabled = (boolean_t *) &debug_enabled;
371 if (size > sizeof(uint32_t))
372 size = sizeof(uint32_t);
373 bcopy(prop, modify_debug_enabled, size);
374#pragma clang diagnostic pop
375 }
376 if (kSuccess == DTGetProperty(entry, "firmware-version",
377 (void **) &prop, &size)) {
378 if (size > sizeof(firmware_version))
379 size = sizeof(firmware_version);
380 bcopy(prop, firmware_version, size);
381 firmware_version[size - 1] = '\0';
382 }
383 if (kSuccess == DTGetProperty(entry, "unique-chip-id",
384 (void **) &prop, &size)) {
385 if (size > sizeof(gPlatformECID))
386 size = sizeof(gPlatformECID);
387 bcopy(prop,gPlatformECID,size);
388 }
389 if (kSuccess == DTGetProperty(entry, "dram-vendor-id",
390 (void **) &prop, &size)) {
391 if (size > sizeof(gPlatformMemoryID))
392 size = sizeof(gPlatformMemoryID);
393 bcopy(prop,&gPlatformMemoryID,size);
394 }
395 }
396 pe_init_debug();
397 }
398}
399
400void
401PE_create_console(void)
402{
403 /*
404 * Check the head of VRAM for a panic log saved on last panic.
405 * Do this before the VRAM is trashed.
406 */
407 check_for_panic_log();
408
409 if (PE_state.video.v_display)
410 PE_initialize_console(&PE_state.video, kPEGraphicsMode);
411 else
412 PE_initialize_console(&PE_state.video, kPETextMode);
413}
414
415int
416PE_current_console(PE_Video * info)
417{
418 *info = PE_state.video;
419 return (0);
420}
421
422void
423PE_display_icon(__unused unsigned int flags, __unused const char *name)
424{
425 if (default_noroot_data)
426 vc_display_icon(&default_noroot, default_noroot_data);
427}
428
429extern boolean_t
430PE_get_hotkey(__unused unsigned char key)
431{
432 return (FALSE);
433}
434
435static timebase_callback_func gTimebaseCallback;
436
437void
438PE_register_timebase_callback(timebase_callback_func callback)
439{
440 gTimebaseCallback = callback;
441
442 PE_call_timebase_callback();
443}
444
445void
446PE_call_timebase_callback(void)
447{
448 struct timebase_freq_t timebase_freq;
449
450 timebase_freq.timebase_num = gPEClockFrequencyInfo.timebase_frequency_hz;
451 timebase_freq.timebase_den = 1;
452
453 if (gTimebaseCallback)
454 gTimebaseCallback(&timebase_freq);
455}
456
457/*
458 * The default PE_poll_input handler.
459 */
460static int
461PE_stub_poll_input(__unused unsigned int options, char *c)
462{
463 *c = uart_getc();
464 return 0; /* 0 for success, 1 for unsupported */
465}
466
467/*
468 * Called by the kernel debugger to poll for keyboard input.
469 * Keyboard drivers may replace the default stub function
470 * with their polled-mode input function.
471 */
472int (*PE_poll_input) (unsigned int options, char *c) = PE_stub_poll_input;
473
474/*
475 * This routine will return 1 if you are running on a device with a variant
476 * of iBoot that allows debugging. This is typically not the case on production
477 * fused parts (even when running development variants of iBoot).
478 *
479 * The routine takes an optional argument of the flags passed to debug="" so
480 * kexts don't have to parse the boot arg themselves.
481 */
482uint32_t
483PE_i_can_has_debugger(uint32_t *debug_flags)
484{
485 if (debug_flags) {
cc8bc92a
A
486#if DEVELOPMENT || DEBUG
487 assert(debug_boot_arg_inited);
488#endif
5ba3f43e
A
489 if (debug_enabled)
490 *debug_flags = debug_boot_arg;
491 else
492 *debug_flags = 0;
493 }
494 return (debug_enabled);
495}
496
d9a64523
A
497/*
498 * This routine returns TRUE if the device is configured
499 * with panic debugging enabled.
500 */
501boolean_t
502PE_panic_debugging_enabled()
503{
504 return panicDebugging;
505}
506
5ba3f43e
A
507void
508PE_save_buffer_to_vram(unsigned char *buf, unsigned int *size)
509{
510 if (!panic_info || !size) {
511 return;
512 }
513
514 if (!buf) {
515 *size = panic_text_len;
516 return;
517 }
518
519 if (*size == 0) {
520 return;
521 }
522
523 *size = *size > panic_text_len ? panic_text_len : *size;
524 if (panic_info->eph_magic != EMBEDDED_PANIC_MAGIC)
525 printf("Error!! Current Magic 0x%X, expected value 0x%x", panic_info->eph_magic, EMBEDDED_PANIC_MAGIC);
526
527 /* CRC everything after the CRC itself - starting with the panic header version */
528 panic_info->eph_crc = crc32(0L, &panic_info->eph_version, (panic_text_len +
529 sizeof(struct embedded_panic_header) - offsetof(struct embedded_panic_header, eph_version)));
530}
531
532uint32_t
533PE_get_offset_into_panic_region(char *location)
534{
535 assert(panic_info != NULL);
536 assert(location > (char *) panic_info);
537 assert((unsigned int)(location - (char *) panic_info) < panic_text_len);
538
539 return (uint32_t) (location - gPanicBase);
540}
541
542void
543PE_init_panicheader()
544{
545 if (!panic_info)
546 return;
547
548 bzero(panic_info, sizeof(struct embedded_panic_header));
549
550 /*
551 * The panic log begins immediately after the panic header -- debugger synchronization and other functions
552 * may log into this region before we've become the exclusive panicking CPU and initialize the header here.
553 */
554 panic_info->eph_panic_log_offset = PE_get_offset_into_panic_region(debug_buf_base);
555
556 panic_info->eph_magic = EMBEDDED_PANIC_MAGIC;
557 panic_info->eph_version = EMBEDDED_PANIC_HEADER_CURRENT_VERSION;
558
559 return;
560}
561
562/*
563 * Tries to update the panic header to keep it consistent on nested panics.
564 *
565 * NOTE: The purpose of this function is NOT to detect/correct corruption in the panic region,
566 * it is to update the panic header to make it consistent when we nest panics.
567 */
568void
569PE_update_panicheader_nestedpanic()
570{
571 if (!panic_info)
572 return;
573
574 /*
575 * If the panic log offset is not set, re-init the panic header
576 */
577 if (panic_info->eph_panic_log_offset == 0) {
578 PE_init_panicheader();
579 panic_info->eph_panic_flags |= EMBEDDED_PANIC_HEADER_FLAG_NESTED_PANIC;
580 return;
581 }
582
583 panic_info->eph_panic_flags |= EMBEDDED_PANIC_HEADER_FLAG_NESTED_PANIC;
584
585 /*
586 * If the panic log length is not set, set the end to
587 * the current location of the debug_buf_ptr to close it.
588 */
589 if (panic_info->eph_panic_log_len == 0) {
590 panic_info->eph_panic_log_len = PE_get_offset_into_panic_region(debug_buf_ptr);
591
592 /* If this assert fires, it's indicative of corruption in the panic region */
593 assert(panic_info->eph_other_log_offset == panic_info->eph_other_log_len == 0);
594 }
595
596 /* If this assert fires, it's likely indicative of corruption in the panic region */
597 assert(((panic_info->eph_stackshot_offset == 0) && (panic_info->eph_stackshot_len == 0)) ||
598 ((panic_info->eph_stackshot_offset != 0) && (panic_info->eph_stackshot_len != 0)));
599
600 /*
601 * If we haven't set up the other log yet, set the beginning of the other log
602 * to the current location of the debug_buf_ptr
603 */
604 if (panic_info->eph_other_log_offset == 0) {
605 panic_info->eph_other_log_offset = PE_get_offset_into_panic_region(debug_buf_ptr);
606
607 /* If this assert fires, it's indicative of corruption in the panic region */
608 assert(panic_info->eph_other_log_len == 0);
609 }
610
611 return;
612}
613
614boolean_t
615PE_reboot_on_panic(void)
616{
617 uint32_t debug_flags;
618
619 if (PE_i_can_has_debugger(&debug_flags)
620 && (debug_flags & DB_NMI)) {
621 /* kernel debugging is active */
622 return FALSE;
623 } else {
624 return TRUE;
625 }
626}
627
628void
629PE_sync_panic_buffers(void)
630{
631 /*
632 * rdar://problem/26453070:
633 * The iBoot panic region is write-combined on arm64. We must flush dirty lines
634 * from L1/L2 as late as possible before reset, with no further reads of the panic
635 * region between the flush and the reset. Some targets have an additional memcache (L3),
636 * and a read may bring dirty lines out of L3 and back into L1/L2, causing the lines to
637 * be discarded on reset. If we can make sure the lines are flushed to L3/DRAM,
638 * the platform reset handler will flush any L3.
639 */
640 if (gPanicBase)
641 CleanPoC_DcacheRegion_Force(gPanicBase, gPanicSize);
642}
643
644static void
645pe_prepare_images(void)
646{
647 if ((1 & PE_state.video.v_rotate) != 0) {
648 // Only square square images with radial symmetry are supported
649 // No need to actually rotate the data
650
651 // Swap the dx and dy offsets
652 uint32_t tmp = default_progress.dx;
653 default_progress.dx = default_progress.dy;
654 default_progress.dy = tmp;
655 }
656#if 0
657 uint32_t cnt, cnt2, cnt3, cnt4;
658 uint32_t tmp, width, height;
659 uint8_t data, *new_data;
660 const uint8_t *old_data;
661
662 width = default_progress.width;
663 height = default_progress.height * default_progress.count;
664
665 // Scale images if the UI is being scaled
666 if (PE_state.video.v_scale > 1) {
667 new_data = kalloc(width * height * scale * scale);
668 if (new_data != 0) {
669 old_data = default_progress_data;
670 default_progress_data = new_data;
671 for (cnt = 0; cnt < height; cnt++) {
672 for (cnt2 = 0; cnt2 < width; cnt2++) {
673 data = *(old_data++);
674 for (cnt3 = 0; cnt3 < scale; cnt3++) {
675 for (cnt4 = 0; cnt4 < scale; cnt4++) {
676 new_data[width * scale * cnt3 + cnt4] = data;
677 }
678 }
679 new_data += scale;
680 }
681 new_data += width * scale * (scale - 1);
682 }
683 default_progress.width *= scale;
684 default_progress.height *= scale;
685 default_progress.dx *= scale;
686 default_progress.dy *= scale;
687 }
688 }
689#endif
690}
691
692void
693PE_mark_hwaccess(uint64_t thread)
694{
695 last_hwaccess_thread = thread;
696 asm volatile("dmb ish");
697}