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