From a5ae1ff3b05a37e7bcf93d796ae3377927576e9b Mon Sep 17 00:00:00 2001 From: R Aadarsh Date: Sun, 12 Jul 2026 12:56:51 +0530 Subject: [PATCH 01/10] Use `Mutex` for round robin index allocation --- src/memory/mod.rs | 38 ++++++++++++++++++++++---------------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/src/memory/mod.rs b/src/memory/mod.rs index c6f4e99216..f1a50524cf 100644 --- a/src/memory/mod.rs +++ b/src/memory/mod.rs @@ -74,30 +74,36 @@ pub fn total_frames() -> usize { /// Allocate a range of frames pub fn allocate_p2frame(order: u32) -> Option { - let initial_index = get_round_robin_index(); - let mut index = initial_index; + static RR_INDEX: Mutex = Mutex::new(0); + let len = FREE_LISTS.get().unwrap().len(); - loop { - if let Some(frame) = allocate_p2frame_complex(order, (), None, order, index).map(|(f, _)| f) - { - return Some(frame); - } - index = get_round_robin_index(); - if index == initial_index { - return None; + if len == 1 { + return allocate_p2frame_complex(order, (), None, order, 0).map(|e| e.0); + } + + let index = { + let mut lock = RR_INDEX.lock(); + let index = + usize::try_from(*lock).expect("Maximum number of memory regions supported is 128"); + *lock = u8::try_from((index + 1) % len).expect("Maximum number of memory regions is 128"); + index + }; + for i in index..len { + if let Some(frame) = allocate_p2frame_complex(order, (), None, order, i) { + return Some(frame.0); } } + for i in 0..index { + if let Some(frame) = allocate_p2frame_complex(order, (), None, order, i) { + return Some(frame.0); + } + } + None } pub fn allocate_frame() -> Option { allocate_p2frame(0) } -fn get_round_robin_index() -> usize { - static CURRENT_INDEX: AtomicUsize = AtomicUsize::new(0); - let len = FREE_LISTS.get().unwrap().len(); - CURRENT_INDEX.fetch_add(1, Ordering::Relaxed) % len -} - // TODO: Flags, strategy pub fn allocate_p2frame_complex( _req_order: u32, From ee4a5602a638ebe4f5b26d820b6d55c820d26147 Mon Sep 17 00:00:00 2001 From: Wildan M Date: Thu, 16 Jul 2026 01:27:47 +0700 Subject: [PATCH 02/10] Implement make install and test targets --- .gitignore | 2 ++ .gitlab-ci.yml | 47 ++++++++++++------------------------------- Makefile | 40 ++++++++++++++++++++++++++++++++---- src/context/mod.rs | 2 +- src/context/switch.rs | 4 +--- 5 files changed, 53 insertions(+), 42 deletions(-) diff --git a/.gitignore b/.gitignore index 7774614018..c72df02094 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ target +/build /config.toml +/sysroot .gitlab-ci-local/ diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 7d82652f70..a8d38d1381 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,8 +1,3 @@ -image: "redoxos/redoxer:latest" - -variables: - GIT_SUBMODULE_STRATEGY: recursive - workflow: rules: - if: '$CI_PROJECT_NAMESPACE == "redox-os"' @@ -12,14 +7,12 @@ stages: - build - cross-build - test - - other-features # TODO: benchmarks and profiling (maybe manually enabled for relevant MRs)? x86_64: stage: build script: - - mkdir -p target/${ARCH} - - redoxer env make BUILD=target/${ARCH} + - redoxer env make variables: ARCH: "x86_64" @@ -27,64 +20,50 @@ aarch64: stage: cross-build image: "redoxos/redoxer:aarch64" script: - - mkdir -p target/${ARCH} - - redoxer env make BUILD=target/${ARCH} + - redoxer env make variables: ARCH: "aarch64" i586: stage: cross-build script: - - mkdir -p target/${ARCH} - - TARGET=${ARCH}-unknown-redox redoxer env make BUILD=target/${ARCH} + - TARGET=${ARCH}-unknown-redox redoxer env make variables: ARCH: "i586" riscv64gc: stage: cross-build script: - - mkdir -p target/${ARCH} - - TARGET=${ARCH}-unknown-redox redoxer env make BUILD=target/${ARCH} + - TARGET=${ARCH}-unknown-redox redoxer env make variables: ARCH: "riscv64gc" fmt: stage: build script: - - rustup component add rustfmt - - rustfmt --check + - redoxer env cargo fmt --check x86_64:boot: stage: test needs: [x86_64] script: - - mkdir -p target/${ARCH} - export COOKBOOK_SOURCE_IDENT=$CI_COMMIT_SHA - - redoxer env make BUILD=target/${ARCH} - - timeout -s KILL 9m redoxer exec --folder target/${ARCH}/:/usr/lib/boot uname -a - variables: - ARCH: "x86_64" + - redoxer env make test x86_64:relibc: stage: test needs: [x86_64] + # TODO: remove + allow_failure: true script: - - redoxer pkg relibc-tests-bins - export COOKBOOK_SOURCE_IDENT=$CI_COMMIT_SHA - - mkdir -p target/${TARGET}/sysroot/{usr/lib/boot,root} target/${TARGET}/root - - redoxer env make BUILD=target/${TARGET}/sysroot/usr/lib/boot - - (cd target/${TARGET}/sysroot && mv home/user/relibc-tests/* root/) - - timeout -s KILL 9m redoxer exec --folder target/${TARGET}/sysroot/:/ make run - # It is fine if failing sometimes - allow_failure: true - variables: - TARGET: "x86_64-unknown-redox" + - timeout -s KILL 9m redoxer env make test-relibc -profiling-compile: - stage: other-features - allow_failure: true +x86_64:profiling: + stage: build + needs: [x86_64] script: - make check + - make check variables: ARCH: "x86_64" KERNEL_CHECK_FEATURES: profiling diff --git a/Makefile b/Makefile index a089f8c350..698bc2340a 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,6 @@ .PHONY: all check SOURCE:=$(dir $(realpath $(lastword $(MAKEFILE_LIST)))) -BUILD?=$(CURDIR) export RUST_TARGET_PATH=$(SOURCE)/targets ifeq ($(TARGET),) @@ -10,6 +9,9 @@ else ARCH?=$(shell echo "$(TARGET)" | cut -d - -f1) endif +BUILD?=$(CURDIR)/build/$(ARCH) +DESTDIR?=./sysroot + ifeq ($(ARCH),riscv64gc) override ARCH:=riscv64 GNU_TARGET=riscv64-unknown-redox @@ -20,6 +22,7 @@ else GNU_TARGET=$(ARCH)-unknown-redox endif +OBJCOPY?=$(GNU_TARGET)-objcopy all: $(BUILD)/kernel $(BUILD)/kernel.sym @@ -30,7 +33,10 @@ TARGET_SPEC=$(RUST_TARGET_PATH)/$(ARCH)-unknown-kernel.json KERNEL_CARGO_FEATURES?= -$(BUILD)/kernel.all: $(LD_SCRIPT) $(LOCKFILE) $(MANIFEST) $(TARGET_SPEC) $(shell find $(SOURCE) -name "*.rs" -type f) +$(BUILD): + mkdir -p "$@" + +$(BUILD)/kernel.all: $(LD_SCRIPT) $(LOCKFILE) $(MANIFEST) $(TARGET_SPEC) $(shell find $(SOURCE) -name "*.rs" -type f) | $(BUILD) cargo rustc \ --bin kernel \ --manifest-path "$(MANIFEST)" \ @@ -45,13 +51,13 @@ $(BUILD)/kernel.all: $(LD_SCRIPT) $(LOCKFILE) $(MANIFEST) $(TARGET_SPEC) $(shell --emit link="$(BUILD)/kernel.all" $(BUILD)/kernel.sym: $(BUILD)/kernel.all - $(GNU_TARGET)-objcopy \ + $(OBJCOPY) \ --only-keep-debug \ "$(BUILD)/kernel.all" \ "$(BUILD)/kernel.sym" $(BUILD)/kernel: $(BUILD)/kernel.all - $(GNU_TARGET)-objcopy \ + $(OBJCOPY) \ --strip-debug \ "$(BUILD)/kernel.all" \ "$(BUILD)/kernel" @@ -66,3 +72,29 @@ check: -Z build-std=core,alloc -Zbuild-std-features=compiler-builtins-mem \ -Z json-target-spec \ --features=$(KERNEL_CHECK_FEATURES) + +clean: + rm -rf build sysroot target config.toml + +install: all + @mkdir -pv "$(DESTDIR)/usr/lib/boot/" + cp -v $(BUILD)/kernel.all "$(DESTDIR)/usr/lib/boot/" + cp -v $(BUILD)/kernel.sym "$(DESTDIR)/usr/lib/boot/" + cp -v $(BUILD)/kernel "$(DESTDIR)/usr/lib/boot/" + +# test if booting +# to ensure it's using this kernel, set COOKBOOK_SOURCE_IDENT env before build +test: all + $(MAKE) install + REDOXER_SYSROOT=$(DESTDIR) redoxer exec uname -a + +# test with interactive gui +test-gui: all + $(MAKE) install + REDOXER_SYSROOT=$(DESTDIR) redoxer exec --gui ion + +# test with relibc tests +test-relibc: all + $(MAKE) install + REDOXER_SYSROOT=$(DESTDIR) redoxer pkg relibc-tests-bins + REDOXER_SYSROOT=$(DESTDIR) redoxer exec relibc-tests-runner diff --git a/src/context/mod.rs b/src/context/mod.rs index 1518dc3367..f5a8a3a266 100644 --- a/src/context/mod.rs +++ b/src/context/mod.rs @@ -83,7 +83,7 @@ static IDLE_CONTEXTS: Mutex> = Mutex::new(VecDeque: pub struct RunContextData { // queue: VecDeque, queue: BTreeMap<(u64, Reverse, u32), (u64, u64, WeakContextRef)>, // ((vd, rem_slice, ctxt_id), (vtime, weight, context)) - timers: BTreeSet<(u128, WeakContextRef)>, // (wake, context) + timers: BTreeSet<(u128, WeakContextRef)>, // (wake, context) count: usize, v: u64, total_weight: u64, diff --git a/src/context/switch.rs b/src/context/switch.rs index 5de01b01ef..2996a86871 100644 --- a/src/context/switch.rs +++ b/src/context/switch.rs @@ -389,9 +389,7 @@ pub fn switch(token: &mut CleanLockToken) -> SwitchResult { } } -fn wakeup_contexts( - token: &mut CleanLockToken, -) -> SmallVec<[WeakContextRef; 16]> { +fn wakeup_contexts(token: &mut CleanLockToken) -> SmallVec<[WeakContextRef; 16]> { // TODO: Optimise this somehow let mut wakeups = SmallVec::new(); let current_context = context::current(); From f5fe18b23c95d5b615e281cb97d95b4a66f1e371 Mon Sep 17 00:00:00 2001 From: Akshit Gaur Date: Thu, 16 Jul 2026 13:48:54 +0000 Subject: [PATCH 03/10] Finish the work on separating timers --- src/context/context.rs | 1 - src/context/mod.rs | 34 +++++++++++++++++++++++++++++-- src/context/switch.rs | 41 ++++++++++++++++++++++++++++++-------- src/scheme/proc.rs | 20 ++++++++++--------- src/scheme/user.rs | 4 ++-- src/sync/wait_condition.rs | 6 +++--- src/syscall/futex.rs | 4 ++-- 7 files changed, 83 insertions(+), 27 deletions(-) diff --git a/src/context/context.rs b/src/context/context.rs index fbb20c4c7c..8bd44177e0 100644 --- a/src/context/context.rs +++ b/src/context/context.rs @@ -279,7 +279,6 @@ impl Context { pub fn unblock_no_ipi(&mut self) -> bool { if self.status.is_soft_blocked() { self.status = Status::Runnable; - self.wake = None; self.status_reason = ""; true diff --git a/src/context/mod.rs b/src/context/mod.rs index f5a8a3a266..7eb3e9199b 100644 --- a/src/context/mod.rs +++ b/src/context/mod.rs @@ -9,13 +9,17 @@ use alloc::{ use core::{cmp::Reverse, num::NonZeroUsize, ops::Deref}; use crate::{ - context::memory::AddrSpaceWrapper, + context::{ + memory::AddrSpaceWrapper, + switch::{BASE_SLICE_TICKS, NANOS_PER_TICK, SCALE, SCHED_PRIO_TO_WEIGHT, TICK_INTERVAL}, + }, cpu_set::LogicalCpuSet, + ipi::{ipi, IpiKind, IpiTarget}, memory::{RmmA, RmmArch, TableKind}, percpu::PercpuBlock, sync::{ ArcRwLockWriteGuard, CleanLockToken, LockToken, Mutex, MutexGuard, RwLock, RwLockReadGuard, - RwLockWriteGuard, L0, L1, L2, L4, + RwLockWriteGuard, L0, L1, L2, L3, L4, }, syscall::error::Result, }; @@ -135,6 +139,32 @@ pub fn run_contexts_try(token: LockToken<'_, L0>) -> Option, token: &mut LockToken<'_, L3>) -> bool { + let was_blocked = { + let mut guard = context_lock.write(token.token()); + if !guard.unblock_no_ipi() { + return false; + } + + true + }; + + let percpu: &PercpuBlock = PercpuBlock::current(); + + let weak = WeakContextRef(Arc::downgrade(context_lock)); + let cpu_id = context_lock.write(token.token()).cpu_id; + + percpu.switch_internals.wakeup_list.borrow_mut().push(weak); + + if let Some(cpu_id) = cpu_id + && cpu_id != crate::cpu_id() + { + ipi(IpiKind::Wakeup, IpiTarget::Other); + } + + true +} + pub fn init(token: &mut CleanLockToken) { let owner = None; // kmain not owned by any fd let mut context = Context::new(owner).expect("failed to create kmain context"); diff --git a/src/context/switch.rs b/src/context/switch.rs index 2996a86871..915921a29f 100644 --- a/src/context/switch.rs +++ b/src/context/switch.rs @@ -13,7 +13,7 @@ use crate::{ percpu::PercpuBlock, sync::{ArcRwLockWriteGuard, CleanLockToken, L4}, }; -use alloc::sync::Arc; +use alloc::{sync::Arc, vec::Vec}; use core::{ cell::{Cell, RefCell}, cmp::Reverse, @@ -32,16 +32,16 @@ enum UpdateResult { } // A simple geometric series where value[i] ~= value[i + 1] * 1.25 -const SCHED_PRIO_TO_WEIGHT: [usize; 40] = [ +pub const SCHED_PRIO_TO_WEIGHT: [usize; 40] = [ 88761, 71755, 56483, 46273, 36291, 29154, 23254, 18705, 14949, 11916, 9548, 7620, 6100, 4904, 3906, 3121, 2501, 1991, 1586, 1277, 1024, 820, 655, 526, 423, 335, 272, 215, 172, 137, 110, 87, 70, 56, 45, 36, 29, 23, 18, 15, ]; -const SCALE: u128 = 1 << 40; -const TICK_INTERVAL: u64 = 3; // Approx 6.75 ms -const BASE_SLICE_TICKS: u64 = TICK_INTERVAL * 3; // Approx 20.25 ms -const NANOS_PER_TICK: u128 = 2_250_000; // 2.25 ms +pub const SCALE: u128 = 1 << 40; +pub const TICK_INTERVAL: u64 = 3; // Approx 6.75 ms +pub const BASE_SLICE_TICKS: u64 = TICK_INTERVAL * 3; // Approx 20.25 ms +pub const NANOS_PER_TICK: u128 = 2_250_000; // 2.25 ms /// Determines if a given context is eligible to be scheduled on a given CPU (in /// principle, the current CPU). @@ -190,6 +190,8 @@ pub fn switch(token: &mut CleanLockToken) -> SwitchResult { let mut wakeups = wakeup_contexts(token); let mut push_idle: SmallVec<[WeakContextRef; 16]> = SmallVec::new(); + // These timers coukd have expired + let mut timers: SmallVec<[(u128, WeakContextRef); 16]> = SmallVec::new(); if let Some(mut run_contexts) = run_contexts_try(token.token()) { // Pop Timers while let Some((wake, _)) = run_contexts.timers.first() { @@ -197,12 +199,31 @@ pub fn switch(token: &mut CleanLockToken) -> SwitchResult { break; } - if let Some((_, context_ref)) = run_contexts.timers.pop_first() { - wakeups.push(context_ref); + if let Some(entry) = run_contexts.timers.pop_first() { + timers.push(entry); } } } + for (wake, context_ref) in timers { + let Some(context_lock) = context_ref.upgrade() else { + continue; + }; + + let guard = context_lock.read(token.token()); + if guard.status.is_soft_blocked() && guard.wake == Some(wake) { + wakeups.push(context_ref); + } + } + + // Drain from percpu + let mut percpu_wake = percpu.switch_internals.wakeup_list.replace(Vec::new()); + if percpu_wake.len() > 0 { + for context_ref in percpu_wake.iter() { + wakeups.push(context_ref.clone()); + } + } + if wakeups.len() > 0 { let mut run_contexts = run_contexts(token.token()); for context_ref in wakeups { @@ -697,6 +718,9 @@ pub struct ContextSwitchPercpu { /// The idle process. idle_ctxt: RefCell>>, pub(crate) being_sigkilled: Cell, + + // wakeups + pub(crate) wakeup_list: RefCell>, } impl ContextSwitchPercpu { @@ -708,6 +732,7 @@ impl ContextSwitchPercpu { current_ctxt: RefCell::new(None), idle_ctxt: RefCell::new(None), being_sigkilled: Cell::new(false), + wakeup_list: RefCell::new(Vec::new()), #[cfg(feature = "profiling")] current_dbg_id: core::sync::atomic::AtomicU32::new(!0), diff --git a/src/scheme/proc.rs b/src/scheme/proc.rs index e2bb2541ac..c9c8fea3f9 100644 --- a/src/scheme/proc.rs +++ b/src/scheme/proc.rs @@ -4,7 +4,7 @@ use crate::{ context::{HardBlockedReason, LockedFdTbl, SignalState}, file::InternalFlags, memory::{handle_notify_files, AddrSpace, AddrSpaceWrapper, Grant, PageSpan, UnmapVec}, - Context, ContextLock, Status, + unblock_context, Context, ContextLock, Status, }, memory::{Page, VirtualAddress, PAGE_SIZE}, ptrace, @@ -1248,8 +1248,7 @@ impl ContextHandle { Ok(size_of::()) } ContextVerb::Interrupt => { - let mut guard = context.write(token.token()); - guard.unblock(); + unblock_context(&context, &mut token.token().downgrade()); Ok(size_of::()) } ContextVerb::ForceKill => { @@ -1278,13 +1277,16 @@ impl ContextHandle { } crate::syscall::exit_this_context(None, token); } else { - let mut ctxt = context.write(token.token()); - //trace!("FORCEKILL NONSELF={} {}, SELF={}", ctxt.debug_id, ctxt.pid, context::current().read().debug_id); - if ctxt.status.is_dead() { - return Ok(size_of::()); + { + let mut ctxt = context.write(token.token()); + //trace!("FORCEKILL NONSELF={} {}, SELF={}", ctxt.debug_id, ctxt.pid, context::current().read().debug_id); + if ctxt.status.is_dead() { + return Ok(size_of::()); + } + ctxt.status = context::Status::Runnable; + ctxt.being_sigkilled = true; } - ctxt.status = context::Status::Runnable; - ctxt.being_sigkilled = true; + unblock_context(&context, &mut token.token().downgrade()); Ok(size_of::()) } } diff --git a/src/scheme/user.rs b/src/scheme/user.rs index 2141f83d2c..0eb0afd8f0 100644 --- a/src/scheme/user.rs +++ b/src/scheme/user.rs @@ -22,7 +22,7 @@ use crate::{ handle_notify_files, AddrSpace, AddrSpaceWrapper, BorrowedFmapSource, Grant, GrantFileRef, MmapMode, PageSpan, UnmapVec, DANGLING, }, - BorrowedHtBuf, ContextLock, PreemptGuard, PreemptGuardL1, Status, + unblock_context, BorrowedHtBuf, ContextLock, PreemptGuard, PreemptGuardL1, Status, }, event, memory::{Frame, Page, VirtualAddress, PAGE_SIZE}, @@ -956,7 +956,7 @@ impl UserInner { match context.upgrade() { Some(context) => { *o = State::Responded(response); - context.write(lock_token.token()).unblock(); + unblock_context(&context, &mut lock_token.token().downgrade()); } _ => { states.remove(tag as usize); diff --git a/src/sync/wait_condition.rs b/src/sync/wait_condition.rs index e521c64189..989b06fdda 100644 --- a/src/sync/wait_condition.rs +++ b/src/sync/wait_condition.rs @@ -6,7 +6,7 @@ use alloc::{ }; use crate::{ - context::{self, ContextLock, PreemptGuardL2}, + context::{self, unblock_context, ContextLock, PreemptGuardL2}, sync::{CleanLockToken, LockToken, Mutex, L1, L2, L3}, }; @@ -33,7 +33,7 @@ impl WaitCondition { let len = contexts.len(); while let Some(context_weak) = contexts.pop() { if let Some(context_ref) = context_weak.upgrade() { - context_ref.write(token.token()).unblock(); + unblock_context(&context_ref, &mut token.token()); } } len @@ -46,7 +46,7 @@ impl WaitCondition { let len = contexts.len(); for context_weak in contexts.iter() { if let Some(context_ref) = context_weak.upgrade() { - context_ref.write(token.token()).unblock(); + unblock_context(&context_ref, &mut token.token()); } } len diff --git a/src/syscall/futex.rs b/src/syscall/futex.rs index fe8a4bf0bd..8fb09d508c 100644 --- a/src/syscall/futex.rs +++ b/src/syscall/futex.rs @@ -15,7 +15,7 @@ use crate::{ context::{ self, memory::{AddrSpace, AddrSpaceWrapper}, - ContextLock, + unblock_context, ContextLock, }, memory::{Page, PhysicalAddress, VirtualAddress}, sync::{CleanLockToken, Mutex, L1}, @@ -214,7 +214,7 @@ pub fn futex( continue; } if let Some(ctx) = futex.context_lock.upgrade() { - ctx.write(token.token()).unblock(); + unblock_context(&ctx, &mut token.token().downgrade()); } futexes.swap_remove(i); woken += 1; From e100dd5a719b05b7102c73d8bf99d860b16d1721 Mon Sep 17 00:00:00 2001 From: Wildan M Date: Thu, 16 Jul 2026 16:04:31 +0700 Subject: [PATCH 04/10] Fix RISCV init memory --- src/arch/riscv64/start.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/arch/riscv64/start.rs b/src/arch/riscv64/start.rs index 2551968f05..3fca90f023 100644 --- a/src/arch/riscv64/start.rs +++ b/src/arch/riscv64/start.rs @@ -105,7 +105,7 @@ unsafe extern "C" fn start(args_ptr: *const KernelArgs) -> ! { } // Initialize RMM - crate::startup::memory::init(&args, None, None); + let bump_allocator = crate::startup::memory::init(&args, None, None); let boot_hart_id = get_boot_hart_id(args.env()).expect("Didn't get boot HART id from bootloader"); @@ -114,6 +114,8 @@ unsafe extern "C" fn start(args_ptr: *const KernelArgs) -> ! { paging::init(); + crate::memory::init_mm(bump_allocator); + crate::arch::misc::init(crate::cpu_set::LogicalCpuId::new(0)); // Setup kernel heap From 37ffa2e29e2be8514c3fb757f7ab8ce4ee1a2916 Mon Sep 17 00:00:00 2001 From: Ibuki Omatsu Date: Sat, 18 Jul 2026 01:49:20 +0000 Subject: [PATCH 05/10] refactor: Allow refreshing filetable data using dup2 to fix O_CLOEXEC --- src/scheme/proc.rs | 68 ++++++++++++++++++++++++++++++++++------------ src/syscall/fs.rs | 41 ++++++++++++++++++++-------- 2 files changed, 80 insertions(+), 29 deletions(-) diff --git a/src/scheme/proc.rs b/src/scheme/proc.rs index c9c8fea3f9..d8abeea4af 100644 --- a/src/scheme/proc.rs +++ b/src/scheme/proc.rs @@ -771,25 +771,59 @@ impl KernelScheme for ProcScheme { } => { // TODO: Maybe allow userspace to either copy or transfer recently dupped file // descriptors between file tables. - if buf != b"copy" { - return Err(Error::new(EINVAL)); - } - let filetable = filetable.upgrade().ok_or(Error::new(EOWNERDEAD))?; + let new_handle = match buf { + b"copy" => { + let filetable = filetable.upgrade().ok_or(Error::new(EOWNERDEAD))?; - let new_filetable = - Arc::new(RwLock::new(filetable.read(token.token()).clone())); + let new_filetable = + Arc::new(RwLock::new(filetable.read(token.token()).clone())); - handle( - Handle { - kind: ContextHandle::NewFiletable { - filetable: new_filetable, - binary_format, - data: data.clone(), - }, - context, - }, - true, - ) + Handle { + kind: ContextHandle::NewFiletable { + filetable: new_filetable, + binary_format, + data: data.clone(), + }, + context, + } + } + b"refresh" => { + let filetable = filetable.upgrade().ok_or(Error::new(EOWNERDEAD))?; + + let new_data = if binary_format { + let mut data = Vec::new(); + for index in filetable + .read(token.token()) + .enumerate() + .filter_map(|(idx, val)| val.as_ref().map(|_| idx)) + { + data.extend((index as u64).to_le_bytes()); + } + data.into_boxed_slice() + } else { + use core::fmt::Write; + let mut data = String::new(); + for index in filetable + .read(token.token()) + .enumerate() + .filter_map(|(idx, val)| val.as_ref().map(|_| idx)) + { + writeln!(data, "{}", index).unwrap(); + } + data.into_bytes().into_boxed_slice() + }; + Handle { + kind: ContextHandle::Filetable { + filetable: Arc::downgrade(&filetable), + binary_format, + data: new_data, + }, + context, + } + } + _ => return Err(Error::new(EINVAL)), + }; + handle(new_handle, true) } Handle { kind: ContextHandle::AddrSpace { ref addrspace }, diff --git a/src/syscall/fs.rs b/src/syscall/fs.rs index f796517c2e..35d6a49e4e 100644 --- a/src/syscall/fs.rs +++ b/src/syscall/fs.rs @@ -235,19 +235,36 @@ pub fn dup2( buf: UserSliceRo, token: &mut CleanLockToken, ) -> Result { - if fd == new_fd { - Ok(new_fd) - } else { - let _ = close(new_fd, token); - 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(EMFILE)) + if fd == new_fd && buf.is_empty() { + return Ok(new_fd); } + + let new_file = duplicate_file(fd, buf, token)?; + + let old_file = { + let current_lock = context::current(); + let mut current = current_lock.write(token.token()); + let (context, mut split_token) = current.token_split(); + + let old_file = context.remove_file(new_fd, &mut split_token.token()); + + if context + .insert_file(new_fd, new_file, &mut split_token.token()) + .is_none() + { + if let Some(old) = old_file { + context.insert_file(new_fd, old, &mut split_token.token()); + } + return Err(Error::new(EMFILE)); + } + old_file + }; + + if let Some(old) = old_file { + let _ = old.close(token); + } + + Ok(new_fd) } pub fn call( fds: &[usize], From cd369993d909aac4d30e7b2f21105fe070cd5f14 Mon Sep 17 00:00:00 2001 From: Wildan M Date: Fri, 17 Jul 2026 06:41:01 +0700 Subject: [PATCH 06/10] Fix EOF using EVENT_TIMEOUT_ID --- src/event.rs | 52 ++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 38 insertions(+), 14 deletions(-) diff --git a/src/event.rs b/src/event.rs index 14d38a7ed0..1b68974731 100644 --- a/src/event.rs +++ b/src/event.rs @@ -5,7 +5,7 @@ use core::{ }; use hashbrown::{hash_map::DefaultHashBuilder, HashMap}; use smallvec::SmallVec; -use syscall::{data::GlobalSchemes, EINTR}; +use syscall::{data::GlobalSchemes, EAGAIN, EINTR}; use crate::{ context, @@ -27,7 +27,7 @@ int_like!(EventQueueId, AtomicEventQueueId, usize, AtomicUsize); pub struct EventQueue { id: EventQueueId, queue: WaitQueue, - timeout_opt: Mutex>, + timeout_opt: Mutex>, } impl EventQueue { @@ -59,19 +59,45 @@ impl EventQueue { pub fn read_with_timeout( &self, buf: UserSliceWo, - timeout: u128, + timeout: usize, token: &mut CleanLockToken, ) -> Result { - context::current().write(token.token()).wake = Some(timeout); + // if zero, instant timeout + let block = timeout > 0; + if block { + let mut time = crate::time::monotonic(token); + time += timeout as u128 * 1_000_000; + context::current().write(token.token()).wake = Some(time); + } let r = self .queue - .receive_into_user(buf, true, "EventQueue::read_with_timeout", token); - let old_wake = context::current().write(token.token()).wake.take(); - // The scheduler clears `wake` on timeout - if old_wake.is_none() && r.is_err_and(|e| e.errno == EINTR) { - return Ok(0); - } - r + .receive_into_user(buf, block, "EventQueue::read_with_timeout", token); + match (r, block) { + (Ok(r), _) => return Ok(r), + (err @ Err(Error { errno: EINTR }), true) => { + let old_wake = context::current().write(token.token()).wake.take(); + // The scheduler clears `wake` on timeout + if !old_wake.is_none() { + return err; + } + // proceed writing timeout + } + (Err(Error { errno: EAGAIN }), false) => { + // proceed writing timeout + } + (err, _) => return err, + }; + + // TODO: let the scheduler write this for us? + let event = Event { + id: syscall::EVENT_TIMEOUT_ID, + // TODO: it's undefined when flags is written to EVENT_TIMEOUT_ID + flags: EventFlags::EVENT_READ, + data: timeout, + }; + + let bytes_copied = buf.copy_common_bytes_from_slice(&event)?; + return Ok(bytes_copied); } pub fn write(&self, events: &[Event], token: &mut CleanLockToken) -> Result { @@ -80,9 +106,7 @@ impl EventQueue { if event.flags.is_empty() { self.timeout_opt.lock(token.token()).take(); } else { - let mut time = crate::time::monotonic(token); - time += (event.data * 1_000_000) as u128; - *self.timeout_opt.lock(token.token()) = Some(time); + *self.timeout_opt.lock(token.token()) = Some(event.data); } continue; From 20800bd2c770dc82771bde63cc4bd393407e9220 Mon Sep 17 00:00:00 2001 From: Luiz Fernando Becher de Araujo Date: Thu, 16 Jul 2026 21:02:08 -0300 Subject: [PATCH 07/10] DTB: Translate MMIO addresses through nested buses MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The existing MMIO helper only translates addresses through the /soc ranges property. This does not handle devices below nested buses, such as the UART in the devicetree used by the Meson boards. Add an address translator that walks each ancestor bus and applies its ranges property until reaching the CPU address space. Treat empty ranges as an identity mapping and reject regions that cross a range boundary. Keep the existing helper’s behavior unchanged for other devices to limit the scope of the behavioral change. Add tests for nested buses, empty ranges, exact range boundaries, and regions crossing a boundary. Signed-off-by: Luiz Fernando Becher de Araujo --- src/dtb/mod.rs | 119 +++++++++++++++++++++++++++++++++++--- src/dtb/testdata/mmio.dtb | Bin 0 -> 701 bytes src/dtb/testdata/mmio.dts | 48 +++++++++++++++ 3 files changed, 160 insertions(+), 7 deletions(-) create mode 100644 src/dtb/testdata/mmio.dtb create mode 100644 src/dtb/testdata/mmio.dts diff --git a/src/dtb/mod.rs b/src/dtb/mod.rs index b9b8b23094..22b2aa797e 100644 --- a/src/dtb/mod.rs +++ b/src/dtb/mod.rs @@ -147,8 +147,61 @@ pub fn register_dev_memory_ranges(dt: &Fdt) { } } -// FIXME return PhysicalAddress -pub fn get_mmio_address(fdt: &Fdt, _device: &FdtNode, region: &MemoryRegion) -> Option { +fn same_node(left: FdtNode<'_, '_>, right: FdtNode<'_, '_>) -> bool { + if left.name != right.name { + return false; + } + // FdtNode is reconstructed while walking the tree and has no identity + // operation. A property's value is a slice into the original DTB, so equal + // `reg` slice pointers identify the same node occurrence without relying + // on names that may repeat below different buses. + match (left.property("reg"), right.property("reg")) { + (Some(left_reg), Some(right_reg)) => core::ptr::eq(left_reg.value, right_reg.value), + _ => false, + } +} + +fn translate_bus_address(bus: FdtNode<'_, '_>, address: usize, size: usize) -> Option { + let ranges_property = bus.property("ranges")?; + if ranges_property.value.is_empty() { + return Some(address); + } + + let last_offset = size.saturating_sub(1); + let ranges = bus.ranges()?; + for range in ranges { + let Some(offset) = address.checked_sub(range.child_bus_address) else { + continue; + }; + if offset < range.size && last_offset < range.size - offset { + return range.parent_bus_address.checked_add(offset); + } + } + None +} + +fn translate_from_subtree( + bus_or_device: FdtNode<'_, '_>, + target: FdtNode<'_, '_>, + address: usize, + size: usize, +) -> Option { + if same_node(bus_or_device, target) { + return Some(address); + } + + for child in bus_or_device.children() { + if let Some(child_address) = translate_from_subtree(child, target, address, size) { + // `bus_or_device` is the bus parent of the subtree that matched. + return translate_bus_address(bus_or_device, child_address, size); + } + } + None +} + +/// Translate a device's bus-relative `reg` address through every ancestor's +/// `ranges` property until it reaches the CPU physical address space. +pub fn translate_mmio_address(fdt: &Fdt, device: &FdtNode, region: &MemoryRegion) -> Option { /* DT spec 2.3.8 "ranges": * The ranges property provides a means of defining a mapping or translation between * the address space of the bus (the child address space) and the address space of the bus @@ -159,15 +212,32 @@ pub fn get_mmio_address(fdt: &Fdt, _device: &FdtNode, region: &MemoryRegion) -> * children of the node and the parent address space. */ - // FIXME assumes all the devices are connected to CPUs via the /soc bus + let address = region.starting_address as usize; + let size = region.size.unwrap_or(1); + let root = fdt.find_node("/")?; + + // The root is already the CPU address space, so only its children perform + // translations. This also supports devices that are not under `/soc`. + for child in root.children() { + if let Some(translated) = translate_from_subtree(child, *device, address, size) { + translated.checked_add(size.saturating_sub(1))?; + return Some(translated); + } + } + None +} + +// FIXME return PhysicalAddress +pub fn get_mmio_address(fdt: &Fdt, _device: &FdtNode, region: &MemoryRegion) -> Option { let mut mapped_addr = region.starting_address as usize; let size = region.size.unwrap_or(0).saturating_sub(1); let last_address = mapped_addr.saturating_add(size); if let Some(parent) = fdt.find_node("/soc") { - let mut ranges = parent.ranges().map(|f| f.peekable())?; + let mut ranges = parent.ranges().map(|ranges| ranges.peekable())?; if ranges.peek().is_some() { - let parent_range = ranges.find(|x| { - x.child_bus_address <= mapped_addr && last_address - x.child_bus_address <= x.size + let parent_range = ranges.find(|range| { + range.child_bus_address <= mapped_addr + && last_address - range.child_bus_address <= range.size })?; mapped_addr = parent_range .parent_bus_address @@ -219,7 +289,7 @@ pub fn diag_uart_range<'a>(dtb: &'a Fdt) -> Option<(PhysicalAddress, usize, bool let mut reg = uart_node.reg()?; let memory = reg.next()?; - let address = get_mmio_address(dtb, &uart_node, &memory)?; + let address = translate_mmio_address(dtb, &uart_node, &memory)?; Some(( PhysicalAddress::new(address), @@ -244,3 +314,38 @@ pub fn fill_env_data(dt: &Fdt, env_base: usize) -> usize { 0 } } + +#[cfg(test)] +mod tests { + use super::translate_mmio_address; + use fdt::Fdt; + + static MMIO_DTB: &[u8] = include_bytes!("testdata/mmio.dtb"); + + fn translated(path: &str) -> Option { + let fdt = Fdt::new(MMIO_DTB).unwrap(); + let node = fdt.find_node(path).unwrap(); + let region = node.reg().unwrap().next().unwrap(); + translate_mmio_address(&fdt, &node, ®ion) + } + + #[test] + fn translates_uart_through_nested_and_empty_ranges() { + assert_eq!(translated("/soc/bus@1000/serial@200"), Some(0x1200)); + } + + #[test] + fn accepts_region_ending_exactly_at_range_boundary() { + assert_eq!(translated("/soc/bus@1000/device@ff0"), Some(0x1ff0)); + } + + #[test] + fn rejects_region_crossing_range_boundary() { + assert_eq!(translated("/soc/bus@1000/device@ff1"), None); + } + + #[test] + fn empty_ranges_is_identity_mapping() { + assert_eq!(translated("/identity-bus/device@3000"), Some(0x3000)); + } +} diff --git a/src/dtb/testdata/mmio.dtb b/src/dtb/testdata/mmio.dtb new file mode 100644 index 0000000000000000000000000000000000000000..c919ca99a1c4df14ea130d75cae78f8755bc7bf0 GIT binary patch literal 701 zcma)3%SyvQ6rEHTib!=QxDmV1O~|Aos32t7-$;|&b|6hkCKYt$kNFS&h@d|po->`P z23+*Q$$iYZ&%7Uhe<(G0Q%c=Izd~OFr@#q7c|?4&F?-+0dwbCP-X1^)e+Zj?R4f`R zwPWz@5;#w6QzX;1%~Gv(Vx={Cm5sH2NYh)W8pBXrKB%;rdjworvs_gYBb(uEKidEG zXC7q?M4_dN>{gR|vIckLPQmAW?g2iGH~fUpVtrmpT95YyKk&$Pd{au@neKUez1?oW+`l&nKEj*3`gw2gHyH~z z@b_^s#xH;{3-#eBFUwY}jSHzNt47wmh_`K5HtQ~4<=sLR%@W#7D^YD;&&8@%=IRF( C)K9+v literal 0 HcmV?d00001 diff --git a/src/dtb/testdata/mmio.dts b/src/dtb/testdata/mmio.dts new file mode 100644 index 0000000000..0ff7049c62 --- /dev/null +++ b/src/dtb/testdata/mmio.dts @@ -0,0 +1,48 @@ +/dts-v1/; + +/ { + #address-cells = <2>; + #size-cells = <2>; + + chosen { + stdout-path = "/soc/bus@1000/serial@200:115200n8"; + }; + + soc { + compatible = "simple-bus"; + #address-cells = <2>; + #size-cells = <2>; + ranges; + + bus@1000 { + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <1>; + reg = <0x0 0x1000 0x0 0x1000>; + ranges = <0x0 0x0 0x1000 0x1000>; + + serial@200 { + reg = <0x200 0x18>; + }; + + device@ff0 { + reg = <0xff0 0x10>; + }; + + device@ff1 { + reg = <0xff1 0x10>; + }; + }; + }; + + identity-bus { + compatible = "simple-bus"; + #address-cells = <2>; + #size-cells = <2>; + ranges; + + device@3000 { + reg = <0x0 0x3000 0x0 0x20>; + }; + }; +}; From eb51898177011d6b02a995defcc1d1658cfac499 Mon Sep 17 00:00:00 2001 From: Luiz Fernando Becher de Araujo Date: Thu, 16 Jul 2026 21:09:41 -0300 Subject: [PATCH 08/10] AArch64: Map diagnostic UART and root GIC MMIO Device memory registration currently stops when /soc or its ranges property is absent and only considers direct /soc children. Some devicetrees place the interrupt controller at the root and the selected UART below nested buses. Register the exact translated range of the diagnostic UART and the register ranges of root interrupt controllers. Use the hierarchical translator when initializing GICv2 and GICv3 registers. This keeps the translation change limited to the selected console and interrupt controllers while preserving the existing behavior for other devices. Signed-off-by: Luiz Fernando Becher de Araujo --- src/arch/aarch64/device/irqchip/gic.rs | 4 +- src/arch/aarch64/device/irqchip/gicv3.rs | 6 +- src/dtb/mod.rs | 111 +++++++++++++++-------- 3 files changed, 80 insertions(+), 41 deletions(-) diff --git a/src/arch/aarch64/device/irqchip/gic.rs b/src/arch/aarch64/device/irqchip/gic.rs index d5aef88c95..d79ed945a2 100644 --- a/src/arch/aarch64/device/irqchip/gic.rs +++ b/src/arch/aarch64/device/irqchip/gic.rs @@ -1,8 +1,8 @@ use super::InterruptController; use crate::{ dtb::{ - get_mmio_address, irqchip::{InterruptHandler, IrqCell, IrqDesc}, + translate_mmio_address, }, sync::CleanLockToken, }; @@ -60,7 +60,7 @@ impl GenericInterruptController { if chunk.size.is_none() { break; } - let addr = get_mmio_address(fdt, node, &chunk).unwrap(); + let addr = translate_mmio_address(fdt, node, &chunk).unwrap(); match idx { 0 => (regs.0, regs.1) = (addr, chunk.size.unwrap()), 2 => (regs.2, regs.3) = (addr, chunk.size.unwrap()), diff --git a/src/arch/aarch64/device/irqchip/gicv3.rs b/src/arch/aarch64/device/irqchip/gicv3.rs index 9d8a0d5211..77d42a35df 100644 --- a/src/arch/aarch64/device/irqchip/gicv3.rs +++ b/src/arch/aarch64/device/irqchip/gicv3.rs @@ -5,8 +5,8 @@ use fdt::{node::NodeProperty, Fdt}; use super::{gic::GicDistIf, InterruptController}; use crate::{ dtb::{ - get_mmio_address, irqchip::{InterruptHandler, IrqCell, IrqDesc}, + translate_mmio_address, }, sync::CleanLockToken, }; @@ -53,7 +53,7 @@ impl GicV3 { // Read registers let mut chunks = node.reg().unwrap(); if let Some(gicd) = chunks.next() - && let Some(addr) = get_mmio_address(fdt, &node, &gicd) + && let Some(addr) = translate_mmio_address(fdt, &node, &gicd) { unsafe { self.gic_dist_if.init(crate::PHYS_OFFSET + addr); @@ -62,7 +62,7 @@ impl GicV3 { for _ in 0..gicrs { if let Some(gicr) = chunks.next() { self.gicrs.push(( - get_mmio_address(fdt, &node, &gicr).unwrap(), + translate_mmio_address(fdt, &node, &gicr).unwrap(), gicr.size.unwrap(), )); } diff --git a/src/dtb/mod.rs b/src/dtb/mod.rs index 22b2aa797e..26a3b292da 100644 --- a/src/dtb/mod.rs +++ b/src/dtb/mod.rs @@ -102,48 +102,87 @@ pub fn register_dev_memory_ranges(dt: &Fdt) { } } - let Some(soc_node) = dt.find_node("/soc") else { - warn!("failed to find /soc in devicetree"); - return; - }; - let Some(reg) = soc_node.ranges() else { - warn!("devicetree /soc has no ranges"); - return; - }; - for chunk in reg { - debug!( - "dev mem 0x{:08x} 0x{:08x} 0x{:08x} 0x{:08x}", - chunk.child_bus_address_hi, - chunk.child_bus_address, - chunk.parent_bus_address, - chunk.size - ); + if let Some(soc_node) = dt.find_node("/soc") { + if let Some(reg) = soc_node.ranges() { + for chunk in reg { + debug!( + "dev mem 0x{:08x} 0x{:08x} 0x{:08x} 0x{:08x}", + chunk.child_bus_address_hi, + chunk.child_bus_address, + chunk.parent_bus_address, + chunk.size + ); - /*TODO: soc memory may contain all free memory! - register_memory_region( - chunk.parent_bus_address, - chunk.size, - BootloaderMemoryKind::Device, - );*/ - } + /*TODO: soc memory may contain all free memory! + register_memory_region( + chunk.parent_bus_address, + chunk.size, + BootloaderMemoryKind::Device, + );*/ + } + } else { + warn!("devicetree /soc has no ranges"); + } - // also add all soc-internal devices because they might not be shown in ranges - // (identity-mapped soc bus may have empty ranges) - for device in soc_node.children() { - if let Some(reg) = device.reg() { - for entry in reg { - if let Some(size) = entry.size { - let addr = entry.starting_address as usize; - if let Some(mapped_addr) = get_mmio_address(dt, &device, &entry) { - debug!( - "soc device {} 0x{:08x} -> 0x{:08x} size 0x{:08x}", - device.name, addr, mapped_addr, size - ); - register_memory_region(mapped_addr, size, BootloaderMemoryKind::Device); + // also add direct /soc children because they might not be shown in + // ranges (an identity-mapped bus may have empty ranges) + for device in soc_node.children() { + if let Some(reg) = device.reg() { + for entry in reg { + if let Some(size) = entry.size { + let addr = entry.starting_address as usize; + if let Some(mapped_addr) = get_mmio_address(dt, &device, &entry) { + debug!( + "soc device {} 0x{:08x} -> 0x{:08x} size 0x{:08x}", + device.name, addr, mapped_addr, size + ); + register_memory_region(mapped_addr, size, BootloaderMemoryKind::Device); + } } } } } + } else { + warn!("failed to find /soc in devicetree"); + } + + // The selected console may be below a nested bus whose own `reg` range + // does not cover all children. Register the exact translated range so it + // remains mapped after the boot-time identity map is replaced. + if let Some((address, size, _, _, _)) = diag_uart_range(dt) { + debug!( + "diagnostic UART 0x{:08x} size 0x{:08x}", + address.data(), + size + ); + register_memory_region(address.data(), size, BootloaderMemoryKind::Device); + } + + // Interrupt controllers are not required to live below /soc. Some + // devicetrees place the primary GIC directly below the root node, so its + // register ranges would otherwise be absent from the kernel physmap. + if let Some(root) = dt.find_node("/") { + for controller in root + .children() + .filter(|node| node.property("interrupt-controller").is_some()) + { + let Some(regions) = controller.reg() else { + continue; + }; + for region in regions { + let Some(size) = region.size else { + continue; + }; + let Some(address) = translate_mmio_address(dt, &controller, ®ion) else { + continue; + }; + debug!( + "root interrupt controller {} 0x{:08x} size 0x{:08x}", + controller.name, address, size + ); + register_memory_region(address, size, BootloaderMemoryKind::Device); + } + } } } From 4251102f0bc9a57d4de978028acd5ed820eafe8e Mon Sep 17 00:00:00 2001 From: Wildan M Date: Fri, 17 Jul 2026 13:27:43 +0700 Subject: [PATCH 09/10] Partially solve missing wakeups after separate timers --- src/context/mod.rs | 23 +++++++++++++---------- src/context/switch.rs | 6 +++--- src/scheme/proc.rs | 6 ++++-- src/scheme/user.rs | 11 +++++++---- src/startup/mod.rs | 6 ++++-- 5 files changed, 31 insertions(+), 21 deletions(-) diff --git a/src/context/mod.rs b/src/context/mod.rs index 7eb3e9199b..6999bc2802 100644 --- a/src/context/mod.rs +++ b/src/context/mod.rs @@ -140,21 +140,19 @@ pub fn run_contexts_try(token: LockToken<'_, L0>) -> Option, token: &mut LockToken<'_, L3>) -> bool { - let was_blocked = { + let cpu_id = { let mut guard = context_lock.write(token.token()); if !guard.unblock_no_ipi() { + if guard.status.is_runnable() { + // already set to runnable externally + wakeup_context(context_lock); + } return false; } - - true + guard.cpu_id }; - let percpu: &PercpuBlock = PercpuBlock::current(); - - let weak = WeakContextRef(Arc::downgrade(context_lock)); - let cpu_id = context_lock.write(token.token()).cpu_id; - - percpu.switch_internals.wakeup_list.borrow_mut().push(weak); + wakeup_context(context_lock); if let Some(cpu_id) = cpu_id && cpu_id != crate::cpu_id() @@ -165,6 +163,12 @@ pub fn unblock_context(context_lock: &Arc, token: &mut LockToken<'_ true } +pub fn wakeup_context(context_lock: &Arc) { + let percpu: &PercpuBlock = PercpuBlock::current(); + let weak = WeakContextRef(Arc::downgrade(context_lock)); + percpu.switch_internals.wakeup_list.borrow_mut().push(weak); +} + pub fn init(token: &mut CleanLockToken) { let owner = None; // kmain not owned by any fd let mut context = Context::new(owner).expect("failed to create kmain context"); @@ -291,7 +295,6 @@ pub fn spawn( let context_lock = Arc::new(ContextLock::new(context)); let context_ref = ContextRef(Arc::clone(&context_lock)); let run_ref = WeakContextRef(Arc::downgrade(&context_ref.0)); - idle_contexts(token.downgrade()).push_back(run_ref); contexts_mut(token.downgrade()).insert(context_ref); Ok(context_lock) diff --git a/src/context/switch.rs b/src/context/switch.rs index 915921a29f..d79db4a3de 100644 --- a/src/context/switch.rs +++ b/src/context/switch.rs @@ -217,9 +217,9 @@ pub fn switch(token: &mut CleanLockToken) -> SwitchResult { } // Drain from percpu - let mut percpu_wake = percpu.switch_internals.wakeup_list.replace(Vec::new()); - if percpu_wake.len() > 0 { - for context_ref in percpu_wake.iter() { + { + let mut percpu_wake = percpu.switch_internals.wakeup_list.borrow_mut(); + for context_ref in percpu_wake.drain(..) { wakeups.push(context_ref.clone()); } } diff --git a/src/scheme/proc.rs b/src/scheme/proc.rs index d8abeea4af..6dcc276375 100644 --- a/src/scheme/proc.rs +++ b/src/scheme/proc.rs @@ -4,7 +4,7 @@ use crate::{ context::{HardBlockedReason, LockedFdTbl, SignalState}, file::InternalFlags, memory::{handle_notify_files, AddrSpace, AddrSpaceWrapper, Grant, PageSpan, UnmapVec}, - unblock_context, Context, ContextLock, Status, + unblock_context, wakeup_context, Context, ContextLock, Status, }, memory::{Page, VirtualAddress, PAGE_SIZE}, ptrace, @@ -1132,6 +1132,7 @@ impl ContextHandle { reason: HardBlockedReason::NotYetStarted, } => { *status = Status::Runnable; + wakeup_context(&context); Ok(buf.len()) } _ => Err(Error::new(EINVAL)), @@ -1278,6 +1279,7 @@ impl ContextHandle { } = guard.status { guard.status = Status::Runnable; + wakeup_context(&context); } Ok(size_of::()) } @@ -1320,7 +1322,7 @@ impl ContextHandle { ctxt.status = context::Status::Runnable; ctxt.being_sigkilled = true; } - unblock_context(&context, &mut token.token().downgrade()); + wakeup_context(&context); Ok(size_of::()) } } diff --git a/src/scheme/user.rs b/src/scheme/user.rs index 0eb0afd8f0..e6e93d3048 100644 --- a/src/scheme/user.rs +++ b/src/scheme/user.rs @@ -22,10 +22,12 @@ use crate::{ handle_notify_files, AddrSpace, AddrSpaceWrapper, BorrowedFmapSource, Grant, GrantFileRef, MmapMode, PageSpan, UnmapVec, DANGLING, }, - unblock_context, BorrowedHtBuf, ContextLock, PreemptGuard, PreemptGuardL1, Status, + unblock_context, wakeup_context, BorrowedHtBuf, ContextLock, PreemptGuard, PreemptGuardL1, + Status, WeakContextRef, }, event, memory::{Frame, Page, VirtualAddress, PAGE_SIZE}, + percpu::PercpuBlock, scheme::SchemeId, sync::{CleanLockToken, LockToken, Mutex, RwLock, WaitQueue, L1}, syscall::{ @@ -873,7 +875,7 @@ impl UserInner { } }; - let context = context.upgrade().ok_or(Error::new(ESRCH))?; + let context_lock = context.upgrade().ok_or(Error::new(ESRCH))?; let mut lock_token = token.token(); let (frame, _) = AddrSpace::current()? @@ -884,12 +886,13 @@ impl UserInner { .ok_or(Error::new(EFAULT))?; { - let mut context = context.write(token.token()); + let mut context = context_lock.write(token.token()); if let Status::HardBlocked { reason: HardBlockedReason::AwaitingMmap { .. }, } = context.status { - context.status = Status::Runnable + context.status = Status::Runnable; + wakeup_context(&context_lock); } context.fmap_ret = Some(Frame::containing(frame)); } diff --git a/src/startup/mod.rs b/src/startup/mod.rs index 7b3f532fa4..b517d41672 100644 --- a/src/startup/mod.rs +++ b/src/startup/mod.rs @@ -7,7 +7,7 @@ use core::{ use crate::{ arch::interrupt, - context::{self, switch::SwitchResult}, + context::{self, switch::SwitchResult, wakeup_context, Status}, memory::{PhysicalAddress, RmmA, RmmArch}, profiling, scheme, sync::CleanLockToken, @@ -175,13 +175,15 @@ pub(crate) fn kmain(bootstrap: Bootstrap) -> ! { match context::spawn(true, owner, userspace_init, &mut token) { Ok(context_lock) => { let mut context = context_lock.write(token.token()); - context.status = context::Status::Runnable; + context.status = Status::Runnable; context.name.clear(); context.name.push_str("[bootstrap]"); // TODO: Remove these from kernel context.euid = 0; context.egid = 0; + + wakeup_context(&context_lock); } Err(err) => { panic!("failed to spawn userspace_init: {:?}", err); From eaf50894aa20041b60ec796526ba017505238934 Mon Sep 17 00:00:00 2001 From: R Aadarsh Date: Sat, 18 Jul 2026 17:25:02 +0530 Subject: [PATCH 10/10] * Do not use `KernelMapper` for ACPI SDT mapping * Clarify doc comment --- src/acpi/mod.rs | 26 +++++++++++++++++++------- src/arch/aarch64/start.rs | 5 +++-- src/arch/riscv64/start.rs | 4 ++-- src/arch/x86_shared/start.rs | 9 ++++++--- src/memory/mod.rs | 4 ++-- 5 files changed, 32 insertions(+), 16 deletions(-) diff --git a/src/acpi/mod.rs b/src/acpi/mod.rs index fd1e88b948..b3a9d541d5 100644 --- a/src/acpi/mod.rs +++ b/src/acpi/mod.rs @@ -6,6 +6,7 @@ use core::ptr::NonNull; use alloc::{boxed::Box, string::String, vec::Vec}; use hashbrown::HashMap; +use rmm::{BumpAllocator, FrameAllocator, PageMapper}; use spin::{Once, RwLock}; use crate::{ @@ -31,7 +32,11 @@ mod spcr; pub mod srat; mod xsdt; -unsafe fn map_linearly(addr: PhysicalAddress, len: usize, mapper: &mut crate::memory::PageMapper) { +unsafe fn map_linearly( + addr: PhysicalAddress, + len: usize, + mapper: &mut PageMapper, +) { unsafe { let base = PhysicalAddress::new(crate::memory::round_down_pages(addr.data())); let aligned_len = crate::memory::round_up_pages(len + (addr.data() - base.data())); @@ -48,7 +53,10 @@ unsafe fn map_linearly(addr: PhysicalAddress, len: usize, mapper: &mut crate::me } } -pub fn get_sdt(sdt_address: PhysicalAddress, mapper: &mut KernelMapper) -> &'static Sdt { +pub fn get_sdt( + sdt_address: PhysicalAddress, + mapper: &mut PageMapper, +) -> &'static Sdt { let sdt; unsafe { @@ -91,16 +99,20 @@ impl Rxsdt for RxsdtEnum { pub static RXSDT_ENUM: Once = Once::new(); -/// Initialses the global `RXSDT_ENUM` if RSDT or XSDT was found and maps the SDT pages. -/// It does not perform any allocations -pub unsafe fn init_before_mem(already_supplied_rsdp: Option>) { +/// Initialises the global `RXSDT_ENUM` if RSDT or XSDT was found and maps the SDT pages. +/// +/// It does not use `TheFrameAllocator` nor does it heap-allocate. +pub unsafe fn init_before_mem( + already_supplied_rsdp: Option>, + mapper: &mut PageMapper>, +) { unsafe { // Search for RSDP let rsdp_opt = Rsdp::get_rsdp(already_supplied_rsdp); if let Some(rsdp) = rsdp_opt { debug!("SDT address: {:#x}", rsdp.sdt_address().data()); - let rxsdt = get_sdt(rsdp.sdt_address(), &mut KernelMapper::lock_rw()); + let rxsdt = get_sdt(rsdp.sdt_address(), mapper); let rxsdt = if let Some(rsdt) = Rsdt::new(rxsdt) { let mut initialized = false; @@ -137,7 +149,7 @@ pub unsafe fn init_before_mem(already_supplied_rsdp: Option>) { // TODO: Don't touch ACPI tables in kernel? for sdt in rxsdt.iter() { - get_sdt(sdt, &mut KernelMapper::lock_rw()); + get_sdt(sdt, mapper); } } else { error!("NO RSDP FOUND"); diff --git a/src/arch/aarch64/start.rs b/src/arch/aarch64/start.rs index 3fdc9ab3f8..68e6ff37d7 100644 --- a/src/arch/aarch64/start.rs +++ b/src/arch/aarch64/start.rs @@ -100,13 +100,14 @@ unsafe extern "C" fn start(args_ptr: *const KernelArgs) -> ! { // Initialize paging paging::init(); + let mut mapper = rmm::PageMapper::current(rmm::TableKind::Kernel, bump_allocator); #[cfg(feature = "acpi")] { use crate::acpi; - acpi::init_before_mem(args.acpi_rsdp()); + acpi::init_before_mem(args.acpi_rsdp(), &mut mapper); } - crate::memory::init_mm(bump_allocator); + crate::memory::init_mm(mapper.allocator_mut()); crate::arch::misc::init(crate::cpu_set::LogicalCpuId::new(0)); diff --git a/src/arch/riscv64/start.rs b/src/arch/riscv64/start.rs index 3fca90f023..146fb37540 100644 --- a/src/arch/riscv64/start.rs +++ b/src/arch/riscv64/start.rs @@ -105,7 +105,7 @@ unsafe extern "C" fn start(args_ptr: *const KernelArgs) -> ! { } // Initialize RMM - let bump_allocator = crate::startup::memory::init(&args, None, None); + let mut bump_allocator = crate::startup::memory::init(&args, None, None); let boot_hart_id = get_boot_hart_id(args.env()).expect("Didn't get boot HART id from bootloader"); @@ -114,7 +114,7 @@ unsafe extern "C" fn start(args_ptr: *const KernelArgs) -> ! { paging::init(); - crate::memory::init_mm(bump_allocator); + crate::memory::init_mm(&mut bump_allocator); crate::arch::misc::init(crate::cpu_set::LogicalCpuId::new(0)); diff --git a/src/arch/x86_shared/start.rs b/src/arch/x86_shared/start.rs index 295c273501..a18491d7e1 100644 --- a/src/arch/x86_shared/start.rs +++ b/src/arch/x86_shared/start.rs @@ -4,6 +4,8 @@ //! defined in other files inside of the `arch` module use core::{arch::naked_asm, cell::SyncUnsafeCell, mem::offset_of}; +use rmm::PageMapper; + use crate::{ allocator, arch::{device, gdt, idt, interrupt, paging}, @@ -112,13 +114,14 @@ unsafe extern "C" fn start(args_ptr: *const KernelArgs, stack_end: usize) -> ! { // Initialize paging paging::init(); + let mut mapper = PageMapper::current(rmm::TableKind::Kernel, bump_allocator); if cfg!(feature = "acpi") { - crate::acpi::init_before_mem(args.acpi_rsdp()); + crate::acpi::init_before_mem(args.acpi_rsdp(), &mut mapper); } - numa::init(&mut bump_allocator); + numa::init(mapper.allocator_mut()); - crate::memory::init_mm(bump_allocator); + crate::memory::init_mm(mapper.allocator_mut()); #[cfg(target_arch = "x86_64")] crate::arch::alternative::early_init(true); diff --git a/src/memory/mod.rs b/src/memory/mod.rs index f1a50524cf..39891bc9fb 100644 --- a/src/memory/mod.rs +++ b/src/memory/mod.rs @@ -549,7 +549,7 @@ const _: () = { }; #[cold] -fn init_sections(mut allocator: BumpAllocator) { +fn init_sections(mut allocator: &mut BumpAllocator) { let number_of_memory_regions = numa::number_of_memory_regions(); let (free_areas, offset_into_first_free_area) = allocator.free_areas(); @@ -958,7 +958,7 @@ fn init_sections(mut allocator: BumpAllocator) { } #[cold] -pub fn init_mm(allocator: BumpAllocator) { +pub fn init_mm(allocator: &mut BumpAllocator) { init_sections(allocator); unsafe {