diff --git a/igvmbuilder/src/gpa_map.rs b/igvmbuilder/src/gpa_map.rs index b54be4ab9..ac6fc038e 100644 --- a/igvmbuilder/src/gpa_map.rs +++ b/igvmbuilder/src/gpa_map.rs @@ -66,6 +66,7 @@ pub struct GpaMap { pub guest_context: GpaRange, pub kernel: GpaRange, pub vmsa: GpaRange, + pub init_page_tables: GpaRange, } impl GpaMap { @@ -73,6 +74,7 @@ impl GpaMap { options: &CmdOptions, firmware: &Option>, ) -> Result> { + // 0x010000-0x010FFF: initial page tables for VSM platforms // 0x800000-0x804FFF: zero-filled (must be pre-validated) // 0x805000-0x805FFF: initial stage 2 stack page // 0x806000-0x806FFF: Secrets page @@ -167,6 +169,7 @@ impl GpaMap { guest_context, kernel, vmsa, + init_page_tables: GpaRange::new(0x10000, 2 * PAGE_SIZE_4K)?, }; if options.verbose { println!("GPA Map: {gpa_map:#X?}"); diff --git a/igvmbuilder/src/igvm_builder.rs b/igvmbuilder/src/igvm_builder.rs index 4604bed27..367405078 100644 --- a/igvmbuilder/src/igvm_builder.rs +++ b/igvmbuilder/src/igvm_builder.rs @@ -26,6 +26,7 @@ use zerocopy::IntoBytes; use crate::cmd_options::{CmdOptions, Hypervisor}; use crate::cpuid::SnpCpuidPage; use crate::firmware::{parse_firmware, Firmware}; +use crate::paging::construct_init_page_tables; use crate::platform::PlatformMask; use crate::stage2_stack::Stage2Stack; use crate::vmsa::{construct_native_start_context, construct_start_context, construct_vmsa}; @@ -130,7 +131,11 @@ impl IgvmBuilder { // Construct a native context object to capture the start context. let start_rip = self.gpa_map.stage2_image.get_start(); let start_rsp = self.gpa_map.stage2_stack.get_end() - size_of::() as u64; - let start_context = construct_start_context(start_rip, start_rsp); + let start_context = construct_start_context( + start_rip, + start_rsp, + self.gpa_map.init_page_tables.get_start(), + ); self.build_directives(¶m_block, &start_context)?; self.build_initialization()?; @@ -513,6 +518,15 @@ impl IgvmBuilder { ); } + if COMPATIBILITY_MASK.contains(VSM_COMPATIBILITY_MASK) { + // Include initial page tables. + construct_init_page_tables( + self.gpa_map.init_page_tables.get_start(), + VSM_COMPATIBILITY_MASK, + &mut self.directives, + ); + } + Ok(()) } diff --git a/igvmbuilder/src/main.rs b/igvmbuilder/src/main.rs index b447ae451..7423a8cbf 100644 --- a/igvmbuilder/src/main.rs +++ b/igvmbuilder/src/main.rs @@ -16,6 +16,7 @@ mod gpa_map; mod igvm_builder; mod igvm_firmware; mod ovmf_firmware; +mod paging; mod platform; mod stage2_stack; mod vmsa; diff --git a/igvmbuilder/src/paging.rs b/igvmbuilder/src/paging.rs new file mode 100644 index 000000000..adf57eab7 --- /dev/null +++ b/igvmbuilder/src/paging.rs @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// +// Copyright (c) 2024 Microsoft Corporation +// +// Author: Jon Lange + +use igvm::IgvmDirectiveHeader; +use igvm_defs::{IgvmPageDataFlags, IgvmPageDataType, PAGE_SIZE_4K}; + +use zerocopy::{Immutable, IntoBytes}; + +#[derive(Clone, Copy, Immutable, IntoBytes)] +struct PageTablePage { + ptes: [u64; 512], +} + +#[derive(Clone, Copy, Default)] +struct InitPageTables { + pages: [PageTablePage; 2], +} + +impl Default for PageTablePage { + fn default() -> Self { + Self { ptes: [0; 512] } + } +} + +pub fn construct_init_page_tables( + init_page_table_gpa: u64, + compatibility_mask: u32, + directives: &mut Vec, +) { + let mut page_tables: InitPageTables = InitPageTables::default(); + + // The initial page tables comparise a single PML4E that points to a page + // that includes entries which map the low 4 GB of the address space + // with an identity map of 1 GB pages. + // This PML4E is present, writable, accesed, and dirty. + page_tables.pages[0].ptes[0] = 0x63 | (init_page_table_gpa + PAGE_SIZE_4K); + + for i in 0..4 { + // This PTE is present, writable, accesed, dirty, and large page. + page_tables.pages[1].ptes[i] = 0xE3 | ((i as u64) << 30); + } + + for (i, data) in page_tables.pages.iter().enumerate() { + // Allocate a byte vector to contain a copy of the initial page table + // data. + let mut page_table_data = Vec::::new(); + page_table_data.extend_from_slice(data.as_bytes()); + + directives.push(IgvmDirectiveHeader::PageData { + gpa: init_page_table_gpa + (i as u64) * PAGE_SIZE_4K, + compatibility_mask, + flags: IgvmPageDataFlags::new(), + data_type: IgvmPageDataType::NORMAL, + data: page_table_data, + }); + } +} diff --git a/igvmbuilder/src/vmsa.rs b/igvmbuilder/src/vmsa.rs index 8816062c6..ddc9c0f5c 100644 --- a/igvmbuilder/src/vmsa.rs +++ b/igvmbuilder/src/vmsa.rs @@ -12,7 +12,11 @@ use zerocopy07::FromZeroes; use crate::cmd_options::{Hypervisor, SevExtraFeatures}; -pub fn construct_start_context(start_rip: u64, start_rsp: u64) -> Vec { +pub fn construct_start_context( + start_rip: u64, + start_rsp: u64, + initial_cr3: u64, +) -> Vec { let mut vec: Vec = Vec::new(); // Establish CS as a 32-bit code selector. @@ -38,10 +42,18 @@ pub fn construct_start_context(start_rip: u64, start_rsp: u64) -> Vec { - vmsa.cr0 = *r; + // Remove CR0.PG. + vmsa.cr0 = *r & !0x8000_0000; } X86Register::Cr3(r) => { vmsa.cr3 = *r; @@ -249,7 +262,8 @@ pub fn construct_vmsa( vmsa.cr4 = *r; } X86Register::Efer(r) => { - vmsa.efer = *r; + // Remove EFER.LMA and EFER.LME. + vmsa.efer = *r & !0x500; } X86Register::Pat(r) => { vmsa.pat = *r;