extern crate alloc;
pub mod builder;
pub mod compat;
pub mod firmware;
pub mod image;
pub mod meta;
pub mod v1;
pub mod v2;
pub use builder::Builder;
pub use firmware::{
DebugConfig, FireConfig, FireCpuFreq, FireServeMode, FireVreg, FirmwareConfig, IceConfig,
IceCpuFreq, LedConfig, ServeAlgParams,
};
pub use image::{Chip, ChipSet, ChipSetType, CsConfig, CsLogic, SizeHandling};
pub use image::{MAX_IMAGE_SIZE, PAD_BLANK_BYTE, PAD_NO_CHIP_BYTE};
pub use meta::{MAX_METADATA_LEN, Metadata, PAD_METADATA_BYTE};
use onerom_config::mcu::Family;
use alloc::string::String;
use onerom_config::chip::ChipType;
use onerom_config::fw::{FirmwareVersion, ServeAlg};
use onerom_config::hw::Board;
pub use v1::MAX_SUPPORTED_FIRMWARE_VERSION as MAX_SUPPORTED_FIRMWARE_VERSION_V1;
pub use v1::MIN_SUPPORTED_FIRMWARE_VERSION as MIN_SUPPORTED_FIRMWARE_VERSION_V1;
pub use v1::SUPPORTED_CHIP_TYPES as SUPPORTED_CHIP_TYPES_V1;
pub use v1::UNSUPPORTED_FIRMWARE_VERSIONS as UNSUPPORTED_FIRMWARE_VERSIONS_V1;
pub use v2::MAX_FW_VERSION as MAX_SUPPORTED_FIRMWARE_VERSION_V2;
pub use v2::MIN_FW_VERSION as MIN_SUPPORTED_FIRMWARE_VERSION_V2;
pub use v2::SUPPORTED_CHIP_TYPES as SUPPORTED_CHIP_TYPES_V2;
pub use v2::UNSUPPORTED_FIRMWARE_VERSIONS as UNSUPPORTED_FIRMWARE_VERSIONS_V2;
pub const METADATA_VERSION: u32 = 1;
const METADATA_VERSION_STR: &str = "1";
pub const FIRMWARE_SIZE: usize = 48 * 1024;
pub const MIN_FIRMWARE_OVERRIDES_VERSION: FirmwareVersion = FirmwareVersion::new(0, 6, 0, 0);
#[derive(Debug, serde::Serialize, serde::Deserialize)]
pub enum Error {
RightSize {
chip_type: ChipType,
size: usize,
size_handling: SizeHandling,
},
ImageTooSmall {
chip_type: ChipType,
index: usize,
expected: usize,
actual: usize,
},
ImageTooLarge {
chip_type: ChipType,
image_size: usize,
expected_size: usize,
},
DuplicationNotExactDivisor {
chip_type: ChipType,
image_size: usize,
expected_size: usize,
},
BufferTooSmall {
location: &'static str,
expected: usize,
actual: usize,
},
NoChips {
id: usize,
},
TooManyChips {
id: usize,
expected: usize,
actual: usize,
},
TooFewChips {
id: usize,
expected: usize,
actual: usize,
},
MissingCsConfig {
chip_type: ChipType,
line: &'static str,
},
MissingPointer {
id: usize,
},
InvalidServeAlg {
serve_alg: ServeAlg,
},
InconsistentCsLogic {
first: CsLogic,
other: CsLogic,
},
InvalidConfig {
error: String,
},
UnsupportedConfigVersion {
version: u32,
},
DuplicateFile {
id: usize,
},
InvalidFile {
id: usize,
total: usize,
},
MissingFile {
id: usize,
},
UnsupportedToolChipType {
chip_type: ChipType,
},
UnsupportedBoardChipType {
board: Board,
chip_type: ChipType,
},
InvalidLicense {
id: usize,
},
UnvalidatedLicense {
id: usize,
},
BadLocation {
id: usize,
reason: String,
},
UnsupportedFrequency {
frequency_mhz: u32,
},
FirmwareTooOld {
feat: &'static str,
version: FirmwareVersion,
minimum: FirmwareVersion,
},
UnsupportedFeature {
feat: &'static str,
},
FirmwareTooNew {
version: FirmwareVersion,
maximum: FirmwareVersion,
},
FirmwareUnsupported {
version: FirmwareVersion,
},
Base64,
Base16,
InvalidPluginImage {
plugin_type: ChipType,
image_file: String,
error: String,
},
UnsupportedMcuFamily {
family: Family,
version: FirmwareVersion,
},
UnsupportedBoardConfig {
board: Board,
reason: String,
},
MetadataOverflow {
size: usize,
},
MissingImageData {
chip_type: ChipType,
index: usize,
},
RomTableTooLarge {
size: usize,
max: usize,
},
}
type Result<T> = core::result::Result<T, Error>;
impl core::fmt::Display for Error {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Error::RightSize {
chip_type,
size,
size_handling,
} => write!(
f,
"The provided image is already the correct size ({size} bytes) for a {chip_type}. The {size_handling} option should not be used. Remove it."
),
Error::ImageTooSmall {
chip_type,
index: _,
expected,
actual,
} => write!(
f,
"The provided image is too small for a {chip_type}.\n Expected at least {expected} bytes, got {actual} bytes.\n Consider using the duplicate or padding options to make the image larger."
),
Error::ImageTooLarge {
chip_type,
image_size,
expected_size,
} => write!(
f,
"The provided chip image is larger than the size supported by a {chip_type}: expected at most {expected_size} bytes, got {image_size} bytes"
),
Error::DuplicationNotExactDivisor {
chip_type,
image_size,
expected_size,
} => write!(
f,
"Image duplication requires that the size of the provided image is an exact divisor of the size required by a {chip_type}.\n {image_size} is not an exact divisor of {expected_size}.\n Consider using the padding option instead."
),
Error::BufferTooSmall {
location,
expected,
actual,
} => write!(
f,
"Internal error: Buffer for {location} is too small: expected at least {expected} bytes, got {actual} bytes"
),
Error::NoChips { id } => write!(f, "No chips were specified for set {id}"),
Error::TooManyChips {
id,
expected,
actual,
} => write!(
f,
"Too many chips specified for set {id}.\n Expected at most {expected}, got {actual}"
),
Error::TooFewChips {
id,
expected,
actual,
} => write!(
f,
"Too few chips specified for set {id}.\n Expected at least {expected}, got {actual}"
),
Error::MissingCsConfig { chip_type, line } => write!(
f,
"The configuration is missing required chip select line {line} configuration for {chip_type}"
),
Error::MissingPointer { id } => {
write!(f, "Internal error: Missing pointer with internal id: {id}")
}
Error::InvalidServeAlg { serve_alg } => {
write!(
f,
"The configured serving algorithm is not valid for the type of chip, ROM or set: {serve_alg}"
)
}
Error::InconsistentCsLogic { first, other } => write!(
f,
"The configured chip select logic is self-inconsistent:\n The first is {first}, the other is {other}"
),
Error::InvalidConfig { error } => write!(
f,
"There is a problem with the supplied configuration:\n {error}"
),
Error::UnsupportedConfigVersion { version } => {
write!(
f,
"The configuration version {version} is unsupported by this tool"
)
}
Error::DuplicateFile { id } => write!(
f,
"Internal error: Duplicate file supplied with internal id: {id}"
),
Error::InvalidFile { id, total } => {
write!(
f,
"Internal error: Invalid file with internal id: {id}, total files: {total}"
)
}
Error::MissingFile { id } => {
write!(f, "Internal error: Missing file with internal id: {id}")
}
Error::UnsupportedToolChipType { chip_type } => {
write!(f, "This tool does not support chip type {chip_type}")
}
Error::UnsupportedBoardChipType { board, chip_type } => {
write!(
f,
"The board {board} does not support chip type {chip_type} with this firmware version"
)
}
Error::InvalidLicense { id } => {
write!(f, "Internal error: No license exists with internal id {id}")
}
Error::UnvalidatedLicense { id } => write!(
f,
"Internal error: A license with internal id {id} has not been validated"
),
Error::BadLocation { id, reason } => {
write!(
f,
"An invalid location was specified for the file with internal id {id}\n {reason}"
)
}
Error::UnsupportedFrequency { frequency_mhz } => {
write!(
f,
"Unsupported MCU frequency for this One ROM: {frequency_mhz}MHz"
)
}
Error::FirmwareTooOld {
feat,
version,
minimum,
} => write!(
f,
"Selected firmware version {version} does not support {feat}\n The minimum supported version for {feat} is {minimum}"
),
Error::UnsupportedFeature { feat } => {
write!(f, "The {feat} feature is currently unsupported")
}
Error::FirmwareTooNew { version, maximum } => write!(
f,
"Selected firmware version {version} is too new\n The maximum firmware version supported by this tool is {maximum}"
),
Error::FirmwareUnsupported { version } => write!(
f,
"Selected firmware version {version} is unsupported by this tool due to known issues"
),
Error::Base64 => write!(f, "Base64 encoding/decoding error"),
Error::Base16 => write!(f, "Base16 encoding/decoding error"),
Error::InvalidPluginImage {
plugin_type,
image_file,
error,
} => write!(
f,
"The provided {plugin_type} image {image_file} is invalid:\n {error}"
),
Error::UnsupportedMcuFamily { family, version } => write!(
f,
"The MCU family {family} is not supported by this firmware version {version}"
),
Error::UnsupportedBoardConfig { board, reason } => write!(
f,
"The board {board} does not support this configuration: {reason}"
),
Error::MetadataOverflow { size } => write!(
f,
"The configuration's metadata exceeds the {size}-byte metadata region"
),
Error::MissingImageData { chip_type, index } => write!(
f,
"Internal error: chip {index} ({chip_type}) in this set has no image data"
),
Error::RomTableTooLarge { size, max } => write!(
f,
"ROM table is {size} bytes, exceeds maximum of {max} bytes for a single slot"
),
}
}
}
impl From<serde_json::Error> for Error {
fn from(e: serde_json::Error) -> Self {
Error::InvalidConfig {
error: e.to_string(),
}
}
}
pub fn crate_version() -> &'static str {
env!("CARGO_PKG_VERSION")
}
pub fn metadata_version() -> &'static str {
METADATA_VERSION_STR
}
pub trait MetadataWriter {
fn metadata_len(&self) -> usize;
fn total_set_count(&self) -> usize;
fn rom_images_size(&self) -> usize;
fn write_all(&self, buf: &mut [u8], rtn_chip_data_ptrs: &mut [u32]) -> Result<usize>;
fn write_roms(&self, buf: &mut [u8]) -> Result<()>;
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct License {
pub id: usize,
pub file_id: usize,
pub url: String,
validated: bool,
}
impl License {
pub fn new(id: usize, file_id: usize, url: String) -> Self {
Self {
id,
file_id,
url,
validated: false,
}
}
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[cfg_attr(feature = "schemars", schemars(title = "One ROM Configuration"))]
pub struct Config {
#[cfg_attr(feature = "schemars", schemars(schema_with = "version_schema"))]
pub version: u32,
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
pub description: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub detail: Option<String>,
#[serde(alias = "rom_sets")]
pub chip_sets: Vec<ChipSetConfig>,
#[serde(skip_serializing_if = "Option::is_none")]
pub notes: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub categories: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub instance_name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub serial_override: Option<String>,
#[serde(default = "default_boot_logging")]
pub boot_logging: bool,
#[serde(default = "default_swd_enabled")]
pub swd_enabled: bool,
#[serde(default = "default_turbo_boot")]
pub turbo_boot: bool,
}
pub(crate) fn default_boot_logging() -> bool {
false
}
pub(crate) fn default_swd_enabled() -> bool {
true
}
pub(crate) fn default_turbo_boot() -> bool {
false
}
#[cfg(feature = "schemars")]
fn version_schema(_gen: &mut schemars::SchemaGenerator) -> schemars::Schema {
schemars::json_schema!({
"const": 1
})
}
#[derive(Debug, Default, Clone, serde::Serialize, serde::Deserialize)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub struct ChipSetConfig {
#[serde(rename = "type")]
#[cfg_attr(feature = "schemars", schemars(default))]
pub set_type: ChipSetType,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(alias = "roms")]
pub chips: Vec<ChipConfig>,
#[serde(skip_serializing_if = "Option::is_none")]
pub serve_alg: Option<ServeAlg>,
#[serde(skip_serializing_if = "Option::is_none")]
pub firmware_overrides: Option<FirmwareConfig>,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub struct ChipConfig {
#[serde(default)]
pub file: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub license: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(rename = "type")]
pub chip_type: ChipType,
#[serde(skip_serializing_if = "Option::is_none")]
pub cs1: Option<CsLogic>,
#[serde(skip_serializing_if = "Option::is_none")]
pub cs2: Option<CsLogic>,
#[serde(skip_serializing_if = "Option::is_none")]
pub cs3: Option<CsLogic>,
#[serde(skip_serializing_if = "Option::is_none")]
pub ce: Option<CsLogic>,
#[serde(skip_serializing_if = "Option::is_none")]
pub oe: Option<CsLogic>,
#[serde(default)]
pub allow_cs_ignore: bool,
#[serde(default, skip_serializing_if = "SizeHandling::is_none")]
pub size_handling: SizeHandling,
#[serde(skip_serializing_if = "Option::is_none")]
pub extract: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub label: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub location: Option<Location>,
}
impl ChipConfig {
fn filename(&self) -> String {
if let Some(label) = &self.label {
return label.clone();
}
let filename_base = if let Some(extract) = &self.extract {
format!("{}|{}", self.file, extract)
} else {
self.file.clone()
};
if let Some(location) = &self.location {
format!(
"{}|start={:#X},length={:#X}",
filename_base, location.start, location.length
)
} else {
filename_base
}
}
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct FileSpec {
pub id: usize,
pub description: Option<String>,
pub source: String,
pub extract: Option<String>,
pub size_handling: SizeHandling,
pub chip_type: ChipType,
pub rom_size: usize,
pub cs1: Option<CsLogic>,
pub cs2: Option<CsLogic>,
pub cs3: Option<CsLogic>,
pub ce: Option<CsLogic>,
pub oe: Option<CsLogic>,
pub set_id: usize,
pub set_type: ChipSetType,
pub set_description: Option<String>,
}
#[derive(Debug, serde::Serialize, serde::Deserialize)]
pub struct FileData {
pub id: usize,
pub data: alloc::vec::Vec<u8>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[serde(rename_all = "snake_case")]
pub struct Location {
pub start: usize,
pub length: usize,
}