1 // Copyright 2016 The Fuchsia Authors
2 //
3 // Use of this source code is governed by a MIT-style
4 // license that can be found in the LICENSE file or at
5 // https://opensource.org/licenses/MIT
6 //
7
8 #include <arch/mp.h>
9 #include <arch/x86.h>
10 #include <arch/x86/apic.h>
11 #include <arch/x86/feature.h>
12 #include <arch/x86/mp.h>
13 #include <fbl/atomic.h>
14 #include <stdio.h>
15 #include <string.h>
16
17 #include <platform.h>
18 #include <platform/keyboard.h>
19
20 #include <lib/console.h>
21 #include <lib/debuglog.h>
22 #include <lib/version.h>
23
24 // The I/O port to write to for QEMU debug exit.
25 const uint16_t kQEMUDebugExitPort = 0xf4;
26
27 // The return code that we should propagate to QEMU on isa-debug-exit.
28 // This number must be non-zero and odd, since QEMU calculates the return
29 // code as (val << 1) | 1 where "val" is the value written to 0xf4.
30 const uint8_t kQEMUExitCode = 0x1f;
31 static_assert(kQEMUExitCode != 0 && kQEMUExitCode % 2 != 0,
32 "QEMU exit code must be non-zero and odd.");
33
reboot(void)34 static void reboot(void) {
35 x86_get_microarch_config()->reboot_system();
36 // We fell through. Try rebooting via keyboard controller.
37 pc_keyboard_reboot();
38 }
39
40 static fbl::atomic<cpu_mask_t> halted_cpus(0);
41
halt_other_cpus(void)42 static void halt_other_cpus(void) {
43 static fbl::atomic<int> halted(0);
44
45 if (halted.exchange(1) == 0) {
46 // stop the other cpus
47 printf("stopping other cpus\n");
48 arch_mp_send_ipi(MP_IPI_TARGET_ALL_BUT_LOCAL, 0, MP_IPI_HALT);
49
50 cpu_mask_t targets = mp_get_online_mask() & ~cpu_num_to_mask(arch_curr_cpu_num());
51 // spin for a while
52 // TODO: find a better way to spin at this low level
53 for (volatile int i = 0; i < 100000000; i++) {
54 if (halted_cpus.load() == targets) {
55 break;
56 }
57 __asm volatile("nop");
58 }
59
60 // Don't send an INIT IPI to the BSP, since that may cause the system to
61 // reboot
62 x86_force_halt_all_but_local_and_bsp();
63 }
64 }
65
platform_halt_cpu(void)66 void platform_halt_cpu(void) {
67 // Signal that this CPU is in its halt loop
68 halted_cpus.fetch_or(cpu_num_to_mask(arch_curr_cpu_num()));
69 }
70
platform_panic_start(void)71 void platform_panic_start(void) {
72 platform_debug_panic_start();
73 arch_disable_ints();
74
75 static fbl::atomic<int> panic_started(0);
76 if (panic_started.exchange(1) == 0) {
77 dlog_bluescreen_init();
78 }
79
80 halt_other_cpus();
81 }
82
83 bool halt_on_panic = false;
84 extern const char* manufacturer;
85
platform_halt(platform_halt_action suggested_action,platform_halt_reason reason)86 void platform_halt(
87 platform_halt_action suggested_action,
88 platform_halt_reason reason) {
89 printf("platform_halt suggested_action %d reason %d\n", suggested_action, reason);
90
91 arch_disable_ints();
92
93 switch (suggested_action) {
94 case HALT_ACTION_SHUTDOWN:
95 if (strcmp("QEMU", manufacturer) == 0) {
96 outp(kQEMUDebugExitPort, (kQEMUExitCode >> 1));
97 }
98 printf("Power off failed, halting\n");
99 break;
100 case HALT_ACTION_REBOOT:
101 printf("Rebooting...\n");
102 reboot();
103 printf("Reboot failed, halting\n");
104 break;
105 case HALT_ACTION_HALT:
106 printf("Halting...\n");
107 halt_other_cpus();
108 break;
109 case HALT_ACTION_REBOOT_BOOTLOADER:
110 case HALT_ACTION_REBOOT_RECOVERY:
111 printf("platform_halt: Unsupported halt reason %d\n", suggested_action);
112 break;
113 }
114
115 if (reason == HALT_REASON_SW_PANIC) {
116 thread_print_current_backtrace();
117 dlog_bluescreen_halt();
118 }
119
120 if (!halt_on_panic) {
121 printf("Rebooting...\n");
122 reboot();
123 }
124
125 printf("Halted\n");
126
127 #if ENABLE_PANIC_SHELL
128 panic_shell_start();
129 #endif
130
131 for (;;) {
132 x86_hlt();
133 }
134 }
135