use alloc::vec;
use alloc::vec::Vec;
use onerom_config::chip::ChipFunction;
use onerom_config::fw::FirmwareVersion;
use onerom_config::hw::Board;
use crate::builder::{FireServeMode, FirmwareConfig, ServeAlgParams};
use crate::image::{ChipSet, ChipSetType};
use crate::{Error, FIRMWARE_SIZE, METADATA_VERSION, MIN_FIRMWARE_OVERRIDES_VERSION, Result};
pub const PAD_METADATA_BYTE: u8 = 0xFF;
const HEADER_MAGIC: &[u8; 16] = b"ONEROM_METADATA\0";
const METADATA_START: u32 = FIRMWARE_SIZE as u32;
const ROM_IMAGE_DATA_START: u32 = 65536;
pub const MAX_METADATA_LEN: usize = 16384;
const METADATA_HEADER_LEN: usize = 256;
const METADATA_CHIP_SET_OFFSET: usize = 24;
pub(crate) const CHIP_SET_METADATA_LEN: usize = 16; pub(crate) const CHIP_SET_METADATA_LEN_EXTRA_INFO: usize = 64; pub(crate) const CHIP_SET_FIRMWARE_OVERRIDES_METADATA_LEN: usize = 64; pub(crate) const CHIP_SET_SERVE_CONFIG_METADATA_LEN: usize = 64;
#[derive(Debug, serde::Serialize, serde::Deserialize)]
pub struct Metadata {
board: Board,
chip_sets: Vec<ChipSet>,
filenames: bool,
pio: bool,
firmware_version: FirmwareVersion,
}
impl Metadata {
pub fn new(
board: Board,
chip_sets: Vec<ChipSet>,
filenames: bool,
pio: bool,
firmware_version: FirmwareVersion,
) -> Self {
Self {
board,
chip_sets,
filenames,
pio,
firmware_version,
}
}
pub fn set_pio(&mut self) {
self.pio = true;
}
pub fn pio(&self) -> bool {
self.pio
}
const fn header_len(&self) -> usize {
METADATA_HEADER_LEN
}
const fn abs_metadata_start(&self) -> u32 {
self.board.mcu_family().get_flash_base() + METADATA_START
}
const fn abs_chip_image_start(&self) -> u32 {
self.board.mcu_family().get_flash_base() + ROM_IMAGE_DATA_START
}
pub fn metadata_len(&self) -> usize {
let len = self.header_len()
+ self.filenames_metadata_len()
+ self.firmware_overrides_len()
+ self.sets_len();
if len > MAX_METADATA_LEN {
panic!(
"Metadata too large: {} bytes (max {})",
len, MAX_METADATA_LEN
);
}
len
}
pub fn total_set_count(&self) -> usize {
self.chip_sets.len()
}
fn total_chip_count(&self) -> usize {
self.chip_sets.iter().map(|rs| rs.chips().len()).sum()
}
fn filenames_metadata_len(&self) -> usize {
let len = if !self.filenames {
0
} else {
self.chip_sets
.iter()
.flat_map(|rs| rs.chips())
.map(|rom| rom.filename().len() + 1)
.sum()
};
if len % 4 != 0 {
len + (4 - (len % 4))
} else {
len
}
}
fn sets_len(&self) -> usize {
let mut total = 0;
for set in &self.chip_sets {
total += set.chips_metadata_len(self.filenames);
total += set.chips().len() * 4;
}
total += self.chip_sets.len() * ChipSet::chip_set_metadata_len(&self.firmware_version);
total
}
pub fn write_all(&self, buf: &mut [u8], rtn_chip_data_ptrs: &mut [u32]) -> Result<usize> {
if self.metadata_len() > buf.len() {
return Err(Error::BufferTooSmall {
location: "write_all",
expected: self.metadata_len(),
actual: buf.len(),
});
}
let mut offset = 0;
offset += self.write_header(&mut buf[offset..])?;
let mut filename_ptrs = vec![0xFF_u32; self.total_chip_count()];
if self.filenames {
let filename_offset = offset;
let filename_len = self.write_filenames(&mut buf[offset..], &mut filename_ptrs)?;
offset += filename_len;
for ptr in filename_ptrs.iter_mut() {
*ptr += (filename_offset as u32) + self.abs_metadata_start();
}
if filename_len % 4 != 0 {
let padding = 4 - (filename_len % 4);
for _ in 0..padding {
buf[offset] = PAD_METADATA_BYTE;
offset += 1;
}
}
assert_eq!(
offset % 4,
0,
"Metadata offset not 4 byte aligned after writing filenames"
);
}
let mut firmware_overrides_ptrs = vec![None; self.chip_sets.len()];
let mut serve_config_ptrs = vec![None; self.chip_sets.len()];
if self.firmware_version >= MIN_FIRMWARE_OVERRIDES_VERSION {
for (ii, chip_set) in self.chip_sets.iter().enumerate() {
if let Some(ref fw_config) = chip_set.firmware_overrides {
firmware_overrides_ptrs[ii] = Some(offset as u32 + self.abs_metadata_start());
let len = Self::serialize_firmware_overrides(fw_config, &mut buf[offset..])?;
offset += len;
if let Some(ref params) = fw_config.serve_alg_params {
serve_config_ptrs[ii] = Some(offset as u32 + self.abs_metadata_start());
let len = Self::serialize_serve_config(params, &mut buf[offset..])?;
offset += len;
}
}
}
}
let mut rom_data_ptrs = vec![0u32; self.chip_sets.len()];
let mut rom_data_ptr = self.abs_chip_image_start();
let mut rtn_chip_data_ptr = 0;
for (ii, set) in self.chip_sets.iter().enumerate() {
if !set.has_data() && (set.chip_function() == ChipFunction::Ram) {
rom_data_ptrs[ii] = 0xFFFF_FFFF;
rtn_chip_data_ptrs[ii] = 0xFFFF_FFFF;
continue;
}
rom_data_ptrs[ii] = rom_data_ptr;
rtn_chip_data_ptrs[ii] = rtn_chip_data_ptr;
let rom_data_size = set.image_size(&self.board, &self.firmware_version);
rom_data_ptr += rom_data_size as u32;
rtn_chip_data_ptr += rom_data_size as u32;
}
let mut rom_array_ptrs = vec![Vec::new(); self.chip_sets.len()];
for (ii, chip_set) in self.chip_sets.iter().enumerate() {
let mut rom_metadata_ptrs = vec![0u32; chip_set.chips().len()];
let len = chip_set.write_chip_metadata(
&mut buf[offset..],
&filename_ptrs,
&mut rom_metadata_ptrs,
self.filenames,
)?;
for ptr in rom_metadata_ptrs.iter_mut() {
*ptr += offset as u32 + self.abs_metadata_start();
}
rom_array_ptrs[ii] = rom_metadata_ptrs;
offset += len;
}
let mut actual_chip_array_ptrs = vec![0u32; self.chip_sets.len()];
for (ii, chip_set) in self.chip_sets.iter().enumerate() {
let len = chip_set.write_chip_pointer_array(&mut buf[offset..], &rom_array_ptrs[ii])?;
actual_chip_array_ptrs[ii] = offset as u32 + self.abs_metadata_start();
offset += len;
}
let first_chip_set_ptr = offset as u32 + self.abs_metadata_start();
for (ii, chip_set) in self.chip_sets.iter().enumerate() {
offset += chip_set.write_set_metadata(
&mut buf[offset..],
rom_data_ptrs[ii],
actual_chip_array_ptrs[ii],
&self.board,
&self.firmware_version,
serve_config_ptrs[ii],
firmware_overrides_ptrs[ii],
)?;
}
self.update_chip_set_ptr(&mut buf[..], first_chip_set_ptr)?;
Ok(offset)
}
fn write_filenames(&self, buf: &mut [u8], ptrs: &mut [u32]) -> Result<usize> {
if !self.filenames {
return Ok(0);
}
if buf.len() < self.filenames_metadata_len() {
return Err(crate::Error::BufferTooSmall {
location: "write_filenames1",
expected: self.filenames_metadata_len(),
actual: buf.len(),
});
}
let mut offset = 0;
let num_roms = self.total_chip_count();
if ptrs.len() < num_roms {
return Err(crate::Error::BufferTooSmall {
location: "write_filenames2",
expected: num_roms,
actual: ptrs.len(),
});
}
for (ii, rom) in self.chip_sets.iter().flat_map(|rs| rs.chips()).enumerate() {
assert_eq!(ii, rom.index());
let name_bytes = rom.filename().as_bytes();
let len = name_bytes.len();
ptrs[ii] = offset as u32;
buf[offset..offset + len].copy_from_slice(name_bytes);
offset += len;
buf[offset] = 0;
offset += 1;
}
Ok(offset)
}
fn write_header(&self, buf: &mut [u8]) -> Result<usize> {
if buf.len() < METADATA_HEADER_LEN {
return Err(crate::Error::BufferTooSmall {
location: "write_header",
expected: METADATA_HEADER_LEN,
actual: buf.len(),
});
}
let mut offset = 0;
let len = 16;
buf[0..offset + len].copy_from_slice(HEADER_MAGIC);
offset += len;
let len = 4;
buf[offset..offset + len].copy_from_slice(&METADATA_VERSION.to_le_bytes());
offset += len;
let len = 1;
buf[offset..offset + len].copy_from_slice(&[self.chip_sets.len() as u8]);
offset += len;
let len = 3;
buf[offset..offset + len].copy_from_slice(&[0u8; 3]);
offset += len;
let len = 4;
assert_eq!(offset, METADATA_CHIP_SET_OFFSET);
buf[offset..offset + len].copy_from_slice(&0xFFFFFFFF_u32.to_le_bytes());
offset += len;
let len = 228;
buf[offset..offset + len].copy_from_slice(&[0xFFu8; 228]);
offset += len;
assert_eq!(offset, self.header_len());
Ok(offset)
}
fn update_chip_set_ptr(&self, buf: &mut [u8], ptr: u32) -> Result<()> {
if buf.len() < (METADATA_CHIP_SET_OFFSET + 4) {
return Err(crate::Error::BufferTooSmall {
location: "update_chip_set_ptr",
expected: (METADATA_CHIP_SET_OFFSET + 4),
actual: buf.len(),
});
}
buf[METADATA_CHIP_SET_OFFSET..METADATA_CHIP_SET_OFFSET + 4]
.copy_from_slice(&ptr.to_le_bytes());
Ok(())
}
pub fn rom_images_size(&self) -> usize {
self.chip_sets
.iter()
.filter(|set| set.has_data())
.map(|set| set.image_size(&self.board, &self.firmware_version))
.sum()
}
pub fn write_roms(&self, buf: &mut [u8]) -> Result<()> {
if buf.len() < self.rom_images_size() {
return Err(Error::BufferTooSmall {
location: "write_roms",
expected: self.rom_images_size(),
actual: buf.len(),
});
}
let mut offset = 0;
for chip_set in &self.chip_sets {
if !chip_set.has_data() && chip_set.chip_function() == ChipFunction::Ram {
continue;
}
let mut pio = self.pio();
if let Some(serve_mode) = chip_set
.firmware_overrides
.as_ref()
.and_then(|o| o.fire.as_ref())
.and_then(|f| f.serve_mode.as_ref())
{
pio = *serve_mode == FireServeMode::Pio;
}
let flip_cs1_x = if pio {
chip_set.set_type == ChipSetType::Multi
} else {
false
};
let size = chip_set.image_size(&self.board, &self.firmware_version);
for addr in 0..size {
buf[offset + addr] =
chip_set.get_byte(addr, &self.board, &self.firmware_version, flip_cs1_x);
}
offset += size;
}
Ok(())
}
#[allow(clippy::collapsible_if)]
fn serialize_firmware_overrides(config: &FirmwareConfig, buf: &mut [u8]) -> Result<usize> {
if buf.len() < CHIP_SET_FIRMWARE_OVERRIDES_METADATA_LEN {
return Err(Error::BufferTooSmall {
location: "serialize_firmware_overrides",
expected: CHIP_SET_FIRMWARE_OVERRIDES_METADATA_LEN,
actual: buf.len(),
});
}
let mut offset = 0;
let mut override_present = [0u8; 8];
if let Some(ref ice_config) = config.ice {
if ice_config.cpu_freq.is_some() {
override_present[0] |= 1 << 0; }
if ice_config.overclock.is_some() {
override_present[0] |= 1 << 1; }
}
if let Some(ref fire_config) = config.fire {
if fire_config.cpu_freq.is_some() {
override_present[0] |= 1 << 2; }
if fire_config.overclock.is_some() {
override_present[0] |= 1 << 3; }
if fire_config.vreg.is_some() {
override_present[0] |= 1 << 4; }
if fire_config.serve_mode.is_some() {
override_present[0] |= 1 << 7; }
override_present[1] |= 1 << 0;
if fire_config.force_16_bit {
override_present[1] |= 1 << 1; }
}
if config.led.is_some() {
override_present[0] |= 1 << 5; }
if config.swd.is_some() {
override_present[0] |= 1 << 6; }
buf[offset..offset + 8].copy_from_slice(&override_present);
offset += 8;
let ice_freq = config
.ice
.as_ref()
.and_then(|c| c.cpu_freq.as_ref())
.map(|f| f.get())
.unwrap_or(0xFFFF);
buf[offset..offset + 2].copy_from_slice(&ice_freq.to_le_bytes());
offset += 2;
let fire_freq = config
.fire
.as_ref()
.and_then(|c| c.cpu_freq.as_ref())
.map(|f| f.get())
.unwrap_or(0xFFFF);
buf[offset..offset + 2].copy_from_slice(&fire_freq.to_le_bytes());
offset += 2;
buf[offset] = config
.fire
.as_ref()
.and_then(|c| c.vreg.as_ref())
.map(|v| v.clone() as u8)
.unwrap_or(0xFF);
offset += 1;
buf[offset..offset + 3].copy_from_slice(&[PAD_METADATA_BYTE; 3]);
offset += 3;
assert_eq!(offset, 16, "Should be at 16 bytes");
let mut override_value = [0u8; 8];
if let Some(ref ice_config) = config.ice {
if let Some(overclock) = ice_config.overclock {
if overclock {
override_value[0] |= 1 << 0;
}
}
}
if let Some(ref fire_config) = config.fire {
if let Some(overclock) = fire_config.overclock {
if overclock {
override_value[0] |= 1 << 1;
}
}
if let Some(ref serve_mode) = fire_config.serve_mode {
if *serve_mode == FireServeMode::Pio {
override_value[0] |= 1 << 4;
}
}
if fire_config.rom_dma_preload {
override_value[0] |= 1 << 5;
}
if fire_config.force_16_bit {
override_value[0] |= 1 << 6;
}
}
if let Some(ref led) = config.led {
if led.enabled {
override_value[0] |= 1 << 2;
}
}
if let Some(ref swd) = config.swd {
if swd.swd_enabled {
override_value[0] |= 1 << 3;
}
}
buf[offset..offset + 8].copy_from_slice(&override_value);
offset += 8;
assert_eq!(offset, 24, "Should be at 24 bytes");
buf[offset..offset + 40].copy_from_slice(&[PAD_METADATA_BYTE; 40]);
offset += 40;
assert_eq!(
offset, CHIP_SET_FIRMWARE_OVERRIDES_METADATA_LEN,
"Should be at 64 bytes"
);
Ok(CHIP_SET_FIRMWARE_OVERRIDES_METADATA_LEN)
}
fn serialize_serve_config(params: &ServeAlgParams, buf: &mut [u8]) -> Result<usize> {
if buf.len() < CHIP_SET_SERVE_CONFIG_METADATA_LEN {
return Err(Error::BufferTooSmall {
location: "serialize_serve_config",
expected: CHIP_SET_SERVE_CONFIG_METADATA_LEN,
actual: buf.len(),
});
}
buf[..64].fill(0xFF);
let len = params.params.len().min(CHIP_SET_SERVE_CONFIG_METADATA_LEN);
buf[..len].copy_from_slice(¶ms.params[..len]);
if len < CHIP_SET_SERVE_CONFIG_METADATA_LEN {
buf[len..CHIP_SET_SERVE_CONFIG_METADATA_LEN].fill(PAD_METADATA_BYTE);
}
Ok(CHIP_SET_SERVE_CONFIG_METADATA_LEN)
}
fn firmware_overrides_len(&self) -> usize {
const MIN_EXTENDED_VERSION: FirmwareVersion = FirmwareVersion::new(0, 6, 0, 0);
if self.firmware_version < MIN_EXTENDED_VERSION {
return 0;
}
let mut total = 0;
for chip_set in &self.chip_sets {
if let Some(ref fw_config) = chip_set.firmware_overrides {
total += CHIP_SET_FIRMWARE_OVERRIDES_METADATA_LEN;
if fw_config.serve_alg_params.is_some() {
total += CHIP_SET_FIRMWARE_OVERRIDES_METADATA_LEN;
}
}
}
total
}
}