use crate::chip::Chip;
#[cfg(feature = "uom")]
use crate::information::Information;
use thiserror::Error;
#[cfg(feature = "uom")]
use uom::si::information::byte;
#[cfg(feature = "serde")]
use crate::information::deser_option_information;
#[cfg(feature = "serde")]
#[derive(Debug, Clone, PartialEq, serde::Deserialize)]
#[serde(deny_unknown_fields)]
pub(crate) struct SerdeSection {
pub name: String,
#[serde(default)]
pub(crate) boot: bool,
#[serde(default)]
pub(crate) maximize: bool,
pub(crate) pages: Option<u64>,
#[serde(default, deserialize_with = "deser_option_information")]
pub(crate) size: Option<u64>,
#[serde(default)]
pub(crate) address: Option<u64>,
#[serde(default)]
pub(crate) address_align: Option<u64>,
#[serde(default)]
pub(crate) relative_pages: i64,
#[serde(default)]
pub(crate) linker_name: Option<String>,
}
#[cfg(feature = "serde")]
impl From<SerdeSection> for Section<()> {
fn from(value: SerdeSection) -> Self {
Self {
name: value.name,
boot: value.boot,
maximize: value.maximize,
pages: value.pages,
size: value.size,
address: value.address,
address_align: value.address_align,
relative_pages: value.relative_pages,
linker_name: value.linker_name,
metadata: (),
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Section<MetaData: Clone> {
pub name: String,
pub(crate) boot: bool,
pub(crate) maximize: bool,
pub(crate) pages: Option<u64>,
pub(crate) size: Option<u64>,
pub(crate) address: Option<u64>,
pub(crate) address_align: Option<u64>,
pub(crate) relative_pages: i64,
pub(crate) linker_name: Option<String>,
pub(crate) metadata: MetaData,
}
#[cfg(feature = "serde")]
impl<'de> serde::Deserialize<'de> for Section<()> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
SerdeSection::deserialize(deserializer).map(Into::into)
}
}
#[derive(Error, Clone, Debug, PartialEq)]
pub enum SectionError {
#[error("incorrect section name")]
InvalidSectionName,
#[error("section not completely resolved")]
UnresolvedSection,
#[error("No page and byte size bound for section")]
NoSizeBound,
}
impl Section<()> {
pub fn new(name: impl Into<String>) -> Result<Self, SectionError> {
let name = name.into();
if name.chars().any(|c| !c.is_ascii_alphanumeric() && c != '_') {
return Err(SectionError::InvalidSectionName);
}
Ok(Self {
name,
boot: false,
maximize: false,
pages: None,
size: None,
address: None,
address_align: None,
relative_pages: 0,
linker_name: None,
metadata: (),
})
}
}
impl<MetaData: Clone> Section<MetaData> {
pub fn merge(&mut self, sec: &Self) {
self.boot = self.boot || sec.boot;
self.maximize = self.maximize || sec.maximize;
if let Some(pages) = sec.pages
&& self.pages.is_none_or(|p| p < pages)
{
self.pages = Some(pages);
}
if let Some(size) = sec.size
&& self.size.is_none_or(|s| s < size)
{
self.size = Some(size);
}
if let Some(address) = sec.address
&& self.address.is_none()
{
self.address = Some(address);
}
if self.relative_pages < sec.relative_pages {
self.relative_pages = sec.relative_pages;
}
if let Some(linker_name) = &sec.linker_name
&& self.linker_name.is_none()
{
self.linker_name = Some(linker_name.clone());
}
}
#[must_use]
pub fn set_boot(mut self, boot: bool) -> Self {
self.boot = boot;
self
}
#[must_use]
pub fn set_maximize(mut self, maximize: bool) -> Self {
self.maximize = maximize;
self
}
#[must_use]
pub fn set_pages(mut self, pages: u64) -> Self {
self.pages = Some(pages);
self
}
#[must_use]
pub fn clear_pages(mut self) -> Self {
self.pages = None;
self
}
#[must_use]
pub fn set_size(mut self, bytes: u64) -> Self {
self.size = Some(bytes);
self
}
#[must_use]
#[cfg(feature = "uom")]
pub fn set_size_bytes(self, bytes: impl Into<Information>) -> Self {
self.set_size(bytes.into().get::<byte>())
}
#[must_use]
pub fn clear_size(mut self) -> Self {
self.size = None;
self
}
#[must_use]
pub fn set_address(mut self, address: u64) -> Self {
self.address = Some(address);
self
}
#[must_use]
pub fn set_relative_pages(mut self, num: i64) -> Self {
self.relative_pages = num;
self
}
#[must_use]
pub fn clear_relative_pages(mut self) -> Self {
self.relative_pages = 0;
self
}
#[must_use]
pub fn clear_address(mut self) -> Self {
self.address = None;
self
}
#[must_use]
pub fn set_address_align(mut self, align: u64) -> Self {
self.address_align = Some(align);
self
}
#[must_use]
pub fn clear_address_align(mut self) -> Self {
self.address_align = None;
self
}
#[must_use]
pub fn is_fixed(&self) -> bool {
self.boot || self.address.is_some()
}
#[must_use]
#[cfg(feature = "uom")]
pub fn pages_required(&self, page_size: Information) -> u64 {
let Some(size) = self.size else {
return self.pages.unwrap_or(0);
};
size.div_ceil(page_size.get::<byte>())
}
#[must_use]
pub fn is_resolved(&self) -> bool {
let resolved_size = self.pages.is_some() || self.size.is_some();
self.address.is_some() && resolved_size && !self.maximize
}
#[must_use]
pub fn needs_maximizing(&self) -> bool {
self.maximize
}
#[must_use]
pub fn needs_allocating(&self) -> bool {
!self.maximize && self.address.is_none()
}
pub(crate) fn as_resolved(
&self,
chip: &Chip,
) -> Result<ResolvedSection<MetaData>, SectionError> {
if !self.is_resolved() {
return Err(SectionError::UnresolvedSection);
}
let Some(address) = self.address else {
return Err(SectionError::UnresolvedSection);
};
let (pages, size) = self.resolve_page_and_size(chip)?;
Ok(ResolvedSection {
name: self.name.clone(),
pages,
size,
address,
linker_name: self.linker_name.as_ref().unwrap_or(&self.name).clone(),
metadata: self.metadata.clone(),
})
}
pub(crate) fn required_pages(&self, page_size: u64) -> Result<u64, SectionError> {
let page_required = self.pages.unwrap_or(0);
let size_pages = self.size.map_or(0, |size| size.div_ceil(page_size));
if page_required == 0 && size_pages == 0 {
Err(SectionError::NoSizeBound)
} else {
Ok(std::cmp::max(page_required, size_pages))
}
}
fn resolve_page_and_size(&self, chip: &Chip) -> Result<(u64, u64), SectionError> {
let Some(address) = self.address else {
return Err(SectionError::UnresolvedSection);
};
let (pages, size) = match (self.pages, self.size) {
(None, None) => return Err(SectionError::UnresolvedSection),
(None, Some(size)) => (self.required_pages(chip.page_size(address))?, size),
(Some(pages), None) => (pages, chip.page_size(address) * pages),
(Some(pages), Some(size)) => {
let size_of_pages = chip.page_size(address) * pages;
if size > size_of_pages {
(self.required_pages(chip.page_size(address))?, size)
} else {
(pages, size_of_pages)
}
}
};
Ok((pages, size))
}
pub(crate) fn required_pages_in_bin(
&self,
bin: &crate::bin::MemoryBin,
) -> Result<u64, SectionError> {
self.required_pages(bin.page_size)
}
pub(crate) fn replace_metadata<NewData: Clone>(self, data: NewData) -> Section<NewData> {
Section {
name: self.name,
boot: self.boot,
maximize: self.maximize,
pages: self.pages,
size: self.size,
address: self.address,
address_align: self.address_align,
relative_pages: self.relative_pages,
linker_name: self.linker_name,
metadata: data,
}
}
#[must_use]
pub fn clear_metadata(self) -> Section<()> {
self.replace_metadata(())
}
}
impl<MetaData: Clone> std::fmt::Display for Section<MetaData> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Section({})", self.name)
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct ResolvedSection<MetaData: Clone> {
pub name: String,
pub pages: u64,
pub size: u64,
pub address: u64,
pub linker_name: String,
pub metadata: MetaData,
}
impl<MetaData: Clone> ResolvedSection<MetaData> {
pub(crate) fn new(
name: String,
pages: u64,
size: u64,
address: u64,
linker_name: Option<String>,
metadata: MetaData,
) -> Self {
let linker_name = linker_name.unwrap_or(name.clone());
Self {
name,
pages,
size,
address,
linker_name,
metadata,
}
}
#[must_use]
pub fn as_memory_section(&self) -> ld_memory::MemorySection {
ld_memory::MemorySection::new(&self.linker_name, self.address, self.size)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn name() {
assert!(Section::new("test").is_ok());
assert!(Section::new("test2").is_ok());
assert!(Section::new("test space").is_err());
}
#[test]
#[cfg(feature = "serde")]
fn deser() {
let input = r#"
"name": test
"boot": false
"pages": 2
"size": 3 KiB
"#;
let section: Section<_> = yaml_serde::from_str(input).unwrap();
assert_eq!(section.name, "test");
assert!(!section.boot);
assert_eq!(section.pages, Some(2));
assert_eq!(section.size, Some(3 * 1024));
}
#[test]
fn merge() {
let mut section = Section::new("t").unwrap().set_size(100);
let second = Section::new("t").unwrap().set_pages(1);
section.merge(&second);
assert_eq!(section.pages, Some(1));
assert_eq!(section.size, Some(100));
}
#[test]
fn page_vs_bytes() {
let section = Section::new("t")
.unwrap()
.set_address(0)
.set_size(10)
.set_pages(2);
let chip = Chip::new(10, 0, 20).unwrap();
let (pages, size) = section.resolve_page_and_size(&chip).unwrap();
assert_eq!(pages, 2);
assert_eq!(size, 20);
let chip = Chip::new(2, 0, 20).unwrap();
let (pages, size) = section.resolve_page_and_size(&chip).unwrap();
assert_eq!(pages, 5);
assert_eq!(size, 10);
let chip = Chip::new(5, 0, 20).unwrap();
let (pages, size) = section.resolve_page_and_size(&chip).unwrap();
assert_eq!(pages, 2);
assert_eq!(size, 10);
}
}