On Sat, Aug 15, 2026 at 9:59 AM Ani Sinha <[email protected]> wrote: > > 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.
This applies on top of the v5 patchset here https://lists.gnu.org/archive/html/qemu-devel/2026-08/msg02509.html > > 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 >
