use std::{backtrace::Backtrace, fmt::Display};
use bytemuck::{Pod, PodCastError, Zeroable};
use serde::{Deserialize, Serialize};
use snafu::Snafu;
use crate::{crypto::rsa::RsaSignature, rom::raw::RawHeaderError};
#[repr(C)]
#[derive(Zeroable, Pod, Clone, Copy, Serialize, Deserialize)]
pub struct MultibootSignature {
magic: u32,
rsa_signature: RsaSignature,
key_seed: u32,
}
pub const MULTIBOOT_SIGNATURE_MAGIC: u32 = 0x00016361;
#[derive(Debug, Snafu)]
pub enum RawMultibootSignatureError {
#[snafu(transparent)]
RawHeader {
source: RawHeaderError,
},
#[snafu(display("expected {expected:#x} bytes for multiboot signature but had only {actual:#x}:\n{backtrace}"))]
DataTooSmall {
expected: usize,
actual: usize,
backtrace: Backtrace,
},
#[snafu(display("expected {expected}-alignment but got {actual}-alignment:\n{backtrace}"))]
Misaligned {
expected: usize,
actual: usize,
backtrace: Backtrace,
},
#[snafu(display("expected magic number {expected:#010x} but got {actual:#010x}:\n{backtrace}"))]
InvalidMagic {
expected: u32,
actual: u32,
backtrace: Backtrace,
},
}
impl MultibootSignature {
fn check_size(data: &'_ [u8]) -> Result<(), RawMultibootSignatureError> {
let size = size_of::<Self>();
if data.len() < size {
DataTooSmallSnafu { expected: size, actual: data.len() }.fail()
} else {
Ok(())
}
}
fn handle_pod_cast<T>(result: Result<T, PodCastError>, addr: usize) -> Result<T, RawMultibootSignatureError> {
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, RawMultibootSignatureError> {
let size = size_of::<Self>();
Self::check_size(data)?;
let addr = data as *const [u8] as *const () as usize;
let multiboot_signature: &Self = Self::handle_pod_cast(bytemuck::try_from_bytes(&data[..size]), addr)?;
if multiboot_signature.magic != MULTIBOOT_SIGNATURE_MAGIC {
return InvalidMagicSnafu { expected: MULTIBOOT_SIGNATURE_MAGIC, actual: multiboot_signature.magic }.fail();
}
Ok(multiboot_signature)
}
pub fn display(&self, indent: usize) -> DisplayMultibootSignature<'_> {
DisplayMultibootSignature { multiboot_signature: self, indent }
}
pub fn magic(&self) -> u32 {
self.magic
}
pub fn rsa_signature(&self) -> &RsaSignature {
&self.rsa_signature
}
pub fn key_seed(&self) -> u32 {
self.key_seed
}
}
pub struct DisplayMultibootSignature<'a> {
multiboot_signature: &'a MultibootSignature,
indent: usize,
}
impl Display for DisplayMultibootSignature<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let i = " ".repeat(self.indent);
let multiboot_signature = &self.multiboot_signature;
writeln!(f, "{i}Magic number ... : {:#010x}", multiboot_signature.magic)?;
writeln!(f, "{i}RSA key seed ... : {:#010x}", multiboot_signature.key_seed)?;
writeln!(f, "{i}RSA signature .. :\n{}", multiboot_signature.rsa_signature.display(self.indent + 2))?;
Ok(())
}
}