use std::{
cmp::Ordering,
fmt::Display,
mem::{align_of, size_of},
};
use bytemuck::{Pod, PodCastError, Zeroable};
use serde::{Deserialize, Serialize};
use snafu::{Backtrace, Snafu};
use super::RawBuildInfoError;
#[repr(C)]
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Zeroable, Pod, Deserialize, Serialize)]
pub struct AutoloadInfoEntry {
pub base_address: u32,
pub code_size: u32,
pub bss_size: u32,
}
#[derive(Clone, Copy, PartialEq, Eq, Debug, Deserialize, Serialize)]
pub enum AutoloadKind {
Itcm,
Dtcm,
Unknown(u32),
}
impl PartialOrd for AutoloadKind {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for AutoloadKind {
fn cmp(&self, other: &Self) -> Ordering {
match (self, other) {
(_, _) if self == other => Ordering::Equal,
(AutoloadKind::Itcm, _) => Ordering::Less,
(_, AutoloadKind::Itcm) => Ordering::Greater,
(AutoloadKind::Dtcm, _) => Ordering::Less,
(_, AutoloadKind::Dtcm) => Ordering::Greater,
(AutoloadKind::Unknown(a), AutoloadKind::Unknown(b)) => a.cmp(b),
}
}
}
#[derive(Clone, Copy, Deserialize, Serialize, Debug, PartialEq, Eq)]
pub struct AutoloadInfo {
#[serde(flatten)]
pub list_entry: AutoloadInfoEntry,
pub kind: AutoloadKind,
}
#[derive(Debug, Snafu)]
pub enum RawAutoloadInfoError {
#[snafu(transparent)]
RawBuildInfo {
source: RawBuildInfoError,
},
#[snafu(display("autoload infos must be a multiple of {} bytes:\n{backtrace}", size_of::<AutoloadInfo>()))]
InvalidSize {
backtrace: Backtrace,
},
#[snafu(display("expected {expected}-alignment for autoload infos but got {actual}-alignment:\n{backtrace}"))]
Misaligned {
expected: usize,
actual: usize,
backtrace: Backtrace,
},
}
impl AutoloadInfoEntry {
fn check_size(data: &'_ [u8]) -> Result<(), RawAutoloadInfoError> {
let size = size_of::<Self>();
if !data.len().is_multiple_of(size) {
InvalidSizeSnafu {}.fail()
} else {
Ok(())
}
}
fn handle_pod_cast<T>(result: Result<T, PodCastError>, addr: usize) -> Result<T, RawAutoloadInfoError> {
match result {
Ok(build_info) => Ok(build_info),
Err(PodCastError::TargetAlignmentGreaterAndInputNotAligned) => {
MisalignedSnafu { expected: align_of::<Self>(), actual: 1usize << addr.trailing_zeros() }.fail()
}
Err(PodCastError::AlignmentMismatch) => panic!(),
Err(PodCastError::OutputSliceWouldHaveSlop) => panic!(),
Err(PodCastError::SizeMismatch) => unreachable!(),
}
}
pub fn borrow_from_slice(data: &'_ [u8]) -> Result<&'_ [Self], RawAutoloadInfoError> {
Self::check_size(data)?;
let addr = data as *const [u8] as *const () as usize;
Self::handle_pod_cast(bytemuck::try_cast_slice(data), addr)
}
}
impl AutoloadInfo {
pub fn new(list_entry: AutoloadInfoEntry, index: u32) -> Self {
let kind = match list_entry.base_address {
0x1ff8000 => AutoloadKind::Itcm,
0x27e0000 | 0x27c0000 | 0x23c0000 => AutoloadKind::Dtcm,
_ => AutoloadKind::Unknown(index),
};
Self { list_entry, kind }
}
pub fn base_address(&self) -> u32 {
self.list_entry.base_address
}
pub fn code_size(&self) -> u32 {
self.list_entry.code_size
}
pub fn bss_size(&self) -> u32 {
self.list_entry.bss_size
}
pub fn kind(&self) -> AutoloadKind {
self.kind
}
pub fn entry(&self) -> &AutoloadInfoEntry {
&self.list_entry
}
pub fn display(&self, indent: usize) -> DisplayAutoloadInfo<'_> {
DisplayAutoloadInfo { info: self, indent }
}
}
pub struct DisplayAutoloadInfo<'a> {
info: &'a AutoloadInfo,
indent: usize,
}
impl Display for DisplayAutoloadInfo<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let i = " ".repeat(self.indent);
let info = &self.info;
writeln!(f, "{i}Type .......... : {}", info.kind)?;
writeln!(f, "{i}Base address .. : {:#x}", info.list_entry.base_address)?;
writeln!(f, "{i}Code size ..... : {:#x}", info.list_entry.code_size)?;
writeln!(f, "{i}.bss size ..... : {:#x}", info.list_entry.bss_size)?;
Ok(())
}
}
impl Display for AutoloadKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
AutoloadKind::Itcm => write!(f, "ITCM"),
AutoloadKind::Dtcm => write!(f, "DTCM"),
AutoloadKind::Unknown(index) => write!(f, "Unknown({index})"),
}
}
}