1use bitfield_struct::bitfield;
7use core::mem::size_of;
8use guid::Guid;
9use zerocopy::FromBytes;
10use zerocopy::Immutable;
11use zerocopy::IntoBytes;
12use zerocopy::KnownLayout;
13
14fn align_8(x: usize) -> usize {
15 (x + 7) & !7
16}
17
18#[derive(Debug)]
20pub struct Blob {
21 data: Vec<u8>,
22 count: u32,
23}
24
25impl Blob {
26 pub fn new() -> Self {
28 let mut blob = Self {
29 data: Vec::new(),
30 count: 0,
31 };
32 blob.add(&StructureCount {
33 total_structure_count: 0,
34 total_config_blob_size: 0,
35 });
36 blob
37 }
38
39 pub fn add<T: BlobStructure>(&mut self, data: &T) -> &mut Self {
41 self.add_raw(T::STRUCTURE_TYPE, data.as_bytes())
42 }
43
44 pub fn add_cstring(&mut self, structure_type: BlobStructureType, data: &[u8]) -> &mut Self {
53 if !data.is_empty() {
54 self.add_raw_inner(structure_type, data, !data.ends_with(&[0]))
55 } else {
56 self
57 }
58 }
59
60 pub fn add_raw(&mut self, structure_type: BlobStructureType, data: &[u8]) -> &mut Self {
63 self.add_raw_inner(structure_type, data, false)
64 }
65
66 fn add_raw_inner(
67 &mut self,
68 structure_type: BlobStructureType,
69 data: &[u8],
70 add_null_term: bool,
71 ) -> &mut Self {
72 let aligned_data_len = align_8(data.len() + add_null_term as usize);
74 self.data.extend_from_slice(
75 Header {
76 structure_type: structure_type as u32,
77 length: (size_of::<Header>() + aligned_data_len) as u32,
78 }
79 .as_bytes(),
80 );
81 self.data.extend_from_slice(data);
82 if add_null_term {
83 self.data.push(0);
84 }
85 self.data
87 .extend_from_slice(&[0; 7][..aligned_data_len - (data.len() + add_null_term as usize)]);
88 self.count += 1;
89 self
90 }
91
92 pub fn complete(mut self) -> Vec<u8> {
95 let total_config_blob_size = self.data.len() as u32;
96 self.data[size_of::<Header>()..size_of::<Header>() + size_of::<StructureCount>()]
97 .copy_from_slice(
98 StructureCount {
99 total_structure_count: self.count,
100 total_config_blob_size,
101 }
102 .as_bytes(),
103 );
104 self.data
105 }
106}
107
108impl Default for Blob {
109 fn default() -> Self {
110 Self::new()
111 }
112}
113
114pub trait BlobStructure: IntoBytes + FromBytes + Immutable + KnownLayout {
115 const STRUCTURE_TYPE: BlobStructureType;
116}
117
118macro_rules! blobtypes {
119 {
120 $($name:ident,)*
121 } => {
122 $(
123 impl BlobStructure for $name {
124 const STRUCTURE_TYPE: BlobStructureType = BlobStructureType::$name;
125 }
126 )*
127 }
128}
129
130blobtypes! {
131 StructureCount,
132 BiosInformation,
133 Entropy,
134 BiosGuid,
135 Smbios31ProcessorInformation,
136 Flags,
137 ProcessorInformation,
138 MmioRanges,
139 NvdimmCount,
140 VpciInstanceFilter,
141 Gic,
142}
143
144#[repr(u32)]
146pub enum BlobStructureType {
147 StructureCount = 0x00,
148 BiosInformation = 0x01,
149 Srat = 0x02,
150 MemoryMap = 0x03,
151 Entropy = 0x04,
152 BiosGuid = 0x05,
153 SmbiosSystemSerialNumber = 0x06,
154 SmbiosBaseSerialNumber = 0x07,
155 SmbiosChassisSerialNumber = 0x08,
156 SmbiosChassisAssetTag = 0x09,
157 SmbiosBiosLockString = 0x0A,
158 Smbios31ProcessorInformation = 0x0B,
159 SmbiosSocketDesignation = 0x0C,
160 SmbiosProcessorManufacturer = 0x0D,
161 SmbiosProcessorVersion = 0x0E,
162 SmbiosProcessorSerialNumber = 0x0F,
163 SmbiosProcessorAssetTag = 0x10,
164 SmbiosProcessorPartNumber = 0x11,
165 Flags = 0x12,
166 ProcessorInformation = 0x13,
167 MmioRanges = 0x14,
168 Aarch64Mpidr = 0x15,
169 AcpiTable = 0x16,
170 NvdimmCount = 0x17,
171 Madt = 0x18,
172 VpciInstanceFilter = 0x19,
173 SmbiosSystemManufacturer = 0x1A,
174 SmbiosSystemProductName = 0x1B,
175 SmbiosSystemVersion = 0x1C,
176 SmbiosSystemSkuNumber = 0x1D,
177 SmbiosSystemFamily = 0x1E,
178 SmbiosMemoryDeviceSerialNumber = 0x1F,
179 Slit = 0x20,
180 Aspt = 0x21,
181 Pptt = 0x22,
182 Gic = 0x23,
183 Mcfg = 0x24,
184 Ssdt = 0x25,
185 Hmat = 0x26,
186 Iort = 0x27,
187}
188
189#[repr(C)]
204#[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
205pub struct Header {
206 pub structure_type: u32,
207 pub length: u32,
208}
209
210#[repr(C)]
217#[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
218pub struct StructureCount {
219 pub total_structure_count: u32,
220 pub total_config_blob_size: u32,
221}
222
223#[repr(C)]
224#[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
225pub struct BiosInformation {
226 pub bios_size_pages: u32,
227 pub flags: u32,
232}
233
234pub const VM_MEMORY_RANGE_FLAG_PLATFORM_RESERVED: u32 = 0x1;
247pub const VM_MEMORY_RANGE_FLAG_PERSISTENT: u32 = 0x2;
248pub const VM_MEMORY_RANGE_FLAG_SPECIFIC_PURPOSE: u32 = 0x4;
249
250#[repr(C)]
251#[derive(Debug, IntoBytes, Immutable, KnownLayout, FromBytes)]
252pub struct MemoryRangeV5 {
253 pub base_address: u64,
254 pub length: u64,
255 pub flags: u32,
256 pub reserved: u32,
257}
258
259#[repr(C)]
260#[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
261pub struct Entropy(pub [u8; 64]);
262
263impl Default for Entropy {
264 fn default() -> Self {
265 Entropy([0; 64])
266 }
267}
268
269#[repr(C)]
270#[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
271pub struct BiosGuid(pub Guid);
272
273#[repr(C)]
274#[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
275pub struct Smbios31ProcessorInformation {
276 pub processor_id: u64,
277 pub external_clock: u16,
278 pub max_speed: u16,
279 pub current_speed: u16,
280 pub processor_characteristics: u16,
281 pub processor_family2: u16,
282 pub processor_type: u8,
283 pub voltage: u8,
284 pub status: u8,
285 pub processor_upgrade: u8,
286 pub reserved: u16,
287}
288
289#[bitfield(u64, debug = false)]
290#[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
291pub struct Flags {
292 pub serial_controllers_enabled: bool,
293 pub pause_after_boot_failure: bool,
294 pub pxe_ip_v6: bool,
295 pub debugger_enabled: bool,
296 pub load_oemp_table: bool,
297 pub tpm_enabled: bool,
298 pub hibernate_enabled: bool,
299
300 #[bits(2)]
301 pub console: ConsolePort,
302
303 pub memory_attributes_table_enabled: bool,
304 pub virtual_battery_enabled: bool,
305 pub sgx_memory_enabled: bool,
306 pub is_vmbfs_boot: bool,
307 pub measure_additional_pcrs: bool,
308 pub disable_frontpage: bool,
309 pub default_boot_always_attempt: bool,
310 pub low_power_s0_idle_enabled: bool,
311 pub vpci_boot_enabled: bool,
312 pub proc_idle_enabled: bool,
313 pub disable_sha384_pcr: bool,
314 pub media_present_enabled_by_default: bool,
315
316 #[bits(2)]
317 pub memory_protection: MemoryProtection,
318
319 pub enable_imc_when_isolated: bool,
320 pub watchdog_enabled: bool,
321 pub tpm_locality_regs_enabled: bool,
322 pub dhcp6_link_layer_address: bool,
323 pub cxl_memory_enabled: bool,
324 pub mtrrs_initialized_at_load: bool,
325 pub hv_sint_enabled: bool,
326 pub azi_hsm_enabled: bool,
327
328 #[bits(33)]
329 _reserved: u64,
330}
331
332#[derive(Clone, Copy)]
333pub enum ConsolePort {
334 Default = 0b00,
335 Com1 = 0b01,
336 Com2 = 0b10,
337 None = 0b11,
338}
339
340impl ConsolePort {
341 const fn from_bits(bits: u64) -> Self {
342 match bits {
343 0b00 => Self::Default,
344 0b01 => Self::Com1,
345 0b10 => Self::Com2,
346 0b11 => Self::None,
347 _ => unreachable!(),
348 }
349 }
350
351 const fn into_bits(self) -> u64 {
352 self as u64
353 }
354}
355
356pub enum MemoryProtection {
357 Disabled = 0b00,
358 Default = 0b01,
359 Strict = 0b10,
360 Relaxed = 0b11,
361}
362
363impl MemoryProtection {
364 const fn from_bits(bits: u64) -> Self {
365 match bits {
366 0b00 => Self::Disabled,
367 0b01 => Self::Default,
368 0b10 => Self::Strict,
369 0b11 => Self::Relaxed,
370 _ => unreachable!(),
371 }
372 }
373
374 const fn into_bits(self) -> u64 {
375 self as u64
376 }
377}
378
379#[repr(C)]
380#[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
381pub struct ProcessorInformation {
382 pub max_processor_count: u32,
383 pub processor_count: u32,
384 pub processors_per_virtual_socket: u32,
385 pub threads_per_processor: u32,
386}
387
388#[repr(C)]
389#[derive(IntoBytes, Immutable, KnownLayout, FromBytes, Copy, Clone)]
390pub struct Mmio {
391 pub mmio_page_number_start: u64,
392 pub mmio_size_in_pages: u64,
393}
394
395#[repr(C)]
396#[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
397pub struct MmioRanges(pub [Mmio; 2]);
398
399#[repr(C)]
400#[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
401pub struct NvdimmCount {
402 pub count: u16,
403 pub padding: [u16; 3],
404}
405
406#[repr(C)]
407#[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
408pub struct VpciInstanceFilter {
409 pub instance_guid: Guid,
410}
411
412#[repr(C)]
413#[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
414pub struct Gic {
415 pub gic_distributor_base: u64,
416 pub gic_redistributors_base: u64,
417}
418
419#[cfg(test)]
420mod tests {
421 use super::*;
422
423 fn read<T>(bytes: &[u8]) -> T
424 where
425 T: FromBytes + Immutable + KnownLayout,
426 {
427 T::read_from_prefix(bytes)
428 .expect("byte slice should always be big enough")
429 .0
430 }
431
432 fn add_one_dynamic(length: usize) {
433 let padded_length = align_8(length);
434 let madt = vec![0xCC; length];
435
436 let data = {
437 let mut blob = Blob::new();
438 blob.add_raw(BlobStructureType::Madt, &madt);
439 blob.complete()
440 };
441
442 assert_eq!(data.len() % 8, 0);
443
444 let header: Header = read(&data[..]);
445 let structure: StructureCount = read(&data[size_of::<Header>()..]);
446
447 let header_exp = Header {
448 structure_type: 0x00,
449 length: (size_of::<Header>() + size_of::<StructureCount>()) as u32,
450 };
451 let structure_exp = StructureCount {
452 total_structure_count: 2,
453 total_config_blob_size: (2 * size_of::<Header>()
454 + size_of::<StructureCount>()
455 + padded_length) as u32,
456 };
457
458 assert_eq!(header.structure_type, header_exp.structure_type);
459 assert_eq!(header.length, header_exp.length);
460 assert_eq!(
461 structure.total_structure_count,
462 structure_exp.total_structure_count
463 );
464 assert_eq!(
465 structure.total_config_blob_size,
466 structure_exp.total_config_blob_size
467 );
468
469 let header: Header = read(&data[size_of::<Header>() + size_of::<StructureCount>()..]);
470 let structure = &data[2 * size_of::<Header>() + size_of::<StructureCount>()..][..length];
471
472 let header_exp = Header {
473 structure_type: 0x18,
474 length: (size_of::<Header>() + padded_length) as u32,
475 };
476 let structure_exp = &madt[..];
477
478 assert_eq!(header.structure_type, header_exp.structure_type);
479 assert_eq!(header.length, header_exp.length);
480 assert_eq!(structure, structure_exp);
481 }
482
483 #[test]
484 fn add_none() {
485 let data = {
486 let blob = Blob::new();
487 blob.complete()
488 };
489
490 assert_eq!(data.len() % 8, 0);
491
492 let header: Header = read(&data[..]);
493 let structure: StructureCount = read(&data[size_of::<Header>()..]);
494
495 let header_exp = Header {
496 structure_type: 0x00,
497 length: (size_of::<Header>() + size_of::<StructureCount>()) as u32,
498 };
499 let structure_exp = StructureCount {
500 total_structure_count: 1,
501 total_config_blob_size: (size_of::<Header>() + size_of::<StructureCount>()) as u32,
502 };
503
504 assert_eq!(header.structure_type, header_exp.structure_type);
505 assert_eq!(header.length, header_exp.length);
506 assert_eq!(
507 structure.total_structure_count,
508 structure_exp.total_structure_count
509 );
510 assert_eq!(
511 structure.total_config_blob_size,
512 structure_exp.total_config_blob_size
513 );
514 }
515
516 #[test]
517 fn add_one_fixed() {
518 let biosinfo = BiosInformation {
519 bios_size_pages: 12345678,
520 flags: 1 << 31,
521 };
522
523 let data = {
524 let mut blob = Blob::new();
525 blob.add(&BiosInformation {
526 bios_size_pages: 12345678,
527 flags: 1 << 31,
528 });
529 blob.complete()
530 };
531
532 assert_eq!(data.len() % 8, 0);
533
534 let header: Header = read(&data[..]);
535 let structure: StructureCount = read(&data[size_of::<Header>()..]);
536
537 let header_exp = Header {
538 structure_type: 0x00,
539 length: (size_of::<Header>() + size_of::<StructureCount>()) as u32,
540 };
541 let structure_exp = StructureCount {
542 total_structure_count: 2,
543 total_config_blob_size: (2 * size_of::<Header>()
544 + size_of::<StructureCount>()
545 + size_of::<BiosInformation>()) as u32,
546 };
547
548 assert_eq!(header.structure_type, header_exp.structure_type);
549 assert_eq!(header.length, header_exp.length);
550 assert_eq!(
551 structure.total_structure_count,
552 structure_exp.total_structure_count
553 );
554 assert_eq!(
555 structure.total_config_blob_size,
556 structure_exp.total_config_blob_size
557 );
558
559 let header: Header = read(&data[size_of::<Header>() + size_of::<StructureCount>()..]);
560 let structure: BiosInformation =
561 read(&data[2 * size_of::<Header>() + size_of::<StructureCount>()..]);
562
563 let header_exp = Header {
564 structure_type: 0x01,
565 length: (size_of::<Header>() + size_of::<BiosInformation>()) as u32,
566 };
567
568 assert_eq!(header.structure_type, header_exp.structure_type);
569 assert_eq!(header.length, header_exp.length);
570 assert_eq!(structure.bios_size_pages, biosinfo.bios_size_pages);
571 assert_eq!(structure.flags, biosinfo.flags);
572 }
573
574 #[test]
575 fn add_one_dynamic_misaligned() {
576 add_one_dynamic(43);
577 }
578
579 #[test]
580 fn add_one_dynamic_aligned() {
581 add_one_dynamic(40);
582 }
583
584 #[test]
585 fn add_two() {
586 const LENGTH: usize = 93;
587 const PADDED_LENGTH: usize = 96;
588 let madt = vec![0xCC; LENGTH];
589 let procinfo = ProcessorInformation {
590 max_processor_count: 4,
591 processor_count: 3,
592 processors_per_virtual_socket: 2,
593 threads_per_processor: 1,
594 };
595
596 let data = {
597 let mut blob = Blob::new();
598 blob.add_raw(BlobStructureType::Madt, &madt).add(&procinfo);
599 blob.complete()
600 };
601
602 assert_eq!(data.len() % 8, 0);
603
604 let header: Header = read(&data[..]);
605 let structure: StructureCount = read(&data[size_of::<Header>()..]);
606
607 let header_exp = Header {
608 structure_type: 0x00,
609 length: (size_of::<Header>() + size_of::<StructureCount>()) as u32,
610 };
611 let structure_exp = StructureCount {
612 total_structure_count: 3,
613 total_config_blob_size: (3 * size_of::<Header>()
614 + size_of::<StructureCount>()
615 + PADDED_LENGTH
616 + size_of::<ProcessorInformation>()) as u32,
617 };
618
619 assert_eq!(header.structure_type, header_exp.structure_type);
620 assert_eq!(header.length, header_exp.length);
621 assert_eq!(
622 structure.total_structure_count,
623 structure_exp.total_structure_count
624 );
625 assert_eq!(
626 structure.total_config_blob_size,
627 structure_exp.total_config_blob_size
628 );
629
630 let header: Header = read(&data[size_of::<Header>() + size_of::<StructureCount>()..]);
631 let structure = &data[2 * size_of::<Header>() + size_of::<StructureCount>()..][..LENGTH];
632 let padding = &data[2 * size_of::<Header>() + size_of::<StructureCount>() + LENGTH..]
633 [..PADDED_LENGTH - LENGTH];
634
635 let header_exp = Header {
636 structure_type: 0x18,
637 length: (size_of::<Header>() + PADDED_LENGTH) as u32,
638 };
639 let structure_exp = &madt[..];
640
641 assert_eq!(header.structure_type, header_exp.structure_type);
642 assert_eq!(header.length, header_exp.length);
643 assert_eq!(structure.as_bytes(), structure_exp.as_bytes());
644 assert_eq!(padding, &[0; PADDED_LENGTH - LENGTH]);
645
646 let header: Header =
647 read(&data[2 * size_of::<Header>() + size_of::<StructureCount>() + PADDED_LENGTH..]);
648 let structure: ProcessorInformation =
649 read(&data[3 * size_of::<Header>() + size_of::<StructureCount>() + PADDED_LENGTH..]);
650
651 let header_exp = Header {
652 structure_type: 0x13,
653 length: (size_of::<Header>() + size_of::<ProcessorInformation>()) as u32,
654 };
655
656 assert_eq!(header.structure_type, header_exp.structure_type);
657 assert_eq!(header.length, header_exp.length);
658 assert_eq!(structure.max_processor_count, procinfo.max_processor_count);
659 assert_eq!(structure.processor_count, procinfo.processor_count);
660 assert_eq!(
661 structure.processors_per_virtual_socket,
662 procinfo.processors_per_virtual_socket
663 );
664 assert_eq!(
665 structure.threads_per_processor,
666 procinfo.threads_per_processor
667 );
668 }
669}