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