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.is_multiple_of(X64_LARGE_PAGE_SIZE) {
153 return Err(Error::MemoryUnaligned(memory_start_address));
154 }
155
156 if !memory_size.is_multiple_of(X64_LARGE_PAGE_SIZE) {
157 return Err(Error::MemoryUnaligned(memory_size));
158 }
159
160 importer.verify_startup_memory_available(
163 memory_start_address / HV_PAGE_SIZE,
164 memory_page_count,
165 if paravisor_present {
166 StartupMemoryType::Vtl2ProtectableRam
167 } else {
168 StartupMemoryType::Ram
169 },
170 )?;
171
172 let kernel_acceptance = match isolation_type {
173 IsolationType::Snp | IsolationType::Tdx => BootPageAcceptance::Shared,
174 _ => BootPageAcceptance::Exclusive,
175 };
176
177 let mut offset = memory_start_address;
178
179 let bounce_buffer = if matches!(isolation_type, IsolationType::Snp | IsolationType::Tdx) {
188 let bounce_buffer_gpa = offset;
189 assert_eq!(bounce_buffer_gpa % X64_LARGE_PAGE_SIZE, 0);
190 let range = MemoryRange::new(bounce_buffer_gpa..bounce_buffer_gpa + X64_LARGE_PAGE_SIZE);
191
192 offset += range.len();
193 Some(range)
194 } else {
195 None
196 };
197
198 tracing::trace!(offset, "loading the kernel");
199
200 let load_info = crate::elf::load_static_elf(
209 importer,
210 kernel_image,
211 offset,
212 0,
213 true,
214 kernel_acceptance,
215 "underhill-kernel",
216 )
217 .map_err(|e| Error::Kernel(crate::linux::Error::ElfLoader(e)))?;
218 tracing::trace!("Kernel loaded at {load_info:x?}");
219 let crate::elf::LoadInfo {
220 minimum_address_used: _min_addr,
221 next_available_address: mut offset,
222 entrypoint: kernel_entrypoint,
223 } = load_info;
224
225 assert_eq!(offset & (HV_PAGE_SIZE - 1), 0);
226
227 let (sidecar_size, sidecar_entrypoint) = if let Some(sidecar) = sidecar {
229 offset = align_up_to_large_page_size(offset);
231
232 let load_info = crate::elf::load_static_elf(
233 importer,
234 sidecar,
235 0,
236 offset,
237 false,
238 BootPageAcceptance::Exclusive,
239 "sidecar-kernel",
240 )
241 .map_err(|e| Error::Kernel(crate::linux::Error::ElfLoader(e)))?;
242
243 (
244 load_info.next_available_address - offset,
245 load_info.entrypoint,
246 )
247 } else {
248 (0, 0)
249 };
250
251 let sidecar_base = offset;
252 offset += sidecar_size;
253
254 let load_info = crate::elf::load_static_elf(
255 importer,
256 shim,
257 0,
258 offset,
259 false,
260 BootPageAcceptance::Exclusive,
261 "underhill-boot-shim",
262 )
263 .map_err(Error::Shim)?;
264 tracing::trace!("The boot shim loaded at {load_info:x?}");
265 let crate::elf::LoadInfo {
266 minimum_address_used: shim_base_addr,
267 next_available_address: mut offset,
268 entrypoint: shim_entry_address,
269 } = load_info;
270
271 let ramdisk = if let Some(initrd) = initrd {
273 let initrd_base = offset;
274 let initrd_size = align_up_to_page_size(initrd.len() as u64);
275
276 importer.import_pages(
277 initrd_base / HV_PAGE_SIZE,
278 initrd_size / HV_PAGE_SIZE,
279 "underhill-initrd",
280 kernel_acceptance,
281 initrd,
282 )?;
283
284 offset += initrd_size;
285 Some((initrd_base, initrd.len() as u64))
286 } else {
287 None
288 };
289
290 let gdt_base_address = offset;
291 let gdt_size = HV_PAGE_SIZE;
292 offset += gdt_size;
293
294 let boot_params_base = offset;
295 let boot_params_size = HV_PAGE_SIZE;
296
297 offset += boot_params_size;
298
299 let cmdline_base = offset;
300 let (cmdline, policy) = match command_line {
301 CommandLineType::Static(val) => (val, CommandLinePolicy::STATIC),
302 CommandLineType::HostAppendable(val) => (val, CommandLinePolicy::APPEND_CHOSEN),
303 };
304
305 if cmdline.len() > COMMAND_LINE_SIZE {
306 return Err(Error::CommandLineSize(cmdline.len()));
307 }
308
309 let mut static_command_line = [0; COMMAND_LINE_SIZE];
310 static_command_line[..cmdline.len()].copy_from_slice(cmdline.as_bytes());
311 let paravisor_command_line = ParavisorCommandLine {
312 policy,
313 static_command_line_len: cmdline.len() as u16,
314 static_command_line,
315 };
316
317 importer.import_pages(
318 cmdline_base / HV_PAGE_SIZE,
319 1,
320 "underhill-command-line",
321 BootPageAcceptance::Exclusive,
322 paravisor_command_line.as_bytes(),
323 )?;
324
325 offset += HV_PAGE_SIZE;
326
327 let reserved_region_size = PARAVISOR_RESERVED_VTL2_PAGE_COUNT_MAX * HV_PAGE_SIZE;
329 let reserved_region_start = offset;
330 offset += reserved_region_size;
331
332 tracing::debug!(reserved_region_start);
333
334 let parameter_region_size = PARAVISOR_VTL2_CONFIG_REGION_PAGE_COUNT_MAX * HV_PAGE_SIZE;
335 let parameter_region_start = offset;
336 offset += parameter_region_size;
337
338 tracing::debug!(parameter_region_start);
339
340 let bootshim_log_size = HV_PAGE_SIZE * 2;
343 let bootshim_log_start = offset;
344 offset += bootshim_log_size;
345
346 importer.import_pages(
347 bootshim_log_start / HV_PAGE_SIZE,
348 bootshim_log_size / HV_PAGE_SIZE,
349 "ohcl-boot-shim-log-buffer",
350 BootPageAcceptance::Exclusive,
351 &[],
352 )?;
353
354 let heap_start = offset;
361 let heap_size = 16 * HV_PAGE_SIZE;
362 importer.import_pages(
363 heap_start / HV_PAGE_SIZE,
364 heap_size / HV_PAGE_SIZE,
365 "ohcl-boot-shim-heap",
366 BootPageAcceptance::Exclusive,
367 &[],
368 )?;
369 offset += heap_size;
370
371 let end_of_underhill_mem = offset;
373
374 let local_map = match isolation_type {
390 IsolationType::Snp | IsolationType::Tdx => {
391 Some((PARAVISOR_LOCAL_MAP_VA, PARAVISOR_LOCAL_MAP_SIZE))
392 }
393 _ => None,
394 };
395
396 let page_table_mapping_size = if isolation_type == IsolationType::Tdx {
412 3 * 1024 * 1024 * 1024
413 } else {
414 memory_size
415 };
416
417 let page_table_base_page_count = 5;
418 let page_table_dynamic_page_count = {
419 calculate_pde_table_count(memory_start_address, page_table_mapping_size) * 2
421 + local_map.map_or(0, |v| calculate_pde_table_count(v.0, v.1))
422 };
423 let page_table_isolation_page_count = match isolation_type {
424 IsolationType::Tdx => {
425 3
428 }
429 _ => 0,
430 };
431 let page_table_page_count = page_table_base_page_count
432 + page_table_dynamic_page_count
433 + page_table_isolation_page_count;
434 let page_table_region_size = HV_PAGE_SIZE * page_table_page_count;
435 let page_table_region_start = offset;
436 offset += page_table_region_size;
437
438 tracing::debug!(page_table_region_start, page_table_region_size);
439
440 let mut page_table_builder = PageTableBuilder::new(page_table_region_start)
441 .with_mapped_region(memory_start_address, page_table_mapping_size);
442
443 if let Some((local_map_start, size)) = local_map {
444 page_table_builder = page_table_builder.with_local_map(local_map_start, size);
445 }
446
447 match isolation_type {
448 IsolationType::Snp => {
449 page_table_builder = page_table_builder.with_confidential_bit(51);
450 }
451 IsolationType::Tdx => {
452 page_table_builder = page_table_builder.with_reset_vector(true);
453 }
454 _ => {}
455 }
456
457 let page_table = page_table_builder.build();
458
459 assert!((page_table.len() as u64).is_multiple_of(HV_PAGE_SIZE));
460 let page_table_page_base = page_table_region_start / HV_PAGE_SIZE;
461 assert!(page_table.len() as u64 <= page_table_region_size);
462
463 let offset = offset;
464
465 if with_relocation {
466 importer.relocation_region(
468 memory_start_address,
469 end_of_underhill_mem - memory_start_address,
470 X64_LARGE_PAGE_SIZE,
471 PARAVISOR_DEFAULT_MEMORY_BASE_ADDRESS,
472 1 << 48,
473 true,
474 true,
475 0, )?;
477
478 importer.page_table_relocation(
480 page_table_region_start,
481 page_table_region_size / HV_PAGE_SIZE,
482 page_table.len() as u64 / HV_PAGE_SIZE,
483 0,
484 )?;
485 }
486
487 if offset > memory_start_address + memory_size {
489 return Err(Error::NotEnoughMemory(offset - memory_start_address));
490 }
491
492 let (initrd_base, initrd_size) = ramdisk.unwrap_or((0, 0));
493 let calculate_shim_offset = |addr: u64| addr.wrapping_sub(shim_base_addr) as i64;
495 let initrd_crc = crc32fast::hash(initrd.unwrap_or(&[]));
496 let shim_params = ShimParamsRaw {
497 kernel_entry_offset: calculate_shim_offset(kernel_entrypoint),
498 cmdline_offset: calculate_shim_offset(cmdline_base),
499 initrd_offset: calculate_shim_offset(initrd_base),
500 initrd_size,
501 initrd_crc,
502 supported_isolation_type: match isolation_type {
503 IsolationType::None | IsolationType::Vbs => {
507 loader_defs::shim::SupportedIsolationType::VBS
508 }
509 IsolationType::Snp => loader_defs::shim::SupportedIsolationType::SNP,
510 IsolationType::Tdx => loader_defs::shim::SupportedIsolationType::TDX,
511 },
512 memory_start_offset: calculate_shim_offset(memory_start_address),
513 memory_size,
514 parameter_region_offset: calculate_shim_offset(parameter_region_start),
515 parameter_region_size,
516 vtl2_reserved_region_offset: calculate_shim_offset(reserved_region_start),
517 vtl2_reserved_region_size: reserved_region_size,
518 sidecar_offset: calculate_shim_offset(sidecar_base),
519 sidecar_size,
520 sidecar_entry_offset: calculate_shim_offset(sidecar_entrypoint),
521 used_start: calculate_shim_offset(memory_start_address),
522 used_end: calculate_shim_offset(offset),
523 bounce_buffer_start: bounce_buffer.map_or(0, |r| calculate_shim_offset(r.start())),
524 bounce_buffer_size: bounce_buffer.map_or(0, |r| r.len()),
525 page_tables_start: calculate_shim_offset(page_table_region_start),
526 page_tables_size: page_table_region_size,
527 log_buffer_start: calculate_shim_offset(bootshim_log_start),
528 log_buffer_size: bootshim_log_size,
529 heap_start_offset: calculate_shim_offset(heap_start),
530 heap_size,
531 };
532
533 tracing::debug!(boot_params_base, "shim gpa");
534
535 importer
536 .import_pages(
537 boot_params_base / HV_PAGE_SIZE,
538 boot_params_size / HV_PAGE_SIZE,
539 "underhill-shim-params",
540 BootPageAcceptance::Exclusive,
541 shim_params.as_bytes(),
542 )
543 .map_err(Error::Importer)?;
544
545 importer.import_pages(
546 page_table_page_base,
547 page_table_page_count,
548 "underhill-page-tables",
549 BootPageAcceptance::Exclusive,
550 &page_table,
551 )?;
552
553 let default_data_attributes: u16 = X64_DEFAULT_DATA_SEGMENT_ATTRIBUTES.into();
566 let default_code64_attributes: u16 = X64_DEFAULT_CODE_SEGMENT_ATTRIBUTES.into();
567 let gdt = [
568 GdtEntry::new_zeroed(),
570 GdtEntry::new_zeroed(),
571 GdtEntry {
573 limit_low: 0xffff,
574 attr_low: default_code64_attributes as u8,
575 attr_high: (default_code64_attributes >> 8) as u8,
576 ..GdtEntry::new_zeroed()
577 },
578 GdtEntry {
580 limit_low: 0xffff,
581 attr_low: default_data_attributes as u8,
582 attr_high: (default_data_attributes >> 8) as u8,
583 ..GdtEntry::new_zeroed()
584 },
585 ];
586
587 const LINEAR_CODE64_DESCRIPTOR_INDEX: usize = 2;
588 const LINEAR_DATA_DESCRIPTOR_INDEX: usize = 3;
589 const RPL: u8 = 0x00; let linear_code64_descriptor_selector =
592 SegmentSelector::from_gdt_index(LINEAR_CODE64_DESCRIPTOR_INDEX as u16, RPL);
593 let linear_data_descriptor_selector =
594 SegmentSelector::from_gdt_index(LINEAR_DATA_DESCRIPTOR_INDEX as u16, RPL);
595
596 importer.import_pages(
597 gdt_base_address / HV_PAGE_SIZE,
598 gdt_size / HV_PAGE_SIZE,
599 "underhill-gdt",
600 BootPageAcceptance::Exclusive,
601 gdt.as_bytes(),
602 )?;
603
604 let mut import_reg = |register| {
605 importer
606 .import_vp_register(register)
607 .map_err(Error::Importer)
608 };
609
610 import_reg(X86Register::Gdtr(TableRegister {
612 base: gdt_base_address,
613 limit: (size_of_val(&gdt) - 1) as u16,
614 }))?;
615
616 let ds = SegmentRegister {
617 selector: linear_data_descriptor_selector.into_bits(),
618 base: 0,
619 limit: 0xffffffff,
620 attributes: default_data_attributes,
621 };
622 import_reg(X86Register::Ds(ds))?;
623 import_reg(X86Register::Es(ds))?;
624 import_reg(X86Register::Fs(ds))?;
625 import_reg(X86Register::Gs(ds))?;
626 import_reg(X86Register::Ss(ds))?;
627
628 let cs = SegmentRegister {
629 selector: linear_code64_descriptor_selector.into_bits(),
630 base: 0,
631 limit: 0xffffffff,
632 attributes: default_code64_attributes,
633 };
634 import_reg(X86Register::Cs(cs))?;
635
636 import_reg(X86Register::Tr(SegmentRegister {
642 selector: 0x0000,
643 base: 0x00000000,
644 limit: 0x0000FFFF,
645 attributes: X64_BUSY_TSS_SEGMENT_ATTRIBUTES.into(),
646 }))?;
647
648 import_reg(X86Register::Cr0(
653 x86defs::X64_CR0_PG | x86defs::X64_CR0_PE | x86defs::X64_CR0_NE,
654 ))?;
655
656 import_reg(X86Register::Cr3(page_table_region_start))?;
658
659 import_reg(X86Register::Cr4(
661 x86defs::X64_CR4_PAE | x86defs::X64_CR4_MCE | x86defs::X64_CR4_OSXSAVE,
662 ))?;
663
664 import_reg(X86Register::Efer(
666 x86defs::X64_EFER_LMA | x86defs::X64_EFER_LME | x86defs::X64_EFER_NXE,
667 ))?;
668
669 import_reg(X86Register::Pat(x86defs::X86X_MSR_DEFAULT_PAT))?;
671
672 let relative_boot_params_base = boot_params_base - shim_base_addr;
675 import_reg(X86Register::Rsi(relative_boot_params_base))?;
676
677 import_reg(X86Register::Rip(shim_entry_address))?;
679
680 let config_region_page_base = parameter_region_start / HV_PAGE_SIZE;
682
683 let slit_page_base = config_region_page_base + PARAVISOR_CONFIG_SLIT_PAGE_INDEX;
685 let slit_parameter_area = importer.create_parameter_area(
686 slit_page_base,
687 PARAVISOR_CONFIG_SLIT_SIZE_PAGES as u32,
688 "underhill-slit",
689 )?;
690 importer.import_parameter(slit_parameter_area, 0, IgvmParameterType::Slit)?;
691
692 let pptt_page_base = config_region_page_base + PARAVISOR_CONFIG_PPTT_PAGE_INDEX;
694 let pptt_parameter_area = importer.create_parameter_area(
695 pptt_page_base,
696 PARAVISOR_CONFIG_PPTT_SIZE_PAGES as u32,
697 "underhill-pptt",
698 )?;
699 importer.import_parameter(pptt_parameter_area, 0, IgvmParameterType::Pptt)?;
700
701 let dt_page_base = config_region_page_base + PARAVISOR_CONFIG_DEVICE_TREE_PAGE_INDEX;
703 let dt_parameter_area = importer.create_parameter_area(
704 dt_page_base,
705 PARAVISOR_CONFIG_DEVICE_TREE_SIZE_PAGES as u32,
706 "underhill-device-tree",
707 )?;
708 importer.import_parameter(dt_parameter_area, 0, IgvmParameterType::DeviceTree)?;
709
710 if isolation_type == IsolationType::Snp {
711 let reserved_region_page_base = reserved_region_start / HV_PAGE_SIZE;
712 let secrets_page_base: u64 =
713 reserved_region_page_base + PARAVISOR_RESERVED_VTL2_SNP_SECRETS_PAGE_INDEX;
714 importer.import_pages(
715 secrets_page_base,
716 PARAVISOR_RESERVED_VTL2_SNP_SECRETS_SIZE_PAGES,
717 "underhill-snp-secrets-page",
718 BootPageAcceptance::SecretsPage,
719 &[],
720 )?;
721
722 let cpuid_page = create_snp_cpuid_page();
723 let cpuid_page_base =
724 reserved_region_page_base + PARAVISOR_RESERVED_VTL2_SNP_CPUID_PAGE_INDEX;
725 importer.import_pages(
726 cpuid_page_base,
727 1,
728 "underhill-snp-cpuid-page",
729 BootPageAcceptance::CpuidPage,
730 cpuid_page.as_bytes(),
731 )?;
732
733 importer.import_pages(
734 cpuid_page_base + 1,
735 1,
736 "underhill-snp-cpuid-extended-state-page",
737 BootPageAcceptance::CpuidExtendedStatePage,
738 &[],
739 )?;
740
741 let vmsa_page_base =
742 reserved_region_page_base + PARAVISOR_RESERVED_VTL2_SNP_VMSA_PAGE_INDEX;
743 importer.set_vp_context_page(vmsa_page_base)?;
744 }
745
746 let mut free_page = 1;
749 let mut measured_config = ParavisorMeasuredVtl0Config {
750 magic: ParavisorMeasuredVtl0Config::MAGIC,
751 ..FromZeros::new_zeroed()
752 };
753
754 let Vtl0Config {
755 supports_pcat,
756 supports_uefi,
757 supports_linux,
758 } = vtl0_config;
759
760 if supports_pcat {
761 measured_config.supported_vtl0.set_pcat_supported(true);
762 }
763
764 if let Some((uefi, vp_context)) = &supports_uefi {
765 measured_config.supported_vtl0.set_uefi_supported(true);
766 let vp_context_page = free_page;
767 free_page += 1;
768 measured_config.uefi_info = UefiInfo {
769 firmware: PageRegionDescriptor {
770 base_page_number: uefi.firmware_base / HV_PAGE_SIZE,
771 page_count: uefi.total_size / HV_PAGE_SIZE,
772 },
773 vtl0_vp_context: PageRegionDescriptor {
774 base_page_number: vp_context_page,
775 page_count: 1,
776 },
777 };
778
779 importer.import_pages(
781 vp_context_page,
782 1,
783 "openhcl-uefi-vp-context",
784 BootPageAcceptance::Exclusive,
785 vp_context,
786 )?;
787 }
788
789 if let Some(linux) = supports_linux {
790 measured_config
791 .supported_vtl0
792 .set_linux_direct_supported(true);
793
794 let kernel_region = PageRegionDescriptor::new(
795 linux.load_info.kernel.gpa / HV_PAGE_SIZE,
796 align_up_to_page_size(linux.load_info.kernel.size) / HV_PAGE_SIZE,
797 );
798
799 let (initrd_region, initrd_size) = match linux.load_info.initrd {
800 Some(info) => {
801 if info.gpa % HV_PAGE_SIZE != 0 {
802 return Err(Error::MemoryUnaligned(info.gpa));
803 }
804 (
805 PageRegionDescriptor::new(
807 info.gpa / HV_PAGE_SIZE,
808 align_up_to_page_size(info.size) / HV_PAGE_SIZE,
809 ),
810 info.size,
811 )
812 }
813 None => (PageRegionDescriptor::EMPTY, 0),
814 };
815
816 let command_line_page = free_page;
817 importer
821 .import_pages(
822 command_line_page,
823 1,
824 "underhill-vtl0-linux-command-line",
825 BootPageAcceptance::Exclusive,
826 linux.command_line.as_bytes_with_nul(),
827 )
828 .map_err(Error::Importer)?;
829 let command_line = PageRegionDescriptor::new(command_line_page, 1);
830
831 measured_config.linux_info = LinuxInfo {
832 kernel_region,
833 kernel_entrypoint: linux.load_info.kernel.entrypoint,
834 initrd_region,
835 initrd_size,
836 command_line,
837 };
838 }
839
840 importer
841 .import_pages(
842 PARAVISOR_VTL0_MEASURED_CONFIG_BASE_PAGE_X64,
843 1,
844 "underhill-measured-config",
845 BootPageAcceptance::Exclusive,
846 measured_config.as_bytes(),
847 )
848 .map_err(Error::Importer)?;
849
850 let vtl2_measured_config = ParavisorMeasuredVtl2Config {
851 magic: ParavisorMeasuredVtl2Config::MAGIC,
852 vtom_offset_bit: shared_gpa_boundary_bits.unwrap_or(0),
853 padding: [0; 7],
854 };
855
856 importer
857 .import_pages(
858 config_region_page_base + PARAVISOR_MEASURED_VTL2_CONFIG_PAGE_INDEX,
859 PARAVISOR_MEASURED_VTL2_CONFIG_SIZE_PAGES,
860 "underhill-vtl2-measured-config",
861 BootPageAcceptance::Exclusive,
862 vtl2_measured_config.as_bytes(),
863 )
864 .map_err(Error::Importer)?;
865
866 let imported_region_base =
867 config_region_page_base + PARAVISOR_MEASURED_VTL2_CONFIG_ACCEPTED_MEMORY_PAGE_INDEX;
868
869 importer.set_imported_regions_config_page(imported_region_base);
870 Ok(())
871}
872
873fn create_snp_cpuid_page() -> HV_PSP_CPUID_PAGE {
875 let mut cpuid_page = HV_PSP_CPUID_PAGE::default();
876
877 for (i, required_leaf) in crate::cpuid::SNP_REQUIRED_CPUID_LEAF_LIST_PARAVISOR
883 .iter()
884 .enumerate()
885 {
886 let entry = &mut cpuid_page.cpuid_leaf_info[i];
887 entry.eax_in = required_leaf.eax;
888 entry.ecx_in = required_leaf.ecx;
889 if required_leaf.eax == CpuidFunction::ExtendedStateEnumeration.0 {
890 entry.xfem_in = 1;
891 }
892 cpuid_page.count += 1;
893 }
894
895 cpuid_page
896}
897
898pub fn load_openhcl_arm64<F>(
905 importer: &mut dyn ImageLoad<Aarch64Register>,
906 kernel_image: &mut F,
907 shim: &mut F,
908 command_line: CommandLineType<'_>,
909 initrd: Option<&[u8]>,
910 memory_page_base: Option<u64>,
911 memory_page_count: u64,
912 vtl0_config: Vtl0Config<'_>,
913) -> Result<(), Error>
914where
915 F: std::io::Read + std::io::Seek,
916{
917 let Vtl0Config {
918 supports_pcat,
919 supports_uefi,
920 supports_linux,
921 } = vtl0_config;
922
923 assert!(!supports_pcat);
924 assert!(supports_uefi.is_some() || supports_linux.is_some());
925
926 let paravisor_present = importer.isolation_config().paravisor_present;
927
928 let with_relocation = memory_page_base.is_none();
930
931 let memory_start_address = memory_page_base
932 .map(|page_number| page_number * HV_PAGE_SIZE)
933 .unwrap_or(PARAVISOR_DEFAULT_MEMORY_BASE_ADDRESS);
934
935 let memory_size = memory_page_count * HV_PAGE_SIZE;
936
937 if !memory_start_address.is_multiple_of(u64::from(Arm64PageSize::Large)) {
939 return Err(Error::MemoryUnaligned(memory_start_address));
940 }
941
942 if !memory_size.is_multiple_of(u64::from(Arm64PageSize::Large)) {
943 return Err(Error::MemoryUnaligned(memory_size));
944 }
945
946 importer.verify_startup_memory_available(
949 memory_start_address / HV_PAGE_SIZE,
950 memory_page_count,
951 if paravisor_present {
952 StartupMemoryType::Vtl2ProtectableRam
953 } else {
954 StartupMemoryType::Ram
955 },
956 )?;
957
958 tracing::trace!(memory_start_address, "loading the kernel");
959
960 let initrd_address_type = InitrdAddressType::AfterKernel;
964 let initrd_config = InitrdConfig {
965 initrd_address: initrd_address_type,
966 initrd: initrd.unwrap_or_default(),
967 };
968 let device_tree_blob = None;
969 let crate::linux::LoadInfo {
970 kernel:
971 KernelInfo {
972 gpa: kernel_base,
973 size: kernel_size,
974 entrypoint: kernel_entry_point,
975 },
976 initrd: initrd_info,
977 dtb,
978 } = load_kernel_and_initrd_arm64(
979 importer,
980 kernel_image,
981 memory_start_address,
982 Some(initrd_config),
983 device_tree_blob,
984 )
985 .map_err(Error::Kernel)?;
986
987 assert!(
988 dtb.is_none(),
989 "DeviceTree is generated dynamically by the boot shim."
990 );
991
992 tracing::trace!(kernel_base, "kernel loaded");
993
994 let mut next_addr;
995
996 let InitrdInfo {
997 gpa: initrd_gpa,
998 size: initrd_size,
999 } = if let Some(initrd_info) = initrd_info {
1000 assert!(initrd_address_type == InitrdAddressType::AfterKernel);
1001 next_addr = initrd_info.gpa + initrd_info.size;
1002 initrd_info
1003 } else {
1004 next_addr = kernel_base + kernel_size;
1005 InitrdInfo { gpa: 0, size: 0 }
1006 };
1007
1008 next_addr = align_up_to_page_size(next_addr);
1009
1010 tracing::trace!(next_addr, "loading the boot shim");
1011
1012 let crate::elf::LoadInfo {
1013 minimum_address_used: shim_base_addr,
1014 next_available_address: mut next_addr,
1015 entrypoint: shim_entry_point,
1016 } = crate::elf::load_static_elf(
1017 importer,
1018 shim,
1019 0,
1020 next_addr,
1021 false,
1022 BootPageAcceptance::Exclusive,
1023 "underhill-boot-shim",
1024 )
1025 .map_err(Error::Shim)?;
1026
1027 tracing::trace!(shim_base_addr, "boot shim loaded");
1028
1029 tracing::trace!(next_addr, "loading the command line");
1030
1031 let cmdline_base = next_addr;
1032 let (cmdline, policy) = match command_line {
1033 CommandLineType::Static(val) => (val, CommandLinePolicy::STATIC),
1034 CommandLineType::HostAppendable(val) => (val, CommandLinePolicy::APPEND_CHOSEN),
1035 };
1036
1037 if cmdline.len() > COMMAND_LINE_SIZE {
1038 return Err(Error::CommandLineSize(cmdline.len()));
1039 }
1040
1041 let mut static_command_line = [0; COMMAND_LINE_SIZE];
1042 static_command_line[..cmdline.len()].copy_from_slice(cmdline.as_bytes());
1043 let paravisor_command_line = ParavisorCommandLine {
1044 policy,
1045 static_command_line_len: cmdline.len() as u16,
1046 static_command_line,
1047 };
1048
1049 importer.import_pages(
1050 cmdline_base / HV_PAGE_SIZE,
1051 1,
1052 "underhill-command-line",
1053 BootPageAcceptance::Exclusive,
1054 paravisor_command_line.as_bytes(),
1055 )?;
1056
1057 next_addr += HV_PAGE_SIZE;
1058
1059 tracing::trace!(next_addr, "loading the boot shim parameters");
1060
1061 let shim_params_base = next_addr;
1062 let shim_params_size = HV_PAGE_SIZE;
1063
1064 next_addr += shim_params_size;
1065
1066 let parameter_region_size = PARAVISOR_VTL2_CONFIG_REGION_PAGE_COUNT_MAX * HV_PAGE_SIZE;
1067 let parameter_region_start = next_addr;
1068 next_addr += parameter_region_size;
1069
1070 tracing::debug!(parameter_region_start);
1071
1072 let bootshim_log_size = HV_PAGE_SIZE * 2;
1074 let bootshim_log_start = next_addr;
1075 next_addr += bootshim_log_size;
1076
1077 importer.import_pages(
1078 bootshim_log_start / HV_PAGE_SIZE,
1079 bootshim_log_size / HV_PAGE_SIZE,
1080 "ohcl-boot-shim-log-buffer",
1081 BootPageAcceptance::Exclusive,
1082 &[],
1083 )?;
1084
1085 let heap_start = next_addr;
1092 let heap_size = 16 * HV_PAGE_SIZE;
1093 importer.import_pages(
1094 heap_start / HV_PAGE_SIZE,
1095 heap_size / HV_PAGE_SIZE,
1096 "ohcl-boot-shim-heap",
1097 BootPageAcceptance::Exclusive,
1098 &[],
1099 )?;
1100 next_addr += heap_size;
1101
1102 let end_of_underhill_mem = next_addr;
1104
1105 let page_table_base_page_count = 5;
1108 let page_table_dynamic_page_count = 2 * page_table_base_page_count;
1109 let page_table_page_count = page_table_base_page_count + page_table_dynamic_page_count;
1110 let page_table_region_size = HV_PAGE_SIZE * page_table_page_count;
1111 let page_table_region_start = next_addr;
1112 next_addr += page_table_region_size;
1113
1114 tracing::debug!(page_table_region_start, page_table_region_size);
1115
1116 let next_addr = next_addr;
1117
1118 if next_addr > memory_start_address + memory_size {
1120 return Err(Error::NotEnoughMemory(next_addr - memory_start_address));
1121 }
1122
1123 let calculate_shim_offset = |addr: u64| -> i64 { addr.wrapping_sub(shim_base_addr) as i64 };
1125 let initrd_crc = crc32fast::hash(initrd.unwrap_or(&[]));
1126 let shim_params = ShimParamsRaw {
1127 kernel_entry_offset: calculate_shim_offset(kernel_entry_point),
1128 cmdline_offset: calculate_shim_offset(cmdline_base),
1129 initrd_offset: calculate_shim_offset(initrd_gpa),
1130 initrd_size,
1131 initrd_crc,
1132 supported_isolation_type: match importer.isolation_config().isolation_type {
1133 IsolationType::None | IsolationType::Vbs => {
1134 loader_defs::shim::SupportedIsolationType::VBS
1135 }
1136 _ => panic!("only None and VBS are supported for ARM64"),
1137 },
1138 memory_start_offset: calculate_shim_offset(memory_start_address),
1139 memory_size,
1140 parameter_region_offset: calculate_shim_offset(parameter_region_start),
1141 parameter_region_size,
1142 vtl2_reserved_region_offset: 0,
1143 vtl2_reserved_region_size: 0,
1144 sidecar_offset: 0,
1145 sidecar_size: 0,
1146 sidecar_entry_offset: 0,
1147 used_start: calculate_shim_offset(memory_start_address),
1148 used_end: calculate_shim_offset(next_addr),
1149 bounce_buffer_start: 0,
1150 bounce_buffer_size: 0,
1151 page_tables_start: 0,
1152 page_tables_size: 0,
1153 log_buffer_start: calculate_shim_offset(bootshim_log_start),
1154 log_buffer_size: bootshim_log_size,
1155 heap_start_offset: calculate_shim_offset(heap_start),
1156 heap_size,
1157 };
1158
1159 importer
1160 .import_pages(
1161 shim_params_base / HV_PAGE_SIZE,
1162 shim_params_size / HV_PAGE_SIZE,
1163 "underhill-shim-params",
1164 BootPageAcceptance::Exclusive,
1165 shim_params.as_bytes(),
1166 )
1167 .map_err(Error::Importer)?;
1168
1169 let mut measured_config = ParavisorMeasuredVtl0Config {
1170 magic: ParavisorMeasuredVtl0Config::MAGIC,
1171 ..FromZeros::new_zeroed()
1172 };
1173
1174 if let Some((uefi, vp_context)) = &supports_uefi {
1175 measured_config.supported_vtl0.set_uefi_supported(true);
1176 let vp_context_page = PARAVISOR_VTL0_MEASURED_CONFIG_BASE_PAGE_AARCH64 + 1;
1177 measured_config.uefi_info = UefiInfo {
1178 firmware: PageRegionDescriptor {
1179 base_page_number: uefi.firmware_base / HV_PAGE_SIZE,
1180 page_count: uefi.total_size / HV_PAGE_SIZE,
1181 },
1182 vtl0_vp_context: PageRegionDescriptor {
1183 base_page_number: vp_context_page,
1184 page_count: 1,
1185 },
1186 };
1187
1188 importer.import_pages(
1190 vp_context_page,
1191 1,
1192 "openhcl-uefi-vp-context",
1193 BootPageAcceptance::Exclusive,
1194 vp_context,
1195 )?;
1196 }
1197
1198 importer
1199 .import_pages(
1200 PARAVISOR_VTL0_MEASURED_CONFIG_BASE_PAGE_AARCH64,
1201 1,
1202 "underhill-measured-config",
1203 BootPageAcceptance::Exclusive,
1204 measured_config.as_bytes(),
1205 )
1206 .map_err(Error::Importer)?;
1207
1208 tracing::trace!(page_table_region_start, "loading the page tables");
1209
1210 let memory_attribute_indirection = MemoryAttributeIndirectionEl1([
1211 MemoryAttributeEl1::Device_nGnRnE,
1212 MemoryAttributeEl1::Normal_NonCacheable,
1213 MemoryAttributeEl1::Normal_WriteThrough,
1214 MemoryAttributeEl1::Normal_WriteBack,
1215 MemoryAttributeEl1::Device_nGnRnE,
1216 MemoryAttributeEl1::Device_nGnRnE,
1217 MemoryAttributeEl1::Device_nGnRnE,
1218 MemoryAttributeEl1::Device_nGnRnE,
1219 ]);
1220 let page_tables = page_table::aarch64::build_identity_page_tables_aarch64(
1221 page_table_region_start,
1222 memory_start_address,
1223 memory_size,
1224 memory_attribute_indirection,
1225 page_table_region_size as usize,
1226 );
1227 assert!((page_tables.len() as u64).is_multiple_of(HV_PAGE_SIZE));
1228 let page_table_page_base = page_table_region_start / HV_PAGE_SIZE;
1229 assert!(page_tables.len() as u64 <= page_table_region_size);
1230 assert!(page_table_region_size as usize > page_tables.len());
1231
1232 if with_relocation {
1233 importer.relocation_region(
1235 memory_start_address,
1236 end_of_underhill_mem - memory_start_address,
1237 Arm64PageSize::Large.into(),
1238 PARAVISOR_DEFAULT_MEMORY_BASE_ADDRESS,
1239 1 << 48,
1240 true,
1241 false,
1242 0, )?;
1244
1245 importer.page_table_relocation(
1247 page_table_region_start,
1248 page_table_region_size / HV_PAGE_SIZE,
1249 page_tables.len() as u64 / HV_PAGE_SIZE,
1250 0,
1251 )?;
1252 }
1253
1254 importer.import_pages(
1255 page_table_page_base,
1256 page_table_page_count,
1257 "underhill-page-tables",
1258 BootPageAcceptance::Exclusive,
1259 &page_tables,
1260 )?;
1261
1262 tracing::trace!("Importing register state");
1263
1264 let mut import_reg = |register| {
1265 importer
1266 .import_vp_register(register)
1267 .map_err(Error::Importer)
1268 };
1269
1270 let relative_boot_params_base = shim_params_base - shim_base_addr;
1272 import_reg(AArch64Register::X0(relative_boot_params_base).into())?;
1273
1274 import_reg(AArch64Register::Pc(shim_entry_point).into())?;
1276
1277 import_reg(AArch64Register::Cpsr(Cpsr64::new().with_sp(true).with_el(1).into()).into())?;
1280
1281 import_reg(
1304 AArch64Register::SctlrEl1(
1305 SctlrEl1::new()
1306 .with_m(true)
1311 .with_c(true)
1313 .with_i(true)
1315 .with_eos(true)
1317 .with_tscxt(true)
1318 .with_eis(true)
1319 .with_span(true)
1320 .with_n_tlsmd(true)
1321 .with_lsmaoe(true)
1322 .into(),
1323 )
1324 .into(),
1325 )?;
1326
1327 import_reg(
1333 AArch64Register::TcrEl1(
1334 TranslationControlEl1::new()
1335 .with_t0sz(0x11)
1336 .with_irgn0(1)
1337 .with_orgn0(1)
1338 .with_sh0(3)
1339 .with_tg0(TranslationGranule0::TG_4KB)
1340 .with_epd1(1)
1342 .with_tg1(TranslationGranule1::TG_4KB)
1344 .with_ips(IntermPhysAddrSize::IPA_48_BITS_256_TB)
1345 .into(),
1346 )
1347 .into(),
1348 )?;
1349
1350 import_reg(AArch64Register::MairEl1(memory_attribute_indirection.into()).into())?;
1352 import_reg(
1353 AArch64Register::Ttbr0El1(
1354 TranslationBaseEl1::new()
1355 .with_baddr(page_table_region_start)
1356 .into(),
1357 )
1358 .into(),
1359 )?;
1360
1361 import_reg(AArch64Register::VbarEl1(0).into())?;
1365
1366 let config_region_page_base = parameter_region_start / HV_PAGE_SIZE;
1368
1369 let slit_page_base = config_region_page_base + PARAVISOR_CONFIG_SLIT_PAGE_INDEX;
1371 let slit_parameter_area = importer.create_parameter_area(
1372 slit_page_base,
1373 PARAVISOR_CONFIG_SLIT_SIZE_PAGES as u32,
1374 "underhill-slit",
1375 )?;
1376 importer.import_parameter(slit_parameter_area, 0, IgvmParameterType::Slit)?;
1377
1378 let pptt_page_base = config_region_page_base + PARAVISOR_CONFIG_PPTT_PAGE_INDEX;
1380 let pptt_parameter_area = importer.create_parameter_area(
1381 pptt_page_base,
1382 PARAVISOR_CONFIG_PPTT_SIZE_PAGES as u32,
1383 "underhill-pptt",
1384 )?;
1385 importer.import_parameter(pptt_parameter_area, 0, IgvmParameterType::Pptt)?;
1386
1387 let dt_page_base = config_region_page_base + PARAVISOR_CONFIG_DEVICE_TREE_PAGE_INDEX;
1389 let dt_parameter_area = importer.create_parameter_area(
1390 dt_page_base,
1391 PARAVISOR_CONFIG_DEVICE_TREE_SIZE_PAGES as u32,
1392 "underhill-device-tree",
1393 )?;
1394 importer.import_parameter(dt_parameter_area, 0, IgvmParameterType::DeviceTree)?;
1395
1396 let vtl2_measured_config = ParavisorMeasuredVtl2Config {
1397 magic: ParavisorMeasuredVtl2Config::MAGIC,
1398 vtom_offset_bit: 0,
1399 padding: [0; 7],
1400 };
1401
1402 importer
1403 .import_pages(
1404 config_region_page_base + PARAVISOR_MEASURED_VTL2_CONFIG_PAGE_INDEX,
1405 PARAVISOR_MEASURED_VTL2_CONFIG_SIZE_PAGES,
1406 "underhill-vtl2-measured-config",
1407 BootPageAcceptance::Exclusive,
1408 vtl2_measured_config.as_bytes(),
1409 )
1410 .map_err(Error::Importer)?;
1411
1412 let imported_region_base =
1413 config_region_page_base + PARAVISOR_MEASURED_VTL2_CONFIG_ACCEPTED_MEMORY_PAGE_INDEX;
1414
1415 importer.set_imported_regions_config_page(imported_region_base);
1416
1417 Ok(())
1418}