1use crate::cpuid::HV_PSP_CPUID_PAGE;
7use crate::importer::Aarch64Register;
8use crate::importer::BootPageAcceptance;
9use crate::importer::IgvmParameterType;
10use crate::importer::ImageLoad;
11use crate::importer::IsolationConfig;
12use crate::importer::IsolationType;
13use crate::importer::SegmentRegister;
14use crate::importer::StartupMemoryType;
15use crate::importer::TableRegister;
16use crate::importer::X86Register;
17use crate::linux::InitrdAddressType;
18use crate::linux::InitrdConfig;
19use crate::linux::InitrdInfo;
20use crate::linux::KernelInfo;
21use crate::linux::load_kernel_and_initrd_arm64;
22use aarch64defs::Cpsr64;
23use aarch64defs::IntermPhysAddrSize;
24use aarch64defs::SctlrEl1;
25use aarch64defs::TranslationBaseEl1;
26use aarch64defs::TranslationControlEl1;
27use aarch64defs::TranslationGranule0;
28use aarch64defs::TranslationGranule1;
29use hvdef::HV_PAGE_SIZE;
30use hvdef::Vtl;
31use igvm::registers::AArch64Register;
32use loader_defs::paravisor::*;
33use loader_defs::shim::ShimParamsRaw;
34use memory_range::MemoryRange;
35use page_table::aarch64::Arm64PageSize;
36use page_table::aarch64::MemoryAttributeEl1;
37use page_table::aarch64::MemoryAttributeIndirectionEl1;
38use page_table::x64::PageTableBuilder;
39use page_table::x64::X64_LARGE_PAGE_SIZE;
40use page_table::x64::align_up_to_large_page_size;
41use page_table::x64::align_up_to_page_size;
42use page_table::x64::calculate_pde_table_count;
43use thiserror::Error;
44use x86defs::GdtEntry;
45use x86defs::SegmentSelector;
46use x86defs::X64_BUSY_TSS_SEGMENT_ATTRIBUTES;
47use x86defs::X64_DEFAULT_CODE_SEGMENT_ATTRIBUTES;
48use x86defs::X64_DEFAULT_DATA_SEGMENT_ATTRIBUTES;
49use x86defs::cpuid::CpuidFunction;
50use zerocopy::FromZeros;
51use zerocopy::IntoBytes;
52
53#[derive(Debug)]
54pub struct Vtl0Linux<'a> {
55 pub command_line: &'a std::ffi::CString,
56 pub load_info: crate::linux::LoadInfo,
57}
58
59#[derive(Debug)]
60pub struct Vtl0Config<'a> {
61 pub supports_pcat: bool,
62 pub supports_uefi: Option<(crate::uefi::LoadInfo, Vec<u8>)>,
64 pub supports_linux: Option<Vtl0Linux<'a>>,
65}
66
67pub const HCL_SECURE_VTL: Vtl = Vtl::Vtl2;
69
70#[derive(Debug, Error)]
71pub enum Error {
72 #[error("memory is unaligned: {0}")]
73 MemoryUnaligned(u64),
74 #[error("command line too large: {0}")]
75 CommandLineSize(usize),
76 #[error("kernel load error")]
77 Kernel(#[source] crate::linux::Error),
78 #[error("shim load error")]
79 Shim(#[source] crate::elf::Error),
80 #[error("invalid initrd size: {0}")]
81 InvalidInitrdSize(u64),
82 #[error("memory used: {0} is greater than available")]
83 NotEnoughMemory(u64),
84 #[error("importer error")]
85 Importer(#[from] anyhow::Error),
86}
87
88pub enum CommandLineType<'a> {
90 Static(&'a str),
92 HostAppendable(&'a str),
97}
98
99pub fn load_openhcl_x64<F>(
106 importer: &mut dyn ImageLoad<X86Register>,
107 kernel_image: &mut F,
108 shim: &mut F,
109 sidecar: Option<&mut F>,
110 command_line: CommandLineType<'_>,
111 initrd: Option<&[u8]>,
112 memory_page_base: Option<u64>,
113 memory_page_count: u64,
114 vtl0_config: Vtl0Config<'_>,
115) -> Result<(), Error>
116where
117 F: std::io::Read + std::io::Seek,
118{
119 let IsolationConfig {
120 isolation_type,
121 paravisor_present,
122 shared_gpa_boundary_bits,
123 } = importer.isolation_config();
124
125 let with_relocation = memory_page_base.is_none() && isolation_type == IsolationType::None;
127
128 let memory_start_address = memory_page_base
129 .map(|page_number| page_number * HV_PAGE_SIZE)
130 .unwrap_or(PARAVISOR_DEFAULT_MEMORY_BASE_ADDRESS);
131
132 let memory_size = memory_page_count * HV_PAGE_SIZE;
133
134 if memory_start_address % X64_LARGE_PAGE_SIZE != 0 {
151 return Err(Error::MemoryUnaligned(memory_start_address));
152 }
153
154 if memory_size % X64_LARGE_PAGE_SIZE != 0 {
155 return Err(Error::MemoryUnaligned(memory_size));
156 }
157
158 importer.verify_startup_memory_available(
161 memory_start_address / HV_PAGE_SIZE,
162 memory_page_count,
163 if paravisor_present {
164 StartupMemoryType::Vtl2ProtectableRam
165 } else {
166 StartupMemoryType::Ram
167 },
168 )?;
169
170 let kernel_acceptance = match isolation_type {
171 IsolationType::Snp | IsolationType::Tdx => BootPageAcceptance::Shared,
172 _ => BootPageAcceptance::Exclusive,
173 };
174
175 let mut offset = memory_start_address;
176
177 let bounce_buffer = if matches!(isolation_type, IsolationType::Snp | IsolationType::Tdx) {
186 let bounce_buffer_gpa = offset;
187 assert_eq!(bounce_buffer_gpa % X64_LARGE_PAGE_SIZE, 0);
188 let range = MemoryRange::new(bounce_buffer_gpa..bounce_buffer_gpa + X64_LARGE_PAGE_SIZE);
189
190 offset += range.len();
191 Some(range)
192 } else {
193 None
194 };
195
196 tracing::trace!(offset, "loading the kernel");
197
198 let load_info = crate::elf::load_static_elf(
207 importer,
208 kernel_image,
209 offset,
210 0,
211 true,
212 kernel_acceptance,
213 "underhill-kernel",
214 )
215 .map_err(|e| Error::Kernel(crate::linux::Error::ElfLoader(e)))?;
216 tracing::trace!("Kernel loaded at {load_info:x?}");
217 let crate::elf::LoadInfo {
218 minimum_address_used: _min_addr,
219 next_available_address: mut offset,
220 entrypoint: kernel_entrypoint,
221 } = load_info;
222
223 assert_eq!(offset & (HV_PAGE_SIZE - 1), 0);
224
225 let (sidecar_size, sidecar_entrypoint) = if let Some(sidecar) = sidecar {
227 offset = align_up_to_large_page_size(offset);
229
230 let load_info = crate::elf::load_static_elf(
231 importer,
232 sidecar,
233 0,
234 offset,
235 false,
236 BootPageAcceptance::Exclusive,
237 "sidecar-kernel",
238 )
239 .map_err(|e| Error::Kernel(crate::linux::Error::ElfLoader(e)))?;
240
241 (
242 load_info.next_available_address - offset,
243 load_info.entrypoint,
244 )
245 } else {
246 (0, 0)
247 };
248
249 let sidecar_base = offset;
250 offset += sidecar_size;
251
252 let load_info = crate::elf::load_static_elf(
253 importer,
254 shim,
255 0,
256 offset,
257 false,
258 BootPageAcceptance::Exclusive,
259 "underhill-boot-shim",
260 )
261 .map_err(Error::Shim)?;
262 tracing::trace!("The boot shim loaded at {load_info:x?}");
263 let crate::elf::LoadInfo {
264 minimum_address_used: shim_base_addr,
265 next_available_address: mut offset,
266 entrypoint: shim_entry_address,
267 } = load_info;
268
269 let ramdisk = if let Some(initrd) = initrd {
271 let initrd_base = offset;
272 let initrd_size = align_up_to_page_size(initrd.len() as u64);
273
274 importer.import_pages(
275 initrd_base / HV_PAGE_SIZE,
276 initrd_size / HV_PAGE_SIZE,
277 "underhill-initrd",
278 kernel_acceptance,
279 initrd,
280 )?;
281
282 offset += initrd_size;
283 Some((initrd_base, initrd.len() as u64))
284 } else {
285 None
286 };
287
288 let gdt_base_address = offset;
289 let gdt_size = HV_PAGE_SIZE;
290 offset += gdt_size;
291
292 let boot_params_base = offset;
293 let boot_params_size = HV_PAGE_SIZE;
294
295 offset += boot_params_size;
296
297 let cmdline_base = offset;
298 let (cmdline, policy) = match command_line {
299 CommandLineType::Static(val) => (val, CommandLinePolicy::STATIC),
300 CommandLineType::HostAppendable(val) => (val, CommandLinePolicy::APPEND_CHOSEN),
301 };
302
303 if cmdline.len() > COMMAND_LINE_SIZE {
304 return Err(Error::CommandLineSize(cmdline.len()));
305 }
306
307 let mut static_command_line = [0; COMMAND_LINE_SIZE];
308 static_command_line[..cmdline.len()].copy_from_slice(cmdline.as_bytes());
309 let paravisor_command_line = ParavisorCommandLine {
310 policy,
311 static_command_line_len: cmdline.len() as u16,
312 static_command_line,
313 };
314
315 importer.import_pages(
316 cmdline_base / HV_PAGE_SIZE,
317 1,
318 "underhill-command-line",
319 BootPageAcceptance::Exclusive,
320 paravisor_command_line.as_bytes(),
321 )?;
322
323 offset += HV_PAGE_SIZE;
324
325 let reserved_region_size = PARAVISOR_RESERVED_VTL2_PAGE_COUNT_MAX * HV_PAGE_SIZE;
327 let reserved_region_start = offset;
328 offset += reserved_region_size;
329
330 tracing::debug!(reserved_region_start);
331
332 let parameter_region_size = PARAVISOR_VTL2_CONFIG_REGION_PAGE_COUNT_MAX * HV_PAGE_SIZE;
333 let parameter_region_start = offset;
334 offset += parameter_region_size;
335
336 tracing::debug!(parameter_region_start);
337
338 let end_of_underhill_mem = offset;
340
341 let local_map = match isolation_type {
357 IsolationType::Snp | IsolationType::Tdx => {
358 Some((PARAVISOR_LOCAL_MAP_VA, PARAVISOR_LOCAL_MAP_SIZE))
359 }
360 _ => None,
361 };
362
363 let page_table_base_page_count = 5;
364 let page_table_dynamic_page_count = {
365 calculate_pde_table_count(memory_start_address, memory_size) * 2
367 + local_map.map_or(0, |v| calculate_pde_table_count(v.0, v.1))
368 };
369 let page_table_isolation_page_count = match isolation_type {
370 IsolationType::Tdx => {
371 3
374 }
375 _ => 0,
376 };
377 let page_table_page_count = page_table_base_page_count
378 + page_table_dynamic_page_count
379 + page_table_isolation_page_count;
380 let page_table_region_size = HV_PAGE_SIZE * page_table_page_count;
381 let page_table_region_start = offset;
382 offset += page_table_region_size;
383
384 tracing::debug!(page_table_region_start, page_table_region_size);
385
386 let mut page_table_builder = PageTableBuilder::new(page_table_region_start)
387 .with_mapped_region(memory_start_address, memory_size);
388
389 if let Some((local_map_start, size)) = local_map {
390 page_table_builder = page_table_builder.with_local_map(local_map_start, size);
391 }
392
393 match isolation_type {
394 IsolationType::Snp => {
395 page_table_builder = page_table_builder.with_confidential_bit(51);
396 }
397 IsolationType::Tdx => {
398 page_table_builder = page_table_builder.with_reset_vector(true);
399 }
400 _ => {}
401 }
402
403 let page_table = page_table_builder.build();
404
405 assert!(page_table.len() as u64 % HV_PAGE_SIZE == 0);
406 let page_table_page_base = page_table_region_start / HV_PAGE_SIZE;
407 assert!(page_table.len() as u64 <= page_table_region_size);
408
409 let offset = offset;
410
411 if with_relocation {
412 importer.relocation_region(
414 memory_start_address,
415 end_of_underhill_mem - memory_start_address,
416 X64_LARGE_PAGE_SIZE,
417 PARAVISOR_DEFAULT_MEMORY_BASE_ADDRESS,
418 1 << 48,
419 true,
420 true,
421 0, )?;
423
424 importer.page_table_relocation(
426 page_table_region_start,
427 page_table_region_size / HV_PAGE_SIZE,
428 page_table.len() as u64 / HV_PAGE_SIZE,
429 0,
430 )?;
431 }
432
433 if offset > memory_start_address + memory_size {
435 return Err(Error::NotEnoughMemory(offset - memory_start_address));
436 }
437
438 let (initrd_base, initrd_size) = ramdisk.unwrap_or((0, 0));
439 let calculate_shim_offset = |addr: u64| addr.wrapping_sub(shim_base_addr) as i64;
441 let initrd_crc = crc32fast::hash(initrd.unwrap_or(&[]));
442 let shim_params = ShimParamsRaw {
443 kernel_entry_offset: calculate_shim_offset(kernel_entrypoint),
444 cmdline_offset: calculate_shim_offset(cmdline_base),
445 initrd_offset: calculate_shim_offset(initrd_base),
446 initrd_size,
447 initrd_crc,
448 supported_isolation_type: match isolation_type {
449 IsolationType::None | IsolationType::Vbs => {
453 loader_defs::shim::SupportedIsolationType::VBS
454 }
455 IsolationType::Snp => loader_defs::shim::SupportedIsolationType::SNP,
456 IsolationType::Tdx => loader_defs::shim::SupportedIsolationType::TDX,
457 },
458 memory_start_offset: calculate_shim_offset(memory_start_address),
459 memory_size,
460 parameter_region_offset: calculate_shim_offset(parameter_region_start),
461 parameter_region_size,
462 vtl2_reserved_region_offset: calculate_shim_offset(reserved_region_start),
463 vtl2_reserved_region_size: reserved_region_size,
464 sidecar_offset: calculate_shim_offset(sidecar_base),
465 sidecar_size,
466 sidecar_entry_offset: calculate_shim_offset(sidecar_entrypoint),
467 used_start: calculate_shim_offset(memory_start_address),
468 used_end: calculate_shim_offset(offset),
469 bounce_buffer_start: bounce_buffer.map_or(0, |r| calculate_shim_offset(r.start())),
470 bounce_buffer_size: bounce_buffer.map_or(0, |r| r.len()),
471 };
472
473 tracing::debug!(boot_params_base, "shim gpa");
474
475 importer
476 .import_pages(
477 boot_params_base / HV_PAGE_SIZE,
478 boot_params_size / HV_PAGE_SIZE,
479 "underhill-shim-params",
480 BootPageAcceptance::Exclusive,
481 shim_params.as_bytes(),
482 )
483 .map_err(Error::Importer)?;
484
485 importer.import_pages(
486 page_table_page_base,
487 page_table_page_count,
488 "underhill-page-tables",
489 BootPageAcceptance::Exclusive,
490 &page_table,
491 )?;
492
493 let default_data_attributes: u16 = X64_DEFAULT_DATA_SEGMENT_ATTRIBUTES.into();
506 let default_code64_attributes: u16 = X64_DEFAULT_CODE_SEGMENT_ATTRIBUTES.into();
507 let gdt = [
508 GdtEntry::new_zeroed(),
510 GdtEntry::new_zeroed(),
511 GdtEntry {
513 limit_low: 0xffff,
514 attr_low: default_code64_attributes as u8,
515 attr_high: (default_code64_attributes >> 8) as u8,
516 ..GdtEntry::new_zeroed()
517 },
518 GdtEntry {
520 limit_low: 0xffff,
521 attr_low: default_data_attributes as u8,
522 attr_high: (default_data_attributes >> 8) as u8,
523 ..GdtEntry::new_zeroed()
524 },
525 ];
526
527 const LINEAR_CODE64_DESCRIPTOR_INDEX: usize = 2;
528 const LINEAR_DATA_DESCRIPTOR_INDEX: usize = 3;
529 const RPL: u8 = 0x00; let linear_code64_descriptor_selector =
532 SegmentSelector::from_gdt_index(LINEAR_CODE64_DESCRIPTOR_INDEX as u16, RPL);
533 let linear_data_descriptor_selector =
534 SegmentSelector::from_gdt_index(LINEAR_DATA_DESCRIPTOR_INDEX as u16, RPL);
535
536 importer.import_pages(
537 gdt_base_address / HV_PAGE_SIZE,
538 gdt_size / HV_PAGE_SIZE,
539 "underhill-gdt",
540 BootPageAcceptance::Exclusive,
541 gdt.as_bytes(),
542 )?;
543
544 let mut import_reg = |register| {
545 importer
546 .import_vp_register(register)
547 .map_err(Error::Importer)
548 };
549
550 import_reg(X86Register::Gdtr(TableRegister {
552 base: gdt_base_address,
553 limit: (size_of_val(&gdt) - 1) as u16,
554 }))?;
555
556 let ds = SegmentRegister {
557 selector: linear_data_descriptor_selector.into_bits(),
558 base: 0,
559 limit: 0xffffffff,
560 attributes: default_data_attributes,
561 };
562 import_reg(X86Register::Ds(ds))?;
563 import_reg(X86Register::Es(ds))?;
564 import_reg(X86Register::Fs(ds))?;
565 import_reg(X86Register::Gs(ds))?;
566 import_reg(X86Register::Ss(ds))?;
567
568 let cs = SegmentRegister {
569 selector: linear_code64_descriptor_selector.into_bits(),
570 base: 0,
571 limit: 0xffffffff,
572 attributes: default_code64_attributes,
573 };
574 import_reg(X86Register::Cs(cs))?;
575
576 import_reg(X86Register::Tr(SegmentRegister {
582 selector: 0x0000,
583 base: 0x00000000,
584 limit: 0x0000FFFF,
585 attributes: X64_BUSY_TSS_SEGMENT_ATTRIBUTES.into(),
586 }))?;
587
588 import_reg(X86Register::Cr0(
593 x86defs::X64_CR0_PG | x86defs::X64_CR0_PE | x86defs::X64_CR0_NE,
594 ))?;
595
596 import_reg(X86Register::Cr3(page_table_region_start))?;
598
599 import_reg(X86Register::Cr4(
601 x86defs::X64_CR4_PAE | x86defs::X64_CR4_MCE | x86defs::X64_CR4_OSXSAVE,
602 ))?;
603
604 import_reg(X86Register::Efer(
606 x86defs::X64_EFER_LMA | x86defs::X64_EFER_LME | x86defs::X64_EFER_NXE,
607 ))?;
608
609 import_reg(X86Register::Pat(x86defs::X86X_MSR_DEFAULT_PAT))?;
611
612 let relative_boot_params_base = boot_params_base - shim_base_addr;
615 import_reg(X86Register::Rsi(relative_boot_params_base))?;
616
617 import_reg(X86Register::Rip(shim_entry_address))?;
619
620 let config_region_page_base = parameter_region_start / HV_PAGE_SIZE;
622
623 let slit_page_base = config_region_page_base + PARAVISOR_CONFIG_SLIT_PAGE_INDEX;
625 let slit_parameter_area = importer.create_parameter_area(
626 slit_page_base,
627 PARAVISOR_CONFIG_SLIT_SIZE_PAGES as u32,
628 "underhill-slit",
629 )?;
630 importer.import_parameter(slit_parameter_area, 0, IgvmParameterType::Slit)?;
631
632 let pptt_page_base = config_region_page_base + PARAVISOR_CONFIG_PPTT_PAGE_INDEX;
634 let pptt_parameter_area = importer.create_parameter_area(
635 pptt_page_base,
636 PARAVISOR_CONFIG_PPTT_SIZE_PAGES as u32,
637 "underhill-pptt",
638 )?;
639 importer.import_parameter(pptt_parameter_area, 0, IgvmParameterType::Pptt)?;
640
641 let dt_page_base = config_region_page_base + PARAVISOR_CONFIG_DEVICE_TREE_PAGE_INDEX;
643 let dt_parameter_area = importer.create_parameter_area(
644 dt_page_base,
645 PARAVISOR_CONFIG_DEVICE_TREE_SIZE_PAGES as u32,
646 "underhill-device-tree",
647 )?;
648 importer.import_parameter(dt_parameter_area, 0, IgvmParameterType::DeviceTree)?;
649
650 if isolation_type == IsolationType::Snp {
651 let reserved_region_page_base = reserved_region_start / HV_PAGE_SIZE;
652 let secrets_page_base: u64 =
653 reserved_region_page_base + PARAVISOR_RESERVED_VTL2_SNP_SECRETS_PAGE_INDEX;
654 importer.import_pages(
655 secrets_page_base,
656 PARAVISOR_RESERVED_VTL2_SNP_SECRETS_SIZE_PAGES,
657 "underhill-snp-secrets-page",
658 BootPageAcceptance::SecretsPage,
659 &[],
660 )?;
661
662 let cpuid_page = create_snp_cpuid_page();
663 let cpuid_page_base =
664 reserved_region_page_base + PARAVISOR_RESERVED_VTL2_SNP_CPUID_PAGE_INDEX;
665 importer.import_pages(
666 cpuid_page_base,
667 1,
668 "underhill-snp-cpuid-page",
669 BootPageAcceptance::CpuidPage,
670 cpuid_page.as_bytes(),
671 )?;
672
673 importer.import_pages(
674 cpuid_page_base + 1,
675 1,
676 "underhill-snp-cpuid-extended-state-page",
677 BootPageAcceptance::CpuidExtendedStatePage,
678 &[],
679 )?;
680
681 let vmsa_page_base =
682 reserved_region_page_base + PARAVISOR_RESERVED_VTL2_SNP_VMSA_PAGE_INDEX;
683 importer.set_vp_context_page(vmsa_page_base)?;
684 }
685
686 let mut free_page = 1;
689 let mut measured_config = ParavisorMeasuredVtl0Config {
690 magic: ParavisorMeasuredVtl0Config::MAGIC,
691 ..FromZeros::new_zeroed()
692 };
693
694 let Vtl0Config {
695 supports_pcat,
696 supports_uefi,
697 supports_linux,
698 } = vtl0_config;
699
700 if supports_pcat {
701 measured_config.supported_vtl0.set_pcat_supported(true);
702 }
703
704 if let Some((uefi, vp_context)) = &supports_uefi {
705 measured_config.supported_vtl0.set_uefi_supported(true);
706 let vp_context_page = free_page;
707 free_page += 1;
708 measured_config.uefi_info = UefiInfo {
709 firmware: PageRegionDescriptor {
710 base_page_number: uefi.firmware_base / HV_PAGE_SIZE,
711 page_count: uefi.total_size / HV_PAGE_SIZE,
712 },
713 vtl0_vp_context: PageRegionDescriptor {
714 base_page_number: vp_context_page,
715 page_count: 1,
716 },
717 };
718
719 importer.import_pages(
721 vp_context_page,
722 1,
723 "openhcl-uefi-vp-context",
724 BootPageAcceptance::Exclusive,
725 vp_context,
726 )?;
727 }
728
729 if let Some(linux) = supports_linux {
730 measured_config
731 .supported_vtl0
732 .set_linux_direct_supported(true);
733
734 let kernel_region = PageRegionDescriptor::new(
735 linux.load_info.kernel.gpa / HV_PAGE_SIZE,
736 align_up_to_page_size(linux.load_info.kernel.size) / HV_PAGE_SIZE,
737 );
738
739 let (initrd_region, initrd_size) = match linux.load_info.initrd {
740 Some(info) => {
741 if info.gpa % HV_PAGE_SIZE != 0 {
742 return Err(Error::MemoryUnaligned(info.gpa));
743 }
744 (
745 PageRegionDescriptor::new(
747 info.gpa / HV_PAGE_SIZE,
748 align_up_to_page_size(info.size) / HV_PAGE_SIZE,
749 ),
750 info.size,
751 )
752 }
753 None => (PageRegionDescriptor::EMPTY, 0),
754 };
755
756 let command_line_page = free_page;
757 importer
761 .import_pages(
762 command_line_page,
763 1,
764 "underhill-vtl0-linux-command-line",
765 BootPageAcceptance::Exclusive,
766 linux.command_line.as_bytes_with_nul(),
767 )
768 .map_err(Error::Importer)?;
769 let command_line = PageRegionDescriptor::new(command_line_page, 1);
770
771 measured_config.linux_info = LinuxInfo {
772 kernel_region,
773 kernel_entrypoint: linux.load_info.kernel.entrypoint,
774 initrd_region,
775 initrd_size,
776 command_line,
777 };
778 }
779
780 importer
781 .import_pages(
782 PARAVISOR_VTL0_MEASURED_CONFIG_BASE_PAGE_X64,
783 1,
784 "underhill-measured-config",
785 BootPageAcceptance::Exclusive,
786 measured_config.as_bytes(),
787 )
788 .map_err(Error::Importer)?;
789
790 let vtl2_measured_config = ParavisorMeasuredVtl2Config {
791 magic: ParavisorMeasuredVtl2Config::MAGIC,
792 vtom_offset_bit: shared_gpa_boundary_bits.unwrap_or(0),
793 padding: [0; 7],
794 };
795
796 importer
797 .import_pages(
798 config_region_page_base + PARAVISOR_MEASURED_VTL2_CONFIG_PAGE_INDEX,
799 PARAVISOR_MEASURED_VTL2_CONFIG_SIZE_PAGES,
800 "underhill-vtl2-measured-config",
801 BootPageAcceptance::Exclusive,
802 vtl2_measured_config.as_bytes(),
803 )
804 .map_err(Error::Importer)?;
805
806 let imported_region_base =
807 config_region_page_base + PARAVISOR_MEASURED_VTL2_CONFIG_ACCEPTED_MEMORY_PAGE_INDEX;
808
809 importer.set_imported_regions_config_page(imported_region_base);
810 Ok(())
811}
812
813fn create_snp_cpuid_page() -> HV_PSP_CPUID_PAGE {
815 let mut cpuid_page = HV_PSP_CPUID_PAGE::default();
816
817 for (i, required_leaf) in crate::cpuid::SNP_REQUIRED_CPUID_LEAF_LIST_PARAVISOR
823 .iter()
824 .enumerate()
825 {
826 let entry = &mut cpuid_page.cpuid_leaf_info[i];
827 entry.eax_in = required_leaf.eax;
828 entry.ecx_in = required_leaf.ecx;
829 if required_leaf.eax == CpuidFunction::ExtendedStateEnumeration.0 {
830 entry.xfem_in = 1;
831 }
832 cpuid_page.count += 1;
833 }
834
835 cpuid_page
836}
837
838pub fn load_openhcl_arm64<F>(
845 importer: &mut dyn ImageLoad<Aarch64Register>,
846 kernel_image: &mut F,
847 shim: &mut F,
848 command_line: CommandLineType<'_>,
849 initrd: Option<&[u8]>,
850 memory_page_base: Option<u64>,
851 memory_page_count: u64,
852 vtl0_config: Vtl0Config<'_>,
853) -> Result<(), Error>
854where
855 F: std::io::Read + std::io::Seek,
856{
857 let Vtl0Config {
858 supports_pcat,
859 supports_uefi,
860 supports_linux,
861 } = vtl0_config;
862
863 assert!(!supports_pcat);
864 assert!(supports_uefi.is_some() || supports_linux.is_some());
865
866 let paravisor_present = importer.isolation_config().paravisor_present;
867
868 let with_relocation = memory_page_base.is_none();
870
871 let memory_start_address = memory_page_base
872 .map(|page_number| page_number * HV_PAGE_SIZE)
873 .unwrap_or(PARAVISOR_DEFAULT_MEMORY_BASE_ADDRESS);
874
875 let memory_size = memory_page_count * HV_PAGE_SIZE;
876
877 if memory_start_address % u64::from(Arm64PageSize::Large) != 0 {
879 return Err(Error::MemoryUnaligned(memory_start_address));
880 }
881
882 if memory_size % u64::from(Arm64PageSize::Large) != 0 {
883 return Err(Error::MemoryUnaligned(memory_size));
884 }
885
886 importer.verify_startup_memory_available(
889 memory_start_address / HV_PAGE_SIZE,
890 memory_page_count,
891 if paravisor_present {
892 StartupMemoryType::Vtl2ProtectableRam
893 } else {
894 StartupMemoryType::Ram
895 },
896 )?;
897
898 tracing::trace!(memory_start_address, "loading the kernel");
899
900 let initrd_address_type = InitrdAddressType::AfterKernel;
904 let initrd_config = InitrdConfig {
905 initrd_address: initrd_address_type,
906 initrd: initrd.unwrap_or_default(),
907 };
908 let device_tree_blob = None;
909 let crate::linux::LoadInfo {
910 kernel:
911 KernelInfo {
912 gpa: kernel_base,
913 size: kernel_size,
914 entrypoint: kernel_entry_point,
915 },
916 initrd: initrd_info,
917 dtb,
918 } = load_kernel_and_initrd_arm64(
919 importer,
920 kernel_image,
921 memory_start_address,
922 Some(initrd_config),
923 device_tree_blob,
924 )
925 .map_err(Error::Kernel)?;
926
927 assert!(
928 dtb.is_none(),
929 "DeviceTree is generated dynamically by the boot shim."
930 );
931
932 tracing::trace!(kernel_base, "kernel loaded");
933
934 let mut next_addr;
935
936 let InitrdInfo {
937 gpa: initrd_gpa,
938 size: initrd_size,
939 } = if let Some(initrd_info) = initrd_info {
940 assert!(initrd_address_type == InitrdAddressType::AfterKernel);
941 next_addr = initrd_info.gpa + initrd_info.size;
942 initrd_info
943 } else {
944 next_addr = kernel_base + kernel_size;
945 InitrdInfo { gpa: 0, size: 0 }
946 };
947
948 next_addr = align_up_to_page_size(next_addr);
949
950 tracing::trace!(next_addr, "loading the boot shim");
951
952 let crate::elf::LoadInfo {
953 minimum_address_used: shim_base_addr,
954 next_available_address: mut next_addr,
955 entrypoint: shim_entry_point,
956 } = crate::elf::load_static_elf(
957 importer,
958 shim,
959 0,
960 next_addr,
961 false,
962 BootPageAcceptance::Exclusive,
963 "underhill-boot-shim",
964 )
965 .map_err(Error::Shim)?;
966
967 tracing::trace!(shim_base_addr, "boot shim loaded");
968
969 tracing::trace!(next_addr, "loading the command line");
970
971 let cmdline_base = next_addr;
972 let (cmdline, policy) = match command_line {
973 CommandLineType::Static(val) => (val, CommandLinePolicy::STATIC),
974 CommandLineType::HostAppendable(val) => (val, CommandLinePolicy::APPEND_CHOSEN),
975 };
976
977 if cmdline.len() > COMMAND_LINE_SIZE {
978 return Err(Error::CommandLineSize(cmdline.len()));
979 }
980
981 let mut static_command_line = [0; COMMAND_LINE_SIZE];
982 static_command_line[..cmdline.len()].copy_from_slice(cmdline.as_bytes());
983 let paravisor_command_line = ParavisorCommandLine {
984 policy,
985 static_command_line_len: cmdline.len() as u16,
986 static_command_line,
987 };
988
989 importer.import_pages(
990 cmdline_base / HV_PAGE_SIZE,
991 1,
992 "underhill-command-line",
993 BootPageAcceptance::Exclusive,
994 paravisor_command_line.as_bytes(),
995 )?;
996
997 next_addr += HV_PAGE_SIZE;
998
999 tracing::trace!(next_addr, "loading the boot shim parameters");
1000
1001 let shim_params_base = next_addr;
1002 let shim_params_size = HV_PAGE_SIZE;
1003
1004 next_addr += shim_params_size;
1005
1006 let parameter_region_size = PARAVISOR_VTL2_CONFIG_REGION_PAGE_COUNT_MAX * HV_PAGE_SIZE;
1007 let parameter_region_start = next_addr;
1008 next_addr += parameter_region_size;
1009
1010 tracing::debug!(parameter_region_start);
1011
1012 let end_of_underhill_mem = next_addr;
1014
1015 let page_table_base_page_count = 5;
1018 let page_table_dynamic_page_count = 2 * page_table_base_page_count;
1019 let page_table_page_count = page_table_base_page_count + page_table_dynamic_page_count;
1020 let page_table_region_size = HV_PAGE_SIZE * page_table_page_count;
1021 let page_table_region_start = next_addr;
1022 next_addr += page_table_region_size;
1023
1024 tracing::debug!(page_table_region_start, page_table_region_size);
1025
1026 let next_addr = next_addr;
1027
1028 if next_addr > memory_start_address + memory_size {
1030 return Err(Error::NotEnoughMemory(next_addr - memory_start_address));
1031 }
1032
1033 let calculate_shim_offset = |addr: u64| -> i64 { addr.wrapping_sub(shim_base_addr) as i64 };
1035 let initrd_crc = crc32fast::hash(initrd.unwrap_or(&[]));
1036 let shim_params = ShimParamsRaw {
1037 kernel_entry_offset: calculate_shim_offset(kernel_entry_point),
1038 cmdline_offset: calculate_shim_offset(cmdline_base),
1039 initrd_offset: calculate_shim_offset(initrd_gpa),
1040 initrd_size,
1041 initrd_crc,
1042 supported_isolation_type: match importer.isolation_config().isolation_type {
1043 IsolationType::None | IsolationType::Vbs => {
1044 loader_defs::shim::SupportedIsolationType::VBS
1045 }
1046 _ => panic!("only None and VBS are supported for ARM64"),
1047 },
1048 memory_start_offset: calculate_shim_offset(memory_start_address),
1049 memory_size,
1050 parameter_region_offset: calculate_shim_offset(parameter_region_start),
1051 parameter_region_size,
1052 vtl2_reserved_region_offset: 0,
1053 vtl2_reserved_region_size: 0,
1054 sidecar_offset: 0,
1055 sidecar_size: 0,
1056 sidecar_entry_offset: 0,
1057 used_start: calculate_shim_offset(memory_start_address),
1058 used_end: calculate_shim_offset(next_addr),
1059 bounce_buffer_start: 0,
1060 bounce_buffer_size: 0,
1061 };
1062
1063 importer
1064 .import_pages(
1065 shim_params_base / HV_PAGE_SIZE,
1066 shim_params_size / HV_PAGE_SIZE,
1067 "underhill-shim-params",
1068 BootPageAcceptance::Exclusive,
1069 shim_params.as_bytes(),
1070 )
1071 .map_err(Error::Importer)?;
1072
1073 let mut measured_config = ParavisorMeasuredVtl0Config {
1074 magic: ParavisorMeasuredVtl0Config::MAGIC,
1075 ..FromZeros::new_zeroed()
1076 };
1077
1078 if let Some((uefi, vp_context)) = &supports_uefi {
1079 measured_config.supported_vtl0.set_uefi_supported(true);
1080 let vp_context_page = PARAVISOR_VTL0_MEASURED_CONFIG_BASE_PAGE_AARCH64 + 1;
1081 measured_config.uefi_info = UefiInfo {
1082 firmware: PageRegionDescriptor {
1083 base_page_number: uefi.firmware_base / HV_PAGE_SIZE,
1084 page_count: uefi.total_size / HV_PAGE_SIZE,
1085 },
1086 vtl0_vp_context: PageRegionDescriptor {
1087 base_page_number: vp_context_page,
1088 page_count: 1,
1089 },
1090 };
1091
1092 importer.import_pages(
1094 vp_context_page,
1095 1,
1096 "openhcl-uefi-vp-context",
1097 BootPageAcceptance::Exclusive,
1098 vp_context,
1099 )?;
1100 }
1101
1102 importer
1103 .import_pages(
1104 PARAVISOR_VTL0_MEASURED_CONFIG_BASE_PAGE_AARCH64,
1105 1,
1106 "underhill-measured-config",
1107 BootPageAcceptance::Exclusive,
1108 measured_config.as_bytes(),
1109 )
1110 .map_err(Error::Importer)?;
1111
1112 tracing::trace!(page_table_region_start, "loading the page tables");
1113
1114 let memory_attribute_indirection = MemoryAttributeIndirectionEl1([
1115 MemoryAttributeEl1::Device_nGnRnE,
1116 MemoryAttributeEl1::Normal_NonCacheable,
1117 MemoryAttributeEl1::Normal_WriteThrough,
1118 MemoryAttributeEl1::Normal_WriteBack,
1119 MemoryAttributeEl1::Device_nGnRnE,
1120 MemoryAttributeEl1::Device_nGnRnE,
1121 MemoryAttributeEl1::Device_nGnRnE,
1122 MemoryAttributeEl1::Device_nGnRnE,
1123 ]);
1124 let page_tables = page_table::aarch64::build_identity_page_tables_aarch64(
1125 page_table_region_start,
1126 memory_start_address,
1127 memory_size,
1128 memory_attribute_indirection,
1129 page_table_region_size as usize,
1130 );
1131 assert!(page_tables.len() as u64 % HV_PAGE_SIZE == 0);
1132 let page_table_page_base = page_table_region_start / HV_PAGE_SIZE;
1133 assert!(page_tables.len() as u64 <= page_table_region_size);
1134 assert!(page_table_region_size as usize > page_tables.len());
1135
1136 if with_relocation {
1137 importer.relocation_region(
1139 memory_start_address,
1140 end_of_underhill_mem - memory_start_address,
1141 Arm64PageSize::Large.into(),
1142 PARAVISOR_DEFAULT_MEMORY_BASE_ADDRESS,
1143 1 << 48,
1144 true,
1145 false,
1146 0, )?;
1148
1149 importer.page_table_relocation(
1151 page_table_region_start,
1152 page_table_region_size / HV_PAGE_SIZE,
1153 page_tables.len() as u64 / HV_PAGE_SIZE,
1154 0,
1155 )?;
1156 }
1157
1158 importer.import_pages(
1159 page_table_page_base,
1160 page_table_page_count,
1161 "underhill-page-tables",
1162 BootPageAcceptance::Exclusive,
1163 &page_tables,
1164 )?;
1165
1166 tracing::trace!("Importing register state");
1167
1168 let mut import_reg = |register| {
1169 importer
1170 .import_vp_register(register)
1171 .map_err(Error::Importer)
1172 };
1173
1174 let relative_boot_params_base = shim_params_base - shim_base_addr;
1176 import_reg(AArch64Register::X0(relative_boot_params_base).into())?;
1177
1178 import_reg(AArch64Register::Pc(shim_entry_point).into())?;
1180
1181 import_reg(AArch64Register::Cpsr(Cpsr64::new().with_sp(true).with_el(1).into()).into())?;
1184
1185 import_reg(
1208 AArch64Register::SctlrEl1(
1209 SctlrEl1::new()
1210 .with_m(true)
1215 .with_c(true)
1217 .with_i(true)
1219 .with_eos(true)
1221 .with_tscxt(true)
1222 .with_eis(true)
1223 .with_span(true)
1224 .with_n_tlsmd(true)
1225 .with_lsmaoe(true)
1226 .into(),
1227 )
1228 .into(),
1229 )?;
1230
1231 import_reg(
1237 AArch64Register::TcrEl1(
1238 TranslationControlEl1::new()
1239 .with_t0sz(0x11)
1240 .with_irgn0(1)
1241 .with_orgn0(1)
1242 .with_sh0(3)
1243 .with_tg0(TranslationGranule0::TG_4KB)
1244 .with_epd1(1)
1246 .with_tg1(TranslationGranule1::TG_4KB)
1248 .with_ips(IntermPhysAddrSize::IPA_48_BITS_256_TB)
1249 .into(),
1250 )
1251 .into(),
1252 )?;
1253
1254 import_reg(AArch64Register::MairEl1(memory_attribute_indirection.into()).into())?;
1256 import_reg(
1257 AArch64Register::Ttbr0El1(
1258 TranslationBaseEl1::new()
1259 .with_baddr(page_table_region_start)
1260 .into(),
1261 )
1262 .into(),
1263 )?;
1264
1265 import_reg(AArch64Register::VbarEl1(0).into())?;
1269
1270 let config_region_page_base = parameter_region_start / HV_PAGE_SIZE;
1272
1273 let slit_page_base = config_region_page_base + PARAVISOR_CONFIG_SLIT_PAGE_INDEX;
1275 let slit_parameter_area = importer.create_parameter_area(
1276 slit_page_base,
1277 PARAVISOR_CONFIG_SLIT_SIZE_PAGES as u32,
1278 "underhill-slit",
1279 )?;
1280 importer.import_parameter(slit_parameter_area, 0, IgvmParameterType::Slit)?;
1281
1282 let pptt_page_base = config_region_page_base + PARAVISOR_CONFIG_PPTT_PAGE_INDEX;
1284 let pptt_parameter_area = importer.create_parameter_area(
1285 pptt_page_base,
1286 PARAVISOR_CONFIG_PPTT_SIZE_PAGES as u32,
1287 "underhill-pptt",
1288 )?;
1289 importer.import_parameter(pptt_parameter_area, 0, IgvmParameterType::Pptt)?;
1290
1291 let dt_page_base = config_region_page_base + PARAVISOR_CONFIG_DEVICE_TREE_PAGE_INDEX;
1293 let dt_parameter_area = importer.create_parameter_area(
1294 dt_page_base,
1295 PARAVISOR_CONFIG_DEVICE_TREE_SIZE_PAGES as u32,
1296 "underhill-device-tree",
1297 )?;
1298 importer.import_parameter(dt_parameter_area, 0, IgvmParameterType::DeviceTree)?;
1299
1300 let vtl2_measured_config = ParavisorMeasuredVtl2Config {
1301 magic: ParavisorMeasuredVtl2Config::MAGIC,
1302 vtom_offset_bit: 0,
1303 padding: [0; 7],
1304 };
1305
1306 importer
1307 .import_pages(
1308 config_region_page_base + PARAVISOR_MEASURED_VTL2_CONFIG_PAGE_INDEX,
1309 PARAVISOR_MEASURED_VTL2_CONFIG_SIZE_PAGES,
1310 "underhill-vtl2-measured-config",
1311 BootPageAcceptance::Exclusive,
1312 vtl2_measured_config.as_bytes(),
1313 )
1314 .map_err(Error::Importer)?;
1315
1316 let imported_region_base =
1317 config_region_page_base + PARAVISOR_MEASURED_VTL2_CONFIG_ACCEPTED_MEMORY_PAGE_INDEX;
1318
1319 importer.set_imported_regions_config_page(imported_region_base);
1320
1321 Ok(())
1322}