loader_defs/
shim.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Loader definitions for the openhcl boot loader (`openhcl_boot`).
5
6use open_enum::open_enum;
7use zerocopy::FromBytes;
8use zerocopy::Immutable;
9use zerocopy::IntoBytes;
10use zerocopy::KnownLayout;
11
12/// Shim parameters set by the loader at IGVM build time. These contain shim
13/// base relative offsets and sizes instead of absolute addresses. Sizes are in
14/// bytes.
15#[repr(C)]
16#[derive(Debug, IntoBytes, Immutable, KnownLayout, FromBytes)]
17pub struct ShimParamsRaw {
18    /// The offset to the Linux kernel entry point.
19    pub kernel_entry_offset: i64,
20    /// The offset to the [`crate::paravisor::ParavisorCommandLine`] structure.
21    pub cmdline_offset: i64,
22    /// The offset to the initrd.
23    pub initrd_offset: i64,
24    /// The size of the initrd.
25    pub initrd_size: u64,
26    /// The crc32 of the initrd.
27    pub initrd_crc: u32,
28    /// Isolation type supported by the igvm file.
29    pub supported_isolation_type: SupportedIsolationType,
30    /// The offset to the start of the VTL2 memory region.
31    pub memory_start_offset: i64,
32    /// The size of the VTL2 memory region.
33    pub memory_size: u64,
34    /// The offset to the parameter region.
35    pub parameter_region_offset: i64,
36    /// The size of the parameter region.
37    pub parameter_region_size: u64,
38    /// The offset to the VTL2 reserved region.
39    pub vtl2_reserved_region_offset: i64,
40    /// The size of the VTL2 reserved region.
41    pub vtl2_reserved_region_size: u64,
42    /// The offset to the sidecar memory region.
43    pub sidecar_offset: i64,
44    /// The size of the sidecar memory region.
45    pub sidecar_size: u64,
46    /// The offset to the entry point for the sidecar.
47    pub sidecar_entry_offset: i64,
48    /// The offset to the populated portion of VTL2 memory.
49    pub used_start: i64,
50    /// The offset to the end of the populated portion of VTL2 memory.
51    pub used_end: i64,
52    /// The offset to the bounce buffer range. This is 0 if unavailable.
53    pub bounce_buffer_start: i64,
54    /// The size of the bounce buffer range. This is 0 if unavailable.
55    pub bounce_buffer_size: u64,
56    /// The offset to the persisted bootshim log buffer.
57    pub log_buffer_start: i64,
58    /// The size of the persisted bootshim log buffer.
59    pub log_buffer_size: u64,
60    /// The offset to the start of the bootshim heap.
61    pub heap_start_offset: i64,
62    /// The size of the bootshim heap.
63    pub heap_size: u64,
64    /// The offset to the start of the supported persisted state region.
65    pub persisted_state_region_offset: i64,
66    /// The size of the supported persisted state region.
67    pub persisted_state_region_size: u64,
68}
69
70open_enum! {
71    /// Possible isolation types supported by the shim.
72    #[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
73    pub enum SupportedIsolationType: u32 {
74        // Starting from 1 for consistency with None usually being 0, but
75        // the IGVM file for None and Vbs will likely be the same, so None will
76        // not be enumerated here.At runtime, calls will be made to query
77        // the actual isolation type of the partition.
78        /// VBS-isolation is supported.
79        VBS = 1,
80        /// AMD SEV-SNP isolation is supported
81        SNP = 2,
82        /// Intel TDX isolation is supported
83        TDX = 3,
84    }
85}
86
87open_enum! {
88    /// The memory type reported from the bootshim to usermode, for which VTL a
89    /// given memory range is for.
90    #[derive(mesh_protobuf::Protobuf)]
91    #[mesh(package = "openhcl.openhcl_boot")]
92    pub enum MemoryVtlType: u32 {
93        /// This memory is for VTL0.
94        VTL0 = 0,
95        /// This memory is used by VTL2 as regular ram.
96        VTL2_RAM = 1,
97        /// This memory holds VTL2 config data, which is marked as reserved to
98        /// the kernel.
99        VTL2_CONFIG = 2,
100        /// This memory is used by the VTL2 sidecar as it's image, and is marked
101        /// as reserved to the kernel.
102        VTL2_SIDECAR_IMAGE = 3,
103        /// This memory is used by the VTL2 sidecar as node memory, and is
104        /// marked as reserved to the kernel.
105        VTL2_SIDECAR_NODE = 4,
106        /// This range is mmio for VTL0.
107        VTL0_MMIO = 5,
108        /// This range is mmio for VTL2.
109        VTL2_MMIO = 6,
110        /// This memory holds VTL2 data which should be preserved by the kernel
111        /// and usermode. Today, this is only used for SNP: VMSA, CPUID pages,
112        /// and secrets pages.
113        VTL2_RESERVED = 7,
114        /// This memory is used by VTL2 usermode as a persisted GPA page pool.
115        /// This memory is part of VTL2's address space, not VTL0's. It is
116        /// marked as reserved to the kernel.
117        VTL2_GPA_POOL = 8,
118        /// This memory is used by VTL2 for TDX AP startup page tables, and is
119        /// marked as reserved to the kernel.
120        VTL2_TDX_PAGE_TABLES = 9,
121        /// This memory is used by VTL2 to store in-memory bootshim logs. It is
122        /// marked as reserved to the kernel.
123        VTL2_BOOTSHIM_LOG_BUFFER = 10,
124        /// This memory is used by VTL2 to store a persisted state header. This
125        /// memory is marked as reserved to the kernel.
126        VTL2_PERSISTED_STATE_HEADER = 11,
127        /// This memory is used by VTL2 to store the persisted protobuf payload.
128        /// This memory is marked as reserved to the kernel.
129        VTL2_PERSISTED_STATE_PROTOBUF = 12,
130    }
131}
132
133impl MemoryVtlType {
134    /// Returns true if this range is a ram type.
135    pub fn ram(&self) -> bool {
136        matches!(
137            *self,
138            MemoryVtlType::VTL0
139                | MemoryVtlType::VTL2_RAM
140                | MemoryVtlType::VTL2_CONFIG
141                | MemoryVtlType::VTL2_SIDECAR_IMAGE
142                | MemoryVtlType::VTL2_SIDECAR_NODE
143                | MemoryVtlType::VTL2_RESERVED
144                | MemoryVtlType::VTL2_GPA_POOL
145                | MemoryVtlType::VTL2_TDX_PAGE_TABLES
146                | MemoryVtlType::VTL2_BOOTSHIM_LOG_BUFFER
147                | MemoryVtlType::VTL2_PERSISTED_STATE_HEADER
148                | MemoryVtlType::VTL2_PERSISTED_STATE_PROTOBUF
149        )
150    }
151
152    /// Returns true if this range is used by VTL2.
153    pub fn vtl2(&self) -> bool {
154        matches!(
155            *self,
156            MemoryVtlType::VTL2_RAM
157                | MemoryVtlType::VTL2_CONFIG
158                | MemoryVtlType::VTL2_SIDECAR_IMAGE
159                | MemoryVtlType::VTL2_SIDECAR_NODE
160                | MemoryVtlType::VTL2_MMIO
161                | MemoryVtlType::VTL2_RESERVED
162                | MemoryVtlType::VTL2_GPA_POOL
163                | MemoryVtlType::VTL2_TDX_PAGE_TABLES
164                | MemoryVtlType::VTL2_BOOTSHIM_LOG_BUFFER
165                | MemoryVtlType::VTL2_PERSISTED_STATE_HEADER
166                | MemoryVtlType::VTL2_PERSISTED_STATE_PROTOBUF
167        )
168    }
169}
170
171/// This structure describes the initial state of the TD VP. When a VP (both BSP and AP)
172/// starts at ResetVector (RV), this is loaded at the beginning of the RV page.
173/// Fields in the trampoline context must be loaded from memory by the
174/// trampoline code.
175///
176/// Note that this trampoline context must also be used for bringing up APs, as
177/// the code placed in the reset vector will use this format to figure out what
178/// register state to load.
179#[repr(C)]
180#[derive(Debug, Default, Clone, Copy, IntoBytes, Immutable)]
181pub struct TdxTrampolineContext {
182    /// Mailbox command
183    pub mailbox_command: u16,
184    /// Reserved
185    pub mailbox_reserved: u16,
186    /// Mailbox APIC ID
187    pub mailbox_apic_id: u32,
188    /// AP wakeup vector
189    pub mailbox_wakeup_vector: u64,
190    /// Padding
191    pub padding_1: u32,
192    /// Data selector
193    pub data_selector: u16,
194    /// Static GDT limit
195    pub static_gdt_limit: u16,
196    /// Static GDT base
197    pub static_gdt_base: u32,
198    /// Task selector
199    pub task_selector: u16,
200    /// IDTR limit
201    pub idtr_limit: u16,
202    /// IDTR base
203    pub idtr_base: u64,
204    /// Initial RIP
205    pub initial_rip: u64,
206    /// CS
207    pub code_selector: u16,
208    /// Padding
209    pub padding_2: [u16; 2],
210    /// GDTR limit
211    pub gdtr_limit: u16,
212    /// GDTR base
213    pub gdtr_base: u64,
214    /// RSP
215    pub rsp: u64,
216    /// RBP
217    pub rbp: u64,
218    /// RSI
219    pub rsi: u64,
220    /// R8
221    pub r8: u64,
222    /// R9
223    pub r9: u64,
224    /// R10
225    pub r10: u64,
226    /// R11
227    pub r11: u64,
228    /// CR0
229    pub cr0: u64,
230    /// CR3
231    pub cr3: u64,
232    /// CR4
233    pub cr4: u64,
234    /// Transistion CR3
235    pub transition_cr3: u32,
236    /// Padding
237    pub padding_3: u32,
238    /// Statuc GDT
239    pub static_gdt: [u8; 16],
240}
241
242/// This is the header used to describe the overall persisted state region. By
243/// convention, the header resides at the start of VTL2 memory, taking a single
244/// page.
245///
246/// This header should never change, instead for new information to be stored
247/// add it to the protobuf payload described below.
248#[repr(C)]
249#[derive(Debug, IntoBytes, Immutable, KnownLayout, FromBytes)]
250pub struct PersistedStateHeader {
251    /// A magic value. If this is not set to [`PersistedStateHeader::MAGIC`],
252    /// then the previous instance did not support this region.
253    pub magic: u64,
254    /// The gpa for the start of the protobuf region. This must be 4K aligned.
255    pub protobuf_base: u64,
256    /// The size of the protobuf region in bytes.
257    pub protobuf_region_len: u64,
258    /// The size of the protobuf payload in bytes.
259    /// This must be less than or equal to `protobuf_region_len`.
260    pub protobuf_payload_len: u64,
261}
262
263impl PersistedStateHeader {
264    /// "OHCLPHDR" in ASCII.
265    pub const MAGIC: u64 = u64::from_le_bytes(*b"OHCLPHDR");
266}
267
268/// Definitions used for save/restore between boots.
269pub mod save_restore {
270    extern crate alloc;
271
272    use super::MemoryVtlType;
273    use alloc::vec::Vec;
274    use memory_range::MemoryRange;
275
276    /// A local newtype wrapper that represents a [`igvm_defs::MemoryMapEntryType`].
277    ///
278    /// This is required to make it protobuf deriveable.
279    #[derive(mesh_protobuf::Protobuf, Clone, Debug, PartialEq)]
280    #[mesh(package = "openhcl.openhcl_boot")]
281    pub struct IgvmMemoryType(#[mesh(1)] u16);
282
283    impl From<igvm_defs::MemoryMapEntryType> for IgvmMemoryType {
284        fn from(igvm_type: igvm_defs::MemoryMapEntryType) -> Self {
285            Self(igvm_type.0)
286        }
287    }
288
289    impl From<IgvmMemoryType> for igvm_defs::MemoryMapEntryType {
290        fn from(igvm_type: IgvmMemoryType) -> Self {
291            igvm_defs::MemoryMapEntryType(igvm_type.0)
292        }
293    }
294
295    /// A memory entry describing what range of address space described as memory is
296    /// used for what.
297    #[derive(mesh_protobuf::Protobuf, Debug)]
298    #[mesh(package = "openhcl.openhcl_boot")]
299    pub struct MemoryEntry {
300        /// The range of memory.
301        #[mesh(1)]
302        pub range: MemoryRange,
303        /// The numa vnode for this range.
304        #[mesh(2)]
305        pub vnode: u32,
306        /// The VTL type for this range.
307        #[mesh(3)]
308        pub vtl_type: MemoryVtlType,
309        /// The IGVM type for this range, which was reported by the host originally.
310        #[mesh(4)]
311        pub igvm_type: IgvmMemoryType,
312    }
313
314    /// A mmio entry describing what range of address space described as mmio is
315    /// used for what.
316    #[derive(mesh_protobuf::Protobuf, Debug)]
317    #[mesh(package = "openhcl.openhcl_boot")]
318    pub struct MmioEntry {
319        /// The range of mmio.
320        #[mesh(1)]
321        pub range: MemoryRange,
322        /// The VTL type for this range, which should always be an mmio type.
323        #[mesh(2)]
324        pub vtl_type: MemoryVtlType,
325    }
326
327    /// The format for saved state between the previous instance of OpenHCL and the
328    /// next.
329    #[derive(mesh_protobuf::Protobuf, Debug)]
330    #[mesh(package = "openhcl.openhcl_boot")]
331    pub struct SavedState {
332        /// The memory entries describing memory for the whole partition.
333        #[mesh(1)]
334        pub partition_memory: Vec<MemoryEntry>,
335        /// The mmio entries describing mmio for the whole partition.
336        #[mesh(2)]
337        pub partition_mmio: Vec<MmioEntry>,
338        /// The list of CPUs with mapped device interrupts present at save time
339        /// that do not have outstanding IO (those CPUs are counted in
340        /// `cpus_with_outstanding_io`).
341        ///
342        /// DEFAULT: For save state from prior versions, this will be empty.
343        /// This is fine: the restore heuristics might be less optimal, but will
344        /// still be functionally correct.
345        #[mesh(3)]
346        pub cpus_with_mapped_interrupts_no_io: Vec<u32>,
347        /// The list of CPUs with mapped device interrupts present at save time,
348        /// and that also have outstanding IO on that CPU.
349        ///
350        /// DEFAULT: For save state from prior versions, this will be empty.
351        /// This is fine: the restore heuristics might be less optimal, but will
352        /// still be functionally correct.
353        #[mesh(4)]
354        pub cpus_with_outstanding_io: Vec<u32>,
355    }
356}