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

testing: support configurations other than SNP/QEMU #575

Merged
merged 2 commits into from
Dec 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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,27 +553,33 @@ 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")]
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 @@ -566,26 +594,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 @@ -602,21 +635,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
Loading