Skip to main content

ds_rom/rom/raw/
autoload_info.rs

1use std::{
2    cmp::Ordering,
3    fmt::Display,
4    mem::{align_of, size_of},
5};
6
7use bytemuck::{Pod, PodCastError, Zeroable};
8use serde::{Deserialize, Serialize};
9use snafu::{Backtrace, Snafu};
10
11use super::RawBuildInfoError;
12
13/// An entry in the autoload list.
14#[repr(C)]
15#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Zeroable, Pod, Deserialize, Serialize)]
16pub struct AutoloadInfoEntry {
17    /// Base address of the autoload module.
18    pub base_address: u32,
19    /// Size of the module's initialized area.
20    pub code_size: u32,
21    /// Size of the module's uninitialized area.
22    pub bss_size: u32,
23}
24
25/// Autoload kind.
26#[derive(Clone, Copy, PartialEq, Eq, Debug, Deserialize, Serialize)]
27pub enum AutoloadKind {
28    /// Instruction TCM (Tightly Coupled Memory). Mainly used to make functions have fast and predictable load times.
29    Itcm,
30    /// Data TCM (Tightly Coupled Memory). Mainly used to make data have fast and predictable access times.
31    Dtcm,
32    /// Other autoload block of unknown purpose.
33    Unknown(u32),
34}
35
36impl PartialOrd for AutoloadKind {
37    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
38        Some(self.cmp(other))
39    }
40}
41
42impl Ord for AutoloadKind {
43    fn cmp(&self, other: &Self) -> Ordering {
44        // ITCM < DTCM < Unknown
45        match (self, other) {
46            (_, _) if self == other => Ordering::Equal,
47            (AutoloadKind::Itcm, _) => Ordering::Less,
48            (_, AutoloadKind::Itcm) => Ordering::Greater,
49            (AutoloadKind::Dtcm, _) => Ordering::Less,
50            (_, AutoloadKind::Dtcm) => Ordering::Greater,
51            (AutoloadKind::Unknown(a), AutoloadKind::Unknown(b)) => a.cmp(b),
52        }
53    }
54}
55
56/// Info about an autoload block.
57#[derive(Clone, Copy, Deserialize, Serialize, Debug, PartialEq, Eq)]
58pub struct AutoloadInfo {
59    #[serde(flatten)]
60    /// Entry in the autoload list.
61    pub list_entry: AutoloadInfoEntry,
62    /// The kind of autoload block.
63    pub kind: AutoloadKind,
64}
65
66/// Errors related to [`AutoloadInfo`].
67#[derive(Debug, Snafu)]
68pub enum RawAutoloadInfoError {
69    /// See [`RawBuildInfoError`].
70    #[snafu(transparent)]
71    RawBuildInfo {
72        /// Source error.
73        source: RawBuildInfoError,
74    },
75    /// Occurs when the input is not evenly divisible into a slice of [`AutoloadInfo`].
76    #[snafu(display("autoload infos must be a multiple of {} bytes:\n{backtrace}", size_of::<AutoloadInfo>()))]
77    InvalidSize {
78        /// Backtrace to the source of the error.
79        backtrace: Backtrace,
80    },
81    /// Occurs when the input is less aligned than [`AutoloadInfo`].
82    #[snafu(display("expected {expected}-alignment for autoload infos but got {actual}-alignment:\n{backtrace}"))]
83    Misaligned {
84        /// Expected alignment.
85        expected: usize,
86        /// Actual input alignment.
87        actual: usize,
88        /// Backtrace to the source of the error.
89        backtrace: Backtrace,
90    },
91}
92
93impl AutoloadInfoEntry {
94    fn check_size(data: &'_ [u8]) -> Result<(), RawAutoloadInfoError> {
95        let size = size_of::<Self>();
96        if !data.len().is_multiple_of(size) {
97            InvalidSizeSnafu {}.fail()
98        } else {
99            Ok(())
100        }
101    }
102
103    fn handle_pod_cast<T>(result: Result<T, PodCastError>, addr: usize) -> Result<T, RawAutoloadInfoError> {
104        match result {
105            Ok(build_info) => Ok(build_info),
106            Err(PodCastError::TargetAlignmentGreaterAndInputNotAligned) => {
107                MisalignedSnafu { expected: align_of::<Self>(), actual: 1usize << addr.trailing_zeros() }.fail()
108            }
109            Err(PodCastError::AlignmentMismatch) => panic!(),
110            Err(PodCastError::OutputSliceWouldHaveSlop) => panic!(),
111            Err(PodCastError::SizeMismatch) => unreachable!(),
112        }
113    }
114
115    /// Reinterprets a `&[u8]` as a slice of [`AutoloadInfo`].
116    ///
117    /// # Errors
118    ///
119    /// This function will return an error if the input has the wrong size or alignment.
120    pub fn borrow_from_slice(data: &'_ [u8]) -> Result<&'_ [Self], RawAutoloadInfoError> {
121        Self::check_size(data)?;
122        let addr = data as *const [u8] as *const () as usize;
123        Self::handle_pod_cast(bytemuck::try_cast_slice(data), addr)
124    }
125}
126
127impl AutoloadInfo {
128    /// Creates a new [`AutoloadInfo`] from an [`AutoloadInfoEntry`].
129    pub fn new(list_entry: AutoloadInfoEntry, index: u32) -> Self {
130        let kind = match list_entry.base_address {
131            0x1ff8000 => AutoloadKind::Itcm,
132            0x27e0000 | 0x27c0000 | 0x23c0000 => AutoloadKind::Dtcm,
133            _ => AutoloadKind::Unknown(index),
134        };
135
136        Self { list_entry, kind }
137    }
138
139    /// Returns the index of this [`AutoloadInfo`].
140    pub fn base_address(&self) -> u32 {
141        self.list_entry.base_address
142    }
143
144    /// Returns the code size of this [`AutoloadInfo`].
145    pub fn code_size(&self) -> u32 {
146        self.list_entry.code_size
147    }
148
149    /// Returns the size of the uninitialized data of this [`AutoloadInfo`].
150    pub fn bss_size(&self) -> u32 {
151        self.list_entry.bss_size
152    }
153
154    /// Returns the kind of this [`AutoloadInfo`].
155    pub fn kind(&self) -> AutoloadKind {
156        self.kind
157    }
158
159    /// Returns the entry of this [`AutoloadInfo`].
160    pub fn entry(&self) -> &AutoloadInfoEntry {
161        &self.list_entry
162    }
163
164    /// Creates a [`DisplayAutoloadInfo`] which implements [`Display`].
165    pub fn display(&self, indent: usize) -> DisplayAutoloadInfo<'_> {
166        DisplayAutoloadInfo { info: self, indent }
167    }
168}
169
170/// Can be used to display values inside [`AutoloadInfo`].
171pub struct DisplayAutoloadInfo<'a> {
172    info: &'a AutoloadInfo,
173    indent: usize,
174}
175
176impl Display for DisplayAutoloadInfo<'_> {
177    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
178        let i = " ".repeat(self.indent);
179        let info = &self.info;
180        writeln!(f, "{i}Type .......... : {}", info.kind)?;
181        writeln!(f, "{i}Base address .. : {:#x}", info.list_entry.base_address)?;
182        writeln!(f, "{i}Code size ..... : {:#x}", info.list_entry.code_size)?;
183        writeln!(f, "{i}.bss size ..... : {:#x}", info.list_entry.bss_size)?;
184        Ok(())
185    }
186}
187
188impl Display for AutoloadKind {
189    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
190        match self {
191            AutoloadKind::Itcm => write!(f, "ITCM"),
192            AutoloadKind::Dtcm => write!(f, "DTCM"),
193            AutoloadKind::Unknown(index) => write!(f, "Unknown({index})"),
194        }
195    }
196}