#![cfg_attr(not(feature = "std"), no_std)]
#[cfg(feature = "esp32")]
use esp_println as _;
use airfrog_rpc::io::Reader;
use onerom_config::fw::FirmwareVersion;
use onerom_config::hw::Board;
use onerom_config::mcu::Variant as McuVariant;
pub const MAX_VERSION_MAJOR: u16 = 0;
pub const MAX_VERSION_MINOR: u16 = 6;
pub const MAX_VERSION_PATCH: u16 = 999;
pub mod info;
pub mod lab;
mod parsing;
pub mod readers;
pub mod types;
#[cfg(not(feature = "std"))]
extern crate alloc;
#[cfg(not(feature = "std"))]
use alloc::vec;
use core::fmt;
#[allow(unused_imports)]
use log::{debug, error, info, trace, warn};
pub use info::{Sdrr, SdrrExtraInfo, SdrrInfo, SdrrPins, SdrrRomInfo, SdrrRomSet, SdrrRuntimeInfo};
pub use lab::{LabFlash, LabParser, LabRam, OneRomLab};
pub use types::{
McuLine, McuStorage, SdrrAddress, SdrrCsSet, SdrrCsState, SdrrLogicalAddress, SdrrMcuPort,
SdrrRomType, SdrrServe, Source,
};
use crate::parsing::{
SdrrInfoHeader, SdrrRuntimeInfoHeader, parse_and_validate_header,
parse_and_validate_runtime_info,
};
pub const SDRR_INFO_FW_OFFSET: u32 = 0x200;
pub const SDRR_RUNTIME_INFO_FW_OFFSET: u32 = 0x0;
#[cfg(not(feature = "std"))]
use alloc::{format, string::String, vec::Vec};
pub(crate) const STM32F4_FLASH_BASE: u32 = 0x08000000;
pub(crate) const STM32F4_RAM_BASE: u32 = 0x20000000;
pub struct Parser<'a, R: Reader> {
reader: &'a mut R,
base_flash_address: u32,
base_ram_address: u32,
}
impl<'a, R: Reader> Parser<'a, R> {
pub fn new(reader: &'a mut R) -> Self {
Self {
reader,
base_flash_address: STM32F4_FLASH_BASE,
base_ram_address: STM32F4_RAM_BASE,
}
}
pub fn with_base_flash_address(
reader: &'a mut R,
base_flash_address: u32,
base_ram_address: u32,
) -> Self {
Self {
reader,
base_flash_address,
base_ram_address,
}
}
async fn retrieve_header(&mut self) -> Result<SdrrInfoHeader, String> {
let sdrr_info_addr = self.base_flash_address + SDRR_INFO_FW_OFFSET;
let mut header_buf = [0u8; SdrrInfoHeader::size()];
self.reader
.read(sdrr_info_addr, &mut header_buf)
.await
.map_err(|_| "Failed to read SDRR header")?;
parse_and_validate_header(&header_buf)
}
async fn retrieve_runtime_header(&mut self) -> Result<SdrrRuntimeInfoHeader, String> {
let sdrr_runtime_info_addr = self.base_ram_address + SDRR_RUNTIME_INFO_FW_OFFSET;
let mut runtime_buf = [0u8; SdrrRuntimeInfoHeader::size()];
self.reader
.read(sdrr_runtime_info_addr, &mut runtime_buf)
.await
.map_err(|_| "Failed to read SDRR runtime info")?;
parse_and_validate_runtime_info(&runtime_buf)
}
async fn retrieve_runtime_header_from_info(
&mut self,
info: &SdrrInfo,
) -> Result<SdrrRuntimeInfoHeader, String> {
let runtime_info_ptr = info.runtime_info_ptr;
if runtime_info_ptr < self.base_ram_address && runtime_info_ptr != 0xFFFF_FFFF_u32 {
return Err(format!(
"Invalid runtime info pointer: 0x{:08X}",
runtime_info_ptr
));
}
let mut runtime_buf = [0u8; SdrrRuntimeInfoHeader::size()];
self.reader
.read(runtime_info_ptr, &mut runtime_buf)
.await
.map_err(|_| "Failed to read SDRR runtime info")?;
parse_and_validate_runtime_info(&runtime_buf)
}
pub async fn detect(&mut self) -> bool {
match self.retrieve_header().await {
Ok(_header) => true,
Err(_) => false,
}
}
pub async fn parse(&mut self) -> Sdrr {
let flash = match self.parse_flash().await {
Ok(f) => Some(f),
Err(e) => {
debug!("Failed to parse flash: {}", e);
None
}
};
let ram = if let Some(flash) = &flash {
self.parse_ram_from_info(flash).await
} else {
self.parse_ram().await
};
let ram = match ram {
Ok(r) => Some(r),
Err(e) => {
debug!("Failed to parse RAM: {}", e);
None
}
};
Sdrr { flash, ram }
}
pub async fn parse_flash(&mut self) -> Result<SdrrInfo, String> {
let mut header = self.retrieve_header().await?;
let version = FirmwareVersion::new(
header.major_version,
header.minor_version,
header.patch_version,
header.build_number,
);
if header.stm_line == McuLine::Rp2350 {
self.base_flash_address = 0x10000000; self.reader.update_base_address(self.base_flash_address);
}
let mut parse_errors = Vec::new();
let build_date = match self.read_string_at_ptr(header.build_date_ptr).await {
Ok(s) => Some(s),
Err(e) => {
parse_errors.push(ParseError::new("Build Date", e));
None
}
};
let hw_rev = match self.read_string_at_ptr(header.hw_rev_ptr).await {
Ok(s) => Some(s),
Err(e) => {
parse_errors.push(ParseError::new("Hardware Revision", e));
None
}
};
let (extra_info, runtime_info_ptr) = match parsing::read_extra_info(
self.reader,
header.extra_ptr,
self.base_flash_address,
&version,
)
.await
{
Ok(info) => {
let runtime_info_ptr = info.runtime_info_ptr;
(Some(info), runtime_info_ptr)
}
Err(e) => {
parse_errors.push(ParseError::new("Extra Info", e));
(None, 0xFFFF_FFFF_u32)
}
};
let metadata_present = if header.major_version > 0 || header.minor_version > 4 {
let metadata_ptr = header.rom_sets_ptr;
header.rom_set_count = 0;
header.rom_sets_ptr = 0;
match parsing::read_one_rom_metadata_header_info(
self.reader,
metadata_ptr,
self.base_flash_address,
)
.await
{
Ok(metadata) => {
if metadata.version == 1 {
if metadata.rom_set_count == 0 {
true
} else if metadata.rom_sets_ptr > 0 {
header.rom_set_count = metadata.rom_set_count;
header.rom_sets_ptr = metadata.rom_sets_ptr;
true
} else {
parse_errors.push(ParseError::new(
"Metadata",
format!(
"Metadata: Invalid ROM sets pointer {}",
metadata.rom_sets_ptr
),
));
false
}
} else {
parse_errors.push(ParseError::new(
"Metadata",
format!("Metadata: Invalid version {}", metadata.version),
));
false
}
}
Err(_) => {
false
}
}
} else {
false
};
let rom_sets =
match parsing::read_rom_sets(self.reader, &header, self.base_flash_address, &version)
.await
{
Ok(sets) => {
if sets.len() != header.rom_set_count as usize {
parse_errors.push(ParseError::new(
"Rom Sets",
format!(
"Incorrect number of ROM sets found: Found {}, expected {}",
sets.len(),
header.rom_set_count
),
));
}
sets
}
Err(e) => {
parse_errors.push(ParseError::new("ROM Sets", e));
Vec::new()
}
};
let pins =
match parsing::read_pins(self.reader, header.pins_ptr, self.base_flash_address).await {
Ok(p) => Some(p),
Err(e) => {
parse_errors.push(ParseError::new("Pins", e));
None
}
};
let board = hw_rev.as_ref().and_then(|s| Board::try_from_str(s));
let model = board.as_ref().map(|b| b.model());
let mcu_lookup_str = format!(
"{}{}",
header.stm_line.chip_suffix(),
header.stm_storage.stm32_suffix()
);
let mcu_variant = McuVariant::try_from_str(&mcu_lookup_str);
if board.is_none() {
parse_errors.push(ParseError::new(
"Board",
format!(
"Could not decode board from hardware revision string: {:?}",
hw_rev
),
));
}
if mcu_variant.is_none() {
parse_errors.push(ParseError::new(
"MCU Variant",
format!(
"Could not decode MCU variant from string: {}",
mcu_lookup_str
),
));
}
Ok(SdrrInfo {
major_version: header.major_version,
minor_version: header.minor_version,
patch_version: header.patch_version,
build_number: header.build_number,
build_date,
commit: header.commit,
hw_rev,
stm_line: header.stm_line,
stm_storage: header.stm_storage,
freq: header.freq,
overclock: header.overclock != 0,
swd_enabled: header.swd_enabled != 0,
preload_image_to_ram: header.preload_image_to_ram != 0,
bootloader_capable: header.bootloader_capable != 0,
status_led_enabled: header.status_led_enabled != 0,
boot_logging_enabled: header.boot_logging_enabled != 0,
mco_enabled: header.mco_enabled != 0,
rom_set_count: header.rom_set_count,
count_rom_access: header.count_rom_access != 0,
rom_sets,
pins,
boot_config: header.boot_config,
parse_errors,
extra_info,
metadata_present,
version,
board,
model,
mcu_variant,
runtime_info_ptr,
})
}
async fn parse_ram_from_runtime_info(
&mut self,
runtime_info: SdrrRuntimeInfoHeader,
) -> Result<SdrrRuntimeInfo, String> {
Ok(SdrrRuntimeInfo {
image_sel: runtime_info.image_sel,
rom_set_index: runtime_info.rom_set_index,
count_rom_access: runtime_info.count_rom_access,
last_parsed_access_count: runtime_info.access_count,
account_count_address: self.base_ram_address
+ SdrrRuntimeInfoHeader::access_count_offset() as u32,
rom_table_address: runtime_info.rom_table_ptr,
rom_table_size: runtime_info.rom_table_size,
overclock_enabled: None,
status_led_enabled: None,
swd_enabled: None,
fire_vreg: None,
ice_freq_mhz: None,
fire_freq_mhz: None,
sysclk_mhz: None,
fire_serve_mode: None,
bit_mode: None,
rom_dma_copy: None,
num_data_pins: None,
force_16_bit: None,
peri_en: None,
limp_mode: None,
})
}
async fn parse_ram(&mut self) -> Result<SdrrRuntimeInfo, String> {
let runtime_info = self.retrieve_runtime_header().await?;
self.parse_ram_from_runtime_info(runtime_info).await
}
async fn parse_ram_from_info(&mut self, info: &SdrrInfo) -> Result<SdrrRuntimeInfo, String> {
let runtime_info = self.retrieve_runtime_header_from_info(info).await?;
self.parse_ram_from_runtime_info(runtime_info).await
}
async fn read_string_at_ptr(&mut self, ptr: u32) -> Result<String, String> {
if ptr < self.base_flash_address {
return Err(format!("Invalid pointer: 0x{:08X}", ptr));
}
read_string_at_ptr(self.reader, ptr).await
}
}
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct ParseError {
pub field: String,
pub reason: String,
}
impl ParseError {
pub fn new(field: impl Into<String>, reason: impl Into<String>) -> Self {
Self {
field: field.into(),
reason: reason.into(),
}
}
}
impl fmt::Display for ParseError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}: {}", self.field, self.reason)
}
}
async fn read_string_at_ptr<R: Reader>(reader: &mut R, ptr: u32) -> Result<String, String> {
let mut result = Vec::new();
let mut addr = ptr;
let mut buf = [0u8; 64];
loop {
let chunk_size = buf.len().min(1024 - result.len()); reader
.read(addr, &mut buf[..chunk_size])
.await
.map_err(|_| format!("Failed to read string at 0x{ptr:08X}"))?;
if let Some(null_pos) = buf[..chunk_size].iter().position(|&b| b == 0) {
result.extend_from_slice(&buf[..null_pos]);
break;
}
result.extend_from_slice(&buf[..chunk_size]);
addr += chunk_size as u32;
if result.len() >= 1024 {
return Err("String too long (>1KB)".into());
}
}
String::from_utf8(result).map_err(|_| "Invalid UTF-8 string".into())
}
async fn read_str_at_ptr<R: Reader>(reader: &mut R, len: u32, ptr: u32) -> Result<String, String> {
if len > 1024 {
return Err("String too long (>1KB)".into());
} else if len == 0 {
return Ok(String::new());
}
let mut buf = vec![0u8; len as usize];
reader
.read(ptr, &mut buf)
.await
.map_err(|_| format!("Failed to read string at 0x{ptr:08X}"))?;
String::from_utf8(buf).map_err(|_| "Invalid UTF-8 string".into())
}
pub fn crate_version() -> &'static str {
env!("CARGO_PKG_VERSION")
}