feat: rewrite TCB ownership to Arc<TCB> & complete P2#202
feat: rewrite TCB ownership to Arc<TCB> & complete P2#202MRNIU wants to merge 68 commits intoSimple-XX:mainfrom
Conversation
…duling Timer interrupts could fire during switch_to when interrupts were enabled after releasing sched_lock but before the context switch completed. This caused nested TickUpdate -> Schedule calls that corrupted the half-saved task context, manifesting as recursive spinlock panics under load (observed on CI ARM64 runners and reproducible locally with stress-ng). Signed-off-by: Niu Zhihong <zhihong@nzhnb.com>
Signed-off-by: Niu Zhihong <zhihong@nzhnb.com>
Signed-off-by: Niu Zhihong <zhihong@nzhnb.com>
Signed-off-by: Niu Zhihong <zhihong@nzhnb.com>
Signed-off-by: Niu Zhihong <zhihong@nzhnb.com>
SpinLock::saved_intr_enable_ was stored inside the lock object and got overwritten across switch_to when different Schedule() callers (timer handler SIE=0 vs normal context SIE=1) acquired the same lock. This caused FinishSwitch to incorrectly enable interrupts inside the timer handler, leading to recursive sched_lock acquisition (CI PANIC). Refactor following Linux raw_spinlock / Zephyr k_spinlock pattern: - SpinLock::Lock()/UnLock() are now pure atomic ops (no interrupt mgmt) - LockGuard saves interrupt state in its own member (lives on stack) - Schedule() saves interrupt state to stack-local var before Lock() - FinishSwitch() does raw unlock only; caller restores interrupt state - LockGuard constrained to std::derived_from<T, SpinLock> to prevent misuse with Mutex (disable-interrupt + blocking = deadlock) - Clone() now calls InitTaskContext for child CalleeSavedContext so forked tasks enter kernel_thread_bootstrap -> FinishSwitch properly - Update spinlock unit tests and system tests for new API Signed-off-by: Niu Zhihong <zhihong@nzhnb.com>
Signed-off-by: Niu Zhihong <zhihong@nzhnb.com>
Signed-off-by: Niu Zhihong <zhihong@nzhnb.com>
Signed-off-by: Niu Zhihong <zhihong@nzhnb.com>
- Repurpose primary core boot task as init (pid 1, kNormal policy) - InitCurrentCore(bool is_primary) encapsulates init vs boot creation - Init enters task_table_ for cross-core visibility (FindTask(1)) - main() runs init loop: non-blocking Wait(-1) + Schedule() - pid_allocator_ starts at 2 to reserve pid 1 - SMP cores unchanged (default is_primary=false) Signed-off-by: Niu Zhihong <zhihong@nzhnb.com>
Exit() had a race window between releasing sched_lock and calling Wakeup/Schedule. A timer interrupt in this window could preempt the zombie, losing the parent wakeup and causing cross-core test timeouts. - Move Wakeup from Exit() to HandleDeferredCleanup() in schedule.cpp - Schedule() stores prev_task before switch_to, new task handles cleanup - kernel_thread_bootstrap also calls HandleDeferredCleanup - Consolidate kInitPid as TaskManager::kInitPid, remove hardcoded values - Remove resolved TODOs in ReparentChildren Signed-off-by: Niu Zhihong <zhihong@nzhnb.com>
Signed-off-by: Niu Zhihong <zhihong@nzhnb.com>
Signed-off-by: Niu Zhihong <zhihong@nzhnb.com>
Signed-off-by: Niu Zhihong <zhihong@nzhnb.com>
Signed-off-by: Niu Zhihong <zhihong@nzhnb.com>
Signed-off-by: Niu Zhihong <zhihong@nzhnb.com>
…urn path TickUpdate() now sets need_resched/need_balance flags instead of directly calling Schedule()/Balance() from hardirq context. TimerHandler checks these flags after hardirq_count-- and executes the deferred calls in process context. This fixes hardirq_count leakage when switch_to never returns to TimerHandler, and prevents recursive spinlock deadlock on sched_lock. Changes: - Add need_resched and need_balance flags to PreemptState (per_cpu.hpp) - Replace Schedule()/Balance() calls with flag setting (tick_update.cpp) - Check flags after hardirq exit in both riscv64 and aarch64 TimerHandler - Move Balance() from private to public in TaskManager for external access Signed-off-by: Niu Zhihong <zhihong@nzhnb.com>
…eduler comparison
Signed-off-by: Niu Zhihong <zhihong@nzhnb.com>
…ection - Move lock_level constants and LockStack from spinlock.hpp to per_cpu.hpp as proper per-CPU data inside PerCpu struct - Merge lock ordering validation with reentrance detection into a single per-CPU lock stack (LockStack) that tracks held SpinLock* pointers - Restore owner_core_ atomic for multi-core safe reentrance detection during spin (per-CPU stack is not thread-safe without held lock) - Derive LockStack::kMaxDepth from lock_level::kCount to auto-adjust when new lock levels are added - Remove IsLockedByCurrentCore() — ownership verified via owner_core_ - Update UT/ST spinlock tests to use Lock()/UnLock() return values instead of removed IsLockedByCurrentCore() - Fix UT core count to use SIMPLEKERNEL_MAX_CORE_COUNT Signed-off-by: Niu Zhihong <zhihong@nzhnb.com>
Signed-off-by: Niu Zhihong <zhihong@nzhnb.com>
Signed-off-by: Niu Zhihong <zhihong@nzhnb.com>
Signed-off-by: Niu Zhihong <zhihong@nzhnb.com>
Signed-off-by: Niu Zhihong <zhihong@nzhnb.com>
Signed-off-by: Niu Zhihong <zhihong@nzhnb.com>
Signed-off-by: Niu Zhihong <zhihong@nzhnb.com>
Signed-off-by: Niu Zhihong <zhihong@nzhnb.com>
Signed-off-by: Niu Zhihong <zhihong@nzhnb.com>
Signed-off-by: Niu Zhihong <zhihong@nzhnb.com>
Signed-off-by: Niu Zhihong <zhihong@nzhnb.com>
Signed-off-by: Niu Zhihong <zhihong@nzhnb.com>
Signed-off-by: Niu Zhihong <zhihong@nzhnb.com>
Signed-off-by: Niu Zhihong <zhihong@nzhnb.com>
Signed-off-by: Niu Zhihong <zhihong@nzhnb.com>
Signed-off-by: Niu Zhihong <zhihong@nzhnb.com>
Signed-off-by: Niu Zhihong <zhihong@nzhnb.com>
Signed-off-by: Niu Zhihong <zhihong@nzhnb.com>
… steps Replace raw-pointer TCB ownership with Arc<TaskControlBlock> pattern (aligned with rCore-Tutorial-v3), eliminating use-after-free risks. Mutable fields protected via interior SpinLock<TcbInner>. All 6 prototype tests pass. Also suppress dead_code warnings for items used in later phases and remove unnecessary unsafe blocks in riscv interrupt ops. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR completes Phase 2 of the Rust kernel rewrite, replacing the prior CMake-based host tooling with a Rust xtask workflow and introducing foundational runtime pieces (panic/backtrace, per-CPU data, locking), while updating the existing C++ kernel side for improved scheduling, signals, SMP balancing, and interrupt preemption handling.
Changes:
- Added
xtasksubcrate to build firmware, generate FIT images, prepare boot artifacts, and run/debug QEMU for riscv64/aarch64. - Refactored C++ scheduling/locking/signal paths: deferred cleanup & SIGCHLD delivery, stop/cont FSM state, load-balancing, and explicit preemption state.
- Added Rust kernel scaffolding for P2 (logging, panic/backtrace, ELF/FDT parsing, per-CPU, sync primitives), plus CI/devcontainer/pre-commit updates to Rust-first.
Reviewed changes
Copilot reviewed 134 out of 271 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| xtask/src/qemu.rs | New QEMU helpers: DTB dump, FIT creation, boot script, TFTP, launch. |
| xtask/src/main.rs | New cargo xtask CLI entrypoint and subcommands. |
| xtask/src/firmware.rs | Firmware build + pre-run firmware presence checks. |
| xtask/src/build.rs | Kernel build + debug artifact generation + boot dir/rootfs prep. |
| xtask/src/arch.rs | Arch abstraction for toolchain triples, QEMU binary, script content. |
| xtask/Cargo.toml | New xtask crate manifest. |
| tools/riscv64_qemu_virt.its.in | FIT template placeholder format adjusted. |
| tools/project_config.h.in | Removed CMake-generated config header template. |
| tools/aarch64_qemu_virt.its.in | Removed legacy aarch64 FIT template. |
| tools/README.md | Removed legacy tools README. |
| tools/.pre-commit-config.yaml.in | Removed CMake-templated pre-commit config. |
| tests/unit_test/mocks/arch.cpp | Added RawDumpStack mock stub. |
| tests/system_test/system_test.h | Added ipi_test, removed idle_scheduler_test declaration. |
| tests/system_test/spinlock_test.cpp | Adjusted tests for new interrupt/lock behavior and expectations. |
| tests/system_test/signal_test.cpp | Added SIGSTOP/SIGCONT, SIGKILL(stopped), SIGCHLD delivery tests. |
| tests/system_test/main.cpp | Registered ipi_test, removed idle_scheduler_test. |
| tests/system_test/ipi_test.cpp | New IPI API + delivery verification tests. |
| tests/system_test/idle_scheduler_test.cpp | Removed idle scheduler system tests. |
| tests/system_test/clone_test.cpp | Added clone signal handler inheritance test. |
| tests/system_test/balance_test.cpp | Replaced popcount builtin to avoid aarch64 FP/NEON trap on secondary cores. |
| tests/system_test/CMakeLists.txt | Added ipi_test.cpp, removed idle_scheduler_test.cpp. |
| tests/CMakeLists.txt | Removed legacy tests top-level CMake configuration. |
| src_cpp/task/balance.cpp | Added work-stealing load balancer implementation. |
| src_cpp/include/per_cpu.hpp | Extended per-CPU data: preempt state + lock stack + lock levels. |
| src_cpp/arch/riscv64/switch.S | New riscv64 context switch + kernel thread entry. |
| src_cpp/arch/riscv64/interrupt.S | New riscv64 trap entry/return implementation. |
| src_cpp/arch/riscv64/boot.S | New riscv64 boot entry and per-core stack setup. |
| src_cpp/arch/aarch64/switch.S | New aarch64 context switch + kernel thread entry. |
| src_cpp/arch/aarch64/macro.S | New aarch64 context save/restore macros. |
| src_cpp/arch/aarch64/interrupt.S | New aarch64 vector table + trap return. |
| src_cpp/arch/aarch64/boot.S | New aarch64 boot entry and per-core stack setup. |
| src/task/tick_update.cpp | Defers balance/resched via per-CPU flags instead of direct calls. |
| src/task/task_control_block.cpp | Destructor frees resources conditionally via owns_resources. |
| src/task/sleep.cpp | Moves running-status assert into sched lock; calls CheckPendingSignals() (non-void cast removed). |
| src/task/signal.cpp | Refined signal delivery: stop/cont handling and wake/enqueue logic changes. |
| src/task/schedule.cpp | Lock-handoff scheduling, deferred cleanup, signal safe-point call. |
| src/task/include/task_messages.hpp | Added Stop/Cont messages + message-name helper. |
| src/task/include/task_manager.hpp | Schedule lock-handoff docs; CheckPendingSignals now void; added Balance(); lock levels. |
| src/task/include/task_fsm.hpp | Added kStopped state, MsgStop/MsgCont transitions, assertions on unknown events. |
| src/task/include/task_control_block.hpp | Added owns_resources flag. |
| src/task/include/scheduler_base.hpp | Removed duplicate kernel_thread_bootstrap declaration. |
| src/task/include/idle_scheduler.hpp | Removed idle scheduler implementation. |
| src/task/exit.cpp | Removed immediate parent wake; relies on deferred cleanup. |
| src/task/clone.cpp | Initializes task context; copies signal actions; improves TODO notes. |
| src/task/CMakeLists.txt | Added balance.cpp to task sources. |
| src/task/AGENTS.md | Updated task subsystem overview (no idle scheduler; new balance.cpp). |
| src/sync/mod.rs | Added Rust sync module exports with unused-import allow. |
| src/per_cpu.rs | New Rust per-CPU + lock stack + BASIC_INFO. |
| src/panic.rs | New Rust panic handler with observers + ELF-backed backtrace. |
| src/memory/memory.cpp | Adjusted bmalloc lock interrupt handling. |
| src/main.rs | New Rust kernel entry + P2 smoke test harness. |
| src/main.cpp | Changed init: create init pid=1; init loop reaps zombies and schedules. |
| src/logging.rs | New Rust logger with colored prefix and console mutex. |
| src/libcxx/kstd_libcxx.cpp | Added RawDumpStack() call on assert; notes about guard SMP correctness. |
| src/lang_items.rs | Rust panic + alloc error handlers. |
| src/include/spinlock.hpp | SpinLock no longer manages interrupts; LockGuard now saves/restores interrupts; adds lock-order checks. |
| src/include/per_cpu.hpp | Removed old per_cpu header (replaced by src_cpp/include/per_cpu.hpp). |
| src/include/kernel_log.hpp | Disabled interrupts during draining to avoid interleaving/races. |
| src/halt.rs | New Rust halt helper. |
| src/fmt_buf.rs | New Rust fixed-size formatting buffer. |
| src/fdt.rs | New Rust FDT wrapper utilities. |
| src/error.rs | New Rust ErrorCode + KResult. |
| src/elf.rs | New Rust ELF symbol lookup helper. |
| src/config.rs | New Rust config constants. |
| src/arch/riscv64/timer.cpp | Uses per-CPU preempt state + deferred balance/schedule. |
| src/arch/riscv64/mod.rs | Rust riscv64 bootstrap wiring. |
| src/arch/riscv64/link.ld | Exposes .eh_frame bounds symbols. |
| src/arch/riscv64/interrupt_main.cpp | Signature cleanup; IPI handler signature cleanup. |
| src/arch/riscv64/init.rs | Rust riscv64 init: FDT parse, BASIC_INFO init, logging. |
| src/arch/riscv64/console.rs | Rust SBI console output. |
| src/arch/riscv64/backtrace.cpp | Added RawDumpStack() implementation. |
| src/arch/mod.rs | Rust arch module selection and re-exported bootstrap. |
| src/arch/arch.h | Declared RawDumpStack(). |
| src/arch/aarch64/timer.cpp | Uses per-CPU preempt state + deferred balance/schedule. |
| src/arch/aarch64/mod.rs | Rust aarch64 bootstrap wiring. |
| src/arch/aarch64/link.ld | Exposes .eh_frame bounds symbols. |
| src/arch/aarch64/interrupt_main.cpp | Added FAR_EL1 to exception print; registered IPI handler; SMP SGI note. |
| src/arch/aarch64/interrupt.cpp | Updated TODO wording for SGI 0 usage. |
| src/arch/aarch64/init.rs | Rust aarch64 init: parse dtb from argv, BASIC_INFO init, logging. |
| src/arch/aarch64/console.rs | Rust PL011 MMIO console. |
| src/arch/aarch64/backtrace.cpp | Added RawDumpStack() implementation. |
| rust-toolchain.toml | Pinned Rust nightly + llvm-tools etc. |
| docs/superpowers/plans/2026-03-23-rust-kernel-rewrite.md | Added/updated rewrite execution plan doc. |
| docs/CMakeLists.txt | Removed docs CMake target. |
| cmake/riscv64-gcc.cmake | Removed legacy toolchain file. |
| cmake/replace_kv.cmake | Removed legacy template-replace helper. |
| cmake/project_config.cmake | Removed legacy config generation. |
| cmake/functions.cmake | Removed legacy run/coverage helpers. |
| cmake/compile_config.cmake | Removed legacy compile/link config. |
| cmake/aarch64-gcc.cmake | Removed legacy toolchain file. |
| build.rs | Added Rust build script for asm compilation + linker args. |
| LICENSE | Updated copyright year to 2026. |
| Cargo.toml | New Rust workspace + kernel crate dependencies and profiles. |
| CMakePresets.json | Removed legacy CMake presets. |
| CMakeLists.txt | Removed legacy top-level CMake project. |
| 3rd/optee/optee_client | Removed submodule entry. |
| 3rd/optee/build | Removed submodule entry. |
| 3rd/cpu_io | Updated submodule revision. |
| 3rd/bmalloc | Updated submodule revision. |
| 3rd/EasyLogger | Removed submodule entry. |
| .pre-commit-config.yaml | New repo-level pre-commit with Rust fmt + shellcheck. |
| .gitmodules | Removed EasyLogger and some OP-TEE submodules. |
| .gitignore | Added Cargo target/ ignores; reformatted legacy entries. |
| .github/workflows/workflow.yml | Migrated CI from CMake to Rust (fmt/clippy/xtask build/run). |
| .devcontainer/devcontainer.json | Switched devcontainer to Rust analyzer focus. |
| .devcontainer/Dockerfile | Simplified toolchain; installed Rust via rustup. |
| .clang-tidy | Removed legacy clang-tidy config. |
| .clang-format | Removed legacy clang-format config. |
| .cargo/config.toml | Added cargo xtask alias and target rustflags for unwind tables. |
Comments suppressed due to low confidence (5)
xtask/src/qemu.rs:1
dump_qemu_dtbpasses-machine dumpdtb=...as a standalone-machineargument, which makes QEMU treatdumpdtb=...as the machine type (invalid) and also conflicts with the-machine <type>already set inbase_qemu_cmd. Fix by injectingdumpdtb=...into the existing machine string (e.g.,virt,dumpdtb=...) or by changingbase_qemu_cmdto not emit-machineand letting callers provide a single coherent-machineargument.
src_cpp/include/per_cpu.hpp:1- This comment says 'same level nesting is forbidden', but the new lock-stack design explicitly allows same-level double-acquire in at least one case (see the
kMaxDepth = kCount + 1comment and the<(not<=) check inSpinLock::Lock). Please update the comment to match the actual policy (e.g., 'same level generally forbidden except for explicitly allowed cases like dual sched locks') or tighten the enforcement to match the comment.
src/per_cpu.rs:1 core_idis used for pointer arithmetic without a bounds check. The comment says hardware guaranteescore_id < MAX_CORE_COUNT, butcurrent_core_id()derives IDs fromtp(riscv64) /MPIDR_EL1 & 0xFF(aarch64), which can exceed the configured array length in valid configurations (e.g., non-contiguous IDs, more cores than configured, different affinity encoding). Add a defensive bounds check (panic/halt) before indexing to avoid UB.
src/panic.rs:1PanicEvent.reasonis a&strpointing intomsg_buf, which is a stack local. Observers are called synchronously so it's safe duringon_panic, but the API does not prevent an observer from storing the reference and using it later (dangling). Consider documenting the lifetime explicitly (valid only during callback) or changing the observer API to pass an owned/static buffer (e.g., fixed-size[u8; N]message) to make misuse harder.
src/panic.rs:1PanicEvent.reasonis a&strpointing intomsg_buf, which is a stack local. Observers are called synchronously so it's safe duringon_panic, but the API does not prevent an observer from storing the reference and using it later (dangling). Consider documenting the lifetime explicitly (valid only during callback) or changing the observer API to pass an owned/static buffer (e.g., fixed-size[u8; N]message) to make misuse harder.
| run: cargo clippy --target riscv64gc-unknown-none-elf -- -D warnings | ||
|
|
||
| - name: Clippy (aarch64) | ||
| run: cargo clippy --target aarch64-unknown-none -- -D warnings |
There was a problem hiding this comment.
The aarch64 clippy target (aarch64-unknown-none) does not match the target triple used elsewhere (aarch64-unknown-none-softfloat, e.g. xtask/src/arch.rs and .cargo/config.toml). This will break CI or silently lint the wrong target. Update the workflow to use aarch64-unknown-none-softfloat consistently.
| run: cargo clippy --target aarch64-unknown-none -- -D warnings | |
| run: cargo clippy --target aarch64-unknown-none-softfloat -- -D warnings |
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: Niu Zhihong <zhihong@nzhnb.com>
- Add PAGE_SIZE and PAGE_SIZE_BITS constants to config.rs
- Create src/memory/mod.rs and src/memory/address.rs
- PhysAddr/VirtAddr: repr(transparent) newtypes with new(), as_usize(),
page_offset(), is_aligned(), align_down(), align_up() (all const fn)
- Impl Add<usize>, Sub<usize>, Sub<Self>->usize, Display (hex 0x{:016x})
- 5 unit tests covering alignment, arithmetic, and display for both types
- Add mod memory to main.rs (active in both test and non-test modes)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Niu Zhihong <zhihong@nzhnb.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: Niu Zhihong <zhihong@nzhnb.com>
…tAddr Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Niu Zhihong <zhihong@nzhnb.com>
Signed-off-by: Niu Zhihong <zhihong@nzhnb.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: Niu Zhihong <zhihong@nzhnb.com>
Implements Task 5: PageFlags (bitflags, V/R/W/X/U/G/A/D), PageTableEntry (#[repr(transparent)] u64 with arch-gated impls for riscv64, aarch64, and host/test Sv39 fallback), PageTable (owns root + intermediate FrameTrackers, provides map_page/unmap_page/get_mapping via find_or_create_pte walk), and activate_page_table (csrw satp / msr ttbr0_el1 / no-op for host). Unit tests cover PTE round-trip, empty/invalid checks, and flag presets. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: Niu Zhihong <zhihong@nzhnb.com>
- Add memory_init() to src/memory/mod.rs: initializes heap, frame
allocator, builds identity-mapped kernel page table, activates paging
- Add phys_to_virt, virt_to_phys, map_mmio, identity_map_range,
memory_init_smp stubs to src/memory/mod.rs
- Add phase3_smoke_test() to src/main.rs; remove panic!("test panic")
from phase2_smoke_test() so execution reaches P3
- Update both arch bootstrap functions (riscv64, aarch64) to call
memory_init() and phase3_smoke_test() after phase2_smoke_test()
- All 30 existing unit tests continue to pass
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Niu Zhihong <zhihong@nzhnb.com>
…ing) The page table structure is fully built and identity-mapped, but activate_page_table() causes a silent hang because no stvec trap handler is set up yet to report page faults. Deferred to P4 when trap handling is implemented. Also fix unnecessary unsafe block warning for riscv interrupt::disable(). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Niu Zhihong <zhihong@nzhnb.com>
- CLAUDE.md → AGENTS.md symlink (Claude Code auto-loads CLAUDE.md) - Move git commit conventions from NOTES to CONVENTIONS section - Make --signoff requirement explicit and prominent - Add subagent dispatch note for signoff Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Niu Zhihong <zhihong@nzhnb.com>
kernel_rw() lacked X (execute) and G (global) bits — Sv39 hardware checks X on instruction fetch, so enabling paging would immediately trigger an instruction page fault (scause=12). Add kernel_rwx() preset with V|R|W|X|G|A|D for the initial identity mapping, matching the C++ GetKernelPagePermissions() defaults. Also add GLOBAL to all kernel_* presets for TLB efficiency across ASID switches. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Niu Zhihong <zhihong@nzhnb.com>
Summary
UnsafeCell + *mut) toArc<TaskControlBlock>model, aligned with rCore-Tutorial-v3SpinLock<TcbInner>, eliminatinguse-after-freerisksunsafeblocks in riscv64 interrupt enable/disabledead_code/unused_importswarnings for items used in later phases (P3+)Test plan
cargo test)cargo buildfor riscv64/aarch64 targets🤖 Generated with Claude Code