sysutils: implement poweroff

This commit is contained in:
2025-08-14 10:31:09 +03:00
parent 9c32c11b0b
commit 0ff48fd520
19 changed files with 264 additions and 51 deletions
+8
View File
@@ -4,6 +4,8 @@ const EXT_HSM: u64 = 0x48534D;
const EXT_TIME: u64 = 0x54494D45;
const EXT_DBCN: u64 = 0x4442434E;
const EXT_SPI: u64 = 0x735049;
const EXT_SYSTEM_SHUTDOWN: u64 = 0x53525354;
const EXT_SYSTEM_SHUTDOWN_LEGACY: u64 = 0x08;
primitive_enum! {
pub enum Status: i64 {
@@ -108,3 +110,9 @@ pub fn sbi_debug_console_write_byte(byte: u8) {
pub fn sbi_set_timer(next_event: u64) {
unsafe { sbi_do_call(EXT_TIME, 0x00, next_event, 0, 0, 0, 0, 0) }.ok();
}
pub fn sbi_system_shutdown() -> ! {
unsafe { sbi_do_call(EXT_SYSTEM_SHUTDOWN, 0x00, 0, 0, 0, 0, 0, 0) }.ok();
unsafe { sbi_do_call(EXT_SYSTEM_SHUTDOWN_LEGACY, 0x00, 0, 0, 0, 0, 0, 0) }.ok();
unreachable!()
}
+13 -8
View File
@@ -1,8 +1,8 @@
#![feature(allocator_api)]
#![feature(allocator_api, never_type)]
#![no_std]
use acpi::AcpiTables;
use acpi_system::{AcpiInterruptMethod, AcpiSystem};
use acpi_system::{AcpiInterruptMethod, AcpiSleepState, AcpiSystem};
use alloc::boxed::Box;
use libk::error::Error;
use libk_util::{sync::IrqSafeSpinlock, OneTimeInit};
@@ -45,6 +45,17 @@ pub fn get_pci_route(
.ok()
}
pub fn power_off() -> Result<!, Error> {
let system = ACPI_SYSTEM.get();
unsafe {
system.lock().enter_sleep_state(AcpiSleepState::S5).ok();
loop {
core::arch::asm!("cli; hlt");
}
}
}
/// Initializes ACPI management
pub fn switch_to_acpi(tables: &'static AcpiTables<AcpiHandlerImpl>) -> Result<(), Error> {
// NOTE mostly broken for real HW
@@ -67,12 +78,6 @@ pub fn switch_to_acpi(tables: &'static AcpiTables<AcpiHandlerImpl>) -> Result<()
// // 6. Do something with the devices
// // 7. Actually enter the S5 state
// unsafe {
// PLATFORM
// .send_ipi(IpiDeliveryTarget::OtherCpus, IpiMessage::Shutdown)
// .unwrap();
// }
// SHUTDOWN_FENCE.signal();
// SHUTDOWN_FENCE.wait_all(CPU_COUNT.load(Ordering::Acquire));
+3 -2
View File
@@ -141,8 +141,9 @@ impl ExternalInterruptController for Gic {
gicc.clear_irq(irq_number);
if irq_number == IPI_VECTOR as usize {
// TODO pop entry
crate::panic::panic_secondary();
let ipi = Cpu::local().get_ipi();
AArch64::handle_ipi(ipi);
return;
}
if irq_number < GIC_PPI_START as usize {
+54 -3
View File
@@ -5,14 +5,15 @@ use core::sync::atomic::{self, Ordering};
use aarch64_cpu::registers::{CNTP_CTL_EL0, CNTP_TVAL_EL0};
use alloc::sync::Arc;
use device_api::{
interrupt::{Irq, LocalInterruptController},
interrupt::{IpiDeliveryTarget, IpiMessage, Irq, LocalInterruptController},
ResetDevice,
};
use device_tree::{
driver::{unflatten_device_tree, InitSequence},
DeviceTree, DeviceTreeNodeExt,
};
use kernel_arch_aarch64::{mem, ArchitectureImpl, PerCpuData};
use kernel_arch::Architecture;
use kernel_arch_aarch64::{mem, ArchitectureImpl, PerCpuData, CPU_COUNT};
use libk::{arch::Cpu, config, debug, device::external_interrupt_controller, error::Error};
use libk_mm::{
address::PhysicalAddress,
@@ -20,7 +21,7 @@ use libk_mm::{
pointer::PhysicalRef,
table::EntryLevelExt,
};
use libk_util::OneTimeInit;
use libk_util::{sync::SpinFence, OneTimeInit};
use tock_registers::interfaces::Writeable;
use ygg_driver_pci::PciBusManager;
@@ -28,6 +29,7 @@ use crate::{
arch::{aarch64::gic::Gic, Platform},
device::{power::arm_psci::Psci, MACHINE_NAME},
fs::{Initrd, INITRD_DATA},
panic,
util::call_init_array,
};
@@ -68,6 +70,8 @@ impl<const SIZE: usize> BootStack<SIZE> {
}
}
static SHUTDOWN_FENCE: SpinFence = SpinFence::new();
impl Platform for AArch64 {
unsafe fn start_application_processors(&self) {
if let Some(compatible) = self.machine_compatible.try_get() {
@@ -96,6 +100,18 @@ impl Platform for AArch64 {
psci.reset()
}
}
unsafe fn power_off(&self) -> Result<!, Error> {
Self::halt_aps()?;
if let Some(psci) = self.psci.try_get() {
log::info!("Powering off");
psci.power_off()
} else {
log::warn!("No power off method, halting");
ArchitectureImpl::halt();
}
}
}
impl AArch64 {
@@ -103,6 +119,41 @@ impl AArch64 {
GIC.init(gic);
}
fn handle_ipi(ipi: Option<IpiMessage>) {
let Some(ipi) = ipi else {
log::warn!("Spurious IPI received, assuming panic");
panic::panic_secondary();
};
match ipi {
IpiMessage::Panic => panic::panic_secondary(),
IpiMessage::Shutdown => Self::secondary_halt_handler(),
}
}
fn secondary_halt_handler() {
log::info!("CPU halted");
SHUTDOWN_FENCE.signal();
ArchitectureImpl::halt();
}
unsafe fn halt_aps() -> Result<(), Error> {
let ap_count = CPU_COUNT.load(Ordering::Acquire) - 1;
if ap_count > 0 {
if let Some(gic) = GIC.try_get() {
gic.send_ipi(IpiDeliveryTarget::OtherCpus, IpiMessage::Shutdown)?;
SHUTDOWN_FENCE.wait_all(ap_count);
Ok(())
} else {
log::warn!("No way to handle secondary CPUs");
Err(Error::NotImplemented)
}
} else {
Ok(())
}
}
#[inline(never)]
unsafe fn init_memory_management(
&'static self,
+14
View File
@@ -1,5 +1,6 @@
//! Provides architecture/platform-specific implementation details
use abi::error::Error;
use kernel_arch::{Architecture, ArchitectureImpl};
#[cfg(any(target_arch = "aarch64", rust_analyzer))]
@@ -51,6 +52,19 @@ pub trait Platform {
}
}
/// Performs full system powerdown.
///
/// # Safety
///
/// The caller must ensure it is actually safe to power down, i.e. no critical processes will be
/// aborted and no data will be lost.
unsafe fn power_off(&self) -> Result<!, Error> {
ArchitectureImpl::set_interrupt_mask(true);
loop {
ArchitectureImpl::wait_for_interrupt();
}
}
// /// Adds a reset device to the system
// fn register_reset_device(&self, reset: Arc<dyn ResetDevice>) -> Result<(), Error> {
// Err(Error::NotImplemented)
+5 -1
View File
@@ -11,7 +11,7 @@ use kernel_arch::{Architecture, ArchitectureImpl};
use kernel_arch_riscv64::{
mem,
registers::{SIE, SSTATUS},
PerCpuData,
sbi, PerCpuData,
};
use libk::{arch::Cpu, config};
use libk_mm::{
@@ -51,6 +51,10 @@ impl Platform for Riscv64 {
ArchitectureImpl::halt();
}
unsafe fn power_off(&self) -> Result<!, Error> {
sbi::sbi_system_shutdown()
}
unsafe fn start_application_processors(&self) {
// TODO asymmetric systems with different hart types are not yet supported.
// e.g., in JH7110 there're two different types of cores
+4 -2
View File
@@ -11,6 +11,8 @@ use libk::{
};
use static_assertions::{const_assert, const_assert_eq};
use crate::arch::x86_64::X86_64;
use super::exception;
pub mod ioapic;
@@ -115,8 +117,8 @@ unsafe extern "C" fn dummy_irq_handler() {
}
unsafe extern "C" fn ipi_handler() {
let cpu = Cpu::local();
todo!("Processor {} received an IPI", cpu.id());
let ipi = Cpu::local().get_ipi();
X86_64::handle_ipi(ipi);
}
global_asm!(
+9 -3
View File
@@ -4,12 +4,12 @@ use core::{arch::global_asm, mem::size_of};
use abi::{bitflags, primitive_enum, process::Signal};
use kernel_arch_x86::registers::{CR2, CR3};
use kernel_arch_x86_64::context::ExceptionFrame;
use libk::task::thread::Thread;
use libk::{arch::Cpu, task::thread::Thread};
use libk_mm::PageFaultKind;
use libk_util::sync::spin_rwlock::IrqSafeRwLock;
use tock_registers::interfaces::Readable;
use crate::arch::x86_64::apic;
use crate::arch::x86_64::{apic, X86_64};
primitive_enum! {
enum ExceptionKind: u64 {
@@ -231,7 +231,13 @@ extern "C" fn __x86_64_exception_handler(frame: *mut ExceptionFrame) {
}
extern "C" fn __x86_64_nmi_handler() -> ! {
crate::panic::panic_secondary();
let ipi = Cpu::local().get_ipi();
if let Some(ipi) = ipi {
X86_64::handle_ipi(Some(ipi));
unreachable!();
} else {
panic!("Spurious/unknown NMI received");
}
}
/// Initializes the interrupt descriptor table for the given CPU.
+54 -5
View File
@@ -1,16 +1,20 @@
//! x86-64 architecture implementation
use core::ptr::null_mut;
use core::{ptr::null_mut, sync::atomic::Ordering};
use abi::error::Error;
use acpi::{mcfg::Mcfg, AcpiTables, HpetInfo, InterruptModel};
use alloc::sync::Arc;
use device_api::device::Device;
use device_api::{
device::Device,
interrupt::{IpiDeliveryTarget, IpiMessage},
};
use kernel_arch::{Architecture, ArchitectureImpl};
use kernel_arch_x86::{
cpuid::{self, CpuFeatures, EcxFeatures, EdxFeatures, ExtEdxFeatures},
gdt,
};
use kernel_arch_x86_64::{mem, LocalApicInterface, PerCpuData};
use kernel_arch_x86_64::{mem, LocalApicInterface, PerCpuData, CPU_COUNT};
use libk::{
arch::Cpu,
config, debug,
@@ -27,7 +31,10 @@ use libk_mm::{
pointer::PhysicalRef,
table::EntryLevel,
};
use libk_util::OneTimeInit;
use libk_util::{
sync::{IrqGuard, SpinFence},
OneTimeInit,
};
use yboot_proto::{
v1::{self, AvailableMemoryRegion},
LoadProtocolV1,
@@ -45,6 +52,7 @@ use crate::{
Platform,
},
device::display::linear_fb::LinearFramebuffer,
panic,
util::call_init_array,
};
@@ -72,7 +80,9 @@ pub static PLATFORM: X86_64 = X86_64 {
fbconsole: OneTimeInit::new(),
};
//
static SHUTDOWN_FENCE: SpinFence = SpinFence::new();
impl Platform for X86_64 {
unsafe fn start_application_processors(&self) {
if let Some(acpi) = self.acpi.try_get() {
@@ -87,9 +97,42 @@ impl Platform for X86_64 {
smp::start_ap_cores(&pinfo);
}
}
unsafe fn power_off(&self) -> Result<!, Error> {
let _guard = IrqGuard::acquire();
let ap_count = CPU_COUNT.load(Ordering::Acquire) - 1;
if ap_count > 0 {
self.send_ipi(IpiDeliveryTarget::OtherCpus, IpiMessage::Shutdown)?;
SHUTDOWN_FENCE.wait_all(ap_count);
}
ygg_driver_acpi::power_off()
}
}
impl X86_64 {
fn handle_ipi(ipi: Option<IpiMessage>) {
let Some(ipi) = ipi else {
log::warn!("Spurious IPI received, assuming panic");
panic::panic_secondary();
};
match ipi {
IpiMessage::Shutdown => {
log::info!("Core halted");
SHUTDOWN_FENCE.signal();
ArchitectureImpl::halt()
}
IpiMessage::Panic => panic::panic_secondary(),
}
}
fn send_ipi(&self, target: IpiDeliveryTarget, message: IpiMessage) -> Result<(), Error> {
let cpu = Cpu::local();
cpu.local_apic.send_ipi(target, message)
}
fn set_boot_data(&self, data: BootData) {
match data {
BootData::YBoot(data) => {
@@ -196,6 +239,12 @@ impl X86_64 {
call_init_array();
x86::init_platform_devices(early);
extern "C" {
static __kernel_start: u8;
}
log::info!("Load base: {:p}", &raw const __kernel_start);
}
Ok(())
+7
View File
@@ -61,6 +61,7 @@ impl ResetDevice for Psci {
impl Psci {
const SYSTEM_RESET: u32 = 0x84000009;
const SYSTEM_OFF: u32 = 0x84000008;
#[inline]
unsafe fn call(&self, mut x0: u64, x1: u64, x2: u64, x3: u64) -> u64 {
@@ -74,6 +75,12 @@ impl Psci {
}
x0
}
/// Shut down the system
pub unsafe fn power_off(&self) -> Result<!, Error> {
self.call(Self::SYSTEM_OFF as _, 0, 0, 0);
unreachable!()
}
}
device_tree_driver! {
+2
View File
@@ -75,8 +75,10 @@ cfg_if::cfg_if! {
if #[cfg(target_arch = "x86_64")] {
extern crate ygg_driver_net_igbe;
} else if #[cfg(target_arch = "aarch64")] {
extern crate ygg_driver_bsp_arm;
extern crate ygg_driver_bsp_bcm283x;
} else if #[cfg(target_arch = "riscv64")] {
extern crate ygg_driver_bsp_riscv;
extern crate ygg_driver_bsp_jh7110;
extern crate ygg_driver_net_stmmac;
+15 -2
View File
@@ -16,7 +16,7 @@ use abi::{
option::OptionValue,
path::Path,
process::{ExecveOptions, ProcessId},
system::{self, SystemInfoVariant},
system::{self, SystemControlVariant, SystemInfoVariant},
time::{ClockType, SystemTime},
};
use libk::{
@@ -26,7 +26,10 @@ use libk::{
};
use libk_mm::phys;
use crate::fs;
use crate::{
arch::{Platform, PLATFORM},
fs,
};
use super::run_with_io;
@@ -86,6 +89,16 @@ pub(crate) fn set_clock(ty: ClockType, value: &SystemTime) -> Result<(), Error>
}
}
pub(crate) fn system_control(option: u32, value: &mut [u8], size: usize) -> Result<usize, Error> {
let _ = (value, size);
let option = SystemControlVariant::try_from(option)?;
match option {
SystemControlVariant::PowerOff => {
unsafe { PLATFORM.power_off() }?;
}
}
}
pub(crate) fn get_system_info(option: u32, buffer: &mut [u8]) -> Result<usize, Error> {
let option = SystemInfoVariant::try_from(option)?;
match option {