diff --git a/etc/syscalls_linux_aarch64.md b/etc/syscalls_linux_aarch64.md index 3410ae5..d4a83bd 100644 --- a/etc/syscalls_linux_aarch64.md +++ b/etc/syscalls_linux_aarch64.md @@ -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 | diff --git a/libkernel/src/fs/mod.rs b/libkernel/src/fs/mod.rs index 76ec708..320f76b 100644 --- a/libkernel/src/fs/mod.rs +++ b/libkernel/src/fs/mod.rs @@ -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 diff --git a/src/arch/arm64/boot/secondary.rs b/src/arch/arm64/boot/secondary.rs index ad18d42..611f602 100644 --- a/src/arch/arm64/boot/secondary.rs +++ b/src/arch/arm64/boot/secondary.rs @@ -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 { diff --git a/src/arch/arm64/boot/secondary/psci.rs b/src/arch/arm64/boot/secondary/psci.rs deleted file mode 100644 index 1897781..0000000 --- a/src/arch/arm64/boot/secondary/psci.rs +++ /dev/null @@ -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, -} - -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") -} diff --git a/src/arch/arm64/exceptions/syscall.rs b/src/arch/arm64/exceptions/syscall.rs index 101c19e..7be3fdf 100644 --- a/src/arch/arm64/exceptions/syscall.rs +++ b/src/arch/arm64/exceptions/syscall.rs @@ -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 _), diff --git a/src/arch/arm64/mod.rs b/src/arch/arm64/mod.rs index 591038b..25adea3 100644 --- a/src/arch/arm64/mod.rs +++ b/src/arch/arm64/mod.rs @@ -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 (), diff --git a/src/arch/arm64/psci.rs b/src/arch/arm64/psci.rs new file mode 100644 index 0000000..e64d82e --- /dev/null +++ b/src/arch/arm64/psci.rs @@ -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, +} + +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") +} diff --git a/src/arch/mod.rs b/src/arch/mod.rs index a668f86..8886252 100644 --- a/src/arch/mod.rs +++ b/src/arch/mod.rs @@ -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, diff --git a/src/fs/mod.rs b/src/fs/mod.rs index f91b283..d7ac9c3 100644 --- a/src/fs/mod.rs +++ b/src/fs/mod.rs @@ -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(()) + } +} diff --git a/src/fs/syscalls/mod.rs b/src/fs/syscalls/mod.rs index a50287b..85412d8 100644 --- a/src/fs/syscalls/mod.rs +++ b/src/fs/syscalls/mod.rs @@ -8,3 +8,4 @@ pub mod rw; pub mod seek; pub mod splice; pub mod stat; +pub mod sync; diff --git a/src/fs/syscalls/sync.rs b/src/fs/syscalls/sync.rs new file mode 100644 index 0000000..47dc60e --- /dev/null +++ b/src/fs/syscalls/sync.rs @@ -0,0 +1,6 @@ +use crate::fs::VFS; + +pub async fn sys_sync() -> libkernel::error::Result { + VFS.sync_all().await?; + Ok(0) +} diff --git a/src/kernel/mod.rs b/src/kernel/mod.rs index 2c8c277..32677b6 100644 --- a/src/kernel/mod.rs +++ b/src/kernel/mod.rs @@ -1,2 +1,3 @@ pub mod kpipe; +pub mod power; pub mod uname; diff --git a/src/kernel/power.rs b/src/kernel/power.rs new file mode 100644 index 0000000..72d9d31 --- /dev/null +++ b/src/kernel/power.rs @@ -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 { + 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), + } +} diff --git a/src/main.rs b/src/main.rs index 2d9fc1c..6e652ef 100644 --- a/src/main.rs +++ b/src/main.rs @@ -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) {