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    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    PcieBarApertures = 0x28,
188}
189
190//
191// Config Structures.
192//
193// NOTE: All config structures _must_ be aligned to 8 bytes, as AARCH64 does not
194// support unaligned accesses. For variable length structures, they must be
195// padded appropriately to 8 byte boundaries.
196//
197
198//
199// Common config header.
200//
201// NOTE: Length is the length of the overall structure in bytes, including the
202// header.
203//
204#[repr(C)]
205#[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
206pub struct Header {
207    pub structure_type: u32,
208    pub length: u32,
209}
210
211//
212// NOTE: TotalStructureCount is the count of all structures in the config blob,
213// including this structure.
214//
215// NOTE: TotalConfigBlobSize is in bytes.
216//
217#[repr(C)]
218#[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
219pub struct StructureCount {
220    pub total_structure_count: u32,
221    pub total_config_blob_size: u32,
222}
223
224#[repr(C)]
225#[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
226pub struct BiosInformation {
227    pub bios_size_pages: u32,
228    // struct {
229    //     UINT32 LegacyMemoryMap : 1;
230    //     UINT32 Reserved : 31;
231    // } Flags;
232    pub flags: u32,
233}
234
235//
236// Memory map range flags beginning with VDev version 5.
237//
238// VM_MEMORY_RANGE_FLAG_PLATFORM_RESERVED is mapped to EfiReservedMemoryType.
239// This means the memory range is reserved and not regular RAM.
240//
241// VM_MEMORY_RANGE_FLAG_PERSISTENT is mapped to EfiPersistentMemory.
242// This means the memory range is byte-addressable and non-volatile, like PMem.
243//
244// VM_MEMORY_RANGE_FLAG_SPECIAL_PURPOSE is mapped to EfiConventionalMemory.
245// This flag instructs the guest to mark the memory with the EFI_MEMORY_SP bit.
246//
247pub const VM_MEMORY_RANGE_FLAG_PLATFORM_RESERVED: u32 = 0x1;
248pub const VM_MEMORY_RANGE_FLAG_PERSISTENT: u32 = 0x2;
249pub const VM_MEMORY_RANGE_FLAG_SPECIFIC_PURPOSE: u32 = 0x4;
250
251#[repr(C)]
252#[derive(Debug, IntoBytes, Immutable, KnownLayout, FromBytes)]
253pub struct MemoryRangeV5 {
254    pub base_address: u64,
255    pub length: u64,
256    pub flags: u32,
257    pub reserved: u32,
258}
259
260#[repr(C)]
261#[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
262pub struct Entropy(pub [u8; 64]);
263
264impl Default for Entropy {
265    fn default() -> Self {
266        Entropy([0; 64])
267    }
268}
269
270#[repr(C)]
271#[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
272pub struct BiosGuid(pub Guid);
273
274#[repr(C)]
275#[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
276pub struct Smbios31ProcessorInformation {
277    pub processor_id: u64,
278    pub external_clock: u16,
279    pub max_speed: u16,
280    pub current_speed: u16,
281    pub processor_characteristics: u16,
282    pub processor_family2: u16,
283    pub processor_type: u8,
284    pub voltage: u8,
285    pub status: u8,
286    pub processor_upgrade: u8,
287    pub reserved: u16,
288}
289
290#[bitfield(u64, debug = false)]
291#[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
292pub struct Flags {
293    pub serial_controllers_enabled: bool,
294    pub pause_after_boot_failure: bool,
295    pub pxe_ip_v6: bool,
296    pub debugger_enabled: bool,
297    pub load_oemp_table: bool,
298    pub tpm_enabled: bool,
299    pub hibernate_enabled: bool,
300
301    #[bits(2)]
302    pub console: ConsolePort,
303
304    pub memory_attributes_table_enabled: bool,
305    pub virtual_battery_enabled: bool,
306    pub sgx_memory_enabled: bool,
307    pub is_vmbfs_boot: bool,
308    pub measure_additional_pcrs: bool,
309    pub disable_frontpage: bool,
310    pub default_boot_always_attempt: bool,
311    pub low_power_s0_idle_enabled: bool,
312    pub vpci_boot_enabled: bool,
313    pub proc_idle_enabled: bool,
314    pub disable_sha384_pcr: bool,
315    pub media_present_enabled_by_default: bool,
316
317    #[bits(2)]
318    pub memory_protection: MemoryProtection,
319
320    pub enable_imc_when_isolated: bool,
321    pub watchdog_enabled: bool,
322    pub tpm_locality_regs_enabled: bool,
323    pub dhcp6_link_layer_address: bool,
324    pub cxl_memory_enabled: bool,
325    pub mtrrs_initialized_at_load: bool,
326    pub hv_sint_enabled: bool,
327
328    #[bits(34)]
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// Describes the BAR Aperture for each PCIe Root Complex / Host bridge. There
420// should be one entry per host bridge that UEFI should enumerate. The MCFG
421// table may contain additional segments that are not described to UEFI via
422// these structures, which UEFI will ignore.
423//
424// This structure is used to pass this information to UEFI instead of having
425// UEFI parse the SSDT.
426#[repr(C)]
427#[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
428pub struct PcieBarApertureEntry {
429    pub segment: u16,
430    pub start_bus: u8,
431    pub end_bus: u8,
432    /// The UID here must match the UID described in the SSDT for the
433    /// corresponding host bridge.
434    pub uid: u32,
435    pub low_mmio_base: u64,
436    pub low_mmio_length: u64,
437    pub high_mmio_base: u64,
438    pub high_mmio_length: u64,
439}
440
441#[cfg(test)]
442mod tests {
443    use super::*;
444
445    fn read<T>(bytes: &[u8]) -> T
446    where
447        T: FromBytes + Immutable + KnownLayout,
448    {
449        T::read_from_prefix(bytes)
450            .expect("byte slice should always be big enough")
451            .0
452    }
453
454    fn add_one_dynamic(length: usize) {
455        let padded_length = align_8(length);
456        let madt = vec![0xCC; length];
457
458        let data = {
459            let mut blob = Blob::new();
460            blob.add_raw(BlobStructureType::Madt, &madt);
461            blob.complete()
462        };
463
464        assert_eq!(data.len() % 8, 0);
465
466        let header: Header = read(&data[..]);
467        let structure: StructureCount = read(&data[size_of::<Header>()..]);
468
469        let header_exp = Header {
470            structure_type: 0x00,
471            length: (size_of::<Header>() + size_of::<StructureCount>()) as u32,
472        };
473        let structure_exp = StructureCount {
474            total_structure_count: 2,
475            total_config_blob_size: (2 * size_of::<Header>()
476                + size_of::<StructureCount>()
477                + padded_length) as u32,
478        };
479
480        assert_eq!(header.structure_type, header_exp.structure_type);
481        assert_eq!(header.length, header_exp.length);
482        assert_eq!(
483            structure.total_structure_count,
484            structure_exp.total_structure_count
485        );
486        assert_eq!(
487            structure.total_config_blob_size,
488            structure_exp.total_config_blob_size
489        );
490
491        let header: Header = read(&data[size_of::<Header>() + size_of::<StructureCount>()..]);
492        let structure = &data[2 * size_of::<Header>() + size_of::<StructureCount>()..][..length];
493
494        let header_exp = Header {
495            structure_type: 0x18,
496            length: (size_of::<Header>() + padded_length) as u32,
497        };
498        let structure_exp = &madt[..];
499
500        assert_eq!(header.structure_type, header_exp.structure_type);
501        assert_eq!(header.length, header_exp.length);
502        assert_eq!(structure, structure_exp);
503    }
504
505    #[test]
506    fn add_none() {
507        let data = {
508            let blob = Blob::new();
509            blob.complete()
510        };
511
512        assert_eq!(data.len() % 8, 0);
513
514        let header: Header = read(&data[..]);
515        let structure: StructureCount = read(&data[size_of::<Header>()..]);
516
517        let header_exp = Header {
518            structure_type: 0x00,
519            length: (size_of::<Header>() + size_of::<StructureCount>()) as u32,
520        };
521        let structure_exp = StructureCount {
522            total_structure_count: 1,
523            total_config_blob_size: (size_of::<Header>() + size_of::<StructureCount>()) as u32,
524        };
525
526        assert_eq!(header.structure_type, header_exp.structure_type);
527        assert_eq!(header.length, header_exp.length);
528        assert_eq!(
529            structure.total_structure_count,
530            structure_exp.total_structure_count
531        );
532        assert_eq!(
533            structure.total_config_blob_size,
534            structure_exp.total_config_blob_size
535        );
536    }
537
538    #[test]
539    fn add_one_fixed() {
540        let biosinfo = BiosInformation {
541            bios_size_pages: 12345678,
542            flags: 1 << 31,
543        };
544
545        let data = {
546            let mut blob = Blob::new();
547            blob.add(&BiosInformation {
548                bios_size_pages: 12345678,
549                flags: 1 << 31,
550            });
551            blob.complete()
552        };
553
554        assert_eq!(data.len() % 8, 0);
555
556        let header: Header = read(&data[..]);
557        let structure: StructureCount = read(&data[size_of::<Header>()..]);
558
559        let header_exp = Header {
560            structure_type: 0x00,
561            length: (size_of::<Header>() + size_of::<StructureCount>()) as u32,
562        };
563        let structure_exp = StructureCount {
564            total_structure_count: 2,
565            total_config_blob_size: (2 * size_of::<Header>()
566                + size_of::<StructureCount>()
567                + size_of::<BiosInformation>()) as u32,
568        };
569
570        assert_eq!(header.structure_type, header_exp.structure_type);
571        assert_eq!(header.length, header_exp.length);
572        assert_eq!(
573            structure.total_structure_count,
574            structure_exp.total_structure_count
575        );
576        assert_eq!(
577            structure.total_config_blob_size,
578            structure_exp.total_config_blob_size
579        );
580
581        let header: Header = read(&data[size_of::<Header>() + size_of::<StructureCount>()..]);
582        let structure: BiosInformation =
583            read(&data[2 * size_of::<Header>() + size_of::<StructureCount>()..]);
584
585        let header_exp = Header {
586            structure_type: 0x01,
587            length: (size_of::<Header>() + size_of::<BiosInformation>()) as u32,
588        };
589
590        assert_eq!(header.structure_type, header_exp.structure_type);
591        assert_eq!(header.length, header_exp.length);
592        assert_eq!(structure.bios_size_pages, biosinfo.bios_size_pages);
593        assert_eq!(structure.flags, biosinfo.flags);
594    }
595
596    #[test]
597    fn add_one_dynamic_misaligned() {
598        add_one_dynamic(43);
599    }
600
601    #[test]
602    fn add_one_dynamic_aligned() {
603        add_one_dynamic(40);
604    }
605
606    #[test]
607    fn add_two() {
608        const LENGTH: usize = 93;
609        const PADDED_LENGTH: usize = 96;
610        let madt = vec![0xCC; LENGTH];
611        let procinfo = ProcessorInformation {
612            max_processor_count: 4,
613            processor_count: 3,
614            processors_per_virtual_socket: 2,
615            threads_per_processor: 1,
616        };
617
618        let data = {
619            let mut blob = Blob::new();
620            blob.add_raw(BlobStructureType::Madt, &madt).add(&procinfo);
621            blob.complete()
622        };
623
624        assert_eq!(data.len() % 8, 0);
625
626        let header: Header = read(&data[..]);
627        let structure: StructureCount = read(&data[size_of::<Header>()..]);
628
629        let header_exp = Header {
630            structure_type: 0x00,
631            length: (size_of::<Header>() + size_of::<StructureCount>()) as u32,
632        };
633        let structure_exp = StructureCount {
634            total_structure_count: 3,
635            total_config_blob_size: (3 * size_of::<Header>()
636                + size_of::<StructureCount>()
637                + PADDED_LENGTH
638                + size_of::<ProcessorInformation>()) as u32,
639        };
640
641        assert_eq!(header.structure_type, header_exp.structure_type);
642        assert_eq!(header.length, header_exp.length);
643        assert_eq!(
644            structure.total_structure_count,
645            structure_exp.total_structure_count
646        );
647        assert_eq!(
648            structure.total_config_blob_size,
649            structure_exp.total_config_blob_size
650        );
651
652        let header: Header = read(&data[size_of::<Header>() + size_of::<StructureCount>()..]);
653        let structure = &data[2 * size_of::<Header>() + size_of::<StructureCount>()..][..LENGTH];
654        let padding = &data[2 * size_of::<Header>() + size_of::<StructureCount>() + LENGTH..]
655            [..PADDED_LENGTH - LENGTH];
656
657        let header_exp = Header {
658            structure_type: 0x18,
659            length: (size_of::<Header>() + PADDED_LENGTH) as u32,
660        };
661        let structure_exp = &madt[..];
662
663        assert_eq!(header.structure_type, header_exp.structure_type);
664        assert_eq!(header.length, header_exp.length);
665        assert_eq!(structure.as_bytes(), structure_exp.as_bytes());
666        assert_eq!(padding, &[0; PADDED_LENGTH - LENGTH]);
667
668        let header: Header =
669            read(&data[2 * size_of::<Header>() + size_of::<StructureCount>() + PADDED_LENGTH..]);
670        let structure: ProcessorInformation =
671            read(&data[3 * size_of::<Header>() + size_of::<StructureCount>() + PADDED_LENGTH..]);
672
673        let header_exp = Header {
674            structure_type: 0x13,
675            length: (size_of::<Header>() + size_of::<ProcessorInformation>()) as u32,
676        };
677
678        assert_eq!(header.structure_type, header_exp.structure_type);
679        assert_eq!(header.length, header_exp.length);
680        assert_eq!(structure.max_processor_count, procinfo.max_processor_count);
681        assert_eq!(structure.processor_count, procinfo.processor_count);
682        assert_eq!(
683            structure.processors_per_virtual_socket,
684            procinfo.processors_per_virtual_socket
685        );
686        assert_eq!(
687            structure.threads_per_processor,
688            procinfo.threads_per_processor
689        );
690    }
691}