use crate::pi::hob::{
CPU, Capsule, Cpu, END_OF_HOB_LIST, FV, FV2, FV3, FirmwareVolume, FirmwareVolume2, FirmwareVolume3, GUID_EXTENSION,
GuidHob, HANDOFF, Hob, HobTrait, MEMORY_ALLOCATION, MemoryAllocation, MemoryAllocationModule,
PhaseHandoffInformationTable, RESOURCE_DESCRIPTOR, RESOURCE_DESCRIPTOR2, ResourceDescriptor, ResourceDescriptorV2,
UEFI_CAPSULE, header,
};
use core::{ffi::c_void, mem, slice};
use indoc::indoc;
use crate::base::{align_down, align_up};
use core::fmt;
use alloc::{boxed::Box, vec::Vec};
pub struct HobList<'a>(Vec<Hob<'a>>);
impl Default for HobList<'_> {
fn default() -> Self {
HobList::new()
}
}
impl<'a> HobList<'a> {
pub const fn new() -> Self {
HobList(Vec::new())
}
pub fn iter(&self) -> impl Iterator<Item = &Hob<'_>> {
self.0.iter()
}
pub fn as_mut_ptr<T>(&mut self) -> *mut T {
self.0.as_mut_ptr() as *mut T
}
pub fn size(&self) -> usize {
let mut size_of_hobs = 0;
for hob in self.iter() {
size_of_hobs += hob.size()
}
size_of_hobs
}
pub fn len(&self) -> usize {
self.0.len()
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub fn push(&mut self, hob: Hob<'a>) {
let cloned_hob = hob.clone();
self.0.push(cloned_hob);
}
pub fn discover_hobs(&mut self, hob_list: *const c_void) {
const NOT_NULL: &str = "Ptr should not be NULL";
fn assert_hob_size<T>(hob: &header::Hob) {
let hob_len = hob.length as usize;
let hob_size = mem::size_of::<T>();
assert_eq!(
hob_len, hob_size,
"Trying to cast hob of length {hob_len} into a pointer of size {hob_size}. Hob type: {:?}",
hob.r#type
);
}
let mut hob_header: *const header::Hob = hob_list as *const header::Hob;
loop {
let current_header = unsafe { hob_header.cast::<header::Hob>().as_ref().expect(NOT_NULL) };
match current_header.r#type {
HANDOFF => {
assert_hob_size::<PhaseHandoffInformationTable>(current_header);
let phit_hob =
unsafe { hob_header.cast::<PhaseHandoffInformationTable>().as_ref().expect(NOT_NULL) };
self.0.push(Hob::Handoff(phit_hob));
}
MEMORY_ALLOCATION => {
if current_header.length == mem::size_of::<MemoryAllocationModule>() as u16 {
let mem_alloc_hob =
unsafe { hob_header.cast::<MemoryAllocationModule>().as_ref().expect(NOT_NULL) };
self.0.push(Hob::MemoryAllocationModule(mem_alloc_hob));
} else {
assert_hob_size::<MemoryAllocation>(current_header);
let mem_alloc_hob = unsafe { hob_header.cast::<MemoryAllocation>().as_ref().expect(NOT_NULL) };
self.0.push(Hob::MemoryAllocation(mem_alloc_hob));
}
}
RESOURCE_DESCRIPTOR => {
assert_hob_size::<ResourceDescriptor>(current_header);
let resource_desc_hob =
unsafe { hob_header.cast::<ResourceDescriptor>().as_ref().expect(NOT_NULL) };
self.0.push(Hob::ResourceDescriptor(resource_desc_hob));
}
GUID_EXTENSION => {
let (guid_hob, data) = unsafe {
let hob = hob_header.cast::<GuidHob>().as_ref().expect(NOT_NULL);
let data_ptr = hob_header.byte_add(mem::size_of::<GuidHob>()) as *mut u8;
let data_len = hob.header.length as usize - mem::size_of::<GuidHob>();
(hob, slice::from_raw_parts(data_ptr, data_len))
};
self.0.push(Hob::GuidHob(guid_hob, data));
}
FV => {
assert_hob_size::<FirmwareVolume>(current_header);
let fv_hob = unsafe { hob_header.cast::<FirmwareVolume>().as_ref().expect(NOT_NULL) };
self.0.push(Hob::FirmwareVolume(fv_hob));
}
FV2 => {
assert_hob_size::<FirmwareVolume2>(current_header);
let fv2_hob = unsafe { hob_header.cast::<FirmwareVolume2>().as_ref().expect(NOT_NULL) };
self.0.push(Hob::FirmwareVolume2(fv2_hob));
}
FV3 => {
assert_hob_size::<FirmwareVolume3>(current_header);
let fv3_hob = unsafe { hob_header.cast::<FirmwareVolume3>().as_ref().expect(NOT_NULL) };
self.0.push(Hob::FirmwareVolume3(fv3_hob));
}
CPU => {
assert_hob_size::<Cpu>(current_header);
let cpu_hob = unsafe { hob_header.cast::<Cpu>().as_ref().expect(NOT_NULL) };
self.0.push(Hob::Cpu(cpu_hob));
}
UEFI_CAPSULE => {
assert_hob_size::<Capsule>(current_header);
let capsule_hob = unsafe { hob_header.cast::<Capsule>().as_ref().expect(NOT_NULL) };
self.0.push(Hob::Capsule(capsule_hob));
}
RESOURCE_DESCRIPTOR2 => {
assert_hob_size::<ResourceDescriptorV2>(current_header);
let resource_desc_hob =
unsafe { hob_header.cast::<ResourceDescriptorV2>().as_ref().expect(NOT_NULL) };
self.0.push(Hob::ResourceDescriptorV2(resource_desc_hob));
}
END_OF_HOB_LIST => {
break;
}
_ => {
self.0.push(Hob::Misc(current_header.r#type));
}
}
let next_hob = hob_header as usize + current_header.length as usize;
hob_header = next_hob as *const header::Hob;
}
}
pub fn relocate_hobs(&mut self) {
for hob in self.0.iter_mut() {
match hob {
Hob::Handoff(hob) => *hob = Box::leak(Box::new(PhaseHandoffInformationTable::clone(hob))),
Hob::MemoryAllocation(hob) => *hob = Box::leak(Box::new(MemoryAllocation::clone(hob))),
Hob::MemoryAllocationModule(hob) => *hob = Box::leak(Box::new(MemoryAllocationModule::clone(hob))),
Hob::Capsule(hob) => *hob = Box::leak(Box::new(Capsule::clone(hob))),
Hob::ResourceDescriptor(hob) => *hob = Box::leak(Box::new(ResourceDescriptor::clone(hob))),
Hob::GuidHob(hob, data) => {
*hob = Box::leak(Box::new(GuidHob::clone(hob)));
*data = Box::leak(data.to_vec().into_boxed_slice());
}
Hob::FirmwareVolume(hob) => *hob = Box::leak(Box::new(FirmwareVolume::clone(hob))),
Hob::FirmwareVolume2(hob) => *hob = Box::leak(Box::new(FirmwareVolume2::clone(hob))),
Hob::FirmwareVolume3(hob) => *hob = Box::leak(Box::new(FirmwareVolume3::clone(hob))),
Hob::Cpu(hob) => *hob = Box::leak(Box::new(Cpu::clone(hob))),
Hob::ResourceDescriptorV2(hob) => *hob = Box::leak(Box::new(ResourceDescriptorV2::clone(hob))),
Hob::Misc(_) => (), };
}
}
}
impl<'a> IntoIterator for HobList<'a> {
type Item = Hob<'a>;
type IntoIter = <Vec<Hob<'a>> as IntoIterator>::IntoIter;
fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
}
}
impl<'a> IntoIterator for &'a HobList<'a> {
type Item = &'a Hob<'a>;
type IntoIter = core::slice::Iter<'a, Hob<'a>>;
fn into_iter(self) -> Self::IntoIter {
self.0.iter()
}
}
impl fmt::Debug for HobList<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for hob in self.0.clone().into_iter() {
match hob {
Hob::Handoff(hob) => {
write!(
f,
indoc! {"
PHASE HANDOFF INFORMATION TABLE (PHIT) HOB
HOB Length: 0x{:x}
Version: 0x{:x}
Boot Mode: {}
Memory Bottom: 0x{:x}
Memory Top: 0x{:x}
Free Memory Bottom: 0x{:x}
Free Memory Top: 0x{:x}
End of HOB List: 0x{:x}\n"},
hob.header.length,
hob.version,
hob.boot_mode,
align_up(hob.memory_bottom, 0x1000).unwrap_or(0),
align_down(hob.memory_top, 0x1000).unwrap_or(0),
align_up(hob.free_memory_bottom, 0x1000).unwrap_or(0),
align_down(hob.free_memory_top, 0x1000).unwrap_or(0),
hob.end_of_hob_list
)?;
}
Hob::MemoryAllocation(hob) => {
write!(
f,
indoc! {"
MEMORY ALLOCATION HOB
HOB Length: 0x{:x}
Memory Base Address: 0x{:x}
Memory Length: 0x{:x}
Memory Type: {:?}\n"},
hob.header.length,
hob.alloc_descriptor.memory_base_address,
hob.alloc_descriptor.memory_length,
hob.alloc_descriptor.memory_type
)?;
}
Hob::ResourceDescriptor(hob) => {
write!(
f,
indoc! {"
RESOURCE DESCRIPTOR HOB
HOB Length: 0x{:x}
Resource Type: 0x{:x}
Resource Attribute Type: 0x{:x}
Resource Start Address: 0x{:x}
Resource Length: 0x{:x}\n"},
hob.header.length,
hob.resource_type,
hob.resource_attribute,
hob.physical_start,
hob.resource_length
)?;
}
Hob::GuidHob(hob, _data) => {
let (f0, f1, f2, f3, f4, &[f5, f6, f7, f8, f9, f10]) = hob.name.as_fields();
write!(
f,
indoc! {"
GUID HOB
Type: {:#x}
Length: {:#x},
GUID: {{{:08x}-{:04x}-{:04x}-{:02x}{:02x}-{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}}}\n"},
hob.header.r#type, hob.header.length, f0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10,
)?;
}
Hob::FirmwareVolume(hob) => {
write!(
f,
indoc! {"
FIRMWARE VOLUME (FV) HOB
HOB Length: 0x{:x}
Base Address: 0x{:x}
Length: 0x{:x}\n"},
hob.header.length, hob.base_address, hob.length
)?;
}
Hob::FirmwareVolume2(hob) => {
write!(
f,
indoc! {"
FIRMWARE VOLUME 2 (FV2) HOB
Base Address: 0x{:x}
Length: 0x{:x}\n"},
hob.base_address, hob.length
)?;
}
Hob::FirmwareVolume3(hob) => {
write!(
f,
indoc! {"
FIRMWARE VOLUME 3 (FV3) HOB
Base Address: 0x{:x}
Length: 0x{:x}\n"},
hob.base_address, hob.length
)?;
}
Hob::Cpu(hob) => {
write!(
f,
indoc! {"
CPU HOB
Memory Space Size: 0x{:x}
IO Space Size: 0x{:x}\n"},
hob.size_of_memory_space, hob.size_of_io_space
)?;
}
Hob::Capsule(hob) => {
write!(
f,
indoc! {"
CAPSULE HOB
Base Address: 0x{:x}
Length: 0x{:x}\n"},
hob.base_address, hob.length
)?;
}
Hob::ResourceDescriptorV2(hob) => {
write!(
f,
indoc! {"
RESOURCE DESCRIPTOR 2 HOB
HOB Length: 0x{:x}
Resource Type: 0x{:x}
Resource Attribute Type: 0x{:x}
Resource Start Address: 0x{:x}
Resource Length: 0x{:x}
Attributes: 0x{:x}\n"},
hob.v1.header.length,
hob.v1.resource_type,
hob.v1.resource_attribute,
hob.v1.physical_start,
hob.v1.resource_length,
hob.attributes
)?;
}
_ => (),
}
}
write!(f, "Parsed HOBs")
}
}
#[cfg(test)]
mod tests {
use crate::pi::{
hob,
hob::{
Capsule, Cpu, FirmwareVolume, Hob, HobTrait, MemoryAllocation, PhaseHandoffInformationTable,
ResourceDescriptor, get_pi_hob_list_size,
hob_list::HobList,
tests::{
gen_capsule, gen_cpu, gen_end_of_hoblist, gen_firmware_volume, gen_firmware_volume2,
gen_firmware_volume3, gen_guid_hob, gen_memory_allocation, gen_memory_allocation_module,
gen_phase_handoff_information_table, gen_resource_descriptor, gen_resource_descriptor_v2,
guid_hob_refs,
},
},
};
use core::{
ffi::c_void,
mem::{drop, forget, size_of},
ptr,
slice::from_raw_parts,
};
use std::vec::Vec;
pub fn to_c_array(hob_list: &HobList) -> (*const c_void, usize) {
let size = hob_list.size();
let mut c_array: Vec<u8> = Vec::with_capacity(size);
for hob in hob_list.iter() {
let slice = unsafe { from_raw_parts(hob.as_ptr(), hob.size()) };
c_array.extend_from_slice(slice);
}
let void_ptr = c_array.as_ptr() as *const c_void;
forget(c_array);
(void_ptr, size)
}
pub fn manually_free_c_array(c_array_ptr: *const c_void, len: usize) {
let ptr = c_array_ptr as *mut u8;
unsafe {
drop(Vec::from_raw_parts(ptr, len, len));
}
}
#[test]
fn test_hoblist_empty() {
let hoblist = HobList::new();
assert_eq!(hoblist.len(), 0);
assert!(hoblist.is_empty());
}
#[test]
fn test_hoblist_push() {
let mut hoblist = HobList::new();
let resource = gen_resource_descriptor();
hoblist.push(Hob::ResourceDescriptor(&resource));
assert_eq!(hoblist.len(), 1);
let firmware_volume = gen_firmware_volume();
hoblist.push(Hob::FirmwareVolume(&firmware_volume));
assert_eq!(hoblist.len(), 2);
let resource_v2 = gen_resource_descriptor_v2();
hoblist.push(Hob::ResourceDescriptorV2(&resource_v2));
assert_eq!(hoblist.len(), 3);
}
#[test]
fn test_hoblist_iterate() {
let mut hoblist = HobList::default();
let resource = gen_resource_descriptor();
let firmware_volume = gen_firmware_volume();
let firmware_volume2 = gen_firmware_volume2();
let firmware_volume3 = gen_firmware_volume3();
let end_of_hob_list = gen_end_of_hoblist();
let capsule = gen_capsule();
let guid_hob_buf = gen_guid_hob();
let (guid_hob, guid_hob_data) = guid_hob_refs(&guid_hob_buf);
let memory_allocation = gen_memory_allocation();
let memory_allocation_module = gen_memory_allocation_module();
hoblist.push(Hob::ResourceDescriptor(&resource));
hoblist.push(Hob::FirmwareVolume(&firmware_volume));
hoblist.push(Hob::FirmwareVolume2(&firmware_volume2));
hoblist.push(Hob::FirmwareVolume3(&firmware_volume3));
hoblist.push(Hob::Capsule(&capsule));
hoblist.push(Hob::GuidHob(guid_hob, guid_hob_data));
hoblist.push(Hob::MemoryAllocation(&memory_allocation));
hoblist.push(Hob::MemoryAllocationModule(&memory_allocation_module));
hoblist.push(Hob::Handoff(&end_of_hob_list));
let mut count = 0;
hoblist.iter().for_each(|hob| {
match hob {
Hob::ResourceDescriptor(resource) => {
assert_eq!(resource.resource_type, hob::EFI_RESOURCE_SYSTEM_MEMORY);
}
Hob::MemoryAllocation(memory_allocation) => {
assert_eq!(memory_allocation.alloc_descriptor.memory_length, 0x0123456789abcdef);
}
Hob::MemoryAllocationModule(memory_allocation_module) => {
assert_eq!(memory_allocation_module.alloc_descriptor.memory_length, 0x0123456789abcdef);
}
Hob::Capsule(capsule) => {
assert_eq!(capsule.base_address, 0);
}
Hob::GuidHob(guid_hob, data) => {
assert_eq!(guid_hob.name, r_efi::efi::Guid::from_fields(1, 2, 3, 4, 5, &[6, 7, 8, 9, 10, 11]));
assert_eq!(*data, &[1_u8, 2, 3, 4, 5, 6, 7, 8]);
}
Hob::FirmwareVolume(firmware_volume) => {
assert_eq!(firmware_volume.length, 0x0123456789abcdef);
}
Hob::FirmwareVolume2(firmware_volume) => {
assert_eq!(firmware_volume.length, 0x0123456789abcdef);
}
Hob::FirmwareVolume3(firmware_volume) => {
assert_eq!(firmware_volume.length, 0x0123456789abcdef);
}
Hob::Handoff(handoff) => {
assert_eq!(handoff.memory_top, 0xdeadbeef);
}
_ => {
panic!("Unexpected hob type");
}
}
count += 1;
});
assert_eq!(count, 9);
}
#[test]
fn test_hoblist_discover() {
let resource = gen_resource_descriptor();
let handoff = gen_phase_handoff_information_table();
let firmware_volume = gen_firmware_volume();
let firmware_volume2 = gen_firmware_volume2();
let firmware_volume3 = gen_firmware_volume3();
let capsule = gen_capsule();
let guid_hob_buf = gen_guid_hob();
let (guid_hob, guid_hob_data) = guid_hob_refs(&guid_hob_buf);
let memory_allocation = gen_memory_allocation();
let memory_allocation_module = gen_memory_allocation_module();
let cpu = gen_cpu();
let resource_v2 = gen_resource_descriptor_v2();
let end_of_hob_list = gen_end_of_hoblist();
let mut hoblist = HobList::new();
hoblist.push(Hob::ResourceDescriptor(&resource));
hoblist.push(Hob::Handoff(&handoff));
hoblist.push(Hob::FirmwareVolume(&firmware_volume));
hoblist.push(Hob::FirmwareVolume2(&firmware_volume2));
hoblist.push(Hob::FirmwareVolume3(&firmware_volume3));
hoblist.push(Hob::Capsule(&capsule));
hoblist.push(Hob::GuidHob(guid_hob, guid_hob_data));
hoblist.push(Hob::MemoryAllocation(&memory_allocation));
hoblist.push(Hob::MemoryAllocationModule(&memory_allocation_module));
hoblist.push(Hob::Cpu(&cpu));
hoblist.push(Hob::ResourceDescriptorV2(&resource_v2));
hoblist.push(Hob::Handoff(&end_of_hob_list));
let mut count = 0;
hoblist.iter().for_each(|hob| {
match hob {
Hob::ResourceDescriptor(resource) => {
assert_eq!(resource.resource_type, hob::EFI_RESOURCE_SYSTEM_MEMORY);
}
Hob::MemoryAllocation(memory_allocation) => {
assert_eq!(memory_allocation.alloc_descriptor.memory_length, 0x0123456789abcdef);
}
Hob::MemoryAllocationModule(memory_allocation_module) => {
assert_eq!(memory_allocation_module.alloc_descriptor.memory_length, 0x0123456789abcdef);
}
Hob::Capsule(capsule) => {
assert_eq!(capsule.base_address, 0);
}
Hob::GuidHob(guid_hob, data) => {
assert_eq!(guid_hob.name, r_efi::efi::Guid::from_fields(1, 2, 3, 4, 5, &[6, 7, 8, 9, 10, 11]));
assert_eq!(&data[..], guid_hob_data);
}
Hob::FirmwareVolume(firmware_volume) => {
assert_eq!(firmware_volume.length, 0x0123456789abcdef);
}
Hob::FirmwareVolume2(firmware_volume) => {
assert_eq!(firmware_volume.length, 0x0123456789abcdef);
}
Hob::FirmwareVolume3(firmware_volume) => {
assert_eq!(firmware_volume.length, 0x0123456789abcdef);
}
Hob::Handoff(handoff) => {
assert_eq!(handoff.memory_top, 0xdeadbeef);
}
Hob::Cpu(cpu) => {
assert_eq!(cpu.size_of_memory_space, 0);
}
Hob::ResourceDescriptorV2(resource) => {
assert_eq!(resource.v1.header.r#type, hob::RESOURCE_DESCRIPTOR2);
assert_eq!(resource.v1.resource_type, hob::EFI_RESOURCE_SYSTEM_MEMORY);
}
_ => {
panic!("Unexpected hob type");
}
}
count += 1;
});
assert_eq!(count, 12);
let (c_array_hoblist, length) = to_c_array(&hoblist);
let mut cloned_hoblist = HobList::new();
cloned_hoblist.discover_hobs(c_array_hoblist);
count = 0;
hoblist.into_iter().for_each(|hob| {
match hob {
Hob::ResourceDescriptor(resource) => {
assert_eq!(resource.resource_type, hob::EFI_RESOURCE_SYSTEM_MEMORY);
}
Hob::MemoryAllocation(memory_allocation) => {
assert_eq!(memory_allocation.alloc_descriptor.memory_length, 0x0123456789abcdef);
}
Hob::MemoryAllocationModule(memory_allocation_module) => {
assert_eq!(memory_allocation_module.alloc_descriptor.memory_length, 0x0123456789abcdef);
}
Hob::Capsule(capsule) => {
assert_eq!(capsule.base_address, 0);
}
Hob::GuidHob(guid_hob, data) => {
assert_eq!(guid_hob.name, r_efi::efi::Guid::from_fields(1, 2, 3, 4, 5, &[6, 7, 8, 9, 10, 11]));
assert_eq!(data, &[1_u8, 2, 3, 4, 5, 6, 7, 8]);
}
Hob::FirmwareVolume(firmware_volume) => {
assert_eq!(firmware_volume.length, 0x0123456789abcdef);
}
Hob::FirmwareVolume2(firmware_volume) => {
assert_eq!(firmware_volume.length, 0x0123456789abcdef);
}
Hob::FirmwareVolume3(firmware_volume) => {
assert_eq!(firmware_volume.length, 0x0123456789abcdef);
}
Hob::Handoff(handoff) => {
assert_eq!(handoff.memory_top, 0xdeadbeef);
}
Hob::ResourceDescriptorV2(resource) => {
assert_eq!(resource.v1.header.r#type, hob::RESOURCE_DESCRIPTOR2);
assert_eq!(resource.v1.resource_type, hob::EFI_RESOURCE_SYSTEM_MEMORY);
}
Hob::Cpu(cpu) => {
assert_eq!(cpu.size_of_memory_space, 0);
}
_ => {
panic!("Unexpected hob type");
}
}
count += 1;
});
assert_eq!(count, 12);
manually_free_c_array(c_array_hoblist, length);
}
#[test]
fn test_hob_iterator() {
let resource = gen_resource_descriptor();
let handoff = gen_phase_handoff_information_table();
let firmware_volume = gen_firmware_volume();
let firmware_volume2 = gen_firmware_volume2();
let firmware_volume3 = gen_firmware_volume3();
let capsule = gen_capsule();
let guid_hob_buf = gen_guid_hob();
let (guid_hob, guid_hob_data) = guid_hob_refs(&guid_hob_buf);
let memory_allocation = gen_memory_allocation();
let memory_allocation_module = gen_memory_allocation_module();
let cpu = gen_cpu();
let end_of_hob_list = gen_end_of_hoblist();
let mut hoblist = HobList::new();
hoblist.push(Hob::ResourceDescriptor(&resource));
hoblist.push(Hob::Handoff(&handoff));
hoblist.push(Hob::FirmwareVolume(&firmware_volume));
hoblist.push(Hob::FirmwareVolume2(&firmware_volume2));
hoblist.push(Hob::FirmwareVolume3(&firmware_volume3));
hoblist.push(Hob::Capsule(&capsule));
hoblist.push(Hob::GuidHob(guid_hob, guid_hob_data));
hoblist.push(Hob::MemoryAllocation(&memory_allocation));
hoblist.push(Hob::MemoryAllocationModule(&memory_allocation_module));
hoblist.push(Hob::Cpu(&cpu));
hoblist.push(Hob::Handoff(&end_of_hob_list));
let (c_array_hoblist, length) = to_c_array(&hoblist);
let hob = Hob::ResourceDescriptor(unsafe {
(c_array_hoblist as *const hob::ResourceDescriptor).as_ref::<'static>().unwrap()
});
for h in &hob {
println!("{:?}", h.header());
}
manually_free_c_array(c_array_hoblist, length);
}
#[test]
fn test_hob_iterator2() {
let resource = gen_resource_descriptor();
let handoff = gen_phase_handoff_information_table();
let firmware_volume = gen_firmware_volume();
let firmware_volume2 = gen_firmware_volume2();
let firmware_volume3 = gen_firmware_volume3();
let capsule = gen_capsule();
let guid_hob_buf = gen_guid_hob();
let (guid_hob, guid_hob_data) = guid_hob_refs(&guid_hob_buf);
let memory_allocation = gen_memory_allocation();
let memory_allocation_module = gen_memory_allocation_module();
let cpu = gen_cpu();
let resource_v2 = gen_resource_descriptor_v2();
let end_of_hob_list = gen_end_of_hoblist();
let mut hoblist = HobList::new();
hoblist.push(Hob::ResourceDescriptor(&resource));
hoblist.push(Hob::Handoff(&handoff));
hoblist.push(Hob::FirmwareVolume(&firmware_volume));
hoblist.push(Hob::FirmwareVolume2(&firmware_volume2));
hoblist.push(Hob::FirmwareVolume3(&firmware_volume3));
hoblist.push(Hob::Capsule(&capsule));
hoblist.push(Hob::GuidHob(guid_hob, guid_hob_data));
hoblist.push(Hob::MemoryAllocation(&memory_allocation));
hoblist.push(Hob::MemoryAllocationModule(&memory_allocation_module));
hoblist.push(Hob::Cpu(&cpu));
hoblist.push(Hob::ResourceDescriptorV2(&resource_v2));
hoblist.push(Hob::Handoff(&end_of_hob_list));
for hob in &hoblist {
println!("{:?}", hob.header());
}
for hob in hoblist {
println!("{:?}", hob.header());
}
}
#[test]
fn test_relocate_hobs() {
let resource = gen_resource_descriptor();
let handoff = gen_phase_handoff_information_table();
let firmware_volume = gen_firmware_volume();
let firmware_volume2 = gen_firmware_volume2();
let firmware_volume3 = gen_firmware_volume3();
let capsule = gen_capsule();
let guid_hob_buf = gen_guid_hob();
let (guid_hob, guid_hob_data) = guid_hob_refs(&guid_hob_buf);
let memory_allocation = gen_memory_allocation();
let memory_allocation_module = gen_memory_allocation_module();
let cpu = gen_cpu();
let resource_v2 = gen_resource_descriptor_v2();
let end_of_hob_list = gen_end_of_hoblist();
let mut hoblist = HobList::new();
hoblist.push(Hob::ResourceDescriptor(&resource));
hoblist.push(Hob::Handoff(&handoff));
hoblist.push(Hob::FirmwareVolume(&firmware_volume));
hoblist.push(Hob::FirmwareVolume2(&firmware_volume2));
hoblist.push(Hob::FirmwareVolume3(&firmware_volume3));
hoblist.push(Hob::Capsule(&capsule));
hoblist.push(Hob::GuidHob(guid_hob, guid_hob_data));
hoblist.push(Hob::MemoryAllocation(&memory_allocation));
hoblist.push(Hob::MemoryAllocationModule(&memory_allocation_module));
hoblist.push(Hob::Cpu(&cpu));
hoblist.push(Hob::Misc(12345));
hoblist.push(Hob::ResourceDescriptorV2(&resource_v2));
hoblist.push(Hob::Handoff(&end_of_hob_list));
let hoblist_address = hoblist.as_mut_ptr::<()>() as usize;
let hoblist_len = hoblist.len();
hoblist.relocate_hobs();
assert_eq!(
hoblist_address,
hoblist.as_mut_ptr::<()>() as usize,
"Only hobs need to be relocated, not the vector."
);
assert_eq!(hoblist_len, hoblist.len());
for (i, hob) in hoblist.into_iter().enumerate() {
match hob {
Hob::ResourceDescriptor(hob) if i == 0 => {
assert_ne!(ptr::addr_of!(resource), hob);
assert_eq!(resource, *hob);
}
Hob::Handoff(hob) if i == 1 => {
assert_ne!(ptr::addr_of!(handoff), hob);
assert_eq!(handoff, *hob);
}
Hob::FirmwareVolume(hob) if i == 2 => {
assert_ne!(ptr::addr_of!(firmware_volume), hob);
assert_eq!(firmware_volume, *hob);
}
Hob::FirmwareVolume2(hob) if i == 3 => {
assert_ne!(ptr::addr_of!(firmware_volume2), hob);
assert_eq!(firmware_volume2, *hob);
}
Hob::FirmwareVolume3(hob) if i == 4 => {
assert_ne!(ptr::addr_of!(firmware_volume3), hob);
assert_eq!(firmware_volume3, *hob);
}
Hob::Capsule(hob) if i == 5 => {
assert_ne!(ptr::addr_of!(capsule), hob);
assert_eq!(capsule, *hob);
}
Hob::GuidHob(hob, hob_data) if i == 6 => {
assert_ne!(ptr::from_ref(guid_hob), ptr::from_ref(hob));
assert_ne!(guid_hob_data.as_ptr(), hob_data.as_ptr());
assert_eq!(guid_hob.header, hob.header);
assert_eq!(guid_hob.name, hob.name);
assert_eq!(guid_hob_data, hob_data);
}
Hob::MemoryAllocation(hob) if i == 7 => {
assert_ne!(ptr::addr_of!(memory_allocation), hob);
assert_eq!(memory_allocation.header, hob.header);
assert_eq!(memory_allocation.alloc_descriptor, hob.alloc_descriptor);
}
Hob::MemoryAllocationModule(hob) if i == 8 => {
assert_ne!(ptr::addr_of!(memory_allocation_module), hob);
assert_eq!(memory_allocation_module, *hob);
}
Hob::Cpu(hob) if i == 9 => {
assert_ne!(ptr::addr_of!(cpu), hob);
assert_eq!(cpu, *hob);
}
Hob::Misc(hob) if i == 10 => {
assert_eq!(12345, hob);
}
Hob::ResourceDescriptorV2(hob) if i == 11 => {
assert_ne!(ptr::addr_of!(resource_v2), hob);
assert_eq!(resource_v2, *hob);
}
Hob::Handoff(hob) if i == 12 => {
assert_ne!(ptr::addr_of!(end_of_hob_list), hob);
assert_eq!(end_of_hob_list, *hob);
}
_ => panic!("Hob at index: {i}."),
}
}
}
#[test]
fn test_hoblist_debug_display() {
use alloc::format;
let mut hoblist = HobList::new();
let handoff = gen_phase_handoff_information_table();
hoblist.push(Hob::Handoff(&handoff));
let debug_output = format!("{:?}", hoblist);
assert!(debug_output.contains("PHASE HANDOFF INFORMATION TABLE"));
assert!(debug_output.contains("HOB Length:"));
assert!(debug_output.contains("Version:"));
assert!(debug_output.contains("Boot Mode:"));
assert!(debug_output.contains("Memory Bottom:"));
assert!(debug_output.contains("Memory Top:"));
assert!(debug_output.contains("Free Memory Bottom:"));
assert!(debug_output.contains("Free Memory Top:"));
assert!(debug_output.contains("End of HOB List:"));
}
#[test]
fn test_get_pi_hob_list_size_single_hob() {
use core::ffi::c_void;
let end_of_list = gen_end_of_hoblist();
let size = unsafe { get_pi_hob_list_size(&end_of_list as *const _ as *const c_void) };
assert_eq!(size, size_of::<PhaseHandoffInformationTable>());
}
#[test]
fn test_get_pi_hob_list_size_multiple_hobs() {
use core::ffi::c_void;
let capsule = gen_capsule();
let firmware_volume = gen_firmware_volume();
let end_of_list = gen_end_of_hoblist();
let expected_size =
size_of::<Capsule>() + size_of::<FirmwareVolume>() + size_of::<PhaseHandoffInformationTable>();
let mut buffer = Vec::new();
let capsule_bytes =
unsafe { core::slice::from_raw_parts(&capsule as *const Capsule as *const u8, size_of::<Capsule>()) };
buffer.extend_from_slice(capsule_bytes);
let fv_bytes = unsafe {
core::slice::from_raw_parts(
&firmware_volume as *const FirmwareVolume as *const u8,
size_of::<FirmwareVolume>(),
)
};
buffer.extend_from_slice(fv_bytes);
let end_bytes = unsafe {
core::slice::from_raw_parts(
&end_of_list as *const PhaseHandoffInformationTable as *const u8,
size_of::<PhaseHandoffInformationTable>(),
)
};
buffer.extend_from_slice(end_bytes);
let size = unsafe { get_pi_hob_list_size(buffer.as_ptr() as *const c_void) };
assert_eq!(size, expected_size);
}
#[test]
fn test_get_pi_hob_list_size_varied_hob_types() {
use core::ffi::c_void;
let cpu = gen_cpu();
let resource = gen_resource_descriptor();
let memory_alloc = gen_memory_allocation();
let end_of_list = gen_end_of_hoblist();
let expected_size = size_of::<Cpu>()
+ size_of::<ResourceDescriptor>()
+ size_of::<MemoryAllocation>()
+ size_of::<PhaseHandoffInformationTable>();
let mut buffer = Vec::new();
buffer.extend_from_slice(unsafe {
core::slice::from_raw_parts(&cpu as *const Cpu as *const u8, size_of::<Cpu>())
});
buffer.extend_from_slice(unsafe {
core::slice::from_raw_parts(
&resource as *const ResourceDescriptor as *const u8,
size_of::<ResourceDescriptor>(),
)
});
buffer.extend_from_slice(unsafe {
core::slice::from_raw_parts(
&memory_alloc as *const MemoryAllocation as *const u8,
size_of::<MemoryAllocation>(),
)
});
buffer.extend_from_slice(unsafe {
core::slice::from_raw_parts(
&end_of_list as *const PhaseHandoffInformationTable as *const u8,
size_of::<PhaseHandoffInformationTable>(),
)
});
let size = unsafe { get_pi_hob_list_size(buffer.as_ptr() as *const c_void) };
assert_eq!(size, expected_size);
}
}