//! Transactional fixed-window ELF process creation. No root is installed here. //! Stage identity metadata privately; publish it only after ELF and argv pass. use crate::{ elf, memory::FrameAllocator, mmu::{self, Tables, UserSpace}, process::{self, ProcessId, ProcessTable}, }; #[derive(Debug, PartialEq, Eq)] pub enum Error { Identity(process::Error), Space(mmu::Error), Elf(elf::Error), Rollback, } #[derive(Debug)] pub struct ProcessImage { pub identity: ProcessId, pub space: UserSpace, pub loaded: elf::Loaded, pub initial: elf::StackStart, } /// # Safety /// Exclusive allocator/tables/process-table access, identity-mapped owned RAM, /// single CPU with IRQs masked. The newly built root must never be published /// to any CPU/walker/device until this returns Ok. Therefore rollback can scrub /// it without an ASID retirement; callers must retire translations after use. pub unsafe fn spawn( processes: &mut ProcessTable, allocator: &mut FrameAllocator, tables: &Tables, ram_base: u64, ram_size: u64, file: &[u8], arguments: &[&[u8]], ) -> Result { let mut staged = processes.clone(); let identity = staged.create().map_err(Error::Identity)?; let space = unsafe { tables.build_user_space(allocator, ram_base, ram_size) }.map_err(Error::Space)?; let prepared = (|| { let loaded = unsafe { elf::load_into_user_space(file, &space, tables.root) }.map_err(Error::Elf)?; let stack = unsafe { core::slice::from_raw_parts_mut( space.stack_frame() as *mut u8, crate::memory::PAGE_SIZE as usize, ) }; let initial = elf::prepare_user_stack(stack, arguments).map_err(Error::Elf)?; Ok((loaded, initial)) })(); match prepared { Ok((loaded, initial)) => { *processes = staged; Ok(ProcessImage { identity, space, loaded, initial, }) } Err(error) => { // This root has never been installed: no live translation can // reference its pages. The same consuming retirement method // validates and atomically scrubs/returns all six frames. unsafe { space.release_after_tlbi(allocator, tables.root) } .map_err(|_| Error::Rollback)?; Err(error) } } }