loader_defs/
paravisor.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Underhill (paravisor) definitions.
5
6use bitfield_struct::bitfield;
7use core::mem::size_of;
8use hvdef::HV_PAGE_SIZE;
9#[cfg(feature = "inspect")]
10use inspect::Inspect;
11use open_enum::open_enum;
12use static_assertions::const_assert_eq;
13use zerocopy::FromBytes;
14use zerocopy::Immutable;
15use zerocopy::IntoBytes;
16use zerocopy::KnownLayout;
17
18// Number of pages for each type of parameter in the vtl 2 unmeasured config
19// region.
20/// Size in pages for the SLIT.
21pub const PARAVISOR_CONFIG_SLIT_SIZE_PAGES: u64 = 20;
22/// Size in pages for the PPTT.
23pub const PARAVISOR_CONFIG_PPTT_SIZE_PAGES: u64 = 20;
24/// Size in pages for the device tree.
25pub const PARAVISOR_CONFIG_DEVICE_TREE_SIZE_PAGES: u64 = 64;
26
27/// The maximum size in pages of the unmeasured vtl 2 config region.
28pub const PARAVISOR_UNMEASURED_VTL2_CONFIG_REGION_PAGE_COUNT_MAX: u64 =
29    PARAVISOR_CONFIG_SLIT_SIZE_PAGES
30        + PARAVISOR_CONFIG_PPTT_SIZE_PAGES
31        + PARAVISOR_CONFIG_DEVICE_TREE_SIZE_PAGES;
32
33// Page indices for different parameters within the unmeasured vtl 2 config region.
34/// The page index to the SLIT.
35pub const PARAVISOR_CONFIG_SLIT_PAGE_INDEX: u64 = 0;
36/// The page index to the PPTT.
37pub const PARAVISOR_CONFIG_PPTT_PAGE_INDEX: u64 =
38    PARAVISOR_CONFIG_SLIT_PAGE_INDEX + PARAVISOR_CONFIG_SLIT_SIZE_PAGES;
39/// The page index to the device tree.
40pub const PARAVISOR_CONFIG_DEVICE_TREE_PAGE_INDEX: u64 =
41    PARAVISOR_CONFIG_PPTT_PAGE_INDEX + PARAVISOR_CONFIG_PPTT_SIZE_PAGES;
42/// Base index for the unmeasured vtl 2 config region
43pub const PARAVISOR_UNMEASURED_VTL2_CONFIG_REGION_BASE_INDEX: u64 =
44    PARAVISOR_CONFIG_SLIT_PAGE_INDEX;
45
46/// Size in pages for the SNP CPUID pages.
47pub const PARAVISOR_RESERVED_VTL2_SNP_CPUID_SIZE_PAGES: u64 = 2;
48/// Size in pages for the VMSA page.
49pub const PARAVISOR_RESERVED_VTL2_SNP_VMSA_SIZE_PAGES: u64 = 1;
50/// Size in pages for the secrets page.
51pub const PARAVISOR_RESERVED_VTL2_SNP_SECRETS_SIZE_PAGES: u64 = 1;
52
53/// Total size of the reserved vtl2 range.
54pub const PARAVISOR_RESERVED_VTL2_PAGE_COUNT_MAX: u64 = PARAVISOR_RESERVED_VTL2_SNP_CPUID_SIZE_PAGES
55    + PARAVISOR_RESERVED_VTL2_SNP_VMSA_SIZE_PAGES
56    + PARAVISOR_RESERVED_VTL2_SNP_SECRETS_SIZE_PAGES;
57
58// Page indices for reserved vtl2 ranges, ranges that are marked as reserved to
59// both the kernel and usermode. Today, these are SNP specific pages.
60//
61// TODO SNP: Does the kernel require that the CPUID and secrets pages are
62// persisted, or after the kernel boots, and usermode reads them, can we discard
63// them?
64//
65/// The page index to the SNP VMSA page.
66pub const PARAVISOR_RESERVED_VTL2_SNP_VMSA_PAGE_INDEX: u64 = 0;
67/// The page index to the first SNP CPUID page.
68pub const PARAVISOR_RESERVED_VTL2_SNP_CPUID_PAGE_INDEX: u64 =
69    PARAVISOR_RESERVED_VTL2_SNP_VMSA_PAGE_INDEX + PARAVISOR_RESERVED_VTL2_SNP_VMSA_SIZE_PAGES;
70/// The page index to the first SNP secrets page.
71pub const PARAVISOR_RESERVED_VTL2_SNP_SECRETS_PAGE_INDEX: u64 =
72    PARAVISOR_RESERVED_VTL2_SNP_CPUID_PAGE_INDEX + PARAVISOR_RESERVED_VTL2_SNP_CPUID_SIZE_PAGES;
73
74// Number of pages for each type of parameter in the vtl 2 measured config
75// region.
76/// Size in pages the list of accepted memory
77pub const PARAVISOR_MEASURED_VTL2_CONFIG_ACCEPTED_MEMORY_SIZE_PAGES: u64 = 1;
78/// Size in pages of VTL2 specific measured config
79pub const PARAVISOR_MEASURED_VTL2_CONFIG_SIZE_PAGES: u64 = 1;
80
81/// Count for vtl 2 measured config region size.
82pub const PARAVISOR_MEASURED_VTL2_CONFIG_REGION_PAGE_COUNT: u64 =
83    PARAVISOR_MEASURED_VTL2_CONFIG_ACCEPTED_MEMORY_SIZE_PAGES
84        + PARAVISOR_MEASURED_VTL2_CONFIG_SIZE_PAGES;
85
86// Measured config comes after the unmeasured config
87/// The page index to the list of accepted pages
88pub const PARAVISOR_MEASURED_VTL2_CONFIG_ACCEPTED_MEMORY_PAGE_INDEX: u64 =
89    PARAVISOR_UNMEASURED_VTL2_CONFIG_REGION_BASE_INDEX
90        + PARAVISOR_UNMEASURED_VTL2_CONFIG_REGION_PAGE_COUNT_MAX;
91
92/// The page index for measured VTL2 config.
93pub const PARAVISOR_MEASURED_VTL2_CONFIG_PAGE_INDEX: u64 =
94    PARAVISOR_MEASURED_VTL2_CONFIG_ACCEPTED_MEMORY_PAGE_INDEX
95        + PARAVISOR_MEASURED_VTL2_CONFIG_ACCEPTED_MEMORY_SIZE_PAGES;
96
97/// The maximum size in pages out of all isolation architectures.
98pub const PARAVISOR_VTL2_CONFIG_REGION_PAGE_COUNT_MAX: u64 =
99    PARAVISOR_UNMEASURED_VTL2_CONFIG_REGION_PAGE_COUNT_MAX
100        + PARAVISOR_MEASURED_VTL2_CONFIG_REGION_PAGE_COUNT; // TODO: const fn max or macro possible?
101
102// Default memory information.
103/// The default base address for the paravisor, 128MB.
104pub const PARAVISOR_DEFAULT_MEMORY_BASE_ADDRESS: u64 = 128 * 1024 * 1024;
105/// The default page count for the memory size for the paravisor, 64MB.
106pub const PARAVISOR_DEFAULT_MEMORY_PAGE_COUNT: u64 = 64 * 1024 * 1024 / HV_PAGE_SIZE;
107/// The base VA for the local map, if present.
108pub const PARAVISOR_LOCAL_MAP_VA: u64 = 0x200000;
109/// The base size in bytes for the local map, if present.
110pub const PARAVISOR_LOCAL_MAP_SIZE: u64 = 0x200000;
111
112open_enum! {
113    /// Underhill command line policy.
114    #[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
115    pub enum CommandLinePolicy : u16 {
116        /// Use the static command line encoded only.
117        STATIC = 0,
118        /// Append the host provided value in the device tree /chosen node to
119        /// the static command line.
120        APPEND_CHOSEN = 1,
121    }
122}
123
124/// Maximum static command line size.
125pub const COMMAND_LINE_SIZE: usize = 4092;
126
127/// Command line information. This structure is an exclusive measured page.
128#[repr(C)]
129#[derive(Copy, Clone, Debug, IntoBytes, Immutable, KnownLayout, FromBytes)]
130pub struct ParavisorCommandLine {
131    /// The policy Underhill should use.
132    pub policy: CommandLinePolicy,
133    /// The length of the command line.
134    pub static_command_line_len: u16,
135    /// The static command line. This is a valid utf8 string of length described
136    /// by the field above. This field should normally not be used, instead the
137    /// corresponding [`Self::command_line`] function should be used that
138    /// returns a [`&str`].
139    pub static_command_line: [u8; COMMAND_LINE_SIZE],
140}
141
142impl ParavisorCommandLine {
143    /// Read the static command line as a [`&str`]. Returns None if the bytes
144    /// are not a valid [`&str`].
145    pub fn command_line(&self) -> Option<&str> {
146        core::str::from_utf8(&self.static_command_line[..self.static_command_line_len as usize])
147            .ok()
148    }
149}
150
151const_assert_eq!(size_of::<ParavisorCommandLine>(), HV_PAGE_SIZE as usize);
152
153/// Describes a region of guest memory.
154#[repr(C)]
155#[derive(Copy, Clone, Debug, IntoBytes, Immutable, KnownLayout, FromBytes, PartialEq)]
156pub struct PageRegionDescriptor {
157    /// Guest physical page number for the base of this region.
158    pub base_page_number: u64,
159    /// Number of pages in this region. 0 means this region is not valid.
160    pub page_count: u64,
161}
162
163#[cfg(feature = "inspect")]
164impl Inspect for PageRegionDescriptor {
165    fn inspect(&self, req: inspect::Request<'_>) {
166        let pages = self.pages();
167
168        match pages {
169            None => {
170                req.ignore();
171            }
172            Some((base, count)) => {
173                req.respond()
174                    .field("base_page_number", base)
175                    .field("page_count", count);
176            }
177        }
178    }
179}
180
181impl PageRegionDescriptor {
182    /// An empty region.
183    pub const EMPTY: Self = PageRegionDescriptor {
184        base_page_number: 0,
185        page_count: 0,
186    };
187
188    /// Create a new page region descriptor with the given base page and page count.
189    pub fn new(base_page_number: u64, page_count: u64) -> Self {
190        PageRegionDescriptor {
191            base_page_number,
192            page_count,
193        }
194    }
195
196    /// Returns `Some((base page number, page count))` described by the descriptor, if valid.
197    pub fn pages(&self) -> Option<(u64, u64)> {
198        if self.page_count != 0 {
199            Some((self.base_page_number, self.page_count))
200        } else {
201            None
202        }
203    }
204}
205
206/// The header field of the imported pages region page.
207#[repr(C)]
208#[derive(Copy, Clone, Debug, IntoBytes, Immutable, KnownLayout, FromBytes, PartialEq)]
209pub struct ImportedRegionsPageHeader {
210    /// The cryptographic hash of the unaccepted pages.
211    pub sha384_hash: [u8; 48],
212}
213
214/// Describes a region of guest memory that has been imported into VTL2.
215#[repr(C)]
216#[derive(Copy, Clone, Debug, IntoBytes, Immutable, KnownLayout, FromBytes, PartialEq)]
217pub struct ImportedRegionDescriptor {
218    /// Guest physical page number for the base of this region.
219    pub base_page_number: u64,
220    /// Number of pages in this region. 0 means this region is not valid.
221    pub page_count: u64,
222    /// Whether the pages in this region were accepted during the import process.
223    pub accepted: u8,
224    /// Padding
225    padding: [u8; 7],
226}
227
228#[cfg(feature = "inspect")]
229impl Inspect for ImportedRegionDescriptor {
230    fn inspect(&self, req: inspect::Request<'_>) {
231        let pages = self.pages();
232
233        match pages {
234            None => {
235                req.ignore();
236            }
237            Some((base, count, accepted)) => {
238                req.respond()
239                    .field("base_page_number", base)
240                    .field("page_count", count)
241                    .field("accepted", accepted);
242            }
243        }
244    }
245}
246
247impl ImportedRegionDescriptor {
248    /// An empty region.
249    pub const EMPTY: Self = ImportedRegionDescriptor {
250        base_page_number: 0,
251        page_count: 0,
252        accepted: false as u8,
253        padding: [0; 7],
254    };
255
256    /// Create a new page region descriptor with the given base page and page count.
257    pub fn new(base_page_number: u64, page_count: u64, accepted: bool) -> Self {
258        ImportedRegionDescriptor {
259            base_page_number,
260            page_count,
261            accepted: accepted as u8,
262            padding: [0; 7],
263        }
264    }
265
266    /// Returns `Some((base page number, page count, accepted))` described by the descriptor, if valid.
267    pub fn pages(&self) -> Option<(u64, u64, bool)> {
268        if self.page_count != 0 {
269            Some((self.base_page_number, self.page_count, self.accepted != 0))
270        } else {
271            None
272        }
273    }
274}
275
276/// Measured config about linux loaded into VTL0.
277#[repr(C)]
278#[derive(Copy, Clone, Debug, IntoBytes, Immutable, KnownLayout, FromBytes)]
279#[cfg_attr(feature = "inspect", derive(Inspect))]
280pub struct LinuxInfo {
281    /// The memory the kernel was loaded into.
282    pub kernel_region: PageRegionDescriptor,
283    /// The gpa entrypoint of the kernel.
284    pub kernel_entrypoint: u64,
285    /// The memory region the initrd was loaded into.
286    pub initrd_region: PageRegionDescriptor,
287    /// The size of the initrd in bytes.
288    pub initrd_size: u64,
289    /// An ASCII command line to use for the kernel.
290    pub command_line: PageRegionDescriptor,
291}
292
293/// Measured config about UEFI loaded into VTL0.
294#[repr(C)]
295#[derive(Copy, Clone, Debug, IntoBytes, Immutable, KnownLayout, FromBytes)]
296#[cfg_attr(feature = "inspect", derive(Inspect))]
297pub struct UefiInfo {
298    /// The information about where UEFI's firmware and misc pages are.
299    pub firmware: PageRegionDescriptor,
300    /// The location of VTL0's VP context data.
301    pub vtl0_vp_context: PageRegionDescriptor,
302}
303
304/// Measured config about what this image can support loading in VTL0.
305#[cfg_attr(feature = "inspect", derive(Inspect))]
306#[bitfield(u64)]
307#[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
308pub struct SupportedVtl0LoadInfo {
309    /// This image supports UEFI.
310    #[bits(1)]
311    pub uefi_supported: bool,
312    /// This image supports PCAT.
313    #[bits(1)]
314    pub pcat_supported: bool,
315    /// This image supports Linux Direct.
316    #[bits(1)]
317    pub linux_direct_supported: bool,
318    /// Currently reserved.
319    #[bits(61)]
320    pub reserved: u64,
321}
322
323/// Paravisor measured config information for vtl 0. Unlike the previous loader
324/// block which contains dynamic parameter info written by the host, this config
325/// information is known at file build time, measured, and deposited as part of
326/// the initial launch data.
327#[repr(C)]
328#[derive(Copy, Clone, Debug, IntoBytes, Immutable, KnownLayout, FromBytes)]
329#[cfg_attr(feature = "inspect", derive(Inspect))]
330pub struct ParavisorMeasuredVtl0Config {
331    /// Magic value. Must be [`Self::MAGIC`].
332    pub magic: u64,
333    /// Supported VTL0 images.
334    pub supported_vtl0: SupportedVtl0LoadInfo,
335    /// If UEFI is supported, information about UEFI for VTL0.
336    pub uefi_info: UefiInfo,
337    /// If Linux is supported, information about Linux for VTL0.
338    pub linux_info: LinuxInfo,
339}
340
341impl ParavisorMeasuredVtl0Config {
342    /// Magic value for the measured config, which is "OHCLVTL0".
343    pub const MAGIC: u64 = 0x4F48434C56544C30;
344}
345
346/// The physical page number for where the vtl 0 measured config is stored, x86_64.
347/// This address is guaranteed to exist in the guest address space as it is
348/// where the ISR table is located at reset.
349pub const PARAVISOR_VTL0_MEASURED_CONFIG_BASE_PAGE_X64: u64 = 0;
350
351/// The physical page number for where the vtl 0 measured config is stored, aarch64.
352/// Not obvious about guaranteed existence. 16MiB might be a reasonable assumption as:
353/// * UEFI uses the GPA range of [0; 0x800000), after that there are page tables,
354///   stack, and the config blob at GPA 0x824000,
355/// * Gen 2 VMs don't work with less than 32MiB,
356/// * the loaders have checks for overlap.
357pub const PARAVISOR_VTL0_MEASURED_CONFIG_BASE_PAGE_AARCH64: u64 = 16 << (20 - 12);
358
359/// Paravisor measured config for vtl2.
360#[repr(C)]
361#[derive(Copy, Clone, Debug, IntoBytes, Immutable, KnownLayout, FromBytes)]
362#[cfg_attr(feature = "inspect", derive(Inspect))]
363pub struct ParavisorMeasuredVtl2Config {
364    /// Magic value. Must be [`Self::MAGIC`].
365    pub magic: u64,
366    /// The bit offset of vTOM, if non-zero.
367    pub vtom_offset_bit: u8,
368    /// Padding.
369    pub padding: [u8; 7],
370}
371
372impl ParavisorMeasuredVtl2Config {
373    /// Magic value for the measured config, which is "OHCLVTL2".
374    pub const MAGIC: u64 = 0x4F48434C56544C32;
375}