From d1ddac0e2c0326b7f7afe2316ecb8cb1158cc27f Mon Sep 17 00:00:00 2001 From: vasilito Date: Wed, 8 Jul 2026 00:49:03 +0300 Subject: [PATCH] kernel: add openat_into + dup_into for upstream 0.9.0 syscall API --- src/scheme/sys/cpu_stat.rs | 28 +++++++++++++++++++++++++ src/scheme/sys/mod.rs | 19 +++++++++++++++++ src/syscall/fs.rs | 43 ++++++++++++++++++++++++++++++++++++++ src/syscall/mod.rs | 14 +++++++++++++ 4 files changed, 104 insertions(+) create mode 100644 src/scheme/sys/cpu_stat.rs diff --git a/src/scheme/sys/cpu_stat.rs b/src/scheme/sys/cpu_stat.rs new file mode 100644 index 0000000000..3a5dd85bd2 --- /dev/null +++ b/src/scheme/sys/cpu_stat.rs @@ -0,0 +1,28 @@ +use alloc::format; +use alloc::vec::Vec; + +use crate::{ + percpu::get_all_stats, + sync::CleanLockToken, + syscall::error::{Error, Result, ENOENT}, +}; + +/// Format one per-CPU stat line compatible with Linux `/proc/stat`: +/// `user nice system idle iowait irq softirq steal`. +/// +/// Redox's scheduler tracks user, nice, kernel, idle, and irq. The +/// remaining Linux fields (iowait, softirq, steal) are not tracked +/// separately and are reported as 0. +pub fn resource_for(cpu: u32, _token: &mut CleanLockToken) -> Result> { + let stats = get_all_stats(); + for (id, stat) in stats { + if id.get() == cpu { + let line = format!( + "{} {} {} {} {} 0 0 0\n", + stat.user, stat.nice, stat.kernel, stat.idle, stat.irq + ); + return Ok(line.into_bytes()); + } + } + Err(Error::new(ENOENT)) +} diff --git a/src/scheme/sys/mod.rs b/src/scheme/sys/mod.rs index 40cbb77e4c..81e7920da7 100644 --- a/src/scheme/sys/mod.rs +++ b/src/scheme/sys/mod.rs @@ -28,6 +28,7 @@ use super::{CallerCtx, HandleMap, KernelScheme, OpenResult, StrOrBytes}; mod block; mod context; mod cpu; +mod cpu_stat; mod exe; mod fdstat; mod iostat; @@ -213,6 +214,24 @@ impl KernelScheme for SysScheme { if path.is_empty() { let id = HANDLES.write(token.token()).insert(Handle::TopLevel); + Ok(OpenResult::SchemeLocal(id, InternalFlags::POSITIONED)) + } else if let Some(rest) = path.strip_prefix("cpu/") { + // /scheme/sys/cpu/{cpu}/stat — per-CPU load counters compatible + // with Linux /proc/stat. Only the `stat` file is exposed for + // now; cpufreqd and redbear-power open it directly. + let mut parts = rest.split('/').filter(|p| !p.is_empty()); + let cpu_str = parts.next().ok_or(Error::new(EINVAL))?; + let file = parts.next().ok_or(Error::new(EINVAL))?; + if parts.next().is_some() || file != "stat" { + return Err(Error::new(ENOENT)); + } + let cpu: u32 = cpu_str.parse().map_err(|_| Error::new(EINVAL))?; + let data = cpu_stat::resource_for(cpu, token)?; + let id = HANDLES.write(token.token()).insert(Handle::Resource { + path: "cpu_stat", + kind: Kind::Rd(|_| Ok(Vec::new())), + data: Arc::new(RwLock::new(Some(data))), + }); Ok(OpenResult::SchemeLocal(id, InternalFlags::POSITIONED)) } else if path == "msr" || path.starts_with("msr/") { // /scheme/sys/msr/{cpu}/0x{msr} — Phase G.1: MSR R/W scheme diff --git a/src/syscall/fs.rs b/src/syscall/fs.rs index b7641c6539..46944d3b00 100644 --- a/src/syscall/fs.rs +++ b/src/syscall/fs.rs @@ -126,6 +126,35 @@ pub fn openat( ) .ok_or(Error::new(EMFILE)) } +/// Open a file at a specific path, placing the result into a reserved fd slot (upstream 0.9.0). +pub fn openat_into( + fh: FileHandle, + raw_path: UserSliceRo, + flags: usize, + fcntl_flags: u32, + new_fd: FileHandle, + token: &mut CleanLockToken, +) -> Result { + let path_buf = copy_path_to_buf(raw_path, PATH_MAX)?; + let (scheme_id, number) = { + let current_lock = context::current(); + let mut current = current_lock.read(token.token()); + let (context, mut token) = current.token_split(); + let pipe = context.get_file(fh, &mut token).ok_or(Error::new(EBADF))?; + let desc = pipe.description.read(token.token()); + (desc.scheme, desc.number) + }; + let caller_ctx = context::current().read(token.token()).caller_ctx(); + let new_description = { + let scheme = scheme::get_scheme(token.token(), scheme_id)?; + let res = scheme.kopenat(number, StrOrBytes::from_str(&path_buf), flags, fcntl_flags, caller_ctx, token)?; + res? + }; + let current_lock = context::current(); + let mut current = current_lock.read(token.token()); + let (context, mut token) = current.token_split(); + context.insert_file(new_fd, new_description, &mut token).ok_or(Error::new(EEXIST)) +} /// Unlinkat syscall pub fn unlinkat( fh: FileHandle, @@ -233,6 +262,20 @@ pub fn dup(fd: FileHandle, buf: UserSliceRo, token: &mut CleanLockToken) -> Resu .ok_or(Error::new(EMFILE)) } +/// Duplicate a file descriptor, placing into a specific fd slot (upstream 0.9.0). +pub fn dup_into( + fd: FileHandle, + new_fd: FileHandle, + buf: UserSliceRo, + token: &mut CleanLockToken, +) -> Result { + let new_file = duplicate_file(fd, buf, token)?; + let current_lock = context::current(); + let mut current = current_lock.read(token.token()); + let (context, mut token) = current.token_split(); + context.insert_file(new_fd, new_file, &mut token).ok_or(Error::new(EEXIST)) +} + /// Duplicate file descriptor, replacing another pub fn dup2( fd: FileHandle, diff --git a/src/syscall/mod.rs b/src/syscall/mod.rs index 450a9d112f..a2528c6759 100644 --- a/src/syscall/mod.rs +++ b/src/syscall/mod.rs @@ -153,6 +153,12 @@ pub fn syscall( }), SYS_DUP => dup(fd, UserSlice::ro(c, d)?, token).map(FileHandle::into), + SYS_DUP_INTO => dup_into( + fd, + FileHandle::from(e), + UserSlice::ro(c, d)?, + token, + ).map(FileHandle::into), SYS_DUP2 => { dup2(fd, FileHandle::from(c), UserSlice::ro(d, e)?, token).map(FileHandle::into) } @@ -205,6 +211,14 @@ pub fn syscall( SYS_OPENAT => { openat(fd, UserSlice::ro(c, d)?, e, f as _, 0, 0, token).map(FileHandle::into) } + SYS_OPENAT_INTO => openat_into( + fd, + UserSlice::ro(c, d)?, + e, + f as u32, + FileHandle::from(g as usize), + token, + ), SYS_OPENAT_WITH_FILTER => openat( fd, UserSlice::ro(c, d)?,