Skip to content

Commit

Permalink
test: make it possible to run tests inside SVSM
Browse files Browse the repository at this point in the history
To build and run a kernel that runs the tests use
```
QEMU=/path/to/qemu OVMF=/path/to/firmware/ make test-in-svsm
```

Signed-off-by: Tom Dohrmann <[email protected]>
  • Loading branch information
Freax13 committed Oct 15, 2023
1 parent 2b31f74 commit 773b3b2
Show file tree
Hide file tree
Showing 7 changed files with 110 additions and 6 deletions.
5 changes: 5 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ gdbstub_arch = { version = "0.2.4", optional = true }
log = { version = "0.4.17", features = ["max_level_info", "release_max_level_info"] }
packit = { git = "https://github.com/coconut-svsm/packit", version = "0.1.0" }

[build-dependencies]
[target."x86_64-unknown-none".dev-dependencies]
test = { version = "0.1.0", path = "test" }

[features]
default = ["enable-stacktrace"]
Expand Down
36 changes: 33 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,49 @@ FEATURES ?= "default"
CARGO_ARGS = --features ${FEATURES}

ifdef RELEASE
TARGET_PATH="release"
TARGET_PATH=release
CARGO_ARGS += --release
else
TARGET_PATH="debug"
TARGET_PATH=debug
endif

STAGE2_ELF = "target/x86_64-unknown-none/${TARGET_PATH}/stage2"
KERNEL_ELF = "target/x86_64-unknown-none/${TARGET_PATH}/svsm"
TEST_KERNEL_ELF = target/x86_64-unknown-none/${TARGET_PATH}/svsm-test
FS_FILE ?= none

C_BIT_POS ?= 51

STAGE1_OBJS = stage1/stage1.o stage1/reset.o

all: stage1/kernel.elf svsm.bin

test:
cargo test --target=x86_64-unknown-linux-gnu

test-in-svsm: stage1/test-kernel.elf svsm.bin
ifndef QEMU
echo "Set QEMU environment variable to QEMU installation path" && exit 1
endif
ifndef OVMF
echo "Set OVMFenvironment variable to a folder containing OVMF_CODE.fd and OVMF_VARS.fd" && exit 1
endif
$(QEMU)/qemu-system-x86_64 \
-enable-kvm \
-cpu EPYC-v4 \
-machine q35,confidential-guest-support=sev0,memory-backend=ram1,kvm-type=protected \
-object memory-backend-memfd-private,id=ram1,size=1G,share=true \
-object sev-snp-guest,id=sev0,cbitpos=$(C_BIT_POS),reduced-phys-bits=1,svsm=on \
-smp 8 \
-no-reboot \
-drive if=pflash,format=raw,unit=0,file=$(OVMF)/OVMF_CODE.fd,readonly=on \
-drive if=pflash,format=raw,unit=1,file=$(OVMF)/OVMF_VARS.fd,snapshot=on \
-drive if=pflash,format=raw,unit=2,file=./svsm.bin,readonly=on \
-nographic \
-monitor none \
-serial stdio \
-device isa-debug-exit,iobase=0xf4,iosize=0x04 || true

utils/gen_meta: utils/gen_meta.c
cc -O3 -Wall -o $@ $<

Expand All @@ -36,6 +62,10 @@ stage1/kernel.elf:
cargo build ${CARGO_ARGS} --bin svsm
objcopy -O elf64-x86-64 --strip-unneeded ${KERNEL_ELF} $@

stage1/test-kernel.elf:
LINK_TEST=1 cargo +nightly test --config 'target.x86_64-unknown-none.runner=["sh", "-c", "cp $$0 ${TEST_KERNEL_ELF}"]'
objcopy -O elf64-x86-64 --strip-unneeded ${TEST_KERNEL_ELF} stage1/kernel.elf

