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