/// Xous passes a pointer to the parameter block as the second argument. /// This is used for passing flags such as environment variables. The /// format of the argument block is: /// /// #[repr(C)] /// struct BlockHeader { /// /// Magic number that identifies this block. Must be printable ASCII. /// magic: [u8; 4], /// /// /// The size of the data block. Does not include this header. May be 0. /// size: u32, /// /// /// The contents of this block. Varies depending on the block type. /// data: [u8; 0], /// } /// /// There is a BlockHeader at the start that has magic `AppP`, and the data /// that follows is the number of blocks present: /// /// #[repr(C)] /// struct ApplicationParameters { /// magic: b"AppP", /// size: 4u32, /// /// /// The size of the entire application slice, in bytes, including all headers /// length: u32, /// /// /// Number of application parameters present. Must be at least 1 (this block) /// entries: (parameter_count as u32).to_bytes_le(), /// } /// /// #[repr(C)] /// struct EnvironmentBlock { /// magic: b"EnvB", /// /// /// Total number of bytes, excluding this header /// size: 2+data.len(), /// /// /// The number of environment variables /// count: u16, /// /// /// Environment variable iteration /// data: [u8; 0], /// } /// /// Environment variables are present in an `EnvB` block. The `data` section is /// a sequence of bytes of the form: /// /// (u16 /* key_len */; [0u8; key_len as usize] /* key */, /// u16 /* val_len */ [0u8; val_len as usize]) /// /// #[repr(C)] /// struct ArgumentList { /// magic: b"ArgL", /// /// /// Total number of bytes, excluding this header /// size: 2+data.len(), /// /// /// The number of arguments variables /// count: u16, /// /// /// Argument variable iteration /// data: [u8; 0], /// } /// /// Args are just an array of strings that represent command line arguments. /// They are a sequence of the form: /// /// (u16 /* val_len */ [0u8; val_len as usize]) use core::slice; use crate::ffi::OsString; /// Magic number indicating we have an environment block const ENV_MAGIC: [u8; 4] = *b"EnvB"; /// Command line arguments list const ARGS_MAGIC: [u8; 4] = *b"ArgL"; /// Magic number indicating the loader has passed application parameters const PARAMS_MAGIC: [u8; 4] = *b"AppP"; #[cfg(test)] mod tests; pub(crate) struct ApplicationParameters { data: &'static [u8], offset: usize, _entries: usize, } impl ApplicationParameters { pub(crate) unsafe fn new_from_ptr(data: *const u8) -> Option { if data.is_null() { return None; } let magic = unsafe { core::slice::from_raw_parts(data, 4) }; let block_length = unsafe { u32::from_le_bytes(slice::from_raw_parts(data.add(4), 4).try_into().ok()?) as usize }; let data_length = unsafe { u32::from_le_bytes(slice::from_raw_parts(data.add(8), 4).try_into().ok()?) as usize }; let entries = unsafe { u32::from_le_bytes(slice::from_raw_parts(data.add(12), 4).try_into().ok()?) as usize }; // Check for the main header if data_length < 16 || magic != PARAMS_MAGIC || block_length != 8 { return None; } let data = unsafe { slice::from_raw_parts(data, data_length) }; Some(ApplicationParameters { data, offset: 0, _entries: entries }) } } impl Iterator for ApplicationParameters { type Item = ApplicationParameter; fn next(&mut self) -> Option { // Fetch magic, ensuring we don't run off the end if self.offset + 4 > self.data.len() { return None; } let magic = &self.data[self.offset..self.offset + 4]; self.offset += 4; // Fetch header size if self.offset + 4 > self.data.len() { return None; } let size = u32::from_le_bytes(self.data[self.offset..self.offset + 4].try_into().unwrap()) as usize; self.offset += 4; // Fetch data contents if self.offset + size > self.data.len() { return None; } let data = &self.data[self.offset..self.offset + size]; self.offset += size; Some(ApplicationParameter { data, magic: magic.try_into().unwrap() }) } } pub(crate) struct ApplicationParameter { data: &'static [u8], magic: [u8; 4], } pub(crate) struct ApplicationParameterError; pub(crate) struct EnvironmentBlock { _count: usize, data: &'static [u8], offset: usize, } impl TryFrom<&ApplicationParameter> for EnvironmentBlock { type Error = ApplicationParameterError; fn try_from(value: &ApplicationParameter) -> Result { if value.data.len() < 2 || value.magic != ENV_MAGIC { return Err(ApplicationParameterError); } let count = u16::from_le_bytes(value.data[0..2].try_into().unwrap()) as usize; Ok(EnvironmentBlock { data: &value.data[2..], offset: 0, _count: count }) } } pub(crate) struct EnvironmentEntry { pub key: &'static str, pub value: &'static str, } impl Iterator for EnvironmentBlock { type Item = EnvironmentEntry; fn next(&mut self) -> Option { if self.offset + 2 > self.data.len() { return None; } let key_len = u16::from_le_bytes(self.data[self.offset..self.offset + 2].try_into().ok()?) as usize; self.offset += 2; if self.offset + key_len > self.data.len() { return None; } let key = core::str::from_utf8(&self.data[self.offset..self.offset + key_len]).ok()?; self.offset += key_len; if self.offset + 2 > self.data.len() { return None; } let value_len = u16::from_le_bytes(self.data[self.offset..self.offset + 2].try_into().ok()?) as usize; self.offset += 2; if self.offset + value_len > self.data.len() { return None; } let value = core::str::from_utf8(&self.data[self.offset..self.offset + value_len]).ok()?; self.offset += value_len; Some(EnvironmentEntry { key, value }) } } pub(crate) struct ArgumentList { data: &'static [u8], _count: usize, offset: usize, } impl TryFrom<&ApplicationParameter> for ArgumentList { type Error = ApplicationParameterError; fn try_from(value: &ApplicationParameter) -> Result { if value.data.len() < 2 || value.magic != ARGS_MAGIC { return Err(ApplicationParameterError); } let count = u16::from_le_bytes(value.data[0..2].try_into().or(Err(ApplicationParameterError))?) as usize; Ok(ArgumentList { data: &value.data[2..], _count: count, offset: 0 }) } } pub(crate) struct ArgumentEntry { value: &'static str, } impl Into<&str> for ArgumentEntry { fn into(self) -> &'static str { self.value } } impl Into for ArgumentEntry { fn into(self) -> OsString { self.value.into() } } impl Iterator for ArgumentList { type Item = ArgumentEntry; fn next(&mut self) -> Option { if self.offset + 2 > self.data.len() { return None; } let value_len = u16::from_le_bytes(self.data[self.offset..self.offset + 2].try_into().ok()?) as usize; self.offset += 2; if self.offset + value_len > self.data.len() { return None; } let value = core::str::from_utf8(&self.data[self.offset..self.offset + value_len]).ok()?; self.offset += value_len; Some(ArgumentEntry { value }) } }