[PATCH] test/qtest/launchupdate: add another test to exercise load and set host igvm
Ani Sinha <[email protected]>
| Newsgroups | org.nongnu.qemu-devel |
|---|---|
| Message-ID | <[email protected]> |
A new test is added that loads a new IGVM bundle and sets host igvm ctrl at the same time. The expected behavior is that first reset will load the passed IGVM. The second reset will restore the original host provided IGVM. This test exercizes this functionality. Some refactoring is also done at the same time to re-use common operations from within multiple test routines. Signed-off-by: Ani Sinha <[email protected]> --- tests/qtest/launchupdate-test.c | 245 +++++++++++++++++++++++++++----- 1 file changed, 210 insertions(+), 35 deletions(-) diff --git a/tests/qtest/launchupdate-test.c b/tests/qtest/launchupdate-test.c index 89a117dfe4..de780eac62 100644 --- a/tests/qtest/launchupdate-test.c +++ b/tests/qtest/launchupdate-test.c @@ -248,6 +248,68 @@ static int wait_for_match(char *serial_f, return ret; } +static void set_test_params(const char **igvm_f, const char **igvm_init, + const char **snp, const char **cgs) +{ + if (confidential) { + *snp = "-object \'{\"qom-type\":\"sev-snp-guest\",\"id\":\"lsec0\"," + "\"cbitpos\":51,\"reduced-phys-bits\":1,\"policy\":196608}\'"; + *cgs = "confidential-guest-support=lsec0"; + /* + * The following two IGVM files can be built from the source + * present in https://gitlab.com/anisinha/virt-firmware-rs . + * Typing 'make' from the top of this repository will build the + * IGVM files for both confidential and + * non-confidential tests. The IGVM files for the non-coco + * case has been checked-in into the QEMU repository for + * convenience and easy CI pipeline testing. + */ + *igvm_f = "tests/data/igvm/snptest.igvm"; /* prints 'hello world' */ + *igvm_init = "tests/data/igvm/snptest-nohello.igvm"; + } else { + *igvm_f = "tests/data/igvm/hello.igvm"; + *igvm_init = "tests/data/igvm/qemuinit.igvm"; + *snp = ""; + *cgs = ""; + } + + return; +} + +static void set_expected_out(const char **exp_out, const char **exp_out2, + const char **exp_out3) +{ + *exp_out = "Hello world!"; + *exp_out2 = "Test succeeded!"; + *exp_out3 = "boot process complete with initial igvm"; + + return; +} + +static QOSState *set_qemu_args(const char *cgs, const char *tp, char *serialf, + const char *igvm_init, const char *snp) +{ + QOSState *qs; + + if (tp) { + qs = qtest_pc_boot("-machine q35,igvm-cfg=igvm0,%s -m 1G -accel kvm " + "-device vm-launch-update %s " + "-chardev file,id=serial0,path=%s " + "-serial chardev:serial0 " + "-object igvm-cfg,id=igvm0,file=%s %s", + cgs, tp, serialf, igvm_init, snp); + } else { + qs = qtest_pc_boot("-machine q35,igvm-cfg=igvm0,%s -m 1G -accel kvm " + "-device vm-launch-update " + "-chardev file,id=serial0,path=%s " + "-serial chardev:serial0 " + "-object igvm-cfg,id=igvm0,file=%s %s", + cgs, serialf, igvm_init, snp); + } + + return qs; +} + static void test_load_igvm(void) { const char *igvm_f; @@ -255,9 +317,9 @@ static void test_load_igvm(void) int ser_fd; g_autofree void *igvm_blob = NULL; g_autofree char *serialtmp = NULL; - const char exp_out[] = "Hello world!"; - const char exp_out2[] = "Test succeeded!"; - const char exp_out3[] = "boot process complete with initial igvm"; + const char *exp_out; + const char *exp_out2; + const char *exp_out3; const char *tracepoints = "--trace memory_region_finalize " "--trace qigvm_cleanup_memory -D /tmp/qemu-debug.log "; const char *snp, *cgs; @@ -268,35 +330,13 @@ static void test_load_igvm(void) QOSState *qs; VMLaunchUpdate launch_update; - if (confidential) { - snp = "-object \'{\"qom-type\":\"sev-snp-guest\",\"id\":\"lsec0\"," - "\"cbitpos\":51,\"reduced-phys-bits\":1,\"policy\":196608}\'"; - cgs = "confidential-guest-support=lsec0"; - /* - * The following two IGVM files can be built from the source - * present in https://gitlab.com/anisinha/virt-firmware-rs . - * Typing 'make' from the top of this repository will build the - * IGVM files for both confidential and - * non-confidential tests. The IGVM files for the non-coco - * case has been checked-in into the QEMU repository for - * convenience and easy CI pipeline testing. - */ - igvm_f = "tests/data/igvm/snptest.igvm"; /* - * this prints 'hello world' - * on console - */ - igvm_init = "tests/data/igvm/snptest-nohello.igvm"; - } else { - igvm_f = "tests/data/igvm/hello.igvm"; - igvm_init = "tests/data/igvm/qemuinit.igvm"; - snp = ""; - cgs = ""; - } - if (!trace) { tracepoints = ""; } + set_test_params(&igvm_f, &igvm_init, &snp, &cgs); + set_expected_out(&exp_out, &exp_out2, &exp_out3); + if (!g_file_test(igvm_f, G_FILE_TEST_EXISTS) || !g_file_test(igvm_init, G_FILE_TEST_EXISTS)) { g_test_skip("igvm file bundle(s) does not exist!"); @@ -326,12 +366,7 @@ static void test_load_igvm(void) fprintf(stderr, "serial console file is %s\n", serialtmp); } - qs = qtest_pc_boot("-machine q35,igvm-cfg=igvm0,%s -m 1G -accel kvm " - "-device vm-launch-update %s " - "-chardev file,id=serial0,path=%s " - "-serial chardev:serial0 " - "-object igvm-cfg,id=igvm0,file=%s %s", - cgs, tracepoints, serialtmp, igvm_init, snp); + qs = set_qemu_args(cgs, tracepoints, serialtmp, igvm_init, snp); fw_cfg = pc_fw_cfg_init(qs->qts); @@ -342,6 +377,11 @@ static void test_load_igvm(void) /* exp_out3 should be printed once from initial boot */ g_assert_true(wait_for_match(serialtmp, exp_out3, WAIT_SEC, 1) == 0); + + if (debug) { + fprintf(stderr, "initially booted with host igvm\n"); + } + g_assert_true(load_image(igvm_f, &igvm_blob, &igvm_sz) == igvm_sz); /* create a data buffer in guest memory */ @@ -389,7 +429,7 @@ static void test_load_igvm(void) /* check if VM_LAUNCHUPDATE_CTL_HOST_IGVM function works */ - /* set VM_LAUNCHUPDATE_CTL_HOST_IGVM control */ + /* set only VM_LAUNCHUPDATE_CTL_HOST_IGVM control without IGVM bundle */ memset(&launch_update, 0, sizeof(launch_update)); launch_update.control |= VM_LAUNCHUPDATE_CTL_HOST_IGVM; @@ -410,6 +450,139 @@ static void test_load_igvm(void) */ g_assert_true(wait_for_match(serialtmp, exp_out3, WAIT_SEC, 2) == 0); + if (debug) { + fprintf(stderr, "booted with host igvm again\n"); + } + + close(ser_fd); + guest_free(&qs->alloc, gaddr); + pc_fw_cfg_uninit(fw_cfg); + /* qtest_quit() kils QEMU, first by sending SIGTERM, then SIGKILL */ + qtest_quit(qs->qts); +} + +static void test_set_ctrl_once_and_reset_to_host_igvm(void) +{ + const char *igvm_f; + const char *igvm_init; + int ser_fd; + g_autofree void *igvm_blob = NULL; + g_autofree char *serialtmp = NULL; + const char *exp_out; + const char *exp_out2; + const char *exp_out3; + const char *snp, *cgs; + uint64_t gaddr; + size_t igvm_sz; + size_t filesize; + QFWCFG *fw_cfg; + QOSState *qs; + VMLaunchUpdate launch_update; + + set_test_params(&igvm_f, &igvm_init, &snp, &cgs); + set_expected_out(&exp_out, &exp_out2, &exp_out3); + + if (!g_file_test(igvm_f, G_FILE_TEST_EXISTS) || + !g_file_test(igvm_init, G_FILE_TEST_EXISTS)) { + g_test_skip("igvm file bundle(s) does not exist!"); + return; + } + + if (!qtest_has_machine("q35")) { + g_test_skip("q35 machine not available"); + return; + } + + if (!qtest_has_accel("kvm")) { + g_test_skip("No KVM accelerator available"); + return; + } + + if (!qtest_has_device("vm-launch-update")) { + g_test_skip("Device vm-launch-update is not available"); + return; + } + + ser_fd = g_file_open_tmp("launchupdate-qtest-serial-sXXXXXX", + &serialtmp, NULL); + g_assert_true(ser_fd != -1); + + if (debug) { + fprintf(stderr, "serial console file is %s\n", serialtmp); + } + + qs = set_qemu_args(cgs, NULL, serialtmp, igvm_init, snp); + + fw_cfg = pc_fw_cfg_init(qs->qts); + + g_assert_true(wait_for_match(serialtmp, exp_out3, WAIT_SEC, 1) == 0); + + if (debug) { + fprintf(stderr, "initially booted with host igvm\n"); + } + + g_assert_true(load_image(igvm_f, &igvm_blob, &igvm_sz) == igvm_sz); + + /* create a data buffer in guest memory */ + gaddr = guest_alloc(&qs->alloc, igvm_sz); + + if (debug) { + fprintf(stderr, "guest paddr: %"PRIx64 " igvm size: %lu\n", + gaddr, igvm_sz); + } + + if (debug) { + fprintf(stderr, "writing igvm file into the guest memory\n"); + } + + qtest_bufwrite(qs->qts, gaddr, igvm_blob, igvm_sz); + + if (debug) { + fprintf(stderr, + "tell hypervisor where igvm is loaded in guest memory\n"); + } + + /* now tell hypervisor where we loaded the bios */ + memset(&launch_update, 0, sizeof(launch_update)); + launch_update.fw_image_size = cpu_to_le64(igvm_sz); + launch_update.fw_image_addr = cpu_to_le64(gaddr); + + /* set both host ctrl and format_igvm ctrl once */ + launch_update.control |= VM_LAUNCHUPDATE_FORMAT_IGVM; + launch_update.control |= VM_LAUNCHUPDATE_CTL_HOST_IGVM; + + filesize = qfw_cfg_write_file(fw_cfg, qs, FILE_VMLAUNCHUPDATE, + &launch_update, sizeof(launch_update)); + g_assert_cmpint(filesize, ==, sizeof(launch_update)); + + if (debug) { + fprintf(stderr, "resetting the virtual machine. This should load " + "user provided igvm.\n"); + } + + qtest_system_reset(qs->qts); + + /* expected string should be printed on the console */ + g_assert_true(wait_for_match(serialtmp, exp_out, WAIT_SEC, 1) == 0); + g_assert_true(wait_for_match(serialtmp, exp_out2, WAIT_SEC, 1) == 0); + + if (debug) { + fprintf(stderr, "hello world found on console\n"); + fprintf(stderr, "Now resetting again in order to reset to host igvm\n"); + } + + qtest_system_reset(qs->qts); + + /* + * exp_out3 should be printed twice, once from initial boot, + * once from restoring host igvm. + */ + g_assert_true(wait_for_match(serialtmp, exp_out3, WAIT_SEC, 2) == 0); + + if (debug) { + fprintf(stderr, "booted with host igvm\n"); + } + close(ser_fd); guest_free(&qs->alloc, gaddr); pc_fw_cfg_uninit(fw_cfg); @@ -435,6 +608,8 @@ int main(int argc, char **argv) g_test_add_func("/vm-launch-update/errorcheck", check_error); g_test_add_func("/vm-launch-update/load_igvm", test_load_igvm); + g_test_add_func("/vm-launch-update/ctrl_set_once", + test_set_ctrl_once_and_reset_to_host_igvm); if (getenv("LAUNCHUPDATE_DEBUG")) { debug = true; -- 2.54.0