Skip to content

Commit f115369

Browse files
committed
patina: Add Memory Type Information HOB serialization to the SDK
Adds a `HobSerDe::MemoryTypeInformation` variant so that GUID extension HOBs matching `MEMORY_TYPE_INFO_HOB_GUID` are serialized with their parsed payload instead of being collapsed into a generic `GuidExtension` entry. Signed-off-by: Michael Kubacki <michael.kubacki@microsoft.com>
1 parent 3715223 commit f115369

1 file changed

Lines changed: 87 additions & 3 deletions

File tree

sdk/patina/src/pi/serializable/serializable_hob.rs

Lines changed: 87 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,10 @@
1414
use core::cmp::Ordering;
1515

1616
use crate::pi::{
17-
hob::Hob,
17+
hob::{Hob, MEMORY_TYPE_INFO_HOB_GUID},
1818
serializable::{Interval, format_guid, hex_format},
1919
};
20-
use alloc::string::String;
20+
use alloc::{string::String, vec::Vec};
2121
use serde::{Deserialize, Serialize};
2222

2323
/// Serializable representation of the different HOB types.
@@ -50,6 +50,14 @@ pub enum HobSerDe {
5050
GuidExtension {
5151
name: String,
5252
},
53+
/// Memory Type Information GUID Extension HOB (Name == `MEMORY_TYPE_INFO_HOB_GUID`).
54+
///
55+
/// The HOB's data payload is parsed into an array of
56+
/// `(memory_type, number_of_pages)` entries up to (but not including)
57+
/// the sentinel entry whose `memory_type` is `>= EFI_MAX_MEMORY_TYPE`.
58+
MemoryTypeInformation {
59+
entries: Vec<MemoryTypeInfoEntrySerDe>,
60+
},
5361
FirmwareVolume {
5462
#[serde(with = "hex_format")]
5563
base_address: u64,
@@ -62,6 +70,37 @@ pub enum HobSerDe {
6270
UnknownHob,
6371
}
6472

73+
/// Serializable representation of one `EFI_MEMORY_TYPE_INFORMATION` entry inside a Memory
74+
/// Type Information GUID Extension HOB.
75+
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
76+
pub struct MemoryTypeInfoEntrySerDe {
77+
/// `EFI_MEMORY_TYPE` value for this bin (e.g. `EfiRuntimeServicesData = 6`).
78+
pub memory_type: u32,
79+
/// Number of 4 KiB pages requested for this bin.
80+
pub number_of_pages: u32,
81+
}
82+
83+
/// Parses the data payload of a Memory Type Information GUID Extension HOB.
84+
///
85+
/// Each entry is `(u32 memory_type, u32 number_of_pages)` in little-endian order. Parsing
86+
/// stops at (and excludes) the sentinel entry whose `memory_type` is `>= EFI_MAX_MEMORY_TYPE`.
87+
fn parse_memory_type_info_entries(data: &[u8]) -> Vec<MemoryTypeInfoEntrySerDe> {
88+
const ENTRY_SIZE: usize = core::mem::size_of::<MemoryTypeInfoEntrySerDe>();
89+
let mut entries = Vec::new();
90+
for chunk in data.chunks_exact(ENTRY_SIZE) {
91+
let &[m0, m1, m2, m3, p0, p1, p2, p3] = chunk else {
92+
continue;
93+
};
94+
let memory_type = u32::from_le_bytes([m0, m1, m2, m3]);
95+
let number_of_pages = u32::from_le_bytes([p0, p1, p2, p3]);
96+
if (memory_type as usize) >= crate::efi_types::EFI_MAX_MEMORY_TYPE {
97+
break;
98+
}
99+
entries.push(MemoryTypeInfoEntrySerDe { memory_type, number_of_pages });
100+
}
101+
entries
102+
}
103+
65104
/// Serializable representation of the memory allocation descriptor inside a Memory Allocation HOB.
66105
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
67106
pub struct MemAllocDescriptorSerDe {
@@ -188,7 +227,13 @@ impl From<&Hob<'_>> for HobSerDe {
188227
},
189228
attributes: resource_desc2.attributes,
190229
},
191-
Hob::GuidHob(guid_ext, _) => Self::GuidExtension { name: format_guid(&guid_ext.name) },
230+
Hob::GuidHob(guid_ext, data) => {
231+
if guid_ext.name == MEMORY_TYPE_INFO_HOB_GUID.into_inner() {
232+
Self::MemoryTypeInformation { entries: parse_memory_type_info_entries(data) }
233+
} else {
234+
Self::GuidExtension { name: format_guid(&guid_ext.name) }
235+
}
236+
}
192237
Hob::FirmwareVolume(fv) => Self::FirmwareVolume { base_address: fv.base_address, length: fv.length },
193238
Hob::Cpu(cpu) => {
194239
Self::Cpu { size_of_memory_space: cpu.size_of_memory_space, size_of_io_space: cpu.size_of_io_space }
@@ -453,4 +498,43 @@ mod tests {
453498
assert!(json.contains(r#""size_of_memory_space": 0"#), "CPU memory space size incorrect");
454499
assert!(json.contains(r#""size_of_io_space": 0"#), "CPU IO space size incorrect");
455500
}
501+
502+
#[test]
503+
fn test_memory_type_information_hob_serialization() {
504+
// Two valid entries followed by a sentinel entry whose memory_type is >= EFI_MAX_MEMORY_TYPE.
505+
let mut data = Vec::new();
506+
data.extend_from_slice(&6_u32.to_le_bytes()); // EfiRuntimeServicesData
507+
data.extend_from_slice(&100_u32.to_le_bytes());
508+
data.extend_from_slice(&9_u32.to_le_bytes()); // EfiACPIReclaimMemory
509+
data.extend_from_slice(&200_u32.to_le_bytes());
510+
data.extend_from_slice(&u32::MAX.to_le_bytes()); // sentinel
511+
data.extend_from_slice(&999_u32.to_le_bytes()); // ignored sentinel pages
512+
513+
let guid_hob = hob::GuidHob {
514+
header: hob::header::Hob {
515+
r#type: hob::GUID_EXTENSION,
516+
length: (size_of::<hob::GuidHob>() + data.len()) as u16,
517+
reserved: 0,
518+
},
519+
name: MEMORY_TYPE_INFO_HOB_GUID,
520+
};
521+
522+
let serde = HobSerDe::from(&Hob::GuidHob(&guid_hob, &data));
523+
let json = to_string_pretty(&serde).expect("Serialization failed");
524+
525+
let HobSerDe::MemoryTypeInformation { entries } = serde else {
526+
panic!("Expected MemoryTypeInformation variant");
527+
};
528+
529+
assert_eq!(
530+
entries,
531+
alloc::vec![
532+
MemoryTypeInfoEntrySerDe { memory_type: 6, number_of_pages: 100 },
533+
MemoryTypeInfoEntrySerDe { memory_type: 9, number_of_pages: 200 },
534+
]
535+
);
536+
537+
assert!(json.contains(r#""type": "memory_type_information""#), "Memory Type Information HOB missing");
538+
assert!(json.contains(r#""number_of_pages": 200"#), "Entry page count incorrect");
539+
}
456540
}

0 commit comments

Comments
 (0)