Skip to main content

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
79/// Size in pages of VTL2 specific measured config
80pub const PARAVISOR_MEASURED_VTL2_CONFIG_SIZE_PAGES: u64 = 1;
81
82/// Count for vtl 2 measured config region size.
83pub const PARAVISOR_MEASURED_VTL2_CONFIG_REGION_PAGE_COUNT: u64 =
84    PARAVISOR_MEASURED_VTL2_CONFIG_ACCEPTED_MEMORY_SIZE_PAGES
85        + PARAVISOR_MEASURED_VTL2_CONFIG_SIZE_PAGES;
86
87// Measured config comes after the unmeasured config
88/// The page index to the list of accepted pages
89pub const PARAVISOR_MEASURED_VTL2_CONFIG_ACCEPTED_MEMORY_PAGE_INDEX: u64 =
90    PARAVISOR_UNMEASURED_VTL2_CONFIG_REGION_BASE_INDEX
91        + PARAVISOR_UNMEASURED_VTL2_CONFIG_REGION_PAGE_COUNT_MAX;
92
93/// The page index for measured VTL2 config.
94pub const PARAVISOR_MEASURED_VTL2_CONFIG_PAGE_INDEX: u64 =
95    PARAVISOR_MEASURED_VTL2_CONFIG_ACCEPTED_MEMORY_PAGE_INDEX
96        + PARAVISOR_MEASURED_VTL2_CONFIG_ACCEPTED_MEMORY_SIZE_PAGES;
97
98/// The maximum size in pages out of all isolation architectures.
99pub const PARAVISOR_VTL2_CONFIG_REGION_PAGE_COUNT_MAX: u64 =
100    PARAVISOR_UNMEASURED_VTL2_CONFIG_REGION_PAGE_COUNT_MAX
101        + PARAVISOR_MEASURED_VTL2_CONFIG_REGION_PAGE_COUNT; // TODO: const fn max or macro possible?
102
103// Default memory information.
104/// The default base address for the paravisor, 128MB.
105pub const PARAVISOR_DEFAULT_MEMORY_BASE_ADDRESS: u64 = 128 * 1024 * 1024;
106/// The default page count for the memory size for the paravisor, 64MB.
107pub const PARAVISOR_DEFAULT_MEMORY_PAGE_COUNT: u64 = 64 * 1024 * 1024 / HV_PAGE_SIZE;
108/// The base VA for the local map, if present.
109pub const PARAVISOR_LOCAL_MAP_VA: u64 = 0x200000;
110/// The base size in bytes for the local map, if present.
111pub const PARAVISOR_LOCAL_MAP_SIZE: u64 = 0x200000;
112
113open_enum! {
114    /// Underhill command line policy.
115    #[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
116    pub enum CommandLinePolicy : u16 {
117        /// Use the static command line encoded only.
118        STATIC = 0,
119        /// Append the host provided value in the device tree /chosen node to
120        /// the static command line.
121        APPEND_CHOSEN = 1,
122    }
123}
124
125/// Maximum static command line size.
126pub const COMMAND_LINE_SIZE: usize = 4092;
127
128/// Command line information. This structure is an exclusive measured page.
129#[repr(C)]
130#[derive(Copy, Clone, Debug, IntoBytes, Immutable, KnownLayout, FromBytes)]
131pub struct ParavisorCommandLine {
132    /// The policy Underhill should use.
133    pub policy: CommandLinePolicy,
134    /// The length of the command line.
135    pub static_command_line_len: u16,
136    /// The static command line. This is a valid utf8 string of length described
137    /// by the field above. This field should normally not be used, instead the
138    /// corresponding [`Self::command_line`] function should be used that
139    /// returns a [`&str`].
140    pub static_command_line: [u8; COMMAND_LINE_SIZE],
141}
142
143impl ParavisorCommandLine {
144    /// Read the static command line as a [`&str`]. Returns None if the bytes
145    /// are not a valid [`&str`].
146    pub fn command_line(&self) -> Option<&str> {
147        core::str::from_utf8(&self.static_command_line[..self.static_command_line_len as usize])
148            .ok()
149    }
150}
151
152const_assert_eq!(size_of::<ParavisorCommandLine>(), HV_PAGE_SIZE as usize);
153
154/// Describes a region of guest memory.
155#[repr(C)]
156#[derive(Copy, Clone, Debug, IntoBytes, Immutable, KnownLayout, FromBytes, PartialEq)]
157pub struct PageRegionDescriptor {
158    /// Guest physical page number for the base of this region.
159    pub base_page_number: u64,
160    /// Number of pages in this region. 0 means this region is not valid.
161    pub page_count: u64,
162}
163
164#[cfg(feature = "inspect")]
165impl Inspect for PageRegionDescriptor {
166    fn inspect(&self, req: inspect::Request<'_>) {
167        let pages = self.pages();
168
169        match pages {
170            None => {
171                req.ignore();
172            }
173            Some((base, count)) => {
174                req.respond()
175                    .field("base_page_number", base)
176                    .field("page_count", count);
177            }
178        }
179    }
180}
181
182impl PageRegionDescriptor {
183    /// An empty region.
184    pub const EMPTY: Self = PageRegionDescriptor {
185        base_page_number: 0,
186        page_count: 0,
187    };
188
189    /// Create a new page region descriptor with the given base page and page count.
190    pub fn new(base_page_number: u64, page_count: u64) -> Self {
191        PageRegionDescriptor {
192            base_page_number,
193            page_count,
194        }
195    }
196
197    /// Returns `Some((base page number, page count))` described by the descriptor, if valid.
198    pub fn pages(&self) -> Option<(u64, u64)> {
199        if self.page_count != 0 {
200            Some((self.base_page_number, self.page_count))
201        } else {
202            None
203        }
204    }
205}
206
207/// The header field of the imported pages region page.
208#[repr(C)]
209#[derive(Copy, Clone, Debug, IntoBytes, Immutable, KnownLayout, FromBytes, PartialEq)]
210pub struct ImportedRegionsPageHeader {
211    /// The cryptographic hash of the unaccepted pages.
212    pub sha384_hash: [u8; 48],
213}
214
215/// Describes a region of guest memory that has been imported into VTL2.
216#[repr(C)]
217#[derive(Copy, Clone, Debug, IntoBytes, Immutable, KnownLayout, FromBytes, PartialEq)]
218pub struct ImportedRegionDescriptor {
219    /// Guest physical page number for the base of this region.
220    pub base_page_number: u64,
221    /// Number of pages in this region. 0 means this region is not valid.
222    pub page_count: u64,
223    /// Whether the pages in this region were accepted during the import process.
224    pub accepted: u8,
225    /// Padding
226    padding: [u8; 7],
227}
228
229#[cfg(feature = "inspect")]
230impl Inspect for ImportedRegionDescriptor {
231    fn inspect(&self, req: inspect::Request<'_>) {
232        let pages = self.pages();
233
234        match pages {
235            None => {
236                req.ignore();
237            }
238            Some((base, count, accepted)) => {
239                req.respond()
240                    .field("base_page_number", base)
241                    .field("page_count", count)
242                    .field("accepted", accepted);
243            }
244        }
245    }
246}
247
248impl ImportedRegionDescriptor {
249    /// An empty region.
250    pub const EMPTY: Self = ImportedRegionDescriptor {
251        base_page_number: 0,
252        page_count: 0,
253        accepted: false as u8,
254        padding: [0; 7],
255    };
256
257    /// Create a new page region descriptor with the given base page and page count.
258    pub fn new(base_page_number: u64, page_count: u64, accepted: bool) -> Self {
259        ImportedRegionDescriptor {
260            base_page_number,
261            page_count,
262            accepted: accepted as u8,
263            padding: [0; 7],
264        }
265    }
266
267    /// Returns `Some((base page number, page count, accepted))` described by the descriptor, if valid.
268    pub fn pages(&self) -> Option<(u64, u64, bool)> {
269        if self.page_count != 0 {
270            Some((self.base_page_number, self.page_count, self.accepted != 0))
271        } else {
272            None
273        }
274    }
275}
276
277/// Measured config about linux loaded into VTL0.
278#[repr(C)]
279#[derive(Copy, Clone, Debug, IntoBytes, Immutable, KnownLayout, FromBytes)]
280#[cfg_attr(feature = "inspect", derive(Inspect))]
281pub struct LinuxInfo {
282    /// The memory the kernel was loaded into.
283    pub kernel_region: PageRegionDescriptor,
284    /// The gpa entrypoint of the kernel.
285    pub kernel_entrypoint: u64,
286    /// The memory region the initrd was loaded into.
287    pub initrd_region: PageRegionDescriptor,
288    /// The size of the initrd in bytes.
289    pub initrd_size: u64,
290    /// An ASCII command line to use for the kernel.
291    pub command_line: PageRegionDescriptor,
292}
293
294/// Measured config about UEFI loaded into VTL0.
295#[repr(C)]
296#[derive(Copy, Clone, Debug, IntoBytes, Immutable, KnownLayout, FromBytes)]
297#[cfg_attr(feature = "inspect", derive(Inspect))]
298pub struct UefiInfo {
299    /// The information about where UEFI's firmware and misc pages are.
300    pub firmware: PageRegionDescriptor,
301    /// The location of VTL0's VP context data.
302    pub vtl0_vp_context: PageRegionDescriptor,
303}
304
305/// Measured config about what this image can support loading in VTL0.
306#[cfg_attr(feature = "inspect", derive(Inspect))]
307#[bitfield(u64)]
308#[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
309pub struct SupportedVtl0LoadInfo {
310    /// This image supports UEFI.
311    #[bits(1)]
312    pub uefi_supported: bool,
313    /// This image supports PCAT.
314    #[bits(1)]
315    pub pcat_supported: bool,
316    /// This image supports Linux Direct.
317    #[bits(1)]
318    pub linux_direct_supported: bool,
319    /// Currently reserved.
320    #[bits(61)]
321    pub reserved: u64,
322}
323
324/// Paravisor measured config information for vtl 0. Unlike the previous loader
325/// block which contains dynamic parameter info written by the host, this config
326/// information is known at file build time, measured, and deposited as part of
327/// the initial launch data.
328#[repr(C)]
329#[derive(Copy, Clone, Debug, IntoBytes, Immutable, KnownLayout, FromBytes)]
330#[cfg_attr(feature = "inspect", derive(Inspect))]
331pub struct ParavisorMeasuredVtl0Config {
332    /// Magic value. Must be [`Self::MAGIC`].
333    pub magic: u64,
334    /// Supported VTL0 images.
335    pub supported_vtl0: SupportedVtl0LoadInfo,
336    /// If UEFI is supported, information about UEFI for VTL0.
337    pub uefi_info: UefiInfo,
338    /// If Linux is supported, information about Linux for VTL0.
339    pub linux_info: LinuxInfo,
340}
341
342impl ParavisorMeasuredVtl0Config {
343    /// Magic value for the measured config, which is "OHCLVTL0".
344    pub const MAGIC: u64 = 0x4F48434C56544C30;
345}
346
347/// The physical page number for where the vtl 0 measured config is stored, x86_64.
348/// This address is guaranteed to exist in the guest address space as it is
349/// where the ISR table is located at reset.
350pub const PARAVISOR_VTL0_MEASURED_CONFIG_BASE_PAGE_X64: u64 = 0;
351
352/// The physical page number for where the vtl 0 measured config is stored, aarch64.
353/// Not obvious about guaranteed existence. 16MiB might be a reasonable assumption as:
354/// * UEFI uses the GPA range of [0; 0x800000), after that there are page tables,
355///   stack, and the config blob at GPA 0x824000,
356/// * Gen 2 VMs don't work with less than 32MiB,
357/// * the loaders have checks for overlap.
358pub const PARAVISOR_VTL0_MEASURED_CONFIG_BASE_PAGE_AARCH64: u64 = 16 << (20 - 12);
359
360/// Paravisor measured config for vtl2.
361///
362/// Followed in place by the optional `ProductPolicy` body at
363/// [`PRODUCT_POLICY_INLINE_OFFSET`]; `product_policy_size == 0` (the
364/// pre-feature zero-filled tail) means absent.
365#[repr(C)]
366#[derive(Copy, Clone, Debug, IntoBytes, Immutable, KnownLayout, FromBytes)]
367#[cfg_attr(feature = "inspect", derive(Inspect))]
368pub struct ParavisorMeasuredVtl2Config {
369    /// Magic value. Must be [`Self::MAGIC`].
370    pub magic: u64,
371    /// The bit offset of vTOM, if non-zero.
372    pub vtom_offset_bit: u8,
373    /// Padding.
374    pub padding: [u8; 7],
375    /// Byte length of the inline `ProductPolicy` body, or `0` if
376    /// absent.
377    pub product_policy_size: u32,
378    /// Reserved; must be zero.
379    pub reserved: [u8; 4],
380}
381
382impl ParavisorMeasuredVtl2Config {
383    /// Magic value for the measured config, which is "OHCLVTL2".
384    pub const MAGIC: u64 = 0x4F48434C56544C32;
385}
386
387/// Byte offset of the inline `ProductPolicy` body within the
388/// measured VTL2 config region.
389pub const PRODUCT_POLICY_INLINE_OFFSET: usize = size_of::<ParavisorMeasuredVtl2Config>();
390
391/// Maximum byte size of an inline `ProductPolicy` body.
392pub const PRODUCT_POLICY_MAX_SIZE_BYTES: usize =
393    (PARAVISOR_MEASURED_VTL2_CONFIG_SIZE_PAGES as usize) * (HV_PAGE_SIZE as usize)
394        - PRODUCT_POLICY_INLINE_OFFSET;
395
396#[cfg(test)]
397mod tests {
398    use super::*;
399    // ---------------------------------------------------------------
400    // ParavisorMeasuredVtl2Config: struct layout
401    // ---------------------------------------------------------------
402
403    #[test]
404    fn measured_vtl2_config_field_offsets() {
405        let cfg = ParavisorMeasuredVtl2Config {
406            magic: 0x1122_3344_5566_7788,
407            vtom_offset_bit: 0x99,
408            padding: [0; 7],
409            product_policy_size: 0xABCDu32,
410            reserved: [0; 4],
411        };
412        let bytes = cfg.as_bytes();
413        assert_eq!(&bytes[0..8], &0x1122_3344_5566_7788u64.to_le_bytes());
414        assert_eq!(bytes[8], 0x99);
415        assert_eq!(&bytes[9..16], &[0u8; 7]);
416        assert_eq!(&bytes[16..20], &0xABCDu32.to_le_bytes());
417        assert_eq!(&bytes[20..24], &[0u8; 4]);
418        assert_eq!(bytes.len(), 24);
419    }
420
421    #[test]
422    fn measured_vtl2_config_round_trips() {
423        let cfg = ParavisorMeasuredVtl2Config {
424            magic: ParavisorMeasuredVtl2Config::MAGIC,
425            vtom_offset_bit: 47,
426            padding: [0; 7],
427            product_policy_size: 256,
428            reserved: [0; 4],
429        };
430        let bytes = cfg.as_bytes().to_vec();
431        let (decoded, rest) = ParavisorMeasuredVtl2Config::ref_from_prefix(&bytes).unwrap();
432        assert!(rest.is_empty());
433        assert_eq!(decoded.magic, ParavisorMeasuredVtl2Config::MAGIC);
434        assert_eq!(decoded.vtom_offset_bit, 47);
435        assert_eq!(decoded.product_policy_size, 256);
436    }
437
438    #[test]
439    fn pre_feature_zeroed_page_decodes_as_absent() {
440        // Pre-feature builders wrote only the 16-byte head; the trailing
441        // zeros must decode as `product_policy_size == 0`.
442        let mut page = [0u8; HV_PAGE_SIZE as usize];
443        page[0..8].copy_from_slice(&ParavisorMeasuredVtl2Config::MAGIC.to_le_bytes());
444        page[8] = 17;
445        let (decoded, _rest) = ParavisorMeasuredVtl2Config::ref_from_prefix(&page).unwrap();
446        assert_eq!(decoded.magic, ParavisorMeasuredVtl2Config::MAGIC);
447        assert_eq!(decoded.vtom_offset_bit, 17);
448        assert_eq!(decoded.product_policy_size, 0);
449    }
450}