> 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
>> 


Reply via email to