Skip to main content

ds_rom/rom/raw/
multiboot_signature.rs

1use std::{backtrace::Backtrace, fmt::Display};
2
3use bytemuck::{Pod, PodCastError, Zeroable};
4use serde::{Deserialize, Serialize};
5use snafu::Snafu;
6
7use crate::{crypto::rsa::RsaSignature, rom::raw::RawHeaderError};
8
9/// Contains the RSA signature used to verify the integrity of the ROM header and the ARM9 and ARM7
10/// programs, after it is transferred for Download Play.
11#[repr(C)]
12#[derive(Zeroable, Pod, Clone, Copy, Serialize, Deserialize)]
13pub struct MultibootSignature {
14    magic: u32,
15    rsa_signature: RsaSignature,
16    key_seed: u32,
17}
18
19/// Magic number at the start of a multiboot signature.
20pub const MULTIBOOT_SIGNATURE_MAGIC: u32 = 0x00016361;
21
22/// Errors related to [`MultibootSignature`].
23#[derive(Debug, Snafu)]
24pub enum RawMultibootSignatureError {
25    /// See [`RawHeaderError`].
26    #[snafu(transparent)]
27    RawHeader {
28        /// Source error.
29        source: RawHeaderError,
30    },
31    /// Occurs when the input is too small to contain a [`MultibootSignature`].
32    #[snafu(display("expected {expected:#x} bytes for multiboot signature but had only {actual:#x}:\n{backtrace}"))]
33    DataTooSmall {
34        /// Expected size.
35        expected: usize,
36        /// Actual input size.
37        actual: usize,
38        /// Backtrace to the source of the error.
39        backtrace: Backtrace,
40    },
41    /// Occurs when the input is less aligned than [`MultibootSignature`].
42    #[snafu(display("expected {expected}-alignment but got {actual}-alignment:\n{backtrace}"))]
43    Misaligned {
44        /// Expected alignment.
45        expected: usize,
46        /// Actual alignment.
47        actual: usize,
48        /// Backtrace to the source of the error.
49        backtrace: Backtrace,
50    },
51    /// Occurs when the magic number does not match [`MULTIBOOT_SIGNATURE_MAGIC`].
52    #[snafu(display("expected magic number {expected:#010x} but got {actual:#010x}:\n{backtrace}"))]
53    InvalidMagic {
54        /// Expected magic number.
55        expected: u32,
56        /// Actual magic number.
57        actual: u32,
58        /// Backtrace to the source of the error.
59        backtrace: Backtrace,
60    },
61}
62
63impl MultibootSignature {
64    fn check_size(data: &'_ [u8]) -> Result<(), RawMultibootSignatureError> {
65        let size = size_of::<Self>();
66        if data.len() < size {
67            DataTooSmallSnafu { expected: size, actual: data.len() }.fail()
68        } else {
69            Ok(())
70        }
71    }
72
73    fn handle_pod_cast<T>(result: Result<T, PodCastError>, addr: usize) -> Result<T, RawMultibootSignatureError> {
74        match result {
75            Ok(build_info) => Ok(build_info),
76            Err(PodCastError::TargetAlignmentGreaterAndInputNotAligned) => {
77                MisalignedSnafu { expected: align_of::<Self>(), actual: 1usize << addr.trailing_zeros() }.fail()
78            }
79            Err(PodCastError::AlignmentMismatch) => panic!(),
80            Err(PodCastError::OutputSliceWouldHaveSlop) => panic!(),
81            Err(PodCastError::SizeMismatch) => unreachable!(),
82        }
83    }
84
85    /// Reinterprets a `&[u8]` as a reference to [`MultibootSignature`].
86    ///
87    /// # Errors
88    ///
89    /// This function will return an error if the input is too small or not aligned enough.
90    pub fn borrow_from_slice(data: &[u8]) -> Result<&Self, RawMultibootSignatureError> {
91        let size = size_of::<Self>();
92        Self::check_size(data)?;
93        let addr = data as *const [u8] as *const () as usize;
94        let multiboot_signature: &Self = Self::handle_pod_cast(bytemuck::try_from_bytes(&data[..size]), addr)?;
95        if multiboot_signature.magic != MULTIBOOT_SIGNATURE_MAGIC {
96            return InvalidMagicSnafu { expected: MULTIBOOT_SIGNATURE_MAGIC, actual: multiboot_signature.magic }.fail();
97        }
98        Ok(multiboot_signature)
99    }
100
101    /// Creates a [`DisplayMultibootSignature`] which implements [`Display`].
102    pub fn display(&self, indent: usize) -> DisplayMultibootSignature<'_> {
103        DisplayMultibootSignature { multiboot_signature: self, indent }
104    }
105
106    /// Returns the magic number of this [`MultibootSignature`]. This is always equal to [`MULTIBOOT_SIGNATURE_MAGIC`].
107    pub fn magic(&self) -> u32 {
108        self.magic
109    }
110
111    /// Returns the [`RsaSignature`] of this [`MultibootSignature`].
112    pub fn rsa_signature(&self) -> &RsaSignature {
113        &self.rsa_signature
114    }
115
116    /// Returns the RSA key seed of this [`MultibootSignature`].
117    pub fn key_seed(&self) -> u32 {
118        self.key_seed
119    }
120}
121
122/// Can be used to display values inside [`MultibootSignature`].
123pub struct DisplayMultibootSignature<'a> {
124    multiboot_signature: &'a MultibootSignature,
125    indent: usize,
126}
127
128impl Display for DisplayMultibootSignature<'_> {
129    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
130        let i = " ".repeat(self.indent);
131        let multiboot_signature = &self.multiboot_signature;
132        writeln!(f, "{i}Magic number ... : {:#010x}", multiboot_signature.magic)?;
133        writeln!(f, "{i}RSA key seed ... : {:#010x}", multiboot_signature.key_seed)?;
134        writeln!(f, "{i}RSA signature .. :\n{}", multiboot_signature.rsa_signature.display(self.indent + 2))?;
135        Ok(())
136    }
137}