use alloc::boxed::Box;
use binrw::{binrw, BinRead, BinWrite};
#[binrw]
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[brw(little, magic = b"VNDRBOOT")]
#[br(assert(header_size == self.header_size()))]
pub struct VendorHeader {
#[br(temp)]
#[bw(calc = self.header_version())]
header_version: u32,
pub page_size: u32,
pub kernel_addr: u32,
pub ramdisk_addr: u32,
pub vendor_ramdisk_size: u32,
pub cmdline: Box<[u8; 2048]>,
pub tags_addr: u32,
pub board_name: [u8; 16],
#[br(temp)]
#[bw(calc = self.header_size())]
header_size: u32,
pub dtb_size: u32,
pub dtb_addr: u64,
#[br(if(header_version == 4))]
pub v4: Option<VendorHeaderV4>,
}
impl VendorHeader {
#[must_use]
pub const fn header_version(&self) -> u32 {
if self.v4.is_some() {
4
} else {
3
}
}
const fn header_size(&self) -> u32 {
if self.v4.is_some() {
2128
} else {
2112
}
}
const fn get_padding(&self, size: usize) -> usize {
let page_size = self.page_size as usize;
(page_size - (size % page_size)) % page_size
}
#[must_use]
pub const fn ramdisk_position(&self) -> usize {
self.header_size() as usize + self.get_padding(self.header_size() as usize)
}
#[must_use]
pub const fn dtb_position(&self) -> usize {
self.ramdisk_position()
+ self.vendor_ramdisk_size as usize
+ self.get_padding(self.vendor_ramdisk_size as usize)
}
#[must_use]
pub const fn ramdisk_table_position(&self) -> usize {
self.dtb_position() + self.dtb_size as usize + self.get_padding(self.dtb_size as usize)
}
#[must_use]
pub const fn bootconfig_position(&self) -> Option<usize> {
if let Some(v4) = &self.v4 {
Some(
self.ramdisk_table_position()
+ v4.vendor_ramdisk_table_size as usize
+ self.get_padding(v4.vendor_ramdisk_table_size as usize),
)
} else {
None
}
}
}
#[derive(BinRead, BinWrite, Clone, Debug, PartialEq, Eq, Hash)]
pub struct VendorHeaderV4 {
pub vendor_ramdisk_table_size: u32,
pub vendor_ramdisk_table_entry_num: u32,
pub vendor_ramdisk_table_entry_size: u32,
pub bootconfig_size: u32,
}
#[cfg(test)]
mod tests {
use alloc::vec::Vec;
use binrw::io::{Cursor, NoSeek};
use expect_test_bytes::expect_file;
use super::*;
#[test]
fn simple_write_read() {
fn pad_slice_to_array<const N: usize>(slice: &[u8]) -> [u8; N] {
let mut arr = [0u8; N];
let len = slice.len().min(N);
arr[..len].copy_from_slice(&slice[..len]);
arr
}
let expected_header = VendorHeader {
page_size: 0x7357_0001,
kernel_addr: 0x7357_0002,
ramdisk_addr: 0x7357_0003,
vendor_ramdisk_size: 0x7357_0004,
cmdline: Box::new(pad_slice_to_array(b"example")),
tags_addr: 0x7357_0005,
board_name: *b"example\0\0\0\0\0\0\0\0\0",
dtb_size: 0x7357_0006,
dtb_addr: 0x7357_7357_7357_0007,
v4: Some(VendorHeaderV4 {
vendor_ramdisk_table_size: 0x7357_0007,
vendor_ramdisk_table_entry_num: 0x7357_0008,
vendor_ramdisk_table_entry_size: 0x7357_0009,
bootconfig_size: 0x7357_000a,
}),
};
let mut actual_bytes = Vec::new();
expected_header
.write(&mut NoSeek::new(&mut actual_bytes))
.unwrap();
expect_file!["test_data/vendor/simple_write_read"].assert_eq(&actual_bytes);
let actual_header = VendorHeader::read(&mut Cursor::new(&actual_bytes)).unwrap();
assert_eq!(expected_header, actual_header);
let either_header = crate::EitherHeader::read(&mut Cursor::new(&actual_bytes)).unwrap();
assert_eq!(crate::EitherHeader::Vendor(expected_header), either_header);
}
}