1use crate::common::ChunkBuf;
7use crate::common::ImportFileRegion;
8use crate::common::ImportFileRegionError;
9use crate::common::ReadSeek;
10use crate::common::import_default_gdt;
11use crate::elf::load_static_elf;
12use crate::importer::Aarch64Register;
13use crate::importer::BootPageAcceptance;
14use crate::importer::GuestArch;
15use crate::importer::ImageLoad;
16use crate::importer::X86Register;
17use aarch64defs::Cpsr64;
18use aarch64defs::IntermPhysAddrSize;
19use aarch64defs::SctlrEl1;
20use aarch64defs::TranslationBaseEl1;
21use aarch64defs::TranslationControlEl1;
22use aarch64defs::TranslationGranule0;
23use aarch64defs::TranslationGranule1;
24use bitfield_struct::bitfield;
25use hvdef::HV_PAGE_SIZE;
26use loader_defs::linux as defs;
27use memory_range::MemoryRange;
28use page_table::IdentityMapSize;
29use page_table::x64::IdentityMapBuilder;
30use page_table::x64::PAGE_TABLE_MAX_BYTES;
31use page_table::x64::PAGE_TABLE_MAX_COUNT;
32use page_table::x64::PageTable;
33use page_table::x64::align_up_to_large_page_size;
34use page_table::x64::align_up_to_page_size;
35use std::ffi::CString;
36use std::io::Read;
37use std::io::Seek;
38use std::io::SeekFrom;
39use std::mem::size_of;
40use thiserror::Error;
41use vm_topology::memory::MemoryLayout;
42use zerocopy::FromBytes;
43use zerocopy::FromZeros;
44use zerocopy::Immutable;
45use zerocopy::IntoBytes;
46use zerocopy::KnownLayout;
47
48struct ZeroPageBuildResult {
49 boot_params: defs::boot_params,
50 additional_pages: Option<MemoryRange>,
51}
52
53fn build_zero_page(
55 mem_layout: &MemoryLayout,
56 acpi_len: usize,
57 smbios_struct_len: usize,
58 additional_page_count: u64,
59 cmdline: &CString,
60 initrd_base: u32,
61 initrd_size: u32,
62 bzimage_header: Option<&defs::setup_header>,
63) -> Result<ZeroPageBuildResult, Error> {
64 const LOADER_TYPE_UNREGISTERED: u8 = 0xff;
68
69 let mut hdr = match bzimage_header {
72 Some(orig) => *orig,
73 None => defs::setup_header {
74 boot_flag: 0xaa55.into(),
75 header: 0x53726448.into(),
76 kernel_alignment: 0x100000.into(),
77 ..FromZeros::new_zeroed()
78 },
79 };
80
81 hdr.type_of_loader = LOADER_TYPE_UNREGISTERED;
83 hdr.cmd_line_ptr = CMDLINE_BASE.try_into().expect("must fit in u32");
84 hdr.cmdline_size = (cmdline.as_bytes().len() as u64)
85 .try_into()
86 .expect("must fit in u32");
87 hdr.ramdisk_image = initrd_base.into();
88 hdr.ramdisk_size = initrd_size.into();
89
90 let mut p = defs::boot_params {
91 hdr,
92 ..FromZeros::new_zeroed()
93 };
94
95 let mut ram = mem_layout.ram().iter().cloned();
96 let range = ram.next().expect("at least one ram range");
97 assert_eq!(range.range.start(), 0);
98 assert!(range.range.end() >= 0x100000);
99
100 const ONE_MB: u64 = 0x100000;
119 let aligned_acpi_len = align_up_to_page_size(acpi_len as u64);
120 let acpi_end = ACPI_TABLES_BASE + aligned_acpi_len;
121 let aligned_smbios_len = align_up_to_page_size(smbios_struct_len as u64);
122 let smbios_end = acpi_end + aligned_smbios_len;
123 let additional_size = additional_page_count
124 .checked_mul(HV_PAGE_SIZE)
125 .ok_or(Error::LowTablesTooLarge(u64::MAX, RSDP_BASE))?;
126 let additional_end = smbios_end
127 .checked_add(additional_size)
128 .ok_or(Error::LowTablesTooLarge(u64::MAX, RSDP_BASE))?;
129 if additional_end > RSDP_BASE {
130 return Err(Error::LowTablesTooLarge(additional_end, RSDP_BASE));
131 }
132 let additional_pages =
133 (additional_size != 0).then(|| MemoryRange::new(smbios_end..additional_end));
134
135 let e820_cap = p.e820_map.len();
140 let mut n = 0;
141 let mut push = |addr: u64, size: u64, typ: u32| -> Result<(), Error> {
142 if size == 0 {
143 return Ok(());
144 }
145 let entry = p
146 .e820_map
147 .get_mut(n)
148 .ok_or(Error::TooManyMemoryRanges(e820_cap))?;
149 *entry = defs::e820entry {
150 addr: addr.into(),
151 size: size.into(),
152 typ: typ.into(),
153 };
154 n += 1;
155 Ok(())
156 };
157 push(0, ACPI_TABLES_BASE, defs::E820_RAM)?;
158 push(ACPI_TABLES_BASE, aligned_acpi_len, defs::E820_ACPI)?;
159 push(acpi_end, aligned_smbios_len, defs::E820_RESERVED)?;
160 push(smbios_end, additional_size, defs::E820_RESERVED)?;
161 push(additional_end, RSDP_BASE - additional_end, defs::E820_RAM)?;
162 push(RSDP_BASE, ONE_MB - RSDP_BASE, defs::E820_RESERVED)?;
163 push(ONE_MB, range.range.end() - ONE_MB, defs::E820_RAM)?;
164 for range in ram {
165 push(range.range.start(), range.range.len(), defs::E820_RAM)?;
166 }
167 p.e820_entries = n as u8;
168
169 Ok(ZeroPageBuildResult {
170 boot_params: p,
171 additional_pages,
172 })
173}
174
175#[derive(Debug, Error)]
176pub enum FlatLoaderError {
177 #[error("unsupported ELF File byte order")]
178 BigEndianElfOnLittle,
179 #[error("error reading kernel data structure")]
180 BadImageMagic,
181 #[error("big-endian kernel image is not supported")]
182 BigEndianKernelImage,
183 #[error("only images with 4K pages are supported")]
184 FourKibPageImageIsRequired,
185 #[error("the kernel is required to run in the low memory; not supported")]
186 LowMemoryKernel,
187 #[error("failed to read kernel image")]
188 ReadKernelImage,
189 #[error("failed to seek to file offset as pointed by the ELF program header")]
190 SeekKernelStart,
191 #[error("failed to seek to offset of kernel image")]
192 SeekKernelImage,
193}
194
195#[derive(Debug, Error)]
196pub enum Error {
197 #[error("elf loader error")]
198 ElfLoader(#[source] crate::elf::Error),
199 #[error("bzImage parse error")]
200 BzImage(#[source] crate::bzimage::Error),
201 #[error("flat loader error")]
202 FlatLoader(#[source] FlatLoaderError),
203 #[error("Address is not page aligned")]
204 UnalignedAddress(u64),
205 #[error("importer error")]
206 Importer(#[source] anyhow::Error),
207 #[error("failed to import initrd")]
208 ImportInitrd(#[source] ImportFileRegionError),
209 #[error("failed to import bzImage payload")]
210 ImportBzImage(#[source] ImportFileRegionError),
211 #[error("PageTableBuilder: {0}")]
212 PageTableBuilder(#[from] page_table::Error),
213 #[error("kernel command line ({0} bytes) exceeds its {1:#x}-byte slot")]
214 CommandLineTooLong(usize, u64),
215 #[error("low-memory tables and boot pages end at {0:#x}, past the reserved region at {1:#x}")]
216 LowTablesTooLarge(u64, u64),
217 #[error("SNP CC blob address {0:#x} does not fit in the Linux boot protocol field")]
218 SnpCcBlobAddressTooHigh(u64),
219 #[error("too many memory ranges to fit in the {0}-entry e820 map")]
220 TooManyMemoryRanges(usize),
221 #[error("acpi tables are empty")]
222 EmptyAcpiTables,
223}
224
225pub struct AcpiTables {
233 pub rsdp: Vec<u8>,
235 pub tables: Vec<u8>,
237}
238
239const GDT_BASE: u64 = 0x1000;
250const ZERO_PAGE_BASE: u64 = 0x2000;
251const CMDLINE_BASE: u64 = 0x3000;
252const CR3_BASE: u64 = 0x4000;
253const LOW_METADATA_END: u64 = CR3_BASE + PAGE_TABLE_MAX_BYTES as u64;
256const ACPI_TABLES_BASE: u64 = LOW_METADATA_END + 0x1000;
259const RSDP_BASE: u64 = 0xe0000;
263const SMBIOS_FSEGMENT_BASE: u64 = 0xf0000;
268const KERNEL_BASE: u64 = 0x100000;
270
271#[derive(Debug, Clone, Copy)]
273pub struct SnpBootConfig;
274
275const SNP_BOOT_PAGE_COUNT: u64 = 4;
276
277fn smbios_struct_table_base(acpi_tables_len: usize) -> u64 {
282 ACPI_TABLES_BASE + align_up_to_page_size(acpi_tables_len as u64)
283}
284
285const _: () = {
288 assert!(GDT_BASE < ZERO_PAGE_BASE);
289 assert!(ZERO_PAGE_BASE < CMDLINE_BASE);
290 assert!(CMDLINE_BASE < CR3_BASE);
291 assert!(ACPI_TABLES_BASE < RSDP_BASE);
292 assert!(RSDP_BASE < SMBIOS_FSEGMENT_BASE);
293 assert!(SMBIOS_FSEGMENT_BASE < KERNEL_BASE);
294};
295
296#[derive(Debug, PartialEq, Eq, Clone, Copy)]
297pub enum InitrdAddressType {
298 AfterKernel,
300 Address(u64),
302}
303
304pub struct InitrdConfig<'a> {
305 pub initrd_address: InitrdAddressType,
306 pub initrd: &'a mut dyn ReadSeek,
307 pub size: u64,
308}
309
310#[derive(Debug, Default)]
312pub struct KernelInfo {
313 pub gpa: u64,
315 pub size: u64,
317 pub entrypoint: u64,
319}
320
321#[derive(Debug, Default)]
323pub struct InitrdInfo {
324 pub gpa: u64,
326 pub size: u64,
328}
329
330#[derive(Debug, Default)]
332pub struct LoadInfo {
333 pub kernel: KernelInfo,
335 pub initrd: Option<InitrdInfo>,
337 pub dtb: Option<std::ops::Range<u64>>,
339 pub bzimage_setup_header: Option<defs::setup_header>,
343}
344
345fn import_snp_boot_pages(
346 importer: &mut impl ImageLoad<X86Register>,
347 range: MemoryRange,
348) -> Result<u64, Error> {
349 assert_eq!(range.len(), SNP_BOOT_PAGE_COUNT * HV_PAGE_SIZE);
350 let secrets_address = range.start();
351 let cpuid_address = range.start() + HV_PAGE_SIZE;
352 let cc_blob_address = range.start() + 2 * HV_PAGE_SIZE;
353 let cc_setup_data_address = range.start() + 3 * HV_PAGE_SIZE;
354
355 importer
356 .import_pages(
357 secrets_address / HV_PAGE_SIZE,
358 1,
359 "linux-snp-secrets",
360 BootPageAcceptance::SecretsPage,
361 &[],
362 )
363 .map_err(Error::Importer)?;
364 importer
365 .import_pages(
366 cpuid_address / HV_PAGE_SIZE,
367 1,
368 "linux-snp-cpuid",
369 BootPageAcceptance::CpuidPage,
370 &[],
371 )
372 .map_err(Error::Importer)?;
373
374 let cc_blob = defs::cc_blob_sev_info {
375 magic: defs::CC_BLOB_SEV_INFO_MAGIC,
376 version: 0,
377 _reserved: 0,
378 secrets_phys: secrets_address,
379 secrets_len: HV_PAGE_SIZE as u32,
380 _rsvd1: 0,
381 cpuid_phys: cpuid_address,
382 cpuid_len: HV_PAGE_SIZE as u32,
383 _rsvd2: 0,
384 };
385 importer
386 .import_pages(
387 cc_blob_address / HV_PAGE_SIZE,
388 1,
389 "linux-snp-cc-blob",
390 BootPageAcceptance::Exclusive,
391 cc_blob.as_bytes(),
392 )
393 .map_err(Error::Importer)?;
394
395 let cc_blob_address = u32::try_from(cc_blob_address)
396 .map_err(|_| Error::SnpCcBlobAddressTooHigh(cc_blob_address))?;
397 let cc_setup_data = defs::cc_setup_data {
398 header: defs::setup_data {
399 next: 0,
400 ty: defs::SETUP_CC_BLOB,
401 len: (size_of::<defs::cc_setup_data>() - size_of::<defs::setup_data>()) as u32,
402 },
403 cc_blob_address,
404 _padding: [0; 3],
405 };
406 importer
407 .import_pages(
408 cc_setup_data_address / HV_PAGE_SIZE,
409 1,
410 "linux-snp-cc-setup-data",
411 BootPageAcceptance::Exclusive,
412 cc_setup_data.as_bytes(),
413 )
414 .map_err(Error::Importer)?;
415
416 Ok(cc_setup_data_address)
417}
418
419fn check_address_alignment(address: u64) -> Result<(), Error> {
421 if !address.is_multiple_of(HV_PAGE_SIZE) {
422 Err(Error::UnalignedAddress(address))
423 } else {
424 Ok(())
425 }
426}
427
428fn import_initrd<R: GuestArch>(
430 initrd: Option<InitrdConfig<'_>>,
431 next_addr: u64,
432 importer: &mut dyn ImageLoad<R>,
433) -> Result<Option<InitrdInfo>, Error> {
434 let initrd_info = match initrd {
435 Some(cfg) => {
436 let initrd_address = match cfg.initrd_address {
437 InitrdAddressType::AfterKernel => align_up_to_large_page_size(next_addr),
438 InitrdAddressType::Address(addr) => addr,
439 };
440
441 tracing::trace!(initrd_address, "loading initrd");
442 check_address_alignment(initrd_address)?;
443
444 ChunkBuf::new()
445 .import_file_region(
446 importer,
447 ImportFileRegion {
448 file: cfg.initrd,
449 file_offset: 0,
450 file_length: cfg.size,
451 gpa: initrd_address,
452 memory_length: cfg.size,
453 acceptance: BootPageAcceptance::Exclusive,
454 tag: "linux-initrd",
455 },
456 )
457 .map_err(Error::ImportInitrd)?;
458
459 Some(InitrdInfo {
460 gpa: initrd_address,
461 size: cfg.size,
462 })
463 }
464 None => None,
465 };
466 Ok(initrd_info)
467}
468
469pub fn load_kernel_and_initrd_x64<F>(
485 importer: &mut dyn ImageLoad<X86Register>,
486 kernel_image: &mut F,
487 kernel_minimum_start_address: u64,
488 initrd: Option<InitrdConfig<'_>>,
489) -> Result<LoadInfo, Error>
490where
491 F: Read + Seek,
492{
493 tracing::trace!(kernel_minimum_start_address, "loading x86_64 kernel");
494
495 if crate::bzimage::is_bzimage(kernel_image).map_err(Error::BzImage)? {
496 tracing::info!("detected bzImage format, loading via Linux boot protocol");
497 return load_bzimage(importer, kernel_image, kernel_minimum_start_address, initrd);
498 }
499
500 let elf_load_info = load_static_elf(
501 importer,
502 kernel_image,
503 kernel_minimum_start_address,
504 0,
505 false,
506 BootPageAcceptance::Exclusive,
507 "linux-kernel",
508 )
509 .map_err(Error::ElfLoader)?;
510
511 let crate::elf::LoadInfo {
512 minimum_address_used: min_addr,
513 next_available_address: next_addr,
514 entrypoint,
515 } = elf_load_info;
516 tracing::trace!(min_addr, next_addr, entrypoint, "loaded kernel");
517
518 let initrd_info = import_initrd(initrd, next_addr, importer)?;
519
520 Ok(LoadInfo {
521 kernel: KernelInfo {
522 gpa: min_addr,
523 size: next_addr - min_addr,
524 entrypoint,
525 },
526 initrd: initrd_info,
527 dtb: None,
528 bzimage_setup_header: None,
529 })
530}
531
532fn load_bzimage(
536 importer: &mut dyn ImageLoad<X86Register>,
537 kernel_image: &mut (impl Read + Seek),
538 kernel_start_address: u64,
539 initrd: Option<InitrdConfig<'_>>,
540) -> Result<LoadInfo, Error> {
541 let info = crate::bzimage::parse_bzimage(kernel_image).map_err(Error::BzImage)?;
542
543 check_address_alignment(kernel_start_address)?;
544
545 let payload_offset = (info.setup_sects as u64 + 1) * 512;
546 let payload_len = info.protected_mode_size;
547 let payload_memory_len = align_up_to_page_size(payload_len);
548 let entrypoint = kernel_start_address + info.entry_offset;
549
550 tracing::info!(
551 kernel_start_address = format_args!("{:#x}", kernel_start_address),
552 payload_offset,
553 payload_len,
554 entrypoint = format_args!("{:#x}", entrypoint),
555 "loading bzImage payload into guest memory"
556 );
557
558 ChunkBuf::new()
559 .import_file_region(
560 importer,
561 ImportFileRegion {
562 file: kernel_image,
563 file_offset: payload_offset,
564 file_length: payload_len,
565 gpa: kernel_start_address,
566 memory_length: payload_memory_len,
567 acceptance: BootPageAcceptance::Exclusive,
568 tag: "linux-kernel",
569 },
570 )
571 .map_err(Error::ImportBzImage)?;
572
573 let next_addr = kernel_start_address + payload_memory_len;
576 let pref_address: u64 = info.setup_header.pref_address.into();
577 let init_end = kernel_start_address
578 .max(pref_address)
579 .saturating_add(info.init_size as u64);
580 let next_addr = next_addr.max(init_end);
581 let initrd_info = import_initrd(initrd, next_addr, importer)?;
582
583 Ok(LoadInfo {
584 kernel: KernelInfo {
585 gpa: kernel_start_address,
586 size: payload_memory_len,
587 entrypoint,
588 },
589 initrd: initrd_info,
590 dtb: None,
591 bzimage_setup_header: Some(info.setup_header),
592 })
593}
594
595fn import_config(
602 importer: &mut impl ImageLoad<X86Register>,
603 load_info: &LoadInfo,
604 cmdline: &CString,
605 mem_layout: &MemoryLayout,
606 acpi: &AcpiTables,
607 smbios: Option<&crate::smbios::BuiltSmbios>,
608 snp_boot: Option<SnpBootConfig>,
609) -> Result<(), Error> {
610 let raw_cmdline = cmdline.as_bytes_with_nul();
613 if raw_cmdline.len() as u64 > CR3_BASE - CMDLINE_BASE {
614 return Err(Error::CommandLineTooLong(
615 raw_cmdline.len(),
616 CR3_BASE - CMDLINE_BASE,
617 ));
618 }
619 if raw_cmdline.len() > 1 {
620 let cmdline_size_pages = align_up_to_page_size(raw_cmdline.len() as u64) / HV_PAGE_SIZE;
621 importer
622 .import_pages(
623 CMDLINE_BASE / HV_PAGE_SIZE,
624 cmdline_size_pages,
625 "linux-commandline",
626 BootPageAcceptance::Exclusive,
627 raw_cmdline,
628 )
629 .map_err(Error::Importer)?;
630 }
631
632 import_default_gdt(importer, GDT_BASE / HV_PAGE_SIZE).map_err(Error::Importer)?;
633 let mut page_table_work_buffer: Vec<PageTable> =
634 vec![PageTable::new_zeroed(); PAGE_TABLE_MAX_COUNT];
635 let mut page_table: Vec<u8> = vec![0; PAGE_TABLE_MAX_BYTES];
636 let page_table_builder = IdentityMapBuilder::new(
637 CR3_BASE,
638 IdentityMapSize::Size4Gb,
639 page_table_work_buffer.as_mut_slice(),
640 page_table.as_mut_slice(),
641 )?;
642 let page_table = page_table_builder.build();
643 assert!((page_table.len() as u64).is_multiple_of(HV_PAGE_SIZE));
644 importer
645 .import_pages(
646 CR3_BASE / HV_PAGE_SIZE,
647 page_table.len() as u64 / HV_PAGE_SIZE,
648 "linux-pagetables",
649 BootPageAcceptance::Exclusive,
650 page_table,
651 )
652 .map_err(Error::Importer)?;
653
654 if acpi.tables.is_empty() {
655 return Err(Error::EmptyAcpiTables);
656 }
657 let acpi_tables_size_pages = align_up_to_page_size(acpi.tables.len() as u64) / HV_PAGE_SIZE;
658 importer
659 .import_pages(
660 RSDP_BASE / HV_PAGE_SIZE,
661 1,
662 "linux-rsdp",
663 BootPageAcceptance::Exclusive,
664 &acpi.rsdp,
665 )
666 .map_err(Error::Importer)?;
667 importer
668 .import_pages(
669 ACPI_TABLES_BASE / HV_PAGE_SIZE,
670 acpi_tables_size_pages,
671 "linux-acpi-tables",
672 BootPageAcceptance::Exclusive,
673 &acpi.tables,
674 )
675 .map_err(Error::Importer)?;
676
677 let requested_page_count = snp_boot.map_or(0, |_| SNP_BOOT_PAGE_COUNT);
678 let ZeroPageBuildResult {
679 mut boot_params,
680 additional_pages,
681 } = build_zero_page(
682 mem_layout,
683 acpi.tables.len(),
684 smbios.map_or(0, |s| s.structure_table.len()),
685 requested_page_count,
686 cmdline,
687 load_info.initrd.as_ref().map(|info| info.gpa).unwrap_or(0) as u32,
688 load_info.initrd.as_ref().map(|info| info.size).unwrap_or(0) as u32,
689 load_info.bzimage_setup_header.as_ref(),
690 )?;
691 if let Some(allocated_range) = additional_pages {
692 boot_params.hdr.setup_data = import_snp_boot_pages(importer, allocated_range)?.into();
693 }
694 importer
695 .import_pages(
696 ZERO_PAGE_BASE / HV_PAGE_SIZE,
697 1,
698 "linux-zeropage",
699 BootPageAcceptance::Exclusive,
700 boot_params.as_bytes(),
701 )
702 .map_err(Error::Importer)?;
703
704 let mut import_reg = |register| {
706 importer
707 .import_vp_register(register)
708 .map_err(Error::Importer)
709 };
710
711 import_reg(X86Register::Cr0(x86defs::X64_CR0_PG | x86defs::X64_CR0_PE))?;
712 import_reg(X86Register::Cr3(CR3_BASE))?;
713 import_reg(X86Register::Cr4(x86defs::X64_CR4_PAE))?;
714 import_reg(X86Register::Efer(
715 x86defs::X64_EFER_SCE
716 | x86defs::X64_EFER_LME
717 | x86defs::X64_EFER_LMA
718 | x86defs::X64_EFER_NXE,
719 ))?;
720 import_reg(X86Register::Pat(x86defs::X86X_MSR_DEFAULT_PAT))?;
721
722 import_reg(X86Register::Rip(load_info.kernel.entrypoint))?;
724 import_reg(X86Register::Rsi(ZERO_PAGE_BASE))?;
725
726 import_reg(X86Register::MtrrDefType(0xc00))?;
729 import_reg(X86Register::MtrrFix64k00000(0x0606060606060606))?;
730 import_reg(X86Register::MtrrFix16k80000(0x0606060606060606))?;
731
732 if let Some(smbios) = smbios {
733 let anchor_pages = align_up_to_page_size(smbios.entry_point.len() as u64) / HV_PAGE_SIZE;
737 importer
738 .import_pages(
739 SMBIOS_FSEGMENT_BASE / HV_PAGE_SIZE,
740 anchor_pages,
741 "linux-smbios-anchor",
742 BootPageAcceptance::Exclusive,
743 &smbios.entry_point,
744 )
745 .map_err(Error::Importer)?;
746
747 let table_base = smbios_struct_table_base(acpi.tables.len());
748 let table_pages = align_up_to_page_size(smbios.structure_table.len() as u64) / HV_PAGE_SIZE;
749 importer
750 .import_pages(
751 table_base / HV_PAGE_SIZE,
752 table_pages,
753 "linux-smbios-tables",
754 BootPageAcceptance::Exclusive,
755 &smbios.structure_table,
756 )
757 .map_err(Error::Importer)?;
758 }
759
760 Ok(())
761}
762
763pub fn load_config_x86(
779 importer: &mut impl ImageLoad<X86Register>,
780 load_info: &LoadInfo,
781 cmdline: &CString,
782 mem_layout: &MemoryLayout,
783 build_acpi: impl FnOnce(u64) -> AcpiTables,
784 smbios: Option<crate::smbios::SmbiosTables<'_>>,
785 snp_boot: Option<SnpBootConfig>,
786) -> Result<(), Error> {
787 let acpi_tables = build_acpi(LOW_METADATA_END);
791
792 let smbios = smbios.map(|tables| {
797 crate::smbios::build(&tables, smbios_struct_table_base(acpi_tables.tables.len()))
798 });
799
800 import_config(
801 importer,
802 load_info,
803 cmdline,
804 mem_layout,
805 &acpi_tables,
806 smbios.as_ref(),
807 snp_boot,
808 )
809}
810
811pub fn load_x86<F>(
818 importer: &mut impl ImageLoad<X86Register>,
819 kernel_image: &mut F,
820 initrd: Option<InitrdConfig<'_>>,
821 cmdline: &CString,
822 mem_layout: &MemoryLayout,
823 build_acpi: impl FnOnce(u64) -> AcpiTables,
824 smbios: Option<crate::smbios::SmbiosTables<'_>>,
825 snp_boot: Option<SnpBootConfig>,
826) -> Result<LoadInfo, Error>
827where
828 F: Read + Seek,
829{
830 let load_info = load_kernel_and_initrd_x64(importer, kernel_image, KERNEL_BASE, initrd)?;
831 load_config_x86(
832 importer, &load_info, cmdline, mem_layout, build_acpi, smbios, snp_boot,
833 )?;
834 Ok(load_info)
835}
836
837open_enum::open_enum! {
838 #[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
839 pub enum Aarch64ImagePageSize: u64 {
840 UNSPECIFIED = 0,
841 PAGE4_K = 1,
842 PAGE16_K = 2,
843 PAGE64_K = 3,
844 }
845
846}
847
848impl Aarch64ImagePageSize {
849 const fn into_bits(self) -> u64 {
850 self.0
851 }
852
853 const fn from_bits(bits: u64) -> Self {
854 Self(bits)
855 }
856}
857
858#[bitfield(u64)]
860struct Aarch64ImageFlags {
861 #[bits(1)]
863 pub big_endian: bool,
864 #[bits(2)]
870 pub page_size: Aarch64ImagePageSize,
871 #[bits(1)]
878 pub any_start_address: bool,
879 #[bits(60)]
881 pub _padding: u64,
882}
883
884#[derive(Debug, IntoBytes, Immutable, KnownLayout, FromBytes)]
887#[repr(C)]
888struct Aarch64ImageHeader {
889 _code0: u32,
891 _code1: u32,
893 text_offset: u64,
895 image_size: u64,
897 flags: u64,
899 _res2: u64,
901 _res3: u64,
903 _res4: u64,
905 magic: [u8; 4],
907 _res5: u32,
909}
910
911const AARCH64_MAGIC_NUMBER: &[u8] = b"ARM\x64";
912
913pub fn load_kernel_and_initrd_arm64<F>(
925 importer: &mut dyn ImageLoad<Aarch64Register>,
926 kernel_image: &mut F,
927 kernel_minimum_start_address: u64,
928 initrd: Option<InitrdConfig<'_>>,
929 device_tree_blob: Option<&[u8]>,
930) -> Result<LoadInfo, Error>
931where
932 F: Read + Seek,
933{
934 tracing::trace!(kernel_minimum_start_address, "loading aarch64 kernel");
935
936 assert_eq!(
937 kernel_minimum_start_address & ((1 << 21) - 1),
938 0,
939 "Start offset must be aligned on the 2MiB boundary"
940 );
941
942 kernel_image
943 .seek(SeekFrom::Start(0))
944 .map_err(|_| Error::FlatLoader(FlatLoaderError::SeekKernelStart))?;
945
946 let mut header = Aarch64ImageHeader::new_zeroed();
947 kernel_image
948 .read_exact(header.as_mut_bytes())
949 .map_err(|_| Error::FlatLoader(FlatLoaderError::ReadKernelImage))?;
950
951 tracing::debug!("aarch64 kernel header {header:x?}");
952
953 if header.magic != AARCH64_MAGIC_NUMBER {
954 return Err(Error::FlatLoader(FlatLoaderError::BadImageMagic));
955 }
956
957 let flags = Aarch64ImageFlags::from(header.flags);
958 if flags.big_endian() {
959 return Err(Error::FlatLoader(FlatLoaderError::BigEndianKernelImage));
960 }
961 if flags.page_size() != Aarch64ImagePageSize::PAGE4_K {
962 return Err(Error::FlatLoader(
963 FlatLoaderError::FourKibPageImageIsRequired,
964 ));
965 }
966 if !flags.any_start_address() {
967 return Err(Error::FlatLoader(FlatLoaderError::LowMemoryKernel));
968 }
969
970 kernel_image
974 .seek(SeekFrom::Start(0))
975 .map_err(|_| Error::FlatLoader(FlatLoaderError::SeekKernelStart))?;
976
977 let mut image = Vec::new();
978 kernel_image
979 .read_to_end(&mut image)
980 .map_err(|_| Error::FlatLoader(FlatLoaderError::ReadKernelImage))?;
981
982 let kernel_load_offset = (kernel_minimum_start_address + header.text_offset) as usize;
983 let kernel_size = if header.image_size != 0 {
984 header.image_size
985 } else {
986 image.len() as u64
987 };
988
989 let kernel_size = align_up_to_page_size(kernel_size);
990 importer
991 .import_pages(
992 kernel_load_offset as u64 / HV_PAGE_SIZE,
993 kernel_size / HV_PAGE_SIZE,
994 "linux-kernel",
995 BootPageAcceptance::Exclusive,
996 &image,
997 )
998 .map_err(Error::Importer)?;
999
1000 let next_addr = kernel_load_offset as u64 + kernel_size;
1001
1002 let (next_addr, dtb) = if let Some(device_tree_blob) = device_tree_blob {
1003 let dtb_addr = align_up_to_page_size(next_addr);
1004 tracing::trace!(dtb_addr, "loading device tree blob at {dtb_addr:x?}");
1005
1006 check_address_alignment(dtb_addr)?;
1007 let dtb_size_pages = align_up_to_page_size(device_tree_blob.len() as u64) / HV_PAGE_SIZE;
1008
1009 importer
1010 .import_pages(
1011 dtb_addr / HV_PAGE_SIZE,
1012 dtb_size_pages,
1013 "linux-device-tree",
1014 BootPageAcceptance::Exclusive,
1015 device_tree_blob,
1016 )
1017 .map_err(Error::Importer)?;
1018
1019 (
1020 dtb_addr + device_tree_blob.len() as u64,
1021 Some(dtb_addr..dtb_addr + device_tree_blob.len() as u64),
1022 )
1023 } else {
1024 (next_addr, None)
1025 };
1026
1027 let initrd_info = import_initrd(initrd, next_addr, importer)?;
1028
1029 Ok(LoadInfo {
1030 kernel: KernelInfo {
1031 gpa: kernel_minimum_start_address,
1032 size: kernel_size,
1033 entrypoint: kernel_load_offset as u64,
1034 },
1035 initrd: initrd_info,
1036 dtb,
1037 bzimage_setup_header: None,
1038 })
1039}
1040
1041pub fn set_direct_boot_registers_arm64(
1047 importer: &mut impl ImageLoad<Aarch64Register>,
1048 load_info: &LoadInfo,
1049) -> Result<(), Error> {
1050 let mut import_reg = |register| {
1051 importer
1052 .import_vp_register(register)
1053 .map_err(Error::Importer)
1054 };
1055
1056 import_reg(Aarch64Register::Pc(load_info.kernel.entrypoint))?;
1057 import_reg(Aarch64Register::Cpsr(
1058 Cpsr64::new()
1059 .with_sp(true)
1060 .with_el(1)
1061 .with_f(true)
1062 .with_i(true)
1063 .with_a(true)
1064 .with_d(true)
1065 .into(),
1066 ))?;
1067 import_reg(Aarch64Register::SctlrEl1(
1068 SctlrEl1::new()
1069 .with_m(false)
1073 .with_c(true)
1075 .with_i(true)
1077 .with_eos(true)
1079 .with_tscxt(true)
1080 .with_eis(true)
1081 .with_span(true)
1082 .with_n_tlsmd(true)
1083 .with_lsmaoe(true)
1084 .into(),
1085 ))?;
1086 import_reg(Aarch64Register::TcrEl1(
1087 TranslationControlEl1::new()
1088 .with_t0sz(0x11)
1089 .with_irgn0(1)
1090 .with_orgn0(1)
1091 .with_sh0(3)
1092 .with_tg0(TranslationGranule0::TG_4KB)
1093 .with_epd0(1)
1095 .with_epd1(1)
1097 .with_tg1(TranslationGranule1::TG_4KB)
1099 .with_ips(IntermPhysAddrSize::IPA_48_BITS_256_TB)
1100 .into(),
1101 ))?;
1102 import_reg(Aarch64Register::Ttbr0El1(TranslationBaseEl1::new().into()))?;
1103 import_reg(Aarch64Register::Ttbr1El1(TranslationBaseEl1::new().into()))?;
1104 import_reg(Aarch64Register::VbarEl1(0))?;
1105
1106 if let Some(dtb) = &load_info.dtb {
1107 import_reg(Aarch64Register::X0(dtb.start))?;
1108 }
1109
1110 Ok(())
1111}
1112#[cfg(test)]
1113mod tests {
1114 use super::*;
1115 use crate::importer::IgvmParameterType;
1116 use crate::importer::IsolationConfig;
1117 use crate::importer::ParameterAreaIndex;
1118 use crate::importer::StartupMemoryType;
1119 use test_with_tracing::test;
1120 use zerocopy::FromBytes;
1121
1122 const MB: u64 = 0x100000;
1123 const GB: u64 = 0x4000_0000;
1124
1125 fn make_layout(ram_size: u64) -> MemoryLayout {
1129 MemoryLayout::new(
1130 ram_size,
1131 &[MemoryRange::new(4 * GB - 128 * MB..4 * GB)],
1132 &[],
1133 &[],
1134 None,
1135 )
1136 .unwrap()
1137 }
1138
1139 fn assert_contiguous(p: &defs::boot_params, first_ram_end: u64) {
1144 let entries = p.e820_entries as usize;
1145 assert!(entries > 0);
1146 let mut expected_addr = 0u64;
1147 for i in 0..entries {
1148 let e = &p.e820_map[i];
1149 assert_ne!(u64::from(e.size), 0, "entry {i} is empty");
1150 assert_eq!(u64::from(e.addr), expected_addr, "gap/overlap at entry {i}");
1151 expected_addr = u64::from(e.addr) + u64::from(e.size);
1152 }
1153 assert_eq!(
1154 expected_addr, first_ram_end,
1155 "map ends at {expected_addr:#x}, expected {first_ram_end:#x}"
1156 );
1157 }
1158
1159 #[test]
1160 fn zero_page_layout_with_smbios() {
1161 let acpi_len = 0x1800; let smbios_len = 0x100; let p = build_zero_page(
1164 &make_layout(256 * MB),
1165 acpi_len,
1166 smbios_len,
1167 0,
1168 &CString::new("root=/dev/sda").unwrap(),
1169 0,
1170 0,
1171 None,
1172 )
1173 .unwrap()
1174 .boot_params;
1175
1176 let acpi_end = ACPI_TABLES_BASE + 0x2000;
1177 let smbios_end = acpi_end + 0x1000;
1178 let expected = [
1179 (0, ACPI_TABLES_BASE, defs::E820_RAM),
1180 (ACPI_TABLES_BASE, 0x2000, defs::E820_ACPI),
1181 (acpi_end, 0x1000, defs::E820_RESERVED),
1182 (smbios_end, RSDP_BASE - smbios_end, defs::E820_RAM),
1183 (RSDP_BASE, 0x100000 - RSDP_BASE, defs::E820_RESERVED),
1184 (0x100000, 256 * MB - 0x100000, defs::E820_RAM),
1185 ];
1186 assert_eq!(p.e820_entries as usize, expected.len());
1187 for (i, (addr, size, typ)) in expected.iter().enumerate() {
1188 let e = &p.e820_map[i];
1189 assert_eq!(u64::from(e.addr), *addr, "entry {i} addr");
1190 assert_eq!(u64::from(e.size), *size, "entry {i} size");
1191 assert_eq!(u32::from(e.typ), *typ, "entry {i} type");
1192 }
1193 assert_contiguous(&p, 256 * MB);
1194 }
1195
1196 #[test]
1197 fn zero_page_skips_empty_smbios_region() {
1198 let p = build_zero_page(
1201 &make_layout(256 * MB),
1202 0x1800,
1203 0,
1204 0,
1205 &CString::new("").unwrap(),
1206 0,
1207 0,
1208 None,
1209 )
1210 .unwrap()
1211 .boot_params;
1212
1213 let acpi_end = ACPI_TABLES_BASE + 0x2000;
1214 let expected = [
1215 (0, ACPI_TABLES_BASE, defs::E820_RAM),
1216 (ACPI_TABLES_BASE, 0x2000, defs::E820_ACPI),
1217 (acpi_end, RSDP_BASE - acpi_end, defs::E820_RAM),
1218 (RSDP_BASE, 0x100000 - RSDP_BASE, defs::E820_RESERVED),
1219 (0x100000, 256 * MB - 0x100000, defs::E820_RAM),
1220 ];
1221 assert_eq!(p.e820_entries as usize, expected.len());
1222 for (i, (addr, size, typ)) in expected.iter().enumerate() {
1223 let e = &p.e820_map[i];
1224 assert_eq!(u64::from(e.addr), *addr, "entry {i} addr");
1225 assert_eq!(u64::from(e.size), *size, "entry {i} size");
1226 assert_eq!(u32::from(e.typ), *typ, "entry {i} type");
1227 }
1228 assert_contiguous(&p, 256 * MB);
1229 }
1230
1231 #[test]
1232 fn zero_page_multiple_ram_ranges() {
1233 let p = build_zero_page(
1236 &make_layout(8 * GB),
1237 0x1000,
1238 0x1000,
1239 0,
1240 &CString::new("").unwrap(),
1241 0,
1242 0,
1243 None,
1244 )
1245 .unwrap()
1246 .boot_params;
1247 assert_eq!(p.e820_entries, 7);
1248 let last = &p.e820_map[6];
1249 assert_eq!(u64::from(last.addr), 4 * GB);
1250 assert_eq!(u32::from(last.typ), defs::E820_RAM);
1251 let below_gap = &p.e820_map[5];
1255 assert_eq!(
1256 u64::from(below_gap.addr) + u64::from(below_gap.size),
1257 4 * GB - 128 * MB
1258 );
1259 }
1260
1261 #[test]
1262 fn zero_page_tables_too_large() {
1263 let result = build_zero_page(
1265 &make_layout(256 * MB),
1266 (RSDP_BASE - ACPI_TABLES_BASE) as usize + 0x1000,
1267 0,
1268 0,
1269 &CString::new("").unwrap(),
1270 0,
1271 0,
1272 None,
1273 );
1274 match result {
1275 Err(Error::LowTablesTooLarge(..)) => {}
1276 other => panic!("expected LowTablesTooLarge, got {:?}", other.err()),
1277 }
1278 }
1279
1280 #[derive(Default)]
1284 struct RecordingImporter {
1285 pages: Vec<(String, u64, u64)>,
1287 imports: Vec<ImportRecord>,
1288 }
1289
1290 #[derive(Debug)]
1291 struct ImportRecord {
1292 page_base: u64,
1293 page_count: u64,
1294 tag: String,
1295 acceptance: BootPageAcceptance,
1296 data: Vec<u8>,
1297 }
1298
1299 impl RecordingImporter {
1300 fn page_base(&self, tag: &str) -> Option<u64> {
1301 self.pages
1302 .iter()
1303 .find(|(t, ..)| t == tag)
1304 .map(|(_, base, _)| *base)
1305 }
1306 }
1307
1308 impl ImageLoad<X86Register> for RecordingImporter {
1309 fn isolation_config(&self) -> IsolationConfig {
1310 IsolationConfig {
1311 paravisor_present: false,
1312 isolation_type: crate::importer::IsolationType::None,
1313 shared_gpa_boundary_bits: None,
1314 }
1315 }
1316
1317 fn create_parameter_area(
1318 &mut self,
1319 _page_base: u64,
1320 _page_count: u32,
1321 _debug_tag: &str,
1322 ) -> anyhow::Result<ParameterAreaIndex> {
1323 unimplemented!()
1324 }
1325
1326 fn create_parameter_area_with_data(
1327 &mut self,
1328 _page_base: u64,
1329 _page_count: u32,
1330 _debug_tag: &str,
1331 _initial_data: &[u8],
1332 ) -> anyhow::Result<ParameterAreaIndex> {
1333 unimplemented!()
1334 }
1335
1336 fn import_parameter(
1337 &mut self,
1338 _parameter_area: ParameterAreaIndex,
1339 _byte_offset: u32,
1340 _parameter_type: IgvmParameterType,
1341 ) -> anyhow::Result<()> {
1342 unimplemented!()
1343 }
1344
1345 fn import_pages(
1346 &mut self,
1347 page_base: u64,
1348 page_count: u64,
1349 debug_tag: &'static str,
1350 acceptance: BootPageAcceptance,
1351 data: &[u8],
1352 ) -> anyhow::Result<()> {
1353 self.pages
1354 .push((debug_tag.to_string(), page_base, page_count));
1355 self.imports.push(ImportRecord {
1356 page_base,
1357 page_count,
1358 tag: debug_tag.to_string(),
1359 acceptance,
1360 data: data.to_vec(),
1361 });
1362 Ok(())
1363 }
1364
1365 fn import_vp_register(&mut self, _register: X86Register) -> anyhow::Result<()> {
1366 Ok(())
1367 }
1368
1369 fn verify_startup_memory_available(
1370 &mut self,
1371 _page_base: u64,
1372 _page_count: u64,
1373 _memory_type: StartupMemoryType,
1374 ) -> anyhow::Result<()> {
1375 Ok(())
1376 }
1377
1378 fn set_vp_context_page(&mut self, _page_base: u64) -> anyhow::Result<()> {
1379 unimplemented!()
1380 }
1381
1382 fn relocation_region(
1383 &mut self,
1384 _gpa: u64,
1385 _size_bytes: u64,
1386 _relocation_alignment: u64,
1387 _minimum_relocation_gpa: u64,
1388 _maximum_relocation_gpa: u64,
1389 _apply_rip_offset: bool,
1390 _apply_gdtr_offset: bool,
1391 _vp_index: u16,
1392 ) -> anyhow::Result<()> {
1393 unimplemented!()
1394 }
1395
1396 fn page_table_relocation(
1397 &mut self,
1398 _page_table_gpa: u64,
1399 _size_pages: u64,
1400 _used_pages: u64,
1401 _vp_index: u16,
1402 ) -> anyhow::Result<()> {
1403 unimplemented!()
1404 }
1405
1406 fn set_imported_regions_config_page(&mut self, _page_base: u64) {
1407 unimplemented!()
1408 }
1409 }
1410
1411 fn test_load_info() -> LoadInfo {
1412 LoadInfo {
1413 kernel: KernelInfo {
1414 gpa: KERNEL_BASE,
1415 size: 0x1000,
1416 entrypoint: KERNEL_BASE,
1417 },
1418 initrd: None,
1419 dtb: None,
1420 bzimage_setup_header: None,
1421 }
1422 }
1423
1424 #[test]
1425 fn import_config_places_tables_at_fixed_addresses() {
1426 let acpi = AcpiTables {
1427 rsdp: vec![0u8; 0x1000],
1428 tables: vec![0u8; 0x1800],
1429 };
1430 let smbios = crate::smbios::BuiltSmbios {
1431 entry_point: vec![0u8; crate::smbios::ENTRY_POINT_SIZE],
1432 structure_table: vec![0u8; 0x100],
1433 };
1434 let mut importer = RecordingImporter::default();
1435 import_config(
1436 &mut importer,
1437 &test_load_info(),
1438 &CString::new("console=ttyS0").unwrap(),
1439 &make_layout(256 * MB),
1440 &acpi,
1441 Some(&smbios),
1442 None,
1443 )
1444 .unwrap();
1445
1446 assert_eq!(
1449 importer.page_base("linux-rsdp"),
1450 Some(RSDP_BASE / HV_PAGE_SIZE)
1451 );
1452 assert_eq!(
1453 importer.page_base("linux-acpi-tables"),
1454 Some(ACPI_TABLES_BASE / HV_PAGE_SIZE)
1455 );
1456 assert_eq!(
1459 importer.page_base("linux-smbios-anchor"),
1460 Some(SMBIOS_FSEGMENT_BASE / HV_PAGE_SIZE)
1461 );
1462 assert_eq!(
1463 importer.page_base("linux-smbios-tables"),
1464 Some(smbios_struct_table_base(acpi.tables.len()) / HV_PAGE_SIZE)
1465 );
1466 assert_eq!(
1468 importer.page_base("linux-zeropage"),
1469 Some(ZERO_PAGE_BASE / HV_PAGE_SIZE)
1470 );
1471 assert_eq!(
1472 importer.page_base("linux-commandline"),
1473 Some(CMDLINE_BASE / HV_PAGE_SIZE)
1474 );
1475 assert_eq!(
1476 importer.page_base("linux-pagetables"),
1477 Some(CR3_BASE / HV_PAGE_SIZE)
1478 );
1479 }
1480
1481 #[test]
1482 fn import_config_rejects_oversized_command_line() {
1483 let acpi = AcpiTables {
1484 rsdp: vec![0u8; 0x1000],
1485 tables: vec![0u8; 0x1000],
1486 };
1487 let cmdline = CString::new(vec![b'a'; (CR3_BASE - CMDLINE_BASE) as usize]).unwrap();
1489 let mut importer = RecordingImporter::default();
1490 let err = import_config(
1491 &mut importer,
1492 &test_load_info(),
1493 &cmdline,
1494 &make_layout(256 * MB),
1495 &acpi,
1496 None,
1497 None,
1498 )
1499 .unwrap_err();
1500 assert!(matches!(err, Error::CommandLineTooLong(..)), "got {err:?}");
1501 assert!(importer.pages.is_empty(), "importer used before the check");
1502 }
1503
1504 #[test]
1505 fn import_config_rejects_empty_acpi_tables() {
1506 let acpi = AcpiTables {
1509 rsdp: vec![0u8; 0x1000],
1510 tables: Vec::new(),
1511 };
1512 let mut importer = RecordingImporter::default();
1513 let err = import_config(
1514 &mut importer,
1515 &test_load_info(),
1516 &CString::new("").unwrap(),
1517 &make_layout(256 * MB),
1518 &acpi,
1519 None,
1520 None,
1521 )
1522 .unwrap_err();
1523 assert!(matches!(err, Error::EmptyAcpiTables), "got {err:?}");
1524 }
1525
1526 #[test]
1527 fn imports_snp_boot_pages_with_linux_cc_blob() {
1528 let acpi_len = 0x1800;
1529 let smbios_len = 0x100;
1530 let ZeroPageBuildResult {
1531 boot_params,
1532 additional_pages,
1533 } = build_zero_page(
1534 &make_layout(256 * MB),
1535 acpi_len,
1536 smbios_len,
1537 SNP_BOOT_PAGE_COUNT,
1538 &CString::new("").unwrap(),
1539 0,
1540 0,
1541 None,
1542 )
1543 .unwrap();
1544 let allocated_range = additional_pages.unwrap();
1545 let mut importer = RecordingImporter::default();
1546
1547 let cc_setup_data_address = import_snp_boot_pages(&mut importer, allocated_range).unwrap();
1548
1549 assert_eq!(importer.imports.len(), 4);
1550 assert_eq!(
1551 allocated_range.start(),
1552 ACPI_TABLES_BASE
1553 + align_up_to_page_size(acpi_len as u64)
1554 + align_up_to_page_size(smbios_len as u64)
1555 );
1556 assert_eq!(
1557 importer.imports[0].page_base,
1558 allocated_range.start() / HV_PAGE_SIZE
1559 );
1560 assert_eq!(importer.imports[0].page_count, 1);
1561 assert_eq!(importer.imports[0].tag, "linux-snp-secrets");
1562 assert_eq!(
1563 importer.imports[0].acceptance,
1564 BootPageAcceptance::SecretsPage
1565 );
1566 assert_eq!(
1567 importer.imports[1].page_base,
1568 allocated_range.start() / HV_PAGE_SIZE + 1
1569 );
1570 assert_eq!(
1571 importer.imports[1].acceptance,
1572 BootPageAcceptance::CpuidPage
1573 );
1574
1575 let cc_blob = defs::cc_blob_sev_info::read_from_bytes(&importer.imports[2].data).unwrap();
1576 assert_eq!(cc_blob.magic, defs::CC_BLOB_SEV_INFO_MAGIC);
1577 assert_eq!(cc_blob.version, 0);
1578 assert_eq!(cc_blob.secrets_phys, allocated_range.start());
1579 assert_eq!(cc_blob.secrets_len, HV_PAGE_SIZE as u32);
1580 assert_eq!(cc_blob.cpuid_phys, allocated_range.start() + HV_PAGE_SIZE);
1581 assert_eq!(cc_blob.cpuid_len, HV_PAGE_SIZE as u32);
1582
1583 let cc_setup_data =
1584 defs::cc_setup_data::read_from_bytes(&importer.imports[3].data).unwrap();
1585 assert_eq!(cc_setup_data.header.next, 0);
1586 assert_eq!(cc_setup_data.header.ty, defs::SETUP_CC_BLOB);
1587 assert_eq!(
1588 cc_setup_data.header.len,
1589 (size_of::<defs::cc_setup_data>() - size_of::<defs::setup_data>()) as u32
1590 );
1591 assert_eq!(
1592 cc_setup_data.cc_blob_address,
1593 u32::try_from(allocated_range.start() + 2 * HV_PAGE_SIZE).unwrap()
1594 );
1595 assert_eq!(allocated_range.end(), cc_setup_data_address + HV_PAGE_SIZE,);
1596 assert!(allocated_range.end() <= RSDP_BASE);
1597
1598 let smbios_reserved = &boot_params.e820_map[2];
1599 let acpi_end = ACPI_TABLES_BASE + align_up_to_page_size(acpi_len as u64);
1600 assert_eq!(u64::from(smbios_reserved.addr), acpi_end);
1601 assert_eq!(
1602 u64::from(smbios_reserved.size),
1603 align_up_to_page_size(smbios_len as u64)
1604 );
1605 let additional_reserved = &boot_params.e820_map[3];
1606 assert_eq!(u64::from(additional_reserved.addr), allocated_range.start());
1607 assert_eq!(u64::from(additional_reserved.size), allocated_range.len());
1608
1609 assert!(matches!(
1610 build_zero_page(
1611 &make_layout(256 * MB),
1612 (RSDP_BASE - ACPI_TABLES_BASE) as usize,
1613 0,
1614 SNP_BOOT_PAGE_COUNT,
1615 &CString::new("").unwrap(),
1616 0,
1617 0,
1618 None,
1619 ),
1620 Err(Error::LowTablesTooLarge(..))
1621 ));
1622 }
1623}