properly shutdown system

This commit is contained in:
Ashwin Naren
2025-11-27 23:35:16 -08:00
committed by Matthew Leach
parent 504f7f01dc
commit de76e243a2
14 changed files with 153 additions and 56 deletions

View File

@@ -1,7 +1,7 @@
# Syscalls
| Number | Name | Signature | Symbol | Implemented |
| ----------- | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------- | ----------- |
| ----------- | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------- |-------------|
| 0x0 (0) | io_setup | (unsigned nr_events, aio_context_t *ctxp) | __arm64_sys_io_setup | false |
| 0x1 (1) | io_destroy | (aio_context_t ctx) | __arm64_sys_io_destroy | false |
| 0x2 (2) | io_submit | (aio_context_t ctx_id, long nr, struct iocb **iocbpp) | __arm64_sys_io_submit | false |
@@ -78,7 +78,7 @@
| 0x4b (75) | vmsplice | (int fd, const struct iovec *uiov, unsigned long nr_segs, unsigned int flags) | __arm64_sys_vmsplice | false |
| 0x4c (76) | splice | (int fd_in, loff_t *off_in, int fd_out, loff_t *off_out, size_t len, unsigned int flags) | __arm64_sys_splice | false |
| 0x4d (77) | tee | (int fdin, int fdout, size_t len, unsigned int flags) | __arm64_sys_tee | false |
| 0x4e (78) | readlinkat | (int dfd, const char *pathname, char *buf, int bufsiz) | __arm64_sys_readlinkat | dummy |
| 0x4e (78) | readlinkat | (int dfd, const char *pathname, char *buf, int bufsiz) | __arm64_sys_readlinkat | dummy |
| 0x4f (79) | newfstatat | (int dfd, const char *filename, struct stat *statbuf, int flag) | __arm64_sys_newfstatat | true |
| 0x50 (80) | newfstat | (unsigned int fd, struct stat *statbuf) | __arm64_sys_newfstat | true |
| 0x51 (81) | sync | () | __arm64_sys_sync | false |
@@ -96,7 +96,7 @@
| 0x5d (93) | exit | (int error_code) | __arm64_sys_exit | true |
| 0x5e (94) | exit_group | (int error_code) | __arm64_sys_exit_group | true |
| 0x5f (95) | waitid | (int which, pid_t upid, struct siginfo *infop, int options, struct rusage *ru) | __arm64_sys_waitid | false |
| 0x60 (96) | set_tid_address | (int *tidptr) | __arm64_sys_set_tid_address | dummy |
| 0x60 (96) | set_tid_address | (int *tidptr) | __arm64_sys_set_tid_address | dummy |
| 0x61 (97) | unshare | (unsigned long unshare_flags) | __arm64_sys_unshare | false |
| 0x62 (98) | futex | (u32 *uaddr, int op, u32 val, const struct __kernel_timespec *utime, u32 *uaddr2, u32 val3) | __arm64_sys_futex | false |
| 0x63 (99) | set_robust_list | (struct robust_list_head *head, size_t len) | __arm64_sys_set_robust_list | false |
@@ -129,7 +129,7 @@
| 0x7e (126) | sched_get_priority_min | (int policy) | __arm64_sys_sched_get_priority_min | false |
| 0x7f (127) | sched_rr_get_interval | (pid_t pid, struct __kernel_timespec *interval) | __arm64_sys_sched_rr_get_interval | false |
| 0x80 (128) | restart_syscall | () | __arm64_sys_restart_syscall | false |
| 0x81 (129) | kill | (pid_t pid, int sig) | __arm64_sys_kill | dummy |
| 0x81 (129) | kill | (pid_t pid, int sig) | __arm64_sys_kill | dummy |
| 0x82 (130) | tkill | (pid_t pid, int sig) | __arm64_sys_tkill | true |
| 0x83 (131) | tgkill | (pid_t tgid, pid_t pid, int sig) | __arm64_sys_tgkill | false |
| 0x84 (132) | sigaltstack | (const stack_t *uss, stack_t *uoss) | __arm64_sys_sigaltstack | true |
@@ -142,7 +142,7 @@
| 0x8b (139) | rt_sigreturn | () | __arm64_sys_rt_sigreturn | true |
| 0x8c (140) | setpriority | (int which, int who, int niceval) | __arm64_sys_setpriority | false |
| 0x8d (141) | getpriority | (int which, int who) | __arm64_sys_getpriority | false |
| 0x8e (142) | reboot | (int magic1, int magic2, unsigned int cmd, void *arg) | __arm64_sys_reboot | false |
| 0x8e (142) | reboot | (int magic1, int magic2, unsigned int cmd, void *arg) | __arm64_sys_reboot | partially |
| 0x8f (143) | setregid | (gid_t rgid, gid_t egid) | __arm64_sys_setregid | false |
| 0x90 (144) | setgid | (gid_t gid) | __arm64_sys_setgid | false |
| 0x91 (145) | setreuid | (uid_t ruid, uid_t euid) | __arm64_sys_setreuid | false |

