Skip to content

Commit

Permalink
Merge pull request #575 from msft-jlange/test-in-hv
Browse files Browse the repository at this point in the history
testing: support configurations other than SNP/QEMU
  • Loading branch information
joergroedel authored Dec 20, 2024
2 parents b841809 + 38f1952 commit 9acdcf4
Show file tree
Hide file tree
Showing 15 changed files with 221 additions and 99 deletions.
2 changes: 2 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@ test-igvm: bin/coconut-test-qemu.igvm bin/coconut-test-hyperv.igvm bin/coconut-t
test-in-svsm: utils/cbit bin/coconut-test-qemu.igvm $(IGVMMEASUREBIN)
./scripts/test-in-svsm.sh

test-in-hyperv: bin/coconut-test-hyperv.igvm

doc:
cargo doc -p svsm --open --all-features --document-private-items

Expand Down
6 changes: 5 additions & 1 deletion bootlib/src/igvm_params.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,12 @@ pub struct IgvmParamBlock {
/// Indicates whether the guest can support alternate injection.
pub use_alternate_injection: u8,

/// Indicates whether the guest can assume firmware services specific to
/// QEMU.
pub is_qemu: u8,

#[doc(hidden)]
pub _reserved: [u8; 5],
pub _reserved: [u8; 4],

/// Metadata containing information about the firmware image embedded in the
/// IGVM file.
Expand Down
6 changes: 6 additions & 0 deletions igvmbuilder/src/igvm_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,11 @@ impl IgvmBuilder {
(fw_info, vtom)
};

let is_qemu: u8 = match self.options.hypervisor {
Hypervisor::Qemu => 1,
_ => 0,
};

// Most of the parameter block can be initialised with constants.
Ok(IgvmParamBlock {
param_area_size,
Expand All @@ -231,6 +236,7 @@ impl IgvmBuilder {
kernel_base: self.gpa_map.kernel.get_start(),
vtom,
use_alternate_injection: u8::from(self.options.alt_injection),
is_qemu,
..Default::default()
})
}
Expand Down
7 changes: 7 additions & 0 deletions kernel/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -179,4 +179,11 @@ impl SvsmConfig<'_> {
SvsmConfig::IgvmConfig(igvm_params) => igvm_params.use_alternate_injection(),
}
}

pub fn is_qemu(&self) -> bool {
match self {
SvsmConfig::FirmwareConfig(_) => true,
SvsmConfig::IgvmConfig(igvm_params) => igvm_params.is_qemu(),
}
}
}
193 changes: 114 additions & 79 deletions kernel/src/cpu/vc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ use crate::mm::GuestPtr;
use crate::sev::ghcb::GHCB;
use core::fmt;

#[cfg(test)]
use crate::testutils::{is_qemu_test_env, is_test_platform_type};

