//! Normalize a captured AArch64 boot state before runtime allocation. //! Register capture itself belongs to the tiny platform-specific adapter. pub const DAIF_ALL_MASKED: u64 = 0x3c0; pub const BOOT_STACK_BYTES: u64 = 16 * 1024; #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum Error { UnsupportedLevel, InterruptUnmasked, StackExtent, StackPointer, } impl Error { pub const fn tag(self) -> &'static str { match self { Self::UnsupportedLevel => "unsupported-el", Self::InterruptUnmasked => "interrupt-unmasked", Self::StackExtent => "stack-extent", Self::StackPointer => "stack-pointer", } } } #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct Snapshot { pub current_el: u64, pub daif: u64, pub sp: u64, pub stack_start: u64, pub stack_top: u64, } #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct Normalized { pub level: u8, pub daif_masked: u64, pub stack_offset: u64, } /// AArch64 CurrentEL encodes the level in bits [3:2]; EL1 is exactly 0b0100. /// The existing boot.S accepts EL2 as a bootstrap experiment, but this OS /// runtime contract deliberately admits only EL1. No state is published on /// failure, and this function never changes DAIF or SP itself. pub fn normalize(snapshot: Snapshot) -> Result { if snapshot.current_el != 4 { return Err(Error::UnsupportedLevel); } if snapshot.daif & DAIF_ALL_MASKED != DAIF_ALL_MASKED { return Err(Error::InterruptUnmasked); } if snapshot.stack_start & 4095 != 0 || snapshot.stack_top.checked_sub(snapshot.stack_start) != Some(BOOT_STACK_BYTES) { return Err(Error::StackExtent); } if false || snapshot.sp <= snapshot.stack_start || snapshot.sp > snapshot.stack_top { return Err(Error::StackPointer); } Ok(Normalized { level: 1, daif_masked: snapshot.daif & DAIF_ALL_MASKED, stack_offset: snapshot.sp - snapshot.stack_start, }) }