blob: 212d2336148f6dbf464ba5c94532651ec4964b31 [file] [log] [blame]
// Copyright 2016 The Fuchsia Authors
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
//
#include <arch/mp.h>
#include <arch/x86.h>
#include <arch/x86/apic.h>
#include <arch/x86/feature.h>
#include <arch/x86/mp.h>
#include <fbl/atomic.h>
#include <stdio.h>
#include <string.h>
#include <platform.h>
#include <platform/keyboard.h>
#include <lib/console.h>
#include <lib/debuglog.h>
#include <lib/version.h>
// The I/O port to write to for QEMU debug exit.
const uint16_t kQEMUDebugExitPort = 0xf4;
// The return code that we should propagate to QEMU on isa-debug-exit.
// This number must be non-zero and odd, since QEMU calculates the return
// code as (val << 1) | 1 where "val" is the value written to 0xf4.
const uint8_t kQEMUExitCode = 0x1f;
static_assert(kQEMUExitCode != 0 && kQEMUExitCode % 2 != 0,
"QEMU exit code must be non-zero and odd.");
static void reboot(void) {
x86_get_microarch_config()->reboot_system();
// We fell through. Try rebooting via keyboard controller.
pc_keyboard_reboot();
}
static fbl::atomic<cpu_mask_t> halted_cpus(0);
static void halt_other_cpus(void) {
static fbl::atomic<int> halted(0);
if (halted.exchange(1) == 0) {
// stop the other cpus
printf("stopping other cpus\n");
arch_mp_send_ipi(MP_IPI_TARGET_ALL_BUT_LOCAL, 0, MP_IPI_HALT);
cpu_mask_t targets = mp_get_online_mask() & ~cpu_num_to_mask(arch_curr_cpu_num());
// spin for a while
// TODO: find a better way to spin at this low level
for (volatile int i = 0; i < 100000000; i++) {
if (halted_cpus.load() == targets) {
break;
}
__asm volatile("nop");
}
// Don't send an INIT IPI to the BSP, since that may cause the system to
// reboot
x86_force_halt_all_but_local_and_bsp();
}
}
void platform_halt_cpu(void) {
// Signal that this CPU is in its halt loop
halted_cpus.fetch_or(cpu_num_to_mask(arch_curr_cpu_num()));
}
void platform_panic_start(void) {
platform_debug_panic_start();
arch_disable_ints();
static fbl::atomic<int> panic_started(0);
if (panic_started.exchange(1) == 0) {
dlog_bluescreen_init();
}
halt_other_cpus();
}
bool halt_on_panic = false;
extern const char* manufacturer;
void platform_halt(
platform_halt_action suggested_action,
platform_halt_reason reason) {
printf("platform_halt suggested_action %d reason %d\n", suggested_action, reason);
arch_disable_ints();
switch (suggested_action) {
case HALT_ACTION_SHUTDOWN:
if (strcmp("QEMU", manufacturer) == 0) {
outp(kQEMUDebugExitPort, (kQEMUExitCode >> 1));
}
printf("Power off failed, halting\n");
break;
case HALT_ACTION_REBOOT:
printf("Rebooting...\n");
reboot();
printf("Reboot failed, halting\n");
break;
case HALT_ACTION_HALT:
printf("Halting...\n");
halt_other_cpus();
break;
case HALT_ACTION_REBOOT_BOOTLOADER:
case HALT_ACTION_REBOOT_RECOVERY:
printf("platform_halt: Unsupported halt reason %d\n", suggested_action);
break;
}
if (reason == HALT_REASON_SW_PANIC) {
thread_print_current_backtrace();
dlog_bluescreen_halt();
}
if (!halt_on_panic) {
printf("Rebooting...\n");
reboot();
}
printf("Halted\n");
#if ENABLE_PANIC_SHELL
panic_shell_start();
#endif
for (;;) {
x86_hlt();
}
}