blob: 11d455641c61153550e28d79c6691e295af4d204 [file] [log] [blame]
// Copyright 2019 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
#include <lib/debuglog.h>
#include <lib/kconcurrent/chainlock_transaction.h>
#include <zircon/boot/crash-reason.h>
#include <kernel/mp.h>
#include <platform/halt_helper.h>
#include <platform/halt_token.h>
// Storage for the global halt token singleton
HaltToken HaltToken::g_instance;
void platform_graceful_halt_helper(platform_halt_action action, zircon_crash_reason_t reason,
zx_time_t panic_deadline) {
if (!HaltToken::Get().Take()) {
printf("platform_graceful_halt_helper: halt/reboot already in progress; sleeping forever\n");
printf("platform_graceful_halt_helper: action=%d reason=%u panic_deadline=%ld current_time=%ld\n",
action, static_cast<uint32_t>(reason), panic_deadline, current_time());
// Migrate to the boot CPU before shutting down the secondary CPUs. Note that
// this action also hard-pins our thread to the boot CPU, so we don't need to
// worry about migration after this.
printf("platform_graceful_halt_helper: Migrated thread to boot CPU.\n");
zx_status_t status = platform_halt_secondary_cpus(panic_deadline);
ASSERT_MSG(status == ZX_OK, "platform_halt_secondary_cpus failed: %d\n", status);
printf("platform_graceful_halt_helper: Halted secondary CPUs.\n");
// Delay shutdown of debuglog to ensure log messages emitted by above calls will be written.
printf("platform_graceful_halt_helper: Shutting down dlog.\n");
status = dlog_shutdown(panic_deadline);
ASSERT_MSG(status == ZX_OK, "dlog_shutdown failed: %d\n", status);
printf("platform_graceful_halt_helper: Calling platform_halt.\n");
platform_halt(action, reason);
panic("ERROR: failed to halt the platform\n");
zx_status_t platform_halt_secondary_cpus(zx_time_t deadline) {
// Ensure the current thread is pinned to the boot CPU.
Thread* current_thread = Thread::Current::Get();
SingletonChainLockGuardIrqSave guard{current_thread->get_lock(),
DEBUG_ASSERT(current_thread->scheduler_state().hard_affinity() == cpu_num_to_mask(BOOT_CPU_ID));
// It's possible that the one or more secondary CPUs is still in the process
// of booting up. Wait for all the started CPUs to check-in before shutting
// them down.
// The deadline here should be long enough that the secondary CPUs have had
// ample time to startup but small enough that we don't hold up the shutdown
// process "too long".
zx_status_t status = mp_wait_for_all_cpus_ready(Deadline::after(ZX_SEC(5)));
if (status != ZX_OK) {
printf("failed to wait for cpus to start: %d\n", status);
// "Unplug" online secondary CPUs before halting them.
cpu_mask_t primary = cpu_num_to_mask(BOOT_CPU_ID);
cpu_mask_t mask = mp_get_online_mask() & ~primary;
status = mp_unplug_cpu_mask(mask, deadline);
if (status == ZX_OK) {
return ZX_OK;
// mp_unplug_cpu_mask failed. Perhaps another thread was trying to shutdown the secondary CPUs.
// If the primary CPU is the only one left, then we've done our job.
if (status == ZX_ERR_BAD_STATE && mp_get_online_mask() == primary) {
return ZX_OK;
return status;