#![no_std]
extern crate alloc;
use alloc::string::String;
use alloc::vec::Vec;
use onerom_config::fw::FirmwareVersion;
use onerom_config::mcu::{RP235X_BASE_FLASH, RP235X_BASE_SRAM, RP235X_END_FLASH, RP235X_END_SRAM};
include!(concat!(env!("OUT_DIR"), "/metadata_generated.rs"));
include!(concat!(env!("OUT_DIR"), "/serialize_generated.rs"));
include!(concat!(env!("OUT_DIR"), "/host_generated.rs"));
mod firmware_overrides_impl;
pub const MIN_SCHEMA_VERSION: FirmwareVersion = FirmwareVersion::new(0, 7, 0, 0);
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ParseError {
OutOfBounds { addr: u32, size: usize },
NullPointer { field: &'static str },
UnknownDiscriminant { type_name: &'static str, value: u32 },
InvalidUtf8,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Pointer {
Null,
Addr32(u32),
}
impl Pointer {
pub fn new(raw: u32) -> Self {
match raw {
0 | 0xFFFF_FFFF => Self::Null,
a => Self::Addr32(a),
}
}
pub fn is_null(&self) -> bool {
matches!(self, Self::Null)
}
pub fn addr(&self) -> Option<u32> {
if let Self::Addr32(a) = self {
Some(*a)
} else {
None
}
}
pub fn raw(&self) -> u32 {
match self {
Self::Null => 0,
Self::Addr32(a) => *a,
}
}
pub fn is_flash(&self) -> bool {
matches!(self, Self::Addr32(a) if matches!(a, RP235X_BASE_FLASH..=RP235X_END_FLASH))
}
pub fn is_sram(&self) -> bool {
matches!(self, Self::Addr32(a) if matches!(a, RP235X_BASE_SRAM..=RP235X_END_SRAM))
}
}
pub struct DeviceMemoryView<'a> {
regions: Vec<(&'a [u8], u32)>,
}
impl<'a> DeviceMemoryView<'a> {
pub fn new(data: &'a [u8], base: u32) -> Self {
Self {
regions: alloc::vec![(data, base)],
}
}
pub fn add_region(&mut self, data: &'a [u8], base: u32) {
self.regions.push((data, base));
}
pub fn read_u8(&self, addr: u32) -> Result<u8, ParseError> {
let (data, off) = self.region_for(addr, 1)?;
Ok(data[off])
}
pub fn read_u16_le(&self, addr: u32) -> Result<u16, ParseError> {
let (data, off) = self.region_for(addr, 2)?;
Ok(u16::from_le_bytes([data[off], data[off + 1]]))
}
pub fn read_u32_le(&self, addr: u32) -> Result<u32, ParseError> {
let (data, off) = self.region_for(addr, 4)?;
Ok(u32::from_le_bytes([
data[off],
data[off + 1],
data[off + 2],
data[off + 3],
]))
}
pub fn read_bytes<const N: usize>(&self, addr: u32) -> Result<[u8; N], ParseError> {
let (data, off) = self.region_for(addr, N)?;
let mut buf = [0u8; N];
buf.copy_from_slice(&data[off..off + N]);
Ok(buf)
}
pub fn read_ptr(&self, addr: u32) -> Result<u32, ParseError> {
self.read_u32_le(addr)
}
pub fn read_cstr(&self, addr: u32) -> Result<String, ParseError> {
let ptr = self.read_u32_le(addr)?;
self.follow_cstr(ptr)
}
pub fn read_cstr_opt(&self, addr: u32) -> Result<Option<String>, ParseError> {
let ptr = self.read_u32_le(addr)?;
if ptr == 0 || ptr == 0xFFFF_FFFF {
Ok(None)
} else {
self.follow_cstr(ptr).map(Some)
}
}
pub fn slice_at(&self, addr: u32, len: usize) -> Result<&'a [u8], ParseError> {
for &(data, base) in &self.regions {
#[allow(clippy::collapsible_if)]
if let Some(off) = addr.checked_sub(base).map(|o| o as usize) {
if off.saturating_add(len) <= data.len() {
return Ok(&data[off..off + len]);
}
}
}
Err(ParseError::OutOfBounds { addr, size: len })
}
fn region_for(&self, addr: u32, size: usize) -> Result<(&[u8], usize), ParseError> {
for &(data, base) in &self.regions {
#[allow(clippy::collapsible_if)]
if let Some(off) = addr.checked_sub(base).map(|o| o as usize) {
if off.saturating_add(size) <= data.len() {
return Ok((data, off));
}
}
}
Err(ParseError::OutOfBounds { addr, size })
}
fn follow_cstr(&self, ptr: u32) -> Result<String, ParseError> {
for &(data, base) in &self.regions {
#[allow(clippy::collapsible_if)]
if let Some(start) = ptr.checked_sub(base).map(|o| o as usize) {
if start < data.len() {
let remaining = &data[start..];
let len =
remaining
.iter()
.position(|&b| b == 0)
.ok_or(ParseError::OutOfBounds {
addr: ptr,
size: remaining.len() + 1,
})?;
let s = core::str::from_utf8(&remaining[..len])
.map_err(|_| ParseError::InvalidUtf8)?;
return Ok(String::from(s));
}
}
}
Err(ParseError::OutOfBounds { addr: ptr, size: 1 })
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum SerializeError {
Overflow,
CountOverflow {
field: &'static str,
},
}
pub fn serialize(
root: &OneromMetadataHeader,
base_addr: u32,
buf: &mut [u8],
) -> Result<(), SerializeError> {
let mut ctx = SerializeContext::new(base_addr, buf);
root.layout(&mut ctx)?;
root.write(&mut ctx, base_addr);
Ok(())
}
pub fn escape_c_string(s: &str) -> alloc::string::String {
let mut out = alloc::string::String::with_capacity(s.len());
for c in s.chars() {
match c {
'"' => out.push_str("\\\""),
'\\' => out.push_str("\\\\"),
'\0' => panic!("NUL byte in C string literal (field value: {:?})", s),
c => out.push(c),
}
}
out
}