View File

@@ -57,6 +57,14 @@ pub trait Filesystem: Send + Sync {
/// Returns the instance ID for this FS.
fn id(&self) -> u64;
/// Flushes all pending data to the underlying storage device(s).
///
/// The default implementation is a no-op so that read-only filesystems do
/// not need to override it.
async fn sync(&self) -> Result<()> {
Ok(())
}
}
// A unique identifier for an inode across the entire VFS. A tuple of

View File

@@ -1,9 +1,12 @@
use crate::{
arch::{
ArchImpl,
arm64::boot::{
arch_init_secondary,
memory::{KERNEL_STACK_PG_ORDER, allocate_kstack_region},
arm64::{
boot::{
arch_init_secondary,
memory::{KERNEL_STACK_PG_ORDER, allocate_kstack_region},
},
psci::{PSCIEntry, PSCIMethod, boot_secondary_psci},
},
},
drivers::{fdt_prober::get_fdt, timer::now},
@@ -27,15 +30,12 @@ use libkernel::{
},
};
use log::{info, warn};
use psci::{PSCIEntry, PSCIMethod, boot_secondary_psci};
unsafe extern "C" {
static __boot_stack: u8;
static exception_return: u8;
}
mod psci;
#[derive(Debug)]
#[repr(C)]
struct SecondaryBootInfo {

View File

@@ -1,43 +0,0 @@
use core::arch::naked_asm;
use libkernel::memory::address::PA;
pub(super) struct PSCIEntry {
pub method: PSCIMethod,
pub cpu_on_id: Option<u32>,
}
pub(super) enum PSCIMethod {
Hvc,
Smc,
}
const CPU_ON_ID: u32 = 0xc400_0003;
pub(super) fn boot_secondary_psci(entry: PSCIEntry, core_id: usize, entry_fn: PA, ctx: PA) {
let method_id = entry.cpu_on_id.unwrap_or(CPU_ON_ID);
match entry.method {
PSCIMethod::Hvc => do_psci_hyp_call(
method_id,
core_id as _,
entry_fn.value() as _,
ctx.value() as _,
),
PSCIMethod::Smc => do_psci_smc_call(
method_id,
core_id as _,
entry_fn.value() as _,
ctx.value() as _,
),
};
}
#[unsafe(naked)]
pub extern "C" fn do_psci_hyp_call(id: u32, arg1: u64, arg2: u64, arg3: u64) -> i64 {
naked_asm!("hvc #0", "ret")
}
#[unsafe(naked)]
pub extern "C" fn do_psci_smc_call(id: u32, arg1: u64, arg2: u64, arg3: u64) -> i64 {
naked_asm!("smc #0", "ret")
}

View File

@@ -1,3 +1,4 @@
use crate::kernel::power::sys_reboot;
use crate::{
arch::{Arch, ArchImpl},
clock::gettime::sys_clock_gettime,
@@ -19,6 +20,7 @@ use crate::{
seek::sys_lseek,
splice::sys_sendfile,
stat::sys_fstat,
sync::sys_sync,
},
},
kernel::uname::sys_uname,
@@ -152,6 +154,7 @@ pub async fn handle_syscall() {
.await
}
0x50 => sys_fstat(arg1.into(), TUA::from_value(arg2 as _)).await,
0x51 => sys_sync().await,
0x5d => sys_exit(arg1 as _),
0x5e => sys_exit_group(arg1 as _),
0x60 => sys_set_tid_address(VA::from_value(arg1 as _)).await,
@@ -186,6 +189,7 @@ pub async fn handle_syscall() {
return;
}
0x8e => sys_reboot(arg1 as _, arg2 as _, arg3 as _, arg4 as _).await,
0x94 => {
sys_getresuid(
TUA::from_value(arg1 as _),

View File

@@ -34,6 +34,7 @@ mod exceptions;
mod fdt;
mod memory;
mod proc;
pub mod psci;
pub struct Aarch64 {}
impl CpuOps for Aarch64 {
@@ -108,6 +109,18 @@ impl Arch for Aarch64 {
proc::idle::create_idle_task()
}
fn power_off() -> ! {
// Try PSCI `SYSTEM_OFF` first (works on QEMU `-machine virt` and most
// real hardware that implements the PSCI interface).
const PSCI_SYSTEM_OFF: u32 = 0x8400_0008;
unsafe {
psci::do_psci_hyp_call(PSCI_SYSTEM_OFF, 0, 0, 0);
}
// Fallback: halt the CPU indefinitely.
Self::halt()
}
unsafe fn copy_from_user(
src: UA,
dst: *mut (),

50
src/arch/arm64/psci.rs Normal file
View File

@@ -0,0 +1,50 @@
use core::arch::naked_asm;
use libkernel::memory::address::PA;
pub struct PSCIEntry {
pub method: PSCIMethod,
pub cpu_on_id: Option<u32>,
}
pub enum PSCIMethod {
Hvc,
Smc,
}
const CPU_ON_ID: u32 = 0xc400_0003;
// Re-export the low-level PSCI helpers so other modules (e.g. `arch::arm64::mod`)
// can invoke them without repeating the `use` dance.
pub fn boot_secondary_psci(entry: PSCIEntry, core_id: usize, entry_fn: PA, ctx: PA) {
let method_id = entry.cpu_on_id.unwrap_or(CPU_ON_ID);
match entry.method {
PSCIMethod::Hvc => unsafe {
do_psci_hyp_call(
method_id,
core_id as _,
entry_fn.value() as _,
ctx.value() as _,
)
},
PSCIMethod::Smc => unsafe {
do_psci_smc_call(
method_id,
core_id as _,
entry_fn.value() as _,
ctx.value() as _,
)
},
};
}
#[unsafe(naked)]
pub unsafe extern "C" fn do_psci_hyp_call(id: u32, arg1: u64, arg2: u64, arg3: u64) -> i64 {
naked_asm!("hvc #0", "ret")
}
#[unsafe(naked)]
pub unsafe extern "C" fn do_psci_smc_call(id: u32, arg1: u64, arg2: u64, arg3: u64) -> i64 {
naked_asm!("smc #0", "ret")
}

View File

@@ -39,6 +39,9 @@ pub trait Arch: CpuOps + VirtualMemory {
/// Construct a new idle task.
fn create_idle_task() -> Task;
/// Powers off the machine. Implementations must never return.
fn power_off() -> !;
/// Call a user-specified signal handler in the current process.
fn do_signal(
sig: SigId,

View File

@@ -11,6 +11,7 @@ use reg::RegFile;
use crate::drivers::{DM, Driver};
use crate::sync::SpinLock;
use alloc::vec::Vec;
pub mod dir;
pub mod fops;
@@ -316,3 +317,22 @@ impl VFS {
}
pub static VFS: VFS = VFS::new();
impl VFS {
/// Flushes all mounted filesystems and their underlying block devices.
/// Any individual error is logged and ignored so that a single faulty
/// filesystem does not block the shutdown sequence.
pub async fn sync_all(&self) -> Result<()> {
let filesystems: Vec<_> = {
let state = self.state.lock_save_irq();
state.filesystems.values().cloned().collect()
};
for fs in filesystems {
// Ignore per-filesystem errors; best-effort
let _ = fs.sync().await;
}
Ok(())
}
}

View File

@@ -8,3 +8,4 @@ pub mod rw;
pub mod seek;
pub mod splice;
pub mod stat;
pub mod sync;

6
src/fs/syscalls/sync.rs Normal file
View File

@@ -0,0 +1,6 @@
use crate::fs::VFS;
pub async fn sys_sync() -> libkernel::error::Result<usize> {
VFS.sync_all().await?;
Ok(0)
}

View File

@@ -1,2 +1,3 @@
pub mod kpipe;
pub mod power;
pub mod uname;

34
src/kernel/power.rs Normal file
View File

@@ -0,0 +1,34 @@
use crate::{ArchImpl, arch::Arch};
use libkernel::error::{KernelError, Result};
pub async fn sys_reboot(magic: u32, magic2: u32, op: u32, _arg: usize) -> Result<usize> {
const LINUX_REBOOT_MAGIC1: u32 = 0xfee1_dead;
const LINUX_REBOOT_MAGIC2: u32 = 672274793;
const LINUX_REBOOT_MAGIC2A: u32 = 852072454;
const LINUX_REBOOT_MAGIC2B: u32 = 369367448;
const LINUX_REBOOT_MAGIC2C: u32 = 537993216;
// const LINUX_REBOOT_CMD_CAD_OFF: u32 = 0x0000_0000;
// const LINUX_REBOOT_CMD_CAD_ON: u32 = 0x89ab_cdef;
// const LINUX_REBOOT_CMD_HALT: u32 = 0xcdef_0123;
// const LINUX_REBOOT_CMD_KEXEC: u32 = 0x4558_4543;
const LINUX_REBOOT_CMD_POWER_OFF: u32 = 0x4321_fedc;
// const LINUX_REBOOT_CMD_RESTART: u32 = 0x0123_4567;
// const LINUX_REBOOT_CMD_RESTART2: u32 = 0xa1b2_c3d4;
// const LINUX_REBOOT_CMD_SW_SUSPEND: u32 = 0xd000_fce1;
if magic != LINUX_REBOOT_MAGIC1
|| (magic2 != LINUX_REBOOT_MAGIC2
&& magic2 != LINUX_REBOOT_MAGIC2A
&& magic2 != LINUX_REBOOT_MAGIC2B
&& magic2 != LINUX_REBOOT_MAGIC2C)
{
return Err(KernelError::InvalidValue);
}
match op {
LINUX_REBOOT_CMD_POWER_OFF => {
// User is supposed to sync first.
ArchImpl::power_off()
}
// TODO: Implement other reboot operations.
_ => Err(KernelError::InvalidValue),
}
}

View File

@@ -7,7 +7,7 @@ use alloc::{
vec,
vec::Vec,
};
use arch::ArchImpl;
use arch::{Arch, ArchImpl};
use core::panic::PanicInfo;
use drivers::{fdt_prober::get_fdt, fs::register_fs_drivers};
use fs::VFS;
@@ -56,7 +56,7 @@ fn on_panic(info: &PanicInfo) -> ! {
error!("Kernel panicked at unknown location: {panic_msg}");
}
ArchImpl::halt();
ArchImpl::power_off();
}
async fn launch_init(opts: KOptions) {