From aca5c0db7915d313190d451df6322ef9768856eb Mon Sep 17 00:00:00 2001 From: Ashwin Naren Date: Wed, 14 Jan 2026 22:17:54 -0800 Subject: [PATCH] implement mincore --- etc/syscalls_linux_aarch64.md | 4 +- src/arch/arm64/exceptions/syscall.rs | 2 + src/memory/mincore.rs | 66 ++++++++++++++++++++++++++++ src/memory/mod.rs | 1 + usertest/src/main.rs | 46 ++++++++++++++++++- 5 files changed, 116 insertions(+), 3 deletions(-) create mode 100644 src/memory/mincore.rs diff --git a/etc/syscalls_linux_aarch64.md b/etc/syscalls_linux_aarch64.md index 0f8a1ab7..d4f3f621 100644 --- a/etc/syscalls_linux_aarch64.md +++ b/etc/syscalls_linux_aarch64.md @@ -98,7 +98,7 @@ | 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 | | 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 | +| 0x62 (98) | futex | (u32 *uaddr, int op, u32 val, const struct __kernel_timespec *utime, u32 *uaddr2, u32 val3) | __arm64_sys_futex | true | | 0x63 (99) | set_robust_list | (struct robust_list_head *head, size_t len) | __arm64_sys_set_robust_list | true | | 0x64 (100) | get_robust_list | (int pid, struct robust_list_head **head_ptr, size_t *len_ptr) | __arm64_sys_get_robust_list | false | | 0x65 (101) | nanosleep | (struct __kernel_timespec *rqtp, struct __kernel_timespec *rmtp) | __arm64_sys_nanosleep | true | @@ -232,7 +232,7 @@ | 0xe5 (229) | munlock | (unsigned long start, size_t len) | __arm64_sys_munlock | false | | 0xe6 (230) | mlockall | (int flags) | __arm64_sys_mlockall | false | | 0xe7 (231) | munlockall | () | __arm64_sys_munlockall | false | -| 0xe8 (232) | mincore | (unsigned long start, size_t len, unsigned char *vec) | __arm64_sys_mincore | false | +| 0xe8 (232) | mincore | (unsigned long start, size_t len, unsigned char *vec) | __arm64_sys_mincore | true | | 0xe9 (233) | madvise | (unsigned long start, size_t len_in, int behavior) | __arm64_sys_madvise | noop | | 0xea (234) | remap_file_pages | (unsigned long start, unsigned long size, unsigned long prot, unsigned long pgoff, unsigned long flags) | __arm64_sys_remap_file_pages | false | | 0xeb (235) | mbind | (unsigned long start, unsigned long len, unsigned long mode, const unsigned long *nmask, unsigned long maxnode, unsigned int flags) | __arm64_sys_mbind | false | diff --git a/src/arch/arm64/exceptions/syscall.rs b/src/arch/arm64/exceptions/syscall.rs index a6e339f0..b326097d 100644 --- a/src/arch/arm64/exceptions/syscall.rs +++ b/src/arch/arm64/exceptions/syscall.rs @@ -45,6 +45,7 @@ use crate::{ kernel::{power::sys_reboot, rand::sys_getrandom, sysinfo::sys_sysinfo, uname::sys_uname}, memory::{ brk::sys_brk, + mincore::sys_mincore, mmap::{sys_mmap, sys_mprotect, sys_munmap}, process_vm::sys_process_vm_readv, }, @@ -511,6 +512,7 @@ pub async fn handle_syscall() { 0xde => sys_mmap(arg1, arg2, arg3, arg4, arg5.into(), arg6).await, 0xdf => Ok(0), // fadvise64_64 is a no-op 0xe2 => sys_mprotect(VA::from_value(arg1 as _), arg2 as _, arg3 as _), + 0xe8 => sys_mincore(arg1, arg2 as _, TUA::from_value(arg3 as _)).await, 0xe9 => Ok(0), // sys_madvise is a no-op 0x104 => { sys_wait4( diff --git a/src/memory/mincore.rs b/src/memory/mincore.rs new file mode 100644 index 00000000..51a69d78 --- /dev/null +++ b/src/memory/mincore.rs @@ -0,0 +1,66 @@ +use alloc::vec; +use alloc::vec::Vec; + +use crate::memory::uaccess::copy_to_user_slice; +use crate::sched::current::current_task; +use libkernel::memory::region::VirtMemoryRegion; +use libkernel::{ + UserAddressSpace, + error::{KernelError, Result}, + memory::PAGE_SHIFT, + memory::address::{UA, VA}, +}; + +pub async fn sys_mincore(start: u64, len: usize, vec: UA) -> Result { + // addr must be a multiple of the system page size + // len must be > 0 + let start_va = VA::from_value(start as usize); + if !start_va.is_page_aligned() { + return Err(KernelError::InvalidValue); + } + + if len == 0 { + return Err(KernelError::InvalidValue); + } + + let region = VirtMemoryRegion::new(start_va, len) + .to_mappable_region() + .region(); + if region.size() == 0 { + return Err(KernelError::InvalidValue); + } + + // Vector length must be number of pages covering the region + let pages = region.size() >> PAGE_SHIFT; + let mut buf: Vec = vec![0; pages]; + + { + let task = current_task(); + let mut vm_guard = task.vm.lock_save_irq(); + let mm = vm_guard.mm_mut(); + + // Validate the entire region is covered by VMAs + for va in region.iter_pages() { + if mm.find_vma(va).is_none() { + return Err(KernelError::NoMemory); + } + } + + let as_ref = mm.address_space_mut(); + + for (i, va) in region.iter_pages().enumerate() { + let resident = as_ref.translate(va).is_some(); + if resident { + buf[i] |= 1; + } else { + buf[i] &= !1; + } + } + } + + copy_to_user_slice(&buf, vec) + .await + .map_err(|_| KernelError::Fault)?; + + Ok(0) +} diff --git a/src/memory/mod.rs b/src/memory/mod.rs index 0222da1a..7bf8d08e 100644 --- a/src/memory/mod.rs +++ b/src/memory/mod.rs @@ -10,6 +10,7 @@ use libkernel::memory::{ pub mod brk; pub mod fault; +pub mod mincore; pub mod mmap; pub mod page; pub mod process_vm; diff --git a/usertest/src/main.rs b/usertest/src/main.rs index 51c1b9c1..bd906aef 100644 --- a/usertest/src/main.rs +++ b/usertest/src/main.rs @@ -151,6 +151,50 @@ fn test_thread_with_name() { register_test!(test_thread_with_name, "Testing thread with name"); +fn test_mincore() { + use std::ptr; + + unsafe { + let page_size = libc::sysconf(libc::_SC_PAGESIZE) as usize; + assert!(page_size > 0); + + // Map exactly one page, read-write, anonymous private + let addr = libc::mmap( + ptr::null_mut(), + page_size, + libc::PROT_READ | libc::PROT_WRITE, + libc::MAP_PRIVATE | libc::MAP_ANONYMOUS, + -1, + 0, + ); + if addr == libc::MAP_FAILED { + panic!("mmap failed: {}", std::io::Error::last_os_error()); + } + + // Touch the page to fault it in + ptr::write(addr as *mut u8, 42); + + let mut vec_byte: u8 = 0; + let ret = libc::mincore(addr as *mut _, page_size, &mut vec_byte as *mut u8); + if ret != 0 { + let err = std::io::Error::last_os_error(); + panic!("mincore failed: {}", err); + } + // LSB set indicates resident + assert!( + vec_byte & 0x1 == 0x1, + "Expected page to be resident, vec={:02x}", + vec_byte + ); + + // Cleanup + let rc = libc::munmap(addr, page_size); + assert_eq!(rc, 0, "munmap failed: {}", std::io::Error::last_os_error()); + } +} + +register_test!(test_mincore, "Testing mincore syscall"); + fn run_test(test_fn: fn()) { // Fork a new process to run the test unsafe { @@ -167,7 +211,7 @@ fn run_test(test_fn: fn()) { libc::waitpid(pid, &mut status, 0); if !libc::WIFEXITED(status) || libc::WEXITSTATUS(status) != 0 { panic!( - "Test failed in child process: {}", + "Test failed in child process: {} (this might be incorrect)", std::io::Error::last_os_error() ); }