浏览代码

Kernel: Add RPi Watchdog and use it for system shutdown

The Raspberry Pi hardware doesn't support a proper software-initiated
shutdown, so this instead uses the watchdog to reboot to a special
partition which the firmware interprets as an immediate halt on
shutdown. When running under Qemu, this causes the emulator to exit.
Daniel Bertalan 2 年之前
父节点
当前提交
d9c557d0b4

+ 47 - 0
Kernel/Arch/aarch64/RPi/Watchdog.cpp

@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2023, Daniel Bertalan <dani@danielbertalan.dev>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <Kernel/Arch/aarch64/RPi/MMIO.h>
+#include <Kernel/Arch/aarch64/RPi/Watchdog.h>
+
+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<WatchdogRegisters>(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;
+}
+}

+ 24 - 0
Kernel/Arch/aarch64/RPi/Watchdog.h

@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2023, Daniel Bertalan <dani@danielbertalan.dev>
+ *
+ * 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;
+};
+}

+ 1 - 0
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

+ 4 - 0
Kernel/FileSystem/SysFS/Subsystems/Kernel/PowerStateSwitch.cpp

@@ -9,6 +9,8 @@
 #if ARCH(X86_64)
 #    include <Kernel/Arch/x86_64/I8042Reboot.h>
 #    include <Kernel/Arch/x86_64/Shutdown.h>
+#elif ARCH(AARCH64)
+#    include <Kernel/Arch/aarch64/RPi/Watchdog.h>
 #endif
 #include <Kernel/FileSystem/FileSystem.h>
 #include <Kernel/FileSystem/SysFS/Subsystems/Kernel/PowerStateSwitch.h>
@@ -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!");

+ 4 - 0
Kernel/Panic.cpp

@@ -8,6 +8,8 @@
 #include <Kernel/Arch/Processor.h>
 #if ARCH(X86_64)
 #    include <Kernel/Arch/x86_64/Shutdown.h>
+#elif ARCH(AARCH64)
+#    include <Kernel/Arch/aarch64/RPi/Watchdog.h>
 #endif
 #include <Kernel/CommandLine.h>
 #include <Kernel/KSyms.h>
@@ -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.