Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for Hyper-V firmware execution #201

Merged
merged 7 commits into from
Jan 9, 2024
6 changes: 5 additions & 1 deletion bootlib/src/igvm_params.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,11 @@ pub struct IgvmParamBlockFwInfo {
/// no firmware is launched after initialization is complete.
pub size: u32,

_reserved: u32,
/// Indicates that the initial location of firmware is at the base of
/// memory and will not be loaded into the ROM range.
pub in_low_memory: u8,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if it makes more sense to invert this flag and name it something like in_system_region (see comment below regarding 'system')? This would then explain the change in behaviour when the firmware uses the ROM region. Or just rename it to outside_system_region?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could; my intention here was that any parameter that is zero continues the existing behavior, and new behavior is only invoked by non-zero parameters. That means that the existing "FW lives in ROM range" idea would be associated with a zero parameter. There's no good reason that I can't invert that if we think it enhances clarity.


_reserved: [u8; 3],

/// The guest physical address at which the firmware expects to find the
/// secrets page.
Expand Down
3 changes: 2 additions & 1 deletion igvmbld/igvmbld.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ typedef struct {
typedef struct {
uint32_t start;
uint32_t size;
uint32_t _reserved;
uint8_t in_low_memory;
uint8_t _reserved[3];
uint32_t secrets_page;
uint32_t caa_page;
uint32_t cpuid_page;
Expand Down
62 changes: 59 additions & 3 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,44 @@ use crate::serial::SERIAL_PORT;
use crate::utils::MemoryRegion;
use alloc::vec::Vec;

fn check_ovmf_regions(
flash_regions: &[MemoryRegion<PhysAddr>],
kernel_region: &MemoryRegion<PhysAddr>,
) {
Comment on lines +23 to +26
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Further to our email/call discussion about the name of 'flash' in SVSM, I wonder if this function and parameters should be renamed. I'm not sure what would be a good name for the region from 3GB-4GB. ROM region? System region?

Suggested change
fn check_ovmf_regions(
flash_regions: &[MemoryRegion<PhysAddr>],
kernel_region: &MemoryRegion<PhysAddr>,
) {
fn check_system_region(
fw_regions: &[MemoryRegion<PhysAddr>],
kernel_region: &MemoryRegion<PhysAddr>,
) {

let flash_range = {
let one_gib = 1024 * 1024 * 1024usize;
let start = PhysAddr::from(3 * one_gib);
MemoryRegion::new(start, one_gib)
};

// Sanity-check flash regions.
for region in flash_regions.iter() {
// Make sure that the regions are between 3GiB and 4GiB.
if !region.overlap(&flash_range) {
panic!("flash region in unexpected region");
}

// Make sure that no regions overlap with the kernel.
if region.overlap(kernel_region) {
panic!("flash region overlaps with kernel");
}
}

// Make sure that regions don't overlap.
for (i, outer) in flash_regions.iter().enumerate() {
for inner in flash_regions[..i].iter() {
if outer.overlap(inner) {
panic!("flash regions overlap");
}
}
// Make sure that one regions ends at 4GiB.
let one_region_ends_at_4gib = flash_regions
.iter()
.any(|region| region.end() == flash_range.end());
assert!(one_region_ends_at_4gib);
}
}

#[derive(Debug)]
pub enum SvsmConfig<'a> {
FirmwareConfig(FwCfg<'a>),
Expand Down Expand Up @@ -93,12 +131,30 @@ impl<'a> SvsmConfig<'a> {
}
}

pub fn get_fw_regions(&self) -> Result<Vec<MemoryRegion<PhysAddr>>, SvsmError> {
pub fn get_fw_regions(
&self,
kernel_region: &MemoryRegion<PhysAddr>,
) -> Vec<MemoryRegion<PhysAddr>> {
match self {
SvsmConfig::FirmwareConfig(fw_cfg) => {
Ok(fw_cfg.iter_flash_regions().collect::<Vec<_>>())
let flash_regions = fw_cfg.iter_flash_regions().collect::<Vec<_>>();
check_ovmf_regions(&flash_regions, kernel_region);
flash_regions
}
SvsmConfig::IgvmConfig(igvm_params) => igvm_params.get_fw_regions(),
SvsmConfig::IgvmConfig(igvm_params) => {
let flash_regions = igvm_params.get_fw_regions();
if !igvm_params.fw_in_low_memory() {
check_ovmf_regions(&flash_regions, kernel_region);
}
Comment on lines +147 to +149
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Renaming in_low_memory and check_ovmf_regions() as per my above comments would result in something like this, which I think conveys the intent more clearly.

Suggested change
if !igvm_params.fw_in_low_memory() {
check_ovmf_regions(&flash_regions, kernel_region);
}
if igvm_params.fw_in_system_region() {
check_system_region(&fw_regions, kernel_region);
}

flash_regions
}
}
}

pub fn fw_in_low_memory(&self) -> bool {
match self {
SvsmConfig::FirmwareConfig(_) => false,
SvsmConfig::IgvmConfig(igvm_params) => igvm_params.fw_in_low_memory(),
}
}

Expand Down
31 changes: 22 additions & 9 deletions src/igvm_params.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ use crate::error::SvsmError::Firmware;
use crate::fw_meta::SevFWMetaData;
use crate::mm::PAGE_SIZE;
use crate::utils::MemoryRegion;
use alloc::vec;
use alloc::vec::Vec;

use bootlib::igvm_params::{IgvmParamBlock, IgvmParamPage};
Expand All @@ -22,6 +21,8 @@ use igvm_defs::{IgvmEnvironmentInfo, MemoryMapEntryType, IGVM_VHS_MEMORY_MAP_ENT

const IGVM_MEMORY_ENTRIES_PER_PAGE: usize = PAGE_SIZE / size_of::<IGVM_VHS_MEMORY_MAP_ENTRY>();

const STAGE2_END_ADDR: usize = 0xA0000;

#[derive(Clone, Debug)]
#[repr(C, align(64))]
pub struct IgvmMemoryMap {
Expand Down Expand Up @@ -199,14 +200,26 @@ impl IgvmParams<'_> {
}
}

pub fn get_fw_regions(&self) -> Result<Vec<MemoryRegion<PhysAddr>>, SvsmError> {
if !self.should_launch_fw() {
Err(Firmware)
} else {
Ok(vec![MemoryRegion::new(
PhysAddr::new(self.igvm_param_block.firmware.start as usize),
self.igvm_param_block.firmware.size as usize,
)])
pub fn get_fw_regions(&self) -> Vec<MemoryRegion<PhysAddr>> {
assert!(self.should_launch_fw());

let mut regions = Vec::new();

if self.igvm_param_block.firmware.in_low_memory != 0 {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could use fw_in_low_memory() here.

// Add the stage 2 region to the firmware region list so
// permissions can be granted to the guest VMPL for that range.
regions.push(MemoryRegion::new(PhysAddr::new(0), STAGE2_END_ADDR));
}

regions.push(MemoryRegion::new(
PhysAddr::new(self.igvm_param_block.firmware.start as usize),
self.igvm_param_block.firmware.size as usize,
));

regions
}

pub fn fw_in_low_memory(&self) -> bool {
self.igvm_param_block.firmware.in_low_memory != 0
}
}
35 changes: 2 additions & 33 deletions src/svsm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ use svsm::svsm_console::SVSMIOPort;
use svsm::svsm_paging::{init_page_table, invalidate_early_boot_memory};
use svsm::task::{create_task, TASK_FLAG_SHARE_PT};
use svsm::types::{PageSize, GUEST_VMPL, PAGE_SIZE};
use svsm::utils::{halt, immut_after_init::ImmutAfterInitCell, zero_mem_region, MemoryRegion};
use svsm::utils::{halt, immut_after_init::ImmutAfterInitCell, zero_mem_region};

use svsm::mm::validate::{init_valid_bitmap_ptr, migrate_valid_bitmap};

Expand Down Expand Up @@ -221,39 +221,8 @@ fn launch_fw() -> Result<(), SvsmError> {
}

fn validate_fw(config: &SvsmConfig, launch_info: &KernelLaunchInfo) -> Result<(), SvsmError> {
let flash_regions = config.get_fw_regions()?;
let kernel_region = new_kernel_region(launch_info);
let flash_range = {
let one_gib = 1024 * 1024 * 1024usize;
let start = PhysAddr::from(3 * one_gib);
MemoryRegion::new(start, one_gib)
};

// Sanity-check flash regions.
for region in flash_regions.iter() {
// Make sure that the regions are between 3GiB and 4GiB.
if !region.overlap(&flash_range) {
panic!("flash region in unexpected region");
}

// Make sure that no regions overlap with the kernel.
if region.overlap(&kernel_region) {
panic!("flash region overlaps with kernel");
}
}
// Make sure that regions don't overlap.
for (i, outer) in flash_regions.iter().enumerate() {
for inner in flash_regions[..i].iter() {
if outer.overlap(inner) {
panic!("flash regions overlap");
}
}
}
// Make sure that one regions ends at 4GiB.
let one_region_ends_at_4gib = flash_regions
.iter()
.any(|region| region.end() == flash_range.end());
assert!(one_region_ends_at_4gib);
let flash_regions = config.get_fw_regions(&kernel_region);

for (i, region) in flash_regions.into_iter().enumerate() {
log::info!(
Expand Down
9 changes: 6 additions & 3 deletions src/svsm_paging.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,9 +133,12 @@ pub fn invalidate_early_boot_memory(
) -> Result<(), SvsmError> {
// Early boot memory must be invalidated after changing to the SVSM page
// page table to avoid invalidating page tables currently in use. Always
// invalidate stage 2 memory, and invalidate the boot data if required.
let stage2_region = MemoryRegion::new(PhysAddr::null(), 640 * 1024);
invalidate_boot_memory_region(config, stage2_region)?;
// invalidate stage 2 memory, unless firmware is loaded into low memory.
// Also invalidate the boot data if required.
if !config.fw_in_low_memory() {
let stage2_region = MemoryRegion::new(PhysAddr::null(), 640 * 1024);
invalidate_boot_memory_region(config, stage2_region)?;
}

if config.invalidate_boot_data() {
let kernel_elf_size =
Expand Down