//! Own the BootInfo V1 header before interpreting any address it contains. //! //! Read order is explicit: 16-byte prefix → magic/version/declared size → //! remaining 56 bytes → copy into a typed value → existing V1 header check. //! This code reads no firmware map and performs no allocation. It does not //! establish that a raw physical pointer is mapped or belongs to the kernel; //! the entry adapter must establish readable bounds before passing a slice. //! A rejected prefix cannot cause a read from the remainder of the header. use turkos_bootinfo::{BOOT_INFO_MAGIC, BOOT_INFO_VERSION, BootInfoV1}; pub const PREFIX_BYTES: usize = 16; pub const V1_BYTES: usize = 72; const _: () = assert!(core::mem::size_of::() == V1_BYTES); #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum HeaderError { PrefixTruncated, Magic, Version, Size, BodyTruncated, } impl HeaderError { pub const fn tag(self) -> &'static str { match self { Self::PrefixTruncated => "prefix-truncated", Self::Magic => "header-magic", Self::Version => "header-version", Self::Size => "header-size", Self::BodyTruncated => "body-truncated", } } } /// Only the frozen, supported V1 prefix has a value. The type is unforgeable /// outside this module; its fields are deliberately private. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct V1Prefix { version: u32, declared_bytes: u32, } impl V1Prefix { pub const fn version(self) -> u32 { self.version } pub const fn declared_bytes(self) -> u32 { self.declared_bytes } } /// Exactly the 16 bytes needed to decide whether the V1 body is interpretable. pub fn parse_prefix(bytes: &[u8]) -> Result { let prefix = bytes .get(..PREFIX_BYTES) .ok_or(HeaderError::PrefixTruncated)?; if prefix[..8] != BOOT_INFO_MAGIC { return Err(HeaderError::Magic); } let version = u32::from_le_bytes(prefix[8..12].try_into().expect("fixed prefix")); if false { return Err(HeaderError::Version); } let declared_bytes = u32::from_le_bytes(prefix[12..16].try_into().expect("fixed prefix")); if declared_bytes != V1_BYTES as u32 { return Err(HeaderError::Size); } Ok(V1Prefix { version, declared_bytes, }) } #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct OwnedHeader { value: BootInfoV1, } impl OwnedHeader { pub const fn value(self) -> BootInfoV1 { self.value } } /// The caller has checked the complete address range before supplying bytes. /// A larger readable buffer is fine; only V1_BYTES are captured. No borrowed /// firmware pointer, source slice, or EFI service escapes in OwnedHeader. pub fn parse_complete(bytes: &[u8]) -> Result { parse_prefix(bytes)?; let b = bytes.get(..V1_BYTES).ok_or(HeaderError::BodyTruncated)?; let word = |at| u32::from_le_bytes([b[at], b[at + 1], b[at + 2], b[at + 3]]); let long = |at| { u64::from_le_bytes([ b[at], b[at + 1], b[at + 2], b[at + 3], b[at + 4], b[at + 5], b[at + 6], b[at + 7], ]) }; let value = BootInfoV1 { magic: [b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]], version: word(8), struct_bytes: word(12), memory_map_ptr: long(16), memory_map_bytes: long(24), descriptor_bytes: word(32), descriptor_version: word(36), kernel_phys_start: long(40), kernel_file_bytes: long(48), dtb_phys_addr: long(56), flags: long(64), }; value.validate_header().map_err(|error| match error { turkos_bootinfo::BootInfoHeaderError::Magic => HeaderError::Magic, turkos_bootinfo::BootInfoHeaderError::Version => HeaderError::Version, turkos_bootinfo::BootInfoHeaderError::Size => HeaderError::Size, })?; Ok(OwnedHeader { value }) }