Skip to content

Commit

Permalink
platform/snp: Add abstractions for FW operations
Browse files Browse the repository at this point in the history
Add new abstractions to SvsmPlatform:

- prepare_fw() performs preparations for launching guest boot firmware
- launch_fw() launches guest boot firmware

Move FW-related functions in svsm.rs to snp_fw.rs as they're
SEV-specific. Make sure snp_fw doesn't export anything unnecessary to
the outside world.

Signed-off-by: Peter Fang <[email protected]>
  • Loading branch information
peterfang committed Nov 27, 2024
1 parent 32c8598 commit 02f7b86
Show file tree
Hide file tree
Showing 8 changed files with 223 additions and 167 deletions.
2 changes: 1 addition & 1 deletion fuzz/fuzz_targets/fw_meta.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

use libfuzzer_sys::{fuzz_target, Corpus};
use std::hint::black_box;
use svsm::platform::snp_fw::parse_fw_meta_data;
use svsm::platform::parse_fw_meta_data;
use svsm::types::PAGE_SIZE;

fuzz_target!(|data: &[u8]| -> Corpus {
Expand Down
2 changes: 1 addition & 1 deletion kernel/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use crate::error::SvsmError;
use crate::fw_cfg::FwCfg;
use crate::igvm_params::IgvmParams;
use crate::mm::{PerCPUPageMappingGuard, PAGE_SIZE, SIZE_1G};
use crate::platform::snp_fw::{parse_fw_meta_data, SevFWMetaData};
use crate::platform::{parse_fw_meta_data, SevFWMetaData};
use crate::serial::SERIAL_PORT;
use crate::utils::MemoryRegion;
use alloc::vec::Vec;
Expand Down
26 changes: 26 additions & 0 deletions kernel/src/cpu/cpuid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,45 @@
//
// Author: Joerg Roedel <[email protected]>

use crate::address::VirtAddr;
use crate::types::PAGE_SIZE;
use crate::utils::immut_after_init::ImmutAfterInitRef;
use cpuarch::snp_cpuid::SnpCpuidTable;

use core::arch::asm;

static CPUID_PAGE: ImmutAfterInitRef<'_, SnpCpuidTable> = ImmutAfterInitRef::uninit();

const _: () = assert!(size_of::<SnpCpuidTable>() <= PAGE_SIZE);

pub fn register_cpuid_table(table: &'static SnpCpuidTable) {
CPUID_PAGE
.init_from_ref(table)
.expect("Could not initialize CPUID page");
}

/// Copy a CPUID page's content to memory pointed to by a [`VirtAddr`]
///
/// # Safety
///
/// The caller should verify that `dst` points to mapped memory whose size is
/// at least 4K. We assert above at compile time that SnpCpuidTable fits within
/// a page, so the write is safe.
///
/// The caller should verify not to corrupt arbitrary memory, as this function
/// doesn't make any checks in that regard.
pub unsafe fn copy_cpuid_table_to(dst: VirtAddr) {
let start = dst.as_mut_ptr::<u8>();
// SAFETY: caller must ensure the address is valid and not aliased.
unsafe {
// Zero target and copy data
start.write_bytes(0, PAGE_SIZE);
start
.cast::<SnpCpuidTable>()
.copy_from_nonoverlapping(&*CPUID_PAGE, 1);
}
}

#[derive(Clone, Copy, Debug)]
#[repr(C, packed)]
pub struct CpuidLeaf {
Expand Down
3 changes: 1 addition & 2 deletions kernel/src/igvm_params.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@ use crate::address::{PhysAddr, VirtAddr};
use crate::cpu::efer::EFERFlags;
use crate::error::SvsmError;
use crate::mm::{GuestPtr, PerCPUPageMappingGuard, PAGE_SIZE};
use crate::platform::snp_fw::SevFWMetaData;
use crate::platform::{PageStateChangeOp, PageValidateOp, SVSM_PLATFORM};
use crate::platform::{PageStateChangeOp, PageValidateOp, SevFWMetaData, SVSM_PLATFORM};
use crate::types::PageSize;
use crate::utils::MemoryRegion;
use alloc::vec::Vec;
Expand Down
19 changes: 18 additions & 1 deletion kernel/src/platform/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,19 @@
pub mod guest_cpu;
pub mod native;
pub mod snp;
pub mod snp_fw;
pub mod tdp;

mod snp_fw;
pub use snp_fw::{parse_fw_meta_data, SevFWMetaData};

use native::NativePlatform;
use snp::SnpPlatform;
use tdp::TdpPlatform;

use core::ops::{Deref, DerefMut};

use crate::address::{PhysAddr, VirtAddr};
use crate::config::SvsmConfig;
use crate::cpu::cpuid::CpuidResult;
use crate::cpu::percpu::PerCpu;
use crate::error::SvsmError;
Expand Down Expand Up @@ -75,6 +78,20 @@ pub trait SvsmPlatform {
/// (for services not used by stage2).
fn env_setup_svsm(&self) -> Result<(), SvsmError>;

/// Performs the necessary preparations for launching guest boot firmware.
fn prepare_fw(
&self,
_config: &SvsmConfig<'_>,
_kernel_region: MemoryRegion<PhysAddr>,
) -> Result<(), SvsmError> {
Ok(())
}

/// Launches guest boot firmware.
fn launch_fw(&self, _config: &SvsmConfig<'_>) -> Result<(), SvsmError> {
Ok(())
}

/// Completes initialization of a per-CPU object during construction.
fn setup_percpu(&self, cpu: &PerCpu) -> Result<(), SvsmError>;

Expand Down
30 changes: 30 additions & 0 deletions kernel/src/platform/snp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,19 @@
//
// Author: Jon Lange <[email protected]>

use super::snp_fw::{
copy_tables_to_fw, launch_fw, prepare_fw_launch, print_fw_meta, validate_fw, validate_fw_memory,
};
use crate::address::{Address, PhysAddr, VirtAddr};
use crate::config::SvsmConfig;
use crate::console::init_svsm_console;
use crate::cpu::cpuid::{cpuid_table, CpuidResult};
use crate::cpu::percpu::{current_ghcb, this_cpu, PerCpu};
use crate::error::ApicError::Registration;
use crate::error::SvsmError;
use crate::greq::driver::guest_request_driver_init;
use crate::io::IOPort;
use crate::mm::memory::write_guest_memory_map;
use crate::mm::{PerCPUPageMappingGuard, PAGE_SIZE, PAGE_SIZE_2M};
use crate::platform::{PageEncryptionMasks, PageStateChangeOp, PageValidateOp, SvsmPlatform};
use crate::sev::ghcb::GHCBIOSize;
Expand Down Expand Up @@ -115,6 +120,31 @@ impl SvsmPlatform for SnpPlatform {
Ok(())
}

fn prepare_fw(
&self,
config: &SvsmConfig<'_>,
kernel_region: MemoryRegion<PhysAddr>,
) -> Result<(), SvsmError> {
let fw_metadata = config.get_fw_metadata();
if let Some(ref fw_meta) = fw_metadata {
print_fw_meta(fw_meta);
write_guest_memory_map(config).expect("Failed to write guest memory map");
validate_fw_memory(config, fw_meta, &kernel_region).expect("Failed to validate memory");
copy_tables_to_fw(fw_meta, &kernel_region).expect("Failed to copy firmware tables");
validate_fw(config, &kernel_region).expect("Failed to validate flash memory");
prepare_fw_launch(fw_meta).expect("Failed to setup guest VMSA/CAA");
}
Ok(())
}

fn launch_fw(&self, config: &SvsmConfig<'_>) -> Result<(), SvsmError> {
if config.should_launch_fw() {
launch_fw(config)
} else {
Ok(())
}
}

fn setup_percpu(&self, cpu: &PerCpu) -> Result<(), SvsmError> {
// Setup GHCB
cpu.setup_ghcb()
Expand Down
143 changes: 135 additions & 8 deletions kernel/src/platform/snp_fw.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,17 @@ extern crate alloc;

use crate::address::PhysAddr;
use crate::config::SvsmConfig;
use crate::cpu::percpu::current_ghcb;
use crate::cpu::cpuid::copy_cpuid_table_to;
use crate::cpu::percpu::{current_ghcb, this_cpu, this_cpu_shared};
use crate::error::SvsmError;
use crate::kernel_region::new_kernel_region;
use crate::mm::PerCPUPageMappingGuard;
use crate::platform::PageStateChangeOp;
use crate::sev::{pvalidate, rmp_adjust, PvalidateOp, RMPFlags};
use crate::types::{PageSize, PAGE_SIZE};
use crate::requests::update_mappings;
use crate::sev::{pvalidate, rmp_adjust, secrets_page, PvalidateOp, RMPFlags};
use crate::types::{PageSize, GUEST_VMPL, PAGE_SIZE};
use crate::utils::fw_meta::{find_table, RawMetaBuffer, Uuid};
use crate::utils::{zero_mem_region, MemoryRegion};
use alloc::vec::Vec;
use bootlib::kernel_launch::KernelLaunchInfo;
use zerocopy::{FromBytes, Immutable, KnownLayout};

use core::mem::{size_of, size_of_val};
Expand Down Expand Up @@ -240,7 +240,7 @@ fn validate_fw_memory_vec(
pub fn validate_fw_memory(
config: &SvsmConfig<'_>,
fw_meta: &SevFWMetaData,
launch_info: &KernelLaunchInfo,
kernel_region: &MemoryRegion<PhysAddr>,
) -> Result<(), SvsmError> {
// Initalize vector with regions from the FW
let mut regions = fw_meta.valid_mem.clone();
Expand All @@ -263,9 +263,8 @@ pub fn validate_fw_memory(
// Sort regions by base address
regions.sort_unstable_by_key(|a| a.start());

let kernel_region = new_kernel_region(launch_info);
for region in regions.iter() {
if region.overlap(&kernel_region) {
if region.overlap(kernel_region) {
log::error!("FwMeta region ovelaps with kernel");
return Err(SvsmError::Firmware);
}
Expand Down Expand Up @@ -300,3 +299,131 @@ pub fn print_fw_meta(fw_meta: &SevFWMetaData) {
);
}
}

fn copy_cpuid_table_to_fw(fw_addr: PhysAddr) -> Result<(), SvsmError> {
let guard = PerCPUPageMappingGuard::create_4k(fw_addr)?;

// SAFETY: this is called from CPU 0, so the underlying physical address
// is not being aliased. We are mapping a full page, which is 4K-aligned,
// and is enough for SnpCpuidTable.
unsafe {
copy_cpuid_table_to(guard.virt_addr());
}

Ok(())
}

fn copy_secrets_page_to_fw(
fw_addr: PhysAddr,
caa_addr: PhysAddr,
kernel_region: &MemoryRegion<PhysAddr>,
) -> Result<(), SvsmError> {
let guard = PerCPUPageMappingGuard::create_4k(fw_addr)?;
let start = guard.virt_addr();

// Zero target
zero_mem_region(start, start + PAGE_SIZE);

// Copy secrets page
let mut fw_secrets_page = secrets_page().copy_for_vmpl(GUEST_VMPL);

fw_secrets_page.set_svsm_data(
kernel_region.start().into(),
kernel_region.len().try_into().unwrap(),
u64::from(caa_addr),
);

// SAFETY: start points to a new allocated and zeroed page.
unsafe {
fw_secrets_page.copy_to(start);
}

Ok(())
}

fn zero_caa_page(fw_addr: PhysAddr) -> Result<(), SvsmError> {
let guard = PerCPUPageMappingGuard::create_4k(fw_addr)?;
let vaddr = guard.virt_addr();

zero_mem_region(vaddr, vaddr + PAGE_SIZE);

Ok(())
}

pub fn copy_tables_to_fw(
fw_meta: &SevFWMetaData,
kernel_region: &MemoryRegion<PhysAddr>,
) -> Result<(), SvsmError> {
if let Some(addr) = fw_meta.cpuid_page {
copy_cpuid_table_to_fw(addr)?;
}

let secrets_page = fw_meta.secrets_page.ok_or(SvsmError::MissingSecrets)?;
let caa_page = fw_meta.caa_page.ok_or(SvsmError::MissingCAA)?;

copy_secrets_page_to_fw(secrets_page, caa_page, kernel_region)?;

zero_caa_page(caa_page)?;

Ok(())
}

pub fn validate_fw(
config: &SvsmConfig<'_>,
kernel_region: &MemoryRegion<PhysAddr>,
) -> Result<(), SvsmError> {
let flash_regions = config.get_fw_regions(kernel_region);

for (i, region) in flash_regions.into_iter().enumerate() {
log::info!(
"Flash region {} at {:#018x} size {:018x}",
i,
region.start(),
region.len(),
);

for paddr in region.iter_pages(PageSize::Regular) {
let guard = PerCPUPageMappingGuard::create_4k(paddr)?;
let vaddr = guard.virt_addr();
if let Err(e) = rmp_adjust(
vaddr,
RMPFlags::GUEST_VMPL | RMPFlags::RWX,
PageSize::Regular,
) {
log::info!("rmpadjust failed for addr {:#018x}", vaddr);
return Err(e);
}
}
}

Ok(())
}

pub fn prepare_fw_launch(fw_meta: &SevFWMetaData) -> Result<(), SvsmError> {
if let Some(caa) = fw_meta.caa_page {
this_cpu_shared().update_guest_caa(caa);
}

this_cpu().alloc_guest_vmsa()?;
update_mappings()?;

Ok(())
}

pub fn launch_fw(config: &SvsmConfig<'_>) -> Result<(), SvsmError> {
let cpu = this_cpu();
let mut vmsa_ref = cpu.guest_vmsa_ref();
let vmsa_pa = vmsa_ref.vmsa_phys().unwrap();
let vmsa = vmsa_ref.vmsa();

config.initialize_guest_vmsa(vmsa)?;

log::info!("VMSA PA: {:#x}", vmsa_pa);

let sev_features = vmsa.sev_features;

log::info!("Launching Firmware");
current_ghcb().register_guest_vmsa(vmsa_pa, 0, GUEST_VMPL as u64, sev_features)?;

Ok(())
}
Loading

0 comments on commit 02f7b86

Please sign in to comment.