Skip to main content

loader/uefi/
config.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! UEFI specific configuration format and construction.
5
6use 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/// A configuration blob builder for passing config information to UEFI.
19#[derive(Debug)]
20pub struct Blob {
21    data: Vec<u8>,
22    count: u32,
23}
24
25impl Blob {
26    /// Creates a new configuration blob with a placeholder StructureCount structure
27    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    /// Aligns and appends a sized structure and its appropriate header to the configuration blob.
40    pub fn add<T: BlobStructure>(&mut self, data: &T) -> &mut Self {
41        self.add_raw(T::STRUCTURE_TYPE, data.as_bytes())
42    }
43
44    /// Aligns and appends a null terminated C string and its appropriate header
45    /// to the configuration blob.
46    ///
47    /// If the data is zero-sized, the configuration blob is not updated.
48    ///
49    /// If the data does not include a null terminator (e.g: because the data
50    /// was pulled from a Rust string), a null terminator is appended to the end
51    /// of the data.
52    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    /// Aligns and appends the raw byte data of a potentially dynamically sized structure
61    /// and its appropriate header to the configuration blob.
62    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        // Align up to 8 bytes.
73        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        // Pad with zeroes.
86        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    /// Returns a serialized binary format of the whole configuration blob. Done by updating the structure count and
93    /// returning the complete binary config blob.
94    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/// Config structure types.
145#[repr(u32)]
146pub enum BlobStructureType {
147    StructureCount = 0x00,
148    BiosInformation = 0x01,
149    #[deprecated(note = "use AcpiTable")]
150    Srat = 0x02,
151    MemoryMap = 0x03,
152    Entropy = 0x04,
153    BiosGuid = 0x05,
154    SmbiosSystemSerialNumber = 0x06,
155    SmbiosBaseSerialNumber = 0x07,
156    SmbiosChassisSerialNumber = 0x08,
157    SmbiosChassisAssetTag = 0x09,
158    SmbiosBiosLockString = 0x0A,
159    Smbios31ProcessorInformation = 0x0B,
160    SmbiosSocketDesignation = 0x0C,
161    SmbiosProcessorManufacturer = 0x0D,
162    SmbiosProcessorVersion = 0x0E,
163    SmbiosProcessorSerialNumber = 0x0F,
164    SmbiosProcessorAssetTag = 0x10,
165    SmbiosProcessorPartNumber = 0x11,
166    Flags = 0x12,
167    ProcessorInformation = 0x13,
168    MmioRanges = 0x14,
169    Aarch64Mpidr = 0x15,
170    AcpiTable = 0x16,
171    NvdimmCount = 0x17,
172    #[deprecated(note = "use AcpiTable")]
173    Madt = 0x18,
174    VpciInstanceFilter = 0x19,
175    SmbiosSystemManufacturer = 0x1A,
176    SmbiosSystemProductName = 0x1B,
177    SmbiosSystemVersion = 0x1C,
178    SmbiosSystemSkuNumber = 0x1D,
179    SmbiosSystemFamily = 0x1E,
180    SmbiosMemoryDeviceSerialNumber = 0x1F,
181    #[deprecated(note = "use AcpiTable")]
182    Slit = 0x20,
183    #[deprecated(note = "use AcpiTable")]
184    Aspt = 0x21,
185    #[deprecated(note = "use AcpiTable")]
186    Pptt = 0x22,
187    Gic = 0x23,
188    #[deprecated(note = "use AcpiTable")]
189    Mcfg = 0x24,
190    #[deprecated(note = "use AcpiTable")]
191    Ssdt = 0x25,
192    #[deprecated(note = "use AcpiTable")]
193    Hmat = 0x26,
194    #[deprecated(note = "use AcpiTable")]
195    Iort = 0x27,
196    PcieBarApertures = 0x28,
197}
198
199//
200// Config Structures.
201//
202// NOTE: All config structures _must_ be aligned to 8 bytes, as AARCH64 does not
203// support unaligned accesses. For variable length structures, they must be
204// padded appropriately to 8 byte boundaries.
205//
206
207//
208// Common config header.
209//
210// NOTE: Length is the length of the overall structure in bytes, including the
211// header.
212//
213#[repr(C)]
214#[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
215pub struct Header {
216    pub structure_type: u32,
217    pub length: u32,
218}
219
220//
221// NOTE: TotalStructureCount is the count of all structures in the config blob,
222// including this structure.
223//
224// NOTE: TotalConfigBlobSize is in bytes.
225//
226#[repr(C)]
227#[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
228pub struct StructureCount {
229    pub total_structure_count: u32,
230    pub total_config_blob_size: u32,
231}
232
233#[repr(C)]
234#[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
235pub struct BiosInformation {
236    pub bios_size_pages: u32,
237    // struct {
238    //     UINT32 LegacyMemoryMap : 1;
239    //     UINT32 Reserved : 31;
240    // } Flags;
241    pub flags: u32,
242}
243
244//
245// Memory map range flags beginning with VDev version 5.
246//
247// VM_MEMORY_RANGE_FLAG_PLATFORM_RESERVED is mapped to EfiReservedMemoryType.
248// This means the memory range is reserved and not regular RAM.
249//
250// VM_MEMORY_RANGE_FLAG_PERSISTENT is mapped to EfiPersistentMemory.
251// This means the memory range is byte-addressable and non-volatile, like PMem.
252//
253// VM_MEMORY_RANGE_FLAG_SPECIAL_PURPOSE is mapped to EfiConventionalMemory.
254// This flag instructs the guest to mark the memory with the EFI_MEMORY_SP bit.
255//
256pub const VM_MEMORY_RANGE_FLAG_PLATFORM_RESERVED: u32 = 0x1;
257pub const VM_MEMORY_RANGE_FLAG_PERSISTENT: u32 = 0x2;
258pub const VM_MEMORY_RANGE_FLAG_SPECIFIC_PURPOSE: u32 = 0x4;
259
260#[repr(C)]
261#[derive(Debug, IntoBytes, Immutable, KnownLayout, FromBytes)]
262pub struct MemoryRangeV5 {
263    pub base_address: u64,
264    pub length: u64,
265    pub flags: u32,
266    pub reserved: u32,
267}
268
269#[repr(C)]
270#[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
271pub struct Entropy(pub [u8; 64]);
272
273impl Default for Entropy {
274    fn default() -> Self {
275        Entropy([0; 64])
276    }
277}
278
279#[repr(C)]
280#[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
281pub struct BiosGuid(pub Guid);
282
283#[repr(C)]
284#[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
285pub struct Smbios31ProcessorInformation {
286    pub processor_id: u64,
287    pub external_clock: u16,
288    pub max_speed: u16,
289    pub current_speed: u16,
290    pub processor_characteristics: u16,
291    pub processor_family2: u16,
292    pub processor_type: u8,
293    pub voltage: u8,
294    pub status: u8,
295    pub processor_upgrade: u8,
296    pub reserved: u16,
297}
298
299#[bitfield(u64, debug = false)]
300#[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
301pub struct Flags {
302    pub serial_controllers_enabled: bool,
303    pub pause_after_boot_failure: bool,
304    pub pxe_ip_v6: bool,
305    pub debugger_enabled: bool,
306    pub load_oemp_table: bool,
307    pub tpm_enabled: bool,
308    pub hibernate_enabled: bool,
309
310    #[bits(2)]
311    pub console: ConsolePort,
312
313    pub memory_attributes_table_enabled: bool,
314    pub virtual_battery_enabled: bool,
315    pub sgx_memory_enabled: bool,
316    pub is_vmbfs_boot: bool,
317    pub measure_additional_pcrs: bool,
318    pub disable_frontpage: bool,
319    pub default_boot_always_attempt: bool,
320    pub low_power_s0_idle_enabled: bool,
321    pub vpci_boot_enabled: bool,
322    pub proc_idle_enabled: bool,
323    pub disable_sha384_pcr: bool,
324    pub media_present_enabled_by_default: bool,
325
326    #[bits(2)]
327    pub memory_protection: MemoryProtection,
328
329    pub enable_imc_when_isolated: bool,
330    pub watchdog_enabled: bool,
331    pub tpm_locality_regs_enabled: bool,
332    pub dhcp6_link_layer_address: bool,
333    pub cxl_memory_enabled: bool,
334    pub mtrrs_initialized_at_load: bool,
335    pub disable_sha1_pcr: bool,
336    pub vmbus_disabled: bool,
337    pub pci_resources_pre_assigned: bool,
338    pub force_dma_bounce_enabled: bool,
339    pub ipmi_enabled: bool,
340
341    #[bits(30)]
342    _reserved: u64,
343}
344
345#[derive(Clone, Copy)]
346pub enum ConsolePort {
347    Default = 0b00,
348    Com1 = 0b01,
349    Com2 = 0b10,
350    None = 0b11,
351}
352
353impl ConsolePort {
354    const fn from_bits(bits: u64) -> Self {
355        match bits {
356            0b00 => Self::Default,
357            0b01 => Self::Com1,
358            0b10 => Self::Com2,
359            0b11 => Self::None,
360            _ => unreachable!(),
361        }
362    }
363
364    const fn into_bits(self) -> u64 {
365        self as u64
366    }
367}
368
369pub enum MemoryProtection {
370    Disabled = 0b00,
371    Default = 0b01,
372    Strict = 0b10,
373    Relaxed = 0b11,
374}
375
376impl MemoryProtection {
377    const fn from_bits(bits: u64) -> Self {
378        match bits {
379            0b00 => Self::Disabled,
380            0b01 => Self::Default,
381            0b10 => Self::Strict,
382            0b11 => Self::Relaxed,
383            _ => unreachable!(),
384        }
385    }
386
387    const fn into_bits(self) -> u64 {
388        self as u64
389    }
390}
391
392#[repr(C)]
393#[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
394pub struct ProcessorInformation {
395    pub max_processor_count: u32,
396    pub processor_count: u32,
397    pub processors_per_virtual_socket: u32,
398    pub threads_per_processor: u32,
399}
400
401#[repr(C)]
402#[derive(IntoBytes, Immutable, KnownLayout, FromBytes, Copy, Clone)]
403pub struct Mmio {
404    pub mmio_page_number_start: u64,
405    pub mmio_size_in_pages: u64,
406}
407
408#[repr(C)]
409#[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
410pub struct MmioRanges(pub [Mmio; 2]);
411
412#[repr(C)]
413#[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
414pub struct NvdimmCount {
415    pub count: u16,
416    pub padding: [u16; 3],
417}
418
419#[repr(C)]
420#[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
421pub struct VpciInstanceFilter {
422    pub instance_guid: Guid,
423}
424
425#[repr(C)]
426#[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
427pub struct Gic {
428    pub gic_distributor_base: u64,
429    pub gic_redistributors_base: u64,
430}
431
432// Describes the BAR Aperture for each PCIe Root Complex / Host bridge. There
433// should be one entry per host bridge that UEFI should enumerate. The MCFG
434// table may contain additional segments that are not described to UEFI via
435// these structures, which UEFI will ignore.
436//
437// This structure is used to pass this information to UEFI instead of having
438// UEFI parse the SSDT.
439#[repr(C)]
440#[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
441pub struct PcieBarApertureEntry {
442    pub segment: u16,
443    pub start_bus: u8,
444    pub end_bus: u8,
445    /// The UID here must match the UID described in the SSDT for the
446    /// corresponding host bridge.
447    pub uid: u32,
448    pub low_mmio_base: u64,
449    pub low_mmio_length: u64,
450    pub high_mmio_base: u64,
451    pub high_mmio_length: u64,
452}
453
454#[cfg(test)]
455mod tests {
456    use super::*;
457
458    fn read<T>(bytes: &[u8]) -> T
459    where
460        T: FromBytes + Immutable + KnownLayout,
461    {
462        T::read_from_prefix(bytes)
463            .expect("byte slice should always be big enough")
464            .0
465    }
466
467    fn add_one_dynamic(length: usize) {
468        let padded_length = align_8(length);
469        let madt = vec![0xCC; length];
470
471        let data = {
472            let mut blob = Blob::new();
473            blob.add_raw(BlobStructureType::AcpiTable, &madt);
474            blob.complete()
475        };
476
477        assert_eq!(data.len() % 8, 0);
478
479        let header: Header = read(&data[..]);
480        let structure: StructureCount = read(&data[size_of::<Header>()..]);
481
482        let header_exp = Header {
483            structure_type: 0x00,
484            length: (size_of::<Header>() + size_of::<StructureCount>()) as u32,
485        };
486        let structure_exp = StructureCount {
487            total_structure_count: 2,
488            total_config_blob_size: (2 * size_of::<Header>()
489                + size_of::<StructureCount>()
490                + padded_length) as u32,
491        };
492
493        assert_eq!(header.structure_type, header_exp.structure_type);
494        assert_eq!(header.length, header_exp.length);
495        assert_eq!(
496            structure.total_structure_count,
497            structure_exp.total_structure_count
498        );
499        assert_eq!(
500            structure.total_config_blob_size,
501            structure_exp.total_config_blob_size
502        );
503
504        let header: Header = read(&data[size_of::<Header>() + size_of::<StructureCount>()..]);
505        let structure = &data[2 * size_of::<Header>() + size_of::<StructureCount>()..][..length];
506
507        let header_exp = Header {
508            structure_type: 0x16,
509            length: (size_of::<Header>() + padded_length) as u32,
510        };
511        let structure_exp = &madt[..];
512
513        assert_eq!(header.structure_type, header_exp.structure_type);
514        assert_eq!(header.length, header_exp.length);
515        assert_eq!(structure, structure_exp);
516    }
517
518    #[test]
519    fn add_none() {
520        let data = {
521            let blob = Blob::new();
522            blob.complete()
523        };
524
525        assert_eq!(data.len() % 8, 0);
526
527        let header: Header = read(&data[..]);
528        let structure: StructureCount = read(&data[size_of::<Header>()..]);
529
530        let header_exp = Header {
531            structure_type: 0x00,
532            length: (size_of::<Header>() + size_of::<StructureCount>()) as u32,
533        };
534        let structure_exp = StructureCount {
535            total_structure_count: 1,
536            total_config_blob_size: (size_of::<Header>() + size_of::<StructureCount>()) as u32,
537        };
538
539        assert_eq!(header.structure_type, header_exp.structure_type);
540        assert_eq!(header.length, header_exp.length);
541        assert_eq!(
542            structure.total_structure_count,
543            structure_exp.total_structure_count
544        );
545        assert_eq!(
546            structure.total_config_blob_size,
547            structure_exp.total_config_blob_size
548        );
549    }
550
551    #[test]
552    fn add_one_fixed() {
553        let biosinfo = BiosInformation {
554            bios_size_pages: 12345678,
555            flags: 1 << 31,
556        };
557
558        let data = {
559            let mut blob = Blob::new();
560            blob.add(&BiosInformation {
561                bios_size_pages: 12345678,
562                flags: 1 << 31,
563            });
564            blob.complete()
565        };
566
567        assert_eq!(data.len() % 8, 0);
568
569        let header: Header = read(&data[..]);
570        let structure: StructureCount = read(&data[size_of::<Header>()..]);
571
572        let header_exp = Header {
573            structure_type: 0x00,
574            length: (size_of::<Header>() + size_of::<StructureCount>()) as u32,
575        };
576        let structure_exp = StructureCount {
577            total_structure_count: 2,
578            total_config_blob_size: (2 * size_of::<Header>()
579                + size_of::<StructureCount>()
580                + size_of::<BiosInformation>()) as u32,
581        };
582
583        assert_eq!(header.structure_type, header_exp.structure_type);
584        assert_eq!(header.length, header_exp.length);
585        assert_eq!(
586            structure.total_structure_count,
587            structure_exp.total_structure_count
588        );
589        assert_eq!(
590            structure.total_config_blob_size,
591            structure_exp.total_config_blob_size
592        );
593
594        let header: Header = read(&data[size_of::<Header>() + size_of::<StructureCount>()..]);
595        let structure: BiosInformation =
596            read(&data[2 * size_of::<Header>() + size_of::<StructureCount>()..]);
597
598        let header_exp = Header {
599            structure_type: 0x01,
600            length: (size_of::<Header>() + size_of::<BiosInformation>()) as u32,
601        };
602
603        assert_eq!(header.structure_type, header_exp.structure_type);
604        assert_eq!(header.length, header_exp.length);
605        assert_eq!(structure.bios_size_pages, biosinfo.bios_size_pages);
606        assert_eq!(structure.flags, biosinfo.flags);
607    }
608
609    #[test]
610    fn add_one_dynamic_misaligned() {
611        add_one_dynamic(43);
612    }
613
614    #[test]
615    fn add_one_dynamic_aligned() {
616        add_one_dynamic(40);
617    }
618
619    #[test]
620    fn add_two() {
621        const LENGTH: usize = 93;
622        const PADDED_LENGTH: usize = 96;
623        let madt = vec![0xCC; LENGTH];
624        let procinfo = ProcessorInformation {
625            max_processor_count: 4,
626            processor_count: 3,
627            processors_per_virtual_socket: 2,
628            threads_per_processor: 1,
629        };
630
631        let data = {
632            let mut blob = Blob::new();
633            blob.add_raw(BlobStructureType::AcpiTable, &madt)
634                .add(&procinfo);
635            blob.complete()
636        };
637
638        assert_eq!(data.len() % 8, 0);
639
640        let header: Header = read(&data[..]);
641        let structure: StructureCount = read(&data[size_of::<Header>()..]);
642
643        let header_exp = Header {
644            structure_type: 0x00,
645            length: (size_of::<Header>() + size_of::<StructureCount>()) as u32,
646        };
647        let structure_exp = StructureCount {
648            total_structure_count: 3,
649            total_config_blob_size: (3 * size_of::<Header>()
650                + size_of::<StructureCount>()
651                + PADDED_LENGTH
652                + size_of::<ProcessorInformation>()) as u32,
653        };
654
655        assert_eq!(header.structure_type, header_exp.structure_type);
656        assert_eq!(header.length, header_exp.length);
657        assert_eq!(
658            structure.total_structure_count,
659            structure_exp.total_structure_count
660        );
661        assert_eq!(
662            structure.total_config_blob_size,
663            structure_exp.total_config_blob_size
664        );
665
666        let header: Header = read(&data[size_of::<Header>() + size_of::<StructureCount>()..]);
667        let structure = &data[2 * size_of::<Header>() + size_of::<StructureCount>()..][..LENGTH];
668        let padding = &data[2 * size_of::<Header>() + size_of::<StructureCount>() + LENGTH..]
669            [..PADDED_LENGTH - LENGTH];
670
671        let header_exp = Header {
672            structure_type: 0x16,
673            length: (size_of::<Header>() + PADDED_LENGTH) as u32,
674        };
675        let structure_exp = &madt[..];
676
677        assert_eq!(header.structure_type, header_exp.structure_type);
678        assert_eq!(header.length, header_exp.length);
679        assert_eq!(structure.as_bytes(), structure_exp.as_bytes());
680        assert_eq!(padding, &[0; PADDED_LENGTH - LENGTH]);
681
682        let header: Header =
683            read(&data[2 * size_of::<Header>() + size_of::<StructureCount>() + PADDED_LENGTH..]);
684        let structure: ProcessorInformation =
685            read(&data[3 * size_of::<Header>() + size_of::<StructureCount>() + PADDED_LENGTH..]);
686
687        let header_exp = Header {
688            structure_type: 0x13,
689            length: (size_of::<Header>() + size_of::<ProcessorInformation>()) as u32,
690        };
691
692        assert_eq!(header.structure_type, header_exp.structure_type);
693        assert_eq!(header.length, header_exp.length);
694        assert_eq!(structure.max_processor_count, procinfo.max_processor_count);
695        assert_eq!(structure.processor_count, procinfo.processor_count);
696        assert_eq!(
697            structure.processors_per_virtual_socket,
698            procinfo.processors_per_virtual_socket
699        );
700        assert_eq!(
701            structure.threads_per_processor,
702            procinfo.threads_per_processor
703        );
704    }
705}