1use 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
18pub const PARAVISOR_CONFIG_SLIT_SIZE_PAGES: u64 = 20;
22pub const PARAVISOR_CONFIG_PPTT_SIZE_PAGES: u64 = 20;
24pub const PARAVISOR_CONFIG_DEVICE_TREE_SIZE_PAGES: u64 = 64;
26
27pub 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
33pub const PARAVISOR_CONFIG_SLIT_PAGE_INDEX: u64 = 0;
36pub const PARAVISOR_CONFIG_PPTT_PAGE_INDEX: u64 =
38 PARAVISOR_CONFIG_SLIT_PAGE_INDEX + PARAVISOR_CONFIG_SLIT_SIZE_PAGES;
39pub const PARAVISOR_CONFIG_DEVICE_TREE_PAGE_INDEX: u64 =
41 PARAVISOR_CONFIG_PPTT_PAGE_INDEX + PARAVISOR_CONFIG_PPTT_SIZE_PAGES;
42pub const PARAVISOR_UNMEASURED_VTL2_CONFIG_REGION_BASE_INDEX: u64 =
44 PARAVISOR_CONFIG_SLIT_PAGE_INDEX;
45
46pub const PARAVISOR_RESERVED_VTL2_SNP_CPUID_SIZE_PAGES: u64 = 2;
48pub const PARAVISOR_RESERVED_VTL2_SNP_VMSA_SIZE_PAGES: u64 = 1;
50pub const PARAVISOR_RESERVED_VTL2_SNP_SECRETS_SIZE_PAGES: u64 = 1;
52
53pub 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
58pub const PARAVISOR_RESERVED_VTL2_SNP_VMSA_PAGE_INDEX: u64 = 0;
67pub const PARAVISOR_RESERVED_VTL2_SNP_CPUID_PAGE_INDEX: u64 =
69 PARAVISOR_RESERVED_VTL2_SNP_VMSA_PAGE_INDEX + PARAVISOR_RESERVED_VTL2_SNP_VMSA_SIZE_PAGES;
70pub 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
74pub const PARAVISOR_MEASURED_VTL2_CONFIG_ACCEPTED_MEMORY_SIZE_PAGES: u64 = 1;
78
79pub const PARAVISOR_MEASURED_VTL2_CONFIG_SIZE_PAGES: u64 = 1;
81
82pub const PARAVISOR_MEASURED_VTL2_CONFIG_PAGE_HASHES_SIZE_PAGES: u64 = 256;
93
94pub const PARAVISOR_MEASURED_VTL2_CONFIG_REGION_PAGE_COUNT: u64 =
96 PARAVISOR_MEASURED_VTL2_CONFIG_ACCEPTED_MEMORY_SIZE_PAGES
97 + PARAVISOR_MEASURED_VTL2_CONFIG_SIZE_PAGES
98 + PARAVISOR_MEASURED_VTL2_CONFIG_PAGE_HASHES_SIZE_PAGES;
99
100pub const PARAVISOR_MEASURED_VTL2_CONFIG_ACCEPTED_MEMORY_PAGE_INDEX: u64 =
103 PARAVISOR_UNMEASURED_VTL2_CONFIG_REGION_BASE_INDEX
104 + PARAVISOR_UNMEASURED_VTL2_CONFIG_REGION_PAGE_COUNT_MAX;
105
106pub const PARAVISOR_MEASURED_VTL2_CONFIG_PAGE_INDEX: u64 =
108 PARAVISOR_MEASURED_VTL2_CONFIG_ACCEPTED_MEMORY_PAGE_INDEX
109 + PARAVISOR_MEASURED_VTL2_CONFIG_ACCEPTED_MEMORY_SIZE_PAGES;
110
111pub const PARAVISOR_MEASURED_VTL2_CONFIG_PAGE_HASHES_PAGE_INDEX: u64 =
121 PARAVISOR_MEASURED_VTL2_CONFIG_PAGE_INDEX + PARAVISOR_MEASURED_VTL2_CONFIG_SIZE_PAGES;
122
123pub const PARAVISOR_VTL2_CONFIG_REGION_PAGE_COUNT_MAX: u64 =
125 PARAVISOR_UNMEASURED_VTL2_CONFIG_REGION_PAGE_COUNT_MAX
126 + PARAVISOR_MEASURED_VTL2_CONFIG_REGION_PAGE_COUNT; pub const PARAVISOR_DEFAULT_MEMORY_BASE_ADDRESS: u64 = 128 * 1024 * 1024;
131pub const PARAVISOR_DEFAULT_MEMORY_PAGE_COUNT: u64 = 64 * 1024 * 1024 / HV_PAGE_SIZE;
133pub const PARAVISOR_LOCAL_MAP_VA: u64 = 0x200000;
135pub const PARAVISOR_LOCAL_MAP_SIZE: u64 = 0x200000;
137
138open_enum! {
139 #[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
141 pub enum CommandLinePolicy : u16 {
142 STATIC = 0,
144 APPEND_CHOSEN = 1,
147 }
148}
149
150pub const COMMAND_LINE_SIZE: usize = 4092;
152
153#[repr(C)]
155#[derive(Copy, Clone, Debug, IntoBytes, Immutable, KnownLayout, FromBytes)]
156pub struct ParavisorCommandLine {
157 pub policy: CommandLinePolicy,
159 pub static_command_line_len: u16,
161 pub static_command_line: [u8; COMMAND_LINE_SIZE],
166}
167
168impl ParavisorCommandLine {
169 pub fn command_line(&self) -> Option<&str> {
172 core::str::from_utf8(&self.static_command_line[..self.static_command_line_len as usize])
173 .ok()
174 }
175}
176
177const_assert_eq!(size_of::<ParavisorCommandLine>(), HV_PAGE_SIZE as usize);
178
179#[repr(C)]
181#[derive(Copy, Clone, Debug, IntoBytes, Immutable, KnownLayout, FromBytes, PartialEq)]
182pub struct PageRegionDescriptor {
183 pub base_page_number: u64,
185 pub page_count: u64,
187}
188
189#[cfg(feature = "inspect")]
190impl Inspect for PageRegionDescriptor {
191 fn inspect(&self, req: inspect::Request<'_>) {
192 let pages = self.pages();
193
194 match pages {
195 None => {
196 req.ignore();
197 }
198 Some((base, count)) => {
199 req.respond()
200 .field("base_page_number", base)
201 .field("page_count", count);
202 }
203 }
204 }
205}
206
207impl PageRegionDescriptor {
208 pub const EMPTY: Self = PageRegionDescriptor {
210 base_page_number: 0,
211 page_count: 0,
212 };
213
214 pub fn new(base_page_number: u64, page_count: u64) -> Self {
216 PageRegionDescriptor {
217 base_page_number,
218 page_count,
219 }
220 }
221
222 pub fn pages(&self) -> Option<(u64, u64)> {
224 if self.page_count != 0 {
225 Some((self.base_page_number, self.page_count))
226 } else {
227 None
228 }
229 }
230}
231
232#[repr(C)]
234#[derive(Copy, Clone, Debug, IntoBytes, Immutable, KnownLayout, FromBytes, PartialEq)]
235pub struct ImportedRegionsPageHeader {
236 pub sha384_hash: [u8; 48],
238}
239
240#[repr(C)]
242#[derive(Copy, Clone, Debug, IntoBytes, Immutable, KnownLayout, FromBytes, PartialEq)]
243pub struct ImportedRegionDescriptor {
244 pub base_page_number: u64,
246 pub page_count: u64,
248 pub accepted: u8,
250 padding: [u8; 7],
252}
253
254#[cfg(feature = "inspect")]
255impl Inspect for ImportedRegionDescriptor {
256 fn inspect(&self, req: inspect::Request<'_>) {
257 let pages = self.pages();
258
259 match pages {
260 None => {
261 req.ignore();
262 }
263 Some((base, count, accepted)) => {
264 req.respond()
265 .field("base_page_number", base)
266 .field("page_count", count)
267 .field("accepted", accepted);
268 }
269 }
270 }
271}
272
273impl ImportedRegionDescriptor {
274 pub const EMPTY: Self = ImportedRegionDescriptor {
276 base_page_number: 0,
277 page_count: 0,
278 accepted: false as u8,
279 padding: [0; 7],
280 };
281
282 pub fn new(base_page_number: u64, page_count: u64, accepted: bool) -> Self {
284 ImportedRegionDescriptor {
285 base_page_number,
286 page_count,
287 accepted: accepted as u8,
288 padding: [0; 7],
289 }
290 }
291
292 pub fn pages(&self) -> Option<(u64, u64, bool)> {
294 if self.page_count != 0 {
295 Some((self.base_page_number, self.page_count, self.accepted != 0))
296 } else {
297 None
298 }
299 }
300}
301
302pub const EXPECTED_PAGE_HASHES_MAGIC: u32 = u32::from_le_bytes(*b"EPHS");
306
307pub const EXPECTED_PAGE_HASHES_VERSION: u32 = 1;
310
311#[repr(C)]
320#[derive(Copy, Clone, Debug, IntoBytes, Immutable, KnownLayout, FromBytes, PartialEq)]
321pub struct ExpectedPageHashesHeader {
322 pub magic: u32,
324 pub version: u32,
326 pub page_hash_count: u32,
328 pub reserved: u32,
330}
331
332#[repr(C)]
336#[derive(Copy, Clone, Debug, IntoBytes, Immutable, KnownLayout, FromBytes, PartialEq)]
337pub struct ExpectedPageHash {
338 pub sha384_hash: [u8; 48],
340}
341
342pub const EXPECTED_PAGE_HASH_MAX_COUNT: usize =
346 (PARAVISOR_MEASURED_VTL2_CONFIG_PAGE_HASHES_SIZE_PAGES as usize * HV_PAGE_SIZE as usize
347 - size_of::<ExpectedPageHashesHeader>())
348 / size_of::<ExpectedPageHash>();
349
350#[repr(C)]
352#[derive(Copy, Clone, Debug, IntoBytes, Immutable, KnownLayout, FromBytes)]
353#[cfg_attr(feature = "inspect", derive(Inspect))]
354pub struct LinuxInfo {
355 pub kernel_region: PageRegionDescriptor,
357 pub kernel_entrypoint: u64,
359 pub initrd_region: PageRegionDescriptor,
361 pub initrd_size: u64,
363 pub command_line: PageRegionDescriptor,
365}
366
367#[repr(C)]
369#[derive(Copy, Clone, Debug, IntoBytes, Immutable, KnownLayout, FromBytes)]
370#[cfg_attr(feature = "inspect", derive(Inspect))]
371pub struct UefiInfo {
372 pub firmware: PageRegionDescriptor,
374 pub vtl0_vp_context: PageRegionDescriptor,
376}
377
378#[cfg_attr(feature = "inspect", derive(Inspect))]
380#[bitfield(u64)]
381#[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
382pub struct SupportedVtl0LoadInfo {
383 #[bits(1)]
385 pub uefi_supported: bool,
386 #[bits(1)]
388 pub pcat_supported: bool,
389 #[bits(1)]
391 pub linux_direct_supported: bool,
392 #[bits(61)]
394 pub reserved: u64,
395}
396
397#[repr(C)]
402#[derive(Copy, Clone, Debug, IntoBytes, Immutable, KnownLayout, FromBytes)]
403#[cfg_attr(feature = "inspect", derive(Inspect))]
404pub struct ParavisorMeasuredVtl0Config {
405 pub magic: u64,
407 pub supported_vtl0: SupportedVtl0LoadInfo,
409 pub uefi_info: UefiInfo,
411 pub linux_info: LinuxInfo,
413}
414
415impl ParavisorMeasuredVtl0Config {
416 pub const MAGIC: u64 = 0x4F48434C56544C30;
418}
419
420pub const PARAVISOR_VTL0_MEASURED_CONFIG_BASE_PAGE_X64: u64 = 0;
424
425pub const PARAVISOR_VTL0_MEASURED_CONFIG_BASE_PAGE_AARCH64: u64 = 16 << (20 - 12);
432
433#[repr(C)]
439#[derive(Copy, Clone, Debug, IntoBytes, Immutable, KnownLayout, FromBytes)]
440#[cfg_attr(feature = "inspect", derive(Inspect))]
441pub struct ParavisorMeasuredVtl2Config {
442 pub magic: u64,
444 pub vtom_offset_bit: u8,
446 pub padding: [u8; 7],
448 pub product_policy_size: u32,
451 pub reserved: [u8; 4],
453}
454
455impl ParavisorMeasuredVtl2Config {
456 pub const MAGIC: u64 = 0x4F48434C56544C32;
458}
459
460pub const PRODUCT_POLICY_INLINE_OFFSET: usize = size_of::<ParavisorMeasuredVtl2Config>();
463
464pub const PRODUCT_POLICY_MAX_SIZE_BYTES: usize =
466 (PARAVISOR_MEASURED_VTL2_CONFIG_SIZE_PAGES as usize) * (HV_PAGE_SIZE as usize)
467 - PRODUCT_POLICY_INLINE_OFFSET;
468
469#[cfg(test)]
470mod tests {
471 use super::*;
472 #[test]
477 fn measured_vtl2_config_field_offsets() {
478 let cfg = ParavisorMeasuredVtl2Config {
479 magic: 0x1122_3344_5566_7788,
480 vtom_offset_bit: 0x99,
481 padding: [0; 7],
482 product_policy_size: 0xABCDu32,
483 reserved: [0; 4],
484 };
485 let bytes = cfg.as_bytes();
486 assert_eq!(&bytes[0..8], &0x1122_3344_5566_7788u64.to_le_bytes());
487 assert_eq!(bytes[8], 0x99);
488 assert_eq!(&bytes[9..16], &[0u8; 7]);
489 assert_eq!(&bytes[16..20], &0xABCDu32.to_le_bytes());
490 assert_eq!(&bytes[20..24], &[0u8; 4]);
491 assert_eq!(bytes.len(), 24);
492 }
493
494 #[test]
495 fn measured_vtl2_config_round_trips() {
496 let cfg = ParavisorMeasuredVtl2Config {
497 magic: ParavisorMeasuredVtl2Config::MAGIC,
498 vtom_offset_bit: 47,
499 padding: [0; 7],
500 product_policy_size: 256,
501 reserved: [0; 4],
502 };
503 let bytes = cfg.as_bytes().to_vec();
504 let (decoded, rest) = ParavisorMeasuredVtl2Config::ref_from_prefix(&bytes).unwrap();
505 assert!(rest.is_empty());
506 assert_eq!(decoded.magic, ParavisorMeasuredVtl2Config::MAGIC);
507 assert_eq!(decoded.vtom_offset_bit, 47);
508 assert_eq!(decoded.product_policy_size, 256);
509 }
510
511 #[test]
512 fn pre_feature_zeroed_page_decodes_as_absent() {
513 let mut page = [0u8; HV_PAGE_SIZE as usize];
516 page[0..8].copy_from_slice(&ParavisorMeasuredVtl2Config::MAGIC.to_le_bytes());
517 page[8] = 17;
518 let (decoded, _rest) = ParavisorMeasuredVtl2Config::ref_from_prefix(&page).unwrap();
519 assert_eq!(decoded.magic, ParavisorMeasuredVtl2Config::MAGIC);
520 assert_eq!(decoded.vtom_offset_bit, 17);
521 assert_eq!(decoded.product_policy_size, 0);
522 }
523}