blob: 0d02cf35477f969e14f6ed1bdac347e79ea1ee30 [file] [log] [blame]
/// 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<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 })
}
}