Skip to main content

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