#![allow(deprecated)]
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[non_exhaustive]
pub struct BootloaderConfig {
pub version: ApiVersion,
pub mappings: Mappings,
pub kernel_stack_size: u64,
#[deprecated(
since = "0.11.1",
note = "The frame buffer is now configured through the `BootConfig` struct when creating the bootable disk image"
)]
pub frame_buffer: FrameBuffer,
}
impl BootloaderConfig {
pub(crate) const UUID: [u8; 16] = [
0x74, 0x3C, 0xA9, 0x61, 0x09, 0x36, 0x46, 0xA0, 0xBB, 0x55, 0x5C, 0x15, 0x89, 0x15, 0x25,
0x3D,
];
#[doc(hidden)]
pub const SERIALIZED_LEN: usize = 124;
pub const fn new_default() -> Self {
Self {
kernel_stack_size: 80 * 1024,
version: ApiVersion::new_default(),
mappings: Mappings::new_default(),
frame_buffer: FrameBuffer::new_default(),
}
}
pub const fn serialize(&self) -> [u8; Self::SERIALIZED_LEN] {
let Self {
version,
mappings,
kernel_stack_size,
frame_buffer
} = self;
let ApiVersion {
version_major,
version_minor,
version_patch,
pre_release,
} = version;
let Mappings {
kernel_stack,
boot_info,
framebuffer,
physical_memory,
page_table_recursive,
aslr,
dynamic_range_start,
dynamic_range_end,
ramdisk_memory,
} = mappings;
let FrameBuffer {
minimum_framebuffer_height,
minimum_framebuffer_width,
} = frame_buffer;
let version = {
let one = concat_2_2(version_major.to_le_bytes(), version_minor.to_le_bytes());
let two = concat_2_1(version_patch.to_le_bytes(), [*pre_release as u8]);
concat_4_3(one, two)
};
let buf = concat_16_7(Self::UUID, version);
let buf = concat_23_8(buf, kernel_stack_size.to_le_bytes());
let buf = concat_31_9(buf, kernel_stack.serialize());
let buf = concat_40_9(buf, boot_info.serialize());
let buf = concat_49_9(buf, framebuffer.serialize());
let buf = concat_58_10(
buf,
match physical_memory {
Option::None => [0; 10],
Option::Some(m) => concat_1_9([1], m.serialize()),
},
);
let buf = concat_68_10(
buf,
match page_table_recursive {
Option::None => [0; 10],
Option::Some(m) => concat_1_9([1], m.serialize()),
},
);
let buf = concat_78_1(buf, [(*aslr) as u8]);
let buf = concat_79_9(
buf,
match dynamic_range_start {
Option::None => [0; 9],
Option::Some(addr) => concat_1_8([1], addr.to_le_bytes()),
},
);
let buf = concat_88_9(
buf,
match dynamic_range_end {
Option::None => [0; 9],
Option::Some(addr) => concat_1_8([1], addr.to_le_bytes()),
},
);
let buf = concat_97_9(buf, ramdisk_memory.serialize());
let buf = concat_106_9(
buf,
match minimum_framebuffer_height {
Option::None => [0; 9],
Option::Some(addr) => concat_1_8([1], addr.to_le_bytes()),
},
);
concat_115_9(
buf,
match minimum_framebuffer_width {
Option::None => [0; 9],
Option::Some(addr) => concat_1_8([1], addr.to_le_bytes()),
},
)
}
pub fn deserialize(serialized: &[u8]) -> Result<Self, &'static str> {
if serialized.len() != Self::SERIALIZED_LEN {
return Err("invalid len");
}
let s = serialized;
let (uuid, s) = split_array_ref(s);
if uuid != &Self::UUID {
return Err("invalid UUID");
}
let (version, s) = {
let (&major, s) = split_array_ref(s);
let (&minor, s) = split_array_ref(s);
let (&patch, s) = split_array_ref(s);
let (&pre, s) = split_array_ref(s);
let pre = match pre {
[0] => false,
[1] => true,
_ => return Err("invalid pre version"),
};
let version = ApiVersion {
version_major: u16::from_le_bytes(major),
version_minor: u16::from_le_bytes(minor),
version_patch: u16::from_le_bytes(patch),
pre_release: pre,
};
(version, s)
};
let (&kernel_stack_size, s) = split_array_ref(s);
let (mappings, s) = {
let (&kernel_stack, s) = split_array_ref(s);
let (&boot_info, s) = split_array_ref(s);
let (&framebuffer, s) = split_array_ref(s);
let (&physical_memory_some, s) = split_array_ref(s);
let (&physical_memory, s) = split_array_ref(s);
let (&page_table_recursive_some, s) = split_array_ref(s);
let (&page_table_recursive, s) = split_array_ref(s);
let (&[alsr], s) = split_array_ref(s);
let (&dynamic_range_start_some, s) = split_array_ref(s);
let (&dynamic_range_start, s) = split_array_ref(s);
let (&dynamic_range_end_some, s) = split_array_ref(s);
let (&dynamic_range_end, s) = split_array_ref(s);
let (&ramdisk_memory, s) = split_array_ref(s);
let mappings = Mappings {
kernel_stack: Mapping::deserialize(&kernel_stack)?,
boot_info: Mapping::deserialize(&boot_info)?,
framebuffer: Mapping::deserialize(&framebuffer)?,
physical_memory: match physical_memory_some {
[0] if physical_memory == [0; 9] => Option::None,
[1] => Option::Some(Mapping::deserialize(&physical_memory)?),
_ => return Err("invalid phys memory value"),
},
page_table_recursive: match page_table_recursive_some {
[0] if page_table_recursive == [0; 9] => Option::None,
[1] => Option::Some(Mapping::deserialize(&page_table_recursive)?),
_ => return Err("invalid page table recursive value"),
},
aslr: match alsr {
1 => true,
0 => false,
_ => return Err("invalid aslr value"),
},
dynamic_range_start: match dynamic_range_start_some {
[0] if dynamic_range_start == [0; 8] => Option::None,
[1] => Option::Some(u64::from_le_bytes(dynamic_range_start)),
_ => return Err("invalid dynamic range start value"),
},
dynamic_range_end: match dynamic_range_end_some {
[0] if dynamic_range_end == [0; 8] => Option::None,
[1] => Option::Some(u64::from_le_bytes(dynamic_range_end)),
_ => return Err("invalid dynamic range end value"),
},
ramdisk_memory: Mapping::deserialize(&ramdisk_memory)?,
};
(mappings, s)
};
let (frame_buffer, s) = {
let (&min_framebuffer_height_some, s) = split_array_ref(s);
let (&min_framebuffer_height, s) = split_array_ref(s);
let (&min_framebuffer_width_some, s) = split_array_ref(s);
let (&min_framebuffer_width, s) = split_array_ref(s);
let frame_buffer = FrameBuffer {
minimum_framebuffer_height: match min_framebuffer_height_some {
[0] if min_framebuffer_height == [0; 8] => Option::None,
[1] => Option::Some(u64::from_le_bytes(min_framebuffer_height)),
_ => return Err("minimum_framebuffer_height invalid"),
},
minimum_framebuffer_width: match min_framebuffer_width_some {
[0] if min_framebuffer_width == [0; 8] => Option::None,
[1] => Option::Some(u64::from_le_bytes(min_framebuffer_width)),
_ => return Err("minimum_framebuffer_width invalid"),
},
};
(frame_buffer, s)
};
if !s.is_empty() {
return Err("unexpected rest");
}
Ok(Self {
version,
kernel_stack_size: u64::from_le_bytes(kernel_stack_size),
mappings,
frame_buffer,
})
}
#[cfg(test)]
fn random() -> Self {
Self {
version: ApiVersion::random(),
mappings: Mappings::random(),
kernel_stack_size: rand::random(),
frame_buffer: FrameBuffer::random(),
}
}
}
impl Default for BootloaderConfig {
fn default() -> Self {
Self::new_default()
}
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[repr(C)]
pub struct ApiVersion {
version_major: u16,
version_minor: u16,
version_patch: u16,
pre_release: bool,
}
impl ApiVersion {
pub(crate) const fn new_default() -> Self {
Self {
version_major: version_info::VERSION_MAJOR,
version_minor: version_info::VERSION_MINOR,
version_patch: version_info::VERSION_PATCH,
pre_release: version_info::VERSION_PRE,
}
}
#[cfg(test)]
fn random() -> ApiVersion {
Self {
version_major: rand::random(),
version_minor: rand::random(),
version_patch: rand::random(),
pre_release: rand::random(),
}
}
pub fn version_major(&self) -> u16 {
self.version_major
}
pub fn version_minor(&self) -> u16 {
self.version_minor
}
pub fn version_patch(&self) -> u16 {
self.version_patch
}
pub fn pre_release(&self) -> bool {
self.pre_release
}
}
impl Default for ApiVersion {
fn default() -> Self {
Self::new_default()
}
}
#[derive(Debug, Default, PartialEq, Eq, Clone, Copy)]
#[non_exhaustive]
pub struct Mappings {
pub kernel_stack: Mapping,
pub boot_info: Mapping,
pub framebuffer: Mapping,
pub physical_memory: Option<Mapping>,
pub page_table_recursive: Option<Mapping>,
pub aslr: bool,
pub dynamic_range_start: Option<u64>,
pub dynamic_range_end: Option<u64>,
pub ramdisk_memory: Mapping,
}
impl Mappings {
pub const fn new_default() -> Self {
Self {
kernel_stack: Mapping::new_default(),
boot_info: Mapping::new_default(),
framebuffer: Mapping::new_default(),
physical_memory: Option::None,
page_table_recursive: Option::None,
aslr: false,
dynamic_range_start: None,
dynamic_range_end: None,
ramdisk_memory: Mapping::new_default(),
}
}
#[cfg(test)]
fn random() -> Mappings {
let phys = rand::random();
let recursive = rand::random();
Self {
kernel_stack: Mapping::random(),
boot_info: Mapping::random(),
framebuffer: Mapping::random(),
physical_memory: if phys {
Option::Some(Mapping::random())
} else {
Option::None
},
page_table_recursive: if recursive {
Option::Some(Mapping::random())
} else {
Option::None
},
aslr: rand::random(),
dynamic_range_start: if rand::random() {
Option::Some(rand::random())
} else {
Option::None
},
dynamic_range_end: if rand::random() {
Option::Some(rand::random())
} else {
Option::None
},
ramdisk_memory: Mapping::random(),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Mapping {
Dynamic,
FixedAddress(u64),
}
impl Mapping {
pub const fn new_default() -> Self {
Self::Dynamic
}
#[cfg(test)]
fn random() -> Mapping {
let fixed = rand::random();
if fixed {
Self::Dynamic
} else {
Self::FixedAddress(rand::random())
}
}
const fn serialize(&self) -> [u8; 9] {
match self {
Mapping::Dynamic => [0; 9],
Mapping::FixedAddress(addr) => concat_1_8([1], addr.to_le_bytes()),
}
}
fn deserialize(serialized: &[u8; 9]) -> Result<Self, &'static str> {
let (&variant, s) = split_array_ref(serialized);
let (&addr, s) = split_array_ref(s);
if !s.is_empty() {
return Err("invalid mapping format");
}
match variant {
[0] if addr == [0; 8] => Ok(Mapping::Dynamic),
[1] => Ok(Mapping::FixedAddress(u64::from_le_bytes(addr))),
_ => Err("invalid mapping value"),
}
}
}
impl Default for Mapping {
fn default() -> Self {
Self::new_default()
}
}
#[derive(Debug, Default, PartialEq, Eq, Clone, Copy)]
#[non_exhaustive]
pub struct FrameBuffer {
pub minimum_framebuffer_height: Option<u64>,
pub minimum_framebuffer_width: Option<u64>,
}
impl FrameBuffer {
pub const fn new_default() -> Self {
Self {
minimum_framebuffer_height: Option::None,
minimum_framebuffer_width: Option::None,
}
}
#[cfg(test)]
fn random() -> FrameBuffer {
Self {
minimum_framebuffer_height: if rand::random() {
Option::Some(rand::random())
} else {
Option::None
},
minimum_framebuffer_width: if rand::random() {
Option::Some(rand::random())
} else {
Option::None
},
}
}
}
fn split_array_ref<const N: usize, T>(slice: &[T]) -> (&[T; N], &[T]) {
let (a, b) = slice.split_at(N);
unsafe { (&*(a.as_ptr() as *const [T; N]), b) }
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn mapping_serde() {
for _ in 0..10000 {
let config = Mapping::random();
assert_eq!(Mapping::deserialize(&config.serialize()), Ok(config));
}
}
#[test]
fn config_serde() {
for _ in 0..10000 {
let config = BootloaderConfig::random();
assert_eq!(
BootloaderConfig::deserialize(&config.serialize()),
Ok(config)
);
}
}
}
use {
crate::{concat::*, version_info}
};