//! Independent K1120 preparation adapter; no switch/ERET capability. use core::{ cell::{Cell, RefCell}, mem, }; use crate::runtime::address_spaces as spaces; use crate::runtime::frame_authority::{Allocator, Lease}; use crate::runtime::frame_metadata as metadata; use crate::runtime::page_tables::descriptor; use crate::runtime::process_table as process; use crate::runtime::process_threads as threads; pub const PAGE: u64 = 4096; pub const CODE: u64 = 0x8000_0000; pub const DATA: u64 = CODE + PAGE; pub const STACK: u64 = CODE + 3 * PAGE; pub const STACK_TOP: u64 = STACK + PAGE; pub const STACK_BACKING_PAGES: usize = 7; pub const STACK_LEASES: usize = STACK_BACKING_PAGES + 1; pub const FRAME_CAPACITY: usize = 32; pub const PROCESS_CAPACITY: usize = 3; const PA_MASK: u64 = 0x0000_ffff_ffff_f000; #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct Registers { pub cpu: u64, pub daif: u64, pub current_el: u64, pub irq_active: bool, pub ttbr: u64, pub tcr: u64, pub sctlr: u64, pub sp: u64, pub spsel: u64, } #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct Execution { pub process: process::Execution, pub ttbr: u64, pub tcr: u64, pub sctlr: u64, pub daif: u64, pub spsel: u64, pub stack_owner: u64, pub stack_low: u64, pub stack_high: u64, } #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct StackBinding { pub owner: u64, pub low: u64, pub high: u64, pub spsel: u64, } #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum Error { Context, Owner, Capacity, Backing, Guard, Image, Drift, Busy, } /// # Safety /// registers is pure: it reads CPU registers and private scalar IRQ state only, /// before any owner/pointer access. Physical callbacks access only the caller's /// validated exclusively owned page. Guards are nonclone retained mapping /// custody; failed restoration returns the same actual token. kernel_branches /// validates the separately owned closed kernel table graph before every pointer /// read and forbids EL0 access. Callbacks never switch, ERET, unmask or sleep. /// completed_tlbi must finish DSB/TLBI/DSB/ISB before returning success. Cache /// synchronization covers every byte of the complete copied code page. /// Failed acquire_guard performs no mapping change; once it changes a PTE it /// must return the actual guard token. It may not discard uncertain custody. pub unsafe trait Hardware { type Guard; fn registers(&self) -> Registers; fn kernel_branches(&self) -> Result<[u64; 2], Error>; fn kernel_entry(&self, address: u64) -> bool; fn guard_candidate(&self, guard: u64, end: u64) -> bool; fn acquire_guard(&self, guard: u64, end: u64) -> Result; fn guard_descriptor(&self, guard: &Self::Guard) -> Result; fn restore_guard(&self, guard: Self::Guard) -> Result<(), (Error, Self::Guard)>; /// # Safety /// The caller holds the live exclusive page owner covering address. unsafe fn read(&self, address: u64) -> u8; /// # Safety /// The caller holds the live exclusive page owner covering address. unsafe fn write(&self, address: u64, value: u8); fn synchronize_code(&self, address: u64) -> Result<(), Error>; fn completed_tlbi(&self, root: u64, asid: u16) -> Result<(), Error>; } /// # Safety /// Implementations own a trusted immutable kernel byte prefix and validate its /// complete header/length before begin. expected_byte describes actual full /// private pages, including zero BSS, bounded argv/env and stack pointers. User /// supplied booleans, lengths, or dangling input slices cannot create this trait. pub unsafe trait PreparedImage { fn entry(&self) -> u64; fn user_sp(&self) -> u64; fn expected_byte(&self, page: usize, offset: usize) -> Result; } pub struct Gate<'a, H: Hardware> { hardware: &'a H, original: Registers, bindings: Cell<[Option; 4]>, faulted: Cell, } impl<'a, H: Hardware> Gate<'a, H> { /// # Safety /// primary is the genuine retained Image-owned guarded kernel stack, with /// stable nonzero owner identity and validated bounds/canaries/guard mapping. /// Its actual authority must outlive this Gate and every child owner loan. pub unsafe fn new(hardware: &'a H, primary: StackBinding) -> Result { let r = hardware.registers(); if primary.owner == 0 || !primary.low.is_multiple_of(16) || !primary.high.is_multiple_of(16) || primary.low >= primary.high || primary.spsel > 1 || r.spsel != primary.spsel || r.sp < primary.low || r.sp > primary.high || !r.sp.is_multiple_of(16) || r.cpu != 0 || r.daif & 128 == 0 || r.current_el != 4 || r.irq_active || r.ttbr == 0 || r.ttbr & !PA_MASK != 0 || r.tcr & 63 != 25 { return Err(Error::Context); } Ok(Self { hardware, original: r, bindings: Cell::new([Some(primary), None, None, None]), faulted: Cell::new(false), }) } pub fn faulted(&self) -> bool { self.faulted.get() } fn capture( &self, current: Option<(process::ProcessId, threads::ThreadId, u64)>, ) -> Result { if self.faulted.get() { return Err(Error::Drift); } let r = self.hardware.registers(); if r.cpu != 0 || r.daif & 128 == 0 || r.current_el != 4 || r.irq_active || r.ttbr != self.original.ttbr || r.tcr != self.original.tcr || r.sctlr != self.original.sctlr || !r.sp.is_multiple_of(16) { return Err(Error::Context); } let bindings = self.bindings.get(); let (index, b) = bindings .iter() .enumerate() .find_map(|(i, b)| { b.filter(|b| r.sp >= b.low && r.sp <= b.high) .map(|b| (i, b)) }) .ok_or(Error::Context)?; if r.spsel != b.spsel { return Err(Error::Context); } let pid = match current { Some((pid, _, owner)) if index != 0 && b.owner == owner => Some(pid), None => None, _ => return Err(Error::Context), }; Ok(Execution { process: process::Execution { cpu: 0, in_irq: false, irq_masked: true, current: pid, kernel_bootstrap: index == 0, }, ttbr: r.ttbr, tcr: r.tcr, sctlr: r.sctlr, daif: r.daif, spsel: r.spsel, stack_owner: b.owner, stack_low: b.low, stack_high: b.high, }) } fn main(&self) -> Result { // Owner backends can also run on a retained process stack. They do not // synthesize a PID; the Process adapter supplies that independently. if self.faulted.get() { return Err(Error::Drift); } let r = self.hardware.registers(); let binding = self .bindings .get() .iter() .flatten() .find(|b| r.sp >= b.low && r.sp <= b.high) .copied() .ok_or(Error::Context)?; if r.cpu != 0 || r.daif & 128 == 0 || r.current_el != 4 || r.irq_active || r.ttbr != self.original.ttbr || r.tcr != self.original.tcr || r.sctlr != self.original.sctlr || !r.sp.is_multiple_of(16) || r.spsel != binding.spsel { return Err(Error::Context); } Ok(Execution { process: process::Execution { cpu: 0, in_irq: false, irq_masked: true, current: None, kernel_bootstrap: binding == self.bindings.get()[0].unwrap(), }, ttbr: r.ttbr, tcr: r.tcr, sctlr: r.sctlr, daif: r.daif, spsel: r.spsel, stack_owner: binding.owner, stack_low: binding.low, stack_high: binding.high, }) } fn after(&self, before: Execution) -> Result<(), Error> { if self.main() != Ok(before) { self.faulted.set(true); return Err(Error::Drift); } Ok(()) } fn add_stack(&self, binding: StackBinding) -> Result<(), Error> { self.main()?; let mut all = self.bindings.get(); if binding.owner == 0 || !binding.low.is_multiple_of(16) || !binding.high.is_multiple_of(16) || binding.spsel != 1 || binding.low >= binding.high || all .iter() .flatten() .any(|b| b.owner == binding.owner || b.low <= binding.high && binding.low <= b.high) { return Err(Error::Owner); } let slot = all .iter_mut() .find(|b| b.is_none()) .ok_or(Error::Capacity)?; *slot = Some(binding); self.bindings.set(all); Ok(()) } fn remove_stack(&self, owner: u64) { let mut all = self.bindings.get(); for b in &mut all[1..] { if b.is_some_and(|b| b.owner == owner) { *b = None; } } self.bindings.set(all); } } pub struct AuthorityCell<'a> { allocator: RefCell<&'a mut Allocator>, } impl<'a> AuthorityCell<'a> { pub fn new(allocator: &'a mut Allocator) -> Self { Self { allocator: RefCell::new(allocator), } } pub fn observe( &self, gate: &Gate<'_, H>, callback: impl FnOnce(&Allocator) -> R, ) -> Result { let before = gate.main()?; let owner = self.allocator.try_borrow().map_err(|_| Error::Busy)?; let result = callback(&owner); gate.after(before)?; Ok(result) } } struct RootMapping { pages: [Option; 3], pins: [Option; 3], } pub struct Root { owner: u64, pages: [Option; 3], pins: [Option; 3], branches: [u64; 2], mapping: RefCell, } pub struct RootBackend<'a, H: Hardware> { store: &'a FrameStore<'a, H>, gate: &'a Gate<'a, H>, failed: RefCell<[Option; 6]>, } impl RootBackend<'_, H> { #[allow(clippy::forget_non_drop)] fn keep(&self, r: Root) { let mut failed = self.failed.borrow_mut(); if let Some(slot) = failed.iter_mut().find(|s| s.is_none()) { *slot = Some(r); } else { mem::forget(r); } self.gate.faulted.set(true); } fn valid_owners(&self, r: &Root) -> Result<(), Error> { self.gate.main()?; for h in &r.pages { self.store.read(h.ok_or(Error::Owner)?, r.owner, 4095)?; } Ok(()) } fn expected(&self, r: &Root, page: usize, index: usize) -> Result { let mapping = r.mapping.try_borrow().map_err(|_| Error::Busy)?; if page == 0 { if index < 2 { return Ok(r.branches[index]); } if index == 2 && mapping.pages[0].is_some() { return Ok(r.pages[1].ok_or(Error::Owner)?.address() | 3); } return Ok(0); } if page == 1 { return Ok(if index == 0 && mapping.pages[0].is_some() { r.pages[2].ok_or(Error::Owner)?.address() | 3 } else { 0 }); } let role = match index { 0 => Some(0), 1 => Some(1), 3 => Some(2), _ => None, }; let Some(role) = role else { return Ok(0) }; let Some(h) = mapping.pages[role] else { return Ok(0); }; descriptor::page( descriptor::Geometry::new(39, 48).map_err(|_| Error::Backing)?, h.address(), descriptor::Memory::Normal, descriptor::Permissions { user: true, write: role != 0, execute: role == 0, }, ) .map_err(|_| Error::Image) } fn validate(&self, r: &Root) -> Result<(), Error> { let before = self.gate.main()?; self.valid_owners(r)?; let branches = self.gate.hardware.kernel_branches(); self.gate.after(before)?; let branches = branches?; if branches != r.branches { return Err(Error::Owner); } { let mapping = r.mapping.try_borrow().map_err(|_| Error::Busy)?; for h in mapping.pages.iter().flatten() { self.store.read(*h, r.owner, 4095)?; } } for (page, h) in r.pages.iter().enumerate() { let h = h.ok_or(Error::Owner)?; for index in 0..512 { if self.store.word(h, r.owner, index)? != self.expected(r, page, index)? { return Err(Error::Backing); } } } Ok(()) } fn discard(&self, r: &mut Root) -> Result<(), Error> { let before = self.gate.main()?; if r.pages[0].is_some_and(|h| h.address() == self.gate.hardware.registers().ttbr & PA_MASK) { return Err(Error::Context); } // Actual synchronous invalidation precedes every data unpin/table free. let root = r.pages[0].map_or(0, |h| h.address()); let result = self.gate.hardware.completed_tlbi(root, 0); self.gate.after(before)?; result?; { let mut mapping = r.mapping.try_borrow_mut().map_err(|_| Error::Busy)?; for i in (0..3).rev() { if let Some(pin) = mapping.pins[i].as_mut() { self.store.unpin(pin, r.owner)?; mapping.pins[i] = None; mapping.pages[i] = None; self.gate.after(before)?; } } } for i in (0..3).rev() { if let Some(pin) = r.pins[i].as_mut() { self.store.unpin(pin, r.owner)?; r.pins[i] = None; self.gate.after(before)?; } if let Some(h) = r.pages[i] { self.store.release(h, r.owner)?; r.pages[i] = None; self.gate.after(before)?; } } Ok(()) } fn install(&self, r: &Root, pages: [metadata::Handle; 3]) -> Result<(), Error> { let before = self.gate.main()?; self.validate(r)?; if r.mapping .try_borrow() .map_err(|_| Error::Busy)? .pages .iter() .any(Option::is_some) { return Err(Error::Owner); } for h in pages { self.store.read(h, r.owner, 4095)?; } { let mut mapping = r.mapping.try_borrow_mut().map_err(|_| Error::Busy)?; for (i, h) in pages.into_iter().enumerate() { let pin = self.store.pin(h, r.owner)?; mapping.pins[i] = Some(pin); mapping.pages[i] = Some(h); self.gate.after(before)?; } } // Both roots are inactive and not publicly runnable. Any partial table // write remains inside the still-retained root/Bag, never a live prefix. for page in (0..3).rev() { let h = r.pages[page].ok_or(Error::Owner)?; for index in 0..512 { self.store .write_word(h, r.owner, index, self.expected(r, page, index)?)?; } } self.validate(r)?; self.gate.after(before) } } impl spaces::Backend for RootBackend<'_, H> { type Root = Root; fn acquire_root(&mut self, owner: u64) -> Result { let before = self.gate.main().map_err(|_| spaces::Error::Backing)?; let branches = self.gate.hardware.kernel_branches(); self.gate .after(before) .map_err(|_| spaces::Error::Backing)?; let branches = branches.map_err(|_| spaces::Error::Backing)?; let mut root = Root { owner, pages: [None; 3], pins: core::array::from_fn(|_| None), branches, mapping: RefCell::new(RootMapping { pages: [None; 3], pins: core::array::from_fn(|_| None), }), }; for i in 0..3 { let h = match self.store.allocate(owner) { Ok(h) => h, Err(_) => { if self.discard(&mut root).is_err() { self.keep(root); } return Err(spaces::Error::NoMemory); } }; root.pages[i] = Some(h); if self.gate.after(before).is_err() { self.keep(root); return Err(spaces::Error::Backing); } match self.store.pin(h, owner) { Ok(p) => root.pins[i] = Some(p), Err(_) => { if self.discard(&mut root).is_err() { self.keep(root); } return Err(spaces::Error::Backing); } } } for (i, raw) in branches.into_iter().enumerate() { if self .store .write_word(root.pages[0].unwrap(), owner, i, raw) .is_err() { if self.discard(&mut root).is_err() { self.keep(root); } return Err(spaces::Error::Backing); } } if self.validate(&root).is_err() { if self.discard(&mut root).is_err() { self.keep(root); } return Err(spaces::Error::Backing); } Ok(root) } fn root_address(&self, r: &Root) -> u64 { r.pages[0].map_or(0, |h| h.address()) } fn validate_root(&self, r: &Root) -> Result<(), spaces::Error> { self.validate(r).map_err(|_| spaces::Error::Backing) } fn invalidate_recycled(&mut self, asid: u16, _old: u64, _new: u64) { let result = self.gate.main().and_then(|before| { let result = self.gate.hardware.completed_tlbi(0, asid); self.gate.after(before)?; result }); if result.is_err() { self.gate.faulted.set(true); } } fn select(&mut self, _r: &Root, _asid: u16) -> Result<(), spaces::Error> { Err(spaces::Error::Active) } fn restore_kernel(&mut self) -> Result<(), spaces::Error> { self.gate .main() .map(|_| ()) .map_err(|_| spaces::Error::Active) } fn retire_root( &mut self, mut r: Root, asid: u16, _generation: u64, ) -> Result<(), (spaces::Error, Root)> { let before = match self.gate.main() { Ok(b) => b, Err(_) => return Err((spaces::Error::Backing, r)), }; let result = self .gate .hardware .completed_tlbi(self.root_address(&r), asid); if self.gate.after(before).is_err() || result.is_err() { return Err((spaces::Error::Retirement, r)); } if self.discard(&mut r).is_err() { return Err((spaces::Error::Retirement, r)); } Ok(()) } } pub struct Bag { pid: process::ProcessId, owner: u64, thread: Option, pending: Option>, spaces: [Option; 2], handles: [Option; 8], pins: [Option; 3], installed: bool, published: bool, } #[allow(clippy::forget_non_drop)] impl Drop for Bag { fn drop(&mut self) { if let Some(stack) = self.pending.take() { mem::forget(stack); } for pin in &mut self.pins { if let Some(pin) = pin.take() { mem::forget(pin); } } } } pub struct Owners<'a, H: Hardware, I: PreparedImage> { gate: &'a Gate<'a, H>, authority: &'a AuthorityCell<'a>, store: &'a FrameStore<'a, H>, image: &'a I, threads: threads::Table, 3>, spaces: spaces::Spaces, 6, 6>, current: Cell>, ready: [Option<(process::ProcessId, threads::ThreadId)>; 3], next_owner: u64, kernel_trampoline: u64, #[cfg(all(feature = "host-fixture", not(target_os = "none")))] landing_owner_reads: Cell, } impl<'a, H: Hardware, I: PreparedImage> Owners<'a, H, I> { #[cfg(all(feature = "host-fixture", not(target_os = "none")))] pub fn landing_owner_reads_for_host(&self) -> usize { self.landing_owner_reads.get() } #[cfg(all(feature = "host-fixture", not(target_os = "none")))] pub fn thread_layout_for_host( &self, pid: process::ProcessId, ) -> Result { self.before()?; let (_, id) = self .ready .iter() .flatten() .find(|(p, _)| *p == pid) .copied() .ok_or(Error::Owner)?; self.threads .record(id) .map(|r| r.layout) .map_err(|_| Error::Owner) } #[cfg(all(feature = "host-fixture", not(target_os = "none")))] pub fn space_roots_for_host(&self) -> Result<[Option; 6], Error> { self.before()?; let mut rows = [None; 6]; for (slot, row) in self.spaces.owned_records().enumerate() { rows[slot] = Some(row); } Ok(rows) } pub fn new( gate: &'a Gate<'a, H>, authority: &'a AuthorityCell<'a>, store: &'a FrameStore<'a, H>, image: &'a I, namespace: u64, configuration: spaces::Configuration, kernel_trampoline: u64, ) -> Result { let before = gate.capture(None)?; if namespace == 0 { return Err(Error::Image); } let executable = gate.hardware.kernel_entry(kernel_trampoline); gate.after(before)?; let entry = image.entry(); gate.after(before)?; let user_sp = image.user_sp(); gate.after(before)?; if !executable || !(CODE..CODE + PAGE).contains(&entry) || !entry.is_multiple_of(4) || !(STACK..=STACK_TOP).contains(&user_sp) || !user_sp.is_multiple_of(16) { return Err(Error::Image); } gate.after(before)?; // Full immutable image preflight occurs before PID reservation or frame // acquisition; callbacks cannot change execution between owner stages. for page in 0..3 { for offset in 0..4096 { let result = image.expected_byte(page, offset); gate.after(before)?; result?; } } let threads = threads::Table::new(StackBackend { authority, gate }).map_err(|_| Error::Context)?; let spaces = spaces::Spaces::new( namespace, configuration, RootBackend { store, gate, failed: RefCell::new(core::array::from_fn(|_| None)), }, ) .map_err(|_| Error::Owner)?; Ok(Self { gate, authority, store, image, threads, spaces, current: Cell::new(None), ready: [None; 3], next_owner: namespace, kernel_trampoline, #[cfg(all(feature = "host-fixture", not(target_os = "none")))] landing_owner_reads: Cell::new(0), }) } fn before(&self) -> Result { self.gate.capture(self.current.get()) } fn after(&self, before: Execution) -> Result<(), Error> { if self.before() != Ok(before) { self.gate.faulted.set(true); Err(Error::Drift) } else { Ok(()) } } pub fn ready(&self) -> Result<[Option<(process::ProcessId, threads::ThreadId)>; 3], Error> { self.before()?; Ok(self.ready) } pub fn inventory(&self) -> Result<[u64; 4], Error> { let before = self.before()?; let threads = self.threads.snapshot(); self.after(before)?; let spaces = self.spaces.owned_records().count(); self.after(before)?; let frames = self.store.snapshot()?; self.after(before)?; let live = self.authority.observe(self.gate, |a| { a.audit().map(|a| a.live_pages).unwrap_or(u64::MAX) })?; self.after(before)?; Ok([ threads.live as u64, spaces as u64, frames.live_pages as u64, live, ]) } fn observe_inner(&self, bag: &Bag) -> Result { let before = self.before().map_err(|_| process::Error::Context)?; let id = bag.thread.ok_or(process::Error::Preparation)?; let thread = self .threads .record(id) .map_err(|_| process::Error::Resource)?; self.after(before).map_err(|_| process::Error::Context)?; if thread.entry != self.kernel_trampoline || thread.argument != bag.owner { return Err(process::Error::Resource); } if thread.state == threads::State::Ready { // Refresh the actual retained guard/canaries through the frozen // Threads owner before admitting a process to the private list. let ready = self.threads.ready_context(id); self.after(before).map_err(|_| process::Error::Context)?; let ready = ready.map_err(|_| process::Error::Preparation)?; if ready.id != id || ready.entry != self.kernel_trampoline || ready.argument != bag.owner || ready.initial_sp != thread.layout.usable_top() { return Err(process::Error::Resource); } } let listed = self .ready .iter() .filter(|slot| slot.is_some_and(|(pid, tid)| pid == bag.pid && tid == id)) .count(); let published = bag.published && if thread.state == threads::State::Dead { listed == 0 } else { listed == 1 }; for h in bag.spaces.iter().flatten() { self.spaces .observe(*h, bag.owner) .map_err(|_| process::Error::Resource)?; self.after(before).map_err(|_| process::Error::Context)?; } for h in bag.handles.iter().flatten() { self.store .read(*h, bag.owner, 4095) .map_err(|_| process::Error::Resource)?; self.after(before).map_err(|_| process::Error::Context)?; } let mut complete = bag.installed; if complete { // Immutable RX code is checked throughout its retained lifetime. // RW data/stack contents are preparation/publication witnesses; // legitimate later user writes do not undo that completed stage. for page in 0..if bag.published { 1 } else { 3 } { let h = bag.handles[page].ok_or(process::Error::Preparation)?; for offset in 0..4096 { let actual = self .store .read(h, bag.owner, offset) .map_err(|_| process::Error::Resource)?; self.after(before).map_err(|_| process::Error::Context)?; let expected = self.image.expected_byte(page, offset); self.after(before).map_err(|_| process::Error::Context)?; let expected = expected.map_err(|_| process::Error::Preparation)?; if actual != expected { complete = false; } } } } Ok(process::BagObservation { process: bag.pid, resource_owner: bag.owner, thread: id, state: thread.state, spaces: bag.spaces, handles: bag.handles, pages: STACK_LEASES as u64 + bag.spaces.iter().flatten().count() as u64 * 3 + bag.handles.iter().flatten().count() as u64, image_ready: complete, stack_ready: complete && thread.layout.bytes == STACK_BACKING_PAGES as u64 * PAGE, published, }) } fn build_failure( &self, bag: Bag, before: Execution, error: process::Error, ) -> process::BuildFailure> { process::BuildFailure { failure: process::Failure { error, boundary: if self.after(before).is_ok() { process::Boundary::Before } else { process::Boundary::After }, }, partial: Some(bag), } } /// # Safety /// Called only after actual assembly has landed on this real retained /// thread's stack, with all prior mutable loans ended. This method does not /// perform a switch. PID and ThreadId must come from the actual published /// record; a caller-supplied scalar cannot establish a landing. pub unsafe fn register_actual_landing(&mut self, pid: process::ProcessId) -> Result<(), Error> { // Even the private published Ready records are owner observations. // Unsafe landing provenance never bypasses this fresh pure gate. let main = self.gate.main()?; if self.current.get().is_some() { return Err(Error::Context); } #[cfg(all(feature = "host-fixture", not(target_os = "none")))] self.landing_owner_reads .set(self.landing_owner_reads.get() + 1); let (_, thread) = self .ready .iter() .flatten() .find(|(id, _)| *id == pid) .copied() .ok_or(Error::Owner)?; let record = self.threads.record(thread).map_err(|_| Error::Owner)?; self.gate.after(main)?; if main.stack_owner == self.gate.bindings.get()[0].unwrap().owner || self.gate.hardware.registers().sp < record.layout.usable_bottom() || self.gate.hardware.registers().sp > record.layout.usable_top() { return Err(Error::Context); } self.threads.activate(thread).map_err(|_| Error::Owner)?; // Running has now changed in the actual frozen Table. Failed fresh // observation quarantines that truthful state and all retained owners. self.gate.after(main)?; self.current.set(Some((pid, thread, main.stack_owner))); self.before()?; Ok(()) } /// # Safety /// Called on the actual current private stack before a separately trusted /// assembly departure. No owner loan may cross that departure. The caller /// must restore the original primary stack before bootstrap operations. pub unsafe fn release_actual_current_to_ready(&mut self) -> Result<(), Error> { let before = self.before()?; let (_, thread, _) = self.current.get().ok_or(Error::Context)?; self.threads .yield_current(thread) .map_err(|_| Error::Owner)?; self.after(before)?; self.current.set(None); Ok(()) } } unsafe impl process::Backend for Owners<'_, H, I> { type Bag = Bag; fn execution(&self) -> process::Execution { self.before().map_or( process::Execution { cpu: u16::MAX, in_irq: true, irq_masked: false, current: None, kernel_bootstrap: false, }, |e| e.process, ) } fn prepare( &mut self, pid: process::ProcessId, spawn: process::Spawn, ) -> Result, process::BuildFailure>> { let before = match self.before() { Ok(b) => b, Err(_) => { return Err(process::BuildFailure { failure: process::Failure { error: process::Error::Context, boundary: process::Boundary::Before, }, partial: None, }); } }; let entry = self.image.entry(); if self.after(before).is_err() { return Err(process::BuildFailure { failure: process::Failure { error: process::Error::Context, boundary: process::Boundary::After, }, partial: None, }); } if spawn.handles != 3 || spawn.code_entry != entry || spawn.user_stack_top != STACK_TOP { return Err(process::BuildFailure { failure: process::Failure { error: process::Error::Preparation, boundary: process::Boundary::Before, }, partial: None, }); } let Some(owner) = self.next_owner.checked_add(1) else { return Err(process::BuildFailure { failure: process::Failure { error: process::Error::Exhausted, boundary: process::Boundary::Before, }, partial: None, }); }; self.next_owner = owner; let mut bag = Bag { pid, owner, thread: None, pending: None, spaces: [None; 2], handles: [None; 8], pins: core::array::from_fn(|_| None), installed: false, published: false, }; let stack_backend = StackBackend { authority: self.authority, gate: self.gate, }; let stack = match stack_backend.acquire(owner) { Ok(stack) => stack, Err(_) => return Err(self.build_failure(bag, before, process::Error::Resource)), }; bag.pending = Some(stack); let stack = bag.pending.take().unwrap(); match self.threads.create(stack, self.kernel_trampoline, owner) { Ok(id) => bag.thread = Some(id), Err((_, stack)) => { bag.pending = Some(stack); return Err(self.build_failure(bag, before, process::Error::Resource)); } } if self.after(before).is_err() { return Err(self.build_failure(bag, before, process::Error::Context)); } for slot in 0..2 { match self.spaces.create(owner) { Ok(h) => bag.spaces[slot] = Some(h), Err(_) => return Err(self.build_failure(bag, before, process::Error::Resource)), } if self.after(before).is_err() { return Err(self.build_failure(bag, before, process::Error::Context)); } } for page in 0..3 { let h = match self.store.allocate(owner) { Ok(h) => h, Err(_) => return Err(self.build_failure(bag, before, process::Error::Resource)), }; bag.handles[page] = Some(h); if self.after(before).is_err() { return Err(self.build_failure(bag, before, process::Error::Context)); } match self.store.pin(h, owner) { Ok(pin) => bag.pins[page] = Some(pin), Err(_) => return Err(self.build_failure(bag, before, process::Error::Resource)), } for offset in 0..4096 { let byte = match self.image.expected_byte(page, offset) { Ok(b) => b, Err(_) => { return Err(self.build_failure(bag, before, process::Error::Preparation)); } }; if self.after(before).is_err() || self.store.write(h, owner, offset, byte).is_err() { return Err(self.build_failure(bag, before, process::Error::Context)); } } } let code = bag.handles[0].unwrap().address(); if self.gate.hardware.synchronize_code(code).is_err() || self.after(before).is_err() { return Err(self.build_failure(bag, before, process::Error::Preparation)); } let pages = core::array::from_fn(|i| bag.handles[i].unwrap()); for h in bag.spaces.iter().flatten() { let result = self .spaces .with_root_observation(*h, owner, |backend, root| backend.install(root, pages)); if !matches!(result, Ok(Ok(()))) || self.after(before).is_err() { return Err(self.build_failure(bag, before, process::Error::Preparation)); } } bag.installed = true; Ok(bag) } fn observe(&self, bag: &Bag) -> Result { self.observe_inner(bag) } fn publish(&mut self, bag: &mut Bag) -> Result<(), process::Failure> { let before = self.before().map_err(|_| process::Failure { error: process::Error::Context, boundary: process::Boundary::Before, })?; let fail = |error| process::Failure { error, boundary: process::Boundary::Before, }; let observed = self.observe_inner(bag).map_err(fail)?; if !observed.image_ready || !observed.stack_ready || observed.published || observed.state != threads::State::Ready { return Err(fail(process::Error::Publication)); } let slot = self .ready .iter_mut() .find(|r| r.is_none()) .ok_or(fail(process::Error::Capacity))?; *slot = Some((bag.pid, bag.thread.unwrap())); bag.published = true; if self.after(before).is_err() { return Err(process::Failure { error: process::Error::Context, boundary: process::Boundary::After, }); } Ok(()) } fn exit(&mut self, bag: &mut Bag, _code: i32) -> Result<(), process::Failure> { let before = self.before().map_err(|_| process::Failure { error: process::Error::Context, boundary: process::Boundary::Before, })?; let id = bag.thread.ok_or(process::Failure { error: process::Error::Resource, boundary: process::Boundary::Before, })?; let result = if self .current .get() .is_some_and(|(_, thread, _)| thread == id) { self.threads.exit_current(id) } else { self.threads.cancel(id) }; if result.is_err() { return Err(process::Failure { error: process::Error::Resource, boundary: if self.after(before).is_ok() { process::Boundary::Before } else { process::Boundary::After }, }); } if self.after(before).is_err() { return Err(process::Failure { error: process::Error::Context, boundary: process::Boundary::After, }); } if self .current .get() .is_some_and(|(_, thread, _)| thread == id) { self.current.set(None); } for slot in &mut self.ready { if slot.is_some_and(|(pid, _)| pid == bag.pid) { *slot = None; } } Ok(()) } fn release(&mut self, mut bag: Bag) -> Result<(), (process::Error, Bag)> { let before = match self.before() { Ok(b) => b, Err(_) => return Err((process::Error::Context, bag)), }; if let Some(id) = bag.thread { let record = match self.threads.record(id) { Ok(r) => r, Err(_) => return Err((process::Error::Resource, bag)), }; if self.after(before).is_err() { return Err((process::Error::Context, bag)); } if record.state == threads::State::Running { return Err((process::Error::Busy, bag)); } if record.state != threads::State::Dead { if self.threads.cancel(id).is_err() { return Err((process::Error::Resource, bag)); } if self.after(before).is_err() { return Err((process::Error::Context, bag)); } } if self.threads.reap(id).is_err() { return Err((process::Error::Resource, bag)); } bag.thread = None; if self.after(before).is_err() { return Err((process::Error::Context, bag)); } } if let Some(stack) = bag.pending.take() { let mut backend = StackBackend { authority: self.authority, gate: self.gate, }; if let Err((_, stack)) = threads::Backend::release(&mut backend, stack) { bag.pending = Some(stack); return Err((process::Error::Resource, bag)); } if self.after(before).is_err() { return Err((process::Error::Context, bag)); } } for slot in (0..2).rev() { if let Some(h) = bag.spaces[slot] { if self.spaces.release(h, bag.owner).is_err() { return Err((process::Error::Resource, bag)); } bag.spaces[slot] = None; if self.after(before).is_err() { return Err((process::Error::Context, bag)); } } } for slot in (0..3).rev() { if let Some(pin) = bag.pins[slot].as_mut() { if self.store.unpin(pin, bag.owner).is_err() { return Err((process::Error::Resource, bag)); } bag.pins[slot] = None; if self.after(before).is_err() { return Err((process::Error::Context, bag)); } } if let Some(h) = bag.handles[slot] { if self.store.release(h, bag.owner).is_err() { return Err((process::Error::Resource, bag)); } bag.handles[slot] = None; if self.after(before).is_err() { return Err((process::Error::Context, bag)); } } } for slot in &mut self.ready { if slot.is_some_and(|(pid, _)| pid == bag.pid) { *slot = None; } } bag.published = false; Ok(()) } } pub struct Frames<'a, H: Hardware> { authority: &'a AuthorityCell<'a>, gate: &'a Gate<'a, H>, leases: [Option; FRAME_CAPACITY], } impl Frames<'_, H> { fn validate(&self, address: u64) -> Result<(), metadata::Error> { self.gate.main().map_err(|_| metadata::Error::Backend)?; let lease = self .leases .iter() .flatten() .find(|l| l.address() == address) .ok_or(metadata::Error::BadAddress)?; self.authority .allocator .try_borrow() .map_err(|_| metadata::Error::Backend)? .validate_lease(lease) .map_err(|_| metadata::Error::Stale) } } impl metadata::Backend for Frames<'_, H> { fn allocate(&mut self) -> Result { let before = self.gate.main().map_err(|_| metadata::Error::Backend)?; let slot = self .leases .iter() .position(Option::is_none) .ok_or(metadata::Error::Capacity)?; let lease = self .authority .allocator .try_borrow_mut() .map_err(|_| metadata::Error::Backend)? .allocate() .map_err(|_| metadata::Error::NoMemory)?; let address = lease.address(); self.leases[slot] = Some(lease); // No foreign callback occurs between the atomic acquisition and this // custody assignment. Any later drift keeps that actual lease here. self.gate .after(before) .map_err(|_| metadata::Error::Backend)?; Ok(address) } fn release(&mut self, address: u64) -> Result<(), metadata::Error> { let before = self.gate.main().map_err(|_| metadata::Error::Backend)?; self.validate(address)?; let slot = self .leases .iter() .position(|l| l.as_ref().is_some_and(|l| l.address() == address)) .ok_or(metadata::Error::BadAddress)?; for offset in 0..4096 { unsafe { self.gate.hardware.write(address + offset, 0) }; self.gate .after(before) .map_err(|_| metadata::Error::Backend)?; } for offset in 0..4096 { let byte = unsafe { self.gate.hardware.read(address + offset) }; self.gate .after(before) .map_err(|_| metadata::Error::Backend)?; if byte != 0 { return Err(metadata::Error::Erasure); } } self.authority .allocator .try_borrow_mut() .map_err(|_| metadata::Error::Backend)? .release(self.leases[slot].as_ref().ok_or(metadata::Error::Backend)?) .map_err(|_| metadata::Error::Backend)?; self.leases[slot] = None; // If consumption succeeded, never reconstruct its token. The outer // layer independently observes drift before touching another owner. let _ = self.gate.after(before); Ok(()) } fn zero_page(&mut self, address: u64) -> Result<(), metadata::Error> { let before = self.gate.main().map_err(|_| metadata::Error::Backend)?; self.validate(address)?; for offset in 0..4096 { unsafe { self.gate.hardware.write(address + offset, 0) }; self.gate .after(before) .map_err(|_| metadata::Error::Backend)?; } Ok(()) } fn read_byte(&self, address: u64, offset: usize) -> Result { let before = self.gate.main().map_err(|_| metadata::Error::Backend)?; if offset >= 4096 { return Err(metadata::Error::BadAddress); } self.validate(address)?; let byte = unsafe { self.gate.hardware.read(address + offset as u64) }; self.gate .after(before) .map_err(|_| metadata::Error::Backend)?; Ok(byte) } fn available_pages(&self) -> u64 { if self.gate.main().is_err() { return 0; } self.leases.iter().filter(|l| l.is_none()).count().min( self.authority .allocator .try_borrow() .map_or(0, |a| a.remaining_pages() as usize), ) as u64 } } pub struct FrameStore<'a, H: Hardware> { registry: RefCell, FRAME_CAPACITY>>, gate: &'a Gate<'a, H>, } impl<'a, H: Hardware> FrameStore<'a, H> { pub fn new( identity: u64, authority: &'a AuthorityCell<'a>, gate: &'a Gate<'a, H>, ) -> Result { gate.main()?; let frames = Frames { authority, gate, leases: core::array::from_fn(|_| None), }; Ok(Self { registry: RefCell::new( metadata::Registry::new(identity, frames).map_err(|_| Error::Owner)?, ), gate, }) } fn read(&self, h: metadata::Handle, owner: u64, offset: usize) -> Result { self.gate.main()?; self.registry .try_borrow() .map_err(|_| Error::Busy)? .read_byte(h, owner, offset) .map_err(|_| Error::Owner) } fn write(&self, h: metadata::Handle, owner: u64, offset: usize, byte: u8) -> Result<(), Error> { let before = self.gate.main()?; if offset >= 4096 { return Err(Error::Backing); } self.read(h, owner, offset)?; self.gate.after(before)?; unsafe { self.gate.hardware.write(h.address() + offset as u64, byte) }; self.gate.after(before) } fn word(&self, h: metadata::Handle, owner: u64, index: usize) -> Result { if index >= 512 { return Err(Error::Backing); } let mut bytes = [0; 8]; for (i, b) in bytes.iter_mut().enumerate() { *b = self.read(h, owner, index * 8 + i)?; } Ok(u64::from_le_bytes(bytes)) } fn write_word( &self, h: metadata::Handle, owner: u64, index: usize, value: u64, ) -> Result<(), Error> { if index >= 512 { return Err(Error::Backing); } for (i, b) in value.to_le_bytes().into_iter().enumerate() { self.write(h, owner, index * 8 + i, b)?; } Ok(()) } fn allocate(&self, owner: u64) -> Result { let before = self.gate.main()?; let mut registry = self.registry.try_borrow_mut().map_err(|_| Error::Busy)?; let quarantined = registry.snapshot().quarantined_pages; let result = registry.allocate_zeroed(owner); // Metadata inspection also stops after callback execution drift. self.gate.after(before)?; if registry.snapshot().quarantined_pages != quarantined { self.gate.faulted.set(true); return Err(Error::Backing); } result.map_err(|_| Error::Capacity) } fn pin(&self, h: metadata::Handle, owner: u64) -> Result { self.gate.main()?; self.registry .try_borrow_mut() .map_err(|_| Error::Busy)? .pin(h, owner) .map_err(|_| Error::Owner) } fn unpin(&self, p: &mut metadata::Pin, owner: u64) -> Result<(), Error> { self.gate.main()?; self.registry .try_borrow_mut() .map_err(|_| Error::Busy)? .unpin(p, owner) .map_err(|_| Error::Owner) } fn release(&self, h: metadata::Handle, owner: u64) -> Result<(), Error> { self.gate.main()?; self.registry .try_borrow_mut() .map_err(|_| Error::Busy)? .release(h, owner) .map_err(|_| Error::Backing) } pub fn snapshot(&self) -> Result { self.gate.main()?; Ok(self .registry .try_borrow() .map_err(|_| Error::Busy)? .snapshot()) } } pub struct Stack { owner: u64, start: u64, leases: [Option; 8], guard: Option, initial: Cell, } impl Stack { fn layout(&self) -> threads::StackLayout { threads::StackLayout { base: self.start + PAGE, physical: self.start + PAGE, bytes: STACK_BACKING_PAGES as u64 * PAGE, } } } pub struct StackBackend<'a, H: Hardware> { authority: &'a AuthorityCell<'a>, gate: &'a Gate<'a, H>, } impl<'a, H: Hardware> StackBackend<'a, H> { fn acquire(&self, owner: u64) -> Result, Error> { let before = self.gate.main()?; let mut allocator = self .authority .allocator .try_borrow_mut() .map_err(|_| Error::Busy)?; let mut selected = None; for region in allocator.free_regions() { let mut start = region.start; while start .checked_add(STACK_LEASES as u64 * PAGE) .is_some_and(|end| end <= region.end) { let candidate = self .gate .hardware .guard_candidate(start, start + STACK_LEASES as u64 * PAGE); self.gate.after(before)?; if candidate { selected = Some(start); break; } start = start.checked_add(PAGE).ok_or(Error::Backing)?; } if selected.is_some() { break; } } let start = selected.ok_or(Error::Capacity)?; let leases = allocator .allocate_many_at(start, STACK_LEASES) .map_err(|_| Error::Capacity)?; Ok(Stack { owner, start, leases, guard: None, initial: Cell::new(false), }) } fn validate(&self, stack: &Stack) -> Result<(), threads::Error> { self.gate.main().map_err(|_| threads::Error::Context)?; if stack.owner == 0 || !stack.start.is_multiple_of(PAGE) || stack.leases[STACK_LEASES..].iter().any(Option::is_some) { return Err(threads::Error::Layout); } let a = self .authority .allocator .try_borrow() .map_err(|_| threads::Error::Backend)?; for (i, l) in stack.leases[..STACK_LEASES].iter().enumerate() { let l = l.as_ref().ok_or(threads::Error::Backend)?; if l.address() != stack.start + i as u64 * PAGE { return Err(threads::Error::Layout); } a.validate_lease(l).map_err(|_| threads::Error::Backend)?; } Ok(()) } fn byte(&self, address: u64) -> Result { let before = self.gate.main().map_err(|_| threads::Error::Context)?; let b = unsafe { self.gate.hardware.read(address) }; self.gate .after(before) .map_err(|_| threads::Error::Context)?; Ok(b) } fn word(&self, address: u64) -> Result { let mut b = [0; 8]; for (i, v) in b.iter_mut().enumerate() { *v = self.byte(address + i as u64)?; } Ok(u64::from_le_bytes(b)) } } unsafe impl threads::Backend for StackBackend<'_, H> { type Stack = Stack; fn execution(&self) -> threads::Execution { let r = self.gate.hardware.registers(); threads::Execution { cpu: if self.gate.main().is_ok() { 0 } else { u32::MAX }, irq_masked: r.daif & 128 != 0, in_irq: r.irq_active, sp: r.sp, } } fn layout(&self, s: &Stack) -> Result { self.validate(s)?; Ok(s.layout()) } fn entry_executable(&self, e: u64) -> bool { let Ok(before) = self.gate.main() else { return false; }; let valid = self.gate.hardware.kernel_entry(e); self.gate.after(before).is_ok() && valid } fn prepare(&mut self, s: &mut Stack) -> Result<(), threads::Error> { let before = self.gate.main().map_err(|_| threads::Error::Context)?; self.validate(s)?; if s.guard.is_some() { return Err(threads::Error::Guard); } let layout = s.layout(); let r = self.gate.hardware.registers(); if r.sp >= s.start && r.sp <= layout.base + layout.bytes { return Err(threads::Error::ActiveStack); } let token = self .gate .hardware .acquire_guard(s.start, layout.base + layout.bytes) .map_err(|_| threads::Error::Guard)?; s.guard = Some(token); self.gate .after(before) .map_err(|_| threads::Error::Context)?; for offset in 0..layout.bytes { unsafe { self.gate.hardware.write(layout.base + offset, 0) }; self.gate .after(before) .map_err(|_| threads::Error::Context)?; } for (address, value) in [ (layout.base, threads::LOW_CANARY), (layout.base + layout.bytes - 8, threads::HIGH_CANARY), ] { for (i, b) in value.to_le_bytes().into_iter().enumerate() { unsafe { self.gate.hardware.write(address + i as u64, b) }; self.gate .after(before) .map_err(|_| threads::Error::Context)?; } } self.gate .add_stack(StackBinding { owner: s.owner, low: layout.usable_bottom(), high: layout.usable_top(), spsel: 1, }) .map_err(|_| threads::Error::Backend)?; s.initial.set(true); Ok(()) } fn observe(&self, s: &Stack) -> Result { let before = self.gate.main().map_err(|_| threads::Error::Context)?; self.validate(s)?; let layout = s.layout(); let descriptor = self .gate .hardware .guard_descriptor(s.guard.as_ref().ok_or(threads::Error::Guard)?) .map_err(|_| threads::Error::Guard)?; self.gate .after(before) .map_err(|_| threads::Error::Context)?; let initial = s.initial.get(); if initial { for offset in 8..layout.bytes - 8 { if self.byte(layout.base + offset)? != 0 { return Err(threads::Error::NotZeroed); } } s.initial.set(false); } Ok(threads::StackObservation { layout, guard_descriptor: descriptor, low_canary: self.word(layout.base)?, high_canary: self.word(layout.base + layout.bytes - 8)?, initially_zeroed: initial, }) } fn release(&mut self, mut s: Stack) -> Result<(), (threads::Error, Stack)> { if let Err(e) = self.validate(&s) { return Err((e, s)); } let before = match self.gate.main() { Ok(e) => e, Err(_) => return Err((threads::Error::Context, s)), }; let layout = s.layout(); let sp = self.gate.hardware.registers().sp; if sp >= s.start && sp <= layout.base + layout.bytes { return Err((threads::Error::ActiveStack, s)); } if let Some(token) = s.guard.take() { if let Err((_, token)) = self.gate.hardware.restore_guard(token) { s.guard = Some(token); return Err((threads::Error::Guard, s)); } if self.gate.after(before).is_err() { return Err((threads::Error::Context, s)); } } for offset in 0..STACK_LEASES as u64 * PAGE { unsafe { self.gate.hardware.write(s.start + offset, 0) }; if self.gate.after(before).is_err() { return Err((threads::Error::Context, s)); } } for offset in 0..STACK_LEASES as u64 * PAGE { let byte = unsafe { self.gate.hardware.read(s.start + offset) }; if self.gate.after(before).is_err() || byte != 0 { return Err((threads::Error::Backend, s)); } } let refs = core::array::from_fn::<_, STACK_LEASES, _>(|i| s.leases[i].as_ref().unwrap()); let released = self .authority .allocator .try_borrow_mut() .map_err(|_| ()) .and_then(|mut a| a.release_many_leases(&refs).map_err(|_| ())); if released.is_err() { return Err((threads::Error::Backend, s)); } for l in &mut s.leases { *l = None; } self.gate.remove_stack(s.owner); let _ = self.gate.after(before); Ok(()) } } unsafe impl process::LandingBackend for Owners<'_, H, I> { fn landing_stack(&self, pid: process::ProcessId) -> Result<(u64, u64), process::Error> { let before = self.before().map_err(|_| process::Error::Context)?; if before.process.current.is_some() || !before.process.kernel_bootstrap { return Err(process::Error::Context); } let (_, thread) = self .ready .iter() .flatten() .find(|(id, _)| *id == pid) .copied() .ok_or(process::Error::Stale)?; let record = self .threads .record(thread) .map_err(|_| process::Error::Resource)?; self.after(before).map_err(|_| process::Error::Context)?; if record.state != threads::State::Ready { return Err(process::Error::State); } let low = record.layout.usable_bottom(); let high = record.layout.usable_top(); if low >= high || !low.is_multiple_of(16) || !high.is_multiple_of(16) { return Err(process::Error::Resource); } Ok((low, high)) } unsafe fn register_actual_landing( &mut self, pid: process::ProcessId, ) -> Result<(), process::Error> { unsafe { Owners::register_actual_landing(self, pid) }.map_err(|_| process::Error::Context) } unsafe fn release_actual_current_to_ready(&mut self) -> Result<(), process::Error> { unsafe { Owners::release_actual_current_to_ready(self) } .map_err(|_| process::Error::Context) } } // K1115 guest fault fixture. Every held entry is a real Registry allocation. // With one free metadata slot, process preparation acquires its first image // page and fails on the second. The caller releases every held page explicitly. pub struct K1115Pressure { owner: u64, held: [Option; FRAME_CAPACITY - 1], } impl<'a, H: Hardware> FrameStore<'a, H> { pub fn k1115_fill_to_one_free(&self, owner: u64) -> Result { let before = self.snapshot()?; if owner == 0 || before.live_pages != 0 || before.free_metadata != FRAME_CAPACITY { return Err(Error::Capacity); } let mut pressure = K1115Pressure { owner, held: [None; FRAME_CAPACITY - 1], }; for index in 0..pressure.held.len() { match self.allocate(owner) { Ok(handle) => pressure.held[index] = Some(handle), Err(error) => { // The diagnostic may only continue if every real allocation // is returned. A failed return faults the gate and fails it. if self.k1115_release_pressure(&mut pressure).is_err() { self.gate.faulted.set(true); return Err(Error::Backing); } return Err(error); } } } let after = self.snapshot()?; if after.live_pages != FRAME_CAPACITY - 1 || after.free_metadata != 1 { self.gate.faulted.set(true); return Err(Error::Backing); } Ok(pressure) } pub fn k1115_release_pressure(&self, pressure: &mut K1115Pressure) -> Result<(), Error> { for slot in pressure.held.iter_mut().rev() { if let Some(handle) = *slot { self.release(handle, pressure.owner)?; *slot = None; } } Ok(()) } } impl process::GuestLaunchBackend for Owners<'_, H, I> { fn current_launch(&self, bag: &Self::Bag, pid: process::ProcessId) -> Result { let before = self.before().map_err(|_| process::Error::Context)?; if before.process.current != Some(pid) || bag.pid != pid || !bag.published || self.current.get().is_none_or(|(current, thread, owner)| current != pid || Some(thread) != bag.thread || owner != bag.owner) { return Err(process::Error::Context); } let observed = self.observe_inner(bag)?; if observed.state != threads::State::Running || !observed.image_ready || !observed.stack_ready || observed.spaces.iter().flatten().count() != 2 || observed.handles.iter().flatten().count() != 3 { return Err(process::Error::Resource); } let thread = bag.thread.ok_or(process::Error::Resource)?; let layout = self.threads.record(thread) .map_err(|_| process::Error::Resource)?.layout; self.after(before).map_err(|_| process::Error::Context)?; if before.stack_low != layout.usable_bottom() || before.stack_high != layout.usable_top() || before.process.kernel_bootstrap || before.stack_owner != bag.owner || before.stack_owner == self.gate.bindings.get()[0].unwrap().owner { return Err(process::Error::Context); } let primary = bag.spaces[0].ok_or(process::Error::Resource)?; let other = bag.spaces[1].ok_or(process::Error::Resource)?; let user = self.spaces.observe(primary, bag.owner) .map_err(|_| process::Error::Resource)?; let second = self.spaces.observe(other, bag.owner) .map_err(|_| process::Error::Resource)?; self.after(before).map_err(|_| process::Error::Context)?; if user.root == 0 || user.root == second.root || user.asid == 0 || user.asid == second.asid || user.root == self.gate.original.ttbr { return Err(process::Error::Resource); } let code = bag.handles[0].ok_or(process::Error::Resource)?; let data = bag.handles[1].ok_or(process::Error::Resource)?; let stack = bag.handles[2].ok_or(process::Error::Resource)?; for page in [code, data, stack] { self.store.read(page, bag.owner, 4095) .map_err(|_| process::Error::Resource)?; self.after(before).map_err(|_| process::Error::Context)?; } let user_pc = self.image.entry(); let user_sp = self.image.user_sp(); self.after(before).map_err(|_| process::Error::Context)?; if !(CODE..CODE + PAGE).contains(&user_pc) || !(STACK..=STACK_TOP).contains(&user_sp) || !user_pc.is_multiple_of(4) || !user_sp.is_multiple_of(16) { return Err(process::Error::Resource); } Ok(process::GuestLaunch { process: pid, thread, resource_owner: bag.owner, user_root: user.root, user_asid: user.asid, kernel_root: self.gate.original.ttbr, code_frame: code.address(), data_frame: data.address(), stack_frame: stack.address(), user_pc, user_sp, user_spsr: 0x3c0, kernel_stack_low: layout.usable_bottom(), kernel_stack_high: layout.usable_top(), }) } } impl process::GuestCopyBackend for Owners<'_, H, I> { fn copy_current_user_data( &self, bag: &Self::Bag, pid: process::ProcessId, address: u64, output: &mut [u8], ) -> Result<(), process::GuestCopyError> { use process::GuestCopyError as CopyError; let before = self.before().map_err(|_| CopyError::Context)?; let launch = ::current_launch(self, bag, pid) .map_err(|_| CopyError::Context)?; let end = address .checked_add(output.len() as u64) .ok_or(CopyError::Fault)?; if address < DATA || end > DATA + PAGE { return Err(CopyError::Fault); } let data = bag.handles[1].ok_or(CopyError::Owner)?; if data.address() != launch.data_frame { return Err(CopyError::Owner); } let mut pin = self.store.pin(data, bag.owner).map_err(|_| CopyError::Owner)?; let offset = (address - DATA) as usize; let mut read_failed = false; for (index, byte) in output.iter_mut().enumerate() { match self.store.read(data, bag.owner, offset + index) { Ok(value) => *byte = value, Err(_) => { read_failed = true; break; } } } if self.store.unpin(&mut pin, bag.owner).is_err() { return Err(CopyError::Owner); } if read_failed { return Err(CopyError::Owner); } self.after(before).map_err(|_| CopyError::Context)?; Ok(()) } }