1use crate::common::ChunkBuf;
7use crate::common::ImportFileRegion;
8use crate::common::ReadSeek;
9use crate::cpuid::HV_PSP_CPUID_PAGE;
10use crate::importer::Aarch64Register;
11use crate::importer::BootPageAcceptance;
12use crate::importer::IgvmParameterType;
13use crate::importer::ImageLoad;
14use crate::importer::IsolationConfig;
15use crate::importer::IsolationType;
16use crate::importer::SegmentRegister;
17use crate::importer::StartupMemoryType;
18use crate::importer::TableRegister;
19use crate::importer::X86Register;
20use crate::linux::InitrdAddressType;
21use crate::linux::InitrdConfig;
22use crate::linux::InitrdInfo;
23use crate::linux::KernelInfo;
24use crate::linux::load_kernel_and_initrd_arm64;
25use aarch64defs::Cpsr64;
26use aarch64defs::IntermPhysAddrSize;
27use aarch64defs::SctlrEl1;
28use aarch64defs::TranslationBaseEl1;
29use aarch64defs::TranslationControlEl1;
30use aarch64defs::TranslationGranule0;
31use aarch64defs::TranslationGranule1;
32use hvdef::HV_PAGE_SIZE;
33use hvdef::Vtl;
34use igvm::registers::AArch64Register;
35use loader_defs::paravisor::*;
36use loader_defs::shim::ShimParamsRaw;
37use memory_range::MemoryRange;
38use page_table::aarch64::Arm64PageSize;
39use page_table::aarch64::MemoryAttributeEl1;
40use page_table::aarch64::MemoryAttributeIndirectionEl1;
41use page_table::x64::MappedRange;
42use page_table::x64::PAGE_TABLE_MAX_BYTES;
43use page_table::x64::PAGE_TABLE_MAX_COUNT;
44use page_table::x64::PageTable;
45use page_table::x64::PageTableBuilder;
46use page_table::x64::X64_LARGE_PAGE_SIZE;
47use page_table::x64::align_up_to_large_page_size;
48use page_table::x64::align_up_to_page_size;
49use page_table::x64::calculate_pde_table_count;
50use std::io::Read;
51use std::io::Seek;
52use thiserror::Error;
53use x86defs::GdtEntry;
54use x86defs::SegmentSelector;
55use x86defs::X64_BUSY_TSS_SEGMENT_ATTRIBUTES;
56use x86defs::X64_DEFAULT_CODE_SEGMENT_ATTRIBUTES;
57use x86defs::X64_DEFAULT_DATA_SEGMENT_ATTRIBUTES;
58use x86defs::cpuid::CpuidFunction;
59use zerocopy::FromZeros;
60use zerocopy::IntoBytes;
61
62#[derive(Debug)]
63pub struct Vtl0Linux<'a> {
64 pub command_line: &'a std::ffi::CString,
65 pub load_info: crate::linux::LoadInfo,
66}
67
68#[derive(Debug)]
69pub struct Vtl0Config<'a> {
70 pub supports_pcat: bool,
71 pub supports_uefi: Option<(crate::uefi::LoadInfo, Vec<u8>)>,
73 pub supports_linux: Option<Vtl0Linux<'a>>,
74}
75
76pub const HCL_SECURE_VTL: Vtl = Vtl::Vtl2;
78
79const PERSISTED_REGION_SIZE: u64 = 2 * 1024 * 1024;
81
82fn avoid_page_table_large_page_boundary(offset: u64, large_page_size: u64) -> u64 {
83 if offset.is_multiple_of(large_page_size) {
84 offset + HV_PAGE_SIZE
85 } else {
86 offset
87 }
88}
89
90#[derive(Debug, Error)]
91pub enum Error {
92 #[error("memory is unaligned: {0}")]
93 MemoryUnaligned(u64),
94 #[error("command line too large: {0}")]
95 CommandLineSize(usize),
96 #[error("kernel load error")]
97 Kernel(#[source] crate::linux::Error),
98 #[error("shim load error")]
99 Shim(#[source] crate::elf::Error),
100 #[error("invalid initrd size: {0}")]
101 InvalidInitrdSize(u64),
102 #[error("memory used: {0} is greater than available")]
103 NotEnoughMemory(u64),
104 #[error("importer error")]
105 Importer(#[from] anyhow::Error),
106 #[error("failed to import initrd")]
107 ImportInitrd(#[source] crate::common::ImportFileRegionError),
108 #[error("failed to read initrd for CRC")]
109 InitrdRead(#[source] std::io::Error),
110 #[error("PageTableBuilder: {0}")]
111 PageTableBuilder(#[from] page_table::Error),
112}
113
114pub enum CommandLineType<'a> {
116 Static(&'a str),
118 HostAppendable(&'a str),
123}
124
125pub fn load_openhcl_x64<F>(
132 importer: &mut dyn ImageLoad<X86Register>,
133 kernel_image: &mut F,
134 shim: &mut F,
135 sidecar: Option<&mut F>,
136 command_line: CommandLineType<'_>,
137 mut initrd: Option<(&mut dyn ReadSeek, u64)>,
138 memory_page_base: Option<u64>,
139 memory_page_count: u64,
140 vtl0_config: Vtl0Config<'_>,
141) -> Result<(), Error>
142where
143 F: Read + Seek,
144{
145 let IsolationConfig {
146 isolation_type,
147 paravisor_present,
148 shared_gpa_boundary_bits,
149 } = importer.isolation_config();
150
151 let with_relocation = memory_page_base.is_none() && isolation_type == IsolationType::None;
153
154 let memory_start_address = memory_page_base
155 .map(|page_number| page_number * HV_PAGE_SIZE)
156 .unwrap_or(PARAVISOR_DEFAULT_MEMORY_BASE_ADDRESS);
157
158 let memory_size = memory_page_count * HV_PAGE_SIZE;
159
160 if !memory_start_address.is_multiple_of(X64_LARGE_PAGE_SIZE) {
180 return Err(Error::MemoryUnaligned(memory_start_address));
181 }
182
183 if !memory_size.is_multiple_of(X64_LARGE_PAGE_SIZE) {
184 return Err(Error::MemoryUnaligned(memory_size));
185 }
186
187 importer.verify_startup_memory_available(
190 memory_start_address / HV_PAGE_SIZE,
191 memory_page_count,
192 if paravisor_present {
193 StartupMemoryType::Vtl2ProtectableRam
194 } else {
195 StartupMemoryType::Ram
196 },
197 )?;
198
199 let kernel_acceptance = match isolation_type {
200 IsolationType::Snp | IsolationType::Tdx => BootPageAcceptance::Shared,
201 _ => BootPageAcceptance::Exclusive,
202 };
203
204 let mut offset = memory_start_address;
205
206 let persisted_region_base = offset;
210 let persisted_region_size = PERSISTED_REGION_SIZE;
211 offset += persisted_region_size;
212
213 let bounce_buffer = if matches!(isolation_type, IsolationType::Snp | IsolationType::Tdx) {
222 let bounce_buffer_gpa = offset;
223 assert_eq!(bounce_buffer_gpa % X64_LARGE_PAGE_SIZE, 0);
224 let range = MemoryRange::new(bounce_buffer_gpa..bounce_buffer_gpa + X64_LARGE_PAGE_SIZE);
225
226 offset += range.len();
227 Some(range)
228 } else {
229 None
230 };
231
232 tracing::trace!(offset, "loading the kernel");
233
234 let load_info = crate::elf::load_static_elf(
243 importer,
244 kernel_image,
245 offset,
246 0,
247 true,
248 kernel_acceptance,
249 "underhill-kernel",
250 )
251 .map_err(|e| Error::Kernel(crate::linux::Error::ElfLoader(e)))?;
252 tracing::trace!("Kernel loaded at {load_info:x?}");
253 let crate::elf::LoadInfo {
254 minimum_address_used: _min_addr,
255 next_available_address: mut offset,
256 entrypoint: kernel_entrypoint,
257 } = load_info;
258
259 assert_eq!(offset & (HV_PAGE_SIZE - 1), 0);
260
261 let (sidecar_size, sidecar_entrypoint) = if let Some(sidecar) = sidecar {
263 offset = align_up_to_large_page_size(offset);
265
266 let load_info = crate::elf::load_static_elf(
267 importer,
268 sidecar,
269 0,
270 offset,
271 false,
272 BootPageAcceptance::Exclusive,
273 "sidecar-kernel",
274 )
275 .map_err(|e| Error::Kernel(crate::linux::Error::ElfLoader(e)))?;
276
277 (
278 load_info.next_available_address - offset,
279 load_info.entrypoint,
280 )
281 } else {
282 (0, 0)
283 };
284
285 let sidecar_base = offset;
286 offset += sidecar_size;
287
288 let load_info = crate::elf::load_static_elf(
289 importer,
290 shim,
291 0,
292 offset,
293 false,
294 BootPageAcceptance::Exclusive,
295 "underhill-boot-shim",
296 )
297 .map_err(Error::Shim)?;
298 tracing::trace!("The boot shim loaded at {load_info:x?}");
299 let crate::elf::LoadInfo {
300 minimum_address_used: shim_base_addr,
301 next_available_address: mut offset,
302 entrypoint: shim_entry_address,
303 } = load_info;
304
305 let mut buf = ChunkBuf::new();
307 let initrd_crc = if let Some((ref mut initrd_file, initrd_len)) = initrd {
308 buf.crc32(*initrd_file, initrd_len)
309 .map_err(Error::InitrdRead)?
310 } else {
311 crc32fast::hash(&[])
312 };
313
314 let ramdisk = if let Some((initrd_file, initrd_len)) = initrd {
316 let initrd_base = offset;
317 let initrd_size = align_up_to_page_size(initrd_len);
318
319 buf.import_file_region(
320 importer,
321 ImportFileRegion {
322 file: initrd_file,
323 file_offset: 0,
324 file_length: initrd_len,
325 gpa: initrd_base,
326 memory_length: initrd_len,
327 acceptance: kernel_acceptance,
328 tag: "underhill-initrd",
329 },
330 )
331 .map_err(Error::ImportInitrd)?;
332
333 offset += initrd_size;
334 Some((initrd_base, initrd_len))
335 } else {
336 None
337 };
338
339 let gdt_base_address = offset;
340 let gdt_size = HV_PAGE_SIZE;
341 offset += gdt_size;
342
343 let boot_params_base = offset;
344 let boot_params_size = HV_PAGE_SIZE;
345
346 offset += boot_params_size;
347
348 let cmdline_base = offset;
349 let (cmdline, policy) = match command_line {
350 CommandLineType::Static(val) => (val, CommandLinePolicy::STATIC),
351 CommandLineType::HostAppendable(val) => (val, CommandLinePolicy::APPEND_CHOSEN),
352 };
353
354 if cmdline.len() > COMMAND_LINE_SIZE {
355 return Err(Error::CommandLineSize(cmdline.len()));
356 }
357
358 let mut static_command_line = [0; COMMAND_LINE_SIZE];
359 static_command_line[..cmdline.len()].copy_from_slice(cmdline.as_bytes());
360 let paravisor_command_line = ParavisorCommandLine {
361 policy,
362 static_command_line_len: cmdline.len() as u16,
363 static_command_line,
364 };
365
366 importer.import_pages(
367 cmdline_base / HV_PAGE_SIZE,
368 1,
369 "underhill-command-line",
370 BootPageAcceptance::Exclusive,
371 paravisor_command_line.as_bytes(),
372 )?;
373
374 offset += HV_PAGE_SIZE;
375
376 let reserved_region_size = PARAVISOR_RESERVED_VTL2_PAGE_COUNT_MAX * HV_PAGE_SIZE;
378 let reserved_region_start = offset;
379 offset += reserved_region_size;
380
381 tracing::debug!(reserved_region_start);
382
383 let parameter_region_size = PARAVISOR_VTL2_CONFIG_REGION_PAGE_COUNT_MAX * HV_PAGE_SIZE;
384 let parameter_region_start = offset;
385 offset += parameter_region_size;
386
387 tracing::debug!(parameter_region_start);
388
389 let bootshim_log_size = HV_PAGE_SIZE * 2;
392 let bootshim_log_start = offset;
393 offset += bootshim_log_size;
394
395 importer.import_pages(
396 bootshim_log_start / HV_PAGE_SIZE,
397 bootshim_log_size / HV_PAGE_SIZE,
398 "ohcl-boot-shim-log-buffer",
399 BootPageAcceptance::Exclusive,
400 &[],
401 )?;
402
403 let heap_start = offset;
410 let heap_size = 16 * HV_PAGE_SIZE;
411 importer.import_pages(
412 heap_start / HV_PAGE_SIZE,
413 heap_size / HV_PAGE_SIZE,
414 "ohcl-boot-shim-heap",
415 BootPageAcceptance::Exclusive,
416 &[],
417 )?;
418 offset += heap_size;
419
420 offset = avoid_page_table_large_page_boundary(offset, X64_LARGE_PAGE_SIZE);
423
424 let end_of_underhill_mem = offset;
426
427 let local_map = match isolation_type {
443 IsolationType::Snp | IsolationType::Tdx => {
444 Some((PARAVISOR_LOCAL_MAP_VA, PARAVISOR_LOCAL_MAP_SIZE))
445 }
446 _ => None,
447 };
448
449 let page_table_base_page_count = 5;
450 let page_table_dynamic_page_count = {
451 calculate_pde_table_count(memory_start_address, memory_size) * 2
453 + local_map.map_or(0, |v| calculate_pde_table_count(v.0, v.1))
454 };
455 let page_table_isolation_page_count = match isolation_type {
456 IsolationType::Tdx => {
457 3
460 }
461 _ => 0,
462 };
463 let page_table_page_count = page_table_base_page_count
464 + page_table_dynamic_page_count
465 + page_table_isolation_page_count;
466 let page_table_region_size = HV_PAGE_SIZE * page_table_page_count;
467 let page_table_region_start = offset;
468 offset += page_table_region_size;
469
470 tracing::debug!(page_table_region_start, page_table_region_size);
471
472 let mut ranges: Vec<MappedRange> = Vec::new();
474
475 ranges.push(MappedRange::new(
476 memory_start_address,
477 memory_start_address + memory_size,
478 ));
479
480 if let Some((local_map_start, size)) = local_map {
481 ranges.push(MappedRange::new(local_map_start, local_map_start + size));
482 }
483
484 if isolation_type == IsolationType::Tdx {
485 const RESET_VECTOR_ADDR: u64 = 0xffff_f000;
486 ranges.push(MappedRange::new(
487 RESET_VECTOR_ADDR,
488 RESET_VECTOR_ADDR + page_table::x64::X64_PAGE_SIZE,
489 ));
490 }
491
492 ranges.sort_by_key(|r| r.start());
493
494 let mut page_table_work_buffer: Vec<PageTable> =
496 vec![PageTable::new_zeroed(); PAGE_TABLE_MAX_COUNT];
497 let mut page_table: Vec<u8> = vec![0; PAGE_TABLE_MAX_BYTES];
498 let mut page_table_builder = PageTableBuilder::new(
499 page_table_region_start,
500 page_table_work_buffer.as_mut_slice(),
501 page_table.as_mut_slice(),
502 ranges.as_slice(),
503 )?;
504
505 if isolation_type == IsolationType::Snp {
506 page_table_builder = page_table_builder.with_confidential_bit(51);
507 }
508
509 let page_table = page_table_builder.build()?;
510
511 assert!((page_table.len() as u64).is_multiple_of(HV_PAGE_SIZE));
512 let page_table_page_base = page_table_region_start / HV_PAGE_SIZE;
513 assert!(page_table.len() as u64 <= page_table_region_size);
514 let offset = offset;
515
516 if with_relocation {
517 importer.relocation_region(
519 memory_start_address,
520 end_of_underhill_mem - memory_start_address,
521 X64_LARGE_PAGE_SIZE,
522 PARAVISOR_DEFAULT_MEMORY_BASE_ADDRESS,
523 1 << 48,
524 true,
525 true,
526 0, )?;
528
529 importer.page_table_relocation(
531 page_table_region_start,
532 page_table_region_size / HV_PAGE_SIZE,
533 page_table.len() as u64 / HV_PAGE_SIZE,
534 0,
535 )?;
536 }
537
538 if offset > memory_start_address + memory_size {
540 return Err(Error::NotEnoughMemory(offset - memory_start_address));
541 }
542
543 let (initrd_base, initrd_size) = ramdisk.unwrap_or((0, 0));
544 let calculate_shim_offset = |addr: u64| addr.wrapping_sub(shim_base_addr) as i64;
546 let shim_params = ShimParamsRaw {
547 kernel_entry_offset: calculate_shim_offset(kernel_entrypoint),
548 cmdline_offset: calculate_shim_offset(cmdline_base),
549 initrd_offset: calculate_shim_offset(initrd_base),
550 initrd_size,
551 initrd_crc,
552 supported_isolation_type: match isolation_type {
553 IsolationType::None | IsolationType::Vbs => {
557 loader_defs::shim::SupportedIsolationType::VBS
558 }
559 IsolationType::Snp => loader_defs::shim::SupportedIsolationType::SNP,
560 IsolationType::Tdx => loader_defs::shim::SupportedIsolationType::TDX,
561 },
562 memory_start_offset: calculate_shim_offset(memory_start_address),
563 memory_size,
564 parameter_region_offset: calculate_shim_offset(parameter_region_start),
565 parameter_region_size,
566 vtl2_reserved_region_offset: calculate_shim_offset(reserved_region_start),
567 vtl2_reserved_region_size: reserved_region_size,
568 sidecar_offset: calculate_shim_offset(sidecar_base),
569 sidecar_size,
570 sidecar_entry_offset: calculate_shim_offset(sidecar_entrypoint),
571 used_start: calculate_shim_offset(memory_start_address),
572 used_end: calculate_shim_offset(offset),
573 bounce_buffer_start: bounce_buffer.map_or(0, |r| calculate_shim_offset(r.start())),
574 bounce_buffer_size: bounce_buffer.map_or(0, |r| r.len()),
575 log_buffer_start: calculate_shim_offset(bootshim_log_start),
576 log_buffer_size: bootshim_log_size,
577 heap_start_offset: calculate_shim_offset(heap_start),
578 heap_size,
579 persisted_state_region_offset: calculate_shim_offset(persisted_region_base),
580 persisted_state_region_size: persisted_region_size,
581 };
582
583 tracing::debug!(boot_params_base, "shim gpa");
584
585 importer
586 .import_pages(
587 boot_params_base / HV_PAGE_SIZE,
588 boot_params_size / HV_PAGE_SIZE,
589 "underhill-shim-params",
590 BootPageAcceptance::Exclusive,
591 shim_params.as_bytes(),
592 )
593 .map_err(Error::Importer)?;
594
595 importer.import_pages(
596 page_table_page_base,
597 page_table_page_count,
598 "underhill-page-tables",
599 BootPageAcceptance::Exclusive,
600 page_table,
601 )?;
602
603 let default_data_attributes: u16 = X64_DEFAULT_DATA_SEGMENT_ATTRIBUTES.into();
616 let default_code64_attributes: u16 = X64_DEFAULT_CODE_SEGMENT_ATTRIBUTES.into();
617 let gdt = [
618 GdtEntry::new_zeroed(),
620 GdtEntry::new_zeroed(),
621 GdtEntry {
623 limit_low: 0xffff,
624 attr_low: default_code64_attributes as u8,
625 attr_high: (default_code64_attributes >> 8) as u8,
626 ..GdtEntry::new_zeroed()
627 },
628 GdtEntry {
630 limit_low: 0xffff,
631 attr_low: default_data_attributes as u8,
632 attr_high: (default_data_attributes >> 8) as u8,
633 ..GdtEntry::new_zeroed()
634 },
635 ];
636
637 const LINEAR_CODE64_DESCRIPTOR_INDEX: usize = 2;
638 const LINEAR_DATA_DESCRIPTOR_INDEX: usize = 3;
639 const RPL: u8 = 0x00; let linear_code64_descriptor_selector =
642 SegmentSelector::from_gdt_index(LINEAR_CODE64_DESCRIPTOR_INDEX as u16, RPL);
643 let linear_data_descriptor_selector =
644 SegmentSelector::from_gdt_index(LINEAR_DATA_DESCRIPTOR_INDEX as u16, RPL);
645
646 importer.import_pages(
647 gdt_base_address / HV_PAGE_SIZE,
648 gdt_size / HV_PAGE_SIZE,
649 "underhill-gdt",
650 BootPageAcceptance::Exclusive,
651 gdt.as_bytes(),
652 )?;
653
654 let mut import_reg = |register| {
655 importer
656 .import_vp_register(register)
657 .map_err(Error::Importer)
658 };
659
660 import_reg(X86Register::Gdtr(TableRegister {
662 base: gdt_base_address,
663 limit: (size_of_val(&gdt) - 1) as u16,
664 }))?;
665
666 let ds = SegmentRegister {
667 selector: linear_data_descriptor_selector.into_bits(),
668 base: 0,
669 limit: 0xffffffff,
670 attributes: default_data_attributes,
671 };
672 import_reg(X86Register::Ds(ds))?;
673 import_reg(X86Register::Es(ds))?;
674 import_reg(X86Register::Fs(ds))?;
675 import_reg(X86Register::Gs(ds))?;
676 import_reg(X86Register::Ss(ds))?;
677
678 let cs = SegmentRegister {
679 selector: linear_code64_descriptor_selector.into_bits(),
680 base: 0,
681 limit: 0xffffffff,
682 attributes: default_code64_attributes,
683 };
684 import_reg(X86Register::Cs(cs))?;
685
686 import_reg(X86Register::Tr(SegmentRegister {
692 selector: 0x0000,
693 base: 0x00000000,
694 limit: 0x0000FFFF,
695 attributes: X64_BUSY_TSS_SEGMENT_ATTRIBUTES.into(),
696 }))?;
697
698 import_reg(X86Register::Cr0(
703 x86defs::X64_CR0_PG | x86defs::X64_CR0_PE | x86defs::X64_CR0_NE,
704 ))?;
705
706 import_reg(X86Register::Cr3(page_table_region_start))?;
708
709 import_reg(X86Register::Cr4(
711 x86defs::X64_CR4_PAE | x86defs::X64_CR4_MCE | x86defs::X64_CR4_OSXSAVE,
712 ))?;
713
714 import_reg(X86Register::Efer(
716 x86defs::X64_EFER_LMA | x86defs::X64_EFER_LME | x86defs::X64_EFER_NXE,
717 ))?;
718
719 import_reg(X86Register::Pat(x86defs::X86X_MSR_DEFAULT_PAT))?;
721
722 let relative_boot_params_base = boot_params_base - shim_base_addr;
725 import_reg(X86Register::Rsi(relative_boot_params_base))?;
726
727 import_reg(X86Register::Rip(shim_entry_address))?;
729
730 let config_region_page_base = parameter_region_start / HV_PAGE_SIZE;
732
733 let slit_page_base = config_region_page_base + PARAVISOR_CONFIG_SLIT_PAGE_INDEX;
735 let slit_parameter_area = importer.create_parameter_area(
736 slit_page_base,
737 PARAVISOR_CONFIG_SLIT_SIZE_PAGES as u32,
738 "underhill-slit",
739 )?;
740 importer.import_parameter(slit_parameter_area, 0, IgvmParameterType::Slit)?;
741
742 let pptt_page_base = config_region_page_base + PARAVISOR_CONFIG_PPTT_PAGE_INDEX;
744 let pptt_parameter_area = importer.create_parameter_area(
745 pptt_page_base,
746 PARAVISOR_CONFIG_PPTT_SIZE_PAGES as u32,
747 "underhill-pptt",
748 )?;
749 importer.import_parameter(pptt_parameter_area, 0, IgvmParameterType::Pptt)?;
750
751 let dt_page_base = config_region_page_base + PARAVISOR_CONFIG_DEVICE_TREE_PAGE_INDEX;
753 let dt_parameter_area = importer.create_parameter_area(
754 dt_page_base,
755 PARAVISOR_CONFIG_DEVICE_TREE_SIZE_PAGES as u32,
756 "underhill-device-tree",
757 )?;
758 importer.import_parameter(dt_parameter_area, 0, IgvmParameterType::DeviceTree)?;
759
760 if isolation_type == IsolationType::Snp {
761 let reserved_region_page_base = reserved_region_start / HV_PAGE_SIZE;
762 let secrets_page_base: u64 =
763 reserved_region_page_base + PARAVISOR_RESERVED_VTL2_SNP_SECRETS_PAGE_INDEX;
764 importer.import_pages(
765 secrets_page_base,
766 PARAVISOR_RESERVED_VTL2_SNP_SECRETS_SIZE_PAGES,
767 "underhill-snp-secrets-page",
768 BootPageAcceptance::SecretsPage,
769 &[],
770 )?;
771
772 let cpuid_page = create_snp_cpuid_page();
773 let cpuid_page_base =
774 reserved_region_page_base + PARAVISOR_RESERVED_VTL2_SNP_CPUID_PAGE_INDEX;
775 importer.import_pages(
776 cpuid_page_base,
777 1,
778 "underhill-snp-cpuid-page",
779 BootPageAcceptance::CpuidPage,
780 cpuid_page.as_bytes(),
781 )?;
782
783 importer.import_pages(
784 cpuid_page_base + 1,
785 1,
786 "underhill-snp-cpuid-extended-state-page",
787 BootPageAcceptance::CpuidExtendedStatePage,
788 &[],
789 )?;
790
791 let vmsa_page_base =
792 reserved_region_page_base + PARAVISOR_RESERVED_VTL2_SNP_VMSA_PAGE_INDEX;
793 importer.set_vp_context_page(vmsa_page_base)?;
794 }
795
796 let mut free_page = 1;
799 let mut measured_config = ParavisorMeasuredVtl0Config {
800 magic: ParavisorMeasuredVtl0Config::MAGIC,
801 ..FromZeros::new_zeroed()
802 };
803
804 let Vtl0Config {
805 supports_pcat,
806 supports_uefi,
807 supports_linux,
808 } = vtl0_config;
809
810 if supports_pcat {
811 measured_config.supported_vtl0.set_pcat_supported(true);
812 }
813
814 if let Some((uefi, vp_context)) = &supports_uefi {
815 measured_config.supported_vtl0.set_uefi_supported(true);
816 let vp_context_page = free_page;
817 free_page += 1;
818 measured_config.uefi_info = UefiInfo {
819 firmware: PageRegionDescriptor {
820 base_page_number: uefi.firmware_base / HV_PAGE_SIZE,
821 page_count: uefi.total_size / HV_PAGE_SIZE,
822 },
823 vtl0_vp_context: PageRegionDescriptor {
824 base_page_number: vp_context_page,
825 page_count: 1,
826 },
827 };
828
829 importer.import_pages(
831 vp_context_page,
832 1,
833 "openhcl-uefi-vp-context",
834 BootPageAcceptance::Exclusive,
835 vp_context,
836 )?;
837 }
838
839 if let Some(linux) = supports_linux {
840 measured_config
841 .supported_vtl0
842 .set_linux_direct_supported(true);
843
844 let kernel_region = PageRegionDescriptor::new(
845 linux.load_info.kernel.gpa / HV_PAGE_SIZE,
846 align_up_to_page_size(linux.load_info.kernel.size) / HV_PAGE_SIZE,
847 );
848
849 let (initrd_region, initrd_size) = match linux.load_info.initrd {
850 Some(info) => {
851 if info.gpa % HV_PAGE_SIZE != 0 {
852 return Err(Error::MemoryUnaligned(info.gpa));
853 }
854 (
855 PageRegionDescriptor::new(
857 info.gpa / HV_PAGE_SIZE,
858 align_up_to_page_size(info.size) / HV_PAGE_SIZE,
859 ),
860 info.size,
861 )
862 }
863 None => (PageRegionDescriptor::EMPTY, 0),
864 };
865
866 let command_line_page = free_page;
867 importer
871 .import_pages(
872 command_line_page,
873 1,
874 "underhill-vtl0-linux-command-line",
875 BootPageAcceptance::Exclusive,
876 linux.command_line.as_bytes_with_nul(),
877 )
878 .map_err(Error::Importer)?;
879 let command_line = PageRegionDescriptor::new(command_line_page, 1);
880
881 measured_config.linux_info = LinuxInfo {
882 kernel_region,
883 kernel_entrypoint: linux.load_info.kernel.entrypoint,
884 initrd_region,
885 initrd_size,
886 command_line,
887 };
888 }
889
890 importer
891 .import_pages(
892 PARAVISOR_VTL0_MEASURED_CONFIG_BASE_PAGE_X64,
893 1,
894 "underhill-measured-config",
895 BootPageAcceptance::Exclusive,
896 measured_config.as_bytes(),
897 )
898 .map_err(Error::Importer)?;
899
900 let vtl2_measured_config = ParavisorMeasuredVtl2Config {
901 magic: ParavisorMeasuredVtl2Config::MAGIC,
902 vtom_offset_bit: shared_gpa_boundary_bits.unwrap_or(0),
903 padding: [0; 7],
904 };
905
906 importer
907 .import_pages(
908 config_region_page_base + PARAVISOR_MEASURED_VTL2_CONFIG_PAGE_INDEX,
909 PARAVISOR_MEASURED_VTL2_CONFIG_SIZE_PAGES,
910 "underhill-vtl2-measured-config",
911 BootPageAcceptance::Exclusive,
912 vtl2_measured_config.as_bytes(),
913 )
914 .map_err(Error::Importer)?;
915
916 let imported_region_base =
917 config_region_page_base + PARAVISOR_MEASURED_VTL2_CONFIG_ACCEPTED_MEMORY_PAGE_INDEX;
918
919 importer.set_imported_regions_config_page(imported_region_base);
920
921 let expected_page_hashes_base =
926 config_region_page_base + PARAVISOR_MEASURED_VTL2_CONFIG_PAGE_HASHES_PAGE_INDEX;
927 importer.set_expected_page_hashes_config_page(expected_page_hashes_base);
928 Ok(())
929}
930
931fn create_snp_cpuid_page() -> HV_PSP_CPUID_PAGE {
933 let mut cpuid_page = HV_PSP_CPUID_PAGE::default();
934
935 for (i, required_leaf) in crate::cpuid::SNP_REQUIRED_CPUID_LEAF_LIST_PARAVISOR
941 .iter()
942 .enumerate()
943 {
944 let entry = &mut cpuid_page.cpuid_leaf_info[i];
945 entry.eax_in = required_leaf.eax;
946 entry.ecx_in = required_leaf.ecx;
947 if required_leaf.eax == CpuidFunction::ExtendedStateEnumeration.0 {
948 entry.xfem_in = 1;
949 }
950 cpuid_page.count += 1;
951 }
952
953 cpuid_page
954}
955
956pub fn load_openhcl_arm64<F>(
963 importer: &mut dyn ImageLoad<Aarch64Register>,
964 kernel_image: &mut F,
965 shim: &mut F,
966 command_line: CommandLineType<'_>,
967 mut initrd: Option<(&mut dyn ReadSeek, u64)>,
968 memory_page_base: Option<u64>,
969 memory_page_count: u64,
970 vtl0_config: Vtl0Config<'_>,
971) -> Result<(), Error>
972where
973 F: Read + Seek,
974{
975 let Vtl0Config {
976 supports_pcat,
977 supports_uefi,
978 supports_linux,
979 } = vtl0_config;
980
981 assert!(!supports_pcat);
982 assert!(supports_uefi.is_some() || supports_linux.is_some());
983
984 let paravisor_present = importer.isolation_config().paravisor_present;
985
986 let with_relocation = memory_page_base.is_none();
988
989 let memory_start_address = memory_page_base
990 .map(|page_number| page_number * HV_PAGE_SIZE)
991 .unwrap_or(PARAVISOR_DEFAULT_MEMORY_BASE_ADDRESS);
992
993 let memory_size = memory_page_count * HV_PAGE_SIZE;
994
995 if !memory_start_address.is_multiple_of(u64::from(Arm64PageSize::Large)) {
997 return Err(Error::MemoryUnaligned(memory_start_address));
998 }
999
1000 if !memory_size.is_multiple_of(u64::from(Arm64PageSize::Large)) {
1001 return Err(Error::MemoryUnaligned(memory_size));
1002 }
1003
1004 importer.verify_startup_memory_available(
1007 memory_start_address / HV_PAGE_SIZE,
1008 memory_page_count,
1009 if paravisor_present {
1010 StartupMemoryType::Vtl2ProtectableRam
1011 } else {
1012 StartupMemoryType::Ram
1013 },
1014 )?;
1015
1016 let mut next_addr = memory_start_address;
1017
1018 let persisted_region_base = next_addr;
1022 let persisted_region_size = PERSISTED_REGION_SIZE;
1023 next_addr += persisted_region_size;
1024
1025 tracing::trace!(next_addr, "loading the kernel");
1026
1027 let initrd_crc = if let Some((ref mut initrd_file, initrd_len)) = initrd {
1029 ChunkBuf::new()
1030 .crc32(*initrd_file, initrd_len)
1031 .map_err(Error::InitrdRead)?
1032 } else {
1033 crc32fast::hash(&[])
1034 };
1035
1036 let initrd_address_type = InitrdAddressType::AfterKernel;
1040 let initrd_config = initrd.map(|(initrd_file, initrd_size)| InitrdConfig {
1041 initrd_address: initrd_address_type,
1042 initrd: initrd_file,
1043 size: initrd_size,
1044 });
1045 let device_tree_blob = None;
1046 let crate::linux::LoadInfo {
1047 kernel:
1048 KernelInfo {
1049 gpa: kernel_base,
1050 size: kernel_size,
1051 entrypoint: kernel_entry_point,
1052 },
1053 initrd: initrd_info,
1054 dtb,
1055 ..
1056 } = load_kernel_and_initrd_arm64(
1057 importer,
1058 kernel_image,
1059 next_addr,
1060 initrd_config,
1061 device_tree_blob,
1062 )
1063 .map_err(Error::Kernel)?;
1064
1065 assert!(
1066 dtb.is_none(),
1067 "DeviceTree is generated dynamically by the boot shim."
1068 );
1069
1070 tracing::trace!(kernel_base, "kernel loaded");
1071
1072 let InitrdInfo {
1073 gpa: initrd_gpa,
1074 size: initrd_size,
1075 } = if let Some(initrd_info) = initrd_info {
1076 assert!(initrd_address_type == InitrdAddressType::AfterKernel);
1077 next_addr = initrd_info.gpa + initrd_info.size;
1078 initrd_info
1079 } else {
1080 next_addr = kernel_base + kernel_size;
1081 InitrdInfo { gpa: 0, size: 0 }
1082 };
1083
1084 next_addr = align_up_to_page_size(next_addr);
1085
1086 tracing::trace!(next_addr, "loading the boot shim");
1087
1088 let crate::elf::LoadInfo {
1089 minimum_address_used: shim_base_addr,
1090 next_available_address: mut next_addr,
1091 entrypoint: shim_entry_point,
1092 } = crate::elf::load_static_elf(
1093 importer,
1094 shim,
1095 0,
1096 next_addr,
1097 false,
1098 BootPageAcceptance::Exclusive,
1099 "underhill-boot-shim",
1100 )
1101 .map_err(Error::Shim)?;
1102
1103 tracing::trace!(shim_base_addr, "boot shim loaded");
1104
1105 tracing::trace!(next_addr, "loading the command line");
1106
1107 let cmdline_base = next_addr;
1108 let (cmdline, policy) = match command_line {
1109 CommandLineType::Static(val) => (val, CommandLinePolicy::STATIC),
1110 CommandLineType::HostAppendable(val) => (val, CommandLinePolicy::APPEND_CHOSEN),
1111 };
1112
1113 if cmdline.len() > COMMAND_LINE_SIZE {
1114 return Err(Error::CommandLineSize(cmdline.len()));
1115 }
1116
1117 let mut static_command_line = [0; COMMAND_LINE_SIZE];
1118 static_command_line[..cmdline.len()].copy_from_slice(cmdline.as_bytes());
1119 let paravisor_command_line = ParavisorCommandLine {
1120 policy,
1121 static_command_line_len: cmdline.len() as u16,
1122 static_command_line,
1123 };
1124
1125 importer.import_pages(
1126 cmdline_base / HV_PAGE_SIZE,
1127 1,
1128 "underhill-command-line",
1129 BootPageAcceptance::Exclusive,
1130 paravisor_command_line.as_bytes(),
1131 )?;
1132
1133 next_addr += HV_PAGE_SIZE;
1134
1135 tracing::trace!(next_addr, "loading the boot shim parameters");
1136
1137 let shim_params_base = next_addr;
1138 let shim_params_size = HV_PAGE_SIZE;
1139
1140 next_addr += shim_params_size;
1141
1142 let parameter_region_size = PARAVISOR_VTL2_CONFIG_REGION_PAGE_COUNT_MAX * HV_PAGE_SIZE;
1143 let parameter_region_start = next_addr;
1144 next_addr += parameter_region_size;
1145
1146 tracing::debug!(parameter_region_start);
1147
1148 let bootshim_log_size = HV_PAGE_SIZE * 2;
1150 let bootshim_log_start = next_addr;
1151 next_addr += bootshim_log_size;
1152
1153 importer.import_pages(
1154 bootshim_log_start / HV_PAGE_SIZE,
1155 bootshim_log_size / HV_PAGE_SIZE,
1156 "ohcl-boot-shim-log-buffer",
1157 BootPageAcceptance::Exclusive,
1158 &[],
1159 )?;
1160
1161 let heap_start = next_addr;
1168 let heap_size = 16 * HV_PAGE_SIZE;
1169 importer.import_pages(
1170 heap_start / HV_PAGE_SIZE,
1171 heap_size / HV_PAGE_SIZE,
1172 "ohcl-boot-shim-heap",
1173 BootPageAcceptance::Exclusive,
1174 &[],
1175 )?;
1176 next_addr += heap_size;
1177
1178 next_addr = avoid_page_table_large_page_boundary(next_addr, u64::from(Arm64PageSize::Large));
1181
1182 let end_of_underhill_mem = next_addr;
1184
1185 let page_table_base_page_count = 5;
1188 let page_table_dynamic_page_count = 2 * page_table_base_page_count;
1189 let page_table_page_count = page_table_base_page_count + page_table_dynamic_page_count;
1190 let page_table_region_size = HV_PAGE_SIZE * page_table_page_count;
1191 let page_table_region_start = next_addr;
1192 next_addr += page_table_region_size;
1193
1194 tracing::debug!(page_table_region_start, page_table_region_size);
1195
1196 let next_addr = next_addr;
1197
1198 if next_addr > memory_start_address + memory_size {
1200 return Err(Error::NotEnoughMemory(next_addr - memory_start_address));
1201 }
1202
1203 let calculate_shim_offset = |addr: u64| -> i64 { addr.wrapping_sub(shim_base_addr) as i64 };
1205 let shim_params = ShimParamsRaw {
1206 kernel_entry_offset: calculate_shim_offset(kernel_entry_point),
1207 cmdline_offset: calculate_shim_offset(cmdline_base),
1208 initrd_offset: calculate_shim_offset(initrd_gpa),
1209 initrd_size,
1210 initrd_crc,
1211 supported_isolation_type: match importer.isolation_config().isolation_type {
1212 IsolationType::None | IsolationType::Vbs => {
1213 loader_defs::shim::SupportedIsolationType::VBS
1214 }
1215 _ => panic!("only None and VBS are supported for ARM64"),
1216 },
1217 memory_start_offset: calculate_shim_offset(memory_start_address),
1218 memory_size,
1219 parameter_region_offset: calculate_shim_offset(parameter_region_start),
1220 parameter_region_size,
1221 vtl2_reserved_region_offset: 0,
1222 vtl2_reserved_region_size: 0,
1223 sidecar_offset: 0,
1224 sidecar_size: 0,
1225 sidecar_entry_offset: 0,
1226 used_start: calculate_shim_offset(memory_start_address),
1227 used_end: calculate_shim_offset(next_addr),
1228 bounce_buffer_start: 0,
1229 bounce_buffer_size: 0,
1230 log_buffer_start: calculate_shim_offset(bootshim_log_start),
1231 log_buffer_size: bootshim_log_size,
1232 heap_start_offset: calculate_shim_offset(heap_start),
1233 heap_size,
1234 persisted_state_region_offset: calculate_shim_offset(persisted_region_base),
1235 persisted_state_region_size: persisted_region_size,
1236 };
1237
1238 importer
1239 .import_pages(
1240 shim_params_base / HV_PAGE_SIZE,
1241 shim_params_size / HV_PAGE_SIZE,
1242 "underhill-shim-params",
1243 BootPageAcceptance::Exclusive,
1244 shim_params.as_bytes(),
1245 )
1246 .map_err(Error::Importer)?;
1247
1248 let mut measured_config = ParavisorMeasuredVtl0Config {
1249 magic: ParavisorMeasuredVtl0Config::MAGIC,
1250 ..FromZeros::new_zeroed()
1251 };
1252
1253 if let Some((uefi, vp_context)) = &supports_uefi {
1254 measured_config.supported_vtl0.set_uefi_supported(true);
1255 let vp_context_page = PARAVISOR_VTL0_MEASURED_CONFIG_BASE_PAGE_AARCH64 + 1;
1256 measured_config.uefi_info = UefiInfo {
1257 firmware: PageRegionDescriptor {
1258 base_page_number: uefi.firmware_base / HV_PAGE_SIZE,
1259 page_count: uefi.total_size / HV_PAGE_SIZE,
1260 },
1261 vtl0_vp_context: PageRegionDescriptor {
1262 base_page_number: vp_context_page,
1263 page_count: 1,
1264 },
1265 };
1266
1267 importer.import_pages(
1269 vp_context_page,
1270 1,
1271 "openhcl-uefi-vp-context",
1272 BootPageAcceptance::Exclusive,
1273 vp_context,
1274 )?;
1275 }
1276
1277 importer
1278 .import_pages(
1279 PARAVISOR_VTL0_MEASURED_CONFIG_BASE_PAGE_AARCH64,
1280 1,
1281 "underhill-measured-config",
1282 BootPageAcceptance::Exclusive,
1283 measured_config.as_bytes(),
1284 )
1285 .map_err(Error::Importer)?;
1286
1287 tracing::trace!(page_table_region_start, "loading the page tables");
1288
1289 let memory_attribute_indirection = MemoryAttributeIndirectionEl1([
1290 MemoryAttributeEl1::Device_nGnRnE,
1291 MemoryAttributeEl1::Normal_NonCacheable,
1292 MemoryAttributeEl1::Normal_WriteThrough,
1293 MemoryAttributeEl1::Normal_WriteBack,
1294 MemoryAttributeEl1::Device_nGnRnE,
1295 MemoryAttributeEl1::Device_nGnRnE,
1296 MemoryAttributeEl1::Device_nGnRnE,
1297 MemoryAttributeEl1::Device_nGnRnE,
1298 ]);
1299 let mut page_tables: Vec<u8> = vec![0; page_table_region_size as usize];
1300 let page_tables = page_table::aarch64::build_identity_page_tables_aarch64(
1301 page_table_region_start,
1302 memory_start_address,
1303 memory_size,
1304 memory_attribute_indirection,
1305 page_tables.as_mut_slice(),
1306 );
1307 assert!((page_tables.len() as u64).is_multiple_of(HV_PAGE_SIZE));
1308 let page_table_page_base = page_table_region_start / HV_PAGE_SIZE;
1309 assert!(page_tables.len() as u64 <= page_table_region_size);
1310 assert!(page_table_region_size as usize > page_tables.len());
1311
1312 if with_relocation {
1313 importer.relocation_region(
1315 memory_start_address,
1316 end_of_underhill_mem - memory_start_address,
1317 Arm64PageSize::Large.into(),
1318 PARAVISOR_DEFAULT_MEMORY_BASE_ADDRESS,
1319 1 << 48,
1320 true,
1321 false,
1322 0, )?;
1324
1325 importer.page_table_relocation(
1327 page_table_region_start,
1328 page_table_region_size / HV_PAGE_SIZE,
1329 page_tables.len() as u64 / HV_PAGE_SIZE,
1330 0,
1331 )?;
1332 }
1333
1334 importer.import_pages(
1335 page_table_page_base,
1336 page_table_page_count,
1337 "underhill-page-tables",
1338 BootPageAcceptance::Exclusive,
1339 page_tables,
1340 )?;
1341
1342 tracing::trace!("Importing register state");
1343
1344 let mut import_reg = |register| {
1345 importer
1346 .import_vp_register(register)
1347 .map_err(Error::Importer)
1348 };
1349
1350 let relative_boot_params_base = shim_params_base - shim_base_addr;
1352 import_reg(AArch64Register::X0(relative_boot_params_base).into())?;
1353
1354 import_reg(AArch64Register::Pc(shim_entry_point).into())?;
1356
1357 import_reg(AArch64Register::Cpsr(Cpsr64::new().with_sp(true).with_el(1).into()).into())?;
1360
1361 import_reg(
1384 AArch64Register::SctlrEl1(
1385 SctlrEl1::new()
1386 .with_m(true)
1391 .with_c(true)
1393 .with_i(true)
1395 .with_eos(true)
1397 .with_tscxt(true)
1398 .with_eis(true)
1399 .with_span(true)
1400 .with_n_tlsmd(true)
1401 .with_lsmaoe(true)
1402 .into(),
1403 )
1404 .into(),
1405 )?;
1406
1407 import_reg(
1413 AArch64Register::TcrEl1(
1414 TranslationControlEl1::new()
1415 .with_t0sz(0x11)
1416 .with_irgn0(1)
1417 .with_orgn0(1)
1418 .with_sh0(3)
1419 .with_tg0(TranslationGranule0::TG_4KB)
1420 .with_epd1(1)
1422 .with_tg1(TranslationGranule1::TG_4KB)
1424 .with_ips(IntermPhysAddrSize::IPA_48_BITS_256_TB)
1425 .into(),
1426 )
1427 .into(),
1428 )?;
1429
1430 import_reg(AArch64Register::MairEl1(memory_attribute_indirection.into()).into())?;
1432 import_reg(
1433 AArch64Register::Ttbr0El1(
1434 TranslationBaseEl1::new()
1435 .with_baddr(page_table_region_start)
1436 .into(),
1437 )
1438 .into(),
1439 )?;
1440
1441 import_reg(AArch64Register::VbarEl1(0).into())?;
1445
1446 let config_region_page_base = parameter_region_start / HV_PAGE_SIZE;
1448
1449 let slit_page_base = config_region_page_base + PARAVISOR_CONFIG_SLIT_PAGE_INDEX;
1451 let slit_parameter_area = importer.create_parameter_area(
1452 slit_page_base,
1453 PARAVISOR_CONFIG_SLIT_SIZE_PAGES as u32,
1454 "underhill-slit",
1455 )?;
1456 importer.import_parameter(slit_parameter_area, 0, IgvmParameterType::Slit)?;
1457
1458 let pptt_page_base = config_region_page_base + PARAVISOR_CONFIG_PPTT_PAGE_INDEX;
1460 let pptt_parameter_area = importer.create_parameter_area(
1461 pptt_page_base,
1462 PARAVISOR_CONFIG_PPTT_SIZE_PAGES as u32,
1463 "underhill-pptt",
1464 )?;
1465 importer.import_parameter(pptt_parameter_area, 0, IgvmParameterType::Pptt)?;
1466
1467 let dt_page_base = config_region_page_base + PARAVISOR_CONFIG_DEVICE_TREE_PAGE_INDEX;
1469 let dt_parameter_area = importer.create_parameter_area(
1470 dt_page_base,
1471 PARAVISOR_CONFIG_DEVICE_TREE_SIZE_PAGES as u32,
1472 "underhill-device-tree",
1473 )?;
1474 importer.import_parameter(dt_parameter_area, 0, IgvmParameterType::DeviceTree)?;
1475
1476 let vtl2_measured_config = ParavisorMeasuredVtl2Config {
1477 magic: ParavisorMeasuredVtl2Config::MAGIC,
1478 vtom_offset_bit: 0,
1479 padding: [0; 7],
1480 };
1481
1482 importer
1483 .import_pages(
1484 config_region_page_base + PARAVISOR_MEASURED_VTL2_CONFIG_PAGE_INDEX,
1485 PARAVISOR_MEASURED_VTL2_CONFIG_SIZE_PAGES,
1486 "underhill-vtl2-measured-config",
1487 BootPageAcceptance::Exclusive,
1488 vtl2_measured_config.as_bytes(),
1489 )
1490 .map_err(Error::Importer)?;
1491
1492 let imported_region_base =
1493 config_region_page_base + PARAVISOR_MEASURED_VTL2_CONFIG_ACCEPTED_MEMORY_PAGE_INDEX;
1494
1495 importer.set_imported_regions_config_page(imported_region_base);
1496
1497 let expected_page_hashes_base =
1500 config_region_page_base + PARAVISOR_MEASURED_VTL2_CONFIG_PAGE_HASHES_PAGE_INDEX;
1501 importer.set_expected_page_hashes_config_page(expected_page_hashes_base);
1502
1503 Ok(())
1504}
1505
1506#[cfg(test)]
1507mod page_table_layout_tests {
1508 use super::*;
1509
1510 #[test]
1511 fn page_table_region_avoids_large_page_boundary() {
1512 for large_page_size in [X64_LARGE_PAGE_SIZE, u64::from(Arm64PageSize::Large)] {
1513 assert_eq!(
1514 avoid_page_table_large_page_boundary(large_page_size, large_page_size),
1515 large_page_size + HV_PAGE_SIZE
1516 );
1517 assert_eq!(
1518 avoid_page_table_large_page_boundary(
1519 large_page_size - HV_PAGE_SIZE,
1520 large_page_size
1521 ),
1522 large_page_size - HV_PAGE_SIZE
1523 );
1524 }
1525 }
1526}