diff --git a/Makefile b/Makefile index 512e7161c..4002b42fa 100644 --- a/Makefile +++ b/Makefile @@ -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 diff --git a/bootlib/src/igvm_params.rs b/bootlib/src/igvm_params.rs index 0de5772e1..b5243bd78 100644 --- a/bootlib/src/igvm_params.rs +++ b/bootlib/src/igvm_params.rs @@ -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. diff --git a/igvmbuilder/src/igvm_builder.rs b/igvmbuilder/src/igvm_builder.rs index 651334f60..4604bed27 100644 --- a/igvmbuilder/src/igvm_builder.rs +++ b/igvmbuilder/src/igvm_builder.rs @@ -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, @@ -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() }) } diff --git a/kernel/src/config.rs b/kernel/src/config.rs index fbb22dbd0..85e1ceda0 100644 --- a/kernel/src/config.rs +++ b/kernel/src/config.rs @@ -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(), + } + } } diff --git a/kernel/src/cpu/vc.rs b/kernel/src/cpu/vc.rs index 9b87a3229..2a011eed7 100644 --- a/kernel/src/cpu/vc.rs +++ b/kernel/src/cpu/vc.rs @@ -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; @@ -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; @@ -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'; @@ -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; @@ -531,16 +553,20 @@ 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; @@ -548,10 +574,12 @@ mod tests { #[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] @@ -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] @@ -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; + } } } diff --git a/kernel/src/greq/services.rs b/kernel/src/greq/services.rs index d810fb175..a2de8e972 100644 --- a/kernel/src/greq/services.rs +++ b/kernel/src/greq/services.rs @@ -121,26 +121,30 @@ mod tests { use crate::serial::Terminal; use crate::testing::{assert_eq_warn, svsm_test_io, IORequest}; + use crate::testutils::{is_qemu_test_env, is_test_platform_type}; use alloc::vec; + use bootlib::platform::SvsmPlatformType; - let sp = svsm_test_io().unwrap(); + if is_qemu_test_env() && is_test_platform_type(SvsmPlatformType::Snp) { + let sp = svsm_test_io().unwrap(); - sp.put_byte(IORequest::GetLaunchMeasurement as u8); + sp.put_byte(IORequest::GetLaunchMeasurement as u8); - let mut expected_measurement = [0u8; 48]; - for byte in &mut expected_measurement { - *byte = sp.get_byte(); - } + let mut expected_measurement = [0u8; 48]; + for byte in &mut expected_measurement { + *byte = sp.get_byte(); + } - let mut buf = vec![0; size_of::()]; - let size = get_regular_report(&mut buf).unwrap(); - assert_eq!(size, buf.len()); + let mut buf = vec![0; size_of::()]; + let size = get_regular_report(&mut buf).unwrap(); + assert_eq!(size, buf.len()); - let (response, _rest) = SnpReportResponse::ref_from_prefix(&buf).unwrap(); - response.validate().unwrap(); - // FIXME: we still have some cases where the precalculated value does - // not match, so for now we just issue a warning until we fix the problem. - assert_eq_warn!(expected_measurement, *response.measurement()); + let (response, _rest) = SnpReportResponse::ref_from_prefix(&buf).unwrap(); + response.validate().unwrap(); + // FIXME: we still have some cases where the precalculated value does + // not match, so for now we just issue a warning until we fix the problem. + assert_eq_warn!(expected_measurement, *response.measurement()); + } } } diff --git a/kernel/src/igvm_params.rs b/kernel/src/igvm_params.rs index 15756bbc3..cfbc13de1 100644 --- a/kernel/src/igvm_params.rs +++ b/kernel/src/igvm_params.rs @@ -388,4 +388,8 @@ impl IgvmParams<'_> { pub fn use_alternate_injection(&self) -> bool { self.igvm_param_block.use_alternate_injection != 0 } + + pub fn is_qemu(&self) -> bool { + self.igvm_param_block.is_qemu != 0 + } } diff --git a/kernel/src/lib.rs b/kernel/src/lib.rs index 36e509702..da9199654 100644 --- a/kernel/src/lib.rs +++ b/kernel/src/lib.rs @@ -56,3 +56,6 @@ extern crate self as svsm; // Include a module containing the test runner. #[cfg(all(test, test_in_svsm))] pub mod testing; +// Utilities for test configurations. +#[cfg(test)] +pub mod testutils; diff --git a/kernel/src/platform/mod.rs b/kernel/src/platform/mod.rs index 890b5e3a0..a1557f077 100644 --- a/kernel/src/platform/mod.rs +++ b/kernel/src/platform/mod.rs @@ -60,6 +60,9 @@ pub enum PageValidateOp { /// This defines a platform abstraction to permit the SVSM to run on different /// underlying architectures. pub trait SvsmPlatform { + #[cfg(test)] + fn platform_type(&self) -> SvsmPlatformType; + /// Halts the system as required by the platform. fn halt() where diff --git a/kernel/src/platform/native.rs b/kernel/src/platform/native.rs index b90fb24e9..cefcc3ec7 100644 --- a/kernel/src/platform/native.rs +++ b/kernel/src/platform/native.rs @@ -20,6 +20,9 @@ use crate::utils::MemoryRegion; #[cfg(debug_assertions)] use crate::mm::virt_to_phys; +#[cfg(test)] +use bootlib::platform::SvsmPlatformType; + const APIC_MSR_EOI: u32 = 0x80B; const APIC_MSR_ICR: u32 = 0x830; @@ -43,6 +46,11 @@ impl Default for NativePlatform { } impl SvsmPlatform for NativePlatform { + #[cfg(test)] + fn platform_type(&self) -> SvsmPlatformType { + SvsmPlatformType::Native + } + fn env_setup(&mut self, debug_serial_port: u16, _vtom: usize) -> Result<(), SvsmError> { // In the native platform, console output does not require the use of // any platform services, so it can be initialized immediately. diff --git a/kernel/src/platform/snp.rs b/kernel/src/platform/snp.rs index 4f734f5bb..ee4ae66ce 100644 --- a/kernel/src/platform/snp.rs +++ b/kernel/src/platform/snp.rs @@ -35,6 +35,9 @@ use crate::utils::MemoryRegion; use core::sync::atomic::{AtomicBool, AtomicU32, Ordering}; +#[cfg(test)] +use bootlib::platform::SvsmPlatformType; + static SVSM_ENV_INITIALIZED: AtomicBool = AtomicBool::new(false); static GHCB_IO_DRIVER: GHCBIOPort = GHCBIOPort::new(); @@ -93,6 +96,11 @@ impl Default for SnpPlatform { } impl SvsmPlatform for SnpPlatform { + #[cfg(test)] + fn platform_type(&self) -> SvsmPlatformType { + SvsmPlatformType::Snp + } + fn env_setup(&mut self, _debug_serial_port: u16, vtom: usize) -> Result<(), SvsmError> { sev_status_init(); VTOM.init(&vtom).map_err(|_| SvsmError::PlatformInit)?; diff --git a/kernel/src/platform/tdp.rs b/kernel/src/platform/tdp.rs index 7ad4f43d0..8fde2f872 100644 --- a/kernel/src/platform/tdp.rs +++ b/kernel/src/platform/tdp.rs @@ -21,6 +21,9 @@ use crate::types::{PageSize, PAGE_SIZE}; use crate::utils::immut_after_init::ImmutAfterInitCell; use crate::utils::{is_aligned, MemoryRegion}; +#[cfg(test)] +use bootlib::platform::SvsmPlatformType; + static GHCI_IO_DRIVER: GHCIIOPort = GHCIIOPort::new(); static VTOM: ImmutAfterInitCell = ImmutAfterInitCell::uninit(); @@ -40,6 +43,11 @@ impl Default for TdpPlatform { } impl SvsmPlatform for TdpPlatform { + #[cfg(test)] + fn platform_type(&self) -> SvsmPlatformType { + SvsmPlatformType::Tdp + } + fn halt() { tdvmcall_halt(); } diff --git a/kernel/src/svsm.rs b/kernel/src/svsm.rs index 24abff074..be941b478 100755 --- a/kernel/src/svsm.rs +++ b/kernel/src/svsm.rs @@ -330,7 +330,12 @@ pub extern "C" fn svsm_main() { .expect("Failed to launch request processing task"); #[cfg(test)] - crate::test_main(); + { + if config.is_qemu() { + crate::testutils::set_qemu_test_env(); + } + crate::test_main(); + } match exec_user("/init", opendir("/").expect("Failed to find FS root")) { Ok(_) => (), diff --git a/kernel/src/testing.rs b/kernel/src/testing.rs index 661924f89..dcd4a977e 100644 --- a/kernel/src/testing.rs +++ b/kernel/src/testing.rs @@ -1,3 +1,4 @@ +use core::arch::asm; use log::info; use test::ShouldPanic; @@ -7,6 +8,7 @@ use crate::{ platform::SVSM_PLATFORM, serial::SerialPort, sev::ghcb::GHCBIOSize, + testutils::is_qemu_test_env, }; #[macro_export] @@ -86,9 +88,15 @@ pub fn svsm_test_runner(test_cases: &[&test::TestDescAndFn]) { } fn exit() -> ! { - const QEMU_EXIT_PORT: u16 = 0xf4; - current_ghcb() - .ioio_out(QEMU_EXIT_PORT, GHCBIOSize::Size32, 0) - .unwrap(); + if is_qemu_test_env() { + const QEMU_EXIT_PORT: u16 = 0xf4; + current_ghcb() + .ioio_out(QEMU_EXIT_PORT, GHCBIOSize::Size32, 0) + .unwrap(); + } + // SAFETY: HLT instruction does not affect memory. + unsafe { + asm!("hlt"); + } unreachable!(); } diff --git a/kernel/src/testutils.rs b/kernel/src/testutils.rs new file mode 100644 index 000000000..d6a7962d9 --- /dev/null +++ b/kernel/src/testutils.rs @@ -0,0 +1,17 @@ +use crate::platform::SVSM_PLATFORM; +use bootlib::platform::SvsmPlatformType; +use core::sync::atomic::{AtomicBool, Ordering}; + +static QEMU_TEST_ENV: AtomicBool = AtomicBool::new(false); + +pub fn is_qemu_test_env() -> bool { + QEMU_TEST_ENV.load(Ordering::Acquire) +} + +pub fn is_test_platform_type(platform_type: SvsmPlatformType) -> bool { + SVSM_PLATFORM.platform_type() == platform_type +} + +pub fn set_qemu_test_env() { + QEMU_TEST_ENV.store(true, Ordering::Release) +}