openhcl_boot/host_params/
shim_params.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Parameters that are fixed at IGVM build time by the underhill loader.
5
6use crate::arch::get_isolation_type;
7use core::slice;
8use loader_defs::paravisor::ImportedRegionDescriptor;
9use loader_defs::paravisor::ParavisorCommandLine;
10use loader_defs::shim::ShimParamsRaw;
11use memory_range::MemoryRange;
12
13/// Isolation type of the partition
14#[derive(Debug, PartialEq, Eq, Copy, Clone)]
15pub enum IsolationType {
16    None,
17    Vbs,
18    #[cfg_attr(target_arch = "aarch64", expect(dead_code))]
19    Snp,
20    #[cfg_attr(target_arch = "aarch64", expect(dead_code))]
21    Tdx,
22}
23
24impl IsolationType {
25    pub fn is_hardware_isolated(&self) -> bool {
26        match self {
27            IsolationType::None => false,
28            IsolationType::Vbs => false,
29            IsolationType::Snp => true,
30            IsolationType::Tdx => true,
31        }
32    }
33}
34
35/// Iterator for the list of accepted regions in the IGVM VTL 2 config region.
36/// Does not increment past the first with page count 0.
37pub struct ImportedRegionIter<'a> {
38    imported_regions: &'a [ImportedRegionDescriptor],
39}
40
41impl Iterator for ImportedRegionIter<'_> {
42    type Item = (MemoryRange, bool);
43
44    fn next(&mut self) -> Option<Self::Item> {
45        if self.imported_regions.is_empty() {
46            None
47        } else {
48            let element = self.imported_regions[0]
49                .pages()
50                .map(|(base_page, count, accepted)| {
51                    let base_address = base_page * hvdef::HV_PAGE_SIZE;
52                    let end_address = base_address + (count * hvdef::HV_PAGE_SIZE);
53                    (MemoryRange::try_new(base_address..end_address).expect(
54                    "page number conversion into addresses results in a valid address range",
55                ), accepted)
56                });
57
58            if element.is_some() {
59                self.imported_regions = &self.imported_regions[1..];
60            } else {
61                self.imported_regions = &[];
62            }
63
64            element
65        }
66    }
67}
68
69/// Parameters fixed at IGVM file build time. These contain information about
70/// where certain sections are located, that are fixed up after figuring out
71/// where the boot loader was relocated to.
72#[derive(Debug)]
73pub struct ShimParams {
74    // TODO: replace all of these base/size pairs with MemoryRange
75    /// The kernel entry address.
76    pub kernel_entry_address: u64,
77    /// The address of the [`ParavisorCommandLine`] structure.
78    pub cmdline_base: u64,
79    /// The initrd address.
80    pub initrd_base: u64,
81    /// The size of the inird, in bytes.
82    pub initrd_size: u64,
83    /// The crc32 of the initrd at file build time.
84    pub initrd_crc: u32,
85    /// The base address of the VTL2 memory region encoded at build time.
86    pub memory_start_address: u64,
87    /// The size of the VTL2 memory region encoded at build time.
88    pub memory_size: u64,
89    /// The base address of the parameter region.
90    pub parameter_region_start: u64,
91    /// The size of the parameter region.
92    pub parameter_region_size: u64,
93    /// The base address of the VTL2 reserved region.
94    pub vtl2_reserved_region_start: u64,
95    /// The size of the VTL2 reserved region.
96    pub vtl2_reserved_region_size: u64,
97    /// Isolation type supported by the boot shim.
98    pub isolation_type: IsolationType,
99    pub sidecar_entry_address: u64,
100    pub sidecar_base: u64,
101    pub sidecar_size: u64,
102    /// Memory used by the shim.
103    pub used: MemoryRange,
104    pub bounce_buffer: Option<MemoryRange>,
105    /// Log buffer region used by the shim.
106    pub log_buffer: MemoryRange,
107    /// Memory to be used for the heap.
108    pub heap: MemoryRange,
109    /// Memory region for persisted state.
110    pub persisted_state: MemoryRange,
111}
112
113impl ShimParams {
114    /// Create a new instance of [`ShimParams`] from the raw offset based
115    /// [`ShimParamsRaw`] and shim base address.
116    pub fn new(shim_base_address: u64, raw: &ShimParamsRaw) -> Self {
117        let &ShimParamsRaw {
118            kernel_entry_offset,
119            cmdline_offset,
120            initrd_offset,
121            initrd_size,
122            initrd_crc,
123            supported_isolation_type,
124            memory_start_offset,
125            memory_size,
126            parameter_region_offset,
127            parameter_region_size,
128            vtl2_reserved_region_offset,
129            vtl2_reserved_region_size,
130            sidecar_offset,
131            sidecar_size,
132            sidecar_entry_offset,
133            used_start,
134            used_end,
135            bounce_buffer_start,
136            bounce_buffer_size,
137            log_buffer_start,
138            log_buffer_size,
139            heap_start_offset,
140            heap_size,
141            persisted_state_region_offset,
142            persisted_state_region_size,
143        } = raw;
144
145        let isolation_type = get_isolation_type(supported_isolation_type);
146
147        let bounce_buffer = if bounce_buffer_size == 0 {
148            None
149        } else {
150            let base = shim_base_address.wrapping_add_signed(bounce_buffer_start);
151            Some(MemoryRange::new(base..base + bounce_buffer_size))
152        };
153
154        let log_buffer = {
155            let base = shim_base_address.wrapping_add_signed(log_buffer_start);
156            MemoryRange::new(base..base + log_buffer_size)
157        };
158
159        let heap = {
160            let base = shim_base_address.wrapping_add_signed(heap_start_offset);
161            MemoryRange::new(base..base + heap_size)
162        };
163
164        let persisted_state = {
165            let base = shim_base_address.wrapping_add_signed(persisted_state_region_offset);
166            MemoryRange::new(base..base + persisted_state_region_size)
167        };
168
169        Self {
170            kernel_entry_address: shim_base_address.wrapping_add_signed(kernel_entry_offset),
171            cmdline_base: shim_base_address.wrapping_add_signed(cmdline_offset),
172            initrd_base: shim_base_address.wrapping_add_signed(initrd_offset),
173            initrd_size,
174            initrd_crc,
175            memory_start_address: shim_base_address.wrapping_add_signed(memory_start_offset),
176            memory_size,
177            parameter_region_start: shim_base_address.wrapping_add_signed(parameter_region_offset),
178            parameter_region_size,
179            vtl2_reserved_region_start: shim_base_address
180                .wrapping_add_signed(vtl2_reserved_region_offset),
181            vtl2_reserved_region_size,
182            isolation_type,
183            sidecar_entry_address: shim_base_address.wrapping_add_signed(sidecar_entry_offset),
184            sidecar_base: shim_base_address.wrapping_add_signed(sidecar_offset),
185            sidecar_size,
186            used: MemoryRange::new(
187                shim_base_address.wrapping_add_signed(used_start)
188                    ..shim_base_address.wrapping_add_signed(used_end),
189            ),
190            bounce_buffer,
191            log_buffer,
192            heap,
193            persisted_state,
194        }
195    }
196
197    /// Get the base address of the secrets page.
198    #[cfg(target_arch = "x86_64")]
199    pub fn secrets_start(&self) -> u64 {
200        self.vtl2_reserved_region_start
201            + loader_defs::paravisor::PARAVISOR_RESERVED_VTL2_SNP_SECRETS_PAGE_INDEX
202                * hvdef::HV_PAGE_SIZE
203    }
204
205    /// Get the size of the CPUID page.
206    #[cfg(target_arch = "x86_64")]
207    pub fn cpuid_start(&self) -> u64 {
208        self.vtl2_reserved_region_start
209            + loader_defs::paravisor::PARAVISOR_RESERVED_VTL2_SNP_CPUID_PAGE_INDEX
210                * hvdef::HV_PAGE_SIZE
211    }
212
213    /// Get the base address of the host provided device tree.
214    pub fn dt_start(&self) -> u64 {
215        self.parameter_region_start
216            + loader_defs::paravisor::PARAVISOR_CONFIG_DEVICE_TREE_PAGE_INDEX * hvdef::HV_PAGE_SIZE
217    }
218
219    /// The size of the device tree region.
220    pub fn dt_size(&self) -> u64 {
221        loader_defs::paravisor::PARAVISOR_CONFIG_DEVICE_TREE_SIZE_PAGES * hvdef::HV_PAGE_SIZE
222    }
223
224    /// Get the initrd as a byte slice.
225    pub fn initrd(&self) -> &'static [u8] {
226        // SAFETY: The initrd base and size are set at file build time, and the
227        // host must relocate the whole region if relocations are performed.
228        unsafe { slice::from_raw_parts(self.initrd_base as *const u8, self.initrd_size as usize) }
229    }
230
231    /// Get the [`ParavisorCommandLine`] structure that describes the command
232    /// line information.
233    pub fn command_line(&self) -> &'static ParavisorCommandLine {
234        // SAFETY: cmdline_base is a valid address pointing to a valid instance
235        // of a ParavisorCommandLine struct.
236        unsafe {
237            (self.cmdline_base as *const ParavisorCommandLine)
238                .as_ref()
239                .expect("should always be non null")
240        }
241    }
242
243    /// Get the device tree parameter region as a byte slice. Note that the byte
244    /// contents of this slice are written by the host which is untrusted and
245    /// must be validated before usage.
246    pub fn device_tree(&self) -> &'static [u8] {
247        // SAFETY: dt_start() and dt_size() are a valid address, size pair being
248        // generated at IGVM file build time.
249        unsafe { slice::from_raw_parts(self.dt_start() as *const u8, self.dt_size() as usize) }
250    }
251
252    /// Get the list of accepted regions from the parameter region as a
253    /// ImportedRegionDescriptor slice. Note that this list is provided by the IGVM
254    /// file and measured.
255    pub fn imported_regions(&self) -> ImportedRegionIter<'_> {
256        use loader_defs::paravisor::ImportedRegionsPageHeader;
257
258        let imported_region_page_address = self.parameter_region_start
259            + (loader_defs::paravisor::PARAVISOR_MEASURED_VTL2_CONFIG_ACCEPTED_MEMORY_PAGE_INDEX
260                * hvdef::HV_PAGE_SIZE);
261
262        assert!(
263            imported_region_page_address + hvdef::HV_PAGE_SIZE
264                <= self.parameter_region_start + self.parameter_region_size
265        );
266
267        let imported_region_start =
268            imported_region_page_address + size_of::<ImportedRegionsPageHeader>() as u64;
269
270        // SAFETY: accepted_region_start and HV_PAGE_SIZE are a valid address, size pair being
271        // generated at IGVM file build time and validated to be within the parameter region.
272        unsafe {
273            ImportedRegionIter {
274                imported_regions: slice::from_raw_parts(
275                    imported_region_start as *const ImportedRegionDescriptor,
276                    (hvdef::HV_PAGE_SIZE as usize - size_of::<ImportedRegionsPageHeader>())
277                        / size_of::<ImportedRegionDescriptor>(),
278                ),
279            }
280        }
281    }
282
283    #[cfg(target_arch = "x86_64")]
284    pub fn imported_regions_hash(&self) -> &'static [u8] {
285        let header_start = self.parameter_region_start
286            + (loader_defs::paravisor::PARAVISOR_MEASURED_VTL2_CONFIG_ACCEPTED_MEMORY_PAGE_INDEX
287                * hvdef::HV_PAGE_SIZE);
288
289        // SAFETY: header_start is a valid address pointing to a valid instance
290        // of an imported region page header.
291        unsafe {
292            let header =
293                &*(header_start as *const loader_defs::paravisor::ImportedRegionsPageHeader);
294            &header.sha384_hash
295        }
296    }
297}