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