diff --git a/Kernel/Arch/aarch64/RPi/Watchdog.cpp b/Kernel/Arch/aarch64/RPi/Watchdog.cpp new file mode 100644 index 0000000000..50555543f9 --- /dev/null +++ b/Kernel/Arch/aarch64/RPi/Watchdog.cpp @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2023, Daniel Bertalan + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include + +namespace Kernel::RPi { +struct WatchdogRegisters { + u32 rstc; + u32 rsts; + u32 wdog; +}; + +constexpr u32 PASSWORD = 0x5a000000; +constexpr u32 RSTS_PARTITION_MASK = 0xfffffaaa; +constexpr u32 RSTS_PARTITION_SHUTDOWN = 0x00000555; +constexpr u32 RSTC_WRCFG_MASK = 0xffffffcf; +constexpr u32 RSTC_WRCFG_FULL_RESET = 0x00000020; + +Watchdog::Watchdog() + : m_registers(MMIO::the().peripheral(0x10'001c)) +{ +} + +Watchdog& Watchdog::the() +{ + static Watchdog watchdog; + return watchdog; +} + +// This is the same mechanism used by Linux, the ARM Trusted Firmware and U-Boot to trigger a system shutdown. +// See e.g. https://github.com/ARM-software/arm-trusted-firmware/blob/dcf430656ca8ef964fa55ad9eb81cf838c7837f2/plat/rpi/common/rpi3_pm.c#L231-L249 +void Watchdog::system_shutdown() +{ + // The Raspberry Pi hardware doesn't support powering off. Setting the reboot target partition to this + // special value will cause the firmware to halt the CPU and put it in a low power state when the watchdog + // timer expires. When running under Qemu, this will cause the emulator to exit. + m_registers->rsts = PASSWORD | (m_registers->rsts & RSTS_PARTITION_MASK) | RSTS_PARTITION_SHUTDOWN; + // Set the timeout to 10 ticks (~150us). + m_registers->wdog = PASSWORD | 10; + // Start the watchdog. + m_registers->rstc = PASSWORD | (m_registers->rstc & RSTC_WRCFG_MASK) | RSTC_WRCFG_FULL_RESET; +} +} diff --git a/Kernel/Arch/aarch64/RPi/Watchdog.h b/Kernel/Arch/aarch64/RPi/Watchdog.h new file mode 100644 index 0000000000..7901fd1ef5 --- /dev/null +++ b/Kernel/Arch/aarch64/RPi/Watchdog.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2023, Daniel Bertalan + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +namespace Kernel::RPi { + +struct WatchdogRegisters; + +class Watchdog { +public: + static Watchdog& the(); + + void system_shutdown(); + +private: + Watchdog(); + + WatchdogRegisters volatile* m_registers; +}; +} diff --git a/Kernel/CMakeLists.txt b/Kernel/CMakeLists.txt index a6c2e9437a..b49cfba45f 100644 --- a/Kernel/CMakeLists.txt +++ b/Kernel/CMakeLists.txt @@ -446,6 +446,7 @@ elseif("${SERENITY_ARCH}" STREQUAL "aarch64") Arch/aarch64/RPi/SDHostController.cpp Arch/aarch64/RPi/Timer.cpp Arch/aarch64/RPi/UART.cpp + Arch/aarch64/RPi/Watchdog.cpp ) set(SOURCES_RUNNING_WITHOUT_MMU Arch/aarch64/Exceptions.cpp diff --git a/Kernel/FileSystem/SysFS/Subsystems/Kernel/PowerStateSwitch.cpp b/Kernel/FileSystem/SysFS/Subsystems/Kernel/PowerStateSwitch.cpp index aabde003e6..5b5b6c81db 100644 --- a/Kernel/FileSystem/SysFS/Subsystems/Kernel/PowerStateSwitch.cpp +++ b/Kernel/FileSystem/SysFS/Subsystems/Kernel/PowerStateSwitch.cpp @@ -9,6 +9,8 @@ #if ARCH(X86_64) # include # include +#elif ARCH(AARCH64) +# include #endif #include #include @@ -101,6 +103,8 @@ void SysFSPowerStateSwitchNode::poweroff() #if ARCH(X86_64) qemu_shutdown(); virtualbox_shutdown(); +#elif ARCH(AARCH64) + RPi::Watchdog::the().system_shutdown(); #endif dbgln("shutdown attempts failed, applications will stop responding."); dmesgln("Shutdown can't be completed. It's safe to turn off the computer!"); diff --git a/Kernel/Panic.cpp b/Kernel/Panic.cpp index ad132f5032..621149cca8 100644 --- a/Kernel/Panic.cpp +++ b/Kernel/Panic.cpp @@ -8,6 +8,8 @@ #include #if ARCH(X86_64) # include +#elif ARCH(AARCH64) +# include #endif #include #include @@ -21,6 +23,8 @@ namespace Kernel { #if ARCH(X86_64) qemu_shutdown(); virtualbox_shutdown(); +#elif ARCH(AARCH64) + RPi::Watchdog::the().system_shutdown(); #endif // Note: If we failed to invoke platform shutdown, we need to halt afterwards // to ensure no further execution on any CPU still happens.