| //! 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 crate::ffi::OsString; | |
| use crate::slice; | |
| #[cfg(test)] | |
| mod tests; | |
| static mut PARAMS: *mut u8 = crate::ptr::null_mut(); | |
| /// Remember the location of the parameter block. | |
| /// | |
| /// # Safety | |
| /// * This function may only be called before `main`. | |
| /// * `data` must either be `null` or point to a valid parameter block. | |
| pub(super) unsafe fn set(data: *mut u8) { | |
| // SAFETY: | |
| // Since this function is called before `main`, there cannot be any threads | |
| // already running. Thus this write cannot race, and all threads will observe | |
| // this write and the parameter block initialization since it happens-before | |
| // their creation. | |
| unsafe { PARAMS = data }; | |
| } | |
| pub(crate) fn get() -> Option<ApplicationParameters> { | |
| // SAFETY: See above. | |
| let data = unsafe { PARAMS }; | |
| unsafe { ApplicationParameters::new_from_ptr(data) } | |
| } | |
| /// 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"; | |
| pub(crate) struct ApplicationParameters { | |
| data: &'static [u8], | |
| offset: usize, | |
| _entries: usize, | |
| } | |
| impl ApplicationParameters { | |
| unsafe fn new_from_ptr(data: *const u8) -> Option<ApplicationParameters> { | |
| 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<Self::Item> { | |
| // 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<Self, Self::Error> { | |
| 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<Self::Item> { | |
| 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<Self, Self::Error> { | |
| 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<OsString> for ArgumentEntry { | |
| fn into(self) -> OsString { | |
| self.value.into() | |
| } | |
| } | |
| impl Iterator for ArgumentList { | |
| type Item = ArgumentEntry; | |
| fn next(&mut self) -> Option<Self::Item> { | |
| 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 }) | |
| } | |
| } |