feat(aarch64): first user-space process bootstrap#438
Conversation
702c485 to
a28a0c5
Compare
Introduce the foundational infrastructure for running the first EL0 user-space process on AArch64: - EL0 exception handling: ESR_EL1.EC discrimination routes EL0 SVC to handle_el0_svc (context save/restore via exception_handler_el0! macro) and non-SVC faults to handle_el0_fault (diagnose + retire, no panic). - User-space page tables: AddressSpace abstraction with map_range / unmap_range backed by mmu::map_user_page / unmap_user_page. - Process struct: pid, address_space, threads; Process::try_new(pid). - MemoryAllocator trait: DirectAllocator (Cortex-M) + PageTableAllocator (AArch64); load_elf() generified over the trait. - init_first_process(): loads embedded PIE ELF, creates EL0-targeted thread, queues on BSP. ELF binary cross-compiled and embedded via GN. - Minimal EL0 syscalls: write (user pointer range validation, Phase 4 deferred for mapped-page check) and exit_thread. - dispatch_syscall default case returns -ENOSYS. - TTBR0_EL1 switch on cross-process context switch with tlbi_all + DSB/ISB (ARM DDI 0487 §D8.14.4 compliance). - Integration checker: asserts "Hello from EL0!" on AArch64 boards. Bug fixes applied during review: - real_start() returns user virtual_start, not kernel VA (R_AARCH64_RELATIVE reloc correctness). - write_value_at bounds-checks vaddr+size against virtual_end. - TLBI added to TTBR0_EL1 switch sequence. Tasks 3.2 (PID allocator), 3.5 (PER_CPU cache), 3.6 (ASID) deferred to Phase 4. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ead TTBR0 cleanup, exit_code migration Three changes that unblock Phase 5b (VMA/mmap) and 5c (fork/exec/waitpid): 1. ASID generation allocator: replace panic-on-255 AtomicU8 counter with AsidAllocator (generations/last_used/live/next_tick arrays). Every alloc unconditionally bumps the slot's generation and broadcasts TLBI ASIDE1IS + DSB + ISB — the soundness invariant that prevents stale TLB entries from aliasing a recycled ASID. free() is generation-guarded so a stale Drop can't clear live on a recycled slot. LRU-by-counter recycles the coldest slot when all 255 are live. 2. Scheduler: 3-key (PA, ASID, generation) per-CPU cache. Same-process switch is a full cache hit (no TTBR0 write, no TLBI). Cross-process switch writes TTBR0 + ASIDE1IS(old_asid). Kernel-thread switch (next_proc=None) clears TTBR0 to 0 and broadcasts TLBI VMALLE1IS (Inner-Shareable) + DSB + ISB, closing the stale-state window. 3. exit_code migration: move exit_code: AtomicI32 from Thread to Process with i32::MIN sentinel (not-exited). Release/Acquire ordering for cross-CPU waitpid reads (Phase 5c). exit(code) handler stores on Process via current_thread_ref() (avoids Arc leak since retire_me() diverges). Tests: 27 new tests covering ASID allocator (recycle, generation guard, stale free noop, Critical regression), Process exit_code (sentinel, roundtrip), and scheduler context switch (kernel-thread clears TTBR0, same-process skip, cross-process update, generation in cache key). TLBI spy counters (static mut u64, cfg(test)-gated) make TLBI issuance observable. All pass on qemu_virt64_aarch64; ARM32 builds clean. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Ponytail reductions: consolidate 3 ProcCache statics + 6 accessors into struct ProcCache + 1 array + 2 functions; drop dead debug_assert_eq! in cache-hit branch; remove alloc_asid() wrapper and test_alloc_pid_unique - Make Thread::set_process() pub for integration test access - Add test_process.rs with 10 integration tests covering PID allocation, exit_code, ASID assignment, thread→process back-pointer, sched_yield, and multi-process independence - rustfmt pass over all modified files Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
a28a0c5 to
fa42cf3
Compare
|
build_prs |
|
Job is started, see https://github.com/vivoblueos/kernel/actions/runs/28944497379. |
|
❌ Job failed. Failed jobs: check_format (failure), build_and_check_boards (failure), see https://github.com/vivoblueos/kernel/actions/runs/28944497379. |
|
build_prs |
|
Job is started, see https://github.com/vivoblueos/kernel/actions/runs/28945846235. |
|
❌ Job failed. Failed jobs: check_format (failure), build_and_check_boards (failure), see https://github.com/vivoblueos/kernel/actions/runs/28945846235. |
0282db7 to
60f0d62
Compare
|
build_prs |
|
Job is started, see https://github.com/vivoblueos/kernel/actions/runs/29000589932. |
|
❌ Job failed. Failed jobs: check_format (failure), build_and_check_boards (failure), see https://github.com/vivoblueos/kernel/actions/runs/29000589932. |
60f0d62 to
06815ab
Compare
|
build_prs |
|
Job is started, see https://github.com/vivoblueos/kernel/actions/runs/29004887161. |
|
❌ Job failed. Failed jobs: build_and_check_boards (failure), see https://github.com/vivoblueos/kernel/actions/runs/29004887161. |
- BUILD.gn: remove qemu_mps3_an547 from _is_aarch64; that board is Cortex-M55 (thumbv8m.main), not AArch64, so first_elf and its AArch64 SVC inline-asm must not be compiled for it - syscall_handlers/mod.rs: move 64-bit USER_SPACE_MAX constant and user-space bounds check inside #[cfg(target_arch = "aarch64")]; the literal 0x0000_FFFF_FFFF_FFFF overflows usize on 32-bit targets Verified: qemu_mps2_an385, qemu_mps3_an547, qemu_riscv32, qemu_riscv64 and qemu_virt64_aarch64 all compile and pass check_kernel. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Introduce the foundational infrastructure for running the first EL0 user-space process on AArch64: