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
>


Reply via email to