2 * Copyright (c) 2000-2017 Apple Inc. All rights reserved.
4 * arm platform expert initialization.
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>
20 /* extern references */
21 extern void pe_identify_machine(boot_args
*bootArgs
);
23 /* static references */
24 static void pe_prepare_images(void);
28 #define FW_VERS_LEN 128
29 char firmware_version
[FW_VERS_LEN
];
32 * This variable is only modified once, when the BSP starts executing. We put it in __TEXT
33 * as page protections on kernel text early in startup are read-write. The kernel is
34 * locked down later in start-up, said mappings become RO and thus this
35 * variable becomes immutable.
37 * See osfmk/arm/arm_vm_init.c for more information.
39 SECURITY_READ_ONLY_SPECIAL_SECTION(volatile uint32_t, "__TEXT,__const") debug_enabled
= FALSE
;
41 uint8_t gPlatformECID
[8];
42 uint32_t gPlatformMemoryID
;
43 static boolean_t vc_progress_initialized
= FALSE
;
44 uint64_t last_hwaccess_thread
= 0;
45 char gTargetTypeBuffer
[8];
46 char gModelTypeBuffer
[32];
48 /* Clock Frequency Info */
49 clock_frequency_info_t gPEClockFrequencyInfo
;
51 vm_offset_t gPanicBase
;
52 unsigned int gPanicSize
;
53 struct embedded_panic_header
*panic_info
= NULL
;
55 /* Maximum size of panic log excluding headers, in bytes */
56 static unsigned int panic_text_len
;
58 /* Whether a console is standing by for panic logging */
59 static boolean_t panic_console_available
= FALSE
;
61 extern uint32_t crc32(uint32_t crc
, const void *buf
, size_t size
);
64 check_for_panic_log(void)
66 DTEntry entry
, chosen
;
69 uint32_t *panic_region_length
;
72 * Find the vram node in the device tree
74 if (kSuccess
!= DTLookupEntry(0, "pram", &entry
))
77 if (kSuccess
!= DTGetProperty(entry
, "reg", (void **)®_prop
, &size
))
80 if (kSuccess
!= DTLookupEntry(0, "/chosen", &chosen
))
83 if (kSuccess
!= DTGetProperty(chosen
, "embedded-panic-log-size", (void **) &panic_region_length
, &size
))
87 * Map the first page of VRAM into the kernel for use in case of
90 /* Note: map as normal memory. */
91 gPanicBase
= ml_io_map_wcomb(reg_prop
[0], panic_region_length
[0]);
93 /* Deduct the size of the panic header from the panic region size */
94 panic_text_len
= panic_region_length
[0] - sizeof(struct embedded_panic_header
);
95 gPanicSize
= panic_region_length
[0];
96 panic_info
= (struct embedded_panic_header
*)gPanicBase
;
98 /* Check if a shared memory console is running in the panic buffer */
99 if (panic_info
->eph_magic
== 'SHMC') {
100 panic_console_available
= TRUE
;
104 /* Check if there's a boot profile in the panic buffer */
105 if (panic_info
->eph_magic
== 'BTRC') {
110 * Check to see if a panic (FUNK) is in VRAM from the last time
112 if (panic_info
->eph_magic
== EMBEDDED_PANIC_MAGIC
) {
113 printf("iBoot didn't extract panic log from previous session crash, this is bad\n");
116 /* Clear panic region */
117 bzero((void *)gPanicBase
, gPanicSize
);
121 PE_initialize_console(PE_Video
* info
, int op
)
123 static int last_console
= -1;
125 if (info
&& (info
!= &PE_state
.video
)) info
->v_scale
= PE_state
.video
.v_scale
;
129 case kPEDisableScreen
:
130 initialize_screen(info
, op
);
131 last_console
= switch_to_serial_console();
132 kprintf("kPEDisableScreen %d\n", last_console
);
135 case kPEEnableScreen
:
136 initialize_screen(info
, op
);
138 PE_state
.video
= *info
;
139 kprintf("kPEEnableScreen %d\n", last_console
);
140 if (last_console
!= -1)
141 switch_to_old_console(last_console
);
144 case kPEReleaseScreen
:
146 * we don't show the progress indicator on boot, but want to
147 * show it afterwards.
149 if (!vc_progress_initialized
) {
150 default_progress
.dx
= 0;
151 default_progress
.dy
= 0;
152 vc_progress_initialize(&default_progress
,
153 default_progress_data1x
,
154 default_progress_data2x
,
155 default_progress_data3x
,
156 (unsigned char *) appleClut8
);
157 vc_progress_initialized
= TRUE
;
159 initialize_screen(info
, op
);
163 initialize_screen(info
, op
);
174 unsigned int size
, scale
;
175 unsigned long display_size
;
177 unsigned int show_progress
;
178 int *delta
, image_size
, flip
;
179 uint32_t start_time_value
= 0;
180 uint32_t debug_wait_start_value
= 0;
181 uint32_t load_kernel_start_value
= 0;
182 uint32_t populate_registry_time_value
= 0;
184 PE_init_kprintf(TRUE
);
185 PE_init_printf(TRUE
);
187 printf("iBoot version: %s\n", firmware_version
);
189 if (kSuccess
== DTLookupEntry(0, "/chosen/memory-map", &entry
)) {
191 boot_progress_element
*bootPict
;
193 if (kSuccess
== DTGetProperty(entry
, "BootCLUT", (void **) &map
, &size
))
194 bcopy(map
[0], appleClut8
, sizeof(appleClut8
));
196 if (kSuccess
== DTGetProperty(entry
, "Pict-FailedBoot", (void **) &map
, &size
)) {
198 bootPict
= (boot_progress_element
*) map
[0];
199 default_noroot
.width
= bootPict
->width
;
200 default_noroot
.height
= bootPict
->height
;
201 default_noroot
.dx
= 0;
202 default_noroot
.dy
= bootPict
->yOffset
;
203 default_noroot_data
= &bootPict
->data
[0];
209 scale
= PE_state
.video
.v_scale
;
212 if (PE_parse_boot_argn("-progress", &show_progress
, sizeof (show_progress
)) && show_progress
) {
213 /* Rotation: 0:normal, 1:right 90, 2:left 180, 3:left 90 */
214 switch (PE_state
.video
.v_rotate
) {
219 display_size
= PE_state
.video
.v_height
;
220 image_size
= default_progress
.height
;
221 delta
= &default_progress
.dy
;
228 display_size
= PE_state
.video
.v_width
;
229 image_size
= default_progress
.width
;
230 delta
= &default_progress
.dx
;
233 while (((unsigned)(*delta
+ image_size
)) >= (display_size
/ 2)) {
234 *delta
-= 50 * scale
;
239 /* Check for DT-defined progress y delta */
240 PE_get_default("progress-dy", &default_progress
.dy
, sizeof(default_progress
.dy
));
242 vc_progress_initialize(&default_progress
,
243 default_progress_data1x
,
244 default_progress_data2x
,
245 default_progress_data3x
,
246 (unsigned char *) appleClut8
);
247 vc_progress_initialized
= TRUE
;
250 if (kdebug_enable
&& kdebug_debugid_enabled(IOKDBG_CODE(DBG_BOOTER
, 0))) {
251 /* Trace iBoot-provided timing information. */
252 if (kSuccess
== DTLookupEntry(0, "/chosen/iBoot", &entry
)) {
253 uint32_t * value_ptr
;
255 if (kSuccess
== DTGetProperty(entry
, "start-time", (void **)&value_ptr
, &size
)) {
256 if (size
== sizeof(start_time_value
))
257 start_time_value
= *value_ptr
;
260 if (kSuccess
== DTGetProperty(entry
, "debug-wait-start", (void **)&value_ptr
, &size
)) {
261 if (size
== sizeof(debug_wait_start_value
))
262 debug_wait_start_value
= *value_ptr
;
265 if (kSuccess
== DTGetProperty(entry
, "load-kernel-start", (void **)&value_ptr
, &size
)) {
266 if (size
== sizeof(load_kernel_start_value
))
267 load_kernel_start_value
= *value_ptr
;
270 if (kSuccess
== DTGetProperty(entry
, "populate-registry-time", (void **)&value_ptr
, &size
)) {
271 if (size
== sizeof(populate_registry_time_value
))
272 populate_registry_time_value
= *value_ptr
;
276 KDBG_RELEASE(IOKDBG_CODE(DBG_BOOTER
, 0), start_time_value
, debug_wait_start_value
, load_kernel_start_value
, populate_registry_time_value
);
279 StartIOKit(PE_state
.deviceTreeHead
, PE_state
.bootArgs
, (void *) 0, (void *) 0);
283 PE_init_platform(boolean_t vm_initialized
, void *args
)
288 boot_args
*boot_args_ptr
= (boot_args
*) args
;
290 if (PE_state
.initialized
== FALSE
) {
291 PE_state
.initialized
= TRUE
;
292 PE_state
.bootArgs
= boot_args_ptr
;
293 PE_state
.deviceTreeHead
= boot_args_ptr
->deviceTreeP
;
294 PE_state
.video
.v_baseAddr
= boot_args_ptr
->Video
.v_baseAddr
;
295 PE_state
.video
.v_rowBytes
= boot_args_ptr
->Video
.v_rowBytes
;
296 PE_state
.video
.v_width
= boot_args_ptr
->Video
.v_width
;
297 PE_state
.video
.v_height
= boot_args_ptr
->Video
.v_height
;
298 PE_state
.video
.v_depth
= (boot_args_ptr
->Video
.v_depth
>> kBootVideoDepthDepthShift
) & kBootVideoDepthMask
;
299 PE_state
.video
.v_rotate
= (boot_args_ptr
->Video
.v_depth
>> kBootVideoDepthRotateShift
) & kBootVideoDepthMask
;
300 PE_state
.video
.v_scale
= ((boot_args_ptr
->Video
.v_depth
>> kBootVideoDepthScaleShift
) & kBootVideoDepthMask
) + 1;
301 PE_state
.video
.v_display
= boot_args_ptr
->Video
.v_display
;
302 strlcpy(PE_state
.video
.v_pixelFormat
, "BBBBBBBBGGGGGGGGRRRRRRRR", sizeof(PE_state
.video
.v_pixelFormat
));
304 if (!vm_initialized
) {
306 * Setup the Device Tree routines
307 * so the console can be found and the right I/O space
310 DTInit(PE_state
.deviceTreeHead
);
311 pe_identify_machine(boot_args_ptr
);
313 pe_arm_init_interrupts(args
);
314 pe_arm_init_debug(args
);
317 if (!vm_initialized
) {
318 if (kSuccess
== (DTFindEntry("name", "device-tree", &entry
))) {
319 if (kSuccess
== DTGetProperty(entry
, "target-type",
320 (void **)&prop
, &size
)) {
321 if (size
> sizeof(gTargetTypeBuffer
))
322 size
= sizeof(gTargetTypeBuffer
);
323 bcopy(prop
,gTargetTypeBuffer
,size
);
324 gTargetTypeBuffer
[size
-1]='\0';
327 if (kSuccess
== (DTFindEntry("name", "device-tree", &entry
))) {
328 if (kSuccess
== DTGetProperty(entry
, "model",
329 (void **)&prop
, &size
)) {
330 if (size
> sizeof(gModelTypeBuffer
))
331 size
= sizeof(gModelTypeBuffer
);
332 bcopy(prop
,gModelTypeBuffer
,size
);
333 gModelTypeBuffer
[size
-1]='\0';
336 if (kSuccess
== DTLookupEntry(NULL
, "/chosen", &entry
)) {
337 if (kSuccess
== DTGetProperty(entry
, "debug-enabled",
338 (void **) &prop
, &size
)) {
340 * We purposefully modify a constified variable as
341 * it will get locked down by a trusted monitor or
342 * via page table mappings. We don't want people easily
343 * modifying this variable...
345 #pragma clang diagnostic push
346 #pragma clang diagnostic ignored "-Wcast-qual"
347 boolean_t
*modify_debug_enabled
= (boolean_t
*) &debug_enabled
;
348 if (size
> sizeof(uint32_t))
349 size
= sizeof(uint32_t);
350 bcopy(prop
, modify_debug_enabled
, size
);
351 #pragma clang diagnostic pop
353 if (kSuccess
== DTGetProperty(entry
, "firmware-version",
354 (void **) &prop
, &size
)) {
355 if (size
> sizeof(firmware_version
))
356 size
= sizeof(firmware_version
);
357 bcopy(prop
, firmware_version
, size
);
358 firmware_version
[size
- 1] = '\0';
360 if (kSuccess
== DTGetProperty(entry
, "unique-chip-id",
361 (void **) &prop
, &size
)) {
362 if (size
> sizeof(gPlatformECID
))
363 size
= sizeof(gPlatformECID
);
364 bcopy(prop
,gPlatformECID
,size
);
366 if (kSuccess
== DTGetProperty(entry
, "dram-vendor-id",
367 (void **) &prop
, &size
)) {
368 if (size
> sizeof(gPlatformMemoryID
))
369 size
= sizeof(gPlatformMemoryID
);
370 bcopy(prop
,&gPlatformMemoryID
,size
);
378 PE_create_console(void)
381 * Check the head of VRAM for a panic log saved on last panic.
382 * Do this before the VRAM is trashed.
384 check_for_panic_log();
386 if (PE_state
.video
.v_display
)
387 PE_initialize_console(&PE_state
.video
, kPEGraphicsMode
);
389 PE_initialize_console(&PE_state
.video
, kPETextMode
);
393 PE_current_console(PE_Video
* info
)
395 *info
= PE_state
.video
;
400 PE_display_icon(__unused
unsigned int flags
, __unused
const char *name
)
402 if (default_noroot_data
)
403 vc_display_icon(&default_noroot
, default_noroot_data
);
407 PE_get_hotkey(__unused
unsigned char key
)
412 static timebase_callback_func gTimebaseCallback
;
415 PE_register_timebase_callback(timebase_callback_func callback
)
417 gTimebaseCallback
= callback
;
419 PE_call_timebase_callback();
423 PE_call_timebase_callback(void)
425 struct timebase_freq_t timebase_freq
;
427 timebase_freq
.timebase_num
= gPEClockFrequencyInfo
.timebase_frequency_hz
;
428 timebase_freq
.timebase_den
= 1;
430 if (gTimebaseCallback
)
431 gTimebaseCallback(&timebase_freq
);
435 * The default PE_poll_input handler.
438 PE_stub_poll_input(__unused
unsigned int options
, char *c
)
441 return 0; /* 0 for success, 1 for unsupported */
445 * Called by the kernel debugger to poll for keyboard input.
446 * Keyboard drivers may replace the default stub function
447 * with their polled-mode input function.
449 int (*PE_poll_input
) (unsigned int options
, char *c
) = PE_stub_poll_input
;
452 * This routine will return 1 if you are running on a device with a variant
453 * of iBoot that allows debugging. This is typically not the case on production
454 * fused parts (even when running development variants of iBoot).
456 * The routine takes an optional argument of the flags passed to debug="" so
457 * kexts don't have to parse the boot arg themselves.
460 PE_i_can_has_debugger(uint32_t *debug_flags
)
464 *debug_flags
= debug_boot_arg
;
468 return (debug_enabled
);
472 PE_save_buffer_to_vram(unsigned char *buf
, unsigned int *size
)
474 if (!panic_info
|| !size
) {
479 *size
= panic_text_len
;
487 *size
= *size
> panic_text_len
? panic_text_len
: *size
;
488 if (panic_info
->eph_magic
!= EMBEDDED_PANIC_MAGIC
)
489 printf("Error!! Current Magic 0x%X, expected value 0x%x", panic_info
->eph_magic
, EMBEDDED_PANIC_MAGIC
);
491 /* CRC everything after the CRC itself - starting with the panic header version */
492 panic_info
->eph_crc
= crc32(0L, &panic_info
->eph_version
, (panic_text_len
+
493 sizeof(struct embedded_panic_header
) - offsetof(struct embedded_panic_header
, eph_version
)));
497 PE_get_offset_into_panic_region(char *location
)
499 assert(panic_info
!= NULL
);
500 assert(location
> (char *) panic_info
);
501 assert((unsigned int)(location
- (char *) panic_info
) < panic_text_len
);
503 return (uint32_t) (location
- gPanicBase
);
507 PE_init_panicheader()
512 bzero(panic_info
, sizeof(struct embedded_panic_header
));
515 * The panic log begins immediately after the panic header -- debugger synchronization and other functions
516 * may log into this region before we've become the exclusive panicking CPU and initialize the header here.
518 panic_info
->eph_panic_log_offset
= PE_get_offset_into_panic_region(debug_buf_base
);
520 panic_info
->eph_magic
= EMBEDDED_PANIC_MAGIC
;
521 panic_info
->eph_version
= EMBEDDED_PANIC_HEADER_CURRENT_VERSION
;
527 * Tries to update the panic header to keep it consistent on nested panics.
529 * NOTE: The purpose of this function is NOT to detect/correct corruption in the panic region,
530 * it is to update the panic header to make it consistent when we nest panics.
533 PE_update_panicheader_nestedpanic()
539 * If the panic log offset is not set, re-init the panic header
541 if (panic_info
->eph_panic_log_offset
== 0) {
542 PE_init_panicheader();
543 panic_info
->eph_panic_flags
|= EMBEDDED_PANIC_HEADER_FLAG_NESTED_PANIC
;
547 panic_info
->eph_panic_flags
|= EMBEDDED_PANIC_HEADER_FLAG_NESTED_PANIC
;
550 * If the panic log length is not set, set the end to
551 * the current location of the debug_buf_ptr to close it.
553 if (panic_info
->eph_panic_log_len
== 0) {
554 panic_info
->eph_panic_log_len
= PE_get_offset_into_panic_region(debug_buf_ptr
);
556 /* If this assert fires, it's indicative of corruption in the panic region */
557 assert(panic_info
->eph_other_log_offset
== panic_info
->eph_other_log_len
== 0);
560 /* If this assert fires, it's likely indicative of corruption in the panic region */
561 assert(((panic_info
->eph_stackshot_offset
== 0) && (panic_info
->eph_stackshot_len
== 0)) ||
562 ((panic_info
->eph_stackshot_offset
!= 0) && (panic_info
->eph_stackshot_len
!= 0)));
565 * If we haven't set up the other log yet, set the beginning of the other log
566 * to the current location of the debug_buf_ptr
568 if (panic_info
->eph_other_log_offset
== 0) {
569 panic_info
->eph_other_log_offset
= PE_get_offset_into_panic_region(debug_buf_ptr
);
571 /* If this assert fires, it's indicative of corruption in the panic region */
572 assert(panic_info
->eph_other_log_len
== 0);
579 PE_reboot_on_panic(void)
581 uint32_t debug_flags
;
583 if (PE_i_can_has_debugger(&debug_flags
)
584 && (debug_flags
& DB_NMI
)) {
585 /* kernel debugging is active */
593 PE_sync_panic_buffers(void)
596 * rdar://problem/26453070:
597 * The iBoot panic region is write-combined on arm64. We must flush dirty lines
598 * from L1/L2 as late as possible before reset, with no further reads of the panic
599 * region between the flush and the reset. Some targets have an additional memcache (L3),
600 * and a read may bring dirty lines out of L3 and back into L1/L2, causing the lines to
601 * be discarded on reset. If we can make sure the lines are flushed to L3/DRAM,
602 * the platform reset handler will flush any L3.
605 CleanPoC_DcacheRegion_Force(gPanicBase
, gPanicSize
);
609 pe_prepare_images(void)
611 if ((1 & PE_state
.video
.v_rotate
) != 0) {
612 // Only square square images with radial symmetry are supported
613 // No need to actually rotate the data
615 // Swap the dx and dy offsets
616 uint32_t tmp
= default_progress
.dx
;
617 default_progress
.dx
= default_progress
.dy
;
618 default_progress
.dy
= tmp
;
621 uint32_t cnt
, cnt2
, cnt3
, cnt4
;
622 uint32_t tmp
, width
, height
;
623 uint8_t data
, *new_data
;
624 const uint8_t *old_data
;
626 width
= default_progress
.width
;
627 height
= default_progress
.height
* default_progress
.count
;
629 // Scale images if the UI is being scaled
630 if (PE_state
.video
.v_scale
> 1) {
631 new_data
= kalloc(width
* height
* scale
* scale
);
633 old_data
= default_progress_data
;
634 default_progress_data
= new_data
;
635 for (cnt
= 0; cnt
< height
; cnt
++) {
636 for (cnt2
= 0; cnt2
< width
; cnt2
++) {
637 data
= *(old_data
++);
638 for (cnt3
= 0; cnt3
< scale
; cnt3
++) {
639 for (cnt4
= 0; cnt4
< scale
; cnt4
++) {
640 new_data
[width
* scale
* cnt3
+ cnt4
] = data
;
645 new_data
+= width
* scale
* (scale
- 1);
647 default_progress
.width
*= scale
;
648 default_progress
.height
*= scale
;
649 default_progress
.dx
*= scale
;
650 default_progress
.dy
*= scale
;
657 PE_mark_hwaccess(uint64_t thread
)
659 last_hwaccess_thread
= thread
;
660 asm volatile("dmb ish");