[PATCH] test/qtest/launchupdate: add another test to exercise load and set host igvm

Ani Sinha posted 1 patch 1 week, 4 days ago
Failed in applying to current master (apply log)
tests/qtest/launchupdate-test.c | 245 +++++++++++++++++++++++++++-----
1 file changed, 210 insertions(+), 35 deletions(-)
[PATCH] test/qtest/launchupdate: add another test to exercise load and set host igvm
Posted by Ani Sinha 1 week, 4 days ago
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 <anisinha@redhat.com>
---
 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
Re: [PATCH] test/qtest/launchupdate: add another test to exercise load and set host igvm
Posted by Ani Sinha 1 week, 4 days ago
On Sat, Aug 15, 2026 at 9:59 AM Ani Sinha <anisinha@redhat.com> 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 <anisinha@redhat.com>
> ---
>  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
>
Re: [PATCH] test/qtest/launchupdate: add another test to exercise load and set host igvm
Posted by Ani Sinha 1 week, 2 days ago

> On 15 Aug 2026, at 10:39 AM, Ani Sinha <anisinha@redhat.com> wrote:
> 
> On Sat, Aug 15, 2026 at 9:59 AM Ani Sinha <anisinha@redhat.com> 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 <anisinha@redhat.com>
>> ---
>> 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
>> 
Re: [PATCH] test/qtest/launchupdate: add another test to exercise load and set host igvm
Posted by Ani Sinha 1 week, 2 days ago

> On 17 Aug 2026, at 12:13 PM, Ani Sinha <anisinha@redhat.com> wrote:
> 
> 
> 
>> On 15 Aug 2026, at 10:39 AM, Ani Sinha <anisinha@redhat.com> wrote:
>> 
>> On Sat, Aug 15, 2026 at 9:59 AM Ani Sinha <anisinha@redhat.com> 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
> 

Squashed into patchset https://lists.gnu.org/archive/html/qemu-devel/2026-08/msg03368.html


>> 
>>> 
>>> Signed-off-by: Ani Sinha <anisinha@redhat.com>