1#![allow(non_camel_case_types)]
6
7pub mod config;
8
9#[cfg(guest_arch = "aarch64")]
10use aarch64 as arch;
11#[cfg(guest_arch = "x86_64")]
12use x86_64 as arch;
13
14pub use arch::CONFIG_BLOB_GPA_BASE;
15pub use arch::IMAGE_SIZE;
16pub use arch::load;
17
18use guid::Guid;
19use thiserror::Error;
20use zerocopy::FromBytes;
21use zerocopy::Immutable;
22use zerocopy::IntoBytes;
23use zerocopy::KnownLayout;
24
25const SEC_FIRMWARE_VOLUME_OFFSET: u64 = 0x005E0000;
30
31fn expand_3byte_integer(size: [u8; 3]) -> u64 {
33 ((size[2] as u64) << 16) + ((size[1] as u64) << 8) + size[0] as u64
34}
35
36const fn signature_16(v: &[u8; 2]) -> u16 {
37 v[0] as u16 | (v[1] as u16) << 8
38}
39
40const fn signature_32(v: &[u8; 4]) -> u32 {
41 v[0] as u32 | (v[1] as u32) << 8 | (v[2] as u32) << 16 | (v[3] as u32) << 24
42}
43
44const IMAGE_DOS_SIGNATURE: u16 = 0x5A4D; const IMAGE_NT_SIGNATURE: u32 = 0x00004550; const TE_IMAGE_HEADER_SIGNATURE: u16 = signature_16(b"VZ");
47const EFI_FVH_SIGNATURE: u32 = signature_32(b"_FVH");
48
49#[repr(C)]
50#[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
51struct ImageDosHeader {
52 e_magic: u16, e_cblp: u16, e_cp: u16, e_crlc: u16, e_cparhdr: u16, e_minalloc: u16, e_maxalloc: u16, e_ss: u16, e_sp: u16, e_csum: u16, e_ip: u16, e_cs: u16, e_lfarlc: u16, e_ovno: u16, e_res: [u16; 4], e_oemid: u16, e_oeminfo: u16, e_res2: [u16; 10], e_lfanew: i32, }
72
73#[repr(C)]
74#[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
75struct TeImageHeader {
76 signature: u16,
77 machine: u16,
78 number_of_sections: u8,
79 subsystem: u8,
80 stripped_size: u16,
81 address_of_entry_point: u32,
82 base_of_code: u32,
83 image_base: u64,
84 data_directory: [ImageDataDirectory; 2],
85}
86
87#[repr(C)]
88#[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
89struct ImageNtHeaders32 {
90 signature: u32,
91 file_header: ImageFileHeader,
92 optional_header: ImageOptionalHeader32,
93}
94
95#[repr(C)]
96#[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
97struct ImageFileHeader {
98 machine: u16,
99 number_of_sections: u16,
100 time_date_stamp: u32,
101 pointer_to_symbol_table: u32,
102 number_of_symbols: u32,
103 size_of_optional_header: u16,
104 characteristics: u16,
105}
106
107#[repr(C)]
108#[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
109struct ImageOptionalHeader32 {
110 magic: u16,
111 major_linker_version: u8,
112 minor_linker_version: u8,
113 size_of_code: u32,
114 size_of_initialized_data: u32,
115 size_of_uninitialized_data: u32,
116 address_of_entry_point: u32,
117 base_of_code: u32,
118 base_of_data: u32,
119 image_base: u32,
120 section_alignment: u32,
121 file_alignment: u32,
122 major_operating_system_version: u16,
123 minor_operating_system_version: u16,
124 major_image_version: u16,
125 minor_image_version: u16,
126 major_subsystem_version: u16,
127 minor_subsystem_version: u16,
128 win32_version_value: u32,
129 size_of_image: u32,
130 size_of_headers: u32,
131 check_sum: u32,
132 subsystem: u16,
133 dll_characteristics: u16,
134 size_of_stack_reserve: u32,
135 size_of_stack_commit: u32,
136 size_of_heap_reserve: u32,
137 size_of_heap_commit: u32,
138 loader_flags: u32,
139 number_of_rva_and_sizes: u32,
140 data_directory: [ImageDataDirectory; 16],
141}
142
143#[repr(C)]
144#[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
145struct ImageDataDirectory {
146 virtual_address: u32,
147 size: u32,
148}
149
150fn pe_get_entry_point_offset(pe32_data: &[u8]) -> Option<u32> {
151 let dos_header = ImageDosHeader::read_from_prefix(pe32_data).ok()?.0; let nt_headers_offset = if dos_header.e_magic == IMAGE_DOS_SIGNATURE {
153 dos_header.e_lfanew as usize
155 } else {
156 0
158 };
159
160 let signature = u32::read_from_prefix(&pe32_data[nt_headers_offset..])
161 .ok()?
162 .0; if signature as u16 == TE_IMAGE_HEADER_SIGNATURE {
167 let te = TeImageHeader::read_from_prefix(&pe32_data[nt_headers_offset..])
168 .ok()?
169 .0; Some(te.address_of_entry_point + size_of_val(&te) as u32 - te.stripped_size as u32)
171 } else if signature == IMAGE_NT_SIGNATURE {
172 let pe = ImageNtHeaders32::read_from_prefix(&pe32_data[nt_headers_offset..])
173 .ok()?
174 .0; Some(pe.optional_header.address_of_entry_point)
176 } else {
177 None
178 }
179}
180
181#[repr(C)]
182#[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
183struct EFI_FIRMWARE_VOLUME_HEADER {
184 zero_vector: [u8; 16],
185 file_system_guid: Guid,
186 fv_length: u64,
187 signature: u32,
188 attributes: u32,
189 header_length: u16,
190 checksum: u16,
191 ext_header_offset: u16,
192 reserved: u8,
193 revision: u8,
194}
195
196#[repr(C)]
197#[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
198struct EFI_FFS_FILE_HEADER {
199 name: Guid,
200 integrity_check: u16,
201 typ: u8,
202 attributes: u8,
203 size: [u8; 3],
204 state: u8,
205}
206
207const EFI_FV_FILETYPE_SECURITY_CORE: u8 = 3;
208
209#[repr(C)]
210#[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
211struct EFI_COMMON_SECTION_HEADER {
212 size: [u8; 3],
213 typ: u8,
214}
215
216const EFI_SECTION_PE32: u8 = 0x10;
217
218fn get_sec_entry_point_offset(image: &[u8]) -> Option<u64> {
220 let mut image_offset = SEC_FIRMWARE_VOLUME_OFFSET;
222
223 let fvh = EFI_FIRMWARE_VOLUME_HEADER::read_from_prefix(&image[image_offset as usize..])
225 .ok()?
226 .0; if fvh.signature != EFI_FVH_SIGNATURE {
228 return None;
229 }
230
231 image_offset += fvh.header_length as u64;
233
234 let mut sec_core_file_header = None;
236 let mut volume_offset = 0;
237 while volume_offset < fvh.fv_length {
238 let new_volume_offset = (volume_offset + 7) & !7;
239 if new_volume_offset > volume_offset {
240 image_offset += new_volume_offset - volume_offset;
241 volume_offset = new_volume_offset;
242 }
243 let fh = EFI_FFS_FILE_HEADER::read_from_prefix(&image[image_offset as usize..])
244 .ok()?
245 .0; if fh.typ == EFI_FV_FILETYPE_SECURITY_CORE {
247 sec_core_file_header = Some(fh);
248 break;
249 }
250
251 image_offset += expand_3byte_integer(fh.size);
252 volume_offset += expand_3byte_integer(fh.size);
253 }
254
255 let sec_core_file_header = sec_core_file_header?;
257 let sec_core_file_size = expand_3byte_integer(sec_core_file_header.size);
258
259 image_offset += size_of::<EFI_FFS_FILE_HEADER>() as u64;
261 volume_offset += size_of::<EFI_FFS_FILE_HEADER>() as u64;
262
263 let mut file_offset = volume_offset;
265 while file_offset < sec_core_file_size {
266 let new_file_offset = (file_offset + 3) & !3;
270 if new_file_offset > file_offset {
271 image_offset += new_file_offset - file_offset;
272 volume_offset += new_file_offset - file_offset;
273 file_offset += new_file_offset - file_offset;
274 }
275
276 let sh = EFI_COMMON_SECTION_HEADER::read_from_prefix(&image[image_offset as usize..])
277 .ok()?
278 .0; if sh.typ == EFI_SECTION_PE32 {
280 let pe_offset = pe_get_entry_point_offset(
281 &image[image_offset as usize + size_of::<EFI_COMMON_SECTION_HEADER>()..],
282 )?;
283 image_offset += size_of::<EFI_COMMON_SECTION_HEADER>() as u64 + pe_offset as u64;
284 break;
285 }
286 image_offset += expand_3byte_integer(sh.size);
287 volume_offset += expand_3byte_integer(sh.size);
288 file_offset += expand_3byte_integer(sh.size);
289 }
290
291 Some(image_offset)
292}
293
294mod igvm {
296 use zerocopy::FromBytes;
297
298 use zerocopy::Immutable;
299 use zerocopy::IntoBytes;
300 use zerocopy::KnownLayout;
301
302 #[repr(C)]
304 #[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
305 pub struct UEFI_IGVM_PARAMETER_INFO {
306 pub parameter_page_count: u32,
307 pub cpuid_pages_offset: u32,
308 pub vp_context_page_number: u64,
309 pub loader_block_offset: u32,
310 pub command_line_offset: u32,
311 pub command_line_page_count: u32,
312 pub memory_map_offset: u32,
313 pub memory_map_page_count: u32,
314 pub madt_offset: u32,
315 pub madt_page_count: u32,
316 pub srat_offset: u32,
317 pub srat_page_count: u32,
318 pub maximum_processor_count: u32,
319 pub uefi_memory_map_offset: u32,
320 pub uefi_memory_map_page_count: u32,
321 }
322
323 pub const UEFI_IGVM_LOADER_BLOCK_NUMBER_OF_PROCESSORS_FIELD_OFFSET: usize = 0;
324}
325
326#[derive(Debug, Error)]
327pub enum Error {
328 #[error("Firmware size invalid")]
329 InvalidImageSize,
330 #[error("Unable to find SEC volume entry point")]
331 NoSecEntryPoint,
332 #[error("Invalid shared gpa boundary")]
333 InvalidSharedGpaBoundary,
334 #[error("Invalid config type")]
335 InvalidConfigType(String),
336 #[error("Importer error")]
337 Importer(#[source] anyhow::Error),
338}
339
340#[derive(Debug)]
341pub enum ConfigType {
342 ConfigBlob(config::Blob),
343 Igvm,
344 None,
345}
346
347#[derive(Debug)]
348pub struct LoadInfo {
349 pub firmware_base: u64,
351 pub firmware_size: u64,
353 pub total_size: u64,
356}
357
358pub mod x86_64 {
359 use super::ConfigType;
360 use super::Error;
361 use super::LoadInfo;
362 use crate::common::DEFAULT_GDT_SIZE;
363 use crate::common::import_default_gdt;
364 use crate::cpuid::HV_PSP_CPUID_PAGE;
365 use crate::importer::BootPageAcceptance;
366 use crate::importer::IgvmParameterType;
367 use crate::importer::ImageLoad;
368 use crate::importer::IsolationType;
369 use crate::importer::StartupMemoryType;
370 use crate::importer::X86Register;
371 use crate::uefi::SEC_FIRMWARE_VOLUME_OFFSET;
372 use crate::uefi::get_sec_entry_point_offset;
373 use hvdef::HV_PAGE_SIZE;
374 use page_table::IdentityMapSize;
375 use page_table::x64::align_up_to_page_size;
376 use page_table::x64::build_page_tables_64;
377 use zerocopy::FromZeros;
378 use zerocopy::IntoBytes;
379
380 pub const IMAGE_SIZE: u64 = 0x00600000; const IMAGE_GPA_BASE: u64 = 0x100000; const PAGE_TABLE_GPA_BASE: u64 = IMAGE_GPA_BASE + IMAGE_SIZE; const PAGE_TABLE_SIZE: u64 = HV_PAGE_SIZE * 6;
384 const GDT_GPA_BASE: u64 = PAGE_TABLE_GPA_BASE + PAGE_TABLE_SIZE; const MISC_PAGES_GPA_BASE: u64 = GDT_GPA_BASE + DEFAULT_GDT_SIZE; const MISC_PAGES_SIZE: u64 = HV_PAGE_SIZE * 2;
387 pub const CONFIG_BLOB_GPA_BASE: u64 = MISC_PAGES_GPA_BASE + MISC_PAGES_SIZE; pub fn load(
391 importer: &mut dyn ImageLoad<X86Register>,
392 image: &[u8],
393 config: ConfigType,
394 ) -> Result<LoadInfo, Error> {
395 if image.len() != IMAGE_SIZE as usize {
396 return Err(Error::InvalidImageSize);
397 }
398
399 let sec_entry_point = get_sec_entry_point_offset(image).ok_or(Error::NoSecEntryPoint)?;
400
401 let isolation = importer.isolation_config();
402
403 let (page_tables, shared_vis_page_tables) =
409 if isolation.isolation_type == IsolationType::Snp && !isolation.paravisor_present {
410 if let ConfigType::ConfigBlob(_) = config {
411 return Err(Error::InvalidConfigType(
412 "Enlightened UEFI must use IGVM parameters".into(),
413 ));
414 }
415
416 let shared_vis_page_table_gpa = CONFIG_BLOB_GPA_BASE + HV_PAGE_SIZE;
417 let shared_gpa_boundary_bits = isolation
418 .shared_gpa_boundary_bits
419 .ok_or(Error::InvalidSharedGpaBoundary)?;
420 let shared_gpa_boundary = 1 << shared_gpa_boundary_bits;
421
422 let shared_vis_page_tables = build_page_tables_64(
426 shared_vis_page_table_gpa,
427 shared_gpa_boundary,
428 IdentityMapSize::Size4Gb,
429 None,
430 );
431
432 let page_tables = build_page_tables_64(
433 PAGE_TABLE_GPA_BASE,
434 0,
435 IdentityMapSize::Size4Gb,
436 Some((shared_vis_page_table_gpa, shared_gpa_boundary)),
437 );
438
439 (page_tables, Some(shared_vis_page_tables))
440 } else {
441 let page_tables =
442 build_page_tables_64(PAGE_TABLE_GPA_BASE, 0, IdentityMapSize::Size4Gb, None);
443
444 (page_tables, None)
445 };
446
447 assert_eq!(page_tables.len(), PAGE_TABLE_SIZE as usize);
449
450 let image_page_count = image.len() as u64 / HV_PAGE_SIZE;
452 importer
453 .import_pages(
454 IMAGE_GPA_BASE / HV_PAGE_SIZE,
455 image_page_count,
456 "uefi-image",
457 BootPageAcceptance::Exclusive,
458 image,
459 )
460 .map_err(Error::Importer)?;
461
462 let mut total_page_count = IMAGE_GPA_BASE / HV_PAGE_SIZE + image_page_count;
463
464 importer
465 .import_pages(
466 PAGE_TABLE_GPA_BASE / HV_PAGE_SIZE,
467 PAGE_TABLE_SIZE / HV_PAGE_SIZE,
468 "uefi-page-tables",
469 BootPageAcceptance::Exclusive,
470 &page_tables,
471 )
472 .map_err(Error::Importer)?;
473
474 total_page_count += PAGE_TABLE_SIZE / HV_PAGE_SIZE;
475
476 assert_eq!(DEFAULT_GDT_SIZE, HV_PAGE_SIZE);
478 import_default_gdt(importer, GDT_GPA_BASE / HV_PAGE_SIZE).map_err(Error::Importer)?;
479 total_page_count += DEFAULT_GDT_SIZE / HV_PAGE_SIZE;
480
481 importer
483 .import_pages(
484 MISC_PAGES_GPA_BASE / HV_PAGE_SIZE,
485 MISC_PAGES_SIZE / HV_PAGE_SIZE,
486 "uefi-misc-pages",
487 BootPageAcceptance::Exclusive,
488 &[],
489 )
490 .map_err(Error::Importer)?;
491
492 total_page_count += MISC_PAGES_SIZE / HV_PAGE_SIZE;
493
494 match config {
497 ConfigType::Igvm => {
498 total_page_count += set_igvm_parameters(
499 importer,
500 CONFIG_BLOB_GPA_BASE / HV_PAGE_SIZE,
501 match isolation.isolation_type {
502 IsolationType::Snp => {
503 let table = shared_vis_page_tables
504 .as_ref()
505 .expect("should be shared vis page tables");
506 table
507 }
508 _ => &[],
509 },
510 )?
511 }
512 ConfigType::ConfigBlob(config) => {
513 let data = config.complete();
514 assert!(!data.is_empty());
515 let config_blob_page_count = (data.len() as u64).div_ceil(HV_PAGE_SIZE);
516 importer
517 .import_pages(
518 CONFIG_BLOB_GPA_BASE / HV_PAGE_SIZE,
519 config_blob_page_count,
520 "uefi-config-blob",
521 BootPageAcceptance::Exclusive,
522 &data,
523 )
524 .map_err(Error::Importer)?;
525
526 total_page_count += config_blob_page_count;
527 }
528 ConfigType::None => {}
529 }
530
531 importer
534 .verify_startup_memory_available(0, total_page_count, StartupMemoryType::Ram)
535 .map_err(Error::Importer)?;
536
537 let mut import_reg = |register| {
538 importer
539 .import_vp_register(register)
540 .map_err(Error::Importer)
541 };
542
543 import_reg(X86Register::Cr0(
545 x86defs::X64_CR0_PG | x86defs::X64_CR0_NE | x86defs::X64_CR0_MP | x86defs::X64_CR0_PE,
546 ))?;
547
548 import_reg(X86Register::Cr3(PAGE_TABLE_GPA_BASE))?;
550
551 import_reg(X86Register::Cr4(
553 x86defs::X64_CR4_PAE
554 | x86defs::X64_CR4_MCE
555 | x86defs::X64_CR4_FXSR
556 | x86defs::X64_CR4_XMMEXCPT,
557 ))?;
558
559 import_reg(X86Register::Efer(
561 x86defs::X64_EFER_LMA | x86defs::X64_EFER_LME | x86defs::X64_EFER_NXE,
562 ))?;
563
564 import_reg(X86Register::Pat(x86defs::X86X_MSR_DEFAULT_PAT))?;
566
567 import_reg(X86Register::Rbp(
570 IMAGE_GPA_BASE + SEC_FIRMWARE_VOLUME_OFFSET,
571 ))?;
572
573 import_reg(X86Register::Rip(IMAGE_GPA_BASE + sec_entry_point))?;
575
576 let isolation_cpuid = isolation.get_cpuid();
578
579 import_reg(X86Register::R8(isolation_cpuid.eax as u64))?;
580 import_reg(X86Register::R9(isolation_cpuid.ebx as u64))?;
581 import_reg(X86Register::R10(isolation_cpuid.ecx as u64))?;
582 import_reg(X86Register::R11(isolation_cpuid.edx as u64))?;
583
584 import_reg(X86Register::MtrrDefType(0xc00))?;
586 import_reg(X86Register::MtrrFix64k00000(0x0606060606060606))?;
587 import_reg(X86Register::MtrrFix16k80000(0x0606060606060606))?;
588
589 Ok(LoadInfo {
590 firmware_base: IMAGE_GPA_BASE,
591 firmware_size: image.len() as u64,
592 total_size: total_page_count * HV_PAGE_SIZE,
593 })
594 }
595
596 struct PageAllocator {
598 base: u32,
599 total_count: u32,
600 }
601
602 impl PageAllocator {
603 fn new(base: u32) -> PageAllocator {
605 PageAllocator {
606 base,
607 total_count: 0,
608 }
609 }
610
611 fn allocate(&mut self, count: u32) -> u32 {
613 let allocation = self.base + self.total_count;
614 self.total_count += count;
615
616 allocation
617 }
618
619 fn total(&self) -> u32 {
621 self.total_count
622 }
623 }
624
625 fn set_igvm_parameters(
628 importer: &mut dyn ImageLoad<X86Register>,
629 config_area_base_page: u64,
630 shared_visibility_page_tables: &[u8],
631 ) -> Result<u64, Error> {
632 let mut parameter_info = super::igvm::UEFI_IGVM_PARAMETER_INFO::new_zeroed();
633
634 let mut allocator = PageAllocator::new(0);
637 allocator.allocate(1);
638
639 let table_page_count = 20;
643
644 let page_table_page_count =
647 align_up_to_page_size(shared_visibility_page_tables.len() as u64) / HV_PAGE_SIZE;
648 let page_table_offset = allocator.allocate(page_table_page_count as u32);
649 parameter_info.loader_block_offset = allocator.allocate(1);
650
651 let command_line_page_count = 1;
652 parameter_info.command_line_offset = allocator.allocate(command_line_page_count);
653 parameter_info.command_line_page_count = command_line_page_count;
654
655 parameter_info.memory_map_offset = allocator.allocate(table_page_count);
656 parameter_info.memory_map_page_count = table_page_count;
657
658 parameter_info.madt_offset = allocator.allocate(table_page_count);
659 parameter_info.madt_page_count = table_page_count;
660
661 parameter_info.srat_offset = allocator.allocate(table_page_count);
662 parameter_info.srat_page_count = table_page_count;
663
664 parameter_info.uefi_memory_map_offset = allocator.allocate(table_page_count);
667 parameter_info.uefi_memory_map_page_count = table_page_count;
668
669 let isolation = importer.isolation_config();
671 if isolation.isolation_type == IsolationType::Snp {
672 if isolation.paravisor_present {
674 return Err(Error::InvalidConfigType(
675 "IGVM ConfigType specified but paravisor is present.".into(),
676 ));
677 }
678
679 importer
682 .import_vp_register(X86Register::R12(config_area_base_page * HV_PAGE_SIZE))
683 .map_err(Error::Importer)?;
684
685 parameter_info.cpuid_pages_offset = allocator.allocate(2);
690
691 let cpuid_page = create_snp_cpuid_page();
692
693 importer
694 .import_pages(
695 config_area_base_page + parameter_info.cpuid_pages_offset as u64,
696 1,
697 "uefi-cpuid-page",
698 BootPageAcceptance::CpuidPage,
699 cpuid_page.as_bytes(),
700 )
701 .map_err(Error::Importer)?;
702
703 importer
704 .import_pages(
705 config_area_base_page + parameter_info.cpuid_pages_offset as u64 + 1,
706 1,
707 "uefi-cpuid-extended-page",
708 BootPageAcceptance::CpuidExtendedStatePage,
709 &[],
710 )
711 .map_err(Error::Importer)?;
712
713 let vp_context_page_number = config_area_base_page + allocator.total() as u64;
722 importer
723 .set_vp_context_page(vp_context_page_number)
724 .map_err(Error::Importer)?;
725
726 parameter_info.vp_context_page_number = vp_context_page_number;
727 } else {
728 parameter_info.vp_context_page_number = 0xfffff;
733 }
734
735 parameter_info.parameter_page_count = allocator.total();
737
738 importer
739 .import_pages(
740 config_area_base_page,
741 1,
742 "uefi-config-base-page",
743 BootPageAcceptance::Exclusive,
744 parameter_info.as_bytes(),
745 )
746 .map_err(Error::Importer)?;
747
748 importer
749 .import_pages(
750 config_area_base_page + parameter_info.uefi_memory_map_offset as u64,
751 parameter_info.uefi_memory_map_page_count as u64,
752 "uefi-memory-map-scratch",
753 BootPageAcceptance::ExclusiveUnmeasured,
754 &[],
755 )
756 .map_err(Error::Importer)?;
757
758 let loader_block = importer
759 .create_parameter_area(
760 config_area_base_page + parameter_info.loader_block_offset as u64,
761 1,
762 "uefi-loader-block",
763 )
764 .map_err(Error::Importer)?;
765 importer
766 .import_parameter(
767 loader_block,
768 super::igvm::UEFI_IGVM_LOADER_BLOCK_NUMBER_OF_PROCESSORS_FIELD_OFFSET as u32,
769 IgvmParameterType::VpCount,
770 )
771 .map_err(Error::Importer)?;
772
773 let command_line = importer
774 .create_parameter_area(
775 config_area_base_page + parameter_info.command_line_offset as u64,
776 parameter_info.command_line_page_count,
777 "uefi-command-line",
778 )
779 .map_err(Error::Importer)?;
780 importer
781 .import_parameter(command_line, 0, IgvmParameterType::CommandLine)
782 .map_err(Error::Importer)?;
783
784 let memory_map = importer
785 .create_parameter_area(
786 config_area_base_page + parameter_info.memory_map_offset as u64,
787 parameter_info.memory_map_page_count,
788 "uefi-memory-map",
789 )
790 .map_err(Error::Importer)?;
791 importer
792 .import_parameter(memory_map, 0, IgvmParameterType::MemoryMap)
793 .map_err(Error::Importer)?;
794
795 let madt = importer
796 .create_parameter_area(
797 config_area_base_page + parameter_info.madt_offset as u64,
798 parameter_info.madt_page_count,
799 "uefi-madt",
800 )
801 .map_err(Error::Importer)?;
802 importer
803 .import_parameter(madt, 0, IgvmParameterType::Madt)
804 .map_err(Error::Importer)?;
805
806 let srat = importer
807 .create_parameter_area(
808 config_area_base_page + parameter_info.srat_offset as u64,
809 parameter_info.srat_page_count,
810 "uefi-srat",
811 )
812 .map_err(Error::Importer)?;
813 importer
814 .import_parameter(srat, 0, IgvmParameterType::Srat)
815 .map_err(Error::Importer)?;
816
817 if page_table_page_count != 0 {
818 importer
819 .import_pages(
820 config_area_base_page + page_table_offset as u64,
821 page_table_page_count,
822 "uefi-igvm-page-tables",
823 BootPageAcceptance::Exclusive,
824 shared_visibility_page_tables,
825 )
826 .map_err(Error::Importer)?;
827 }
828
829 Ok(allocator.total() as u64)
830 }
831
832 fn create_snp_cpuid_page() -> HV_PSP_CPUID_PAGE {
834 let mut cpuid_page = HV_PSP_CPUID_PAGE::default();
835
836 for (i, required_leaf) in crate::cpuid::SNP_REQUIRED_CPUID_LEAF_LIST_UEFI
837 .iter()
838 .enumerate()
839 {
840 cpuid_page.cpuid_leaf_info[i].eax_in = required_leaf.eax;
841 cpuid_page.cpuid_leaf_info[i].eax_out = required_leaf.ecx;
842 cpuid_page.count += 1;
843 }
844
845 cpuid_page
846 }
847}
848
849pub mod aarch64 {
850 use super::ConfigType;
851 use super::Error;
852 use super::LoadInfo;
853 use crate::importer::Aarch64Register;
854 use crate::importer::BootPageAcceptance;
855 use crate::importer::ImageLoad;
856 use aarch64defs::Cpsr64;
857 use hvdef::HV_PAGE_SIZE;
858
859 use zerocopy::IntoBytes;
860
861 pub const IMAGE_SIZE: u64 = 0x800000;
862 pub const CONFIG_BLOB_GPA_BASE: u64 = 0x824000;
863
864 pub fn load(
866 importer: &mut dyn ImageLoad<Aarch64Register>,
867 image: &[u8],
868 config: ConfigType,
869 ) -> Result<LoadInfo, Error> {
870 if image.len() != IMAGE_SIZE as usize {
871 return Err(Error::InvalidImageSize);
872 }
873
874 const BYTES_2MB: u64 = 0x200000;
875
876 let image_size = (image.len() as u64 + BYTES_2MB - 1) & !(BYTES_2MB - 1);
877 importer
878 .import_pages(
879 0,
880 image_size / HV_PAGE_SIZE,
881 "uefi-image",
882 BootPageAcceptance::Exclusive,
883 image,
884 )
885 .map_err(Error::Importer)?;
886
887 let stack_offset = image_size;
889 let stack_size = 32 * HV_PAGE_SIZE;
890 let stack_end = stack_offset + stack_size;
891 importer
892 .import_pages(
893 stack_offset / HV_PAGE_SIZE,
894 stack_size / HV_PAGE_SIZE,
895 "uefi-stack",
896 BootPageAcceptance::Exclusive,
897 &[],
898 )
899 .map_err(Error::Importer)?;
900
901 let page_table_offset = stack_end;
903 let page_tables = page_tables(page_table_offset, 1 << 30 );
904 importer
905 .import_pages(
906 page_table_offset / HV_PAGE_SIZE,
907 page_tables.as_bytes().len() as u64 / HV_PAGE_SIZE,
908 "uefi-page-tables",
909 BootPageAcceptance::Exclusive,
910 page_tables.as_bytes(),
911 )
912 .map_err(Error::Importer)?;
913
914 let blob_offset = CONFIG_BLOB_GPA_BASE;
915
916 let blob_size = match config {
918 ConfigType::ConfigBlob(blob) => {
919 let blob = blob.complete();
920 let blob_size = (blob.len() as u64 + HV_PAGE_SIZE - 1) & !(HV_PAGE_SIZE - 1);
921 importer
922 .import_pages(
923 blob_offset / HV_PAGE_SIZE,
924 blob_size / HV_PAGE_SIZE,
925 "uefi-config-blob",
926 BootPageAcceptance::Exclusive,
927 &blob,
928 )
929 .map_err(Error::Importer)?;
930
931 blob_size
932 }
933 ConfigType::None => 0,
934 ConfigType::Igvm => {
935 return Err(Error::InvalidConfigType("igvm not supported".to_owned()));
936 }
937 };
938
939 let total_size = blob_offset + blob_size;
940
941 let mut import_reg = |reg| importer.import_vp_register(reg).map_err(Error::Importer);
942
943 import_reg(Aarch64Register::Cpsr(
944 Cpsr64::new().with_sp(true).with_el(1).into(),
945 ))?;
946 import_reg(Aarch64Register::X0(0x1000))?;
947 import_reg(Aarch64Register::Pc(0x1000))?;
948 import_reg(Aarch64Register::X1(stack_end))?;
949
950 import_reg(Aarch64Register::Ttbr0El1(page_table_offset))?;
951
952 const ARM64_MAIR_CACHE_WBWA: u64 = 0xff;
954 const ARM64_MAIR_CACHE_NC: u64 = 0x00;
955 const ARM64_MAIR_CACHE_WTNA: u64 = 0xaa;
956 const ARM64_MAIR_CACHE_WC: u64 = 0x44;
957
958 import_reg(Aarch64Register::MairEl1(
959 ARM64_MAIR_CACHE_WBWA
960 | (ARM64_MAIR_CACHE_NC << 8)
961 | (ARM64_MAIR_CACHE_WTNA << 16)
962 | (ARM64_MAIR_CACHE_WC << 24)
963 | (ARM64_MAIR_CACHE_WBWA << 32)
964 | (ARM64_MAIR_CACHE_NC << 40)
965 | (ARM64_MAIR_CACHE_WTNA << 48)
966 | (ARM64_MAIR_CACHE_WC << 56),
967 ))?;
968
969 const ARM64_SCTLR_M: u64 = 0x00000001;
971 const ARM64_SCTLR_C: u64 = 0x00000004;
972 const ARM64_SCTLR_RES1_11: u64 = 0x00000800;
973 const ARM64_SCTLR_I: u64 = 0x00001000;
974 const ARM64_SCTLR_RES1_20: u64 = 0x00100000;
975 const ARM64_SCTLR_RES1_22: u64 = 0x00400000;
976 const ARM64_SCTLR_RES1_23: u64 = 0x00800000;
977 const ARM64_SCTLR_RES1_28: u64 = 0x10000000;
978 const ARM64_SCTLR_RES1_29: u64 = 0x20000000;
979
980 import_reg(Aarch64Register::SctlrEl1(
981 ARM64_SCTLR_M
982 | ARM64_SCTLR_C
983 | ARM64_SCTLR_I
984 | ARM64_SCTLR_RES1_11
985 | ARM64_SCTLR_RES1_20
986 | ARM64_SCTLR_RES1_22
987 | ARM64_SCTLR_RES1_23
988 | ARM64_SCTLR_RES1_28
989 | ARM64_SCTLR_RES1_29,
990 ))?;
991
992 const ARM64_TCR_IRGN0_WBWA: u64 = 0x0000000000000100;
995 const ARM64_TCR_ORGN0_WBWA: u64 = 0x0000000000000400;
996 const ARM64_TCR_SH0_INNER_SHARED: u64 = 0x0000000000003000;
997 const ARM64_TCR_TG0_4K: u64 = 0x0000000000000000;
998 const ARM64_TCR_EPD1: u64 = 0x0000000000800000;
999 const ARM64_TCR_T0SZ_SHIFT: u32 = 0;
1000 const ARM64_TCR_T1SZ_SHIFT: u32 = 16;
1001
1002 import_reg(Aarch64Register::TcrEl1(
1003 ARM64_TCR_EPD1
1004 | ARM64_TCR_TG0_4K
1005 | ARM64_TCR_SH0_INNER_SHARED
1006 | ARM64_TCR_ORGN0_WBWA
1007 | ARM64_TCR_IRGN0_WBWA
1008 | (16 << ARM64_TCR_T0SZ_SHIFT)
1009 | (16 << ARM64_TCR_T1SZ_SHIFT),
1010 ))?;
1011
1012 Ok(LoadInfo {
1013 firmware_base: 0,
1014 firmware_size: image.len() as u64,
1015 total_size,
1016 })
1017 }
1018
1019 const PTE_VALID: u64 = 1 << 0;
1020 const PTE_NOT_LARGE: u64 = 1 << 1;
1021 const PTE_MAIR_WB: u64 = 0 << 2;
1022 const PTE_MAIR_UC: u64 = 1 << 2;
1023 const PTE_SHARABILITY_INNER: u64 = 3 << 8;
1024 const PTE_ACCESSED: u64 = 1 << 10;
1025 const PTE_USER_NX: u64 = 1 << 54;
1026
1027 fn large_leaf_entry(normal: bool, address: u64) -> u64 {
1028 address
1029 | PTE_VALID
1030 | PTE_ACCESSED
1031 | PTE_SHARABILITY_INNER
1032 | PTE_USER_NX
1033 | if normal { PTE_MAIR_WB } else { PTE_MAIR_UC }
1034 }
1035
1036 fn non_leaf_entry(address: u64) -> u64 {
1037 address | PTE_VALID | PTE_NOT_LARGE
1038 }
1039
1040 fn leaf_entry(normal: bool, address: u64) -> u64 {
1041 address
1042 | PTE_VALID
1043 | PTE_ACCESSED
1044 | PTE_NOT_LARGE
1045 | PTE_SHARABILITY_INNER
1046 | PTE_USER_NX
1047 | if normal { PTE_MAIR_WB } else { PTE_MAIR_UC }
1048 }
1049
1050 fn table_index(va: u64, level: u32) -> usize {
1051 let index = va >> (9 * (3 - level) + 12);
1052 let index = index & ((1 << 9) - 1);
1053 index as usize
1054 }
1055
1056 fn page_tables(address: u64, end_of_ram: u64) -> Vec<[u64; 512]> {
1057 const PT_SIZE: u64 = 4096;
1058 const VA_4GB: u64 = 1 << 32;
1059 const VA_1GB: u64 = 1 << 30;
1060 const VA_2MB: u64 = 2 << 20;
1061 const VA_4KB: u64 = 4 << 10;
1062
1063 let mut buffer = vec![[0u64; PT_SIZE as usize / 8]; 4];
1064 let [level0, level1, level2, level3] = buffer.as_mut_slice() else {
1065 unreachable!()
1066 };
1067
1068 level0[0] = non_leaf_entry(address + PT_SIZE);
1078
1079 let mut normal = true;
1084 let mut va = 0;
1085 let mut end_va = end_of_ram;
1086 while va < VA_4GB {
1087 if normal && va == end_va {
1091 normal = false;
1092 end_va = VA_4GB;
1093 continue;
1094 }
1095
1096 let level1_index = table_index(va, 1);
1098 if level1[level1_index] & PTE_VALID == 0
1099 && ((va & (VA_1GB - 1)) == 0)
1100 && (end_va - va >= VA_1GB)
1101 {
1102 level1[level1_index] = large_leaf_entry(normal, va);
1103 va += VA_1GB;
1104 continue;
1105 }
1106
1107 if level1[level1_index] & PTE_VALID == 0 {
1112 level1[level1_index] = non_leaf_entry(address + PT_SIZE * 2);
1113 }
1114
1115 let level2_index = table_index(va, 2);
1119 if level2[level2_index] & PTE_VALID == 0
1120 && ((va & (VA_2MB - 1)) == 0)
1121 && (end_va - va >= VA_2MB)
1122 {
1123 level2[level2_index] = large_leaf_entry(normal, va);
1124 va += VA_2MB;
1125 continue;
1126 }
1127
1128 if level2[level2_index] & PTE_VALID == 0 {
1133 level2[level2_index] = non_leaf_entry(address + PT_SIZE * 3);
1134 }
1135
1136 let level3_index = table_index(va, 3);
1137 level3[level3_index] = leaf_entry(normal, va);
1138 va += VA_4KB;
1139 }
1140
1141 buffer
1142 }
1143}