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