stage1/svsm-fs.bin:
ifneq ($(FS_FILE), none)
cp -f $(FS_FILE) stage1/svsm-fs.bin
Expand All @@ -57,4 +87,4 @@ clean:
cargo clean
rm -f stage1/stage2.bin svsm.bin stage1/meta.bin stage1/kernel.elf stage1/stage1 stage1/svsm-fs.bin ${STAGE1_OBJS} utils/gen_meta utils/print-meta

.PHONY: stage1/stage2.bin stage1/kernel.elf svsm.bin clean stage1/svsm-fs.bin test
.PHONY: stage1/stage2.bin stage1/kernel.elf stage1/test-kernel.elf svsm.bin clean stage1/svsm-fs.bin test test-in-svsm
10 changes: 10 additions & 0 deletions build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,14 @@ fn main() {
println!("cargo:rustc-link-arg-bin=svsm=--no-relax");
println!("cargo:rustc-link-arg-bin=svsm=-Tsvsm.lds");
println!("cargo:rustc-link-arg-bin=svsm=-no-pie");

// Extra linker args for tests.
println!("cargo:rerun-if-env-changed=LINK_TEST");
if std::env::var("LINK_TEST").is_ok() {
println!("cargo:rustc-link-arg=-nostdlib");
println!("cargo:rustc-link-arg=--build-id=none");
println!("cargo:rustc-link-arg=--no-relax");
println!("cargo:rustc-link-arg=-Tsvsm.lds");
println!("cargo:rustc-link-arg=-no-pie");
}
}
22 changes: 22 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,16 @@
// Author: Nicolai Stange <[email protected]>

#![no_std]
#![cfg_attr(all(test, target_os = "none"), no_main)]
#![cfg_attr(all(test, target_os = "none"), feature(custom_test_frameworks))]
#![cfg_attr(
all(test, target_os = "none"),
test_runner(crate::testing::svsm_test_runner)
)]
#![cfg_attr(
all(test, target_os = "none"),
reexport_test_harness_main = "test_main"
)]

pub mod acpi;
pub mod address;
Expand Down Expand Up @@ -33,3 +43,15 @@ pub mod utils;

#[test]
fn test_nop() {}

// When running tests inside the SVSM:
// Build the kernel entrypoint.
#[cfg(all(test, target_os = "none"))]
#[path = "svsm.rs"]
pub mod svsm_bin;
// The kernel expects to access this crate as svsm, so reexport.
#[cfg(all(test, target_os = "none"))]
extern crate self as svsm;
// Include a module containing the test runner.
#[cfg(all(test, target_os = "none"))]
pub mod testing;
7 changes: 5 additions & 2 deletions src/svsm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
//
// Author: Joerg Roedel <[email protected]>

#![no_std]
#![no_main]
#![cfg_attr(not(test), no_std)]
#![cfg_attr(not(test), no_main)]

extern crate alloc;

Expand Down Expand Up @@ -470,6 +470,9 @@ pub extern "C" fn svsm_main() {
panic!("Failed to launch FW: {:#?}", e);
}

#[cfg(test)]
crate::test_main();

request_loop();

panic!("Road ends here!");
Expand Down
33 changes: 33 additions & 0 deletions src/testing.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
use log::info;
use test::ShouldPanic;

use crate::sev::msr_protocol::request_termination_msr;

pub fn svsm_test_runner(test_cases: &[&test::TestDescAndFn]) {
info!("running {} tests", test_cases.len());
for mut test_case in test_cases.iter().copied().copied() {
if test_case.desc.should_panic == ShouldPanic::Yes {
test_case.desc.ignore = true;
test_case
.desc
.ignore_message
.get_or_insert("#[should_panic] not supported");
}

if test_case.desc.ignore {
if let Some(message) = test_case.desc.ignore_message {
info!("test {} ... ignored, {message}", test_case.desc.name.0);
} else {
info!("test {} ... ignored", test_case.desc.name.0);
}
continue;
}

info!("test {} ...", test_case.desc.name.0);
(test_case.testfn.0)();
}

info!("All tests passed!");

request_termination_msr();
}

0 comments on commit 773b3b2

Please sign in to comment.