pub const SVM_EXIT_EXCP_BASE: usize = 0x40;
pub const SVM_EXIT_LAST_EXCP: usize = 0x5f;
pub const SVM_EXIT_RDTSC: usize = 0x6e;
Expand Down Expand Up @@ -305,6 +308,7 @@ mod tests {
use crate::cpu::msr::{rdtsc, rdtscp, read_msr, write_msr, RdtscpOut};
use crate::sev::ghcb::GHCB;
use crate::sev::utils::{get_dr7, raw_vmmcall, set_dr7};
use bootlib::platform::SvsmPlatformType;
use core::arch::asm;
use core::arch::x86_64::__cpuid_count;

Expand All @@ -320,15 +324,17 @@ mod tests {
#[test]
#[cfg_attr(not(test_in_svsm), ignore = "Can only be run inside guest")]
fn test_has_amd_cpuid() {
const CPUID_VENDOR_INFO: u32 = 0;
if is_test_platform_type(SvsmPlatformType::Snp) {
const CPUID_VENDOR_INFO: u32 = 0;

let vendor_info = unsafe { __cpuid_count(CPUID_VENDOR_INFO, 0) };
let vendor_info = unsafe { __cpuid_count(CPUID_VENDOR_INFO, 0) };

let vendor_name_bytes = [vendor_info.ebx, vendor_info.edx, vendor_info.ecx]
.map(|v| v.to_le_bytes())
.concat();
let vendor_name_bytes = [vendor_info.ebx, vendor_info.edx, vendor_info.ecx]
.map(|v| v.to_le_bytes())
.concat();

assert_eq!(core::str::from_utf8(&vendor_name_bytes), Ok("AuthenticAMD"));
assert_eq!(core::str::from_utf8(&vendor_name_bytes), Ok("AuthenticAMD"));
}
}

const GHCB_FILL_TEST_VALUE: u8 = b'1';
Expand Down Expand Up @@ -442,85 +448,101 @@ mod tests {
#[test]
#[cfg_attr(not(test_in_svsm), ignore = "Can only be run inside guest")]
fn test_port_io_8() {
const TEST_VAL: u8 = 0x12;
verify_ghcb_gets_altered(|| outb(TESTDEV_ECHO_LAST_PORT, TEST_VAL));
assert_eq!(
TEST_VAL,
verify_ghcb_gets_altered(|| inb(TESTDEV_ECHO_LAST_PORT))
);
if is_qemu_test_env() && is_test_platform_type(SvsmPlatformType::Snp) {
const TEST_VAL: u8 = 0x12;
verify_ghcb_gets_altered(|| outb(TESTDEV_ECHO_LAST_PORT, TEST_VAL));
assert_eq!(
TEST_VAL,
verify_ghcb_gets_altered(|| inb(TESTDEV_ECHO_LAST_PORT))
);
}
}

#[test]
#[cfg_attr(not(test_in_svsm), ignore = "Can only be run inside guest")]
fn test_port_io_16() {
const TEST_VAL: u16 = 0x4321;
verify_ghcb_gets_altered(|| outw(TESTDEV_ECHO_LAST_PORT, TEST_VAL));
assert_eq!(
TEST_VAL,
verify_ghcb_gets_altered(|| inw(TESTDEV_ECHO_LAST_PORT))
);
if is_qemu_test_env() && is_test_platform_type(SvsmPlatformType::Snp) {
const TEST_VAL: u16 = 0x4321;
verify_ghcb_gets_altered(|| outw(TESTDEV_ECHO_LAST_PORT, TEST_VAL));
assert_eq!(
TEST_VAL,
verify_ghcb_gets_altered(|| inw(TESTDEV_ECHO_LAST_PORT))
);
}
}

#[test]
#[cfg_attr(not(test_in_svsm), ignore = "Can only be run inside guest")]
fn test_port_io_32() {
const TEST_VAL: u32 = 0xabcd1234;
verify_ghcb_gets_altered(|| outl(TESTDEV_ECHO_LAST_PORT, TEST_VAL));
assert_eq!(
TEST_VAL,
verify_ghcb_gets_altered(|| inl(TESTDEV_ECHO_LAST_PORT))
);
if is_qemu_test_env() && is_test_platform_type(SvsmPlatformType::Snp) {
const TEST_VAL: u32 = 0xabcd1234;
verify_ghcb_gets_altered(|| outl(TESTDEV_ECHO_LAST_PORT, TEST_VAL));
assert_eq!(
TEST_VAL,
verify_ghcb_gets_altered(|| inl(TESTDEV_ECHO_LAST_PORT))
);
}
}

#[test]
#[cfg_attr(not(test_in_svsm), ignore = "Can only be run inside guest")]
fn test_port_io_8_hardcoded() {
const TEST_VAL: u8 = 0x12;
verify_ghcb_gets_altered(|| outb_to_testdev_echo(TEST_VAL));
assert_eq!(TEST_VAL, verify_ghcb_gets_altered(inb_from_testdev_echo));
if is_qemu_test_env() && is_test_platform_type(SvsmPlatformType::Snp) {
const TEST_VAL: u8 = 0x12;
verify_ghcb_gets_altered(|| outb_to_testdev_echo(TEST_VAL));
assert_eq!(TEST_VAL, verify_ghcb_gets_altered(inb_from_testdev_echo));
}
}

#[test]
#[cfg_attr(not(test_in_svsm), ignore = "Can only be run inside guest")]
fn test_port_io_16_hardcoded() {
const TEST_VAL: u16 = 0x4321;
verify_ghcb_gets_altered(|| outw_to_testdev_echo(TEST_VAL));
assert_eq!(TEST_VAL, verify_ghcb_gets_altered(inw_from_testdev_echo));
if is_qemu_test_env() && is_test_platform_type(SvsmPlatformType::Snp) {
const TEST_VAL: u16 = 0x4321;
verify_ghcb_gets_altered(|| outw_to_testdev_echo(TEST_VAL));
assert_eq!(TEST_VAL, verify_ghcb_gets_altered(inw_from_testdev_echo));
}
}

#[test]
#[cfg_attr(not(test_in_svsm), ignore = "Can only be run inside guest")]
fn test_port_io_32_hardcoded() {
const TEST_VAL: u32 = 0xabcd1234;
verify_ghcb_gets_altered(|| outl_to_testdev_echo(TEST_VAL));
assert_eq!(TEST_VAL, verify_ghcb_gets_altered(inl_from_testdev_echo));
if is_qemu_test_env() && is_test_platform_type(SvsmPlatformType::Snp) {
const TEST_VAL: u32 = 0xabcd1234;
verify_ghcb_gets_altered(|| outl_to_testdev_echo(TEST_VAL));
assert_eq!(TEST_VAL, verify_ghcb_gets_altered(inl_from_testdev_echo));
}
}

#[test]
#[cfg_attr(not(test_in_svsm), ignore = "Can only be run inside guest")]
fn test_port_io_string_16_get_last() {
const TEST_DATA: &[u16] = &[0x1234, 0x5678, 0x9abc, 0xdef0];
verify_ghcb_gets_altered(|| rep_outsw(TESTDEV_ECHO_LAST_PORT, TEST_DATA));
assert_eq!(
TEST_DATA.last().unwrap(),
&verify_ghcb_gets_altered(|| inw(TESTDEV_ECHO_LAST_PORT))
);

let mut test_data: [u16; 4] = [0; 4];
verify_ghcb_gets_altered(|| rep_insw(TESTDEV_ECHO_LAST_PORT, &mut test_data));
for d in test_data.iter() {
assert_eq!(d, TEST_DATA.last().unwrap());
if is_qemu_test_env() && is_test_platform_type(SvsmPlatformType::Snp) {
const TEST_DATA: &[u16] = &[0x1234, 0x5678, 0x9abc, 0xdef0];
verify_ghcb_gets_altered(|| rep_outsw(TESTDEV_ECHO_LAST_PORT, TEST_DATA));
assert_eq!(
TEST_DATA.last().unwrap(),
&verify_ghcb_gets_altered(|| inw(TESTDEV_ECHO_LAST_PORT))
);

let mut test_data: [u16; 4] = [0; 4];
verify_ghcb_gets_altered(|| rep_insw(TESTDEV_ECHO_LAST_PORT, &mut test_data));
for d in test_data.iter() {
assert_eq!(d, TEST_DATA.last().unwrap());
}
}
}

#[test]
#[cfg_attr(not(test_in_svsm), ignore = "Can only be run inside guest")]
fn test_sev_snp_enablement_msr() {
const MSR_SEV_STATUS: u32 = 0xc0010131;
const MSR_SEV_STATUS_SEV_SNP_ENABLED: u64 = 0b10;
if is_test_platform_type(SvsmPlatformType::Snp) {
const MSR_SEV_STATUS: u32 = 0xc0010131;
const MSR_SEV_STATUS_SEV_SNP_ENABLED: u64 = 0b10;

let sev_status = read_msr(MSR_SEV_STATUS);
assert_ne!(sev_status & MSR_SEV_STATUS_SEV_SNP_ENABLED, 0);
let sev_status = read_msr(MSR_SEV_STATUS);
assert_ne!(sev_status & MSR_SEV_STATUS_SEV_SNP_ENABLED, 0);
}
}

const MSR_APIC_BASE: u32 = 0x1b;
Expand All @@ -531,28 +553,34 @@ mod tests {
#[test]
#[cfg_attr(not(test_in_svsm), ignore = "Can only be run inside guest")]
fn test_rdmsr_apic() {
let apic_base = verify_ghcb_gets_altered(|| read_msr(MSR_APIC_BASE));
assert_eq!(apic_base & APIC_BASE_PHYS_ADDR_MASK, APIC_DEFAULT_PHYS_BASE);
if is_qemu_test_env() && is_test_platform_type(SvsmPlatformType::Snp) {
let apic_base = verify_ghcb_gets_altered(|| read_msr(MSR_APIC_BASE));
assert_eq!(apic_base & APIC_BASE_PHYS_ADDR_MASK, APIC_DEFAULT_PHYS_BASE);
}
}

#[test]
//#[cfg_attr(not(test_in_svsm), ignore = "Can only be run inside guest")]
#[ignore = "DBG_CTL access no longer intercepted"]
fn test_rdmsr_debug_ctl() {
const MSR_DEBUG_CTL: u32 = 0x1d9;
let apic_base = verify_ghcb_gets_altered(|| read_msr(MSR_DEBUG_CTL));
assert_eq!(apic_base, 0);
if is_qemu_test_env() && is_test_platform_type(SvsmPlatformType::Snp) {
const MSR_DEBUG_CTL: u32 = 0x1d9;
let apic_base = verify_ghcb_gets_altered(|| read_msr(MSR_DEBUG_CTL));
assert_eq!(apic_base, 0);
}
}

const MSR_TSC_AUX: u32 = 0xc0000103;

#[test]
#[cfg_attr(not(test_in_svsm), ignore = "Can only be run inside guest")]
fn test_wrmsr_tsc_aux() {
let test_val = 0x1234;
verify_ghcb_gets_altered(|| write_msr(MSR_TSC_AUX, test_val));
let readback = verify_ghcb_gets_altered(|| read_msr(MSR_TSC_AUX));
assert_eq!(test_val, readback);
if is_qemu_test_env() && is_test_platform_type(SvsmPlatformType::Snp) {
let test_val = 0x1234;
verify_ghcb_gets_altered(|| write_msr(MSR_TSC_AUX, test_val));
let readback = verify_ghcb_gets_altered(|| read_msr(MSR_TSC_AUX));
assert_eq!(test_val, readback);
}
}

#[test]
Expand All @@ -567,26 +595,31 @@ mod tests {
// #[cfg_attr(not(test_in_svsm), ignore = "Can only be run inside guest")]
#[ignore = "Currently unhandled by #VC handler"]
fn test_vmmcall_vapic_poll_irq() {
const VMMCALL_HC_VAPIC_POLL_IRQ: u32 = 1;
if is_qemu_test_env() && is_test_platform_type(SvsmPlatformType::Snp) {
const VMMCALL_HC_VAPIC_POLL_IRQ: u32 = 1;

let res =
verify_ghcb_gets_altered(|| unsafe { raw_vmmcall(VMMCALL_HC_VAPIC_POLL_IRQ, 0, 0, 0) });
assert_eq!(res, 0);
let res = verify_ghcb_gets_altered(|| unsafe {
raw_vmmcall(VMMCALL_HC_VAPIC_POLL_IRQ, 0, 0, 0)
});
assert_eq!(res, 0);
}
}

#[test]
// #[cfg_attr(not(test_in_svsm), ignore = "Can only be run inside guest")]
#[ignore = "Currently unhandled by #VC handler"]
fn test_read_write_dr7() {
const DR7_DEFAULT: u64 = 0x400;
const DR7_TEST: u64 = 0x401;
if is_qemu_test_env() && is_test_platform_type(SvsmPlatformType::Snp) {
const DR7_DEFAULT: u64 = 0x400;
const DR7_TEST: u64 = 0x401;

let old_dr7 = verify_ghcb_gets_altered(get_dr7);
assert_eq!(old_dr7, DR7_DEFAULT);
let old_dr7 = verify_ghcb_gets_altered(get_dr7);
assert_eq!(old_dr7, DR7_DEFAULT);

verify_ghcb_gets_altered(|| set_dr7(DR7_TEST));
let new_dr7 = verify_ghcb_gets_altered(get_dr7);
assert_eq!(new_dr7, DR7_TEST);
verify_ghcb_gets_altered(|| set_dr7(DR7_TEST));
let new_dr7 = verify_ghcb_gets_altered(get_dr7);
assert_eq!(new_dr7, DR7_TEST);
}
}

#[test]
Expand All @@ -603,21 +636,23 @@ mod tests {
#[test]
#[cfg_attr(not(test_in_svsm), ignore = "Can only be run inside guest")]
fn test_rdtscp() {
let expected_pid = u32::try_from(verify_ghcb_gets_altered(|| read_msr(MSR_TSC_AUX)))
.expect("pid should be 32 bits");
let RdtscpOut {
timestamp: mut prev,
pid,
} = rdtscp();
assert_eq!(pid, expected_pid);
for _ in 0..50 {
if is_test_platform_type(SvsmPlatformType::Snp) {
let expected_pid = u32::try_from(verify_ghcb_gets_altered(|| read_msr(MSR_TSC_AUX)))
.expect("pid should be 32 bits");
let RdtscpOut {
timestamp: cur,
timestamp: mut prev,
pid,
} = rdtscp();
assert_eq!(pid, expected_pid);
assert!(cur > prev);
prev = cur;
for _ in 0..50 {
let RdtscpOut {
timestamp: cur,
pid,
} = rdtscp();
assert_eq!(pid, expected_pid);
assert!(cur > prev);
prev = cur;
}
}
}

Expand Down
Loading

0 comments on commit 9acdcf4

Please sign in to comment.