> On 15 Aug 2026, at 10:39 AM, Ani Sinha <[email protected]> wrote:
>
> 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
Tested this patch on CoCo and the test runs fine there …
serial console file is /tmp/launchupdate-qtest-serial-sKLAJU3
# starting QEMU: exec ./qemu-system-x86_64 -qtest unix:/tmp/qtest-896542.sock
-qtest-log /dev/null -chardev socket,path=/tmp/qtest-896542.qmp,id=char0
-object monitor-qmp,id=qmp0,chardev=char0 -display none -audio none -run-with
exit-with-parent=on -machine
q35,igvm-cfg=igvm0,confidential-guest-support=lsec0 -m 1G -accel kvm -device
vm-launch-update -chardev
file,id=serial0,path=/tmp/launchupdate-qtest-serial-sKLAJU3 -serial
chardev:serial0 -object
igvm-cfg,id=igvm0,file=tests/data/igvm/snptest-nohello.igvm -object
'{"qom-type":"sev-snp-guest","id":"lsec0","cbitpos":51,"reduced-phys-bits":1,"policy":196608}'
-accel qtest
initially booted with host igvm
guest paddr: 100000 igvm size: 195048
writing igvm file into the guest memory
tell hypervisor where igvm is loaded in guest memory
qemu-system-x86_64: info: vmlaunchupdate: new IGVM context set.
resetting the virtual machine. This should load user provided igvm.
qemu-system-x86_64: info: virtual machine state has been rebuilt with new guest
file handle.
qemu-system-x86_64: info: vmlaunchupdate: next reset will use host igvm
hello world found on console
Now resetting again in order to reset to host igvm
qemu-system-x86_64: info: virtual machine state has been rebuilt with new guest
file handle.
qemu-system-x86_64: info: restoring original host IGVM:
tests/data/igvm/snptest-nohello.igvm
qemu-system-x86_64: info: vmlaunchupdate: host IGVM context set.
booted with host igvm
ok 5 /vm-launch-update/ctrl_set_once
# slow test /vm-launch-update/ctrl_set_once executed in 4.81 secs
# End of vm-launch-update tests
>
>>
>> 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
>>