Skip to main content

loader/
importer.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Common loader image loading traits and types used by all loaders.
5
6#[cfg(guest_arch = "aarch64")]
7pub use Aarch64Register as Register;
8#[cfg(guest_arch = "x86_64")]
9pub use X86Register as Register;
10
11/// The page acceptance used for importing pages into the initial launch context
12/// of the guest.
13#[derive(Debug, PartialEq, Eq, Clone, Copy)]
14pub enum BootPageAcceptance {
15    /// The page is accepted exclusive (no host visibility) and the page data is
16    /// measured.
17    Exclusive,
18    /// The page is accepted exclusive (no host visibility) and the page data is
19    /// unmeasured.
20    ExclusiveUnmeasured,
21    /// The page contains hardware-specific VP context information.
22    VpContext,
23    /// This page communicates hardware-specific secret information and the page
24    /// data is unmeasured.
25    SecretsPage,
26    /// This page includes guest-specified CPUID information.
27    CpuidPage,
28    /// This page should include the enumeration of extended state CPUID leaves.
29    CpuidExtendedStatePage,
30    /// This page is host visible and contains valid data. The page has not been accepted.
31    Shared,
32}
33
34/// The guest isolation type of the platform.
35#[derive(Debug, PartialEq, Eq)]
36pub enum IsolationType {
37    /// No isolation is in use by this guest.
38    None,
39    /// This guest is isolated with VBS.
40    Vbs,
41    /// This guest is isolated with SNP (physical or emulated).
42    Snp,
43    /// This guest is isolated with TDX (physical or emulated).
44    Tdx,
45}
46
47/// The startup memory type used to notify a well behaved host that memory
48/// should be present before attempting to start the guest.
49#[derive(Debug, PartialEq, Eq)]
50pub enum StartupMemoryType {
51    /// The range is normal memory.
52    Ram,
53    /// The range is normal memory that additionally can have VTL2 protections
54    /// applied by the guest.
55    Vtl2ProtectableRam,
56}
57
58/// An x86 Table register, like GDTR.
59#[derive(Copy, Clone, Debug, PartialEq, Eq)]
60pub struct TableRegister {
61    pub base: u64,
62    pub limit: u16,
63}
64
65impl From<igvm::registers::TableRegister> for TableRegister {
66    fn from(value: igvm::registers::TableRegister) -> Self {
67        Self {
68            base: value.base,
69            limit: value.limit,
70        }
71    }
72}
73
74impl From<TableRegister> for igvm::registers::TableRegister {
75    fn from(value: TableRegister) -> Self {
76        Self {
77            base: value.base,
78            limit: value.limit,
79        }
80    }
81}
82
83/// An x86 Segment Register, used for the segment selectors.
84#[derive(Copy, Clone, Debug, PartialEq, Eq)]
85pub struct SegmentRegister {
86    pub base: u64,
87    pub limit: u32,
88    pub selector: u16,
89    pub attributes: u16,
90}
91
92impl From<igvm::registers::SegmentRegister> for SegmentRegister {
93    fn from(value: igvm::registers::SegmentRegister) -> Self {
94        Self {
95            base: value.base,
96            limit: value.limit,
97            selector: value.selector,
98            attributes: value.attributes,
99        }
100    }
101}
102
103impl From<SegmentRegister> for igvm::registers::SegmentRegister {
104    fn from(value: SegmentRegister) -> Self {
105        Self {
106            base: value.base,
107            limit: value.limit,
108            selector: value.selector,
109            attributes: value.attributes,
110        }
111    }
112}
113
114/// x86 registers that can be loaded via [ImageLoad::import_vp_register]
115#[derive(Debug, Clone, Copy, PartialEq, Eq)]
116pub enum X86Register {
117    Gdtr(TableRegister),
118    Idtr(TableRegister),
119    Ds(SegmentRegister),
120    Es(SegmentRegister),
121    Fs(SegmentRegister),
122    Gs(SegmentRegister),
123    Ss(SegmentRegister),
124    Cs(SegmentRegister),
125    Tr(SegmentRegister),
126    Cr0(u64),
127    Cr3(u64),
128    Cr4(u64),
129    Efer(u64),
130    Pat(u64),
131    Rbp(u64),
132    Rip(u64),
133    Rsi(u64),
134    Rsp(u64),
135    R8(u64),
136    R9(u64),
137    R10(u64),
138    R11(u64),
139    R12(u64),
140    Rflags(u64),
141    MtrrDefType(u64),
142    MtrrPhysBase0(u64),
143    MtrrPhysMask0(u64),
144    MtrrPhysBase1(u64),
145    MtrrPhysMask1(u64),
146    MtrrPhysBase2(u64),
147    MtrrPhysMask2(u64),
148    MtrrPhysBase3(u64),
149    MtrrPhysMask3(u64),
150    MtrrPhysBase4(u64),
151    MtrrPhysMask4(u64),
152    MtrrFix64k00000(u64),
153    MtrrFix16k80000(u64),
154    // We do not currently have a need for the middle fixed MTRRs.
155    MtrrFix4kE0000(u64),
156    MtrrFix4kE8000(u64),
157    MtrrFix4kF0000(u64),
158    MtrrFix4kF8000(u64),
159}
160
161impl From<igvm::registers::X86Register> for X86Register {
162    fn from(value: igvm::registers::X86Register) -> Self {
163        use igvm::registers::X86Register as igvm_reg;
164        match value {
165            igvm_reg::Gdtr(v) => X86Register::Gdtr(v.into()),
166            igvm_reg::Idtr(v) => X86Register::Idtr(v.into()),
167            igvm_reg::Ds(v) => X86Register::Ds(v.into()),
168            igvm_reg::Es(v) => X86Register::Es(v.into()),
169            igvm_reg::Fs(v) => X86Register::Fs(v.into()),
170            igvm_reg::Gs(v) => X86Register::Gs(v.into()),
171            igvm_reg::Ss(v) => X86Register::Ss(v.into()),
172            igvm_reg::Cs(v) => X86Register::Cs(v.into()),
173            igvm_reg::Tr(v) => X86Register::Tr(v.into()),
174            igvm_reg::Cr0(v) => X86Register::Cr0(v),
175            igvm_reg::Cr3(v) => X86Register::Cr3(v),
176            igvm_reg::Cr4(v) => X86Register::Cr4(v),
177            igvm_reg::Efer(v) => X86Register::Efer(v),
178            igvm_reg::Pat(v) => X86Register::Pat(v),
179            igvm_reg::Rbp(v) => X86Register::Rbp(v),
180            igvm_reg::Rip(v) => X86Register::Rip(v),
181            igvm_reg::Rsi(v) => X86Register::Rsi(v),
182            igvm_reg::Rsp(v) => X86Register::Rsp(v),
183            igvm_reg::R8(v) => X86Register::R8(v),
184            igvm_reg::R9(v) => X86Register::R9(v),
185            igvm_reg::R10(v) => X86Register::R10(v),
186            igvm_reg::R11(v) => X86Register::R11(v),
187            igvm_reg::R12(v) => X86Register::R12(v),
188            igvm_reg::Rflags(v) => X86Register::Rflags(v),
189            igvm_reg::MtrrDefType(v) => X86Register::MtrrDefType(v),
190            igvm_reg::MtrrPhysBase0(v) => X86Register::MtrrPhysBase0(v),
191            igvm_reg::MtrrPhysMask0(v) => X86Register::MtrrPhysMask0(v),
192            igvm_reg::MtrrPhysBase1(v) => X86Register::MtrrPhysBase1(v),
193            igvm_reg::MtrrPhysMask1(v) => X86Register::MtrrPhysMask1(v),
194            igvm_reg::MtrrPhysBase2(v) => X86Register::MtrrPhysBase2(v),
195            igvm_reg::MtrrPhysMask2(v) => X86Register::MtrrPhysMask2(v),
196            igvm_reg::MtrrPhysBase3(v) => X86Register::MtrrPhysBase3(v),
197            igvm_reg::MtrrPhysMask3(v) => X86Register::MtrrPhysMask3(v),
198            igvm_reg::MtrrPhysBase4(v) => X86Register::MtrrPhysBase4(v),
199            igvm_reg::MtrrPhysMask4(v) => X86Register::MtrrPhysMask4(v),
200            igvm_reg::MtrrFix64k00000(v) => X86Register::MtrrFix64k00000(v),
201            igvm_reg::MtrrFix16k80000(v) => X86Register::MtrrFix16k80000(v),
202            igvm_reg::MtrrFix4kE0000(v) => X86Register::MtrrFix4kE0000(v),
203            igvm_reg::MtrrFix4kE8000(v) => X86Register::MtrrFix4kE8000(v),
204            igvm_reg::MtrrFix4kF0000(v) => X86Register::MtrrFix4kF0000(v),
205            igvm_reg::MtrrFix4kF8000(v) => X86Register::MtrrFix4kF8000(v),
206        }
207    }
208}
209
210impl From<X86Register> for igvm::registers::X86Register {
211    fn from(value: X86Register) -> Self {
212        use igvm::registers::X86Register as igvm_reg;
213        match value {
214            X86Register::Gdtr(v) => igvm_reg::Gdtr(v.into()),
215            X86Register::Idtr(v) => igvm_reg::Idtr(v.into()),
216            X86Register::Ds(v) => igvm_reg::Ds(v.into()),
217            X86Register::Es(v) => igvm_reg::Es(v.into()),
218            X86Register::Fs(v) => igvm_reg::Fs(v.into()),
219            X86Register::Gs(v) => igvm_reg::Gs(v.into()),
220            X86Register::Ss(v) => igvm_reg::Ss(v.into()),
221            X86Register::Cs(v) => igvm_reg::Cs(v.into()),
222            X86Register::Tr(v) => igvm_reg::Tr(v.into()),
223            X86Register::Cr0(v) => igvm_reg::Cr0(v),
224            X86Register::Cr3(v) => igvm_reg::Cr3(v),
225            X86Register::Cr4(v) => igvm_reg::Cr4(v),
226            X86Register::Efer(v) => igvm_reg::Efer(v),
227            X86Register::Pat(v) => igvm_reg::Pat(v),
228            X86Register::Rbp(v) => igvm_reg::Rbp(v),
229            X86Register::Rip(v) => igvm_reg::Rip(v),
230            X86Register::Rsi(v) => igvm_reg::Rsi(v),
231            X86Register::Rsp(v) => igvm_reg::Rsp(v),
232            X86Register::R8(v) => igvm_reg::R8(v),
233            X86Register::R9(v) => igvm_reg::R9(v),
234            X86Register::R10(v) => igvm_reg::R10(v),
235            X86Register::R11(v) => igvm_reg::R11(v),
236            X86Register::R12(v) => igvm_reg::R12(v),
237            X86Register::Rflags(v) => igvm_reg::Rflags(v),
238            X86Register::MtrrDefType(v) => igvm_reg::MtrrDefType(v),
239            X86Register::MtrrPhysBase0(v) => igvm_reg::MtrrPhysBase0(v),
240            X86Register::MtrrPhysMask0(v) => igvm_reg::MtrrPhysMask0(v),
241            X86Register::MtrrPhysBase1(v) => igvm_reg::MtrrPhysBase1(v),
242            X86Register::MtrrPhysMask1(v) => igvm_reg::MtrrPhysMask1(v),
243            X86Register::MtrrPhysBase2(v) => igvm_reg::MtrrPhysBase2(v),
244            X86Register::MtrrPhysMask2(v) => igvm_reg::MtrrPhysMask2(v),
245            X86Register::MtrrPhysBase3(v) => igvm_reg::MtrrPhysBase3(v),
246            X86Register::MtrrPhysMask3(v) => igvm_reg::MtrrPhysMask3(v),
247            X86Register::MtrrPhysBase4(v) => igvm_reg::MtrrPhysBase4(v),
248            X86Register::MtrrPhysMask4(v) => igvm_reg::MtrrPhysMask4(v),
249            X86Register::MtrrFix64k00000(v) => igvm_reg::MtrrFix64k00000(v),
250            X86Register::MtrrFix16k80000(v) => igvm_reg::MtrrFix16k80000(v),
251            X86Register::MtrrFix4kE0000(v) => igvm_reg::MtrrFix4kE0000(v),
252            X86Register::MtrrFix4kE8000(v) => igvm_reg::MtrrFix4kE8000(v),
253            X86Register::MtrrFix4kF0000(v) => igvm_reg::MtrrFix4kF0000(v),
254            X86Register::MtrrFix4kF8000(v) => igvm_reg::MtrrFix4kF8000(v),
255        }
256    }
257}
258
259#[derive(Debug, Clone, Copy, PartialEq, Eq)]
260pub enum Aarch64Register {
261    Pc(u64),
262    X0(u64),
263    X1(u64),
264    X2(u64),
265    X3(u64),
266    X4(u64),
267    X5(u64),
268    X6(u64),
269    X7(u64),
270    Cpsr(u64),
271    VbarEl1(u64),
272    Ttbr0El1(u64),
273    Ttbr1El1(u64),
274    MairEl1(u64),
275    SctlrEl1(u64),
276    TcrEl1(u64),
277}
278
279impl From<igvm::registers::AArch64Register> for Aarch64Register {
280    fn from(value: igvm::registers::AArch64Register) -> Self {
281        use igvm::registers::AArch64Register as igvm_reg;
282        match value {
283            igvm_reg::Pc(v) => Aarch64Register::Pc(v),
284            igvm_reg::X0(v) => Aarch64Register::X0(v),
285            igvm_reg::X1(v) => Aarch64Register::X1(v),
286            igvm_reg::X2(v) => Aarch64Register::X2(v),
287            igvm_reg::X3(v) => Aarch64Register::X3(v),
288            igvm_reg::X4(v) => Aarch64Register::X4(v),
289            igvm_reg::X5(v) => Aarch64Register::X5(v),
290            igvm_reg::X6(v) => Aarch64Register::X6(v),
291            igvm_reg::X7(v) => Aarch64Register::X7(v),
292            igvm_reg::Cpsr(v) => Aarch64Register::Cpsr(v),
293            igvm_reg::SctlrEl1(v) => Aarch64Register::SctlrEl1(v),
294            igvm_reg::TcrEl1(v) => Aarch64Register::TcrEl1(v),
295            igvm_reg::MairEl1(v) => Aarch64Register::MairEl1(v),
296            igvm_reg::VbarEl1(v) => Aarch64Register::VbarEl1(v),
297            igvm_reg::Ttbr0El1(v) => Aarch64Register::Ttbr0El1(v),
298            igvm_reg::Ttbr1El1(v) => Aarch64Register::Ttbr1El1(v),
299        }
300    }
301}
302
303impl From<Aarch64Register> for igvm::registers::AArch64Register {
304    fn from(value: Aarch64Register) -> Self {
305        use igvm::registers::AArch64Register as igvm_reg;
306        match value {
307            Aarch64Register::Pc(v) => igvm_reg::Pc(v),
308            Aarch64Register::X0(v) => igvm_reg::X0(v),
309            Aarch64Register::X1(v) => igvm_reg::X1(v),
310            Aarch64Register::X2(v) => igvm_reg::X2(v),
311            Aarch64Register::X3(v) => igvm_reg::X3(v),
312            Aarch64Register::X4(v) => igvm_reg::X4(v),
313            Aarch64Register::X5(v) => igvm_reg::X5(v),
314            Aarch64Register::X6(v) => igvm_reg::X6(v),
315            Aarch64Register::X7(v) => igvm_reg::X7(v),
316            Aarch64Register::Cpsr(v) => igvm_reg::Cpsr(v),
317            Aarch64Register::SctlrEl1(v) => igvm_reg::SctlrEl1(v),
318            Aarch64Register::TcrEl1(v) => igvm_reg::TcrEl1(v),
319            Aarch64Register::MairEl1(v) => igvm_reg::MairEl1(v),
320            Aarch64Register::VbarEl1(v) => igvm_reg::VbarEl1(v),
321            Aarch64Register::Ttbr0El1(v) => igvm_reg::Ttbr0El1(v),
322            Aarch64Register::Ttbr1El1(v) => igvm_reg::Ttbr1El1(v),
323        }
324    }
325}
326
327/// Isolation information returned by the importer to loaders.
328#[derive(Debug)]
329pub struct IsolationConfig {
330    /// True if VTL2 is enabled, representing a paravisor present.
331    pub paravisor_present: bool,
332
333    /// The isolation type of the platform.
334    pub isolation_type: IsolationType,
335
336    /// If there is a shared gpa boundary, the number of bits.
337    pub shared_gpa_boundary_bits: Option<u8>,
338}
339
340#[derive(Debug, Default)]
341pub struct CpuidResult {
342    pub eax: u32,
343    pub ebx: u32,
344    pub ecx: u32,
345    pub edx: u32,
346}
347
348impl IsolationConfig {
349    /// Get the isolation config in the format of a CPUID result.
350    pub fn get_cpuid(&self) -> CpuidResult {
351        // See HV_HYPERVISOR_ISOLATION_CONFIGURATION for format info.
352        let eax = if self.paravisor_present { 1 } else { 0 };
353
354        let mut ebx = 0;
355        match self.isolation_type {
356            IsolationType::None => {}
357            IsolationType::Vbs => ebx = hvdef::HvPartitionIsolationType::VBS.0 as _,
358            IsolationType::Snp => ebx = hvdef::HvPartitionIsolationType::SNP.0 as _,
359            IsolationType::Tdx => ebx = hvdef::HvPartitionIsolationType::TDX.0 as _,
360        }
361
362        match self.shared_gpa_boundary_bits {
363            None => {}
364            Some(bits) => {
365                ebx |= 1 << 5;
366                ebx |= ((bits & 0x3F) as u32) << 6;
367            }
368        }
369
370        CpuidResult {
371            eax,
372            ebx,
373            ecx: 0,
374            edx: 0,
375        }
376    }
377}
378
379#[derive(Debug)]
380pub enum IgvmParameterType {
381    VpCount,
382    Srat,
383    Madt,
384    MmioRanges,
385    MemoryMap,
386    CommandLine,
387    Slit,
388    Pptt,
389    DeviceTree,
390}
391
392#[repr(transparent)]
393#[derive(Debug, Clone, Copy)]
394pub struct ParameterAreaIndex(pub u32);
395
396#[derive(Debug, Clone, Copy, PartialEq, Eq)]
397pub enum GuestArchKind {
398    X86_64,
399    Aarch64,
400}
401
402pub trait GuestArch {
403    fn arch() -> GuestArchKind;
404}
405
406impl GuestArch for X86Register {
407    fn arch() -> GuestArchKind {
408        GuestArchKind::X86_64
409    }
410}
411
412impl GuestArch for Aarch64Register {
413    fn arch() -> GuestArchKind {
414        GuestArchKind::Aarch64
415    }
416}
417
418pub trait ImageLoad<R>
419where
420    R: GuestArch,
421{
422    /// Get the isolation configuration for this loader. This can be used by loaders
423    /// to load different state depending on the platform.
424    fn isolation_config(&self) -> IsolationConfig;
425
426    /// Create a parameter area for the given page_base and page_count, which
427    /// can be used to import parameters.
428    ///
429    /// `debug_tag` is a human readable string used by the loader to identify
430    /// this region for debugging and reporting.
431    fn create_parameter_area(
432        &mut self,
433        page_base: u64,
434        page_count: u32,
435        debug_tag: &str,
436    ) -> anyhow::Result<ParameterAreaIndex>;
437
438    /// Create a parameter area for the given page_base, page_count, and initial_data
439    /// which can be used to import parameters.
440    ///
441    /// `debug_tag` is a human readable string used by the loader to identify
442    /// this region for debugging and reporting.
443    fn create_parameter_area_with_data(
444        &mut self,
445        page_base: u64,
446        page_count: u32,
447        debug_tag: &str,
448        initial_data: &[u8],
449    ) -> anyhow::Result<ParameterAreaIndex>;
450
451    /// Import an IGVM parameter into the given parameter area index at the given offset.
452    ///
453    /// IGVM Parameters are used to specify where OS agnostic runtime dynamic information
454    /// should be loaded into the guest memory space. This allows loaders to load a base IGVM
455    /// file with a given measurement that can be specialized with runtime unmeasured parameters.
456    fn import_parameter(
457        &mut self,
458        parameter_area: ParameterAreaIndex,
459        byte_offset: u32,
460        parameter_type: IgvmParameterType,
461    ) -> anyhow::Result<()>;
462
463    /// Import data into the guest address space with the given acceptance type.
464    /// data.len() must be smaller than or equal to the number of pages being imported.
465    ///
466    /// `debug_tag` is a human readable string used by the loader to identify
467    /// this region for debugging and reporting.
468    fn import_pages(
469        &mut self,
470        page_base: u64,
471        page_count: u64,
472        debug_tag: &'static str,
473        acceptance: BootPageAcceptance,
474        data: &[u8],
475    ) -> anyhow::Result<()>;
476
477    /// Import a register into the BSP.
478    fn import_vp_register(&mut self, register: R) -> anyhow::Result<()>;
479
480    /// Verify with the loader that memory is available in guest address space with the given type.
481    fn verify_startup_memory_available(
482        &mut self,
483        page_base: u64,
484        page_count: u64,
485        memory_type: StartupMemoryType,
486    ) -> anyhow::Result<()>;
487
488    /// Notify the loader to deposit architecture specific VP context information at the given page.
489    ///
490    /// TODO: It probably makes sense to use a different acceptance type than the default one?
491    fn set_vp_context_page(&mut self, page_base: u64) -> anyhow::Result<()>;
492
493    /// Specify this region as relocatable.
494    fn relocation_region(
495        &mut self,
496        gpa: u64,
497        size_bytes: u64,
498        relocation_alignment: u64,
499        minimum_relocation_gpa: u64,
500        maximum_relocation_gpa: u64,
501        apply_rip_offset: bool,
502        apply_gdtr_offset: bool,
503        vp_index: u16,
504    ) -> anyhow::Result<()>;
505
506    /// Specify a region as relocatable page table memory.
507    fn page_table_relocation(
508        &mut self,
509        page_table_gpa: u64,
510        size_pages: u64,
511        used_pages: u64,
512        vp_index: u16,
513    ) -> anyhow::Result<()>;
514
515    /// Lets the loader know what the base page of where the config page
516    /// containing list of accepted regions should be. This list should contain
517    /// the pages that will be accepted by the loader and therefore should not
518    /// be accepted again by either the boot shim or the vtl 2 firmware. The
519    /// list will be sorted in ascending order (on the base page) and be an
520    /// array of non-overlapping
521    /// [`loader_defs::paravisor::ImportedRegionDescriptor`]. A
522    /// [`loader_defs::paravisor::ImportedRegionDescriptor`] with a page count
523    /// of 0 indicates the end of the list.
524    fn set_imported_regions_config_page(&mut self, page_base: u64);
525
526    /// Lets the loader know the base page of the measured region that will
527    /// carry an [`loader_defs::paravisor::ExpectedPageHashesHeader`] followed
528    /// by an array of
529    /// [`loader_defs::paravisor::ExpectedPageHash`] entries -- one SHA-384
530    /// per unmeasured (shared) 4 KB page, in the same order the boot shim
531    /// walks `imported_regions().filter(!already_accepted)`. Lets the boot
532    /// shim identify which individual pages diverged from the measured
533    /// baseline (rather than only knowing "the combined hash was wrong").
534    ///
535    /// Default implementation is a no-op: only the IGVM file loader
536    /// materially populates this region; runtime loaders that don't emit an
537    /// IGVM can ignore it.
538    fn set_expected_page_hashes_config_page(&mut self, _page_base: u64) {}
539}