qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v3 00/15] Introduce support for IGVM files
@ 2024-06-21 14:29 Roy Hopkins
  2024-06-21 14:29 ` [PATCH v3 01/15] meson: Add optional dependency on IGVM library Roy Hopkins
                   ` (15 more replies)
  0 siblings, 16 replies; 39+ messages in thread
From: Roy Hopkins @ 2024-06-21 14:29 UTC (permalink / raw)
  To: qemu-devel
  Cc: Roy Hopkins, Paolo Bonzini, Daniel P . Berrangé,
	Stefano Garzarella, Marcelo Tosatti, Michael S . Tsirkin,
	Cornelia Huck, Marcel Apfelbaum, Sergio Lopez, Eduardo Habkost,
	Alistair Francis, Peter Xu, David Hildenbrand, Igor Mammedov,
	Tom Lendacky, Michael Roth, Ani Sinha, Jörg Roedel

Based-on: 02d9c38236

Here is v3 of the set of patches to add support for IGVM files to QEMU.

Firstly, apologies for the long gap between v2 and v3. This was due to a number
of factors, but particularly holding back until SEV-SNP support landed in QEMU
as well as for some changes to be merged in the upstream IGVM specification and
library. The delay meant that I could include the SEV-SNP IGVM changes that I
had been separately maintaining for COCONUT-SVSM into this series, giving full
support for the full range of SEV technologies.

Thank-you to everyone who reviewed the previous set of patches [1]. I
have hopefully addressed all of the comments in those reviews. Some of these
changes required a reasonable amount of rework. Along with the inclusion of
support for SEV-SNP, this has resulted in a fairly large set of differences from
v2. This v3 patch series is also available on github: [2]

For testing IGVM support in QEMU you need to generate an IGVM file that is
configured for the platform you want to launch. I have updated the `buildigvm`
test tool [3] to allow generation of IGVM files for all currently supported
platforms. 

In my own testing I have launched guests using IGVM files for each supported
platform. In addition, I have verified that the launch measurement for SEV,
SEV-ES and SEV-SNP when using QEMU with IGVM matches the pre-calculated 
measurement using the COCONUT-SVSM `igvmmeasure` tool [4]. This same tool
was used to sign the SEV-SNP IGVM file to verify the correct operation of
the new support for ID_BLOCKs in v3.

This patch series requires version v0.3.2 of the IGVM library to be installed
[5].

Changes in v3:

* Added support for SEV-SNP on top of SEV and SEV-ES.
* Introduced a new `IgvmCfg` user accessible object to configure and process the
  IGVM file, moving this from `ConfidentialGuestSupport` where it resided in v2.
  `ConfidentialGuestSupport` is still used to abstract the implementation of
  guest configuration.
* The IGVM processing code will use the `ConfidentialGuestSupport` functions if
  provided, but will allow processing of a supported subset of directives if a
  CGS instance is not provided, allowing non-confidential guest to be configured
  with an IGVM file.
* Added support for setting platform policy via the IGVM file.
* Added ID_BLOCK support as part of SEV-SNP for using the platform to verify the
  measurement and signature of the IGVM file.
* Update documentation to describe configuring IGVM using the `igvm-cfg` object
  and referring to it from `-machine`.
* Pre-processing of the IGVM file during KVM initialization to determine the
  `sev_features` to pass to the new KVM_SEV_INIT2 ioctl.
* Tidying/rework of code as per previous review comments. In particular, the
  IGVM library dependency has been updated to remove potential namespace
  clashes.

Patch summary:

The patches have been organized so the first patches in the series mostly match
those in v2 but with additional support for SEV-SNP and native platforms.
This hopefully simplifies the review process for those that have already looked
at the previous series.

1-10: Add support and documentation for processing IGVM files for SEV, SEV-ES,
SEV-SNP and native platforms. 

11-14: Processing of policy and SEV-SNP ID_BLOCK from IGVM file. 

15: Add pre-processing of IGVM file to support synchronization of 'SEV_FEATURES'
from IGVM VMSA to KVM.

[1] Link to v2:
https://lore.kernel.org/qemu-devel/cover.1712138654.git.roy.hopkins@suse.com/

[2] v3 patches also available here:
https://github.com/roy-hopkins/qemu/tree/igvm_master_v3

[3] `buildigvm` tool v0.2.0
https://github.com/roy-hopkins/buildigvm/releases/tag/v0.2.0

[4] `igvmmeasure` tool 
https://github.com/coconut-svsm/svsm/tree/main/igvmmeasure

[5] IGVM library v0.3.2
https://github.com/microsoft/igvm/releases/tag/igvm-v0.3.2

Roy Hopkins (15):
  meson: Add optional dependency on IGVM library
  backends/confidential-guest-support: Add functions to support IGVM
  backends/igvm: Add IGVM loader and configuration
  hw/core/machine: Add igvm-cfg object and processing for IGVM files
  i386/pc_sysfw: Ensure sysfw flash configuration does not conflict with
    IGVM
  sev: Update launch_update_data functions to use Error handling
  i386/sev: Refactor setting of reset vector and initial CPU state
  i386/sev: Implement ConfidentialGuestSupport functions for SEV
  docs/system: Add documentation on support for IGVM
  docs/interop/firmware.json: Add igvm to FirmwareDevice
  backends/confidential-guest-support: Add set_guest_policy() function
  backends/igvm: Process initialization sections in IGVM file
  backends/igvm: Handle policy for SEV guests
  i386/sev: Add implementation of CGS set_guest_policy()
  sev: Provide sev_features flags from IGVM VMSA to KVM_SEV_INIT2

 docs/interop/firmware.json                 |   9 +-
 docs/system/i386/amd-memory-encryption.rst |   2 +
 docs/system/igvm.rst                       | 157 ++++
 docs/system/index.rst                      |   1 +
 meson.build                                |   8 +
 qapi/qom.json                              |  16 +
 backends/igvm.h                            |  37 +
 include/exec/confidential-guest-support.h  |  96 +++
 include/hw/boards.h                        |   2 +
 include/sysemu/igvm-cfg.h                  |  54 ++
 target/i386/sev.h                          | 124 +++
 backends/confidential-guest-support.c      |  43 +
 backends/igvm-cfg.c                        |  66 ++
 backends/igvm.c                            | 948 +++++++++++++++++++++
 hw/core/machine.c                          |  20 +
 hw/i386/pc_sysfw.c                         |  23 +-
 target/i386/sev.c                          | 830 ++++++++++++++++--
 backends/meson.build                       |   5 +
 meson_options.txt                          |   2 +
 qemu-options.hx                            |  25 +
 scripts/meson-buildoptions.sh              |   3 +
 21 files changed, 2393 insertions(+), 78 deletions(-)
 create mode 100644 docs/system/igvm.rst
 create mode 100644 backends/igvm.h
 create mode 100644 include/sysemu/igvm-cfg.h
 create mode 100644 backends/igvm-cfg.c
 create mode 100644 backends/igvm.c

-- 
2.43.0



^ permalink raw reply	[flat|nested] 39+ messages in thread

* [PATCH v3 01/15] meson: Add optional dependency on IGVM library
  2024-06-21 14:29 [PATCH v3 00/15] Introduce support for IGVM files Roy Hopkins
@ 2024-06-21 14:29 ` Roy Hopkins
  2024-06-21 14:29 ` [PATCH v3 02/15] backends/confidential-guest-support: Add functions to support IGVM Roy Hopkins
                   ` (14 subsequent siblings)
  15 siblings, 0 replies; 39+ messages in thread
From: Roy Hopkins @ 2024-06-21 14:29 UTC (permalink / raw)
  To: qemu-devel
  Cc: Roy Hopkins, Paolo Bonzini, Daniel P . Berrangé,
	Stefano Garzarella, Marcelo Tosatti, Michael S . Tsirkin,
	Cornelia Huck, Marcel Apfelbaum, Sergio Lopez, Eduardo Habkost,
	Alistair Francis, Peter Xu, David Hildenbrand, Igor Mammedov,
	Tom Lendacky, Michael Roth, Ani Sinha, Jörg Roedel

The IGVM library allows Independent Guest Virtual Machine files to be
parsed and processed. IGVM files are used to configure guest memory
layout, initial processor state and other configuration pertaining to
secure virtual machines.

This adds the --enable-igvm configure option, enabled by default, which
attempts to locate and link against the IGVM library via pkgconfig and
sets CONFIG_IGVM if found.

The library is added to the system_ss target in backends/meson.build
where the IGVM parsing will be performed by the ConfidentialGuestSupport
object.

Signed-off-by: Roy Hopkins <roy.hopkins@suse.com>
---
 meson.build                   | 8 ++++++++
 backends/meson.build          | 3 +++
 meson_options.txt             | 2 ++
 scripts/meson-buildoptions.sh | 3 +++
 4 files changed, 16 insertions(+)

diff --git a/meson.build b/meson.build
index 97e00d6f59..4252f1cf97 100644
--- a/meson.build
+++ b/meson.build
@@ -1251,6 +1251,12 @@ if host_os == 'linux' and (have_system or have_tools)
                        method: 'pkg-config',
                        required: get_option('libudev'))
 endif
+igvm = not_found
+if not get_option('igvm').auto() or have_system
+  igvm = dependency('igvm',
+                       method: 'pkg-config',
+                       required: get_option('igvm'))
+endif
 
 mpathlibs = [libudev]
 mpathpersist = not_found
@@ -2368,6 +2374,7 @@ config_host_data.set('CONFIG_CFI', get_option('cfi'))
 config_host_data.set('CONFIG_SELINUX', selinux.found())
 config_host_data.set('CONFIG_XEN_BACKEND', xen.found())
 config_host_data.set('CONFIG_LIBDW', libdw.found())
+config_host_data.set('CONFIG_IGVM', igvm.found())
 if xen.found()
   # protect from xen.version() having less than three components
   xen_version = xen.version().split('.') + ['0', '0']
@@ -4460,6 +4467,7 @@ summary_info += {'seccomp support':   seccomp}
 summary_info += {'GlusterFS support': glusterfs}
 summary_info += {'hv-balloon support': hv_balloon}
 summary_info += {'TPM support':       have_tpm}
+summary_info += {'IGVM support':      igvm}
 summary_info += {'libssh support':    libssh}
 summary_info += {'lzo support':       lzo}
 summary_info += {'snappy support':    snappy}
diff --git a/backends/meson.build b/backends/meson.build
index 8b2b111497..d550ac19f7 100644
--- a/backends/meson.build
+++ b/backends/meson.build
@@ -30,5 +30,8 @@ if have_vhost_user_crypto
 endif
 system_ss.add(when: gio, if_true: files('dbus-vmstate.c'))
 system_ss.add(when: 'CONFIG_SGX', if_true: files('hostmem-epc.c'))
+if igvm.found()
+  system_ss.add(igvm)
+endif
 
 subdir('tpm')
diff --git a/meson_options.txt b/meson_options.txt
index 7a79dd8970..9ceacca4ed 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -111,6 +111,8 @@ option('dbus_display', type: 'feature', value: 'auto',
        description: '-display dbus support')
 option('tpm', type : 'feature', value : 'auto',
        description: 'TPM support')
+option('igvm', type: 'feature', value: 'auto',
+       description: 'Independent Guest Virtual Machine (IGVM) file support')
 
 # Do not enable it by default even for Mingw32, because it doesn't
 # work on Wine.
diff --git a/scripts/meson-buildoptions.sh b/scripts/meson-buildoptions.sh
index 58d49a447d..d7d4b033e2 100644
--- a/scripts/meson-buildoptions.sh
+++ b/scripts/meson-buildoptions.sh
@@ -127,6 +127,7 @@ meson_options_help() {
   printf "%s\n" '  hv-balloon      hv-balloon driver (requires Glib 2.68+ GTree API)'
   printf "%s\n" '  hvf             HVF acceleration support'
   printf "%s\n" '  iconv           Font glyph conversion support'
+  printf "%s\n" '  igvm            IGVM file support'
   printf "%s\n" '  jack            JACK sound support'
   printf "%s\n" '  keyring         Linux keyring support'
   printf "%s\n" '  kvm             KVM acceleration support'
@@ -344,6 +345,8 @@ _meson_option_parse() {
     --iasl=*) quote_sh "-Diasl=$2" ;;
     --enable-iconv) printf "%s" -Diconv=enabled ;;
     --disable-iconv) printf "%s" -Diconv=disabled ;;
+    --enable-igvm) printf "%s" -Digvm=enabled ;;
+    --disable-igvm) printf "%s" -Digvm=disabled ;;
     --includedir=*) quote_sh "-Dincludedir=$2" ;;
     --enable-install-blobs) printf "%s" -Dinstall_blobs=true ;;
     --disable-install-blobs) printf "%s" -Dinstall_blobs=false ;;
-- 
2.43.0



^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH v3 02/15] backends/confidential-guest-support: Add functions to support IGVM
  2024-06-21 14:29 [PATCH v3 00/15] Introduce support for IGVM files Roy Hopkins
  2024-06-21 14:29 ` [PATCH v3 01/15] meson: Add optional dependency on IGVM library Roy Hopkins
@ 2024-06-21 14:29 ` Roy Hopkins
  2024-06-21 14:29 ` [PATCH v3 03/15] backends/igvm: Add IGVM loader and configuration Roy Hopkins
                   ` (13 subsequent siblings)
  15 siblings, 0 replies; 39+ messages in thread
From: Roy Hopkins @ 2024-06-21 14:29 UTC (permalink / raw)
  To: qemu-devel
  Cc: Roy Hopkins, Paolo Bonzini, Daniel P . Berrangé,
	Stefano Garzarella, Marcelo Tosatti, Michael S . Tsirkin,
	Cornelia Huck, Marcel Apfelbaum, Sergio Lopez, Eduardo Habkost,
	Alistair Francis, Peter Xu, David Hildenbrand, Igor Mammedov,
	Tom Lendacky, Michael Roth, Ani Sinha, Jörg Roedel

In preparation for supporting the processing of IGVM files to configure
guests, this adds a set of functions to ConfidentialGuestSupport
allowing configuration of secure virtual machines that can be
implemented for each supported isolation platform type such as Intel TDX
or AMD SEV-SNP. These functions will be called by IGVM processing code
in subsequent patches.

This commit provides a default implementation of the functions that
either perform no action or generate an error when they are called.
Targets that support ConfidentalGuestSupport should override these
implementations.

Signed-off-by: Roy Hopkins <roy.hopkins@suse.com>
---
 include/exec/confidential-guest-support.h | 75 +++++++++++++++++++++++
 backends/confidential-guest-support.c     | 31 ++++++++++
 2 files changed, 106 insertions(+)

diff --git a/include/exec/confidential-guest-support.h b/include/exec/confidential-guest-support.h
index 02dc4e518f..4834efbe38 100644
--- a/include/exec/confidential-guest-support.h
+++ b/include/exec/confidential-guest-support.h
@@ -21,6 +21,7 @@
 #ifndef CONFIG_USER_ONLY
 
 #include "qom/object.h"
+#include "exec/hwaddr.h"
 
 #define TYPE_CONFIDENTIAL_GUEST_SUPPORT "confidential-guest-support"
 OBJECT_DECLARE_TYPE(ConfidentialGuestSupport,
@@ -28,6 +29,36 @@ OBJECT_DECLARE_TYPE(ConfidentialGuestSupport,
                     CONFIDENTIAL_GUEST_SUPPORT)
 
 
+typedef enum ConfidentialGuestPlatformType {
+    CGS_PLATFORM_SEV,
+    CGS_PLATFORM_SEV_ES,
+    CGS_PLATFORM_SEV_SNP,
+} ConfidentialGuestPlatformType;
+
+typedef enum ConfidentialGuestMemoryType {
+    CGS_MEM_RAM,
+    CGS_MEM_RESERVED,
+    CGS_MEM_ACPI,
+    CGS_MEM_NVS,
+    CGS_MEM_UNUSABLE,
+} ConfidentialGuestMemoryType;
+
+typedef struct ConfidentialGuestMemoryMapEntry {
+    uint64_t gpa;
+    uint64_t size;
+    ConfidentialGuestMemoryType type;
+} ConfidentialGuestMemoryMapEntry;
+
+typedef enum ConfidentialGuestPageType {
+    CGS_PAGE_TYPE_NORMAL,
+    CGS_PAGE_TYPE_VMSA,
+    CGS_PAGE_TYPE_ZERO,
+    CGS_PAGE_TYPE_UNMEASURED,
+    CGS_PAGE_TYPE_SECRETS,
+    CGS_PAGE_TYPE_CPUID,
+    CGS_PAGE_TYPE_REQUIRED_MEMORY,
+} ConfidentialGuestPageType;
+
 struct ConfidentialGuestSupport {
     Object parent;
 
@@ -66,6 +97,40 @@ typedef struct ConfidentialGuestSupportClass {
 
     int (*kvm_init)(ConfidentialGuestSupport *cgs, Error **errp);
     int (*kvm_reset)(ConfidentialGuestSupport *cgs, Error **errp);
+
+    /*
+     * Check for to see if this confidential guest supports a particular
+     * platform or configuration
+     */
+    int (*check_support)(ConfidentialGuestPlatformType platform,
+                         uint16_t platform_version, uint8_t highest_vtl,
+                         uint64_t shared_gpa_boundary);
+
+    /*
+     * Configure part of the state of a guest for a particular set of data, page
+     * type and gpa. This can be used for example to pre-populate and measure
+     * guest memory contents, define private ranges or set the initial CPU state
+     * for one or more CPUs.
+     *
+     * If memory_type is CGS_PAGE_TYPE_VMSA then ptr points to the initial CPU
+     * context for a virtual CPU. The format of the data depends on the type of
+     * confidential virtual machine. For example, for SEV-ES ptr will point to a
+     * vmcb_save_area structure that should be copied into guest memory at the
+     * address specified in gpa. The cpu_index parameter contains the index of
+     * the CPU the VMSA applies to.
+     */
+    int (*set_guest_state)(hwaddr gpa, uint8_t *ptr, uint64_t len,
+                           ConfidentialGuestPageType memory_type,
+                           uint16_t cpu_index, Error **errp);
+
+    /*
+     * Iterate the system memory map, getting the entry with the given index
+     * that can be populated into guest memory.
+     *
+     * Returns 0 for ok, 1 if the index is out of range and -1 on error.
+     */
+    int (*get_mem_map_entry)(int index, ConfidentialGuestMemoryMapEntry *entry,
+                             Error **errp);
 } ConfidentialGuestSupportClass;
 
 static inline int confidential_guest_kvm_init(ConfidentialGuestSupport *cgs,
@@ -94,6 +159,16 @@ static inline int confidential_guest_kvm_reset(ConfidentialGuestSupport *cgs,
     return 0;
 }
 
+#define CONFIDENTIAL_GUEST_SUPPORT_CLASS(klass)                \
+    OBJECT_CLASS_CHECK(ConfidentialGuestSupportClass, (klass), \
+                       TYPE_CONFIDENTIAL_GUEST_SUPPORT)
+#define CONFIDENTIAL_GUEST_SUPPORT(obj)           \
+    OBJECT_CHECK(ConfidentialGuestSupport, (obj), \
+                 TYPE_CONFIDENTIAL_GUEST_SUPPORT)
+#define CONFIDENTIAL_GUEST_SUPPORT_GET_CLASS(obj)          \
+    OBJECT_GET_CLASS(ConfidentialGuestSupportClass, (obj), \
+                     TYPE_CONFIDENTIAL_GUEST_SUPPORT)
+
 #endif /* !CONFIG_USER_ONLY */
 
 #endif /* QEMU_CONFIDENTIAL_GUEST_SUPPORT_H */
diff --git a/backends/confidential-guest-support.c b/backends/confidential-guest-support.c
index 052fde8db0..68e6fd9d18 100644
--- a/backends/confidential-guest-support.c
+++ b/backends/confidential-guest-support.c
@@ -14,14 +14,45 @@
 #include "qemu/osdep.h"
 
 #include "exec/confidential-guest-support.h"
+#include "qapi/error.h"
 
 OBJECT_DEFINE_ABSTRACT_TYPE(ConfidentialGuestSupport,
                             confidential_guest_support,
                             CONFIDENTIAL_GUEST_SUPPORT,
                             OBJECT)
 
+static int check_support(ConfidentialGuestPlatformType platform,
+                         uint16_t platform_version, uint8_t highest_vtl,
+                         uint64_t shared_gpa_boundary)
+{
+    /* Default: no support. */
+    return 0;
+}
+
+static int set_guest_state(hwaddr gpa, uint8_t *ptr, uint64_t len,
+                           ConfidentialGuestPageType memory_type,
+                           uint16_t cpu_index, Error **errp)
+{
+    error_setg(errp,
+               "Setting confidential guest state is not supported for this platform");
+    return -1;
+}
+
+static int get_mem_map_entry(int index, ConfidentialGuestMemoryMapEntry *entry,
+                             Error **errp)
+{
+    error_setg(
+        errp,
+        "Obtaining the confidential guest memory map is not supported for this platform");
+    return -1;
+}
+
 static void confidential_guest_support_class_init(ObjectClass *oc, void *data)
 {
+    ConfidentialGuestSupportClass *cgsc = CONFIDENTIAL_GUEST_SUPPORT_CLASS(oc);
+    cgsc->check_support = check_support;
+    cgsc->set_guest_state = set_guest_state;
+    cgsc->get_mem_map_entry = get_mem_map_entry;
 }
 
 static void confidential_guest_support_init(Object *obj)
-- 
2.43.0



^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH v3 03/15] backends/igvm: Add IGVM loader and configuration
  2024-06-21 14:29 [PATCH v3 00/15] Introduce support for IGVM files Roy Hopkins
  2024-06-21 14:29 ` [PATCH v3 01/15] meson: Add optional dependency on IGVM library Roy Hopkins
  2024-06-21 14:29 ` [PATCH v3 02/15] backends/confidential-guest-support: Add functions to support IGVM Roy Hopkins
@ 2024-06-21 14:29 ` Roy Hopkins
  2024-06-24 13:29   ` Daniel P. Berrangé
  2024-06-27  9:06   ` Stefano Garzarella
  2024-06-21 14:29 ` [PATCH v3 04/15] hw/core/machine: Add igvm-cfg object and processing for IGVM files Roy Hopkins
                   ` (12 subsequent siblings)
  15 siblings, 2 replies; 39+ messages in thread
From: Roy Hopkins @ 2024-06-21 14:29 UTC (permalink / raw)
  To: qemu-devel
  Cc: Roy Hopkins, Paolo Bonzini, Daniel P . Berrangé,
	Stefano Garzarella, Marcelo Tosatti, Michael S . Tsirkin,
	Cornelia Huck, Marcel Apfelbaum, Sergio Lopez, Eduardo Habkost,
	Alistair Francis, Peter Xu, David Hildenbrand, Igor Mammedov,
	Tom Lendacky, Michael Roth, Ani Sinha, Jörg Roedel

Adds an IGVM loader to QEMU which processes a given IGVM file and
applies the directives within the file to the current guest
configuration.

The IGVM loader can be used to configure both confidential and
non-confidential guests. For confidential guests, the
ConfidentialGuestSupport object for the system is used to encrypt
memory, apply the initial CPU state and perform other confidential guest
operations.

The loader is configured via a new IgvmCfg QOM object which allows the
user to provide a path to the IGVM file to process.

Signed-off-by: Roy Hopkins <roy.hopkins@suse.com>
---
 qapi/qom.json             |  16 +
 backends/igvm.h           |  37 ++
 include/sysemu/igvm-cfg.h |  54 +++
 backends/igvm-cfg.c       |  66 ++++
 backends/igvm.c           | 791 ++++++++++++++++++++++++++++++++++++++
 backends/meson.build      |   2 +
 6 files changed, 966 insertions(+)
 create mode 100644 backends/igvm.h
 create mode 100644 include/sysemu/igvm-cfg.h
 create mode 100644 backends/igvm-cfg.c
 create mode 100644 backends/igvm.c

diff --git a/qapi/qom.json b/qapi/qom.json
index 8bd299265e..e586707c4c 100644
--- a/qapi/qom.json
+++ b/qapi/qom.json
@@ -874,6 +874,18 @@
   'base': 'RngProperties',
   'data': { '*filename': 'str' } }
 
+##
+# @IgvmCfgProperties:
+#
+# Properties common to objects that handle IGVM files.
+#
+# @file: IGVM file to use to configure guest (default: none)
+#
+# Since: 9.1
+##
+{ 'struct': 'IgvmCfgProperties',
+  'data': { '*file': 'str' } }
+
 ##
 # @SevCommonProperties:
 #
@@ -1039,6 +1051,8 @@
     'filter-redirector',
     'filter-replay',
     'filter-rewriter',
+    { 'name': 'igvm-cfg',
+      'if': 'CONFIG_IGVM' },
     'input-barrier',
     { 'name': 'input-linux',
       'if': 'CONFIG_LINUX' },
@@ -1111,6 +1125,8 @@
       'filter-redirector':          'FilterRedirectorProperties',
       'filter-replay':              'NetfilterProperties',
       'filter-rewriter':            'FilterRewriterProperties',
+      'igvm-cfg':                   { 'type': 'IgvmCfgProperties',
+                                      'if': 'CONFIG_IGVM' },
       'input-barrier':              'InputBarrierProperties',
       'input-linux':                { 'type': 'InputLinuxProperties',
                                       'if': 'CONFIG_LINUX' },
diff --git a/backends/igvm.h b/backends/igvm.h
new file mode 100644
index 0000000000..3a3824b391
--- /dev/null
+++ b/backends/igvm.h
@@ -0,0 +1,37 @@
+/*
+ * QEMU IGVM configuration backend for Confidential Guests
+ *
+ * Copyright (C) 2023-2024 SUSE
+ *
+ * Authors:
+ *  Roy Hopkins <roy.hopkins@suse.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#ifndef BACKENDS_IGVM_H
+#define BACKENDS_IGVM_H
+
+#include "exec/confidential-guest-support.h"
+#include "sysemu/igvm-cfg.h"
+#include "qapi/error.h"
+
+#if defined(CONFIG_IGVM)
+
+int igvm_process_file(IgvmCfgState *igvm, ConfidentialGuestSupport *cgs,
+                      Error **errp);
+
+#else
+
+static inline int igvm_process_file(IgvmCfgState *igvm,
+                                    ConfidentialGuestSupport *cgs, Error **errp)
+{
+    error_setg(
+        errp, "Invalid call to igvm_process_file when CONFIG_IGVM is disabled");
+    return -1;
+}
+
+#endif
+
+#endif
diff --git a/include/sysemu/igvm-cfg.h b/include/sysemu/igvm-cfg.h
new file mode 100644
index 0000000000..8ac8b33d8d
--- /dev/null
+++ b/include/sysemu/igvm-cfg.h
@@ -0,0 +1,54 @@
+/*
+ * QEMU IGVM interface
+ *
+ * Copyright (C) 2024 SUSE
+ *
+ * Authors:
+ *  Roy Hopkins <roy.hopkins@suse.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#ifndef QEMU_IGVM_CFG_H
+#define QEMU_IGVM_CFG_H
+
+#include "qom/object.h"
+
+typedef struct IgvmCfgState {
+    ObjectClass parent_class;
+
+    /*
+     * filename: Filename that specifies a file that contains the configuration
+     *           of the guest in Independent Guest Virtual Machine (IGVM)
+     *           format.
+     */
+    char *filename;
+} IgvmCfgState;
+
+typedef struct IgvmCfgClass {
+    ObjectClass parent_class;
+
+    /*
+     * If an IGVM filename has been specified then process the IGVM file.
+     * Performs a no-op if no filename has been specified.
+     *
+     * Returns 0 for ok and -1 on error.
+     */
+    int (*process)(IgvmCfgState *cfg, ConfidentialGuestSupport *cgs,
+                   Error **errp);
+
+} IgvmCfgClass;
+
+#define TYPE_IGVM_CFG "igvm-cfg"
+
+#define IGVM_CFG_CLASS_SUFFIX "-" TYPE_IGVM_CFG
+#define IGVM_CFG_CLASS_NAME(a) (a IGVM_CFG_CLASS_SUFFIX)
+
+#define IGVM_CFG_CLASS(klass) \
+    OBJECT_CLASS_CHECK(IgvmCfgClass, (klass), TYPE_IGVM_CFG)
+#define IGVM_CFG(obj) OBJECT_CHECK(IgvmCfgState, (obj), TYPE_IGVM_CFG)
+#define IGVM_CFG_GET_CLASS(obj) \
+    OBJECT_GET_CLASS(IgvmCfgClass, (obj), TYPE_IGVM_CFG)
+
+#endif
diff --git a/backends/igvm-cfg.c b/backends/igvm-cfg.c
new file mode 100644
index 0000000000..5e18f3fd5f
--- /dev/null
+++ b/backends/igvm-cfg.c
@@ -0,0 +1,66 @@
+/*
+ * QEMU IGVM interface
+ *
+ * Copyright (C) 2023-2024 SUSE
+ *
+ * Authors:
+ *  Roy Hopkins <roy.hopkins@suse.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+
+#include "sysemu/igvm-cfg.h"
+#include "igvm.h"
+#include "qom/object_interfaces.h"
+
+static char *get_igvm(Object *obj, Error **errp)
+{
+    IgvmCfgState *igvm = IGVM_CFG(obj);
+    return g_strdup(igvm->filename);
+}
+
+static void set_igvm(Object *obj, const char *value, Error **errp)
+{
+    IgvmCfgState *igvm = IGVM_CFG(obj);
+    g_free(igvm->filename);
+    igvm->filename = g_strdup(value);
+}
+
+static int igvm_process(IgvmCfgState *cfg, ConfidentialGuestSupport *cgs,
+                        Error **errp)
+{
+    if (!cfg->filename) {
+        return 0;
+    }
+    return igvm_process_file(cfg, cgs, errp);
+}
+
+static void igvm_cfg_class_init(ObjectClass *oc, void *data)
+{
+    IgvmCfgClass *igvmc = IGVM_CFG_CLASS(oc);
+
+    object_class_property_add_str(oc, "file", get_igvm, set_igvm);
+    object_class_property_set_description(oc, "file",
+                                          "Set the IGVM filename to use");
+
+    igvmc->process = igvm_process;
+}
+
+static const TypeInfo igvm_cfg_type = {
+    .name = TYPE_IGVM_CFG,
+    .parent = TYPE_OBJECT,
+    .class_init = igvm_cfg_class_init,
+    .class_size = sizeof(IgvmCfgClass),
+    .instance_size = sizeof(IgvmCfgState),
+    .interfaces = (InterfaceInfo[]){ { TYPE_USER_CREATABLE }, {} }
+};
+
+static void igvm_cfg_type_init(void)
+{
+    type_register_static(&igvm_cfg_type);
+}
+
+type_init(igvm_cfg_type_init);
diff --git a/backends/igvm.c b/backends/igvm.c
new file mode 100644
index 0000000000..25bbddfa33
--- /dev/null
+++ b/backends/igvm.c
@@ -0,0 +1,791 @@
+/*
+ * QEMU IGVM configuration backend for guests
+ *
+ * Copyright (C) 2023-2024 SUSE
+ *
+ * Authors:
+ *  Roy Hopkins <roy.hopkins@suse.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+
+#include "igvm.h"
+#include "qapi/error.h"
+#include "exec/memory.h"
+#include "exec/address-spaces.h"
+#include "hw/core/cpu.h"
+
+#include <igvm/igvm.h>
+#include <igvm/igvm_defs.h>
+
+typedef struct IgvmParameterData {
+    QTAILQ_ENTRY(IgvmParameterData) next;
+    uint8_t *data;
+    uint32_t size;
+    uint32_t index;
+} IgvmParameterData;
+
+/*
+ * QemuIgvm contains the information required during processing
+ * of a single IGVM file.
+ */
+typedef struct QemuIgvm {
+    IgvmHandle file;
+    ConfidentialGuestSupport *cgs;
+    ConfidentialGuestSupportClass *cgsc;
+    uint32_t compatibility_mask;
+    unsigned current_header_index;
+    QTAILQ_HEAD(, IgvmParameterData) parameter_data;
+
+    /* These variables keep track of contiguous page regions */
+    IGVM_VHS_PAGE_DATA region_prev_page_data;
+    uint64_t region_start;
+    unsigned region_start_index;
+    unsigned region_last_index;
+    unsigned region_page_count;
+} QemuIgvm;
+
+static int directive_page_data(QemuIgvm *ctx, const uint8_t *header_data,
+                               Error **errp);
+static int directive_vp_context(QemuIgvm *ctx, const uint8_t *header_data,
+                                Error **errp);
+static int directive_parameter_area(QemuIgvm *ctx, const uint8_t *header_data,
+                                    Error **errp);
+static int directive_parameter_insert(QemuIgvm *ctx, const uint8_t *header_data,
+                                      Error **errp);
+static int directive_memory_map(QemuIgvm *ctx, const uint8_t *header_data,
+                                Error **errp);
+static int directive_vp_count(QemuIgvm *ctx, const uint8_t *header_data,
+                              Error **errp);
+static int directive_environment_info(QemuIgvm *ctx, const uint8_t *header_data,
+                                      Error **errp);
+static int directive_required_memory(QemuIgvm *ctx, const uint8_t *header_data,
+                                     Error **errp);
+
+struct IGVMHandler {
+    uint32_t type;
+    uint32_t section;
+    int (*handler)(QemuIgvm *ctx, const uint8_t *header_data, Error **errp);
+};
+
+static struct IGVMHandler handlers[] = {
+    { IGVM_VHT_PAGE_DATA, IGVM_HEADER_SECTION_DIRECTIVE, directive_page_data },
+    { IGVM_VHT_VP_CONTEXT, IGVM_HEADER_SECTION_DIRECTIVE,
+      directive_vp_context },
+    { IGVM_VHT_PARAMETER_AREA, IGVM_HEADER_SECTION_DIRECTIVE,
+      directive_parameter_area },
+    { IGVM_VHT_PARAMETER_INSERT, IGVM_HEADER_SECTION_DIRECTIVE,
+      directive_parameter_insert },
+    { IGVM_VHT_MEMORY_MAP, IGVM_HEADER_SECTION_DIRECTIVE,
+      directive_memory_map },
+    { IGVM_VHT_VP_COUNT_PARAMETER, IGVM_HEADER_SECTION_DIRECTIVE,
+      directive_vp_count },
+    { IGVM_VHT_ENVIRONMENT_INFO_PARAMETER, IGVM_HEADER_SECTION_DIRECTIVE,
+      directive_environment_info },
+    { IGVM_VHT_REQUIRED_MEMORY, IGVM_HEADER_SECTION_DIRECTIVE,
+      directive_required_memory },
+};
+
+static int handler(QemuIgvm *ctx, uint32_t type, Error **errp)
+{
+    size_t handler;
+    IgvmHandle header_handle;
+    const uint8_t *header_data;
+    int result;
+
+    for (handler = 0; handler < G_N_ELEMENTS(handlers); handler++) {
+        if (handlers[handler].type != type) {
+            continue;
+        }
+        header_handle = igvm_get_header(ctx->file,
+                                        handlers[handler].section,
+                                        ctx->current_header_index);
+        if (header_handle < 0) {
+            error_setg(
+                errp,
+                "IGVM file is invalid: Failed to read directive header (code: %d)",
+                (int)header_handle);
+            return -1;
+        }
+        header_data = igvm_get_buffer(ctx->file, header_handle) +
+                      sizeof(IGVM_VHS_VARIABLE_HEADER);
+        result = handlers[handler].handler(ctx, header_data, errp);
+        igvm_free_buffer(ctx->file, header_handle);
+        return result;
+    }
+    error_setg(errp,
+               "IGVM: Unknown header type encountered when processing file: "
+               "(type 0x%X)",
+               type);
+    return -1;
+}
+
+static void *igvm_prepare_memory(QemuIgvm *ctx, uint64_t addr, uint64_t size,
+                                 int region_identifier, Error **errp)
+{
+    ERRP_GUARD();
+    MemoryRegion *igvm_pages = NULL;
+    Int128 gpa_region_size;
+    MemoryRegionSection mrs =
+        memory_region_find(get_system_memory(), addr, size);
+    if (mrs.mr) {
+        if (!memory_region_is_ram(mrs.mr)) {
+            memory_region_unref(mrs.mr);
+            error_setg(
+                errp,
+                "Processing of IGVM file failed: Could not prepare memory "
+                "at address 0x%lX due to existing non-RAM region",
+                addr);
+            return NULL;
+        }
+
+        gpa_region_size = int128_make64(size);
+        if (int128_lt(mrs.size, gpa_region_size)) {
+            memory_region_unref(mrs.mr);
+            error_setg(
+                errp,
+                "Processing of IGVM file failed: Could not prepare memory "
+                "at address 0x%lX: region size exceeded",
+                addr);
+            return NULL;
+        }
+        return qemu_map_ram_ptr(mrs.mr->ram_block, mrs.offset_within_region);
+    } else {
+        /*
+         * The region_identifier is the is the index of the IGVM directive that
+         * contains the page with the lowest GPA in the region. This will
+         * generate a unique region name.
+         */
+        g_autofree char *region_name =
+            g_strdup_printf("igvm.%X", region_identifier);
+        igvm_pages = g_malloc(sizeof(*igvm_pages));
+        if (ctx->cgs && ctx->cgs->require_guest_memfd) {
+            if (!memory_region_init_ram_guest_memfd(igvm_pages, NULL,
+                                                    region_name, size, errp)) {
+                return NULL;
+            }
+        } else {
+            if (!memory_region_init_ram(igvm_pages, NULL, region_name, size,
+                                        errp)) {
+                return NULL;
+            }
+        }
+        memory_region_add_subregion(get_system_memory(), addr, igvm_pages);
+        return memory_region_get_ram_ptr(igvm_pages);
+    }
+}
+
+static int igvm_type_to_cgs_type(IgvmPageDataType memory_type, bool unmeasured,
+                                 bool zero)
+{
+    switch (memory_type) {
+    case IGVM_PAGE_DATA_TYPE_NORMAL: {
+        if (unmeasured) {
+            return CGS_PAGE_TYPE_UNMEASURED;
+        } else {
+            return zero ? CGS_PAGE_TYPE_ZERO : CGS_PAGE_TYPE_NORMAL;
+        }
+    }
+    case IGVM_PAGE_DATA_TYPE_SECRETS:
+        return CGS_PAGE_TYPE_SECRETS;
+    case IGVM_PAGE_DATA_TYPE_CPUID_DATA:
+        return CGS_PAGE_TYPE_CPUID;
+    case IGVM_PAGE_DATA_TYPE_CPUID_XF:
+        return CGS_PAGE_TYPE_CPUID;
+    default:
+        return -1;
+    }
+}
+
+static bool page_attrs_equal(IgvmHandle igvm, unsigned header_index,
+                             const IGVM_VHS_PAGE_DATA *page_1,
+                             const IGVM_VHS_PAGE_DATA *page_2)
+{
+    IgvmHandle data_handle1, data_handle2;
+
+    /*
+     * If one page has data and the other doesn't then this results in different
+     * page types: NORMAL vs ZERO.
+     */
+    data_handle1 = igvm_get_header_data(igvm, IGVM_HEADER_SECTION_DIRECTIVE,
+                                        header_index - 1);
+    data_handle2 =
+        igvm_get_header_data(igvm, IGVM_HEADER_SECTION_DIRECTIVE, header_index);
+    if ((data_handle1 == IGVMAPI_NO_DATA) &&
+        (data_handle2 != IGVMAPI_NO_DATA)) {
+        return false;
+    } else if ((data_handle1 != IGVMAPI_NO_DATA) &&
+               (data_handle2 == IGVMAPI_NO_DATA)) {
+        return false;
+    }
+    return ((*(const uint32_t *)&page_1->flags ==
+             *(const uint32_t *)&page_2->flags) &&
+            (page_1->data_type == page_2->data_type) &&
+            (page_1->compatibility_mask == page_2->compatibility_mask));
+}
+
+static int igvm_process_mem_region(QemuIgvm *ctx, unsigned start_index,
+                                   uint64_t gpa_start, unsigned page_count,
+                                   const IgvmPageDataFlags *flags,
+                                   const IgvmPageDataType page_type,
+                                   Error **errp)
+{
+    uint8_t *region;
+    IgvmHandle data_handle;
+    const void *data;
+    uint32_t data_size;
+    unsigned page_index;
+    bool zero = true;
+    const uint64_t page_size = flags->is_2mb_page ? 0x200000 : 0x1000;
+    int result;
+    int cgs_page_type;
+
+    region = igvm_prepare_memory(ctx, gpa_start, page_count * page_size,
+                                 start_index, errp);
+    if (!region) {
+        return -1;
+    }
+
+    for (page_index = 0; page_index < page_count; page_index++) {
+        data_handle = igvm_get_header_data(
+            ctx->file, IGVM_HEADER_SECTION_DIRECTIVE, page_index + start_index);
+        if (data_handle == IGVMAPI_NO_DATA) {
+            /* No data indicates a zero page */
+            memset(&region[page_index * page_size], 0, page_size);
+        } else if (data_handle < 0) {
+            error_setg(
+                errp,
+                "IGVM file contains invalid page data for directive with "
+                "index %d",
+                page_index + start_index);
+            return -1;
+        } else {
+            zero = false;
+            data_size = igvm_get_buffer_size(ctx->file, data_handle);
+            if (data_size < page_size) {
+                memset(&region[page_index * page_size], 0, page_size);
+            } else if (data_size > page_size) {
+                error_setg(errp,
+                           "IGVM file contains page data with invalid size for "
+                           "directive with index %d",
+                           page_index + start_index);
+                return -1;
+            }
+            data = igvm_get_buffer(ctx->file, data_handle);
+            memcpy(&region[page_index * page_size], data, data_size);
+            igvm_free_buffer(ctx->file, data_handle);
+        }
+    }
+
+    /*
+     * If a confidential guest support object is provided then use it to set the
+     * guest state.
+     */
+    if (ctx->cgs) {
+        cgs_page_type =
+            igvm_type_to_cgs_type(page_type, flags->unmeasured, zero);
+        if (cgs_page_type < 0) {
+            error_setg(errp,
+                       "Invalid page type in IGVM file. Directives: %d to %d, "
+                       "page type: %d",
+                       start_index, start_index + page_count, page_type);
+            return -1;
+        }
+
+        result = ctx->cgsc->set_guest_state(
+            gpa_start, region, page_size * page_count, cgs_page_type, 0, errp);
+        if (result < 0) {
+            return result;
+        }
+    }
+    return 0;
+}
+
+static int process_mem_page(QemuIgvm *ctx, const IGVM_VHS_PAGE_DATA *page_data,
+                            Error **errp)
+{
+    if (page_data) {
+        if (ctx->region_page_count == 0) {
+            ctx->region_start = page_data->gpa;
+            ctx->region_start_index = ctx->current_header_index;
+        } else {
+            if (!page_attrs_equal(ctx->file, ctx->current_header_index,
+                                  page_data, &ctx->region_prev_page_data) ||
+                ((ctx->region_prev_page_data.gpa +
+                  (ctx->region_prev_page_data.flags.is_2mb_page ? 0x200000 :
+                                                                  0x1000)) !=
+                 page_data->gpa) ||
+                (ctx->region_last_index != (ctx->current_header_index - 1))) {
+                /* End of current region */
+                if (igvm_process_mem_region(
+                        ctx, ctx->region_start_index, ctx->region_start,
+                        ctx->region_page_count,
+                        &ctx->region_prev_page_data.flags,
+                        ctx->region_prev_page_data.data_type, errp) < 0) {
+                    return -1;
+                }
+                ctx->region_page_count = 0;
+                ctx->region_start = page_data->gpa;
+                ctx->region_start_index = ctx->current_header_index;
+            }
+        }
+        memcpy(&ctx->region_prev_page_data, page_data,
+               sizeof(ctx->region_prev_page_data));
+        ctx->region_last_index = ctx->current_header_index;
+        ctx->region_page_count++;
+    } else {
+        if (ctx->region_page_count > 0) {
+            if (igvm_process_mem_region(
+                    ctx, ctx->region_start_index, ctx->region_start,
+                    ctx->region_page_count, &ctx->region_prev_page_data.flags,
+                    ctx->region_prev_page_data.data_type, errp) < 0) {
+                return -1;
+            }
+            ctx->region_page_count = 0;
+        }
+    }
+    return 0;
+}
+
+static int directive_page_data(QemuIgvm *ctx, const uint8_t *header_data,
+                               Error **errp)
+{
+    const IGVM_VHS_PAGE_DATA *page_data =
+        (const IGVM_VHS_PAGE_DATA *)header_data;
+    if (page_data->compatibility_mask & ctx->compatibility_mask) {
+        return process_mem_page(ctx, page_data, errp);
+    }
+    return 0;
+}
+
+static int directive_vp_context(QemuIgvm *ctx, const uint8_t *header_data,
+                                Error **errp)
+{
+    const IGVM_VHS_VP_CONTEXT *vp_context =
+        (const IGVM_VHS_VP_CONTEXT *)header_data;
+    IgvmHandle data_handle;
+    uint8_t *data;
+    int result;
+
+    /*
+     * If a confidential guest support object is provided then use it to set the
+     * guest state.
+     */
+    if (!ctx->cgs) {
+        error_setg(
+            errp,
+            "A VP context is present in the IGVM file but is not supported "
+            "by the current system.");
+        return -1;
+    }
+
+    if (vp_context->compatibility_mask & ctx->compatibility_mask) {
+        data_handle = igvm_get_header_data(ctx->file,
+                                           IGVM_HEADER_SECTION_DIRECTIVE,
+                                           ctx->current_header_index);
+        if (data_handle < 0) {
+            error_setg(errp, "Invalid VP context in IGVM file. Error code: %X",
+                       data_handle);
+            return -1;
+        }
+
+        data = (uint8_t *)igvm_get_buffer(ctx->file, data_handle);
+        result = ctx->cgsc->set_guest_state(
+            vp_context->gpa, data, igvm_get_buffer_size(ctx->file, data_handle),
+            CGS_PAGE_TYPE_VMSA, vp_context->vp_index, errp);
+        igvm_free_buffer(ctx->file, data_handle);
+        if (result < 0) {
+            return result;
+        }
+    }
+    return 0;
+}
+
+static int directive_parameter_area(QemuIgvm *ctx, const uint8_t *header_data,
+                                    Error **errp)
+{
+    const IGVM_VHS_PARAMETER_AREA *param_area =
+        (const IGVM_VHS_PARAMETER_AREA *)header_data;
+    IgvmParameterData *param_entry;
+
+    param_entry = g_new0(IgvmParameterData, 1);
+    param_entry->size = param_area->number_of_bytes;
+    param_entry->index = param_area->parameter_area_index;
+    param_entry->data = g_malloc0(param_entry->size);
+
+    QTAILQ_INSERT_TAIL(&ctx->parameter_data, param_entry, next);
+    return 0;
+}
+
+static int directive_parameter_insert(QemuIgvm *ctx, const uint8_t *header_data,
+                                      Error **errp)
+{
+    const IGVM_VHS_PARAMETER_INSERT *param =
+        (const IGVM_VHS_PARAMETER_INSERT *)header_data;
+    IgvmParameterData *param_entry;
+    int result;
+    void *region;
+
+    QTAILQ_FOREACH(param_entry, &ctx->parameter_data, next)
+    {
+        if (param_entry->index == param->parameter_area_index) {
+            region = igvm_prepare_memory(ctx, param->gpa, param_entry->size,
+                                         ctx->current_header_index, errp);
+            if (!region) {
+                return -1;
+            }
+            memcpy(region, param_entry->data, param_entry->size);
+            g_free(param_entry->data);
+            param_entry->data = NULL;
+
+            /*
+             * If a confidential guest support object is provided then use it to
+             * set the guest state.
+             */
+            if (ctx->cgs) {
+                result = ctx->cgsc->set_guest_state(param->gpa, region,
+                                                    param_entry->size,
+                                                    CGS_PAGE_TYPE_UNMEASURED, 0,
+                                                    errp);
+                if (result < 0) {
+                    return -1;
+                }
+            }
+        }
+    }
+    return 0;
+}
+
+static int cmp_mm_entry(const void *a, const void *b)
+{
+    const IGVM_VHS_MEMORY_MAP_ENTRY *entry_a =
+        (const IGVM_VHS_MEMORY_MAP_ENTRY *)a;
+    const IGVM_VHS_MEMORY_MAP_ENTRY *entry_b =
+        (const IGVM_VHS_MEMORY_MAP_ENTRY *)b;
+    if (entry_a->starting_gpa_page_number < entry_b->starting_gpa_page_number) {
+        return -1;
+    } else if (entry_a->starting_gpa_page_number >
+               entry_b->starting_gpa_page_number) {
+        return 1;
+    } else {
+        return 0;
+    }
+}
+
+static int directive_memory_map(QemuIgvm *ctx, const uint8_t *header_data,
+                                Error **errp)
+{
+    const IGVM_VHS_PARAMETER *param = (const IGVM_VHS_PARAMETER *)header_data;
+    IgvmParameterData *param_entry;
+    int max_entry_count;
+    int entry = 0;
+    IGVM_VHS_MEMORY_MAP_ENTRY *mm_entry;
+    ConfidentialGuestMemoryMapEntry cgmm_entry;
+    int retval = 0;
+
+    if (!ctx->cgs) {
+        error_setg(errp,
+                   "IGVM file contains a memory map but this is not supported "
+                   "by the current system.");
+        return -1;
+    }
+
+    /* Find the parameter area that should hold the memory map */
+    QTAILQ_FOREACH(param_entry, &ctx->parameter_data, next)
+    {
+        if (param_entry->index == param->parameter_area_index) {
+            max_entry_count =
+                param_entry->size / sizeof(IGVM_VHS_MEMORY_MAP_ENTRY);
+            mm_entry = (IGVM_VHS_MEMORY_MAP_ENTRY *)param_entry->data;
+
+            retval = ctx->cgsc->get_mem_map_entry(entry, &cgmm_entry, errp);
+            while (retval == 0) {
+                if (entry > max_entry_count) {
+                    error_setg(
+                        errp,
+                        "IGVM: guest memory map size exceeds parameter area defined in IGVM file");
+                    return -1;
+                }
+                mm_entry[entry].starting_gpa_page_number = cgmm_entry.gpa >> 12;
+                mm_entry[entry].number_of_pages = cgmm_entry.size >> 12;
+
+                switch (cgmm_entry.type) {
+                case CGS_MEM_RAM:
+                    mm_entry[entry].entry_type =
+                        IGVM_MEMORY_MAP_ENTRY_TYPE_MEMORY;
+                    break;
+                case CGS_MEM_RESERVED:
+                    mm_entry[entry].entry_type =
+                        IGVM_MEMORY_MAP_ENTRY_TYPE_PLATFORM_RESERVED;
+                    break;
+                case CGS_MEM_ACPI:
+                    mm_entry[entry].entry_type =
+                        IGVM_MEMORY_MAP_ENTRY_TYPE_PLATFORM_RESERVED;
+                    break;
+                case CGS_MEM_NVS:
+                    mm_entry[entry].entry_type =
+                        IGVM_MEMORY_MAP_ENTRY_TYPE_PERSISTENT;
+                    break;
+                case CGS_MEM_UNUSABLE:
+                    mm_entry[entry].entry_type =
+                        IGVM_MEMORY_MAP_ENTRY_TYPE_PLATFORM_RESERVED;
+                    break;
+                }
+                retval =
+                    ctx->cgsc->get_mem_map_entry(++entry, &cgmm_entry, errp);
+            }
+            if (retval < 0) {
+                return retval;
+            }
+            /* The entries need to be sorted */
+            qsort(mm_entry, entry, sizeof(IGVM_VHS_MEMORY_MAP_ENTRY),
+                  cmp_mm_entry);
+
+            break;
+        }
+    }
+    return 0;
+}
+
+static int directive_vp_count(QemuIgvm *ctx, const uint8_t *header_data,
+                              Error **errp)
+{
+    const IGVM_VHS_PARAMETER *param = (const IGVM_VHS_PARAMETER *)header_data;
+    IgvmParameterData *param_entry;
+    uint32_t *vp_count;
+    CPUState *cpu;
+
+    QTAILQ_FOREACH(param_entry, &ctx->parameter_data, next)
+    {
+        if (param_entry->index == param->parameter_area_index) {
+            vp_count = (uint32_t *)(param_entry->data + param->byte_offset);
+            *vp_count = 0;
+            CPU_FOREACH(cpu)
+            {
+                (*vp_count)++;
+            }
+            break;
+        }
+    }
+    return 0;
+}
+
+static int directive_environment_info(QemuIgvm *ctx, const uint8_t *header_data,
+                                      Error **errp)
+{
+    const IGVM_VHS_PARAMETER *param = (const IGVM_VHS_PARAMETER *)header_data;
+    IgvmParameterData *param_entry;
+    IgvmEnvironmentInfo *environmental_state;
+
+    QTAILQ_FOREACH(param_entry, &ctx->parameter_data, next)
+    {
+        if (param_entry->index == param->parameter_area_index) {
+            environmental_state =
+                (IgvmEnvironmentInfo *)(param_entry->data + param->byte_offset);
+            environmental_state->memory_is_shared = 1;
+            break;
+        }
+    }
+    return 0;
+}
+
+static int directive_required_memory(QemuIgvm *ctx, const uint8_t *header_data,
+                                     Error **errp)
+{
+    const IGVM_VHS_REQUIRED_MEMORY *mem =
+        (const IGVM_VHS_REQUIRED_MEMORY *)header_data;
+    uint8_t *region;
+    int result;
+
+    if (mem->compatibility_mask & ctx->compatibility_mask) {
+        region = igvm_prepare_memory(ctx, mem->gpa, mem->number_of_bytes,
+                                     ctx->current_header_index, errp);
+        if (!region) {
+            return -1;
+        }
+        if (ctx->cgs) {
+            result = ctx->cgsc->set_guest_state(mem->gpa, region,
+                                                mem->number_of_bytes,
+                                                CGS_PAGE_TYPE_REQUIRED_MEMORY,
+                                                0, errp);
+            if (result < 0) {
+                return result;
+            }
+        }
+    }
+    return 0;
+}
+
+static int supported_platform_compat_mask(QemuIgvm *ctx, Error **errp)
+{
+    int32_t header_count;
+    unsigned header_index;
+    IgvmHandle header_handle;
+    IGVM_VHS_SUPPORTED_PLATFORM *platform;
+    uint32_t compatibility_mask_sev = 0;
+    uint32_t compatibility_mask_sev_es = 0;
+    uint32_t compatibility_mask_sev_snp = 0;
+    uint32_t compatibility_mask = 0;
+
+    header_count = igvm_header_count(ctx->file, IGVM_HEADER_SECTION_PLATFORM);
+    if (header_count < 0) {
+        error_setg(errp,
+                   "Invalid platform header count in IGVM file. Error code: %X",
+                   header_count);
+        return -1;
+    }
+
+    for (header_index = 0; header_index < (unsigned)header_count;
+         header_index++) {
+        IgvmVariableHeaderType typ = igvm_get_header_type(
+            ctx->file, IGVM_HEADER_SECTION_PLATFORM, header_index);
+        if (typ == IGVM_VHT_SUPPORTED_PLATFORM) {
+            header_handle = igvm_get_header(
+                ctx->file, IGVM_HEADER_SECTION_PLATFORM, header_index);
+            if (header_handle < 0) {
+                error_setg(errp,
+                           "Invalid platform header in IGVM file. "
+                           "Index: %d, Error code: %X",
+                           header_index, header_handle);
+                return -1;
+            }
+            platform =
+                (IGVM_VHS_SUPPORTED_PLATFORM *)(igvm_get_buffer(ctx->file,
+                                                                header_handle) +
+                                                sizeof(
+                                                    IGVM_VHS_VARIABLE_HEADER));
+            if ((platform->platform_type == IGVM_PLATFORM_TYPE_SEV_ES) &&
+                ctx->cgs) {
+                if (ctx->cgsc->check_support(
+                        CGS_PLATFORM_SEV_ES, platform->platform_version,
+                        platform->highest_vtl, platform->shared_gpa_boundary)) {
+                    compatibility_mask_sev_es = platform->compatibility_mask;
+                }
+            } else if ((platform->platform_type == IGVM_PLATFORM_TYPE_SEV) &&
+                ctx->cgs) {
+                if (ctx->cgsc->check_support(
+                        CGS_PLATFORM_SEV, platform->platform_version,
+                        platform->highest_vtl, platform->shared_gpa_boundary)) {
+                    compatibility_mask_sev = platform->compatibility_mask;
+                }
+            } else if ((platform->platform_type ==
+                        IGVM_PLATFORM_TYPE_SEV_SNP) &&
+                       ctx->cgs) {
+                if (ctx->cgsc->check_support(
+                        CGS_PLATFORM_SEV_SNP, platform->platform_version,
+                        platform->highest_vtl, platform->shared_gpa_boundary)) {
+                    compatibility_mask_sev_snp = platform->compatibility_mask;
+                }
+            } else if (platform->platform_type == IGVM_PLATFORM_TYPE_NATIVE) {
+                compatibility_mask = platform->compatibility_mask;
+            }
+            igvm_free_buffer(ctx->file, header_handle);
+        }
+    }
+    /* Choose the strongest supported isolation technology */
+    if (compatibility_mask_sev_snp != 0) {
+        ctx->compatibility_mask = compatibility_mask_sev_snp;
+    } else if (compatibility_mask_sev_es != 0) {
+        ctx->compatibility_mask = compatibility_mask_sev_es;
+    } else if (compatibility_mask_sev != 0) {
+        ctx->compatibility_mask = compatibility_mask_sev;
+    } else if (compatibility_mask != 0) {
+        ctx->compatibility_mask = compatibility_mask;
+    } else {
+        error_setg(
+            errp,
+            "IGVM file does not describe a compatible supported platform");
+        return -1;
+    }
+    return 0;
+}
+
+static IgvmHandle igvm_file_init(char *filename, Error **errp)
+{
+    IgvmHandle igvm;
+    g_autofree uint8_t *buf = NULL;
+    unsigned long len;
+    g_autoptr(GError) gerr = NULL;
+
+    if (!g_file_get_contents(filename, (gchar **)&buf, &len, &gerr)) {
+        error_setg(errp, "Unable to load %s: %s", filename, gerr->message);
+        return -1;
+    }
+
+    igvm = igvm_new_from_binary(buf, len);
+    if (igvm < 0) {
+        error_setg(errp, "Unable to parse IGVM file %s: %d", filename, igvm);
+        return -1;
+    }
+    return igvm;
+}
+
+int igvm_process_file(IgvmCfgState *cfg, ConfidentialGuestSupport *cgs,
+                      Error **errp)
+{
+    int32_t header_count;
+    IgvmParameterData *parameter;
+    int retval = -1;
+    QemuIgvm ctx;
+
+    memset(&ctx, 0, sizeof(ctx));
+    ctx.file = igvm_file_init(cfg->filename, errp);
+    if (ctx.file < 0) {
+        return -1;
+    }
+
+    /*
+     * The ConfidentialGuestSupport object is optional and allows a confidential
+     * guest platform to perform extra processing, such as page measurement, on
+     * IGVM directives.
+     */
+    ctx.cgs = cgs;
+    ctx.cgsc = cgs ? CONFIDENTIAL_GUEST_SUPPORT_GET_CLASS(cgs) : NULL;
+
+    /*
+     * Check that the IGVM file provides configuration for the current
+     * platform
+     */
+    if (supported_platform_compat_mask(&ctx, errp) < 0) {
+        return -1;
+    }
+
+    header_count = igvm_header_count(ctx.file, IGVM_HEADER_SECTION_DIRECTIVE);
+    if (header_count <= 0) {
+        error_setg(
+            errp, "Invalid directive header count in IGVM file. Error code: %X",
+            header_count);
+        return -1;
+    }
+
+    QTAILQ_INIT(&ctx.parameter_data);
+
+    for (ctx.current_header_index = 0;
+         ctx.current_header_index < (unsigned)header_count;
+         ctx.current_header_index++) {
+        IgvmVariableHeaderType type = igvm_get_header_type(
+            ctx.file, IGVM_HEADER_SECTION_DIRECTIVE, ctx.current_header_index);
+        if (handler(&ctx, type, errp) < 0) {
+            goto cleanup;
+        }
+    }
+
+    /*
+     * Contiguous pages of data with compatible flags are grouped together in
+     * order to reduce the number of memory regions we create. Make sure the
+     * last group is processed with this call.
+     */
+    retval = process_mem_page(&ctx, NULL, errp);
+
+cleanup:
+    QTAILQ_FOREACH(parameter, &ctx.parameter_data, next)
+    {
+        g_free(parameter->data);
+        parameter->data = NULL;
+    }
+
+    return retval;
+}
diff --git a/backends/meson.build b/backends/meson.build
index d550ac19f7..7e07fa743b 100644
--- a/backends/meson.build
+++ b/backends/meson.build
@@ -32,6 +32,8 @@ system_ss.add(when: gio, if_true: files('dbus-vmstate.c'))
 system_ss.add(when: 'CONFIG_SGX', if_true: files('hostmem-epc.c'))
 if igvm.found()
   system_ss.add(igvm)
+  system_ss.add(files('igvm-cfg.c'), igvm)
+  system_ss.add(files('igvm.c'), igvm)
 endif
 
 subdir('tpm')
-- 
2.43.0



^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH v3 04/15] hw/core/machine: Add igvm-cfg object and processing for IGVM files
  2024-06-21 14:29 [PATCH v3 00/15] Introduce support for IGVM files Roy Hopkins
                   ` (2 preceding siblings ...)
  2024-06-21 14:29 ` [PATCH v3 03/15] backends/igvm: Add IGVM loader and configuration Roy Hopkins
@ 2024-06-21 14:29 ` Roy Hopkins
  2024-06-24 14:00   ` Daniel P. Berrangé
  2024-06-21 14:29 ` [PATCH v3 05/15] i386/pc_sysfw: Ensure sysfw flash configuration does not conflict with IGVM Roy Hopkins
                   ` (11 subsequent siblings)
  15 siblings, 1 reply; 39+ messages in thread
From: Roy Hopkins @ 2024-06-21 14:29 UTC (permalink / raw)
  To: qemu-devel
  Cc: Roy Hopkins, Paolo Bonzini, Daniel P . Berrangé,
	Stefano Garzarella, Marcelo Tosatti, Michael S . Tsirkin,
	Cornelia Huck, Marcel Apfelbaum, Sergio Lopez, Eduardo Habkost,
	Alistair Francis, Peter Xu, David Hildenbrand, Igor Mammedov,
	Tom Lendacky, Michael Roth, Ani Sinha, Jörg Roedel

An IGVM file contains configuration of guest state that should be
applied during configuration of the guest, before the guest is started.

This patch allows the user to add an igvm-cfg object to the machine
configuration that allows an IGVM file to be configured that will be
applied to the guest before it is started.

If an IGVM configuration is provided then the IGVM file is processed at
the end of the board initialization, before the state transition to
PHASE_MACHINE_INITIALIZED.

Signed-off-by: Roy Hopkins <roy.hopkins@suse.com>
---
 include/hw/boards.h |  2 ++
 hw/core/machine.c   | 20 ++++++++++++++++++++
 qemu-options.hx     | 25 +++++++++++++++++++++++++
 3 files changed, 47 insertions(+)

diff --git a/include/hw/boards.h b/include/hw/boards.h
index 73ad319d7d..4c1484ba0b 100644
--- a/include/hw/boards.h
+++ b/include/hw/boards.h
@@ -4,6 +4,7 @@
 #define HW_BOARDS_H
 
 #include "exec/memory.h"
+#include "sysemu/igvm-cfg.h"
 #include "sysemu/hostmem.h"
 #include "sysemu/blockdev.h"
 #include "qapi/qapi-types-machine.h"
@@ -382,6 +383,7 @@ struct MachineState {
     bool suppress_vmdesc;
     bool enable_graphics;
     ConfidentialGuestSupport *cgs;
+    IgvmCfgState *igvm;
     HostMemoryBackend *memdev;
     /*
      * convenience alias to ram_memdev_id backend memory region
diff --git a/hw/core/machine.c b/hw/core/machine.c
index 655d75c21f..f9f879172c 100644
--- a/hw/core/machine.c
+++ b/hw/core/machine.c
@@ -1094,6 +1094,16 @@ static void machine_class_init(ObjectClass *oc, void *data)
     object_class_property_set_description(oc, "confidential-guest-support",
                                           "Set confidential guest scheme to support");
 
+#if defined(CONFIG_IGVM)
+    object_class_property_add_link(oc, "igvm-cfg",
+                                   TYPE_IGVM_CFG,
+                                   offsetof(MachineState, igvm),
+                                   object_property_allow_set_link,
+                                   OBJ_PROP_LINK_STRONG);
+    object_class_property_set_description(oc, "igvm-cfg",
+                                          "Set IGVM configuration");
+#endif
+
     /* For compatibility */
     object_class_property_add_str(oc, "memory-encryption",
         machine_get_memory_encryption, machine_set_memory_encryption);
@@ -1582,6 +1592,16 @@ void machine_run_board_init(MachineState *machine, const char *mem_path, Error *
 
     accel_init_interfaces(ACCEL_GET_CLASS(machine->accelerator));
     machine_class->init(machine);
+
+#if defined(CONFIG_IGVM)
+    /* Apply guest state from IGVM if supplied */
+    if (machine->igvm) {
+        if (IGVM_CFG_GET_CLASS(machine->igvm)
+                ->process(machine->igvm, machine->cgs, &error_abort) == -1) {
+            return;
+        }
+    }
+#endif
     phase_advance(PHASE_MACHINE_INITIALIZED);
 }
 
diff --git a/qemu-options.hx b/qemu-options.hx
index 8ca7f34ef0..fd36390416 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -5719,6 +5719,31 @@ SRST
                  -machine ...,memory-encryption=sev0 \\
                  .....
 
+    ``-object igvm-cfg,file=file``
+        Create an IGVM configuration object that defines the initial state
+        of the guest using a file in that conforms to the Independent Guest
+        Virtual Machine (IGVM) file format.
+
+        The ``file`` parameter is used to specify the IGVM file to load.
+        When provided, the IGVM file is used to populate the initial
+        memory of the virtual machine and, depending on the platform, can
+        define the initial processor state, memory map and parameters.
+
+        The IGVM file is expected to contain the firmware for the virtual
+        machine, therefore an ``igvm-cfg`` object cannot be provided along
+        with other ways of specifying firmware, such as the ``-bios``
+        parameter on x86 machines.
+
+        e.g to launch a machine providing the firmware in an IGVM file
+
+        .. parsed-literal::
+
+             # |qemu_system_x86| \\
+                 ...... \\
+                 -object igvm-cfg,id=igvm0,file=bios.igvm \\
+                 -machine ...,igvm-cfg=igvm0 \\
+                 .....
+
     ``-object authz-simple,id=id,identity=string``
         Create an authorization object that will control access to
         network services.
-- 
2.43.0



^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH v3 05/15] i386/pc_sysfw: Ensure sysfw flash configuration does not conflict with IGVM
  2024-06-21 14:29 [PATCH v3 00/15] Introduce support for IGVM files Roy Hopkins
                   ` (3 preceding siblings ...)
  2024-06-21 14:29 ` [PATCH v3 04/15] hw/core/machine: Add igvm-cfg object and processing for IGVM files Roy Hopkins
@ 2024-06-21 14:29 ` Roy Hopkins
  2024-06-27 12:38   ` Stefano Garzarella
  2024-06-21 14:29 ` [PATCH v3 06/15] sev: Update launch_update_data functions to use Error handling Roy Hopkins
                   ` (10 subsequent siblings)
  15 siblings, 1 reply; 39+ messages in thread
From: Roy Hopkins @ 2024-06-21 14:29 UTC (permalink / raw)
  To: qemu-devel
  Cc: Roy Hopkins, Paolo Bonzini, Daniel P . Berrangé,
	Stefano Garzarella, Marcelo Tosatti, Michael S . Tsirkin,
	Cornelia Huck, Marcel Apfelbaum, Sergio Lopez, Eduardo Habkost,
	Alistair Francis, Peter Xu, David Hildenbrand, Igor Mammedov,
	Tom Lendacky, Michael Roth, Ani Sinha, Jörg Roedel

When using an IGVM file the configuration of the system firmware is
defined by IGVM directives contained in the file. In this case the user
should not configure any pflash devices.

This commit skips initialization of the ROM mode when pflash0 is not set
then checks to ensure no pflash devices have been configured when using
IGVM, exiting with an error message if this is not the case.

Signed-off-by: Roy Hopkins <roy.hopkins@suse.com>
---
 hw/i386/pc_sysfw.c | 23 +++++++++++++++++++++--
 1 file changed, 21 insertions(+), 2 deletions(-)

diff --git a/hw/i386/pc_sysfw.c b/hw/i386/pc_sysfw.c
index ef80281d28..39e94ce144 100644
--- a/hw/i386/pc_sysfw.c
+++ b/hw/i386/pc_sysfw.c
@@ -239,8 +239,13 @@ void pc_system_firmware_init(PCMachineState *pcms,
     }
 
     if (!pflash_blk[0]) {
-        /* Machine property pflash0 not set, use ROM mode */
-        x86_bios_rom_init(X86_MACHINE(pcms), "bios.bin", rom_memory, false);
+        /*
+         * Machine property pflash0 not set, use ROM mode unless using IGVM,
+         * in which case the firmware must be provided by the IGVM file.
+         */
+        if (!MACHINE(pcms)->igvm) {
+            x86_bios_rom_init(X86_MACHINE(pcms), "bios.bin", rom_memory, false);
+        }
     } else {
         if (kvm_enabled() && !kvm_readonly_mem_enabled()) {
             /*
@@ -256,6 +261,20 @@ void pc_system_firmware_init(PCMachineState *pcms,
     }
 
     pc_system_flash_cleanup_unused(pcms);
+
+    /*
+     * The user should not have specified any pflash devices when using IGVM
+     * to configure the guest.
+     */
+    if (MACHINE(pcms)->igvm) {
+        for (i = 0; i < ARRAY_SIZE(pcms->flash); i++) {
+            if (pcms->flash[i]) {
+                error_report("pflash devices cannot be configured when "
+                             "using IGVM");
+                exit(1);
+            }
+        }
+    }
 }
 
 void x86_firmware_configure(hwaddr gpa, void *ptr, int size)
-- 
2.43.0



^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH v3 06/15] sev: Update launch_update_data functions to use Error handling
  2024-06-21 14:29 [PATCH v3 00/15] Introduce support for IGVM files Roy Hopkins
                   ` (4 preceding siblings ...)
  2024-06-21 14:29 ` [PATCH v3 05/15] i386/pc_sysfw: Ensure sysfw flash configuration does not conflict with IGVM Roy Hopkins
@ 2024-06-21 14:29 ` Roy Hopkins
  2024-06-27 12:48   ` Stefano Garzarella
  2024-06-21 14:29 ` [PATCH v3 07/15] i386/sev: Refactor setting of reset vector and initial CPU state Roy Hopkins
                   ` (9 subsequent siblings)
  15 siblings, 1 reply; 39+ messages in thread
From: Roy Hopkins @ 2024-06-21 14:29 UTC (permalink / raw)
  To: qemu-devel
  Cc: Roy Hopkins, Paolo Bonzini, Daniel P . Berrangé,
	Stefano Garzarella, Marcelo Tosatti, Michael S . Tsirkin,
	Cornelia Huck, Marcel Apfelbaum, Sergio Lopez, Eduardo Habkost,
	Alistair Francis, Peter Xu, David Hildenbrand, Igor Mammedov,
	Tom Lendacky, Michael Roth, Ani Sinha, Jörg Roedel

The class function and implementations for updating launch data return
a code in case of error. In some cases an error message is generated and
in other cases, just the error return value is used.

This small refactor adds an 'Error **errp' parameter to all functions
which consistently set an error condition if a non-zero value is
returned.

Signed-off-by: Roy Hopkins <roy.hopkins@suse.com>
---
 target/i386/sev.c | 67 +++++++++++++++++++++++++----------------------
 1 file changed, 35 insertions(+), 32 deletions(-)

diff --git a/target/i386/sev.c b/target/i386/sev.c
index 30b83f1d77..1900c3d9b4 100644
--- a/target/i386/sev.c
+++ b/target/i386/sev.c
@@ -121,7 +121,8 @@ struct SevCommonStateClass {
                                        Error **errp);
     int (*launch_start)(SevCommonState *sev_common);
     void (*launch_finish)(SevCommonState *sev_common);
-    int (*launch_update_data)(SevCommonState *sev_common, hwaddr gpa, uint8_t *ptr, uint64_t len);
+    int (*launch_update_data)(SevCommonState *sev_common, hwaddr gpa,
+                              uint8_t *ptr, uint64_t len, Error **errp);
     int (*kvm_init)(ConfidentialGuestSupport *cgs, Error **errp);
 };
 
@@ -942,14 +943,17 @@ out:
     return ret;
 }
 
-static int
-sev_launch_update_data(SevCommonState *sev_common, hwaddr gpa, uint8_t *addr, uint64_t len)
+static int sev_launch_update_data(SevCommonState *sev_common, hwaddr gpa,
+                                  uint8_t *addr, uint64_t len, Error **errp)
 {
     int ret, fw_error;
     struct kvm_sev_launch_update_data update;
 
     if (!addr || !len) {
-        return 1;
+        error_setg(errp,
+                   "%s: Invalid parameters provided for updating launch data.",
+                   __func__);
+        return -1;
     }
 
     update.uaddr = (uintptr_t)addr;
@@ -958,8 +962,8 @@ sev_launch_update_data(SevCommonState *sev_common, hwaddr gpa, uint8_t *addr, ui
     ret = sev_ioctl(sev_common->sev_fd, KVM_SEV_LAUNCH_UPDATE_DATA,
                     &update, &fw_error);
     if (ret) {
-        error_report("%s: LAUNCH_UPDATE ret=%d fw_error=%d '%s'",
-                __func__, ret, fw_error, fw_error_to_str(fw_error));
+        error_setg(errp, "%s: LAUNCH_UPDATE ret=%d fw_error=%d '%s'", __func__,
+                   ret, fw_error, fw_error_to_str(fw_error));
     }
 
     return ret;
@@ -1087,9 +1091,8 @@ sev_launch_finish(SevCommonState *sev_common)
     migrate_add_blocker(&sev_mig_blocker, &error_fatal);
 }
 
-static int
-snp_launch_update_data(uint64_t gpa, void *hva,
-                       uint32_t len, int type)
+static int snp_launch_update_data(uint64_t gpa, void *hva, uint32_t len,
+                                  int type, Error **errp)
 {
     SevLaunchUpdateData *data;
 
@@ -1104,13 +1107,12 @@ snp_launch_update_data(uint64_t gpa, void *hva,
     return 0;
 }
 
-static int
-sev_snp_launch_update_data(SevCommonState *sev_common, hwaddr gpa,
-                           uint8_t *ptr, uint64_t len)
+static int sev_snp_launch_update_data(SevCommonState *sev_common, hwaddr gpa,
+                                      uint8_t *ptr, uint64_t len, Error **errp)
 {
-       int ret = snp_launch_update_data(gpa, ptr, len,
-                                         KVM_SEV_SNP_PAGE_TYPE_NORMAL);
-       return ret;
+    int ret = snp_launch_update_data(gpa, ptr, len,
+                                     KVM_SEV_SNP_PAGE_TYPE_NORMAL, errp);
+    return ret;
 }
 
 static int
@@ -1162,8 +1164,8 @@ sev_snp_cpuid_info_fill(SnpCpuidInfo *snp_cpuid_info,
     return 0;
 }
 
-static int
-snp_launch_update_cpuid(uint32_t cpuid_addr, void *hva, uint32_t cpuid_len)
+static int snp_launch_update_cpuid(uint32_t cpuid_addr, void *hva,
+                                   uint32_t cpuid_len, Error **errp)
 {
     KvmCpuidInfo kvm_cpuid_info = {0};
     SnpCpuidInfo snp_cpuid_info;
@@ -1180,26 +1182,26 @@ snp_launch_update_cpuid(uint32_t cpuid_addr, void *hva, uint32_t cpuid_len)
     } while (ret == -E2BIG);
 
     if (ret) {
-        error_report("SEV-SNP: unable to query CPUID values for CPU: '%s'",
-                     strerror(-ret));
-        return 1;
+        error_setg(errp, "SEV-SNP: unable to query CPUID values for CPU: '%s'",
+                   strerror(-ret));
+        return -1;
     }
 
     ret = sev_snp_cpuid_info_fill(&snp_cpuid_info, &kvm_cpuid_info);
     if (ret) {
-        error_report("SEV-SNP: failed to generate CPUID table information");
-        return 1;
+        error_setg(errp, "SEV-SNP: failed to generate CPUID table information");
+        return -1;
     }
 
     memcpy(hva, &snp_cpuid_info, sizeof(snp_cpuid_info));
 
     return snp_launch_update_data(cpuid_addr, hva, cpuid_len,
-                                  KVM_SEV_SNP_PAGE_TYPE_CPUID);
+                                  KVM_SEV_SNP_PAGE_TYPE_CPUID, errp);
 }
 
-static int
-snp_launch_update_kernel_hashes(SevSnpGuestState *sev_snp, uint32_t addr,
-                                void *hva, uint32_t len)
+static int snp_launch_update_kernel_hashes(SevSnpGuestState *sev_snp,
+                                           uint32_t addr, void *hva,
+                                           uint32_t len, Error **errp)
 {
     int type = KVM_SEV_SNP_PAGE_TYPE_ZERO;
     if (sev_snp->parent_obj.kernel_hashes) {
@@ -1211,7 +1213,7 @@ snp_launch_update_kernel_hashes(SevSnpGuestState *sev_snp, uint32_t addr,
                sizeof(*sev_snp->kernel_hashes_data));
         type = KVM_SEV_SNP_PAGE_TYPE_NORMAL;
     }
-    return snp_launch_update_data(addr, hva, len, type);
+    return snp_launch_update_data(addr, hva, len, type, errp);
 }
 
 static int
@@ -1249,12 +1251,14 @@ snp_populate_metadata_pages(SevSnpGuestState *sev_snp,
         }
 
         if (type == KVM_SEV_SNP_PAGE_TYPE_CPUID) {
-            ret = snp_launch_update_cpuid(desc->base, hva, desc->len);
+            ret = snp_launch_update_cpuid(desc->base, hva, desc->len,
+                                          &error_fatal);
         } else if (desc->type == SEV_DESC_TYPE_SNP_KERNEL_HASHES) {
             ret = snp_launch_update_kernel_hashes(sev_snp, desc->base, hva,
-                                                  desc->len);
+                                                  desc->len, &error_fatal);
         } else {
-            ret = snp_launch_update_data(desc->base, hva, desc->len, type);
+            ret = snp_launch_update_data(desc->base, hva, desc->len, type,
+                                         &error_fatal);
         }
 
         if (ret) {
@@ -1541,9 +1545,8 @@ sev_encrypt_flash(hwaddr gpa, uint8_t *ptr, uint64_t len, Error **errp)
     if (sev_check_state(sev_common, SEV_STATE_LAUNCH_UPDATE)) {
         int ret;
 
-        ret = klass->launch_update_data(sev_common, gpa, ptr, len);
+        ret = klass->launch_update_data(sev_common, gpa, ptr, len, errp);
         if (ret < 0) {
-            error_setg(errp, "SEV: Failed to encrypt pflash rom");
             return ret;
         }
     }
-- 
2.43.0



^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH v3 07/15] i386/sev: Refactor setting of reset vector and initial CPU state
  2024-06-21 14:29 [PATCH v3 00/15] Introduce support for IGVM files Roy Hopkins
                   ` (5 preceding siblings ...)
  2024-06-21 14:29 ` [PATCH v3 06/15] sev: Update launch_update_data functions to use Error handling Roy Hopkins
@ 2024-06-21 14:29 ` Roy Hopkins
  2024-06-21 14:29 ` [PATCH v3 08/15] i386/sev: Implement ConfidentialGuestSupport functions for SEV Roy Hopkins
                   ` (8 subsequent siblings)
  15 siblings, 0 replies; 39+ messages in thread
From: Roy Hopkins @ 2024-06-21 14:29 UTC (permalink / raw)
  To: qemu-devel
  Cc: Roy Hopkins, Paolo Bonzini, Daniel P . Berrangé,
	Stefano Garzarella, Marcelo Tosatti, Michael S . Tsirkin,
	Cornelia Huck, Marcel Apfelbaum, Sergio Lopez, Eduardo Habkost,
	Alistair Francis, Peter Xu, David Hildenbrand, Igor Mammedov,
	Tom Lendacky, Michael Roth, Ani Sinha, Jörg Roedel

When an SEV guest is started, the reset vector and state are
extracted from metadata that is contained in the firmware volume.

In preparation for using IGVM to setup the initial CPU state,
the code has been refactored to populate vmcb_save_area for each
CPU which is then applied during guest startup and CPU reset.

Signed-off-by: Roy Hopkins <roy.hopkins@suse.com>
---
 target/i386/sev.h | 110 ++++++++++++++++
 target/i386/sev.c | 323 +++++++++++++++++++++++++++++++++++++++++-----
 2 files changed, 400 insertions(+), 33 deletions(-)

diff --git a/target/i386/sev.h b/target/i386/sev.h
index 858005a119..167dd154d6 100644
--- a/target/i386/sev.h
+++ b/target/i386/sev.h
@@ -45,6 +45,116 @@ typedef struct SevKernelLoaderContext {
     size_t cmdline_size;
 } SevKernelLoaderContext;
 
+/* Save area definition for SEV-ES and SEV-SNP guests */
+struct QEMU_PACKED sev_es_save_area {
+    struct vmcb_seg es;
+    struct vmcb_seg cs;
+    struct vmcb_seg ss;
+    struct vmcb_seg ds;
+    struct vmcb_seg fs;
+    struct vmcb_seg gs;
+    struct vmcb_seg gdtr;
+    struct vmcb_seg ldtr;
+    struct vmcb_seg idtr;
+    struct vmcb_seg tr;
+    uint64_t vmpl0_ssp;
+    uint64_t vmpl1_ssp;
+    uint64_t vmpl2_ssp;
+    uint64_t vmpl3_ssp;
+    uint64_t u_cet;
+    uint8_t reserved_0xc8[2];
+    uint8_t vmpl;
+    uint8_t cpl;
+    uint8_t reserved_0xcc[4];
+    uint64_t efer;
+    uint8_t reserved_0xd8[104];
+    uint64_t xss;
+    uint64_t cr4;
+    uint64_t cr3;
+    uint64_t cr0;
+    uint64_t dr7;
+    uint64_t dr6;
+    uint64_t rflags;
+    uint64_t rip;
+    uint64_t dr0;
+    uint64_t dr1;
+    uint64_t dr2;
+    uint64_t dr3;
+    uint64_t dr0_addr_mask;
+    uint64_t dr1_addr_mask;
+    uint64_t dr2_addr_mask;
+    uint64_t dr3_addr_mask;
+    uint8_t reserved_0x1c0[24];
+    uint64_t rsp;
+    uint64_t s_cet;
+    uint64_t ssp;
+    uint64_t isst_addr;
+    uint64_t rax;
+    uint64_t star;
+    uint64_t lstar;
+    uint64_t cstar;
+    uint64_t sfmask;
+    uint64_t kernel_gs_base;
+    uint64_t sysenter_cs;
+    uint64_t sysenter_esp;
+    uint64_t sysenter_eip;
+    uint64_t cr2;
+    uint8_t reserved_0x248[32];
+    uint64_t g_pat;
+    uint64_t dbgctl;
+    uint64_t br_from;
+    uint64_t br_to;
+    uint64_t last_excp_from;
+    uint64_t last_excp_to;
+    uint8_t reserved_0x298[80];
+    uint32_t pkru;
+    uint32_t tsc_aux;
+    uint8_t reserved_0x2f0[24];
+    uint64_t rcx;
+    uint64_t rdx;
+    uint64_t rbx;
+    uint64_t reserved_0x320; /* rsp already available at 0x01d8 */
+    uint64_t rbp;
+    uint64_t rsi;
+    uint64_t rdi;
+    uint64_t r8;
+    uint64_t r9;
+    uint64_t r10;
+    uint64_t r11;
+    uint64_t r12;
+    uint64_t r13;
+    uint64_t r14;
+    uint64_t r15;
+    uint8_t reserved_0x380[16];
+    uint64_t guest_exit_info_1;
+    uint64_t guest_exit_info_2;
+    uint64_t guest_exit_int_info;
+    uint64_t guest_nrip;
+    uint64_t sev_features;
+    uint64_t vintr_ctrl;
+    uint64_t guest_exit_code;
+    uint64_t virtual_tom;
+    uint64_t tlb_id;
+    uint64_t pcpu_id;
+    uint64_t event_inj;
+    uint64_t xcr0;
+    uint8_t reserved_0x3f0[16];
+
+    /* Floating point area */
+    uint64_t x87_dp;
+    uint32_t mxcsr;
+    uint16_t x87_ftw;
+    uint16_t x87_fsw;
+    uint16_t x87_fcw;
+    uint16_t x87_fop;
+    uint16_t x87_ds;
+    uint16_t x87_cs;
+    uint64_t x87_rip;
+    uint8_t fpreg_x87[80];
+    uint8_t fpreg_xmm[256];
+    uint8_t fpreg_ymm[256];
+};
+
 #ifdef CONFIG_SEV
 bool sev_enabled(void);
 bool sev_es_enabled(void);
diff --git a/target/i386/sev.c b/target/i386/sev.c
index 1900c3d9b4..6d5f102894 100644
--- a/target/i386/sev.c
+++ b/target/i386/sev.c
@@ -49,6 +49,12 @@ OBJECT_DECLARE_TYPE(SevSnpGuestState, SevCommonStateClass, SEV_SNP_GUEST)
 /* hard code sha256 digest size */
 #define HASH_SIZE 32
 
+/* Convert between SEV-ES VMSA and SegmentCache flags/attributes */
+#define FLAGS_VMSA_TO_SEGCACHE(flags) \
+    ((((flags) & 0xff00) << 12) | (((flags) & 0xff) << 8))
+#define FLAGS_SEGCACHE_TO_VMSA(flags) \
+    ((((flags) & 0xff00) >> 8) | (((flags) & 0xf00000) >> 12))
+
 typedef struct QEMU_PACKED SevHashTableEntry {
     QemuUUID guid;
     uint16_t len;
@@ -88,6 +94,14 @@ typedef struct QEMU_PACKED SevHashTableDescriptor {
     uint32_t size;
 } SevHashTableDescriptor;
 
+typedef struct SevLaunchVmsa {
+    QTAILQ_ENTRY(SevLaunchVmsa) next;
+
+    uint16_t cpu_index;
+    uint64_t gpa;
+    struct sev_es_save_area vmsa;
+} SevLaunchVmsa;
+
 struct SevCommonState {
     X86ConfidentialGuest parent_obj;
 
@@ -106,9 +120,7 @@ struct SevCommonState {
     int sev_fd;
     SevState state;
 
-    uint32_t reset_cs;
-    uint32_t reset_ip;
-    bool reset_data_valid;
+    QTAILQ_HEAD(, SevLaunchVmsa) launch_vmsa;
 };
 
 struct SevCommonStateClass {
@@ -369,6 +381,172 @@ static struct RAMBlockNotifier sev_ram_notifier = {
     .ram_block_removed = sev_ram_block_removed,
 };
 
+static void sev_apply_cpu_context(CPUState *cpu)
+{
+    SevCommonState *sev_common = SEV_COMMON(MACHINE(qdev_get_machine())->cgs);
+    X86CPU *x86;
+    CPUX86State *env;
+    struct SevLaunchVmsa *launch_vmsa;
+
+    /* See if an initial VMSA has been provided for this CPU */
+    QTAILQ_FOREACH(launch_vmsa, &sev_common->launch_vmsa, next)
+    {
+        if (cpu->cpu_index == launch_vmsa->cpu_index) {
+            x86 = X86_CPU(cpu);
+            env = &x86->env;
+
+            /*
+             * Ideally we would provide the VMSA directly to kvm which would
+             * ensure that the resulting initial VMSA measurement which is
+             * calculated during KVM_SEV_LAUNCH_UPDATE_VMSA is calculated from
+             * exactly what we provide here. Currently this is not possible so
+             * we need to copy the parts of the VMSA structure that we currently
+             * support into the CPU state.
+             */
+            cpu_load_efer(env, launch_vmsa->vmsa.efer);
+            cpu_x86_update_cr4(env, launch_vmsa->vmsa.cr4);
+            cpu_x86_update_cr0(env, launch_vmsa->vmsa.cr0);
+            cpu_x86_update_cr3(env, launch_vmsa->vmsa.cr3);
+            env->xcr0 = launch_vmsa->vmsa.xcr0;
+            env->pat = launch_vmsa->vmsa.g_pat;
+
+            cpu_x86_load_seg_cache(
+                env, R_CS, launch_vmsa->vmsa.cs.selector,
+                launch_vmsa->vmsa.cs.base, launch_vmsa->vmsa.cs.limit,
+                FLAGS_VMSA_TO_SEGCACHE(launch_vmsa->vmsa.cs.attrib));
+            cpu_x86_load_seg_cache(
+                env, R_DS, launch_vmsa->vmsa.ds.selector,
+                launch_vmsa->vmsa.ds.base, launch_vmsa->vmsa.ds.limit,
+                FLAGS_VMSA_TO_SEGCACHE(launch_vmsa->vmsa.ds.attrib));
+            cpu_x86_load_seg_cache(
+                env, R_ES, launch_vmsa->vmsa.es.selector,
+                launch_vmsa->vmsa.es.base, launch_vmsa->vmsa.es.limit,
+                FLAGS_VMSA_TO_SEGCACHE(launch_vmsa->vmsa.es.attrib));
+            cpu_x86_load_seg_cache(
+                env, R_FS, launch_vmsa->vmsa.fs.selector,
+                launch_vmsa->vmsa.fs.base, launch_vmsa->vmsa.fs.limit,
+                FLAGS_VMSA_TO_SEGCACHE(launch_vmsa->vmsa.fs.attrib));
+            cpu_x86_load_seg_cache(
+                env, R_GS, launch_vmsa->vmsa.gs.selector,
+                launch_vmsa->vmsa.gs.base, launch_vmsa->vmsa.gs.limit,
+                FLAGS_VMSA_TO_SEGCACHE(launch_vmsa->vmsa.gs.attrib));
+            cpu_x86_load_seg_cache(
+                env, R_SS, launch_vmsa->vmsa.ss.selector,
+                launch_vmsa->vmsa.ss.base, launch_vmsa->vmsa.ss.limit,
+                FLAGS_VMSA_TO_SEGCACHE(launch_vmsa->vmsa.ss.attrib));
+
+            env->gdt.base = launch_vmsa->vmsa.gdtr.base;
+            env->gdt.limit = launch_vmsa->vmsa.gdtr.limit;
+            env->gdt.flags =
+                FLAGS_VMSA_TO_SEGCACHE(launch_vmsa->vmsa.gdtr.attrib);
+            env->idt.base = launch_vmsa->vmsa.idtr.base;
+            env->idt.limit = launch_vmsa->vmsa.idtr.limit;
+            env->idt.flags =
+                FLAGS_VMSA_TO_SEGCACHE(launch_vmsa->vmsa.idtr.attrib);
+
+            cpu_x86_load_seg_cache(
+                env, R_LDTR, launch_vmsa->vmsa.ldtr.selector,
+                launch_vmsa->vmsa.ldtr.base, launch_vmsa->vmsa.ldtr.limit,
+                FLAGS_VMSA_TO_SEGCACHE(launch_vmsa->vmsa.ldtr.attrib));
+            cpu_x86_load_seg_cache(
+                env, R_TR, launch_vmsa->vmsa.tr.selector,
+                launch_vmsa->vmsa.ldtr.base, launch_vmsa->vmsa.tr.limit,
+                FLAGS_VMSA_TO_SEGCACHE(launch_vmsa->vmsa.tr.attrib));
+
+            env->dr[6] = launch_vmsa->vmsa.dr6;
+            env->dr[7] = launch_vmsa->vmsa.dr7;
+
+            env->regs[R_EAX] = launch_vmsa->vmsa.rax;
+            env->regs[R_ECX] = launch_vmsa->vmsa.rcx;
+            env->regs[R_EDX] = launch_vmsa->vmsa.rdx;
+            env->regs[R_EBX] = launch_vmsa->vmsa.rbx;
+            env->regs[R_ESP] = launch_vmsa->vmsa.rsp;
+            env->regs[R_EBP] = launch_vmsa->vmsa.rbp;
+            env->regs[R_ESI] = launch_vmsa->vmsa.rsi;
+            env->regs[R_EDI] = launch_vmsa->vmsa.rdi;
+#ifdef TARGET_X86_64
+            env->regs[R_R8] = launch_vmsa->vmsa.r8;
+            env->regs[R_R9] = launch_vmsa->vmsa.r9;
+            env->regs[R_R10] = launch_vmsa->vmsa.r10;
+            env->regs[R_R11] = launch_vmsa->vmsa.r11;
+            env->regs[R_R12] = launch_vmsa->vmsa.r12;
+            env->regs[R_R13] = launch_vmsa->vmsa.r13;
+            env->regs[R_R14] = launch_vmsa->vmsa.r14;
+            env->regs[R_R15] = launch_vmsa->vmsa.r15;
+#endif
+            env->eip = launch_vmsa->vmsa.rip;
+            env->eflags = launch_vmsa->vmsa.rflags;
+
+            cpu_set_fpuc(env, launch_vmsa->vmsa.x87_fcw);
+            env->mxcsr = launch_vmsa->vmsa.mxcsr;
+
+            break;
+        }
+    }
+}
+
+static int sev_set_cpu_context(uint16_t cpu_index, const void *ctx,
+                               uint32_t ctx_len, hwaddr gpa, Error **errp)
+{
+    SevCommonState *sev_common = SEV_COMMON(MACHINE(qdev_get_machine())->cgs);
+    SevLaunchVmsa *launch_vmsa;
+    CPUState *cpu;
+    bool exists = false;
+
+    /*
+     * Setting the CPU context is only supported for SEV-ES and SEV-SNP. The
+     * context buffer will contain a sev_es_save_area from the Linux kernel
+     * which is defined by "Table B-4. VMSA Layout, State Save Area for SEV-ES"
+     * in the AMD64 APM, Volume 2.
+     */
+
+    if (!sev_es_enabled()) {
+        error_setg(errp, "SEV: unable to set CPU context: Not supported");
+        return -1;
+    }
+
+    if (ctx_len < sizeof(struct sev_es_save_area)) {
+        error_setg(errp, "SEV: unable to set CPU context: "
+                     "Invalid context provided");
+        return -1;
+    }
+
+    cpu = qemu_get_cpu(cpu_index);
+    if (!cpu) {
+        error_setg(errp, "SEV: unable to set CPU context for out of bounds "
+                     "CPU index %d", cpu_index);
+        return -1;
+    }
+
+    /*
+     * If the context of this VP has already been set then replace it with the
+     * new context.
+     */
+    QTAILQ_FOREACH(launch_vmsa, &sev_common->launch_vmsa, next)
+    {
+        if (cpu_index == launch_vmsa->cpu_index) {
+            launch_vmsa->gpa = gpa;
+            memcpy(&launch_vmsa->vmsa, ctx, sizeof(launch_vmsa->vmsa));
+            exists = true;
+            break;
+        }
+    }
+
+    if (!exists) {
+        /* New VP context */
+        launch_vmsa = g_new0(SevLaunchVmsa, 1);
+        memcpy(&launch_vmsa->vmsa, ctx, sizeof(launch_vmsa->vmsa));
+        launch_vmsa->cpu_index = cpu_index;
+        launch_vmsa->gpa = gpa;
+        QTAILQ_INSERT_TAIL(&sev_common->launch_vmsa, launch_vmsa, next);
+    }
+
+    /* Synchronise the VMSA with the current CPU state */
+    sev_apply_cpu_context(cpu);
+
+    return 0;
+}
+
 bool
 sev_enabled(void)
 {
@@ -973,6 +1151,16 @@ static int
 sev_launch_update_vmsa(SevGuestState *sev_guest)
 {
     int ret, fw_error;
+    CPUState *cpu;
+
+    /*
+     * The initial CPU state is measured as part of KVM_SEV_LAUNCH_UPDATE_VMSA.
+     * Synchronise the CPU state to any provided launch VMSA structures.
+     */
+    CPU_FOREACH(cpu) {
+        sev_apply_cpu_context(cpu);
+    }
+
 
     ret = sev_ioctl(SEV_COMMON(sev_guest)->sev_fd, KVM_SEV_LAUNCH_UPDATE_VMSA,
                     NULL, &fw_error);
@@ -1711,40 +1899,110 @@ sev_es_find_reset_vector(void *flash_ptr, uint64_t flash_size,
     return sev_es_parse_reset_block(info, addr);
 }
 
-void sev_es_set_reset_vector(CPUState *cpu)
+
+static void seg_to_vmsa(const SegmentCache *cpu_seg, struct vmcb_seg *vmsa_seg)
 {
-    X86CPU *x86;
-    CPUX86State *env;
-    ConfidentialGuestSupport *cgs = MACHINE(qdev_get_machine())->cgs;
-    SevCommonState *sev_common = SEV_COMMON(
-        object_dynamic_cast(OBJECT(cgs), TYPE_SEV_COMMON));
+    vmsa_seg->selector = cpu_seg->selector;
+    vmsa_seg->base = cpu_seg->base;
+    vmsa_seg->limit = cpu_seg->limit;
+    vmsa_seg->attrib = FLAGS_SEGCACHE_TO_VMSA(cpu_seg->flags);
+}
 
-    /* Only update if we have valid reset information */
-    if (!sev_common || !sev_common->reset_data_valid) {
-        return;
-    }
+static void initialize_vmsa(const CPUState *cpu, struct sev_es_save_area *vmsa)
+{
+    const X86CPU *x86 = X86_CPU(cpu);
+    const CPUX86State *env = &x86->env;
 
-    /* Do not update the BSP reset state */
-    if (cpu->cpu_index == 0) {
-        return;
+    /*
+     * Initialize the SEV-ES save area from the current state of
+     * the CPU. The entire state does not need to be copied, only the state
+     * that is copied back to the CPUState in sev_apply_cpu_context.
+     */
+    memset(vmsa, 0, sizeof(struct sev_es_save_area));
+    vmsa->efer = env->efer;
+    vmsa->cr0 = env->cr[0];
+    vmsa->cr3 = env->cr[3];
+    vmsa->cr4 = env->cr[4];
+    vmsa->xcr0 = env->xcr0;
+    vmsa->g_pat = env->pat;
+
+    seg_to_vmsa(&env->segs[R_CS], &vmsa->cs);
+    seg_to_vmsa(&env->segs[R_DS], &vmsa->ds);
+    seg_to_vmsa(&env->segs[R_ES], &vmsa->es);
+    seg_to_vmsa(&env->segs[R_FS], &vmsa->fs);
+    seg_to_vmsa(&env->segs[R_GS], &vmsa->gs);
+    seg_to_vmsa(&env->segs[R_SS], &vmsa->ss);
+
+    seg_to_vmsa(&env->gdt, &vmsa->gdtr);
+    seg_to_vmsa(&env->idt, &vmsa->idtr);
+    seg_to_vmsa(&env->ldt, &vmsa->ldtr);
+    seg_to_vmsa(&env->tr, &vmsa->tr);
+
+    vmsa->dr6 = env->dr[6];
+    vmsa->dr7 = env->dr[7];
+
+    vmsa->rax = env->regs[R_EAX];
+    vmsa->rcx = env->regs[R_ECX];
+    vmsa->rdx = env->regs[R_EDX];
+    vmsa->rbx = env->regs[R_EBX];
+    vmsa->rsp = env->regs[R_ESP];
+    vmsa->rbp = env->regs[R_EBP];
+    vmsa->rsi = env->regs[R_ESI];
+    vmsa->rdi = env->regs[R_EDI];
+
+#ifdef TARGET_X86_64
+    vmsa->r8 = env->regs[R_R8];
+    vmsa->r9 = env->regs[R_R9];
+    vmsa->r10 = env->regs[R_R10];
+    vmsa->r11 = env->regs[R_R11];
+    vmsa->r12 = env->regs[R_R12];
+    vmsa->r13 = env->regs[R_R13];
+    vmsa->r14 = env->regs[R_R14];
+    vmsa->r15 = env->regs[R_R15];
+#endif
+
+    vmsa->rip = env->eip;
+    vmsa->rflags = env->eflags;
+}
+
+static void sev_es_set_ap_context(uint32_t reset_addr)
+{
+    CPUState *cpu;
+    struct sev_es_save_area vmsa;
+    SegmentCache cs;
+
+    cs.selector = 0xf000;
+    cs.base = reset_addr & 0xffff0000;
+    cs.limit = 0xffff;
+    cs.flags = DESC_P_MASK | DESC_S_MASK | DESC_CS_MASK | DESC_R_MASK |
+               DESC_A_MASK;
+
+    CPU_FOREACH(cpu) {
+        if (cpu->cpu_index == 0) {
+            /* Do not update the BSP reset state */
+            continue;
+        }
+        initialize_vmsa(cpu, &vmsa);
+        seg_to_vmsa(&cs, &vmsa.cs);
+        vmsa.rip = reset_addr & 0x0000ffff;
+        sev_set_cpu_context(cpu->cpu_index, &vmsa,
+                            sizeof(struct sev_es_save_area),
+                            0, &error_fatal);
+        sev_apply_cpu_context(cpu);
     }
+}
 
-    x86 = X86_CPU(cpu);
-    env = &x86->env;
-
-    cpu_x86_load_seg_cache(env, R_CS, 0xf000, sev_common->reset_cs, 0xffff,
-                           DESC_P_MASK | DESC_S_MASK | DESC_CS_MASK |
-                           DESC_R_MASK | DESC_A_MASK);
-
-    env->eip = sev_common->reset_ip;
+void sev_es_set_reset_vector(CPUState *cpu)
+{
+    if (sev_enabled()) {
+        sev_apply_cpu_context(cpu);
+    }
 }
 
 int sev_es_save_reset_vector(void *flash_ptr, uint64_t flash_size)
 {
-    CPUState *cpu;
     uint32_t addr;
     int ret;
-    SevCommonState *sev_common = SEV_COMMON(MACHINE(qdev_get_machine())->cgs);
 
     if (!sev_es_enabled()) {
         return 0;
@@ -1757,14 +2015,12 @@ int sev_es_save_reset_vector(void *flash_ptr, uint64_t flash_size)
         return ret;
     }
 
+    /*
+     * The reset vector is saved into a CPU context for each AP but not for
+     * the BSP. This is applied during guest startup or when the CPU is reset.
+     */
     if (addr) {
-        sev_common->reset_cs = addr & 0xffff0000;
-        sev_common->reset_ip = addr & 0x0000ffff;
-        sev_common->reset_data_valid = true;
-
-        CPU_FOREACH(cpu) {
-            sev_es_set_reset_vector(cpu);
-        }
+        sev_es_set_ap_context(addr);
     }
 
     return 0;
@@ -1999,6 +2255,7 @@ sev_common_instance_init(Object *obj)
     object_property_add_uint32_ptr(obj, "reduced-phys-bits",
                                    &sev_common->reduced_phys_bits,
                                    OBJ_PROP_FLAG_READWRITE);
+    QTAILQ_INIT(&sev_common->launch_vmsa);
 }
 
 /* sev guest info common to sev/sev-es/sev-snp */
-- 
2.43.0



^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH v3 08/15] i386/sev: Implement ConfidentialGuestSupport functions for SEV
  2024-06-21 14:29 [PATCH v3 00/15] Introduce support for IGVM files Roy Hopkins
                   ` (6 preceding siblings ...)
  2024-06-21 14:29 ` [PATCH v3 07/15] i386/sev: Refactor setting of reset vector and initial CPU state Roy Hopkins
@ 2024-06-21 14:29 ` Roy Hopkins
  2024-06-21 14:29 ` [PATCH v3 09/15] docs/system: Add documentation on support for IGVM Roy Hopkins
                   ` (7 subsequent siblings)
  15 siblings, 0 replies; 39+ messages in thread
From: Roy Hopkins @ 2024-06-21 14:29 UTC (permalink / raw)
  To: qemu-devel
  Cc: Roy Hopkins, Paolo Bonzini, Daniel P . Berrangé,
	Stefano Garzarella, Marcelo Tosatti, Michael S . Tsirkin,
	Cornelia Huck, Marcel Apfelbaum, Sergio Lopez, Eduardo Habkost,
	Alistair Francis, Peter Xu, David Hildenbrand, Igor Mammedov,
	Tom Lendacky, Michael Roth, Ani Sinha, Jörg Roedel

The ConfidentialGuestSupport object defines a number of virtual
functions that are called during processing of IGVM directives to query
or configure initial guest state. In order to support processing of IGVM
files, these functions need to be implemented by relevant isolation
hardware support code such as SEV.

This commit implements the required functions for SEV-ES and adds
support for processing IGVM files for configuring the guest.

Signed-off-by: Roy Hopkins <roy.hopkins@suse.com>
---
 target/i386/sev.h |   2 +
 target/i386/sev.c | 250 ++++++++++++++++++++++++++++++++++++++++++++--
 2 files changed, 242 insertions(+), 10 deletions(-)

diff --git a/target/i386/sev.h b/target/i386/sev.h
index 167dd154d6..2ccd6fe1e8 100644
--- a/target/i386/sev.h
+++ b/target/i386/sev.h
@@ -34,6 +34,8 @@
 #define SEV_SNP_POLICY_SMT      0x10000
 #define SEV_SNP_POLICY_DBG      0x80000
 
+#define SVM_SEV_FEAT_SNP_ACTIVE 1
+
 typedef struct SevKernelLoaderContext {
     char *setup_data;
     size_t setup_size;
diff --git a/target/i386/sev.c b/target/i386/sev.c
index 6d5f102894..5d9ef745bb 100644
--- a/target/i386/sev.c
+++ b/target/i386/sev.c
@@ -39,8 +39,10 @@
 #include "qapi/qapi-commands-misc-target.h"
 #include "confidential-guest.h"
 #include "hw/i386/pc.h"
+#include "hw/i386/e820_memory_layout.h"
 #include "exec/address-spaces.h"
 #include "qemu/queue.h"
+#include "qemu/cutils.h"
 
 OBJECT_DECLARE_TYPE(SevCommonState, SevCommonStateClass, SEV_COMMON)
 OBJECT_DECLARE_TYPE(SevGuestState, SevCommonStateClass, SEV_GUEST)
@@ -49,6 +51,9 @@ OBJECT_DECLARE_TYPE(SevSnpGuestState, SevCommonStateClass, SEV_SNP_GUEST)
 /* hard code sha256 digest size */
 #define HASH_SIZE 32
 
+/* Hard coded GPA that KVM uses for the VMSA */
+#define KVM_VMSA_GPA 0xFFFFFFFFF000
+
 /* Convert between SEV-ES VMSA and SegmentCache flags/attributes */
 #define FLAGS_VMSA_TO_SEGCACHE(flags) \
     ((((flags) & 0xff00) << 12) | (((flags) & 0xff) << 8))
@@ -485,6 +490,103 @@ static void sev_apply_cpu_context(CPUState *cpu)
     }
 }
 
+static int check_vmsa_supported(hwaddr gpa, const struct sev_es_save_area *vmsa,
+                                Error **errp)
+{
+    struct sev_es_save_area vmsa_check;
+
+    /*
+     * KVM always populates the VMSA at a fixed GPA which cannot be modified
+     * from userspace. Specifying a different GPA will not prevent the guest
+     * from starting but will cause the launch measurement to be different
+     * from expected. Therefore check that the provided GPA matches the KVM
+     * hardcoded value.
+     */
+    if (gpa != KVM_VMSA_GPA) {
+        error_setg(errp,
+                "%s: The VMSA GPA must be %lX but is specified as %lX",
+                __func__, KVM_VMSA_GPA, gpa);
+        return -1;
+    }
+
+    /*
+     * Clear all supported fields so we can then check the entire structure
+     * is zero.
+     */
+    memcpy(&vmsa_check, vmsa, sizeof(struct sev_es_save_area));
+    memset(&vmsa_check.es, 0, sizeof(vmsa_check.es));
+    memset(&vmsa_check.cs, 0, sizeof(vmsa_check.cs));
+    memset(&vmsa_check.ss, 0, sizeof(vmsa_check.ss));
+    memset(&vmsa_check.ds, 0, sizeof(vmsa_check.ds));
+    memset(&vmsa_check.fs, 0, sizeof(vmsa_check.fs));
+    memset(&vmsa_check.gs, 0, sizeof(vmsa_check.gs));
+    memset(&vmsa_check.gdtr, 0, sizeof(vmsa_check.gdtr));
+    memset(&vmsa_check.idtr, 0, sizeof(vmsa_check.idtr));
+    memset(&vmsa_check.ldtr, 0, sizeof(vmsa_check.ldtr));
+    memset(&vmsa_check.tr, 0, sizeof(vmsa_check.tr));
+    vmsa_check.efer = 0;
+    vmsa_check.cr0 = 0;
+    vmsa_check.cr3 = 0;
+    vmsa_check.cr4 = 0;
+    vmsa_check.xcr0 = 0;
+    vmsa_check.dr6 = 0;
+    vmsa_check.dr7 = 0;
+    vmsa_check.rax = 0;
+    vmsa_check.rcx = 0;
+    vmsa_check.rdx = 0;
+    vmsa_check.rbx = 0;
+    vmsa_check.rsp = 0;
+    vmsa_check.rbp = 0;
+    vmsa_check.rsi = 0;
+    vmsa_check.rdi = 0;
+    vmsa_check.r8 = 0;
+    vmsa_check.r9 = 0;
+    vmsa_check.r10 = 0;
+    vmsa_check.r11 = 0;
+    vmsa_check.r12 = 0;
+    vmsa_check.r13 = 0;
+    vmsa_check.r14 = 0;
+    vmsa_check.r15 = 0;
+    vmsa_check.rip = 0;
+    vmsa_check.rflags = 0;
+
+    vmsa_check.g_pat = 0;
+    vmsa_check.xcr0 = 0;
+
+    vmsa_check.x87_fcw = 0;
+    vmsa_check.mxcsr = 0;
+
+    if (sev_snp_enabled()) {
+        if (vmsa_check.sev_features != SVM_SEV_FEAT_SNP_ACTIVE) {
+            error_setg(errp,
+                       "%s: sev_features in the VMSA contains an unsupported "
+                       "value. For SEV-SNP, sev_features must be set to %x.",
+                       __func__, SVM_SEV_FEAT_SNP_ACTIVE);
+            return -1;
+        }
+        vmsa_check.sev_features = 0;
+    } else {
+        if (vmsa_check.sev_features != 0) {
+            error_setg(errp,
+                       "%s: sev_features in the VMSA contains an unsupported "
+                       "value. For SEV-ES and SEV, sev_features must be "
+                       "set to 0.", __func__);
+            return -1;
+        }
+    }
+
+    if (!buffer_is_zero(&vmsa_check, sizeof(vmsa_check))) {
+        error_setg(errp,
+                "%s: The VMSA contains fields that are not "
+                "synchronized with KVM. Continuing would result in "
+                "either unpredictable guest behavior, or a "
+                "mismatched launch measurement.",
+                __func__);
+        return -1;
+    }
+    return 0;
+}
+
 static int sev_set_cpu_context(uint16_t cpu_index, const void *ctx,
                                uint32_t ctx_len, hwaddr gpa, Error **errp)
 {
@@ -1468,18 +1570,26 @@ sev_snp_launch_finish(SevCommonState *sev_common)
     struct kvm_sev_snp_launch_finish *finish = &sev_snp->kvm_finish_conf;
 
     /*
-     * To boot the SNP guest, the hypervisor is required to populate the CPUID
-     * and Secrets page before finalizing the launch flow. The location of
-     * the secrets and CPUID page is available through the OVMF metadata GUID.
+     * Populate all the metadata pages if not using an IGVM file. In the case
+     * where an IGVM file is provided it will be used to configure the metadata
+     * pages directly.
      */
-    metadata = pc_system_get_ovmf_sev_metadata_ptr();
-    if (metadata == NULL) {
-        error_report("%s: Failed to locate SEV metadata header", __func__);
-        exit(1);
-    }
+    if (!MACHINE(qdev_get_machine())->igvm) {
+        /*
+         * To boot the SNP guest, the hypervisor is required to populate the
+         * CPUID and Secrets page before finalizing the launch flow. The
+         * location of the secrets and CPUID page is available through the
+         * OVMF metadata GUID.
+         */
+        metadata = pc_system_get_ovmf_sev_metadata_ptr();
+        if (metadata == NULL) {
+            error_report("%s: Failed to locate SEV metadata header", __func__);
+            exit(1);
+        }
 
-    /* Populate all the metadata pages */
-    snp_populate_metadata_pages(sev_snp, metadata);
+        /* Populate all the metadata pages */
+        snp_populate_metadata_pages(sev_snp, metadata);
+    }
 
     QTAILQ_FOREACH(data, &launch_update, next) {
         ret = sev_snp_launch_update(sev_snp, data);
@@ -2222,6 +2332,120 @@ static void sev_common_set_kernel_hashes(Object *obj, bool value, Error **errp)
     SEV_COMMON(obj)->kernel_hashes = value;
 }
 
+static int cgs_check_support(ConfidentialGuestPlatformType platform,
+                             uint16_t platform_version, uint8_t highest_vtl,
+                             uint64_t shared_gpa_boundary)
+{
+    return (((platform == CGS_PLATFORM_SEV_SNP) && sev_snp_enabled()) ||
+            ((platform == CGS_PLATFORM_SEV_ES) && sev_es_enabled()) ||
+            ((platform == CGS_PLATFORM_SEV) && sev_enabled())) ? 1 : 0;
+}
+
+static int cgs_set_guest_state(hwaddr gpa, uint8_t *ptr, uint64_t len,
+                               ConfidentialGuestPageType memory_type,
+                               uint16_t cpu_index, Error **errp)
+{
+    SevCommonState *sev_common = SEV_COMMON(MACHINE(qdev_get_machine())->cgs);
+    SevCommonStateClass *klass = SEV_COMMON_GET_CLASS(sev_common);
+
+    if (!sev_enabled()) {
+        error_setg(errp, "%s: attempt to configure guest memory, but SEV "
+                     "is not enabled", __func__);
+        return -1;
+    }
+
+    switch (memory_type) {
+    case CGS_PAGE_TYPE_NORMAL:
+    case CGS_PAGE_TYPE_ZERO:
+        return klass->launch_update_data(sev_common, gpa, ptr, len, errp);
+
+    case CGS_PAGE_TYPE_VMSA:
+        if (!sev_es_enabled()) {
+            error_setg(errp,
+                       "%s: attempt to configure initial VMSA, but SEV-ES "
+                       "is not supported",
+                       __func__);
+            return -1;
+        }
+        if (check_vmsa_supported(gpa, (const struct sev_es_save_area *)ptr,
+                                 errp) < 0) {
+            return -1;
+        }
+        return sev_set_cpu_context(cpu_index, ptr, len, gpa, errp);
+
+    case CGS_PAGE_TYPE_UNMEASURED:
+        if (sev_snp_enabled()) {
+            return snp_launch_update_data(
+                gpa, ptr, len, KVM_SEV_SNP_PAGE_TYPE_UNMEASURED, errp);
+        }
+        /* No action required if not SEV-SNP */
+        return 0;
+
+    case CGS_PAGE_TYPE_SECRETS:
+        if (!sev_snp_enabled()) {
+            error_setg(errp,
+                       "%s: attempt to configure secrets page, but SEV-SNP "
+                       "is not supported",
+                       __func__);
+            return -1;
+        }
+        return snp_launch_update_data(gpa, ptr, len,
+                                      KVM_SEV_SNP_PAGE_TYPE_SECRETS, errp);
+
+    case CGS_PAGE_TYPE_REQUIRED_MEMORY:
+        if (kvm_convert_memory(gpa, len, true) < 0) {
+            error_setg(
+                errp,
+                "%s: failed to configure required memory. gpa: %lX, type: %d",
+                __func__, gpa, memory_type);
+            return -1;
+        }
+        return 0;
+
+    case CGS_PAGE_TYPE_CPUID:
+        if (!sev_snp_enabled()) {
+            error_setg(errp,
+                       "%s: attempt to configure CPUID page, but SEV-SNP "
+                       "is not supported",
+                       __func__);
+            return -1;
+        }
+        return snp_launch_update_cpuid(gpa, ptr, len, errp);
+    }
+    error_setg(errp, "%s: failed to update guest. gpa: %lX, type: %d", __func__,
+               gpa, memory_type);
+    return -1;
+}
+
+static int cgs_get_mem_map_entry(int index,
+                                 ConfidentialGuestMemoryMapEntry *entry,
+                                 Error **errp)
+{
+    if ((index < 0) || (index >= e820_get_num_entries())) {
+        return 1;
+    }
+    entry->gpa = e820_table[index].address;
+    entry->size = e820_table[index].length;
+    switch (e820_table[index].type) {
+    case E820_RAM:
+        entry->type = CGS_MEM_RAM;
+        break;
+    case E820_RESERVED:
+        entry->type = CGS_MEM_RESERVED;
+        break;
+    case E820_ACPI:
+        entry->type = CGS_MEM_ACPI;
+        break;
+    case E820_NVS:
+        entry->type = CGS_MEM_NVS;
+        break;
+    case E820_UNUSABLE:
+        entry->type = CGS_MEM_UNUSABLE;
+        break;
+    }
+    return 0;
+}
+
 static void
 sev_common_class_init(ObjectClass *oc, void *data)
 {
@@ -2245,6 +2469,8 @@ static void
 sev_common_instance_init(Object *obj)
 {
     SevCommonState *sev_common = SEV_COMMON(obj);
+    ConfidentialGuestSupportClass *cgs =
+        CONFIDENTIAL_GUEST_SUPPORT_GET_CLASS(obj);
 
     sev_common->kvm_type = -1;
 
@@ -2255,6 +2481,10 @@ sev_common_instance_init(Object *obj)
     object_property_add_uint32_ptr(obj, "reduced-phys-bits",
                                    &sev_common->reduced_phys_bits,
                                    OBJ_PROP_FLAG_READWRITE);
+    cgs->check_support = cgs_check_support;
+    cgs->set_guest_state = cgs_set_guest_state;
+    cgs->get_mem_map_entry = cgs_get_mem_map_entry;
+
     QTAILQ_INIT(&sev_common->launch_vmsa);
 }
 
-- 
2.43.0



^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH v3 09/15] docs/system: Add documentation on support for IGVM
  2024-06-21 14:29 [PATCH v3 00/15] Introduce support for IGVM files Roy Hopkins
                   ` (7 preceding siblings ...)
  2024-06-21 14:29 ` [PATCH v3 08/15] i386/sev: Implement ConfidentialGuestSupport functions for SEV Roy Hopkins
@ 2024-06-21 14:29 ` Roy Hopkins
  2024-06-24 14:09   ` Daniel P. Berrangé
  2024-06-21 14:29 ` [PATCH v3 10/15] docs/interop/firmware.json: Add igvm to FirmwareDevice Roy Hopkins
                   ` (6 subsequent siblings)
  15 siblings, 1 reply; 39+ messages in thread
From: Roy Hopkins @ 2024-06-21 14:29 UTC (permalink / raw)
  To: qemu-devel
  Cc: Roy Hopkins, Paolo Bonzini, Daniel P . Berrangé,
	Stefano Garzarella, Marcelo Tosatti, Michael S . Tsirkin,
	Cornelia Huck, Marcel Apfelbaum, Sergio Lopez, Eduardo Habkost,
	Alistair Francis, Peter Xu, David Hildenbrand, Igor Mammedov,
	Tom Lendacky, Michael Roth, Ani Sinha, Jörg Roedel

IGVM support has been implemented for Confidential Guests that support
AMD SEV and AMD SEV-ES. Add some documentation that gives some
background on the IGVM format and how to use it to configure a
confidential guest.

Signed-off-by: Roy Hopkins <roy.hopkins@suse.com>
---
 docs/system/i386/amd-memory-encryption.rst |   2 +
 docs/system/igvm.rst                       | 157 +++++++++++++++++++++
 docs/system/index.rst                      |   1 +
 3 files changed, 160 insertions(+)
 create mode 100644 docs/system/igvm.rst

diff --git a/docs/system/i386/amd-memory-encryption.rst b/docs/system/i386/amd-memory-encryption.rst
index 748f5094ba..6c23f3535f 100644
--- a/docs/system/i386/amd-memory-encryption.rst
+++ b/docs/system/i386/amd-memory-encryption.rst
@@ -1,3 +1,5 @@
+.. _amd-sev:
+
 AMD Secure Encrypted Virtualization (SEV)
 =========================================
 
diff --git a/docs/system/igvm.rst b/docs/system/igvm.rst
new file mode 100644
index 0000000000..b6e544a508
--- /dev/null
+++ b/docs/system/igvm.rst
@@ -0,0 +1,157 @@
+Independent Guest Virtual Machine (IGVM) support
+================================================
+
+IGVM files are designed to encapsulate all the information required to launch a
+virtual machine on any given virtualization stack in a deterministic way. This
+allows the cryptographic measurement of initial guest state for Confidential
+Guests to be calculated when the IGVM file is built, allowing a relying party to
+verify the initial state of a guest via a remote attestation.
+
+Although IGVM files are designed with Confidential Computing in mind, they can
+also be used to configure non-confidential guests. Multiple platforms can be
+defined by a single IGVM file, allowing a single IGVM file to configure a
+virtual machine that can run on, for example, TDX, SEV and non-confidential
+hosts.
+
+QEMU supports IGVM files through the user-creatable ``igvm-cfg`` object. This
+object is used to define the filename of the IGVM file to process. A reference
+to the object is added to the ``-machine`` to configure the virtual machine
+to use the IGVM file for configuration.
+
+Confidential platform support is provided through the use of
+the ``ConfidentialGuestSupport`` object. If the virtual machine provides an
+instance of this object then this is used by the IGVM loader to configure the
+isolation properties of the directives within the file.
+
+Further Information on IGVM
+---------------------------
+
+Information about the IGVM format, including links to the format specification
+and documentation for the Rust and C libraries can be found at the project
+repository:
+
+https://github.com/microsoft/igvm
+
+
+Supported Platforms
+-------------------
+
+Currently, IGVM files can be provided for Confidential Guests on host systems
+that support AMD SEV, SEV-ES and SEV-SNP with KVM. IGVM files can also be
+provided for non-confidential guests.
+
+
+Limitations when using IGVM with AMD SEV, SEV-ES and SEV-SNP
+------------------------------------------------------------
+
+IGVM files configure the initial state of the guest using a set of directives.
+Not every directive is supported by every Confidential Guest type. For example,
+AMD SEV does not support encrypted save state regions, therefore setting the
+initial CPU state using IGVM for SEV is not possible. When an IGVM file contains
+directives that are not supported for the active platform, an error is generated
+and the guest launch is aborted.
+
+The table below describes the list of directives that are supported for SEV,
+SEV-ES, SEV-SNP and non-confidential platforms.
+
+.. list-table:: SEV, SEV-ES, SEV-SNP & non-confidential Supported Directives
+   :widths: 35 65
+   :header-rows: 1
+
+   * - IGVM directive
+     - Notes
+   * - IGVM_VHT_PAGE_DATA
+     - ``NORMAL`` zero, measured and unmeasured page types are supported. Other
+       page types result in an error.
+   * - IGVM_VHT_PARAMETER_AREA
+     -
+   * - IGVM_VHT_PARAMETER_INSERT
+     -
+   * - IGVM_VHT_VP_COUNT_PARAMETER
+     - The guest parameter page is populated with the CPU count.
+   * - IGVM_VHT_ENVIRONMENT_INFO_PARAMETER
+     - The ``memory_is_shared`` parameter is set to 1 in the guest parameter
+       page.
+
+.. list-table:: Additional SEV, SEV-ES & SEV_SNP Supported Directives
+   :widths: 25 75
+   :header-rows: 1
+
+   * - IGVM directive
+     - Notes
+   * - IGVM_VHT_MEMORY_MAP
+     - The memory map page is populated using entries from the E820 table.
+   * - IGVM_VHT_REQUIRED_MEMORY
+     -
+
+.. list-table:: Additional SEV-ES & SEV-SNP Supported Directives
+   :widths: 25 75
+   :header-rows: 1
+
+   * - IGVM directive
+     - Notes
+   * - IGVM_VHT_VP_CONTEXT
+     - Setting of the initial CPU state for the boot CPU and additional CPUs is
+       supported with limitations on the fields that can be provided in the
+       VMSA. See below for details on which fields are supported.
+
+Initial CPU state with VMSA
+---------------------------
+
+The initial state of guest CPUs can be defined in the IGVM file for AMD SEV-ES
+and SEV-SNP. The state data is provided as a VMSA structure as defined in Table
+B-4 in the AMD64 Architecture Programmer's Manual, Volume 2 [1].
+
+The IGVM VMSA is translated to CPU state in QEMU which is then synchronized
+by KVM to the guest VMSA during the launch process where it contributes to the
+launch measurement. See :ref:`amd-sev` for details on the launch process and
+guest launch measurement.
+
+It is important that no information is lost or changed when translating the
+VMSA provided by the IGVM file into the VSMA that is used to launch the guest.
+Therefore, QEMU restricts the VMSA fields that can be provided in the IGVM
+VMSA structure to the following registers:
+
+RAX, RCX, RDX, RBX, RBP, RSI, RDI, R8-R15, RSP, RIP, CS, DS, ES, FS, GS, SS,
+CR0, CR3, CR4, XCR0, EFER, PAT, GDT, IDT, LDTR, TR, DR6, DR7, RFLAGS, X87_FCW,
+MXCSR.
+
+When processing the IGVM file, QEMU will check if any fields other than the
+above are non-zero and generate an error if this is the case.
+
+KVM uses a hardcoded GPA of 0xFFFFFFFFF000 for the VMSA. When an IGVM file
+defines initial CPU state, the GPA for each VMSA must match this hardcoded
+value.
+
+Firmware Images with IGVM
+-------------------------
+
+When an IGVM filename is specified for a Confidential Guest Support object it
+overrides the default handling of system firmware: the firmware image, such as
+an OVMF binary should be contained as a payload of the IGVM file and not
+provided as a flash drive or via the ``-bios`` parameter. The default QEMU
+firmware is not automatically populated into the guest memory space.
+
+If an IGVM file is provided along with either the ``-bios`` parameter or pflash
+devices then an error is displayed and the guest startup is aborted.
+
+Running a Confidential Guest configured using IGVM
+--------------------------------------------------
+
+To run a confidential guest configured with IGVM you need to add an
+``igvm-cfg`` object and refer to it from the ``-machine`` parameter:
+
+Example (for AMD SEV)::
+
+    qemu-system-x86_64 \
+        <other parameters> \
+        -machine ...,confidential-guest-support=sev0,igvm-cfg=igvm0 \
+        -object sev-guest,id=sev0,cbitpos=47,reduced-phys-bits=1 \
+        -object igvm-cfg,id=igvm0,file=/path/to/guest.igvm
+
+References
+----------
+
+[1] AMD64 Architecture Programmer's Manual, Volume 2: System Programming
+  Rev 3.41
+  https://www.amd.com/content/dam/amd/en/documents/processor-tech-docs/programmer-references/24593.pdf
\ No newline at end of file
diff --git a/docs/system/index.rst b/docs/system/index.rst
index c21065e519..6235dfab87 100644
--- a/docs/system/index.rst
+++ b/docs/system/index.rst
@@ -38,4 +38,5 @@ or Hypervisor.Framework.
    security
    multi-process
    confidential-guest-support
+   igvm
    vm-templating
-- 
2.43.0



^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH v3 10/15] docs/interop/firmware.json: Add igvm to FirmwareDevice
  2024-06-21 14:29 [PATCH v3 00/15] Introduce support for IGVM files Roy Hopkins
                   ` (8 preceding siblings ...)
  2024-06-21 14:29 ` [PATCH v3 09/15] docs/system: Add documentation on support for IGVM Roy Hopkins
@ 2024-06-21 14:29 ` Roy Hopkins
  2024-06-27 12:53   ` Stefano Garzarella
  2024-06-21 14:29 ` [PATCH v3 11/15] backends/confidential-guest-support: Add set_guest_policy() function Roy Hopkins
                   ` (5 subsequent siblings)
  15 siblings, 1 reply; 39+ messages in thread
From: Roy Hopkins @ 2024-06-21 14:29 UTC (permalink / raw)
  To: qemu-devel
  Cc: Roy Hopkins, Paolo Bonzini, Daniel P . Berrangé,
	Stefano Garzarella, Marcelo Tosatti, Michael S . Tsirkin,
	Cornelia Huck, Marcel Apfelbaum, Sergio Lopez, Eduardo Habkost,
	Alistair Francis, Peter Xu, David Hildenbrand, Igor Mammedov,
	Tom Lendacky, Michael Roth, Ani Sinha, Jörg Roedel

Create an enum entry within FirmwareDevice for 'igvm' to describe that
an IGVM file can be used to map firmware into memory as an alternative
to pre-existing firmware devices.

Signed-off-by: Roy Hopkins <roy.hopkins@suse.com>
---
 docs/interop/firmware.json | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/docs/interop/firmware.json b/docs/interop/firmware.json
index 54a1fc6c10..9a9178606e 100644
--- a/docs/interop/firmware.json
+++ b/docs/interop/firmware.json
@@ -55,10 +55,17 @@
 #
 # @memory: The firmware is to be mapped into memory.
 #
+# @igvm: The firmware is defined by a file conforming to the IGVM
+#        specification and mapped into memory according to directives
+#        defined in the file. This is similar to @memory but may
+#        include additional processing defined by the IGVM file
+#        including initial CPU state or population of metadata into
+#        the guest address space.
+#
 # Since: 3.0
 ##
 { 'enum' : 'FirmwareDevice',
-  'data' : [ 'flash', 'kernel', 'memory' ] }
+  'data' : [ 'flash', 'kernel', 'memory', 'igvm' ] }
 
 ##
 # @FirmwareTarget:
-- 
2.43.0



^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH v3 11/15] backends/confidential-guest-support: Add set_guest_policy() function
  2024-06-21 14:29 [PATCH v3 00/15] Introduce support for IGVM files Roy Hopkins
                   ` (9 preceding siblings ...)
  2024-06-21 14:29 ` [PATCH v3 10/15] docs/interop/firmware.json: Add igvm to FirmwareDevice Roy Hopkins
@ 2024-06-21 14:29 ` Roy Hopkins
  2024-06-21 14:29 ` [PATCH v3 12/15] backends/igvm: Process initialization sections in IGVM file Roy Hopkins
                   ` (4 subsequent siblings)
  15 siblings, 0 replies; 39+ messages in thread
From: Roy Hopkins @ 2024-06-21 14:29 UTC (permalink / raw)
  To: qemu-devel
  Cc: Roy Hopkins, Paolo Bonzini, Daniel P . Berrangé,
	Stefano Garzarella, Marcelo Tosatti, Michael S . Tsirkin,
	Cornelia Huck, Marcel Apfelbaum, Sergio Lopez, Eduardo Habkost,
	Alistair Francis, Peter Xu, David Hildenbrand, Igor Mammedov,
	Tom Lendacky, Michael Roth, Ani Sinha, Jörg Roedel

For confidential guests a policy can be provided that defines the
security level, debug status, expected launch measurement and other
parameters that define the configuration of the confidential platform.

This commit adds a new function named set_guest_policy() that can be
implemented by each confidential platform, such as AMD SEV to set the
policy. This will allow configuration of the policy from a
multi-platform resource such as an IGVM file without the IGVM processor
requiring specific implementation details for each platform.

Signed-off-by: Roy Hopkins <roy.hopkins@suse.com>
---
 include/exec/confidential-guest-support.h | 21 +++++++++++++++++++++
 backends/confidential-guest-support.c     | 12 ++++++++++++
 2 files changed, 33 insertions(+)

diff --git a/include/exec/confidential-guest-support.h b/include/exec/confidential-guest-support.h
index 4834efbe38..218bab9714 100644
--- a/include/exec/confidential-guest-support.h
+++ b/include/exec/confidential-guest-support.h
@@ -59,6 +59,10 @@ typedef enum ConfidentialGuestPageType {
     CGS_PAGE_TYPE_REQUIRED_MEMORY,
 } ConfidentialGuestPageType;
 
+typedef enum ConfidentialGuestPolicyType {
+    GUEST_POLICY_SEV,
+} ConfidentialGuestPolicyType;
+
 struct ConfidentialGuestSupport {
     Object parent;
 
@@ -123,6 +127,23 @@ typedef struct ConfidentialGuestSupportClass {
                            ConfidentialGuestPageType memory_type,
                            uint16_t cpu_index, Error **errp);
 
+    /*
+     * Set the guest policy. The policy can be used to configure the
+     * confidential platform, such as if debug is enabled or not and can contain
+     * information about expected launch measurements, signed verification of
+     * guest configuration and other platform data.
+     *
+     * The format of the policy data is specific to each platform. For example,
+     * SEV-SNP uses a policy bitfield in the 'policy' argument and provides an
+     * ID block and ID authentication in the 'policy_data' parameters. The type
+     * of policy data is identified by the 'policy_type' argument.
+     */
+    int (*set_guest_policy)(ConfidentialGuestPolicyType policy_type,
+                            uint64_t policy,
+                            void *policy_data1, uint32_t policy_data1_size,
+                            void *policy_data2, uint32_t policy_data2_size,
+                            Error **errp);
+
     /*
      * Iterate the system memory map, getting the entry with the given index
      * that can be populated into guest memory.
diff --git a/backends/confidential-guest-support.c b/backends/confidential-guest-support.c
index 68e6fd9d18..3c46b2cd6b 100644
--- a/backends/confidential-guest-support.c
+++ b/backends/confidential-guest-support.c
@@ -38,6 +38,17 @@ static int set_guest_state(hwaddr gpa, uint8_t *ptr, uint64_t len,
     return -1;
 }
 
+static int set_guest_policy(ConfidentialGuestPolicyType policy_type,
+                            uint64_t policy,
+                            void *policy_data1, uint32_t policy_data1_size,
+                            void *policy_data2, uint32_t policy_data2_size,
+                            Error **errp)
+{
+    error_setg(errp,
+               "Setting confidential guest policy is not supported for this platform");
+    return -1;
+}
+
 static int get_mem_map_entry(int index, ConfidentialGuestMemoryMapEntry *entry,
                              Error **errp)
 {
@@ -52,6 +63,7 @@ static void confidential_guest_support_class_init(ObjectClass *oc, void *data)
     ConfidentialGuestSupportClass *cgsc = CONFIDENTIAL_GUEST_SUPPORT_CLASS(oc);
     cgsc->check_support = check_support;
     cgsc->set_guest_state = set_guest_state;
+    cgsc->set_guest_policy = set_guest_policy;
     cgsc->get_mem_map_entry = get_mem_map_entry;
 }
 
-- 
2.43.0



^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH v3 12/15] backends/igvm: Process initialization sections in IGVM file
  2024-06-21 14:29 [PATCH v3 00/15] Introduce support for IGVM files Roy Hopkins
                   ` (10 preceding siblings ...)
  2024-06-21 14:29 ` [PATCH v3 11/15] backends/confidential-guest-support: Add set_guest_policy() function Roy Hopkins
@ 2024-06-21 14:29 ` Roy Hopkins
  2024-06-21 14:29 ` [PATCH v3 13/15] backends/igvm: Handle policy for SEV guests Roy Hopkins
                   ` (3 subsequent siblings)
  15 siblings, 0 replies; 39+ messages in thread
From: Roy Hopkins @ 2024-06-21 14:29 UTC (permalink / raw)
  To: qemu-devel
  Cc: Roy Hopkins, Paolo Bonzini, Daniel P . Berrangé,
	Stefano Garzarella, Marcelo Tosatti, Michael S . Tsirkin,
	Cornelia Huck, Marcel Apfelbaum, Sergio Lopez, Eduardo Habkost,
	Alistair Francis, Peter Xu, David Hildenbrand, Igor Mammedov,
	Tom Lendacky, Michael Roth, Ani Sinha, Jörg Roedel

The initialization sections in IGVM files contain configuration that
should be applied to the guest platform before it is started. This
includes guest policy and other information that can affect the security
level and the startup measurement of a guest.

This commit introduces handling of the initialization sections during
processing of the IGVM file.

Signed-off-by: Roy Hopkins <roy.hopkins@suse.com>
---
 backends/igvm.c | 21 +++++++++++++++++++++
 1 file changed, 21 insertions(+)

diff --git a/backends/igvm.c b/backends/igvm.c
index 25bbddfa33..b6b7d30a3f 100644
--- a/backends/igvm.c
+++ b/backends/igvm.c
@@ -773,6 +773,27 @@ int igvm_process_file(IgvmCfgState *cfg, ConfidentialGuestSupport *cgs,
         }
     }
 
+    header_count =
+        igvm_header_count(ctx.file, IGVM_HEADER_SECTION_INITIALIZATION);
+    if (header_count < 0) {
+        error_setg(
+            errp,
+            "Invalid initialization header count in IGVM file. Error code: %X",
+            header_count);
+        return -1;
+    }
+
+    for (ctx.current_header_index = 0;
+         ctx.current_header_index < (unsigned)header_count;
+         ctx.current_header_index++) {
+        IgvmVariableHeaderType type =
+            igvm_get_header_type(ctx.file, IGVM_HEADER_SECTION_INITIALIZATION,
+                                 ctx.current_header_index);
+        if (handler(&ctx, type, errp) < 0) {
+            goto cleanup;
+        }
+    }
+
     /*
      * Contiguous pages of data with compatible flags are grouped together in
      * order to reduce the number of memory regions we create. Make sure the
-- 
2.43.0



^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH v3 13/15] backends/igvm: Handle policy for SEV guests
  2024-06-21 14:29 [PATCH v3 00/15] Introduce support for IGVM files Roy Hopkins
                   ` (11 preceding siblings ...)
  2024-06-21 14:29 ` [PATCH v3 12/15] backends/igvm: Process initialization sections in IGVM file Roy Hopkins
@ 2024-06-21 14:29 ` Roy Hopkins
  2024-06-24 14:56   ` Daniel P. Berrangé
  2024-06-21 14:29 ` [PATCH v3 14/15] i386/sev: Add implementation of CGS set_guest_policy() Roy Hopkins
                   ` (2 subsequent siblings)
  15 siblings, 1 reply; 39+ messages in thread
From: Roy Hopkins @ 2024-06-21 14:29 UTC (permalink / raw)
  To: qemu-devel
  Cc: Roy Hopkins, Paolo Bonzini, Daniel P . Berrangé,
	Stefano Garzarella, Marcelo Tosatti, Michael S . Tsirkin,
	Cornelia Huck, Marcel Apfelbaum, Sergio Lopez, Eduardo Habkost,
	Alistair Francis, Peter Xu, David Hildenbrand, Igor Mammedov,
	Tom Lendacky, Michael Roth, Ani Sinha, Jörg Roedel

Adds a handler for the guest policy initialization IGVM section and
builds an SEV policy based on this information and the ID block
directive if present. The policy is applied using by calling
'set_guest_policy()' on the ConfidentialGuestSupport object.

Signed-off-by: Roy Hopkins <roy.hopkins@suse.com>
---
 backends/igvm.c | 136 ++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 136 insertions(+)

diff --git a/backends/igvm.c b/backends/igvm.c
index b6b7d30a3f..bb8038f9e9 100644
--- a/backends/igvm.c
+++ b/backends/igvm.c
@@ -28,6 +28,33 @@ typedef struct IgvmParameterData {
     uint32_t index;
 } IgvmParameterData;
 
+/*
+ * Some directives are specific to particular confidential computing platforms.
+ * Define required types for each of those platforms here.
+ */
+
+/* SEV/SEV-ES/SEV-SNP */
+struct QEMU_PACKED sev_id_block {
+    uint8_t ld[48];
+    uint8_t family_id[16];
+    uint8_t image_id[16];
+    uint32_t version;
+    uint32_t guest_svn;
+    uint64_t policy;
+};
+
+struct QEMU_PACKED sev_id_authentication {
+    uint32_t id_key_alg;
+    uint32_t auth_key_algo;
+    uint8_t reserved[56];
+    uint8_t id_block_sig[512];
+    uint8_t id_key[1028];
+    uint8_t reserved2[60];
+    uint8_t id_key_sig[512];
+    uint8_t author_key[1028];
+    uint8_t reserved3[892];
+};
+
 /*
  * QemuIgvm contains the information required during processing
  * of a single IGVM file.
@@ -39,6 +66,17 @@ typedef struct QemuIgvm {
     uint32_t compatibility_mask;
     unsigned current_header_index;
     QTAILQ_HEAD(, IgvmParameterData) parameter_data;
+    IgvmPlatformType platform_type;
+
+    /*
+     * SEV-SNP platforms can contain an ID block and authentication
+     * that should be verified by the guest.
+     */
+    struct sev_id_block *id_block;
+    struct sev_id_authentication *id_auth;
+
+    /* Define the guest policy for SEV guests */
+    uint64_t sev_policy;
 
     /* These variables keep track of contiguous page regions */
     IGVM_VHS_PAGE_DATA region_prev_page_data;
@@ -64,6 +102,11 @@ static int directive_environment_info(QemuIgvm *ctx, const uint8_t *header_data,
                                       Error **errp);
 static int directive_required_memory(QemuIgvm *ctx, const uint8_t *header_data,
                                      Error **errp);
+static int directive_snp_id_block(QemuIgvm *ctx, const uint8_t *header_data,
+                                  Error **errp);
+static int initialization_guest_policy(QemuIgvm *ctx,
+                                       const uint8_t *header_data,
+                                       Error **errp);
 
 struct IGVMHandler {
     uint32_t type;
@@ -87,6 +130,10 @@ static struct IGVMHandler handlers[] = {
       directive_environment_info },
     { IGVM_VHT_REQUIRED_MEMORY, IGVM_HEADER_SECTION_DIRECTIVE,
       directive_required_memory },
+    { IGVM_VHT_SNP_ID_BLOCK, IGVM_HEADER_SECTION_DIRECTIVE,
+      directive_snp_id_block },
+    { IGVM_VHT_GUEST_POLICY, IGVM_HEADER_SECTION_INITIALIZATION,
+      initialization_guest_policy },
 };
 
 static int handler(QemuIgvm *ctx, uint32_t type, Error **errp)
@@ -619,6 +666,68 @@ static int directive_required_memory(QemuIgvm *ctx, const uint8_t *header_data,
     return 0;
 }
 
+static int directive_snp_id_block(QemuIgvm *ctx, const uint8_t *header_data,
+                                  Error **errp)
+{
+    const IGVM_VHS_SNP_ID_BLOCK *igvm_id =
+        (const IGVM_VHS_SNP_ID_BLOCK *)header_data;
+
+    if (ctx->compatibility_mask & igvm_id->compatibility_mask) {
+        if (ctx->id_block) {
+            error_setg(errp, "IGVM: Multiple ID blocks encountered "
+                             "in IGVM file.");
+            return -1;
+        }
+        ctx->id_block = g_malloc0(sizeof(struct sev_id_block));
+        ctx->id_auth = g_malloc0(sizeof(struct sev_id_authentication));
+
+        memcpy(ctx->id_block->family_id, igvm_id->family_id,
+               sizeof(ctx->id_block->family_id));
+        memcpy(ctx->id_block->image_id, igvm_id->image_id,
+               sizeof(ctx->id_block->image_id));
+        ctx->id_block->guest_svn = igvm_id->guest_svn;
+        ctx->id_block->version = 1;
+        memcpy(ctx->id_block->ld, igvm_id->ld, sizeof(ctx->id_block->ld));
+
+        ctx->id_auth->id_key_alg = igvm_id->id_key_algorithm;
+        memcpy(ctx->id_auth->id_block_sig, &igvm_id->id_key_signature,
+               sizeof(igvm_id->id_key_signature));
+
+        ctx->id_auth->auth_key_algo = igvm_id->author_key_algorithm;
+        memcpy(ctx->id_auth->id_key_sig, &igvm_id->author_key_signature,
+               sizeof(igvm_id->author_key_signature));
+
+        /*
+         * SEV and IGVM public key structure population are slightly different.
+         * See SEV Secure Nested Paging Firmware ABI Specification, Chapter 10.
+         */
+        *((uint32_t *)ctx->id_auth->id_key) = igvm_id->id_public_key.curve;
+        memcpy(&ctx->id_auth->id_key[4], &igvm_id->id_public_key.qx, 72);
+        memcpy(&ctx->id_auth->id_key[76], &igvm_id->id_public_key.qy, 72);
+
+        *((uint32_t *)ctx->id_auth->author_key) =
+            igvm_id->author_public_key.curve;
+        memcpy(&ctx->id_auth->author_key[4], &igvm_id->author_public_key.qx,
+               72);
+        memcpy(&ctx->id_auth->author_key[76], &igvm_id->author_public_key.qy,
+               72);
+    }
+
+    return 0;
+}
+
+static int initialization_guest_policy(QemuIgvm *ctx,
+                                       const uint8_t *header_data, Error **errp)
+{
+    const IGVM_VHS_GUEST_POLICY *guest =
+        (const IGVM_VHS_GUEST_POLICY *)header_data;
+
+    if (guest->compatibility_mask & ctx->compatibility_mask) {
+        ctx->sev_policy = guest->policy;
+    }
+    return 0;
+}
+
 static int supported_platform_compat_mask(QemuIgvm *ctx, Error **errp)
 {
     int32_t header_count;
@@ -688,12 +797,16 @@ static int supported_platform_compat_mask(QemuIgvm *ctx, Error **errp)
     /* Choose the strongest supported isolation technology */
     if (compatibility_mask_sev_snp != 0) {
         ctx->compatibility_mask = compatibility_mask_sev_snp;
+        ctx->platform_type = IGVM_PLATFORM_TYPE_SEV_SNP;
     } else if (compatibility_mask_sev_es != 0) {
         ctx->compatibility_mask = compatibility_mask_sev_es;
+        ctx->platform_type = IGVM_PLATFORM_TYPE_SEV_ES;
     } else if (compatibility_mask_sev != 0) {
         ctx->compatibility_mask = compatibility_mask_sev;
+        ctx->platform_type = IGVM_PLATFORM_TYPE_SEV;
     } else if (compatibility_mask != 0) {
         ctx->compatibility_mask = compatibility_mask;
+        ctx->platform_type = IGVM_PLATFORM_TYPE_NATIVE;
     } else {
         error_setg(
             errp,
@@ -703,6 +816,23 @@ static int supported_platform_compat_mask(QemuIgvm *ctx, Error **errp)
     return 0;
 }
 
+static int handle_policy(QemuIgvm *ctx, Error **errp)
+{
+    if (ctx->platform_type == IGVM_PLATFORM_TYPE_SEV_SNP) {
+        int id_block_len = 0;
+        int id_auth_len = 0;
+        if (ctx->id_block) {
+            ctx->id_block->policy = ctx->sev_policy;
+            id_block_len = sizeof(struct sev_id_block);
+            id_auth_len = sizeof(struct sev_id_authentication);
+        }
+        return ctx->cgsc->set_guest_policy(GUEST_POLICY_SEV, ctx->sev_policy,
+                                          ctx->id_block, id_block_len,
+                                          ctx->id_auth, id_auth_len, errp);
+    }
+    return 0;
+}
+
 static IgvmHandle igvm_file_init(char *filename, Error **errp)
 {
     IgvmHandle igvm;
@@ -801,12 +931,18 @@ int igvm_process_file(IgvmCfgState *cfg, ConfidentialGuestSupport *cgs,
      */
     retval = process_mem_page(&ctx, NULL, errp);
 
+    if (retval == 0) {
+        retval = handle_policy(&ctx, errp);
+    }
+
 cleanup:
     QTAILQ_FOREACH(parameter, &ctx.parameter_data, next)
     {
         g_free(parameter->data);
         parameter->data = NULL;
     }
+    g_free(ctx.id_block);
+    g_free(ctx.id_auth);
 
     return retval;
 }
-- 
2.43.0



^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH v3 14/15] i386/sev: Add implementation of CGS set_guest_policy()
  2024-06-21 14:29 [PATCH v3 00/15] Introduce support for IGVM files Roy Hopkins
                   ` (12 preceding siblings ...)
  2024-06-21 14:29 ` [PATCH v3 13/15] backends/igvm: Handle policy for SEV guests Roy Hopkins
@ 2024-06-21 14:29 ` Roy Hopkins
  2024-06-24 14:53   ` Daniel P. Berrangé
  2024-06-21 14:29 ` [PATCH v3 15/15] sev: Provide sev_features flags from IGVM VMSA to KVM_SEV_INIT2 Roy Hopkins
  2024-06-24 13:50 ` [PATCH v3 00/15] Introduce support for IGVM files Daniel P. Berrangé
  15 siblings, 1 reply; 39+ messages in thread
From: Roy Hopkins @ 2024-06-21 14:29 UTC (permalink / raw)
  To: qemu-devel
  Cc: Roy Hopkins, Paolo Bonzini, Daniel P . Berrangé,
	Stefano Garzarella, Marcelo Tosatti, Michael S . Tsirkin,
	Cornelia Huck, Marcel Apfelbaum, Sergio Lopez, Eduardo Habkost,
	Alistair Francis, Peter Xu, David Hildenbrand, Igor Mammedov,
	Tom Lendacky, Michael Roth, Ani Sinha, Jörg Roedel

The new cgs_set_guest_policy() function is provided to receive the guest
policy flags, SNP ID block and SNP ID authentication from guest
configuration such as an IGVM file and apply it to the platform prior to
launching the guest.

The policy is used to populate values for the existing 'policy',
'id_block' and 'id_auth' parameters. When provided, the guest policy is
applied and the ID block configuration is used to verify the launch
measurement and signatures. The guest is only successfully started if
the expected launch measurements match the actual measurements and the
signatures are valid.

Signed-off-by: Roy Hopkins <roy.hopkins@suse.com>
---
 target/i386/sev.h | 12 +++++++
 target/i386/sev.c | 83 +++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 95 insertions(+)

diff --git a/target/i386/sev.h b/target/i386/sev.h
index 2ccd6fe1e8..7b92102bd0 100644
--- a/target/i386/sev.h
+++ b/target/i386/sev.h
@@ -157,6 +157,18 @@ struct QEMU_PACKED sev_es_save_area {
     uint8_t fpreg_ymm[256];
 };
 
+struct QEMU_PACKED sev_snp_id_authentication {
+    uint32_t id_key_alg;
+    uint32_t auth_key_algo;
+    uint8_t reserved[56];
+    uint8_t id_block_sig[512];
+    uint8_t id_key[1028];
+    uint8_t reserved2[60];
+    uint8_t id_key_sig[512];
+    uint8_t author_key[1028];
+    uint8_t reserved3[892];
+};
+
 #ifdef CONFIG_SEV
 bool sev_enabled(void);
 bool sev_es_enabled(void);
diff --git a/target/i386/sev.c b/target/i386/sev.c
index 5d9ef745bb..688b378c42 100644
--- a/target/i386/sev.c
+++ b/target/i386/sev.c
@@ -2446,6 +2446,88 @@ static int cgs_get_mem_map_entry(int index,
     return 0;
 }
 
+static int cgs_set_guest_policy(ConfidentialGuestPolicyType policy_type,
+                                uint64_t policy, void *policy_data1,
+                                uint32_t policy_data1_size, void *policy_data2,
+                                uint32_t policy_data2_size, Error **errp)
+{
+    if (policy_type != GUEST_POLICY_SEV) {
+        error_setg(errp, "%s: Invalid guest policy type provided for SEV: %d",
+        __func__, policy_type);
+        return -1;
+    }
+    /*
+     * SEV-SNP handles policy differently. The policy flags are defined in
+     * kvm_start_conf.policy and an ID block and ID auth can be provided.
+     */
+    if (sev_snp_enabled()) {
+        SevSnpGuestState *sev_snp_guest =
+            SEV_SNP_GUEST(MACHINE(qdev_get_machine())->cgs);
+        struct kvm_sev_snp_launch_finish *finish =
+            &sev_snp_guest->kvm_finish_conf;
+
+        /*
+         * The policy consists of flags in 'policy' and optionally an ID block
+         * and ID auth in policy_data1 and policy_data2 respectively. The ID
+         * block and auth are optional so clear any previous ID block and auth
+         * and set them if provided, but always set the policy flags.
+         */
+        g_free(sev_snp_guest->id_block);
+        g_free((guchar *)finish->id_block_uaddr);
+        g_free(sev_snp_guest->id_auth);
+        g_free((guchar *)finish->id_auth_uaddr);
+        sev_snp_guest->id_block = NULL;
+        finish->id_block_uaddr = 0;
+        sev_snp_guest->id_auth = NULL;
+        finish->id_auth_uaddr = 0;
+
+        if (policy_data1_size > 0) {
+            struct sev_snp_id_authentication *id_auth =
+                (struct sev_snp_id_authentication *)policy_data2;
+
+            if (policy_data1_size != KVM_SEV_SNP_ID_BLOCK_SIZE) {
+                error_setg(errp, "%s: Invalid SEV-SNP ID block: incorrect size",
+                           __func__);
+                return -1;
+            }
+            if (policy_data2_size != KVM_SEV_SNP_ID_AUTH_SIZE) {
+                error_setg(errp,
+                           "%s: Invalid SEV-SNP ID auth block: incorrect size",
+                           __func__);
+                return -1;
+            }
+            finish->id_block_uaddr =
+                (__u64)g_malloc0(KVM_SEV_SNP_ID_BLOCK_SIZE);
+            finish->id_auth_uaddr = (__u64)g_malloc0(KVM_SEV_SNP_ID_AUTH_SIZE);
+            memcpy((void *)finish->id_block_uaddr, policy_data1,
+                   KVM_SEV_SNP_ID_BLOCK_SIZE);
+            memcpy((void *)finish->id_auth_uaddr, policy_data2,
+                   KVM_SEV_SNP_ID_AUTH_SIZE);
+
+            /*
+             * Check if an author key has been provided and use that to flag
+             * whether the author key is enabled. The first of the author key
+             * must be non-zero to indicate the key type, which will currently
+             * always be 2.
+             */
+            sev_snp_guest->kvm_finish_conf.auth_key_en =
+                id_auth->author_key[0] ? 1 : 0;
+            finish->id_block_en = 1;
+        }
+        sev_snp_guest->kvm_start_conf.policy = policy;
+    } else {
+        SevGuestState *sev_guest = SEV_GUEST(MACHINE(qdev_get_machine())->cgs);
+        /* Only the policy flags are supported for SEV and SEV-ES */
+        if ((policy_data1_size > 0) || (policy_data2_size > 0) || !sev_guest) {
+            error_setg(errp, "%s: An ID block/ID auth block has been provided "
+                             "but SEV-SNP is not supported", __func__);
+            return -1;
+        }
+        sev_guest->policy = policy;
+    }
+    return 0;
+}
+
 static void
 sev_common_class_init(ObjectClass *oc, void *data)
 {
@@ -2484,6 +2566,7 @@ sev_common_instance_init(Object *obj)
     cgs->check_support = cgs_check_support;
     cgs->set_guest_state = cgs_set_guest_state;
     cgs->get_mem_map_entry = cgs_get_mem_map_entry;
+    cgs->set_guest_policy = cgs_set_guest_policy;
 
     QTAILQ_INIT(&sev_common->launch_vmsa);
 }
-- 
2.43.0



^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH v3 15/15] sev: Provide sev_features flags from IGVM VMSA to KVM_SEV_INIT2
  2024-06-21 14:29 [PATCH v3 00/15] Introduce support for IGVM files Roy Hopkins
                   ` (13 preceding siblings ...)
  2024-06-21 14:29 ` [PATCH v3 14/15] i386/sev: Add implementation of CGS set_guest_policy() Roy Hopkins
@ 2024-06-21 14:29 ` Roy Hopkins
  2024-06-24 14:14   ` Daniel P. Berrangé
  2024-06-24 13:50 ` [PATCH v3 00/15] Introduce support for IGVM files Daniel P. Berrangé
  15 siblings, 1 reply; 39+ messages in thread
From: Roy Hopkins @ 2024-06-21 14:29 UTC (permalink / raw)
  To: qemu-devel
  Cc: Roy Hopkins, Paolo Bonzini, Daniel P . Berrangé,
	Stefano Garzarella, Marcelo Tosatti, Michael S . Tsirkin,
	Cornelia Huck, Marcel Apfelbaum, Sergio Lopez, Eduardo Habkost,
	Alistair Francis, Peter Xu, David Hildenbrand, Igor Mammedov,
	Tom Lendacky, Michael Roth, Ani Sinha, Jörg Roedel

IGVM files can contain an initial VMSA that should be applied to each
vcpu as part of the initial guest state. The sev_features flags are
provided as part of the VMSA structure. However, KVM only allows
sev_features to be set during initialization and not as the guest is
being prepared for launch.

This patch queries KVM for the supported set of sev_features flags and
processes the IGVM file during kvm_init to determine any sev_features
flags set in the IGVM file. These are then provided in the call to
KVM_SEV_INIT2 to ensure the guest state matches that specified in the
IGVM file.

This does cause the IGVM file to be processed twice. Firstly to extract
the sev_features then secondly to actually configure the guest. However,
the first pass is largely ignored meaning the overhead is minimal.

Signed-off-by: Roy Hopkins <roy.hopkins@suse.com>
---
 target/i386/sev.c | 145 ++++++++++++++++++++++++++++++++++++++++------
 1 file changed, 126 insertions(+), 19 deletions(-)

diff --git a/target/i386/sev.c b/target/i386/sev.c
index 688b378c42..4b9d74207b 100644
--- a/target/i386/sev.c
+++ b/target/i386/sev.c
@@ -117,6 +117,8 @@ struct SevCommonState {
     uint32_t cbitpos;
     uint32_t reduced_phys_bits;
     bool kernel_hashes;
+    uint64_t sev_features;
+    uint64_t supported_sev_features;
 
     /* runtime state */
     uint8_t api_major;
@@ -490,7 +492,40 @@ static void sev_apply_cpu_context(CPUState *cpu)
     }
 }
 
-static int check_vmsa_supported(hwaddr gpa, const struct sev_es_save_area *vmsa,
+static int check_sev_features(SevCommonState *sev_common, uint64_t sev_features,
+                              Error **errp)
+{
+    /*
+     * Ensure SEV_FEATURES is configured for correct SEV hardware and that
+     * the requested features are supported. If SEV-SNP is enabled then
+     * that feature must be enabled, otherwise it must be cleared.
+     */
+    if (sev_snp_enabled() && !(sev_features & SVM_SEV_FEAT_SNP_ACTIVE)) {
+        error_setg(
+            errp,
+            "%s: SEV_SNP is enabled but is not enabled in VMSA sev_features",
+            __func__);
+        return -1;
+    } else if (!sev_snp_enabled() &&
+               (sev_features & SVM_SEV_FEAT_SNP_ACTIVE)) {
+        error_setg(
+            errp,
+            "%s: SEV_SNP is not enabled but is enabled in VMSA sev_features",
+            __func__);
+        return -1;
+    }
+    if (sev_features & ~sev_common->supported_sev_features) {
+        error_setg(errp,
+                   "%s: VMSA contains unsupported sev_features: %lX, "
+                   "supported features: %lX",
+                   __func__, sev_features, sev_common->supported_sev_features);
+        return -1;
+    }
+    return 0;
+}
+
+static int check_vmsa_supported(SevCommonState *sev_common, hwaddr gpa,
+                                const struct sev_es_save_area *vmsa,
                                 Error **errp)
 {
     struct sev_es_save_area vmsa_check;
@@ -556,24 +591,10 @@ static int check_vmsa_supported(hwaddr gpa, const struct sev_es_save_area *vmsa,
     vmsa_check.x87_fcw = 0;
     vmsa_check.mxcsr = 0;
 
-    if (sev_snp_enabled()) {
-        if (vmsa_check.sev_features != SVM_SEV_FEAT_SNP_ACTIVE) {
-            error_setg(errp,
-                       "%s: sev_features in the VMSA contains an unsupported "
-                       "value. For SEV-SNP, sev_features must be set to %x.",
-                       __func__, SVM_SEV_FEAT_SNP_ACTIVE);
-            return -1;
-        }
-        vmsa_check.sev_features = 0;
-    } else {
-        if (vmsa_check.sev_features != 0) {
-            error_setg(errp,
-                       "%s: sev_features in the VMSA contains an unsupported "
-                       "value. For SEV-ES and SEV, sev_features must be "
-                       "set to 0.", __func__);
-            return -1;
-        }
+    if (check_sev_features(sev_common, vmsa_check.sev_features, errp) < 0) {
+        return -1;
     }
+    vmsa_check.sev_features = 0;
 
     if (!buffer_is_zero(&vmsa_check, sizeof(vmsa_check))) {
         error_setg(errp,
@@ -1663,6 +1684,25 @@ static int sev_snp_kvm_type(X86ConfidentialGuest *cg)
     return KVM_X86_SNP_VM;
 }
 
+static int sev_init_supported_features(SevCommonState *sev_common, Error **errp)
+{
+    /* Query KVM for the supported set of sev_features */
+    struct kvm_device_attr attr = {
+        .group = KVM_X86_GRP_SEV,
+        .attr = KVM_X86_SEV_VMSA_FEATURES,
+        .addr = (unsigned long)&sev_common->supported_sev_features,
+    };
+    if (kvm_ioctl(kvm_state, KVM_GET_DEVICE_ATTR, &attr) < 0) {
+        error_setg(errp, "%s: failed to query supported sev_features",
+                   __func__);
+        return -1;
+    }
+    if (sev_snp_enabled()) {
+        sev_common->supported_sev_features |= SVM_SEV_FEAT_SNP_ACTIVE;
+    }
+    return 0;
+}
+
 static int sev_common_kvm_init(ConfidentialGuestSupport *cgs, Error **errp)
 {
     char *devname;
@@ -1743,6 +1783,10 @@ static int sev_common_kvm_init(ConfidentialGuestSupport *cgs, Error **errp)
         }
     }
 
+    if (sev_init_supported_features(sev_common, errp) < 0) {
+        return -1;
+    }
+
     trace_kvm_sev_init();
     if (x86_klass->kvm_type(X86_CONFIDENTIAL_GUEST(sev_common)) == KVM_X86_DEFAULT_VM) {
         cmd = sev_es_enabled() ? KVM_SEV_ES_INIT : KVM_SEV_INIT;
@@ -1750,6 +1794,38 @@ static int sev_common_kvm_init(ConfidentialGuestSupport *cgs, Error **errp)
         ret = sev_ioctl(sev_common->sev_fd, cmd, NULL, &fw_error);
     } else {
         struct kvm_sev_init args = { 0 };
+        MachineState *machine = MACHINE(qdev_get_machine());
+
+        /*
+         * If configuration is provided via an IGVM file then the IGVM file
+         * might contain configuration of the initial vcpu context. For SEV
+         * the vcpu context includes the sev_features which should be applied
+         * to the vcpu.
+         *
+         * KVM does not synchronize sev_features from CPU state. Instead it
+         * requires sev_features to be provided as part of this initialization
+         * call which is subsequently automatically applied to the VMSA of
+         * each vcpu.
+         *
+         * The IGVM file is normally processed after initialization. Therefore
+         * we need to pre-process it here to extract sev_features in order to
+         * provide it to KVM_SEV_INIT2. Each cgs_* function that is called by
+         * the IGVM processor detects this pre-process by observing the state
+         * as SEV_STATE_UNINIT.
+         */
+        if (machine->igvm) {
+            if (IGVM_CFG_GET_CLASS(machine->igvm)
+                    ->process(machine->igvm, machine->cgs, errp) == -1) {
+                return -1;
+            }
+            /*
+             * KVM maintains a bitmask of allowed sev_features. This does not
+             * include SVM_SEV_FEAT_SNP_ACTIVE which is set accordingly by KVM
+             * itself. Therefore we need to clear this flag.
+             */
+            args.vmsa_features = sev_common->sev_features &
+                                 ~SVM_SEV_FEAT_SNP_ACTIVE;
+        }
 
         ret = sev_ioctl(sev_common->sev_fd, KVM_SEV_INIT2, &args, &fw_error);
     }
@@ -2348,6 +2424,24 @@ static int cgs_set_guest_state(hwaddr gpa, uint8_t *ptr, uint64_t len,
     SevCommonState *sev_common = SEV_COMMON(MACHINE(qdev_get_machine())->cgs);
     SevCommonStateClass *klass = SEV_COMMON_GET_CLASS(sev_common);
 
+    if (sev_common->state == SEV_STATE_UNINIT) {
+        /* Pre-processing of IGVM file called from sev_common_kvm_init() */
+        if ((cpu_index == 0) && (memory_type == CGS_PAGE_TYPE_VMSA)) {
+            const struct sev_es_save_area *sa =
+                (const struct sev_es_save_area *)ptr;
+            if (len < sizeof(*sa)) {
+                error_setg(errp, "%s: invalid VMSA length encountered",
+                           __func__);
+                return -1;
+            }
+            if (check_sev_features(sev_common, sa->sev_features, errp) < 0) {
+                return -1;
+            }
+            sev_common->sev_features = sa->sev_features;
+        }
+        return 0;
+    }
+
     if (!sev_enabled()) {
         error_setg(errp, "%s: attempt to configure guest memory, but SEV "
                      "is not enabled", __func__);
@@ -2367,7 +2461,8 @@ static int cgs_set_guest_state(hwaddr gpa, uint8_t *ptr, uint64_t len,
                        __func__);
             return -1;
         }
-        if (check_vmsa_supported(gpa, (const struct sev_es_save_area *)ptr,
+        if (check_vmsa_supported(sev_common, gpa,
+                                 (const struct sev_es_save_area *)ptr,
                                  errp) < 0) {
             return -1;
         }
@@ -2421,6 +2516,12 @@ static int cgs_get_mem_map_entry(int index,
                                  ConfidentialGuestMemoryMapEntry *entry,
                                  Error **errp)
 {
+    SevCommonState *sev_common = SEV_COMMON(MACHINE(qdev_get_machine())->cgs);
+    if (sev_common->state == SEV_STATE_UNINIT) {
+        /* Pre-processing of IGVM file called from sev_common_kvm_init() */
+        return 1;
+    }
+
     if ((index < 0) || (index >= e820_get_num_entries())) {
         return 1;
     }
@@ -2451,6 +2552,12 @@ static int cgs_set_guest_policy(ConfidentialGuestPolicyType policy_type,
                                 uint32_t policy_data1_size, void *policy_data2,
                                 uint32_t policy_data2_size, Error **errp)
 {
+    SevCommonState *sev_common = SEV_COMMON(MACHINE(qdev_get_machine())->cgs);
+    if (sev_common->state == SEV_STATE_UNINIT) {
+        /* Pre-processing of IGVM file called from sev_common_kvm_init() */
+        return 0;
+    }
+
     if (policy_type != GUEST_POLICY_SEV) {
         error_setg(errp, "%s: Invalid guest policy type provided for SEV: %d",
         __func__, policy_type);
-- 
2.43.0



^ permalink raw reply related	[flat|nested] 39+ messages in thread

* Re: [PATCH v3 03/15] backends/igvm: Add IGVM loader and configuration
  2024-06-21 14:29 ` [PATCH v3 03/15] backends/igvm: Add IGVM loader and configuration Roy Hopkins
@ 2024-06-24 13:29   ` Daniel P. Berrangé
  2024-06-28 10:59     ` Roy Hopkins
  2024-06-27  9:06   ` Stefano Garzarella
  1 sibling, 1 reply; 39+ messages in thread
From: Daniel P. Berrangé @ 2024-06-24 13:29 UTC (permalink / raw)
  To: Roy Hopkins
  Cc: qemu-devel, Paolo Bonzini, Stefano Garzarella, Marcelo Tosatti,
	Michael S . Tsirkin, Cornelia Huck, Marcel Apfelbaum,
	Sergio Lopez, Eduardo Habkost, Alistair Francis, Peter Xu,
	David Hildenbrand, Igor Mammedov, Tom Lendacky, Michael Roth,
	Ani Sinha, Jörg Roedel

On Fri, Jun 21, 2024 at 03:29:06PM +0100, Roy Hopkins wrote:
> Adds an IGVM loader to QEMU which processes a given IGVM file and
> applies the directives within the file to the current guest
> configuration.
> 
> The IGVM loader can be used to configure both confidential and
> non-confidential guests. For confidential guests, the
> ConfidentialGuestSupport object for the system is used to encrypt
> memory, apply the initial CPU state and perform other confidential guest
> operations.
> 
> The loader is configured via a new IgvmCfg QOM object which allows the
> user to provide a path to the IGVM file to process.
> 
> Signed-off-by: Roy Hopkins <roy.hopkins@suse.com>
> ---
>  qapi/qom.json             |  16 +
>  backends/igvm.h           |  37 ++
>  include/sysemu/igvm-cfg.h |  54 +++
>  backends/igvm-cfg.c       |  66 ++++
>  backends/igvm.c           | 791 ++++++++++++++++++++++++++++++++++++++
>  backends/meson.build      |   2 +
>  6 files changed, 966 insertions(+)
>  create mode 100644 backends/igvm.h
>  create mode 100644 include/sysemu/igvm-cfg.h
>  create mode 100644 backends/igvm-cfg.c
>  create mode 100644 backends/igvm.c
> 
> diff --git a/qapi/qom.json b/qapi/qom.json
> index 8bd299265e..e586707c4c 100644
> --- a/qapi/qom.json
> +++ b/qapi/qom.json
> @@ -874,6 +874,18 @@
>    'base': 'RngProperties',
>    'data': { '*filename': 'str' } }
>  
> +##
> +# @IgvmCfgProperties:
> +#
> +# Properties common to objects that handle IGVM files.
> +#
> +# @file: IGVM file to use to configure guest (default: none)
> +#
> +# Since: 9.1
> +##
> +{ 'struct': 'IgvmCfgProperties',
> +  'data': { '*file': 'str' } }
> +
>  ##
>  # @SevCommonProperties:
>  #
> @@ -1039,6 +1051,8 @@
>      'filter-redirector',
>      'filter-replay',
>      'filter-rewriter',
> +    { 'name': 'igvm-cfg',
> +      'if': 'CONFIG_IGVM' },
>      'input-barrier',
>      { 'name': 'input-linux',
>        'if': 'CONFIG_LINUX' },
> @@ -1111,6 +1125,8 @@
>        'filter-redirector':          'FilterRedirectorProperties',
>        'filter-replay':              'NetfilterProperties',
>        'filter-rewriter':            'FilterRewriterProperties',
> +      'igvm-cfg':                   { 'type': 'IgvmCfgProperties',
> +                                      'if': 'CONFIG_IGVM' },
>        'input-barrier':              'InputBarrierProperties',
>        'input-linux':                { 'type': 'InputLinuxProperties',
>                                        'if': 'CONFIG_LINUX' },
> diff --git a/backends/igvm.h b/backends/igvm.h
> new file mode 100644
> index 0000000000..3a3824b391
> --- /dev/null
> +++ b/backends/igvm.h
> @@ -0,0 +1,37 @@
> +/*
> + * QEMU IGVM configuration backend for Confidential Guests
> + *
> + * Copyright (C) 2023-2024 SUSE
> + *
> + * Authors:
> + *  Roy Hopkins <roy.hopkins@suse.com>
> + *
> + * This work is licensed under the terms of the GNU GPL, version 2 or later.
> + * See the COPYING file in the top-level directory.
> + */
> +
> +#ifndef BACKENDS_IGVM_H
> +#define BACKENDS_IGVM_H
> +
> +#include "exec/confidential-guest-support.h"
> +#include "sysemu/igvm-cfg.h"
> +#include "qapi/error.h"
> +
> +#if defined(CONFIG_IGVM)
> +
> +int igvm_process_file(IgvmCfgState *igvm, ConfidentialGuestSupport *cgs,
> +                      Error **errp);
> +
> +#else
> +
> +static inline int igvm_process_file(IgvmCfgState *igvm,
> +                                    ConfidentialGuestSupport *cgs, Error **errp)
> +{
> +    error_setg(
> +        errp, "Invalid call to igvm_process_file when CONFIG_IGVM is disabled");
> +    return -1;
> +}

IIUC, the only call to this is from igvm-cfg.c, but that file is not
compiled if CONFIG_IGVM is unset.

IOW, isn't this inline stub deadcode that will never been compiled,
let alone called ?  Can we just get rid of the conditional in this
file entirely ? If someone were to change the build rules to create
an inconsistent build scenario, they'd get a linker error from the
missing igvm_process_file which seems good enough ?




> diff --git a/backends/igvm.c b/backends/igvm.c
> new file mode 100644
> index 0000000000..25bbddfa33
> --- /dev/null
> +++ b/backends/igvm.c
> @@ -0,0 +1,791 @@
> +/*
> + * QEMU IGVM configuration backend for guests
> + *
> + * Copyright (C) 2023-2024 SUSE
> + *
> + * Authors:
> + *  Roy Hopkins <roy.hopkins@suse.com>
> + *
> + * This work is licensed under the terms of the GNU GPL, version 2 or later.
> + * See the COPYING file in the top-level directory.
> + */
> +
> +#include "qemu/osdep.h"
> +
> +#include "igvm.h"
> +#include "qapi/error.h"
> +#include "exec/memory.h"
> +#include "exec/address-spaces.h"
> +#include "hw/core/cpu.h"
> +
> +#include <igvm/igvm.h>
> +#include <igvm/igvm_defs.h>
> +
> +typedef struct IgvmParameterData {
> +    QTAILQ_ENTRY(IgvmParameterData) next;
> +    uint8_t *data;
> +    uint32_t size;
> +    uint32_t index;
> +} IgvmParameterData;
> +
> +/*
> + * QemuIgvm contains the information required during processing
> + * of a single IGVM file.
> + */
> +typedef struct QemuIgvm {
> +    IgvmHandle file;
> +    ConfidentialGuestSupport *cgs;
> +    ConfidentialGuestSupportClass *cgsc;
> +    uint32_t compatibility_mask;
> +    unsigned current_header_index;
> +    QTAILQ_HEAD(, IgvmParameterData) parameter_data;
> +
> +    /* These variables keep track of contiguous page regions */
> +    IGVM_VHS_PAGE_DATA region_prev_page_data;
> +    uint64_t region_start;
> +    unsigned region_start_index;
> +    unsigned region_last_index;
> +    unsigned region_page_count;
> +} QemuIgvm;
> +
> +static int directive_page_data(QemuIgvm *ctx, const uint8_t *header_data,
> +                               Error **errp);
> +static int directive_vp_context(QemuIgvm *ctx, const uint8_t *header_data,
> +                                Error **errp);
> +static int directive_parameter_area(QemuIgvm *ctx, const uint8_t *header_data,
> +                                    Error **errp);
> +static int directive_parameter_insert(QemuIgvm *ctx, const uint8_t *header_data,
> +                                      Error **errp);
> +static int directive_memory_map(QemuIgvm *ctx, const uint8_t *header_data,
> +                                Error **errp);
> +static int directive_vp_count(QemuIgvm *ctx, const uint8_t *header_data,
> +                              Error **errp);
> +static int directive_environment_info(QemuIgvm *ctx, const uint8_t *header_data,
> +                                      Error **errp);
> +static int directive_required_memory(QemuIgvm *ctx, const uint8_t *header_data,
> +                                     Error **errp);
> +
> +struct IGVMHandler {
> +    uint32_t type;
> +    uint32_t section;
> +    int (*handler)(QemuIgvm *ctx, const uint8_t *header_data, Error **errp);

All the impls of the handler are doing manual casts from 'header_data' to
some other struct. If you declare this 'void *' instead, then all those
manual casts are redundant.

> +};
> +
> +static struct IGVMHandler handlers[] = {
> +    { IGVM_VHT_PAGE_DATA, IGVM_HEADER_SECTION_DIRECTIVE, directive_page_data },
> +    { IGVM_VHT_VP_CONTEXT, IGVM_HEADER_SECTION_DIRECTIVE,
> +      directive_vp_context },
> +    { IGVM_VHT_PARAMETER_AREA, IGVM_HEADER_SECTION_DIRECTIVE,
> +      directive_parameter_area },
> +    { IGVM_VHT_PARAMETER_INSERT, IGVM_HEADER_SECTION_DIRECTIVE,
> +      directive_parameter_insert },
> +    { IGVM_VHT_MEMORY_MAP, IGVM_HEADER_SECTION_DIRECTIVE,
> +      directive_memory_map },
> +    { IGVM_VHT_VP_COUNT_PARAMETER, IGVM_HEADER_SECTION_DIRECTIVE,
> +      directive_vp_count },
> +    { IGVM_VHT_ENVIRONMENT_INFO_PARAMETER, IGVM_HEADER_SECTION_DIRECTIVE,
> +      directive_environment_info },
> +    { IGVM_VHT_REQUIRED_MEMORY, IGVM_HEADER_SECTION_DIRECTIVE,
> +      directive_required_memory },
> +};
> +
> +static int handler(QemuIgvm *ctx, uint32_t type, Error **errp)
> +{
> +    size_t handler;
> +    IgvmHandle header_handle;
> +    const uint8_t *header_data;
> +    int result;
> +
> +    for (handler = 0; handler < G_N_ELEMENTS(handlers); handler++) {
> +        if (handlers[handler].type != type) {
> +            continue;
> +        }
> +        header_handle = igvm_get_header(ctx->file,
> +                                        handlers[handler].section,
> +                                        ctx->current_header_index);
> +        if (header_handle < 0) {
> +            error_setg(
> +                errp,
> +                "IGVM file is invalid: Failed to read directive header (code: %d)",
> +                (int)header_handle);
> +            return -1;
> +        }
> +        header_data = igvm_get_buffer(ctx->file, header_handle) +
> +                      sizeof(IGVM_VHS_VARIABLE_HEADER);
> +        result = handlers[handler].handler(ctx, header_data, errp);
> +        igvm_free_buffer(ctx->file, header_handle);
> +        return result;
> +    }
> +    error_setg(errp,
> +               "IGVM: Unknown header type encountered when processing file: "
> +               "(type 0x%X)",
> +               type);
> +    return -1;
> +}
> +
> +static void *igvm_prepare_memory(QemuIgvm *ctx, uint64_t addr, uint64_t size,
> +                                 int region_identifier, Error **errp)
> +{
> +    ERRP_GUARD();
> +    MemoryRegion *igvm_pages = NULL;
> +    Int128 gpa_region_size;
> +    MemoryRegionSection mrs =
> +        memory_region_find(get_system_memory(), addr, size);
> +    if (mrs.mr) {
> +        if (!memory_region_is_ram(mrs.mr)) {
> +            memory_region_unref(mrs.mr);
> +            error_setg(
> +                errp,
> +                "Processing of IGVM file failed: Could not prepare memory "
> +                "at address 0x%lX due to existing non-RAM region",
> +                addr);
> +            return NULL;
> +        }
> +
> +        gpa_region_size = int128_make64(size);
> +        if (int128_lt(mrs.size, gpa_region_size)) {
> +            memory_region_unref(mrs.mr);
> +            error_setg(
> +                errp,
> +                "Processing of IGVM file failed: Could not prepare memory "
> +                "at address 0x%lX: region size exceeded",
> +                addr);
> +            return NULL;
> +        }
> +        return qemu_map_ram_ptr(mrs.mr->ram_block, mrs.offset_within_region);
> +    } else {
> +        /*
> +         * The region_identifier is the is the index of the IGVM directive that
> +         * contains the page with the lowest GPA in the region. This will
> +         * generate a unique region name.
> +         */
> +        g_autofree char *region_name =
> +            g_strdup_printf("igvm.%X", region_identifier);
> +        igvm_pages = g_malloc(sizeof(*igvm_pages));

nit-pick, typically we'd prefer g_new0 over g_malloc + sizeof

> +        if (ctx->cgs && ctx->cgs->require_guest_memfd) {
> +            if (!memory_region_init_ram_guest_memfd(igvm_pages, NULL,
> +                                                    region_name, size, errp)) {
> +                return NULL;
> +            }
> +        } else {
> +            if (!memory_region_init_ram(igvm_pages, NULL, region_name, size,
> +                                        errp)) {
> +                return NULL;
> +            }
> +        }
> +        memory_region_add_subregion(get_system_memory(), addr, igvm_pages);
> +        return memory_region_get_ram_ptr(igvm_pages);
> +    }

> +static int directive_memory_map(QemuIgvm *ctx, const uint8_t *header_data,
> +                                Error **errp)
> +{
> +    const IGVM_VHS_PARAMETER *param = (const IGVM_VHS_PARAMETER *)header_data;

If changing header_data to 'const void *', then this repetition
of typename goes away e.g.

   const IGVM_VHS_PARAMETER *param = header_data;




> +int igvm_process_file(IgvmCfgState *cfg, ConfidentialGuestSupport *cgs,
> +                      Error **errp)
> +{
> +    int32_t header_count;
> +    IgvmParameterData *parameter;
> +    int retval = -1;
> +    QemuIgvm ctx;
> +
> +    memset(&ctx, 0, sizeof(ctx));

Can avoid the manual memset using

  QemuIgvm ctx = {};

> +    ctx.file = igvm_file_init(cfg->filename, errp);
> +    if (ctx.file < 0) {
> +        return -1;
> +    }
> +
> +    /*
> +     * The ConfidentialGuestSupport object is optional and allows a confidential
> +     * guest platform to perform extra processing, such as page measurement, on
> +     * IGVM directives.
> +     */
> +    ctx.cgs = cgs;
> +    ctx.cgsc = cgs ? CONFIDENTIAL_GUEST_SUPPORT_GET_CLASS(cgs) : NULL;
> +
> +    /*
> +     * Check that the IGVM file provides configuration for the current
> +     * platform
> +     */
> +    if (supported_platform_compat_mask(&ctx, errp) < 0) {
> +        return -1;
> +    }
> +
> +    header_count = igvm_header_count(ctx.file, IGVM_HEADER_SECTION_DIRECTIVE);
> +    if (header_count <= 0) {
> +        error_setg(
> +            errp, "Invalid directive header count in IGVM file. Error code: %X",
> +            header_count);
> +        return -1;
> +    }
> +
> +    QTAILQ_INIT(&ctx.parameter_data);
> +
> +    for (ctx.current_header_index = 0;
> +         ctx.current_header_index < (unsigned)header_count;
> +         ctx.current_header_index++) {
> +        IgvmVariableHeaderType type = igvm_get_header_type(
> +            ctx.file, IGVM_HEADER_SECTION_DIRECTIVE, ctx.current_header_index);
> +        if (handler(&ctx, type, errp) < 0) {
> +            goto cleanup;
> +        }
> +    }
> +
> +    /*
> +     * Contiguous pages of data with compatible flags are grouped together in
> +     * order to reduce the number of memory regions we create. Make sure the
> +     * last group is processed with this call.
> +     */
> +    retval = process_mem_page(&ctx, NULL, errp);
> +
> +cleanup:
> +    QTAILQ_FOREACH(parameter, &ctx.parameter_data, next)
> +    {
> +        g_free(parameter->data);
> +        parameter->data = NULL;
> +    }
> +
> +    return retval;
> +}

With regards,
Daniel
-- 
|: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org         -o-            https://fstop138.berrange.com :|
|: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|



^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH v3 00/15] Introduce support for IGVM files
  2024-06-21 14:29 [PATCH v3 00/15] Introduce support for IGVM files Roy Hopkins
                   ` (14 preceding siblings ...)
  2024-06-21 14:29 ` [PATCH v3 15/15] sev: Provide sev_features flags from IGVM VMSA to KVM_SEV_INIT2 Roy Hopkins
@ 2024-06-24 13:50 ` Daniel P. Berrangé
  2024-06-28 10:56   ` Roy Hopkins
  15 siblings, 1 reply; 39+ messages in thread
From: Daniel P. Berrangé @ 2024-06-24 13:50 UTC (permalink / raw)
  To: Roy Hopkins
  Cc: qemu-devel, Paolo Bonzini, Stefano Garzarella, Marcelo Tosatti,
	Michael S . Tsirkin, Cornelia Huck, Marcel Apfelbaum,
	Sergio Lopez, Eduardo Habkost, Alistair Francis, Peter Xu,
	David Hildenbrand, Igor Mammedov, Tom Lendacky, Michael Roth,
	Ani Sinha, Jörg Roedel

On Fri, Jun 21, 2024 at 03:29:03PM +0100, Roy Hopkins wrote:
> Based-on: 02d9c38236
> 
> Here is v3 of the set of patches to add support for IGVM files to QEMU.
> 
> Firstly, apologies for the long gap between v2 and v3. This was due to a number
> of factors, but particularly holding back until SEV-SNP support landed in QEMU
> as well as for some changes to be merged in the upstream IGVM specification and
> library. The delay meant that I could include the SEV-SNP IGVM changes that I
> had been separately maintaining for COCONUT-SVSM into this series, giving full
> support for the full range of SEV technologies.
> 
> Thank-you to everyone who reviewed the previous set of patches [1]. I
> have hopefully addressed all of the comments in those reviews. Some of these
> changes required a reasonable amount of rework. Along with the inclusion of
> support for SEV-SNP, this has resulted in a fairly large set of differences from
> v2. This v3 patch series is also available on github: [2]

snip

FYI, I hit some compile problems reporting array bounds issues,
with this posting. I'm using Fedora 40, which has gcc 14 in
case that matters.


In file included from /var/home/berrange/src/virt/qemu/include/sysemu/kvm.h:214,
                 from ../target/i386/sev.c:29:
In function ‘cpu_x86_load_seg_cache’,
    inlined from ‘sev_apply_cpu_context’ at ../target/i386/sev.c:454:13:
../target/i386/cpu.h:2236:20: error: array subscript 6 is above array bounds of ‘SegmentCache[6]’ [-Werror=array-bounds=]
 2236 |     sc = &env->segs[seg_reg];
      |           ~~~~~~~~~^~~~~~~~~
../target/i386/cpu.h: In function ‘sev_apply_cpu_context’:
../target/i386/cpu.h:1682:18: note: while referencing ‘segs’
 1682 |     SegmentCache segs[6]; /* selector values */
      |                  ^~~~
In function ‘cpu_x86_load_seg_cache’,
    inlined from ‘sev_apply_cpu_context’ at ../target/i386/sev.c:454:13:
../target/i386/cpu.h:2236:20: error: array subscript 6 is above array bounds of ‘SegmentCache[6]’ [-Werror=array-bounds=]
 2236 |     sc = &env->segs[seg_reg];
      |           ~~~~~~~~~^~~~~~~~~
../target/i386/cpu.h: In function ‘sev_apply_cpu_context’:
../target/i386/cpu.h:1682:18: note: while referencing ‘segs’
 1682 |     SegmentCache segs[6]; /* selector values */
      |                  ^~~~
In function ‘cpu_x86_load_seg_cache’,
    inlined from ‘sev_apply_cpu_context’ at ../target/i386/sev.c:454:13:
../target/i386/cpu.h:2236:20: error: array subscript 6 is above array bounds of ‘SegmentCache[6]’ [-Werror=array-bounds=]
 2236 |     sc = &env->segs[seg_reg];
      |           ~~~~~~~~~^~~~~~~~~
../target/i386/cpu.h: In function ‘sev_apply_cpu_context’:
../target/i386/cpu.h:1682:18: note: while referencing ‘segs’
 1682 |     SegmentCache segs[6]; /* selector values */
      |                  ^~~~
...cut many more similar warnings...


With regards,
Daniel
-- 
|: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org         -o-            https://fstop138.berrange.com :|
|: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|



^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH v3 04/15] hw/core/machine: Add igvm-cfg object and processing for IGVM files
  2024-06-21 14:29 ` [PATCH v3 04/15] hw/core/machine: Add igvm-cfg object and processing for IGVM files Roy Hopkins
@ 2024-06-24 14:00   ` Daniel P. Berrangé
  2024-06-28 11:09     ` Roy Hopkins
  0 siblings, 1 reply; 39+ messages in thread
From: Daniel P. Berrangé @ 2024-06-24 14:00 UTC (permalink / raw)
  To: Roy Hopkins
  Cc: qemu-devel, Paolo Bonzini, Stefano Garzarella, Marcelo Tosatti,
	Michael S . Tsirkin, Cornelia Huck, Marcel Apfelbaum,
	Sergio Lopez, Eduardo Habkost, Alistair Francis, Peter Xu,
	David Hildenbrand, Igor Mammedov, Tom Lendacky, Michael Roth,
	Ani Sinha, Jörg Roedel

On Fri, Jun 21, 2024 at 03:29:07PM +0100, Roy Hopkins wrote:
> An IGVM file contains configuration of guest state that should be
> applied during configuration of the guest, before the guest is started.
> 
> This patch allows the user to add an igvm-cfg object to the machine
> configuration that allows an IGVM file to be configured that will be
> applied to the guest before it is started.
> 
> If an IGVM configuration is provided then the IGVM file is processed at
> the end of the board initialization, before the state transition to
> PHASE_MACHINE_INITIALIZED.
> 
> Signed-off-by: Roy Hopkins <roy.hopkins@suse.com>
> ---
>  include/hw/boards.h |  2 ++
>  hw/core/machine.c   | 20 ++++++++++++++++++++
>  qemu-options.hx     | 25 +++++++++++++++++++++++++
>  3 files changed, 47 insertions(+)
> 
> diff --git a/include/hw/boards.h b/include/hw/boards.h
> index 73ad319d7d..4c1484ba0b 100644
> --- a/include/hw/boards.h
> +++ b/include/hw/boards.h
> @@ -4,6 +4,7 @@
>  #define HW_BOARDS_H
>  
>  #include "exec/memory.h"
> +#include "sysemu/igvm-cfg.h"
>  #include "sysemu/hostmem.h"
>  #include "sysemu/blockdev.h"
>  #include "qapi/qapi-types-machine.h"
> @@ -382,6 +383,7 @@ struct MachineState {
>      bool suppress_vmdesc;
>      bool enable_graphics;
>      ConfidentialGuestSupport *cgs;
> +    IgvmCfgState *igvm;
>      HostMemoryBackend *memdev;
>      /*
>       * convenience alias to ram_memdev_id backend memory region
> diff --git a/hw/core/machine.c b/hw/core/machine.c
> index 655d75c21f..f9f879172c 100644
> --- a/hw/core/machine.c
> +++ b/hw/core/machine.c
> @@ -1094,6 +1094,16 @@ static void machine_class_init(ObjectClass *oc, void *data)
>      object_class_property_set_description(oc, "confidential-guest-support",
>                                            "Set confidential guest scheme to support");
>  
> +#if defined(CONFIG_IGVM)
> +    object_class_property_add_link(oc, "igvm-cfg",
> +                                   TYPE_IGVM_CFG,
> +                                   offsetof(MachineState, igvm),
> +                                   object_property_allow_set_link,
> +                                   OBJ_PROP_LINK_STRONG);
> +    object_class_property_set_description(oc, "igvm-cfg",
> +                                          "Set IGVM configuration");
> +#endif
> +
>      /* For compatibility */
>      object_class_property_add_str(oc, "memory-encryption",
>          machine_get_memory_encryption, machine_set_memory_encryption);
> @@ -1582,6 +1592,16 @@ void machine_run_board_init(MachineState *machine, const char *mem_path, Error *
>  
>      accel_init_interfaces(ACCEL_GET_CLASS(machine->accelerator));
>      machine_class->init(machine);
> +
> +#if defined(CONFIG_IGVM)
> +    /* Apply guest state from IGVM if supplied */
> +    if (machine->igvm) {
> +        if (IGVM_CFG_GET_CLASS(machine->igvm)
> +                ->process(machine->igvm, machine->cgs, &error_abort) == -1) {

Perhaps use error_fatal rather than error_abort, since failures here are
more likely to be user errors (incompatible igvm config), rather than QEMU
programmer bugs.

> +            return;
> +        }
> +    }
> +#endif
>      phase_advance(PHASE_MACHINE_INITIALIZED);
>  }
>  

This adds igvm-cfg for all machines, regardless of architecture target.

Are igvm files fully cross-platform portable, or should we just put
this into the TYPE_X86_MACHINE base class to limit it ?

It at least reports errors if I try to load an IGVM file with
qemu-system-aarch64 + virt type

$ ./build/qemu-system-aarch64 -object igvm-cfg,file=../buildigvm/ovmf-sev.igvm,id=igvm -machine virt,igvm-cfg=igvm
qemu-system-aarch64: IGVM file does not describe a compatible supported platform

so that's good.

With regards,
Daniel
-- 
|: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org         -o-            https://fstop138.berrange.com :|
|: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|



^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH v3 09/15] docs/system: Add documentation on support for IGVM
  2024-06-21 14:29 ` [PATCH v3 09/15] docs/system: Add documentation on support for IGVM Roy Hopkins
@ 2024-06-24 14:09   ` Daniel P. Berrangé
  2024-07-01 14:28     ` Roy Hopkins
  0 siblings, 1 reply; 39+ messages in thread
From: Daniel P. Berrangé @ 2024-06-24 14:09 UTC (permalink / raw)
  To: Roy Hopkins
  Cc: qemu-devel, Paolo Bonzini, Stefano Garzarella, Marcelo Tosatti,
	Michael S . Tsirkin, Cornelia Huck, Marcel Apfelbaum,
	Sergio Lopez, Eduardo Habkost, Alistair Francis, Peter Xu,
	David Hildenbrand, Igor Mammedov, Tom Lendacky, Michael Roth,
	Ani Sinha, Jörg Roedel

On Fri, Jun 21, 2024 at 03:29:12PM +0100, Roy Hopkins wrote:
> IGVM support has been implemented for Confidential Guests that support
> AMD SEV and AMD SEV-ES. Add some documentation that gives some
> background on the IGVM format and how to use it to configure a
> confidential guest.
> 
> Signed-off-by: Roy Hopkins <roy.hopkins@suse.com>
> ---
>  docs/system/i386/amd-memory-encryption.rst |   2 +
>  docs/system/igvm.rst                       | 157 +++++++++++++++++++++
>  docs/system/index.rst                      |   1 +
>  3 files changed, 160 insertions(+)
>  create mode 100644 docs/system/igvm.rst

> diff --git a/docs/system/igvm.rst b/docs/system/igvm.rst
> new file mode 100644
> index 0000000000..b6e544a508
> --- /dev/null
> +++ b/docs/system/igvm.rst

> +Running a Confidential Guest configured using IGVM
> +--------------------------------------------------
> +
> +To run a confidential guest configured with IGVM you need to add an
> +``igvm-cfg`` object and refer to it from the ``-machine`` parameter:
> +
> +Example (for AMD SEV)::
> +
> +    qemu-system-x86_64 \
> +        <other parameters> \
> +        -machine ...,confidential-guest-support=sev0,igvm-cfg=igvm0 \
> +        -object sev-guest,id=sev0,cbitpos=47,reduced-phys-bits=1 \
> +        -object igvm-cfg,id=igvm0,file=/path/to/guest.igvm

Perhaps also illustrate use of your 'buildigvm' tool for creating
the igvm file first, assuming that's the tool users are most likely
to end up needing ?


With regards,
Daniel
-- 
|: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org         -o-            https://fstop138.berrange.com :|
|: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|



^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH v3 15/15] sev: Provide sev_features flags from IGVM VMSA to KVM_SEV_INIT2
  2024-06-21 14:29 ` [PATCH v3 15/15] sev: Provide sev_features flags from IGVM VMSA to KVM_SEV_INIT2 Roy Hopkins
@ 2024-06-24 14:14   ` Daniel P. Berrangé
  2024-07-01 13:50     ` Roy Hopkins
  0 siblings, 1 reply; 39+ messages in thread
From: Daniel P. Berrangé @ 2024-06-24 14:14 UTC (permalink / raw)
  To: Roy Hopkins
  Cc: qemu-devel, Paolo Bonzini, Stefano Garzarella, Marcelo Tosatti,
	Michael S . Tsirkin, Cornelia Huck, Marcel Apfelbaum,
	Sergio Lopez, Eduardo Habkost, Alistair Francis, Peter Xu,
	David Hildenbrand, Igor Mammedov, Tom Lendacky, Michael Roth,
	Ani Sinha, Jörg Roedel

On Fri, Jun 21, 2024 at 03:29:18PM +0100, Roy Hopkins wrote:
> IGVM files can contain an initial VMSA that should be applied to each
> vcpu as part of the initial guest state. The sev_features flags are
> provided as part of the VMSA structure. However, KVM only allows
> sev_features to be set during initialization and not as the guest is
> being prepared for launch.
> 
> This patch queries KVM for the supported set of sev_features flags and
> processes the IGVM file during kvm_init to determine any sev_features
> flags set in the IGVM file. These are then provided in the call to
> KVM_SEV_INIT2 to ensure the guest state matches that specified in the
> IGVM file.
> 
> This does cause the IGVM file to be processed twice. Firstly to extract
> the sev_features then secondly to actually configure the guest. However,
> the first pass is largely ignored meaning the overhead is minimal.
> 
> Signed-off-by: Roy Hopkins <roy.hopkins@suse.com>
> ---
>  target/i386/sev.c | 145 ++++++++++++++++++++++++++++++++++++++++------
>  1 file changed, 126 insertions(+), 19 deletions(-)
> 

>  static int sev_common_kvm_init(ConfidentialGuestSupport *cgs, Error **errp)
>  {
>      char *devname;
> @@ -1743,6 +1783,10 @@ static int sev_common_kvm_init(ConfidentialGuestSupport *cgs, Error **errp)
>          }
>      }
>  
> +    if (sev_init_supported_features(sev_common, errp) < 0) {
> +        return -1;
> +    }
> +
>      trace_kvm_sev_init();
>      if (x86_klass->kvm_type(X86_CONFIDENTIAL_GUEST(sev_common)) == KVM_X86_DEFAULT_VM) {
>          cmd = sev_es_enabled() ? KVM_SEV_ES_INIT : KVM_SEV_INIT;
> @@ -1750,6 +1794,38 @@ static int sev_common_kvm_init(ConfidentialGuestSupport *cgs, Error **errp)
>          ret = sev_ioctl(sev_common->sev_fd, cmd, NULL, &fw_error);
>      } else {
>          struct kvm_sev_init args = { 0 };
> +        MachineState *machine = MACHINE(qdev_get_machine());
> +
> +        /*
> +         * If configuration is provided via an IGVM file then the IGVM file
> +         * might contain configuration of the initial vcpu context. For SEV
> +         * the vcpu context includes the sev_features which should be applied
> +         * to the vcpu.
> +         *
> +         * KVM does not synchronize sev_features from CPU state. Instead it
> +         * requires sev_features to be provided as part of this initialization
> +         * call which is subsequently automatically applied to the VMSA of
> +         * each vcpu.
> +         *
> +         * The IGVM file is normally processed after initialization. Therefore
> +         * we need to pre-process it here to extract sev_features in order to
> +         * provide it to KVM_SEV_INIT2. Each cgs_* function that is called by
> +         * the IGVM processor detects this pre-process by observing the state
> +         * as SEV_STATE_UNINIT.
> +         */
> +        if (machine->igvm) {
> +            if (IGVM_CFG_GET_CLASS(machine->igvm)
> +                    ->process(machine->igvm, machine->cgs, errp) == -1) {
> +                return -1;
> +            }
> +            /*
> +             * KVM maintains a bitmask of allowed sev_features. This does not
> +             * include SVM_SEV_FEAT_SNP_ACTIVE which is set accordingly by KVM
> +             * itself. Therefore we need to clear this flag.
> +             */
> +            args.vmsa_features = sev_common->sev_features &
> +                                 ~SVM_SEV_FEAT_SNP_ACTIVE;
> +        }
>  
>          ret = sev_ioctl(sev_common->sev_fd, KVM_SEV_INIT2, &args, &fw_error);
>      }

What happens if the code path takes us down the KVM_SEV_INIT
route, rather than KVM_SEV_INIT2 ?  Should we be reporting an
error indicating that IGVM usage is incompatible with the legacy
KVM_SEV_INIT path ?


With regards,
Daniel
-- 
|: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org         -o-            https://fstop138.berrange.com :|
|: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|



^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH v3 14/15] i386/sev: Add implementation of CGS set_guest_policy()
  2024-06-21 14:29 ` [PATCH v3 14/15] i386/sev: Add implementation of CGS set_guest_policy() Roy Hopkins
@ 2024-06-24 14:53   ` Daniel P. Berrangé
  0 siblings, 0 replies; 39+ messages in thread
From: Daniel P. Berrangé @ 2024-06-24 14:53 UTC (permalink / raw)
  To: Roy Hopkins
  Cc: qemu-devel, Paolo Bonzini, Stefano Garzarella, Marcelo Tosatti,
	Michael S . Tsirkin, Cornelia Huck, Marcel Apfelbaum,
	Sergio Lopez, Eduardo Habkost, Alistair Francis, Peter Xu,
	David Hildenbrand, Igor Mammedov, Tom Lendacky, Michael Roth,
	Ani Sinha, Jörg Roedel

On Fri, Jun 21, 2024 at 03:29:17PM +0100, Roy Hopkins wrote:
> The new cgs_set_guest_policy() function is provided to receive the guest
> policy flags, SNP ID block and SNP ID authentication from guest
> configuration such as an IGVM file and apply it to the platform prior to
> launching the guest.
> 
> The policy is used to populate values for the existing 'policy',
> 'id_block' and 'id_auth' parameters. When provided, the guest policy is
> applied and the ID block configuration is used to verify the launch
> measurement and signatures. The guest is only successfully started if
> the expected launch measurements match the actual measurements and the
> signatures are valid.
> 
> Signed-off-by: Roy Hopkins <roy.hopkins@suse.com>
> ---
>  target/i386/sev.h | 12 +++++++
>  target/i386/sev.c | 83 +++++++++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 95 insertions(+)
> 
> diff --git a/target/i386/sev.h b/target/i386/sev.h
> index 2ccd6fe1e8..7b92102bd0 100644
> --- a/target/i386/sev.h
> +++ b/target/i386/sev.h
> @@ -157,6 +157,18 @@ struct QEMU_PACKED sev_es_save_area {
>      uint8_t fpreg_ymm[256];
>  };
>  
> +struct QEMU_PACKED sev_snp_id_authentication {
> +    uint32_t id_key_alg;
> +    uint32_t auth_key_algo;
> +    uint8_t reserved[56];
> +    uint8_t id_block_sig[512];
> +    uint8_t id_key[1028];
> +    uint8_t reserved2[60];
> +    uint8_t id_key_sig[512];
> +    uint8_t author_key[1028];
> +    uint8_t reserved3[892];
> +};
> +
>  #ifdef CONFIG_SEV
>  bool sev_enabled(void);
>  bool sev_es_enabled(void);
> diff --git a/target/i386/sev.c b/target/i386/sev.c
> index 5d9ef745bb..688b378c42 100644
> --- a/target/i386/sev.c
> +++ b/target/i386/sev.c
> @@ -2446,6 +2446,88 @@ static int cgs_get_mem_map_entry(int index,
>      return 0;
>  }
>  
> +static int cgs_set_guest_policy(ConfidentialGuestPolicyType policy_type,
> +                                uint64_t policy, void *policy_data1,
> +                                uint32_t policy_data1_size, void *policy_data2,
> +                                uint32_t policy_data2_size, Error **errp)
> +{
> +    if (policy_type != GUEST_POLICY_SEV) {
> +        error_setg(errp, "%s: Invalid guest policy type provided for SEV: %d",
> +        __func__, policy_type);
> +        return -1;
> +    }
> +    /*
> +     * SEV-SNP handles policy differently. The policy flags are defined in
> +     * kvm_start_conf.policy and an ID block and ID auth can be provided.
> +     */
> +    if (sev_snp_enabled()) {
> +        SevSnpGuestState *sev_snp_guest =
> +            SEV_SNP_GUEST(MACHINE(qdev_get_machine())->cgs);
> +        struct kvm_sev_snp_launch_finish *finish =
> +            &sev_snp_guest->kvm_finish_conf;
> +
> +        /*
> +         * The policy consists of flags in 'policy' and optionally an ID block
> +         * and ID auth in policy_data1 and policy_data2 respectively. The ID
> +         * block and auth are optional so clear any previous ID block and auth
> +         * and set them if provided, but always set the policy flags.
> +         */
> +        g_free(sev_snp_guest->id_block);
> +        g_free((guchar *)finish->id_block_uaddr);
> +        g_free(sev_snp_guest->id_auth);
> +        g_free((guchar *)finish->id_auth_uaddr);
> +        sev_snp_guest->id_block = NULL;
> +        finish->id_block_uaddr = 0;
> +        sev_snp_guest->id_auth = NULL;
> +        finish->id_auth_uaddr = 0;
> +
> +        if (policy_data1_size > 0) {
> +            struct sev_snp_id_authentication *id_auth =
> +                (struct sev_snp_id_authentication *)policy_data2;
> +
> +            if (policy_data1_size != KVM_SEV_SNP_ID_BLOCK_SIZE) {
> +                error_setg(errp, "%s: Invalid SEV-SNP ID block: incorrect size",
> +                           __func__);
> +                return -1;
> +            }
> +            if (policy_data2_size != KVM_SEV_SNP_ID_AUTH_SIZE) {
> +                error_setg(errp,
> +                           "%s: Invalid SEV-SNP ID auth block: incorrect size",
> +                           __func__);
> +                return -1;
> +            }

Perhaps add

  assert(policy_data1 != NULL);
  assert(policy_data2 != NULL);


> +            finish->id_block_uaddr =
> +                (__u64)g_malloc0(KVM_SEV_SNP_ID_BLOCK_SIZE);
> +            finish->id_authu_addr = (__u64)g_malloc0(KVM_SEV_SNP_ID_AUTH_SIZE);
> +            memcpy((void *)finish->id_block_uaddr, policy_data1,
> +                   KVM_SEV_SNP_ID_BLOCK_SIZE);
> +            memcpy((void *)finish->id_auth_uaddr, policy_data2,
> +                   KVM_SEV_SNP_ID_AUTH_SIZE);

How about using g_memdup2 ?

   finish->id_block_uaddr = (__u64)g_memdup2(policy_data1, KVM_SEV_SNP_ID_BLOCK_SIZE);
   finish->id_auth_uaddr = (__u64)g_memdup2(policy_data2, KVM_SEV_SNP_ID_AUTH_SIZE);

> +
> +            /*
> +             * Check if an author key has been provided and use that to flag
> +             * whether the author key is enabled. The first of the author key
> +             * must be non-zero to indicate the key type, which will currently
> +             * always be 2.
> +             */
> +            sev_snp_guest->kvm_finish_conf.auth_key_en =
> +                id_auth->author_key[0] ? 1 : 0;
> +            finish->id_block_en = 1;
> +        }
> +        sev_snp_guest->kvm_start_conf.policy = policy;
> +    } else {
> +        SevGuestState *sev_guest = SEV_GUEST(MACHINE(qdev_get_machine())->cgs);
> +        /* Only the policy flags are supported for SEV and SEV-ES */
> +        if ((policy_data1_size > 0) || (policy_data2_size > 0) || !sev_guest) {
> +            error_setg(errp, "%s: An ID block/ID auth block has been provided "
> +                             "but SEV-SNP is not supported", __func__);

Slightly more accurate to say  s/is not supported/is not enabled/

> +            return -1;
> +        }
> +        sev_guest->policy = policy;
> +    }
> +    return 0;
> +}
> +
>  static void
>  sev_common_class_init(ObjectClass *oc, void *data)
>  {
> @@ -2484,6 +2566,7 @@ sev_common_instance_init(Object *obj)
>      cgs->check_support = cgs_check_support;
>      cgs->set_guest_state = cgs_set_guest_state;
>      cgs->get_mem_map_entry = cgs_get_mem_map_entry;
> +    cgs->set_guest_policy = cgs_set_guest_policy;
>  
>      QTAILQ_INIT(&sev_common->launch_vmsa);
>  }
> -- 
> 2.43.0
> 

With regards,
Daniel
-- 
|: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org         -o-            https://fstop138.berrange.com :|
|: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|



^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH v3 13/15] backends/igvm: Handle policy for SEV guests
  2024-06-21 14:29 ` [PATCH v3 13/15] backends/igvm: Handle policy for SEV guests Roy Hopkins
@ 2024-06-24 14:56   ` Daniel P. Berrangé
  0 siblings, 0 replies; 39+ messages in thread
From: Daniel P. Berrangé @ 2024-06-24 14:56 UTC (permalink / raw)
  To: Roy Hopkins
  Cc: qemu-devel, Paolo Bonzini, Stefano Garzarella, Marcelo Tosatti,
	Michael S . Tsirkin, Cornelia Huck, Marcel Apfelbaum,
	Sergio Lopez, Eduardo Habkost, Alistair Francis, Peter Xu,
	David Hildenbrand, Igor Mammedov, Tom Lendacky, Michael Roth,
	Ani Sinha, Jörg Roedel

On Fri, Jun 21, 2024 at 03:29:16PM +0100, Roy Hopkins wrote:
> Adds a handler for the guest policy initialization IGVM section and
> builds an SEV policy based on this information and the ID block
> directive if present. The policy is applied using by calling
> 'set_guest_policy()' on the ConfidentialGuestSupport object.
> 
> Signed-off-by: Roy Hopkins <roy.hopkins@suse.com>
> ---
>  backends/igvm.c | 136 ++++++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 136 insertions(+)
> 
> diff --git a/backends/igvm.c b/backends/igvm.c
> index b6b7d30a3f..bb8038f9e9 100644
> --- a/backends/igvm.c
> +++ b/backends/igvm.c
> @@ -28,6 +28,33 @@ typedef struct IgvmParameterData {
>      uint32_t index;
>  } IgvmParameterData;
>  
> +/*
> + * Some directives are specific to particular confidential computing platforms.
> + * Define required types for each of those platforms here.
> + */
> +
> +/* SEV/SEV-ES/SEV-SNP */
> +struct QEMU_PACKED sev_id_block {
> +    uint8_t ld[48];
> +    uint8_t family_id[16];
> +    uint8_t image_id[16];
> +    uint32_t version;
> +    uint32_t guest_svn;
> +    uint64_t policy;
> +};
> +
> +struct QEMU_PACKED sev_id_authentication {
> +    uint32_t id_key_alg;
> +    uint32_t auth_key_algo;
> +    uint8_t reserved[56];
> +    uint8_t id_block_sig[512];
> +    uint8_t id_key[1028];
> +    uint8_t reserved2[60];
> +    uint8_t id_key_sig[512];
> +    uint8_t author_key[1028];
> +    uint8_t reserved3[892];
> +};
> +
>  /*
>   * QemuIgvm contains the information required during processing
>   * of a single IGVM file.
> @@ -39,6 +66,17 @@ typedef struct QemuIgvm {
>      uint32_t compatibility_mask;
>      unsigned current_header_index;
>      QTAILQ_HEAD(, IgvmParameterData) parameter_data;
> +    IgvmPlatformType platform_type;
> +
> +    /*
> +     * SEV-SNP platforms can contain an ID block and authentication
> +     * that should be verified by the guest.
> +     */
> +    struct sev_id_block *id_block;
> +    struct sev_id_authentication *id_auth;
> +
> +    /* Define the guest policy for SEV guests */
> +    uint64_t sev_policy;
>  
>      /* These variables keep track of contiguous page regions */
>      IGVM_VHS_PAGE_DATA region_prev_page_data;
> @@ -64,6 +102,11 @@ static int directive_environment_info(QemuIgvm *ctx, const uint8_t *header_data,
>                                        Error **errp);
>  static int directive_required_memory(QemuIgvm *ctx, const uint8_t *header_data,
>                                       Error **errp);
> +static int directive_snp_id_block(QemuIgvm *ctx, const uint8_t *header_data,
> +                                  Error **errp);
> +static int initialization_guest_policy(QemuIgvm *ctx,
> +                                       const uint8_t *header_data,
> +                                       Error **errp);
>  
>  struct IGVMHandler {
>      uint32_t type;
> @@ -87,6 +130,10 @@ static struct IGVMHandler handlers[] = {
>        directive_environment_info },
>      { IGVM_VHT_REQUIRED_MEMORY, IGVM_HEADER_SECTION_DIRECTIVE,
>        directive_required_memory },
> +    { IGVM_VHT_SNP_ID_BLOCK, IGVM_HEADER_SECTION_DIRECTIVE,
> +      directive_snp_id_block },
> +    { IGVM_VHT_GUEST_POLICY, IGVM_HEADER_SECTION_INITIALIZATION,
> +      initialization_guest_policy },
>  };
>  
>  static int handler(QemuIgvm *ctx, uint32_t type, Error **errp)
> @@ -619,6 +666,68 @@ static int directive_required_memory(QemuIgvm *ctx, const uint8_t *header_data,
>      return 0;
>  }
>  
> +static int directive_snp_id_block(QemuIgvm *ctx, const uint8_t *header_data,
> +                                  Error **errp)
> +{
> +    const IGVM_VHS_SNP_ID_BLOCK *igvm_id =
> +        (const IGVM_VHS_SNP_ID_BLOCK *)header_data;
> +
> +    if (ctx->compatibility_mask & igvm_id->compatibility_mask) {
> +        if (ctx->id_block) {
> +            error_setg(errp, "IGVM: Multiple ID blocks encountered "
> +                             "in IGVM file.");
> +            return -1;
> +        }
> +        ctx->id_block = g_malloc0(sizeof(struct sev_id_block));
> +        ctx->id_auth = g_malloc0(sizeof(struct sev_id_authentication));

QEMU has a preference for

  g_new0(struct sev_id_block, 1);
  g_new0(struct sev_id_authentication, 1);

> +
> +        memcpy(ctx->id_block->family_id, igvm_id->family_id,
> +               sizeof(ctx->id_block->family_id));
> +        memcpy(ctx->id_block->image_id, igvm_id->image_id,
> +               sizeof(ctx->id_block->image_id));
> +        ctx->id_block->guest_svn = igvm_id->guest_svn;
> +        ctx->id_block->version = 1;
> +        memcpy(ctx->id_block->ld, igvm_id->ld, sizeof(ctx->id_block->ld));
> +
> +        ctx->id_auth->id_key_alg = igvm_id->id_key_algorithm;
> +        memcpy(ctx->id_auth->id_block_sig, &igvm_id->id_key_signature,
> +               sizeof(igvm_id->id_key_signature));
> +
> +        ctx->id_auth->auth_key_algo = igvm_id->author_key_algorithm;
> +        memcpy(ctx->id_auth->id_key_sig, &igvm_id->author_key_signature,
> +               sizeof(igvm_id->author_key_signature));
> +
> +        /*
> +         * SEV and IGVM public key structure population are slightly different.
> +         * See SEV Secure Nested Paging Firmware ABI Specification, Chapter 10.
> +         */
> +        *((uint32_t *)ctx->id_auth->id_key) = igvm_id->id_public_key.curve;
> +        memcpy(&ctx->id_auth->id_key[4], &igvm_id->id_public_key.qx, 72);
> +        memcpy(&ctx->id_auth->id_key[76], &igvm_id->id_public_key.qy, 72);
> +
> +        *((uint32_t *)ctx->id_auth->author_key) =
> +            igvm_id->author_public_key.curve;
> +        memcpy(&ctx->id_auth->author_key[4], &igvm_id->author_public_key.qx,
> +               72);
> +        memcpy(&ctx->id_auth->author_key[76], &igvm_id->author_public_key.qy,
> +               72);
> +    }
> +
> +    return 0;
> +}

With regards,
Daniel
-- 
|: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org         -o-            https://fstop138.berrange.com :|
|: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|



^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH v3 03/15] backends/igvm: Add IGVM loader and configuration
  2024-06-21 14:29 ` [PATCH v3 03/15] backends/igvm: Add IGVM loader and configuration Roy Hopkins
  2024-06-24 13:29   ` Daniel P. Berrangé
@ 2024-06-27  9:06   ` Stefano Garzarella
  2024-06-27  9:14     ` Daniel P. Berrangé
  1 sibling, 1 reply; 39+ messages in thread
From: Stefano Garzarella @ 2024-06-27  9:06 UTC (permalink / raw)
  To: Roy Hopkins
  Cc: qemu-devel, Paolo Bonzini, Daniel P . Berrangé,
	Marcelo Tosatti, Michael S . Tsirkin, Cornelia Huck,
	Marcel Apfelbaum, Sergio Lopez, Eduardo Habkost, Alistair Francis,
	Peter Xu, David Hildenbrand, Igor Mammedov, Tom Lendacky,
	Michael Roth, Ani Sinha, Jörg Roedel

On Fri, Jun 21, 2024 at 03:29:06PM GMT, Roy Hopkins wrote:
>Adds an IGVM loader to QEMU which processes a given IGVM file and
>applies the directives within the file to the current guest
>configuration.
>
>The IGVM loader can be used to configure both confidential and
>non-confidential guests. For confidential guests, the
>ConfidentialGuestSupport object for the system is used to encrypt
>memory, apply the initial CPU state and perform other confidential guest
>operations.
>
>The loader is configured via a new IgvmCfg QOM object which allows the
>user to provide a path to the IGVM file to process.
>
>Signed-off-by: Roy Hopkins <roy.hopkins@suse.com>
>---
> qapi/qom.json             |  16 +
> backends/igvm.h           |  37 ++
> include/sysemu/igvm-cfg.h |  54 +++
> backends/igvm-cfg.c       |  66 ++++
> backends/igvm.c           | 791 ++++++++++++++++++++++++++++++++++++++
> backends/meson.build      |   2 +
> 6 files changed, 966 insertions(+)
> create mode 100644 backends/igvm.h
> create mode 100644 include/sysemu/igvm-cfg.h
> create mode 100644 backends/igvm-cfg.c
> create mode 100644 backends/igvm.c
>
>diff --git a/qapi/qom.json b/qapi/qom.json
>index 8bd299265e..e586707c4c 100644
>--- a/qapi/qom.json
>+++ b/qapi/qom.json
>@@ -874,6 +874,18 @@
>   'base': 'RngProperties',
>   'data': { '*filename': 'str' } }
>
>+##
>+# @IgvmCfgProperties:
>+#
>+# Properties common to objects that handle IGVM files.
>+#
>+# @file: IGVM file to use to configure guest (default: none)
>+#
>+# Since: 9.1
>+##
>+{ 'struct': 'IgvmCfgProperties',
>+  'data': { '*file': 'str' } }

     'if': 'CONFIG_IGVM'

I recently did a similar modification to QAPIs and Markus suggested to 
add the if here as well, see 
https://lore.kernel.org/qemu-devel/87zfs2z7jo.fsf@pond.sub.org/


>+
> ##
> # @SevCommonProperties:
> #
>@@ -1039,6 +1051,8 @@
>     'filter-redirector',
>     'filter-replay',
>     'filter-rewriter',
>+    { 'name': 'igvm-cfg',
>+      'if': 'CONFIG_IGVM' },
>     'input-barrier',
>     { 'name': 'input-linux',
>       'if': 'CONFIG_LINUX' },
>@@ -1111,6 +1125,8 @@
>       'filter-redirector':          'FilterRedirectorProperties',
>       'filter-replay':              'NetfilterProperties',
>       'filter-rewriter':            'FilterRewriterProperties',
>+      'igvm-cfg':                   { 'type': 'IgvmCfgProperties',
>+                                      'if': 'CONFIG_IGVM' },
>       'input-barrier':              'InputBarrierProperties',
>       'input-linux':                { 'type': 'InputLinuxProperties',
>                                       'if': 'CONFIG_LINUX' },
>diff --git a/backends/igvm.h b/backends/igvm.h
>new file mode 100644
>index 0000000000..3a3824b391
>--- /dev/null
>+++ b/backends/igvm.h
>@@ -0,0 +1,37 @@
>+/*
>+ * QEMU IGVM configuration backend for Confidential Guests
>+ *
>+ * Copyright (C) 2023-2024 SUSE
>+ *
>+ * Authors:
>+ *  Roy Hopkins <roy.hopkins@suse.com>
>+ *
>+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
>+ * See the COPYING file in the top-level directory.
>+ */
>+
>+#ifndef BACKENDS_IGVM_H
>+#define BACKENDS_IGVM_H
>+
>+#include "exec/confidential-guest-support.h"
>+#include "sysemu/igvm-cfg.h"
>+#include "qapi/error.h"
>+
>+#if defined(CONFIG_IGVM)
>+
>+int igvm_process_file(IgvmCfgState *igvm, ConfidentialGuestSupport *cgs,
>+                      Error **errp);
>+
>+#else
>+
>+static inline int igvm_process_file(IgvmCfgState *igvm,
>+                                    ConfidentialGuestSupport *cgs, Error **errp)
>+{
>+    error_setg(
>+        errp, "Invalid call to igvm_process_file when CONFIG_IGVM is disabled");
>+    return -1;
>+}
>+
>+#endif
>+
>+#endif
>diff --git a/include/sysemu/igvm-cfg.h b/include/sysemu/igvm-cfg.h
>new file mode 100644
>index 0000000000..8ac8b33d8d
>--- /dev/null
>+++ b/include/sysemu/igvm-cfg.h
>@@ -0,0 +1,54 @@
>+/*
>+ * QEMU IGVM interface
>+ *
>+ * Copyright (C) 2024 SUSE
>+ *
>+ * Authors:
>+ *  Roy Hopkins <roy.hopkins@suse.com>
>+ *
>+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
>+ * See the COPYING file in the top-level directory.
>+ */
>+
>+#ifndef QEMU_IGVM_CFG_H
>+#define QEMU_IGVM_CFG_H
>+
>+#include "qom/object.h"
>+
>+typedef struct IgvmCfgState {
>+    ObjectClass parent_class;
>+
>+    /*
>+     * filename: Filename that specifies a file that contains the configuration
>+     *           of the guest in Independent Guest Virtual Machine (IGVM)
>+     *           format.
>+     */
>+    char *filename;
>+} IgvmCfgState;
>+
>+typedef struct IgvmCfgClass {
>+    ObjectClass parent_class;
>+
>+    /*
>+     * If an IGVM filename has been specified then process the IGVM 
>file.
>+     * Performs a no-op if no filename has been specified.
>+     *
>+     * Returns 0 for ok and -1 on error.
>+     */
>+    int (*process)(IgvmCfgState *cfg, ConfidentialGuestSupport *cgs,
>+                   Error **errp);
>+
>+} IgvmCfgClass;
>+
>+#define TYPE_IGVM_CFG "igvm-cfg"
>+
>+#define IGVM_CFG_CLASS_SUFFIX "-" TYPE_IGVM_CFG
>+#define IGVM_CFG_CLASS_NAME(a) (a IGVM_CFG_CLASS_SUFFIX)
>+
>+#define IGVM_CFG_CLASS(klass) \
>+    OBJECT_CLASS_CHECK(IgvmCfgClass, (klass), TYPE_IGVM_CFG)
>+#define IGVM_CFG(obj) OBJECT_CHECK(IgvmCfgState, (obj), TYPE_IGVM_CFG)
>+#define IGVM_CFG_GET_CLASS(obj) \
>+    OBJECT_GET_CLASS(IgvmCfgClass, (obj), TYPE_IGVM_CFG)
>+
>+#endif
>diff --git a/backends/igvm-cfg.c b/backends/igvm-cfg.c
>new file mode 100644
>index 0000000000..5e18f3fd5f
>--- /dev/null
>+++ b/backends/igvm-cfg.c
>@@ -0,0 +1,66 @@
>+/*
>+ * QEMU IGVM interface
>+ *
>+ * Copyright (C) 2023-2024 SUSE
>+ *
>+ * Authors:
>+ *  Roy Hopkins <roy.hopkins@suse.com>
>+ *
>+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
>+ * See the COPYING file in the top-level directory.
>+ */
>+
>+#include "qemu/osdep.h"
>+
>+#include "sysemu/igvm-cfg.h"
>+#include "igvm.h"
>+#include "qom/object_interfaces.h"
>+
>+static char *get_igvm(Object *obj, Error **errp)
>+{
>+    IgvmCfgState *igvm = IGVM_CFG(obj);
>+    return g_strdup(igvm->filename);
>+}
>+
>+static void set_igvm(Object *obj, const char *value, Error **errp)
>+{
>+    IgvmCfgState *igvm = IGVM_CFG(obj);
>+    g_free(igvm->filename);
>+    igvm->filename = g_strdup(value);
>+}
>+
>+static int igvm_process(IgvmCfgState *cfg, ConfidentialGuestSupport *cgs,
>+                        Error **errp)
>+{
>+    if (!cfg->filename) {
>+        return 0;
>+    }
>+    return igvm_process_file(cfg, cgs, errp);
>+}
>+
>+static void igvm_cfg_class_init(ObjectClass *oc, void *data)
>+{
>+    IgvmCfgClass *igvmc = IGVM_CFG_CLASS(oc);
>+
>+    object_class_property_add_str(oc, "file", get_igvm, set_igvm);
>+    object_class_property_set_description(oc, "file",
>+                                          "Set the IGVM filename to use");
>+
>+    igvmc->process = igvm_process;
>+}
>+
>+static const TypeInfo igvm_cfg_type = {
>+    .name = TYPE_IGVM_CFG,
>+    .parent = TYPE_OBJECT,
>+    .class_init = igvm_cfg_class_init,
>+    .class_size = sizeof(IgvmCfgClass),
>+    .instance_size = sizeof(IgvmCfgState),
>+    .interfaces = (InterfaceInfo[]){ { TYPE_USER_CREATABLE }, {} }
>+};
>+
>+static void igvm_cfg_type_init(void)
>+{
>+    type_register_static(&igvm_cfg_type);
>+}
>+
>+type_init(igvm_cfg_type_init);
>diff --git a/backends/igvm.c b/backends/igvm.c
>new file mode 100644
>index 0000000000..25bbddfa33
>--- /dev/null
>+++ b/backends/igvm.c
>@@ -0,0 +1,791 @@
>+/*
>+ * QEMU IGVM configuration backend for guests
>+ *
>+ * Copyright (C) 2023-2024 SUSE
>+ *
>+ * Authors:
>+ *  Roy Hopkins <roy.hopkins@suse.com>
>+ *
>+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
>+ * See the COPYING file in the top-level directory.
>+ */
>+
>+#include "qemu/osdep.h"
>+
>+#include "igvm.h"
>+#include "qapi/error.h"
>+#include "exec/memory.h"
>+#include "exec/address-spaces.h"
>+#include "hw/core/cpu.h"
>+
>+#include <igvm/igvm.h>
>+#include <igvm/igvm_defs.h>
>+
>+typedef struct IgvmParameterData {
>+    QTAILQ_ENTRY(IgvmParameterData) next;
>+    uint8_t *data;
>+    uint32_t size;
>+    uint32_t index;
>+} IgvmParameterData;
>+
>+/*
>+ * QemuIgvm contains the information required during processing
>+ * of a single IGVM file.
>+ */
>+typedef struct QemuIgvm {
>+    IgvmHandle file;
>+    ConfidentialGuestSupport *cgs;
>+    ConfidentialGuestSupportClass *cgsc;
>+    uint32_t compatibility_mask;
>+    unsigned current_header_index;
>+    QTAILQ_HEAD(, IgvmParameterData) parameter_data;
>+
>+    /* These variables keep track of contiguous page regions */
>+    IGVM_VHS_PAGE_DATA region_prev_page_data;
>+    uint64_t region_start;
>+    unsigned region_start_index;
>+    unsigned region_last_index;
>+    unsigned region_page_count;
>+} QemuIgvm;
>+
>+static int directive_page_data(QemuIgvm *ctx, const uint8_t *header_data,
>+                               Error **errp);
>+static int directive_vp_context(QemuIgvm *ctx, const uint8_t *header_data,
>+                                Error **errp);
>+static int directive_parameter_area(QemuIgvm *ctx, const uint8_t *header_data,
>+                                    Error **errp);
>+static int directive_parameter_insert(QemuIgvm *ctx, const uint8_t *header_data,
>+                                      Error **errp);
>+static int directive_memory_map(QemuIgvm *ctx, const uint8_t *header_data,
>+                                Error **errp);
>+static int directive_vp_count(QemuIgvm *ctx, const uint8_t *header_data,
>+                              Error **errp);
>+static int directive_environment_info(QemuIgvm *ctx, const uint8_t *header_data,
>+                                      Error **errp);
>+static int directive_required_memory(QemuIgvm *ctx, const uint8_t 
>*header_data,
>+                                     Error **errp);
>+
>+struct IGVMHandler {
>+    uint32_t type;
>+    uint32_t section;
>+    int (*handler)(QemuIgvm *ctx, const uint8_t *header_data, Error **errp);
>+};
>+
>+static struct IGVMHandler handlers[] = {
>+    { IGVM_VHT_PAGE_DATA, IGVM_HEADER_SECTION_DIRECTIVE, directive_page_data },
>+    { IGVM_VHT_VP_CONTEXT, IGVM_HEADER_SECTION_DIRECTIVE,
>+      directive_vp_context },
>+    { IGVM_VHT_PARAMETER_AREA, IGVM_HEADER_SECTION_DIRECTIVE,
>+      directive_parameter_area },
>+    { IGVM_VHT_PARAMETER_INSERT, IGVM_HEADER_SECTION_DIRECTIVE,
>+      directive_parameter_insert },
>+    { IGVM_VHT_MEMORY_MAP, IGVM_HEADER_SECTION_DIRECTIVE,
>+      directive_memory_map },
>+    { IGVM_VHT_VP_COUNT_PARAMETER, IGVM_HEADER_SECTION_DIRECTIVE,
>+      directive_vp_count },
>+    { IGVM_VHT_ENVIRONMENT_INFO_PARAMETER, IGVM_HEADER_SECTION_DIRECTIVE,
>+      directive_environment_info },
>+    { IGVM_VHT_REQUIRED_MEMORY, IGVM_HEADER_SECTION_DIRECTIVE,
>+      directive_required_memory },
>+};
>+
>+static int handler(QemuIgvm *ctx, uint32_t type, Error **errp)
>+{
>+    size_t handler;
>+    IgvmHandle header_handle;
>+    const uint8_t *header_data;
>+    int result;
>+
>+    for (handler = 0; handler < G_N_ELEMENTS(handlers); handler++) {
>+        if (handlers[handler].type != type) {
>+            continue;
>+        }
>+        header_handle = igvm_get_header(ctx->file,
>+                                        handlers[handler].section,
>+                                        ctx->current_header_index);
>+        if (header_handle < 0) {
>+            error_setg(
>+                errp,
>+                "IGVM file is invalid: Failed to read directive header (code: %d)",
>+                (int)header_handle);
>+            return -1;
>+        }
>+        header_data = igvm_get_buffer(ctx->file, header_handle) +
>+                      sizeof(IGVM_VHS_VARIABLE_HEADER);

Should we check igvm_get_buffer() return value?

>+        result = handlers[handler].handler(ctx, header_data, errp);
>+        igvm_free_buffer(ctx->file, header_handle);
>+        return result;
>+    }
>+    error_setg(errp,
>+               "IGVM: Unknown header type encountered when processing file: "
>+               "(type 0x%X)",
>+               type);
>+    return -1;
>+}
>+
>+static void *igvm_prepare_memory(QemuIgvm *ctx, uint64_t addr, 
>uint64_t size,
>+                                 int region_identifier, Error **errp)
>+{
>+    ERRP_GUARD();
>+    MemoryRegion *igvm_pages = NULL;
>+    Int128 gpa_region_size;
>+    MemoryRegionSection mrs =
>+        memory_region_find(get_system_memory(), addr, size);
>+    if (mrs.mr) {
>+        if (!memory_region_is_ram(mrs.mr)) {
>+            memory_region_unref(mrs.mr);
>+            error_setg(
>+                errp,
>+                "Processing of IGVM file failed: Could not prepare memory "
>+                "at address 0x%lX due to existing non-RAM region",
>+                addr);
>+            return NULL;
>+        }
>+
>+        gpa_region_size = int128_make64(size);
>+        if (int128_lt(mrs.size, gpa_region_size)) {
>+            memory_region_unref(mrs.mr);
>+            error_setg(
>+                errp,
>+                "Processing of IGVM file failed: Could not prepare memory "
>+                "at address 0x%lX: region size exceeded",
>+                addr);
>+            return NULL;
>+        }
>+        return qemu_map_ram_ptr(mrs.mr->ram_block, mrs.offset_within_region);
>+    } else {
>+        /*
>+         * The region_identifier is the is the index of the IGVM directive that
>+         * contains the page with the lowest GPA in the region. This will
>+         * generate a unique region name.
>+         */
>+        g_autofree char *region_name =
>+            g_strdup_printf("igvm.%X", region_identifier);
>+        igvm_pages = g_malloc(sizeof(*igvm_pages));
>+        if (ctx->cgs && ctx->cgs->require_guest_memfd) {
>+            if (!memory_region_init_ram_guest_memfd(igvm_pages, NULL,
>+                                                    region_name, size, 
>errp)) {
>+                return NULL;
>+            }
>+        } else {
>+            if (!memory_region_init_ram(igvm_pages, NULL, region_name, size,
>+                                        errp)) {
>+                return NULL;
>+            }
>+        }
>+        memory_region_add_subregion(get_system_memory(), addr, igvm_pages);
>+        return memory_region_get_ram_ptr(igvm_pages);
>+    }
>+}
>+
>+static int igvm_type_to_cgs_type(IgvmPageDataType memory_type, bool unmeasured,
>+                                 bool zero)
>+{
>+    switch (memory_type) {
>+    case IGVM_PAGE_DATA_TYPE_NORMAL: {
>+        if (unmeasured) {
>+            return CGS_PAGE_TYPE_UNMEASURED;
>+        } else {
>+            return zero ? CGS_PAGE_TYPE_ZERO : CGS_PAGE_TYPE_NORMAL;
>+        }
>+    }
>+    case IGVM_PAGE_DATA_TYPE_SECRETS:
>+        return CGS_PAGE_TYPE_SECRETS;
>+    case IGVM_PAGE_DATA_TYPE_CPUID_DATA:
>+        return CGS_PAGE_TYPE_CPUID;
>+    case IGVM_PAGE_DATA_TYPE_CPUID_XF:
>+        return CGS_PAGE_TYPE_CPUID;
>+    default:
>+        return -1;
>+    }
>+}
>+
>+static bool page_attrs_equal(IgvmHandle igvm, unsigned header_index,
>+                             const IGVM_VHS_PAGE_DATA *page_1,
>+                             const IGVM_VHS_PAGE_DATA *page_2)
>+{
>+    IgvmHandle data_handle1, data_handle2;
>+
>+    /*
>+     * If one page has data and the other doesn't then this results in different
>+     * page types: NORMAL vs ZERO.
>+     */
>+    data_handle1 = igvm_get_header_data(igvm, IGVM_HEADER_SECTION_DIRECTIVE,
>+                                        header_index - 1);
>+    data_handle2 =
>+        igvm_get_header_data(igvm, IGVM_HEADER_SECTION_DIRECTIVE, header_index);
>+    if ((data_handle1 == IGVMAPI_NO_DATA) &&
>+        (data_handle2 != IGVMAPI_NO_DATA)) {
>+        return false;
>+    } else if ((data_handle1 != IGVMAPI_NO_DATA) &&
>+               (data_handle2 == IGVMAPI_NO_DATA)) {
>+        return false;
>+    }

What about this:
     if ((data_handle1 == IGVMAPI_NO_DATA ||
          data_handle2 == IGVMAPI_NO_DATA) &&
          data_handle1 != data_handle2) {
         return false;
     }

Should we also check other return values or igvm_get_header_data() can 
return just IGVMAPI_NO_DATA as error?

>+    return ((*(const uint32_t *)&page_1->flags ==
>+             *(const uint32_t *)&page_2->flags) &&
>+            (page_1->data_type == page_2->data_type) &&
>+            (page_1->compatibility_mask == page_2->compatibility_mask));
>+}
>+
>+static int igvm_process_mem_region(QemuIgvm *ctx, unsigned start_index,
>+                                   uint64_t gpa_start, unsigned page_count,
>+                                   const IgvmPageDataFlags *flags,
>+                                   const IgvmPageDataType page_type,
>+                                   Error **errp)
>+{
>+    uint8_t *region;
>+    IgvmHandle data_handle;
>+    const void *data;
>+    uint32_t data_size;
>+    unsigned page_index;
>+    bool zero = true;
>+    const uint64_t page_size = flags->is_2mb_page ? 0x200000 : 0x1000;
>+    int result;
>+    int cgs_page_type;
>+
>+    region = igvm_prepare_memory(ctx, gpa_start, page_count * page_size,
>+                                 start_index, errp);
>+    if (!region) {
>+        return -1;
>+    }
>+
>+    for (page_index = 0; page_index < page_count; page_index++) {
>+        data_handle = igvm_get_header_data(
>+            ctx->file, IGVM_HEADER_SECTION_DIRECTIVE, page_index + start_index);
>+        if (data_handle == IGVMAPI_NO_DATA) {
>+            /* No data indicates a zero page */
>+            memset(&region[page_index * page_size], 0, page_size);
>+        } else if (data_handle < 0) {
>+            error_setg(
>+                errp,
>+                "IGVM file contains invalid page data for directive with "
>+                "index %d",
>+                page_index + start_index);
>+            return -1;
>+        } else {
>+            zero = false;
>+            data_size = igvm_get_buffer_size(ctx->file, data_handle);
>+            if (data_size < page_size) {
>+                memset(&region[page_index * page_size], 0, page_size);
>+            } else if (data_size > page_size) {
>+                error_setg(errp,
>+                           "IGVM file contains page data with invalid size for "
>+                           "directive with index %d",
>+                           page_index + start_index);
>+                return -1;
>+            }
>+            data = igvm_get_buffer(ctx->file, data_handle);

Ditto.

>+            memcpy(&region[page_index * page_size], data, data_size);
>+            igvm_free_buffer(ctx->file, data_handle);
>+        }
>+    }
>+
>+    /*
>+     * If a confidential guest support object is provided then use it to set the
>+     * guest state.
>+     */
>+    if (ctx->cgs) {
>+        cgs_page_type =
>+            igvm_type_to_cgs_type(page_type, flags->unmeasured, zero);
>+        if (cgs_page_type < 0) {
>+            error_setg(errp,
>+                       "Invalid page type in IGVM file. Directives: %d to %d, "
>+                       "page type: %d",
>+                       start_index, start_index + page_count, page_type);
>+            return -1;
>+        }
>+
>+        result = ctx->cgsc->set_guest_state(
>+            gpa_start, region, page_size * page_count, cgs_page_type, 0, errp);
>+        if (result < 0) {
>+            return result;
>+        }
>+    }
>+    return 0;
>+}
>+
>+static int process_mem_page(QemuIgvm *ctx, const IGVM_VHS_PAGE_DATA *page_data,
>+                            Error **errp)
>+{
>+    if (page_data) {
>+        if (ctx->region_page_count == 0) {
>+            ctx->region_start = page_data->gpa;
>+            ctx->region_start_index = ctx->current_header_index;
>+        } else {
>+            if (!page_attrs_equal(ctx->file, ctx->current_header_index,
>+                                  page_data, &ctx->region_prev_page_data) ||
>+                ((ctx->region_prev_page_data.gpa +
>+                  (ctx->region_prev_page_data.flags.is_2mb_page ? 0x200000 :
>+                                                                  0x1000)) !=
>+                 page_data->gpa) ||
>+                (ctx->region_last_index != (ctx->current_header_index - 1))) {
>+                /* End of current region */
>+                if (igvm_process_mem_region(
>+                        ctx, ctx->region_start_index, ctx->region_start,
>+                        ctx->region_page_count,
>+                        &ctx->region_prev_page_data.flags,
>+                        ctx->region_prev_page_data.data_type, errp) < 0) {
>+                    return -1;
>+                }
>+                ctx->region_page_count = 0;
>+                ctx->region_start = page_data->gpa;
>+                ctx->region_start_index = ctx->current_header_index;
>+            }
>+        }
>+        memcpy(&ctx->region_prev_page_data, page_data,
>+               sizeof(ctx->region_prev_page_data));
>+        ctx->region_last_index = ctx->current_header_index;
>+        ctx->region_page_count++;
>+    } else {
>+        if (ctx->region_page_count > 0) {
>+            if (igvm_process_mem_region(
>+                    ctx, ctx->region_start_index, ctx->region_start,
>+                    ctx->region_page_count, &ctx->region_prev_page_data.flags,
>+                    ctx->region_prev_page_data.data_type, errp) < 0) {
>+                return -1;
>+            }
>+            ctx->region_page_count = 0;
>+        }
>+    }
>+    return 0;
>+}
>+
>+static int directive_page_data(QemuIgvm *ctx, const uint8_t *header_data,
>+                               Error **errp)
>+{
>+    const IGVM_VHS_PAGE_DATA *page_data =
>+        (const IGVM_VHS_PAGE_DATA *)header_data;
>+    if (page_data->compatibility_mask & ctx->compatibility_mask) {
>+        return process_mem_page(ctx, page_data, errp);
>+    }
>+    return 0;
>+}
>+
>+static int directive_vp_context(QemuIgvm *ctx, const uint8_t *header_data,
>+                                Error **errp)
>+{
>+    const IGVM_VHS_VP_CONTEXT *vp_context =
>+        (const IGVM_VHS_VP_CONTEXT *)header_data;
>+    IgvmHandle data_handle;
>+    uint8_t *data;
>+    int result;
>+
>+    /*
>+     * If a confidential guest support object is provided then use it to set the
>+     * guest state.
>+     */
>+    if (!ctx->cgs) {
>+        error_setg(
>+            errp,
>+            "A VP context is present in the IGVM file but is not supported "
>+            "by the current system.");
>+        return -1;
>+    }
>+
>+    if (vp_context->compatibility_mask & ctx->compatibility_mask) {
>+        data_handle = igvm_get_header_data(ctx->file,
>+                                           IGVM_HEADER_SECTION_DIRECTIVE,
>+                                           ctx->current_header_index);
>+        if (data_handle < 0) {
>+            error_setg(errp, "Invalid VP context in IGVM file. Error code: %X",
>+                       data_handle);
>+            return -1;
>+        }
>+
>+        data = (uint8_t *)igvm_get_buffer(ctx->file, data_handle);

Ditto.

>+        result = ctx->cgsc->set_guest_state(
>+            vp_context->gpa, data, igvm_get_buffer_size(ctx->file, data_handle),
>+            CGS_PAGE_TYPE_VMSA, vp_context->vp_index, errp);
>+        igvm_free_buffer(ctx->file, data_handle);
>+        if (result < 0) {
>+            return result;
>+        }
>+    }
>+    return 0;
>+}
>+
>+static int directive_parameter_area(QemuIgvm *ctx, const uint8_t *header_data,
>+                                    Error **errp)
>+{
>+    const IGVM_VHS_PARAMETER_AREA *param_area =
>+        (const IGVM_VHS_PARAMETER_AREA *)header_data;
>+    IgvmParameterData *param_entry;
>+
>+    param_entry = g_new0(IgvmParameterData, 1);
>+    param_entry->size = param_area->number_of_bytes;
>+    param_entry->index = param_area->parameter_area_index;
>+    param_entry->data = g_malloc0(param_entry->size);
>+
>+    QTAILQ_INSERT_TAIL(&ctx->parameter_data, param_entry, next);
>+    return 0;
>+}
>+
>+static int directive_parameter_insert(QemuIgvm *ctx, const uint8_t *header_data,
>+                                      Error **errp)
>+{
>+    const IGVM_VHS_PARAMETER_INSERT *param =
>+        (const IGVM_VHS_PARAMETER_INSERT *)header_data;
>+    IgvmParameterData *param_entry;
>+    int result;
>+    void *region;
>+
>+    QTAILQ_FOREACH(param_entry, &ctx->parameter_data, next)
>+    {
>+        if (param_entry->index == param->parameter_area_index) {
>+            region = igvm_prepare_memory(ctx, param->gpa, param_entry->size,
>+                                         ctx->current_header_index, errp);
>+            if (!region) {
>+                return -1;
>+            }
>+            memcpy(region, param_entry->data, param_entry->size);
>+            g_free(param_entry->data);
>+            param_entry->data = NULL;
>+
>+            /*
>+             * If a confidential guest support object is provided then use it to
>+             * set the guest state.
>+             */
>+            if (ctx->cgs) {
>+                result = ctx->cgsc->set_guest_state(param->gpa, region,
>+                                                    param_entry->size,
>+                                                    CGS_PAGE_TYPE_UNMEASURED, 0,
>+                                                    errp);
>+                if (result < 0) {
>+                    return -1;
>+                }
>+            }
>+        }
>+    }
>+    return 0;
>+}
>+
>+static int cmp_mm_entry(const void *a, const void *b)
>+{
>+    const IGVM_VHS_MEMORY_MAP_ENTRY *entry_a =
>+        (const IGVM_VHS_MEMORY_MAP_ENTRY *)a;
>+    const IGVM_VHS_MEMORY_MAP_ENTRY *entry_b =
>+        (const IGVM_VHS_MEMORY_MAP_ENTRY *)b;
>+    if (entry_a->starting_gpa_page_number < entry_b->starting_gpa_page_number) {
>+        return -1;
>+    } else if (entry_a->starting_gpa_page_number >
>+               entry_b->starting_gpa_page_number) {
>+        return 1;
>+    } else {
>+        return 0;
>+    }
>+}
>+
>+static int directive_memory_map(QemuIgvm *ctx, const uint8_t *header_data,
>+                                Error **errp)
>+{
>+    const IGVM_VHS_PARAMETER *param = (const IGVM_VHS_PARAMETER *)header_data;
>+    IgvmParameterData *param_entry;
>+    int max_entry_count;
>+    int entry = 0;
>+    IGVM_VHS_MEMORY_MAP_ENTRY *mm_entry;
>+    ConfidentialGuestMemoryMapEntry cgmm_entry;
>+    int retval = 0;
>+
>+    if (!ctx->cgs) {
>+        error_setg(errp,
>+                   "IGVM file contains a memory map but this is not supported "
>+                   "by the current system.");
>+        return -1;
>+    }
>+
>+    /* Find the parameter area that should hold the memory map */
>+    QTAILQ_FOREACH(param_entry, &ctx->parameter_data, next)
>+    {
>+        if (param_entry->index == param->parameter_area_index) {
>+            max_entry_count =
>+                param_entry->size / sizeof(IGVM_VHS_MEMORY_MAP_ENTRY);
>+            mm_entry = (IGVM_VHS_MEMORY_MAP_ENTRY *)param_entry->data;
>+
>+            retval = ctx->cgsc->get_mem_map_entry(entry, &cgmm_entry, errp);
>+            while (retval == 0) {
>+                if (entry > max_entry_count) {
>+                    error_setg(
>+                        errp,
>+                        "IGVM: guest memory map size exceeds parameter area defined in IGVM file");
>+                    return -1;
>+                }
>+                mm_entry[entry].starting_gpa_page_number = cgmm_entry.gpa >> 12;
>+                mm_entry[entry].number_of_pages = cgmm_entry.size >> 12;
>+
>+                switch (cgmm_entry.type) {
>+                case CGS_MEM_RAM:
>+                    mm_entry[entry].entry_type =
>+                        IGVM_MEMORY_MAP_ENTRY_TYPE_MEMORY;
>+                    break;
>+                case CGS_MEM_RESERVED:
>+                    mm_entry[entry].entry_type =
>+                        IGVM_MEMORY_MAP_ENTRY_TYPE_PLATFORM_RESERVED;
>+                    break;
>+                case CGS_MEM_ACPI:
>+                    mm_entry[entry].entry_type =
>+                        IGVM_MEMORY_MAP_ENTRY_TYPE_PLATFORM_RESERVED;
>+                    break;
>+                case CGS_MEM_NVS:
>+                    mm_entry[entry].entry_type =
>+                        IGVM_MEMORY_MAP_ENTRY_TYPE_PERSISTENT;
>+                    break;
>+                case CGS_MEM_UNUSABLE:
>+                    mm_entry[entry].entry_type =
>+                        IGVM_MEMORY_MAP_ENTRY_TYPE_PLATFORM_RESERVED;
>+                    break;
>+                }
>+                retval =
>+                    ctx->cgsc->get_mem_map_entry(++entry, &cgmm_entry, errp);
>+            }
>+            if (retval < 0) {
>+                return retval;
>+            }
>+            /* The entries need to be sorted */
>+            qsort(mm_entry, entry, sizeof(IGVM_VHS_MEMORY_MAP_ENTRY),
>+                  cmp_mm_entry);
>+
>+            break;
>+        }
>+    }
>+    return 0;
>+}
>+
>+static int directive_vp_count(QemuIgvm *ctx, const uint8_t *header_data,
>+                              Error **errp)
>+{
>+    const IGVM_VHS_PARAMETER *param = (const IGVM_VHS_PARAMETER *)header_data;
>+    IgvmParameterData *param_entry;
>+    uint32_t *vp_count;
>+    CPUState *cpu;
>+
>+    QTAILQ_FOREACH(param_entry, &ctx->parameter_data, next)
>+    {
>+        if (param_entry->index == param->parameter_area_index) {
>+            vp_count = (uint32_t *)(param_entry->data + param->byte_offset);
>+            *vp_count = 0;
>+            CPU_FOREACH(cpu)
>+            {
>+                (*vp_count)++;
>+            }
>+            break;
>+        }
>+    }
>+    return 0;
>+}
>+
>+static int directive_environment_info(QemuIgvm *ctx, const uint8_t *header_data,
>+                                      Error **errp)
>+{
>+    const IGVM_VHS_PARAMETER *param = (const IGVM_VHS_PARAMETER *)header_data;
>+    IgvmParameterData *param_entry;
>+    IgvmEnvironmentInfo *environmental_state;
>+
>+    QTAILQ_FOREACH(param_entry, &ctx->parameter_data, next)
>+    {
>+        if (param_entry->index == param->parameter_area_index) {
>+            environmental_state =
>+                (IgvmEnvironmentInfo *)(param_entry->data + param->byte_offset);
>+            environmental_state->memory_is_shared = 1;
>+            break;
>+        }
>+    }
>+    return 0;
>+}
>+
>+static int directive_required_memory(QemuIgvm *ctx, const uint8_t *header_data,
>+                                     Error **errp)
>+{
>+    const IGVM_VHS_REQUIRED_MEMORY *mem =
>+        (const IGVM_VHS_REQUIRED_MEMORY *)header_data;
>+    uint8_t *region;
>+    int result;
>+
>+    if (mem->compatibility_mask & ctx->compatibility_mask) {
>+        region = igvm_prepare_memory(ctx, mem->gpa, mem->number_of_bytes,
>+                                     ctx->current_header_index, errp);
>+        if (!region) {
>+            return -1;
>+        }
>+        if (ctx->cgs) {
>+            result = ctx->cgsc->set_guest_state(mem->gpa, region,
>+                                                mem->number_of_bytes,
>+                                                CGS_PAGE_TYPE_REQUIRED_MEMORY,
>+                                                0, errp);
>+            if (result < 0) {
>+                return result;
>+            }
>+        }
>+    }
>+    return 0;
>+}
>+
>+static int supported_platform_compat_mask(QemuIgvm *ctx, Error **errp)
>+{
>+    int32_t header_count;
>+    unsigned header_index;
>+    IgvmHandle header_handle;
>+    IGVM_VHS_SUPPORTED_PLATFORM *platform;
>+    uint32_t compatibility_mask_sev = 0;
>+    uint32_t compatibility_mask_sev_es = 0;
>+    uint32_t compatibility_mask_sev_snp = 0;
>+    uint32_t compatibility_mask = 0;
>+
>+    header_count = igvm_header_count(ctx->file, IGVM_HEADER_SECTION_PLATFORM);
>+    if (header_count < 0) {
>+        error_setg(errp,
>+                   "Invalid platform header count in IGVM file. Error code: %X",
>+                   header_count);
>+        return -1;
>+    }
>+
>+    for (header_index = 0; header_index < (unsigned)header_count;
>+         header_index++) {
>+        IgvmVariableHeaderType typ = igvm_get_header_type(
>+            ctx->file, IGVM_HEADER_SECTION_PLATFORM, header_index);
>+        if (typ == IGVM_VHT_SUPPORTED_PLATFORM) {
>+            header_handle = igvm_get_header(
>+                ctx->file, IGVM_HEADER_SECTION_PLATFORM, header_index);
>+            if (header_handle < 0) {
>+                error_setg(errp,
>+                           "Invalid platform header in IGVM file. "
>+                           "Index: %d, Error code: %X",
>+                           header_index, header_handle);
>+                return -1;
>+            }
>+            platform =
>+                (IGVM_VHS_SUPPORTED_PLATFORM *)(igvm_get_buffer(ctx->file,
>+                                                                header_handle) +
>+                                                sizeof(
>+                                                    IGVM_VHS_VARIABLE_HEADER));
>+            if ((platform->platform_type == IGVM_PLATFORM_TYPE_SEV_ES) &&
>+                ctx->cgs) {
>+                if (ctx->cgsc->check_support(
>+                        CGS_PLATFORM_SEV_ES, platform->platform_version,
>+                        platform->highest_vtl, platform->shared_gpa_boundary)) {
>+                    compatibility_mask_sev_es = platform->compatibility_mask;
>+                }
>+            } else if ((platform->platform_type == IGVM_PLATFORM_TYPE_SEV) &&
>+                ctx->cgs) {
>+                if (ctx->cgsc->check_support(
>+                        CGS_PLATFORM_SEV, platform->platform_version,
>+                        platform->highest_vtl, platform->shared_gpa_boundary)) {
>+                    compatibility_mask_sev = platform->compatibility_mask;
>+                }
>+            } else if ((platform->platform_type ==
>+                        IGVM_PLATFORM_TYPE_SEV_SNP) &&
>+                       ctx->cgs) {
>+                if (ctx->cgsc->check_support(
>+                        CGS_PLATFORM_SEV_SNP, platform->platform_version,
>+                        platform->highest_vtl, platform->shared_gpa_boundary)) {
>+                    compatibility_mask_sev_snp = platform->compatibility_mask;
>+                }
>+            } else if (platform->platform_type == IGVM_PLATFORM_TYPE_NATIVE) {
>+                compatibility_mask = platform->compatibility_mask;
>+            }
>+            igvm_free_buffer(ctx->file, header_handle);
>+        }
>+    }
>+    /* Choose the strongest supported isolation technology */
>+    if (compatibility_mask_sev_snp != 0) {
>+        ctx->compatibility_mask = compatibility_mask_sev_snp;
>+    } else if (compatibility_mask_sev_es != 0) {
>+        ctx->compatibility_mask = compatibility_mask_sev_es;
>+    } else if (compatibility_mask_sev != 0) {
>+        ctx->compatibility_mask = compatibility_mask_sev;
>+    } else if (compatibility_mask != 0) {
>+        ctx->compatibility_mask = compatibility_mask;
>+    } else {
>+        error_setg(
>+            errp,
>+            "IGVM file does not describe a compatible supported platform");
>+        return -1;
>+    }
>+    return 0;
>+}
>+
>+static IgvmHandle igvm_file_init(char *filename, Error **errp)
>+{
>+    IgvmHandle igvm;
>+    g_autofree uint8_t *buf = NULL;
>+    unsigned long len;
>+    g_autoptr(GError) gerr = NULL;
>+
>+    if (!g_file_get_contents(filename, (gchar **)&buf, &len, &gerr)) {
>+        error_setg(errp, "Unable to load %s: %s", filename, gerr->message);
>+        return -1;
>+    }
>+
>+    igvm = igvm_new_from_binary(buf, len);
>+    if (igvm < 0) {
>+        error_setg(errp, "Unable to parse IGVM file %s: %d", filename, igvm);
>+        return -1;
>+    }
>+    return igvm;
>+}
>+
>+int igvm_process_file(IgvmCfgState *cfg, ConfidentialGuestSupport *cgs,
>+                      Error **errp)
>+{
>+    int32_t header_count;
>+    IgvmParameterData *parameter;
>+    int retval = -1;
>+    QemuIgvm ctx;
>+
>+    memset(&ctx, 0, sizeof(ctx));
>+    ctx.file = igvm_file_init(cfg->filename, errp);
>+    if (ctx.file < 0) {
>+        return -1;
>+    }
>+
>+    /*
>+     * The ConfidentialGuestSupport object is optional and allows a confidential
>+     * guest platform to perform extra processing, such as page measurement, on
>+     * IGVM directives.
>+     */
>+    ctx.cgs = cgs;
>+    ctx.cgsc = cgs ? CONFIDENTIAL_GUEST_SUPPORT_GET_CLASS(cgs) : NULL;
>+
>+    /*
>+     * Check that the IGVM file provides configuration for the current
>+     * platform
>+     */
>+    if (supported_platform_compat_mask(&ctx, errp) < 0) {
>+        return -1;
>+    }
>+
>+    header_count = igvm_header_count(ctx.file, IGVM_HEADER_SECTION_DIRECTIVE);
>+    if (header_count <= 0) {
>+        error_setg(
>+            errp, "Invalid directive header count in IGVM file. Error code: %X",
>+            header_count);
>+        return -1;
>+    }
>+
>+    QTAILQ_INIT(&ctx.parameter_data);
>+
>+    for (ctx.current_header_index = 0;
>+         ctx.current_header_index < (unsigned)header_count;
>+         ctx.current_header_index++) {
>+        IgvmVariableHeaderType type = igvm_get_header_type(
>+            ctx.file, IGVM_HEADER_SECTION_DIRECTIVE, ctx.current_header_index);
>+        if (handler(&ctx, type, errp) < 0) {
>+            goto cleanup;
>+        }
>+    }
>+
>+    /*
>+     * Contiguous pages of data with compatible flags are grouped together in
>+     * order to reduce the number of memory regions we create. Make sure the
>+     * last group is processed with this call.
>+     */
>+    retval = process_mem_page(&ctx, NULL, errp);
>+
>+cleanup:
>+    QTAILQ_FOREACH(parameter, &ctx.parameter_data, next)
>+    {
>+        g_free(parameter->data);
>+        parameter->data = NULL;
>+    }
>+
>+    return retval;
>+}
>diff --git a/backends/meson.build b/backends/meson.build
>index d550ac19f7..7e07fa743b 100644
>--- a/backends/meson.build
>+++ b/backends/meson.build
>@@ -32,6 +32,8 @@ system_ss.add(when: gio, if_true: files('dbus-vmstate.c'))
> system_ss.add(when: 'CONFIG_SGX', if_true: files('hostmem-epc.c'))
> if igvm.found()
>   system_ss.add(igvm)
>+  system_ss.add(files('igvm-cfg.c'), igvm)
>+  system_ss.add(files('igvm.c'), igvm)
> endif
>
> subdir('tpm')
>-- 
>2.43.0
>



^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH v3 03/15] backends/igvm: Add IGVM loader and configuration
  2024-06-27  9:06   ` Stefano Garzarella
@ 2024-06-27  9:14     ` Daniel P. Berrangé
  2024-06-28 11:00       ` Roy Hopkins
  0 siblings, 1 reply; 39+ messages in thread
From: Daniel P. Berrangé @ 2024-06-27  9:14 UTC (permalink / raw)
  To: Stefano Garzarella
  Cc: Roy Hopkins, qemu-devel, Paolo Bonzini, Marcelo Tosatti,
	Michael S . Tsirkin, Cornelia Huck, Marcel Apfelbaum,
	Sergio Lopez, Eduardo Habkost, Alistair Francis, Peter Xu,
	David Hildenbrand, Igor Mammedov, Tom Lendacky, Michael Roth,
	Ani Sinha, Jörg Roedel

On Thu, Jun 27, 2024 at 11:06:50AM +0200, Stefano Garzarella wrote:
> On Fri, Jun 21, 2024 at 03:29:06PM GMT, Roy Hopkins wrote:
> > Adds an IGVM loader to QEMU which processes a given IGVM file and
> > applies the directives within the file to the current guest
> > configuration.
> > 
> > The IGVM loader can be used to configure both confidential and
> > non-confidential guests. For confidential guests, the
> > ConfidentialGuestSupport object for the system is used to encrypt
> > memory, apply the initial CPU state and perform other confidential guest
> > operations.
> > 
> > The loader is configured via a new IgvmCfg QOM object which allows the
> > user to provide a path to the IGVM file to process.
> > 
> > Signed-off-by: Roy Hopkins <roy.hopkins@suse.com>
> > ---
> > qapi/qom.json             |  16 +
> > backends/igvm.h           |  37 ++
> > include/sysemu/igvm-cfg.h |  54 +++
> > backends/igvm-cfg.c       |  66 ++++
> > backends/igvm.c           | 791 ++++++++++++++++++++++++++++++++++++++
> > backends/meson.build      |   2 +
> > 6 files changed, 966 insertions(+)
> > create mode 100644 backends/igvm.h
> > create mode 100644 include/sysemu/igvm-cfg.h
> > create mode 100644 backends/igvm-cfg.c
> > create mode 100644 backends/igvm.c
> > 
> > diff --git a/qapi/qom.json b/qapi/qom.json
> > index 8bd299265e..e586707c4c 100644
> > --- a/qapi/qom.json
> > +++ b/qapi/qom.json
> > @@ -874,6 +874,18 @@
> >   'base': 'RngProperties',
> >   'data': { '*filename': 'str' } }
> > 
> > +##
> > +# @IgvmCfgProperties:
> > +#
> > +# Properties common to objects that handle IGVM files.
> > +#
> > +# @file: IGVM file to use to configure guest (default: none)
> > +#
> > +# Since: 9.1
> > +##
> > +{ 'struct': 'IgvmCfgProperties',
> > +  'data': { '*file': 'str' } }
> 
>     'if': 'CONFIG_IGVM'
> 
> I recently did a similar modification to QAPIs and Markus suggested to add
> the if here as well, see
> https://lore.kernel.org/qemu-devel/87zfs2z7jo.fsf@pond.sub.org/

Yes, it avoids the code generator emitting an otherwise unusd
struct when CONFIG_IGVM is unset.


With regards,
Daniel
-- 
|: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org         -o-            https://fstop138.berrange.com :|
|: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|



^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH v3 05/15] i386/pc_sysfw: Ensure sysfw flash configuration does not conflict with IGVM
  2024-06-21 14:29 ` [PATCH v3 05/15] i386/pc_sysfw: Ensure sysfw flash configuration does not conflict with IGVM Roy Hopkins
@ 2024-06-27 12:38   ` Stefano Garzarella
  2024-06-28 11:10     ` Roy Hopkins
  0 siblings, 1 reply; 39+ messages in thread
From: Stefano Garzarella @ 2024-06-27 12:38 UTC (permalink / raw)
  To: Roy Hopkins
  Cc: qemu-devel, Paolo Bonzini, Daniel P . Berrangé,
	Marcelo Tosatti, Michael S . Tsirkin, Cornelia Huck,
	Marcel Apfelbaum, Sergio Lopez, Eduardo Habkost, Alistair Francis,
	Peter Xu, David Hildenbrand, Igor Mammedov, Tom Lendacky,
	Michael Roth, Ani Sinha, Jörg Roedel

On Fri, Jun 21, 2024 at 03:29:08PM GMT, Roy Hopkins wrote:
>When using an IGVM file the configuration of the system firmware is
>defined by IGVM directives contained in the file. In this case the user
>should not configure any pflash devices.
>
>This commit skips initialization of the ROM mode when pflash0 is not set
>then checks to ensure no pflash devices have been configured when using
>IGVM, exiting with an error message if this is not the case.
>
>Signed-off-by: Roy Hopkins <roy.hopkins@suse.com>
>---
> hw/i386/pc_sysfw.c | 23 +++++++++++++++++++++--
> 1 file changed, 21 insertions(+), 2 deletions(-)
>
>diff --git a/hw/i386/pc_sysfw.c b/hw/i386/pc_sysfw.c
>index ef80281d28..39e94ce144 100644
>--- a/hw/i386/pc_sysfw.c
>+++ b/hw/i386/pc_sysfw.c
>@@ -239,8 +239,13 @@ void pc_system_firmware_init(PCMachineState *pcms,
>     }
>
>     if (!pflash_blk[0]) {
>-        /* Machine property pflash0 not set, use ROM mode */
>-        x86_bios_rom_init(X86_MACHINE(pcms), "bios.bin", rom_memory, false);

We have the same call, a few lines above if `pci_enabled` is false, 
should we make the same change there as well?

>+        /*
>+         * Machine property pflash0 not set, use ROM mode unless using 
>IGVM,
>+         * in which case the firmware must be provided by the IGVM file.
>+         */
>+        if (!MACHINE(pcms)->igvm) {
>+            x86_bios_rom_init(X86_MACHINE(pcms), "bios.bin", rom_memory, false);
>+        }
>     } else {
>         if (kvm_enabled() && !kvm_readonly_mem_enabled()) {
>             /*
>@@ -256,6 +261,20 @@ void pc_system_firmware_init(PCMachineState *pcms,
>     }
>
>     pc_system_flash_cleanup_unused(pcms);
>+
>+    /*
>+     * The user should not have specified any pflash devices when using IGVM
>+     * to configure the guest.
>+     */
>+    if (MACHINE(pcms)->igvm) {
>+        for (i = 0; i < ARRAY_SIZE(pcms->flash); i++) {
>+            if (pcms->flash[i]) {
>+                error_report("pflash devices cannot be configured when "
>+                             "using IGVM");
>+                exit(1);
>+            }
>+        }
>+    }
> }
>
> void x86_firmware_configure(hwaddr gpa, void *ptr, int size)
>-- 
>2.43.0
>



^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH v3 06/15] sev: Update launch_update_data functions to use Error handling
  2024-06-21 14:29 ` [PATCH v3 06/15] sev: Update launch_update_data functions to use Error handling Roy Hopkins
@ 2024-06-27 12:48   ` Stefano Garzarella
  2024-06-28 11:20     ` Roy Hopkins
  0 siblings, 1 reply; 39+ messages in thread
From: Stefano Garzarella @ 2024-06-27 12:48 UTC (permalink / raw)
  To: Roy Hopkins
  Cc: qemu-devel, Paolo Bonzini, Daniel P . Berrangé,
	Marcelo Tosatti, Michael S . Tsirkin, Cornelia Huck,
	Marcel Apfelbaum, Sergio Lopez, Eduardo Habkost, Alistair Francis,
	Peter Xu, David Hildenbrand, Igor Mammedov, Tom Lendacky,
	Michael Roth, Ani Sinha, Jörg Roedel

On Fri, Jun 21, 2024 at 03:29:09PM GMT, Roy Hopkins wrote:
>The class function and implementations for updating launch data return
>a code in case of error. In some cases an error message is generated and
>in other cases, just the error return value is used.
>
>This small refactor adds an 'Error **errp' parameter to all functions
>which consistently set an error condition if a non-zero value is
>returned.
>
>Signed-off-by: Roy Hopkins <roy.hopkins@suse.com>
>---
> target/i386/sev.c | 67 +++++++++++++++++++++++++----------------------
> 1 file changed, 35 insertions(+), 32 deletions(-)
>
>diff --git a/target/i386/sev.c b/target/i386/sev.c
>index 30b83f1d77..1900c3d9b4 100644
>--- a/target/i386/sev.c
>+++ b/target/i386/sev.c
>@@ -121,7 +121,8 @@ struct SevCommonStateClass {
>                                        Error **errp);
>     int (*launch_start)(SevCommonState *sev_common);
>     void (*launch_finish)(SevCommonState *sev_common);
>-    int (*launch_update_data)(SevCommonState *sev_common, hwaddr gpa, uint8_t *ptr, uint64_t len);
>+    int (*launch_update_data)(SevCommonState *sev_common, hwaddr gpa,
>+                              uint8_t *ptr, uint64_t len, Error **errp);
>     int (*kvm_init)(ConfidentialGuestSupport *cgs, Error **errp);
> };
>
>@@ -942,14 +943,17 @@ out:
>     return ret;
> }
>
>-static int
>-sev_launch_update_data(SevCommonState *sev_common, hwaddr gpa, uint8_t *addr, uint64_t len)
>+static int sev_launch_update_data(SevCommonState *sev_common, hwaddr gpa,
>+                                  uint8_t *addr, uint64_t len, Error **errp)
> {
>     int ret, fw_error;
>     struct kvm_sev_launch_update_data update;
>
>     if (!addr || !len) {
>-        return 1;

Why were we returning 1 before? Was that a mistake?
Maybe we should mention it in the patch or fix it in another patch.

>+        error_setg(errp,
>+                   "%s: Invalid parameters provided for updating 
>launch data.",
>+                   __func__);
>+        return -1;
>     }
>
>     update.uaddr = (uintptr_t)addr;
>@@ -958,8 +962,8 @@ sev_launch_update_data(SevCommonState *sev_common, hwaddr gpa, uint8_t *addr, ui
>     ret = sev_ioctl(sev_common->sev_fd, KVM_SEV_LAUNCH_UPDATE_DATA,
>                     &update, &fw_error);
>     if (ret) {
>-        error_report("%s: LAUNCH_UPDATE ret=%d fw_error=%d '%s'",
>-                __func__, ret, fw_error, fw_error_to_str(fw_error));
>+        error_setg(errp, "%s: LAUNCH_UPDATE ret=%d fw_error=%d '%s'", __func__,
>+                   ret, fw_error, fw_error_to_str(fw_error));
>     }
>
>     return ret;
>@@ -1087,9 +1091,8 @@ sev_launch_finish(SevCommonState *sev_common)
>     migrate_add_blocker(&sev_mig_blocker, &error_fatal);
> }
>
>-static int
>-snp_launch_update_data(uint64_t gpa, void *hva,
>-                       uint32_t len, int type)
>+static int snp_launch_update_data(uint64_t gpa, void *hva, uint32_t len,
>+                                  int type, Error **errp)
> {
>     SevLaunchUpdateData *data;
>
>@@ -1104,13 +1107,12 @@ snp_launch_update_data(uint64_t gpa, void *hva,
>     return 0;
> }
>
>-static int
>-sev_snp_launch_update_data(SevCommonState *sev_common, hwaddr gpa,
>-                           uint8_t *ptr, uint64_t len)
>+static int sev_snp_launch_update_data(SevCommonState *sev_common, hwaddr gpa,
>+                                      uint8_t *ptr, uint64_t len, Error **errp)
> {
>-       int ret = snp_launch_update_data(gpa, ptr, len,
>-                                         KVM_SEV_SNP_PAGE_TYPE_NORMAL);
>-       return ret;
>+    int ret = snp_launch_update_data(gpa, ptr, len,
>+                                     KVM_SEV_SNP_PAGE_TYPE_NORMAL, errp);
>+    return ret;

Pre-existing, but while we're at it maybe we can remove ret.

> }
>
> static int
>@@ -1162,8 +1164,8 @@ sev_snp_cpuid_info_fill(SnpCpuidInfo *snp_cpuid_info,
>     return 0;
> }
>
>-static int
>-snp_launch_update_cpuid(uint32_t cpuid_addr, void *hva, uint32_t cpuid_len)
>+static int snp_launch_update_cpuid(uint32_t cpuid_addr, void *hva,
>+                                   uint32_t cpuid_len, Error **errp)
> {
>     KvmCpuidInfo kvm_cpuid_info = {0};
>     SnpCpuidInfo snp_cpuid_info;
>@@ -1180,26 +1182,26 @@ snp_launch_update_cpuid(uint32_t cpuid_addr, void *hva, uint32_t cpuid_len)
>     } while (ret == -E2BIG);
>
>     if (ret) {
>-        error_report("SEV-SNP: unable to query CPUID values for CPU: '%s'",
>-                     strerror(-ret));
>-        return 1;
>+        error_setg(errp, "SEV-SNP: unable to query CPUID values for CPU: '%s'",
>+                   strerror(-ret));
>+        return -1;
>     }
>
>     ret = sev_snp_cpuid_info_fill(&snp_cpuid_info, &kvm_cpuid_info);
>     if (ret) {
>-        error_report("SEV-SNP: failed to generate CPUID table information");
>-        return 1;
>+        error_setg(errp, "SEV-SNP: failed to generate CPUID table information");
>+        return -1;

Ditto for the 2 changes, although IIUC we never check the return value 
of snp_launch_update_cpuid().

>     }
>
>     memcpy(hva, &snp_cpuid_info, sizeof(snp_cpuid_info));
>
>     return snp_launch_update_data(cpuid_addr, hva, cpuid_len,
>-                                  KVM_SEV_SNP_PAGE_TYPE_CPUID);
>+                                  KVM_SEV_SNP_PAGE_TYPE_CPUID, errp);
> }
>
>-static int
>-snp_launch_update_kernel_hashes(SevSnpGuestState *sev_snp, uint32_t addr,
>-                                void *hva, uint32_t len)
>+static int snp_launch_update_kernel_hashes(SevSnpGuestState *sev_snp,
>+                                           uint32_t addr, void *hva,
>+                                           uint32_t len, Error **errp)
> {
>     int type = KVM_SEV_SNP_PAGE_TYPE_ZERO;
>     if (sev_snp->parent_obj.kernel_hashes) {
>@@ -1211,7 +1213,7 @@ snp_launch_update_kernel_hashes(SevSnpGuestState 
>*sev_snp, uint32_t addr,
>                sizeof(*sev_snp->kernel_hashes_data));
>         type = KVM_SEV_SNP_PAGE_TYPE_NORMAL;
>     }
>-    return snp_launch_update_data(addr, hva, len, type);
>+    return snp_launch_update_data(addr, hva, len, type, errp);
> }
>
> static int
>@@ -1249,12 +1251,14 @@ snp_populate_metadata_pages(SevSnpGuestState 
>*sev_snp,
>         }
>
>         if (type == KVM_SEV_SNP_PAGE_TYPE_CPUID) {
>-            ret = snp_launch_update_cpuid(desc->base, hva, desc->len);
>+            ret = snp_launch_update_cpuid(desc->base, hva, desc->len,
>+                                          &error_fatal);
>         } else if (desc->type == SEV_DESC_TYPE_SNP_KERNEL_HASHES) {
>             ret = snp_launch_update_kernel_hashes(sev_snp, desc->base, hva,
>-                                                  desc->len);
>+                                                  desc->len, &error_fatal);
>         } else {
>-            ret = snp_launch_update_data(desc->base, hva, desc->len, type);
>+            ret = snp_launch_update_data(desc->base, hva, desc->len, type,
>+                                         &error_fatal);
>         }
>
>         if (ret) {
>@@ -1541,9 +1545,8 @@ sev_encrypt_flash(hwaddr gpa, uint8_t *ptr, uint64_t len, Error **errp)
>     if (sev_check_state(sev_common, SEV_STATE_LAUNCH_UPDATE)) {
>         int ret;
>
>-        ret = klass->launch_update_data(sev_common, gpa, ptr, len);
>+        ret = klass->launch_update_data(sev_common, gpa, ptr, len, errp);
>         if (ret < 0) {
>-            error_setg(errp, "SEV: Failed to encrypt pflash rom");
>             return ret;
>         }
>     }
>-- 
>2.43.0
>



^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH v3 10/15] docs/interop/firmware.json: Add igvm to FirmwareDevice
  2024-06-21 14:29 ` [PATCH v3 10/15] docs/interop/firmware.json: Add igvm to FirmwareDevice Roy Hopkins
@ 2024-06-27 12:53   ` Stefano Garzarella
  2024-07-02 10:36     ` Roy Hopkins
  0 siblings, 1 reply; 39+ messages in thread
From: Stefano Garzarella @ 2024-06-27 12:53 UTC (permalink / raw)
  To: Roy Hopkins
  Cc: qemu-devel, Paolo Bonzini, Daniel P . Berrangé,
	Marcelo Tosatti, Michael S . Tsirkin, Cornelia Huck,
	Marcel Apfelbaum, Sergio Lopez, Eduardo Habkost, Alistair Francis,
	Peter Xu, David Hildenbrand, Igor Mammedov, Tom Lendacky,
	Michael Roth, Ani Sinha, Jörg Roedel

On Fri, Jun 21, 2024 at 03:29:13PM GMT, Roy Hopkins wrote:
>Create an enum entry within FirmwareDevice for 'igvm' to describe that
>an IGVM file can be used to map firmware into memory as an alternative
>to pre-existing firmware devices.
>
>Signed-off-by: Roy Hopkins <roy.hopkins@suse.com>
>---
> docs/interop/firmware.json | 9 ++++++++-
> 1 file changed, 8 insertions(+), 1 deletion(-)
>
>diff --git a/docs/interop/firmware.json b/docs/interop/firmware.json
>index 54a1fc6c10..9a9178606e 100644
>--- a/docs/interop/firmware.json
>+++ b/docs/interop/firmware.json
>@@ -55,10 +55,17 @@
> #
> # @memory: The firmware is to be mapped into memory.
> #
>+# @igvm: The firmware is defined by a file conforming to the IGVM
>+#        specification and mapped into memory according to directives
>+#        defined in the file. This is similar to @memory but may
>+#        include additional processing defined by the IGVM file
>+#        including initial CPU state or population of metadata into
>+#        the guest address space.

Should we add (Since: 9.1) ?

I'm not sure about that, since I don't see it used much in docs/interop/

Thanks,
Stefano

>+#
> # Since: 3.0
> ##
> { 'enum' : 'FirmwareDevice',
>-  'data' : [ 'flash', 'kernel', 'memory' ] }
>+  'data' : [ 'flash', 'kernel', 'memory', 'igvm' ] }
>
> ##
> # @FirmwareTarget:
>-- 
>2.43.0
>



^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH v3 00/15] Introduce support for IGVM files
  2024-06-24 13:50 ` [PATCH v3 00/15] Introduce support for IGVM files Daniel P. Berrangé
@ 2024-06-28 10:56   ` Roy Hopkins
  0 siblings, 0 replies; 39+ messages in thread
From: Roy Hopkins @ 2024-06-28 10:56 UTC (permalink / raw)
  To: Daniel P. Berrangé
  Cc: qemu-devel, Paolo Bonzini, Stefano Garzarella, Marcelo Tosatti,
	Michael S . Tsirkin, Cornelia Huck, Marcel Apfelbaum,
	Sergio Lopez, Eduardo Habkost, Alistair Francis, Peter Xu,
	David Hildenbrand, Igor Mammedov, Tom Lendacky, Michael Roth,
	Ani Sinha, Jörg Roedel

On Mon, 2024-06-24 at 14:50 +0100, Daniel P. Berrangé wrote:
> On Fri, Jun 21, 2024 at 03:29:03PM +0100, Roy Hopkins wrote:
> > Based-on: 02d9c38236
> > 
> > Here is v3 of the set of patches to add support for IGVM files to QEMU.
> > 
> > Firstly, apologies for the long gap between v2 and v3. This was due to a
> > number
> > of factors, but particularly holding back until SEV-SNP support landed in
> > QEMU
> > as well as for some changes to be merged in the upstream IGVM specification
> > and
> > library. The delay meant that I could include the SEV-SNP IGVM changes that
> > I
> > had been separately maintaining for COCONUT-SVSM into this series, giving
> > full
> > support for the full range of SEV technologies.
> > 
> > Thank-you to everyone who reviewed the previous set of patches [1]. I
> > have hopefully addressed all of the comments in those reviews. Some of these
> > changes required a reasonable amount of rework. Along with the inclusion of
> > support for SEV-SNP, this has resulted in a fairly large set of differences
> > from
> > v2. This v3 patch series is also available on github: [2]
> 
> snip
> 
> FYI, I hit some compile problems reporting array bounds issues,
> with this posting. I'm using Fedora 40, which has gcc 14 in
> case that matters.
> 

The reason I was not seeing this was because I included `--enable-debug` which
apparently hides the problem.

There is technically a bounds issue with the function overflowing the end of the
array for the TR and LDTR registers but, coincidently or not, it overflows into
the correct register storage meaning the code works correctly.

I've added a patch to fix this for v4.

> 
> In file included from
> /var/home/berrange/src/virt/qemu/include/sysemu/kvm.h:214,
>                  from ../target/i386/sev.c:29:
> In function ‘cpu_x86_load_seg_cache’,
>     inlined from ‘sev_apply_cpu_context’ at ../target/i386/sev.c:454:13:
> ../target/i386/cpu.h:2236:20: error: array subscript 6 is above array bounds
> of ‘SegmentCache[6]’ [-Werror=array-bounds=]
>  2236 |     sc = &env->segs[seg_reg];
>       |           ~~~~~~~~~^~~~~~~~~
> ../target/i386/cpu.h: In function ‘sev_apply_cpu_context’:
> ../target/i386/cpu.h:1682:18: note: while referencing ‘segs’
>  1682 |     SegmentCache segs[6]; /* selector values */
>       |                  ^~~~
> In function ‘cpu_x86_load_seg_cache’,
>     inlined from ‘sev_apply_cpu_context’ at ../target/i386/sev.c:454:13:
> ../target/i386/cpu.h:2236:20: error: array subscript 6 is above array bounds
> of ‘SegmentCache[6]’ [-Werror=array-bounds=]
>  2236 |     sc = &env->segs[seg_reg];
>       |           ~~~~~~~~~^~~~~~~~~
> ../target/i386/cpu.h: In function ‘sev_apply_cpu_context’:
> ../target/i386/cpu.h:1682:18: note: while referencing ‘segs’
>  1682 |     SegmentCache segs[6]; /* selector values */
>       |                  ^~~~
> In function ‘cpu_x86_load_seg_cache’,
>     inlined from ‘sev_apply_cpu_context’ at ../target/i386/sev.c:454:13:
> ../target/i386/cpu.h:2236:20: error: array subscript 6 is above array bounds
> of ‘SegmentCache[6]’ [-Werror=array-bounds=]
>  2236 |     sc = &env->segs[seg_reg];
>       |           ~~~~~~~~~^~~~~~~~~
> ../target/i386/cpu.h: In function ‘sev_apply_cpu_context’:
> ../target/i386/cpu.h:1682:18: note: while referencing ‘segs’
>  1682 |     SegmentCache segs[6]; /* selector values */
>       |                  ^~~~
> ...cut many more similar warnings...
> 
> 
> With regards,
> Daniel


Regards,
Roy


^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH v3 03/15] backends/igvm: Add IGVM loader and configuration
  2024-06-24 13:29   ` Daniel P. Berrangé
@ 2024-06-28 10:59     ` Roy Hopkins
  0 siblings, 0 replies; 39+ messages in thread
From: Roy Hopkins @ 2024-06-28 10:59 UTC (permalink / raw)
  To: Daniel P. Berrangé
  Cc: qemu-devel, Paolo Bonzini, Stefano Garzarella, Marcelo Tosatti,
	Michael S . Tsirkin, Cornelia Huck, Marcel Apfelbaum,
	Sergio Lopez, Eduardo Habkost, Alistair Francis, Peter Xu,
	David Hildenbrand, Igor Mammedov, Tom Lendacky, Michael Roth,
	Ani Sinha, Jörg Roedel

On Mon, 2024-06-24 at 14:29 +0100, Daniel P. Berrangé wrote:
> On Fri, Jun 21, 2024 at 03:29:06PM +0100, Roy Hopkins wrote:
> > Adds an IGVM loader to QEMU which processes a given IGVM file and
> > applies the directives within the file to the current guest
> > configuration.
> > 
> > The IGVM loader can be used to configure both confidential and
> > non-confidential guests. For confidential guests, the
> > ConfidentialGuestSupport object for the system is used to encrypt
> > memory, apply the initial CPU state and perform other confidential guest
> > operations.
> > 
> > The loader is configured via a new IgvmCfg QOM object which allows the
> > user to provide a path to the IGVM file to process.
> > 
> > Signed-off-by: Roy Hopkins <roy.hopkins@suse.com>
> > ---
> >  qapi/qom.json             |  16 +
> >  backends/igvm.h           |  37 ++
> >  include/sysemu/igvm-cfg.h |  54 +++
> >  backends/igvm-cfg.c       |  66 ++++
> >  backends/igvm.c           | 791 ++++++++++++++++++++++++++++++++++++++
> >  backends/meson.build      |   2 +
> >  6 files changed, 966 insertions(+)
> >  create mode 100644 backends/igvm.h
> >  create mode 100644 include/sysemu/igvm-cfg.h
> >  create mode 100644 backends/igvm-cfg.c
> >  create mode 100644 backends/igvm.c
> > 
> > diff --git a/qapi/qom.json b/qapi/qom.json
> > index 8bd299265e..e586707c4c 100644
> > --- a/qapi/qom.json
> > +++ b/qapi/qom.json
> > @@ -874,6 +874,18 @@
> >    'base': 'RngProperties',
> >    'data': { '*filename': 'str' } }
> >  
> > +##
> > +# @IgvmCfgProperties:
> > +#
> > +# Properties common to objects that handle IGVM files.
> > +#
> > +# @file: IGVM file to use to configure guest (default: none)
> > +#
> > +# Since: 9.1
> > +##
> > +{ 'struct': 'IgvmCfgProperties',
> > +  'data': { '*file': 'str' } }
> > +
> >  ##
> >  # @SevCommonProperties:
> >  #
> > @@ -1039,6 +1051,8 @@
> >      'filter-redirector',
> >      'filter-replay',
> >      'filter-rewriter',
> > +    { 'name': 'igvm-cfg',
> > +      'if': 'CONFIG_IGVM' },
> >      'input-barrier',
> >      { 'name': 'input-linux',
> >        'if': 'CONFIG_LINUX' },
> > @@ -1111,6 +1125,8 @@
> >        'filter-redirector':          'FilterRedirectorProperties',
> >        'filter-replay':              'NetfilterProperties',
> >        'filter-rewriter':            'FilterRewriterProperties',
> > +      'igvm-cfg':                   { 'type': 'IgvmCfgProperties',
> > +                                      'if': 'CONFIG_IGVM' },
> >        'input-barrier':              'InputBarrierProperties',
> >        'input-linux':                { 'type': 'InputLinuxProperties',
> >                                        'if': 'CONFIG_LINUX' },
> > diff --git a/backends/igvm.h b/backends/igvm.h
> > new file mode 100644
> > index 0000000000..3a3824b391
> > --- /dev/null
> > +++ b/backends/igvm.h
> > @@ -0,0 +1,37 @@
> > +/*
> > + * QEMU IGVM configuration backend for Confidential Guests
> > + *
> > + * Copyright (C) 2023-2024 SUSE
> > + *
> > + * Authors:
> > + *  Roy Hopkins <roy.hopkins@suse.com>
> > + *
> > + * This work is licensed under the terms of the GNU GPL, version 2 or
> > later.
> > + * See the COPYING file in the top-level directory.
> > + */
> > +
> > +#ifndef BACKENDS_IGVM_H
> > +#define BACKENDS_IGVM_H
> > +
> > +#include "exec/confidential-guest-support.h"
> > +#include "sysemu/igvm-cfg.h"
> > +#include "qapi/error.h"
> > +
> > +#if defined(CONFIG_IGVM)
> > +
> > +int igvm_process_file(IgvmCfgState *igvm, ConfidentialGuestSupport *cgs,
> > +                      Error **errp);
> > +
> > +#else
> > +
> > +static inline int igvm_process_file(IgvmCfgState *igvm,
> > +                                    ConfidentialGuestSupport *cgs, Error
> > **errp)
> > +{
> > +    error_setg(
> > +        errp, "Invalid call to igvm_process_file when CONFIG_IGVM is
> > disabled");
> > +    return -1;
> > +}
> 
> IIUC, the only call to this is from igvm-cfg.c, but that file is not
> compiled if CONFIG_IGVM is unset.
> 
> IOW, isn't this inline stub deadcode that will never been compiled,
> let alone called ?  Can we just get rid of the conditional in this
> file entirely ? If someone were to change the build rules to create
> an inconsistent build scenario, they'd get a linker error from the
> missing igvm_process_file which seems good enough ?
> 
> 
Yes, this has now ended up as dead code since being moved around during the
rework of previous versions. I'll remove it and the conditionals.
> 
> 
> > diff --git a/backends/igvm.c b/backends/igvm.c
> > new file mode 100644
> > index 0000000000..25bbddfa33
> > --- /dev/null
> > +++ b/backends/igvm.c
> > @@ -0,0 +1,791 @@
> > +/*
> > + * QEMU IGVM configuration backend for guests
> > + *
> > + * Copyright (C) 2023-2024 SUSE
> > + *
> > + * Authors:
> > + *  Roy Hopkins <roy.hopkins@suse.com>
> > + *
> > + * This work is licensed under the terms of the GNU GPL, version 2 or
> > later.
> > + * See the COPYING file in the top-level directory.
> > + */
> > +
> > +#include "qemu/osdep.h"
> > +
> > +#include "igvm.h"
> > +#include "qapi/error.h"
> > +#include "exec/memory.h"
> > +#include "exec/address-spaces.h"
> > +#include "hw/core/cpu.h"
> > +
> > +#include <igvm/igvm.h>
> > +#include <igvm/igvm_defs.h>
> > +
> > +typedef struct IgvmParameterData {
> > +    QTAILQ_ENTRY(IgvmParameterData) next;
> > +    uint8_t *data;
> > +    uint32_t size;
> > +    uint32_t index;
> > +} IgvmParameterData;
> > +
> > +/*
> > + * QemuIgvm contains the information required during processing
> > + * of a single IGVM file.
> > + */
> > +typedef struct QemuIgvm {
> > +    IgvmHandle file;
> > +    ConfidentialGuestSupport *cgs;
> > +    ConfidentialGuestSupportClass *cgsc;
> > +    uint32_t compatibility_mask;
> > +    unsigned current_header_index;
> > +    QTAILQ_HEAD(, IgvmParameterData) parameter_data;
> > +
> > +    /* These variables keep track of contiguous page regions */
> > +    IGVM_VHS_PAGE_DATA region_prev_page_data;
> > +    uint64_t region_start;
> > +    unsigned region_start_index;
> > +    unsigned region_last_index;
> > +    unsigned region_page_count;
> > +} QemuIgvm;
> > +
> > +static int directive_page_data(QemuIgvm *ctx, const uint8_t *header_data,
> > +                               Error **errp);
> > +static int directive_vp_context(QemuIgvm *ctx, const uint8_t *header_data,
> > +                                Error **errp);
> > +static int directive_parameter_area(QemuIgvm *ctx, const uint8_t
> > *header_data,
> > +                                    Error **errp);
> > +static int directive_parameter_insert(QemuIgvm *ctx, const uint8_t
> > *header_data,
> > +                                      Error **errp);
> > +static int directive_memory_map(QemuIgvm *ctx, const uint8_t *header_data,
> > +                                Error **errp);
> > +static int directive_vp_count(QemuIgvm *ctx, const uint8_t *header_data,
> > +                              Error **errp);
> > +static int directive_environment_info(QemuIgvm *ctx, const uint8_t
> > *header_data,
> > +                                      Error **errp);
> > +static int directive_required_memory(QemuIgvm *ctx, const uint8_t
> > *header_data,
> > +                                     Error **errp);
> > +
> > +struct IGVMHandler {
> > +    uint32_t type;
> > +    uint32_t section;
> > +    int (*handler)(QemuIgvm *ctx, const uint8_t *header_data, Error
> > **errp);
> 
> All the impls of the handler are doing manual casts from 'header_data' to
> some other struct. If you declare this 'void *' instead, then all those
> manual casts are redundant.
> 
> > +};
> > +
> > +static struct IGVMHandler handlers[] = {
> > +    { IGVM_VHT_PAGE_DATA, IGVM_HEADER_SECTION_DIRECTIVE,
> > directive_page_data },
> > +    { IGVM_VHT_VP_CONTEXT, IGVM_HEADER_SECTION_DIRECTIVE,
> > +      directive_vp_context },
> > +    { IGVM_VHT_PARAMETER_AREA, IGVM_HEADER_SECTION_DIRECTIVE,
> > +      directive_parameter_area },
> > +    { IGVM_VHT_PARAMETER_INSERT, IGVM_HEADER_SECTION_DIRECTIVE,
> > +      directive_parameter_insert },
> > +    { IGVM_VHT_MEMORY_MAP, IGVM_HEADER_SECTION_DIRECTIVE,
> > +      directive_memory_map },
> > +    { IGVM_VHT_VP_COUNT_PARAMETER, IGVM_HEADER_SECTION_DIRECTIVE,
> > +      directive_vp_count },
> > +    { IGVM_VHT_ENVIRONMENT_INFO_PARAMETER, IGVM_HEADER_SECTION_DIRECTIVE,
> > +      directive_environment_info },
> > +    { IGVM_VHT_REQUIRED_MEMORY, IGVM_HEADER_SECTION_DIRECTIVE,
> > +      directive_required_memory },
> > +};
> > +
> > +static int handler(QemuIgvm *ctx, uint32_t type, Error **errp)
> > +{
> > +    size_t handler;
> > +    IgvmHandle header_handle;
> > +    const uint8_t *header_data;
> > +    int result;
> > +
> > +    for (handler = 0; handler < G_N_ELEMENTS(handlers); handler++) {
> > +        if (handlers[handler].type != type) {
> > +            continue;
> > +        }
> > +        header_handle = igvm_get_header(ctx->file,
> > +                                        handlers[handler].section,
> > +                                        ctx->current_header_index);
> > +        if (header_handle < 0) {
> > +            error_setg(
> > +                errp,
> > +                "IGVM file is invalid: Failed to read directive header
> > (code: %d)",
> > +                (int)header_handle);
> > +            return -1;
> > +        }
> > +        header_data = igvm_get_buffer(ctx->file, header_handle) +
> > +                      sizeof(IGVM_VHS_VARIABLE_HEADER);
> > +        result = handlers[handler].handler(ctx, header_data, errp);
> > +        igvm_free_buffer(ctx->file, header_handle);
> > +        return result;
> > +    }
> > +    error_setg(errp,
> > +               "IGVM: Unknown header type encountered when processing file:
> > "
> > +               "(type 0x%X)",
> > +               type);
> > +    return -1;
> > +}
> > +
> > +static void *igvm_prepare_memory(QemuIgvm *ctx, uint64_t addr, uint64_t
> > size,
> > +                                 int region_identifier, Error **errp)
> > +{
> > +    ERRP_GUARD();
> > +    MemoryRegion *igvm_pages = NULL;
> > +    Int128 gpa_region_size;
> > +    MemoryRegionSection mrs =
> > +        memory_region_find(get_system_memory(), addr, size);
> > +    if (mrs.mr) {
> > +        if (!memory_region_is_ram(mrs.mr)) {
> > +            memory_region_unref(mrs.mr);
> > +            error_setg(
> > +                errp,
> > +                "Processing of IGVM file failed: Could not prepare memory "
> > +                "at address 0x%lX due to existing non-RAM region",
> > +                addr);
> > +            return NULL;
> > +        }
> > +
> > +        gpa_region_size = int128_make64(size);
> > +        if (int128_lt(mrs.size, gpa_region_size)) {
> > +            memory_region_unref(mrs.mr);
> > +            error_setg(
> > +                errp,
> > +                "Processing of IGVM file failed: Could not prepare memory "
> > +                "at address 0x%lX: region size exceeded",
> > +                addr);
> > +            return NULL;
> > +        }
> > +        return qemu_map_ram_ptr(mrs.mr->ram_block,
> > mrs.offset_within_region);
> > +    } else {
> > +        /*
> > +         * The region_identifier is the is the index of the IGVM directive
> > that
> > +         * contains the page with the lowest GPA in the region. This will
> > +         * generate a unique region name.
> > +         */
> > +        g_autofree char *region_name =
> > +            g_strdup_printf("igvm.%X", region_identifier);
> > +        igvm_pages = g_malloc(sizeof(*igvm_pages));
> 
> nit-pick, typically we'd prefer g_new0 over g_malloc + sizeof
> 
> > +        if (ctx->cgs && ctx->cgs->require_guest_memfd) {
> > +            if (!memory_region_init_ram_guest_memfd(igvm_pages, NULL,
> > +                                                    region_name, size,
> > errp)) {
> > +                return NULL;
> > +            }
> > +        } else {
> > +            if (!memory_region_init_ram(igvm_pages, NULL, region_name,
> > size,
> > +                                        errp)) {
> > +                return NULL;
> > +            }
> > +        }
> > +        memory_region_add_subregion(get_system_memory(), addr, igvm_pages);
> > +        return memory_region_get_ram_ptr(igvm_pages);
> > +    }
> 
> > +static int directive_memory_map(QemuIgvm *ctx, const uint8_t *header_data,
> > +                                Error **errp)
> > +{
> > +    const IGVM_VHS_PARAMETER *param = (const IGVM_VHS_PARAMETER
> > *)header_data;
> 
> If changing header_data to 'const void *', then this repetition
> of typename goes away e.g.
> 
>    const IGVM_VHS_PARAMETER *param = header_data;
> 
> 
> 
> 
> > +int igvm_process_file(IgvmCfgState *cfg, ConfidentialGuestSupport *cgs,
> > +                      Error **errp)
> > +{
> > +    int32_t header_count;
> > +    IgvmParameterData *parameter;
> > +    int retval = -1;
> > +    QemuIgvm ctx;
> > +
> > +    memset(&ctx, 0, sizeof(ctx));
> 
> Can avoid the manual memset using
> 
>   QemuIgvm ctx = {};
> 
> > +    ctx.file = igvm_file_init(cfg->filename, errp);
> > +    if (ctx.file < 0) {
> > +        return -1;
> > +    }
> > +
> > +    /*
> > +     * The ConfidentialGuestSupport object is optional and allows a
> > confidential
> > +     * guest platform to perform extra processing, such as page
> > measurement, on
> > +     * IGVM directives.
> > +     */
> > +    ctx.cgs = cgs;
> > +    ctx.cgsc = cgs ? CONFIDENTIAL_GUEST_SUPPORT_GET_CLASS(cgs) : NULL;
> > +
> > +    /*
> > +     * Check that the IGVM file provides configuration for the current
> > +     * platform
> > +     */
> > +    if (supported_platform_compat_mask(&ctx, errp) < 0) {
> > +        return -1;
> > +    }
> > +
> > +    header_count = igvm_header_count(ctx.file,
> > IGVM_HEADER_SECTION_DIRECTIVE);
> > +    if (header_count <= 0) {
> > +        error_setg(
> > +            errp, "Invalid directive header count in IGVM file. Error code:
> > %X",
> > +            header_count);
> > +        return -1;
> > +    }
> > +
> > +    QTAILQ_INIT(&ctx.parameter_data);
> > +
> > +    for (ctx.current_header_index = 0;
> > +         ctx.current_header_index < (unsigned)header_count;
> > +         ctx.current_header_index++) {
> > +        IgvmVariableHeaderType type = igvm_get_header_type(
> > +            ctx.file, IGVM_HEADER_SECTION_DIRECTIVE,
> > ctx.current_header_index);
> > +        if (handler(&ctx, type, errp) < 0) {
> > +            goto cleanup;
> > +        }
> > +    }
> > +
> > +    /*
> > +     * Contiguous pages of data with compatible flags are grouped together
> > in
> > +     * order to reduce the number of memory regions we create. Make sure
> > the
> > +     * last group is processed with this call.
> > +     */
> > +    retval = process_mem_page(&ctx, NULL, errp);
> > +
> > +cleanup:
> > +    QTAILQ_FOREACH(parameter, &ctx.parameter_data, next)
> > +    {
> > +        g_free(parameter->data);
> > +        parameter->data = NULL;
> > +    }
> > +
> > +    return retval;
> > +}
> 
> With regards,
> Daniel


^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH v3 03/15] backends/igvm: Add IGVM loader and configuration
  2024-06-27  9:14     ` Daniel P. Berrangé
@ 2024-06-28 11:00       ` Roy Hopkins
  0 siblings, 0 replies; 39+ messages in thread
From: Roy Hopkins @ 2024-06-28 11:00 UTC (permalink / raw)
  To: Daniel P. Berrangé, Stefano Garzarella
  Cc: qemu-devel, Paolo Bonzini, Marcelo Tosatti, Michael S . Tsirkin,
	Cornelia Huck, Marcel Apfelbaum, Sergio Lopez, Eduardo Habkost,
	Alistair Francis, Peter Xu, David Hildenbrand, Igor Mammedov,
	Tom Lendacky, Michael Roth, Ani Sinha, Jörg Roedel

On Thu, 2024-06-27 at 10:14 +0100, Daniel P. Berrangé wrote:
> On Thu, Jun 27, 2024 at 11:06:50AM +0200, Stefano Garzarella wrote:
> > On Fri, Jun 21, 2024 at 03:29:06PM GMT, Roy Hopkins wrote:
> > > Adds an IGVM loader to QEMU which processes a given IGVM file and
> > > applies the directives within the file to the current guest
> > > configuration.
> > > 
> > > The IGVM loader can be used to configure both confidential and
> > > non-confidential guests. For confidential guests, the
> > > ConfidentialGuestSupport object for the system is used to encrypt
> > > memory, apply the initial CPU state and perform other confidential guest
> > > operations.
> > > 
> > > The loader is configured via a new IgvmCfg QOM object which allows the
> > > user to provide a path to the IGVM file to process.
> > > 
> > > Signed-off-by: Roy Hopkins <roy.hopkins@suse.com>
> > > ---
> > > qapi/qom.json             |  16 +
> > > backends/igvm.h           |  37 ++
> > > include/sysemu/igvm-cfg.h |  54 +++
> > > backends/igvm-cfg.c       |  66 ++++
> > > backends/igvm.c           | 791 ++++++++++++++++++++++++++++++++++++++
> > > backends/meson.build      |   2 +
> > > 6 files changed, 966 insertions(+)
> > > create mode 100644 backends/igvm.h
> > > create mode 100644 include/sysemu/igvm-cfg.h
> > > create mode 100644 backends/igvm-cfg.c
> > > create mode 100644 backends/igvm.c
> > > 
> > > diff --git a/qapi/qom.json b/qapi/qom.json
> > > index 8bd299265e..e586707c4c 100644
> > > --- a/qapi/qom.json
> > > +++ b/qapi/qom.json
> > > @@ -874,6 +874,18 @@
> > >   'base': 'RngProperties',
> > >   'data': { '*filename': 'str' } }
> > > 
> > > +##
> > > +# @IgvmCfgProperties:
> > > +#
> > > +# Properties common to objects that handle IGVM files.
> > > +#
> > > +# @file: IGVM file to use to configure guest (default: none)
> > > +#
> > > +# Since: 9.1
> > > +##
> > > +{ 'struct': 'IgvmCfgProperties',
> > > +  'data': { '*file': 'str' } }
> > 
> >     'if': 'CONFIG_IGVM'
> > 
> > I recently did a similar modification to QAPIs and Markus suggested to add
> > the if here as well, see
> > https://lore.kernel.org/qemu-devel/87zfs2z7jo.fsf@pond.sub.org/
> 
> Yes, it avoids the code generator emitting an otherwise unusd
> struct when CONFIG_IGVM is unset.

Thanks. I'll add that change.

> 
> 
> With regards,
> Daniel



^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH v3 04/15] hw/core/machine: Add igvm-cfg object and processing for IGVM files
  2024-06-24 14:00   ` Daniel P. Berrangé
@ 2024-06-28 11:09     ` Roy Hopkins
  2024-06-28 11:23       ` Daniel P. Berrangé
  0 siblings, 1 reply; 39+ messages in thread
From: Roy Hopkins @ 2024-06-28 11:09 UTC (permalink / raw)
  To: Daniel P. Berrangé
  Cc: qemu-devel, Paolo Bonzini, Stefano Garzarella, Marcelo Tosatti,
	Michael S . Tsirkin, Cornelia Huck, Marcel Apfelbaum,
	Sergio Lopez, Eduardo Habkost, Alistair Francis, Peter Xu,
	David Hildenbrand, Igor Mammedov, Tom Lendacky, Michael Roth,
	Ani Sinha, Jörg Roedel

On Mon, 2024-06-24 at 15:00 +0100, Daniel P. Berrangé wrote:
> On Fri, Jun 21, 2024 at 03:29:07PM +0100, Roy Hopkins wrote:
> > An IGVM file contains configuration of guest state that should be
> > applied during configuration of the guest, before the guest is started.
> > 
> > This patch allows the user to add an igvm-cfg object to the machine
> > configuration that allows an IGVM file to be configured that will be
> > applied to the guest before it is started.
> > 
> > If an IGVM configuration is provided then the IGVM file is processed at
> > the end of the board initialization, before the state transition to
> > PHASE_MACHINE_INITIALIZED.
> > 
> > Signed-off-by: Roy Hopkins <roy.hopkins@suse.com>
> > ---
> >  include/hw/boards.h |  2 ++
> >  hw/core/machine.c   | 20 ++++++++++++++++++++
> >  qemu-options.hx     | 25 +++++++++++++++++++++++++
> >  3 files changed, 47 insertions(+)
> > 
> > diff --git a/include/hw/boards.h b/include/hw/boards.h
> > index 73ad319d7d..4c1484ba0b 100644
> > --- a/include/hw/boards.h
> > +++ b/include/hw/boards.h
> > @@ -4,6 +4,7 @@
> >  #define HW_BOARDS_H
> >  
> >  #include "exec/memory.h"
> > +#include "sysemu/igvm-cfg.h"
> >  #include "sysemu/hostmem.h"
> >  #include "sysemu/blockdev.h"
> >  #include "qapi/qapi-types-machine.h"
> > @@ -382,6 +383,7 @@ struct MachineState {
> >      bool suppress_vmdesc;
> >      bool enable_graphics;
> >      ConfidentialGuestSupport *cgs;
> > +    IgvmCfgState *igvm;
> >      HostMemoryBackend *memdev;
> >      /*
> >       * convenience alias to ram_memdev_id backend memory region
> > diff --git a/hw/core/machine.c b/hw/core/machine.c
> > index 655d75c21f..f9f879172c 100644
> > --- a/hw/core/machine.c
> > +++ b/hw/core/machine.c
> > @@ -1094,6 +1094,16 @@ static void machine_class_init(ObjectClass *oc, void
> > *data)
> >      object_class_property_set_description(oc, "confidential-guest-support",
> >                                            "Set confidential guest scheme to
> > support");
> >  
> > +#if defined(CONFIG_IGVM)
> > +    object_class_property_add_link(oc, "igvm-cfg",
> > +                                   TYPE_IGVM_CFG,
> > +                                   offsetof(MachineState, igvm),
> > +                                   object_property_allow_set_link,
> > +                                   OBJ_PROP_LINK_STRONG);
> > +    object_class_property_set_description(oc, "igvm-cfg",
> > +                                          "Set IGVM configuration");
> > +#endif
> > +
> >      /* For compatibility */
> >      object_class_property_add_str(oc, "memory-encryption",
> >          machine_get_memory_encryption, machine_set_memory_encryption);
> > @@ -1582,6 +1592,16 @@ void machine_run_board_init(MachineState *machine,
> > const char *mem_path, Error *
> >  
> >      accel_init_interfaces(ACCEL_GET_CLASS(machine->accelerator));
> >      machine_class->init(machine);
> > +
> > +#if defined(CONFIG_IGVM)
> > +    /* Apply guest state from IGVM if supplied */
> > +    if (machine->igvm) {
> > +        if (IGVM_CFG_GET_CLASS(machine->igvm)
> > +                ->process(machine->igvm, machine->cgs, &error_abort) == -1)
> > {
> 
> Perhaps use error_fatal rather than error_abort, since failures here are
> more likely to be user errors (incompatible igvm config), rather than QEMU
> programmer bugs.

Makes sense. I'll change it.

> 
> > +            return;
> > +        }
> > +    }
> > +#endif
> >      phase_advance(PHASE_MACHINE_INITIALIZED);
> >  }
> >  
> 
> This adds igvm-cfg for all machines, regardless of architecture target.
> 
> Are igvm files fully cross-platform portable, or should we just put
> this into the TYPE_X86_MACHINE base class to limit it ?
> 
> It at least reports errors if I try to load an IGVM file with
> qemu-system-aarch64 + virt type
> 
> $ ./build/qemu-system-aarch64 -object igvm-cfg,file=../buildigvm/ovmf-
> sev.igvm,id=igvm -machine virt,igvm-cfg=igvm
> qemu-system-aarch64: IGVM file does not describe a compatible supported
> platform
> 
> so that's good.

The IGVM specification is designed to support non X86 platforms, hence its
inclusion for all machines. Support for non-X86 is likely to result in changes
to the specification though that will impact the library we depend on.

There would obviously need to be some further implementation to support non-X86
machines in QEMU, in the same way that further implementation is required to
support other X86 confidential computing platforms such as TDX.

So, this poses the question: should we move it to TYPE_X86_MACHINE as the
current supported platforms are all on X86? Or should we leave it where it is
with a view to adding non X86 platform support with less impact later? I'd
appreciate your views on this.

> 
> With regards,
> Daniel



^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH v3 05/15] i386/pc_sysfw: Ensure sysfw flash configuration does not conflict with IGVM
  2024-06-27 12:38   ` Stefano Garzarella
@ 2024-06-28 11:10     ` Roy Hopkins
  0 siblings, 0 replies; 39+ messages in thread
From: Roy Hopkins @ 2024-06-28 11:10 UTC (permalink / raw)
  To: Stefano Garzarella
  Cc: qemu-devel, Paolo Bonzini, Daniel P . Berrangé,
	Marcelo Tosatti, Michael S . Tsirkin, Cornelia Huck,
	Marcel Apfelbaum, Sergio Lopez, Eduardo Habkost, Alistair Francis,
	Peter Xu, David Hildenbrand, Igor Mammedov, Tom Lendacky,
	Michael Roth, Ani Sinha, Jörg Roedel

On Thu, 2024-06-27 at 14:38 +0200, Stefano Garzarella wrote:
> On Fri, Jun 21, 2024 at 03:29:08PM GMT, Roy Hopkins wrote:
> > When using an IGVM file the configuration of the system firmware is
> > defined by IGVM directives contained in the file. In this case the user
> > should not configure any pflash devices.
> > 
> > This commit skips initialization of the ROM mode when pflash0 is not set
> > then checks to ensure no pflash devices have been configured when using
> > IGVM, exiting with an error message if this is not the case.
> > 
> > Signed-off-by: Roy Hopkins <roy.hopkins@suse.com>
> > ---
> > hw/i386/pc_sysfw.c | 23 +++++++++++++++++++++--
> > 1 file changed, 21 insertions(+), 2 deletions(-)
> > 
> > diff --git a/hw/i386/pc_sysfw.c b/hw/i386/pc_sysfw.c
> > index ef80281d28..39e94ce144 100644
> > --- a/hw/i386/pc_sysfw.c
> > +++ b/hw/i386/pc_sysfw.c
> > @@ -239,8 +239,13 @@ void pc_system_firmware_init(PCMachineState *pcms,
> >     }
> > 
> >     if (!pflash_blk[0]) {
> > -        /* Machine property pflash0 not set, use ROM mode */
> > -        x86_bios_rom_init(X86_MACHINE(pcms), "bios.bin", rom_memory,
> > false);
> 
> We have the same call, a few lines above if `pci_enabled` is false, 
> should we make the same change there as well?

Yes. I'll add the same change there.

> 
> > +        /*
> > +         * Machine property pflash0 not set, use ROM mode unless using 
> > IGVM,
> > +         * in which case the firmware must be provided by the IGVM file.
> > +         */
> > +        if (!MACHINE(pcms)->igvm) {
> > +            x86_bios_rom_init(X86_MACHINE(pcms), "bios.bin", rom_memory,
> > false);
> > +        }
> >     } else {
> >         if (kvm_enabled() && !kvm_readonly_mem_enabled()) {
> >             /*
> > @@ -256,6 +261,20 @@ void pc_system_firmware_init(PCMachineState *pcms,
> >     }
> > 
> >     pc_system_flash_cleanup_unused(pcms);
> > +
> > +    /*
> > +     * The user should not have specified any pflash devices when using
> > IGVM
> > +     * to configure the guest.
> > +     */
> > +    if (MACHINE(pcms)->igvm) {
> > +        for (i = 0; i < ARRAY_SIZE(pcms->flash); i++) {
> > +            if (pcms->flash[i]) {
> > +                error_report("pflash devices cannot be configured when "
> > +                             "using IGVM");
> > +                exit(1);
> > +            }
> > +        }
> > +    }
> > }
> > 
> > void x86_firmware_configure(hwaddr gpa, void *ptr, int size)
> > -- 
> > 2.43.0
> > 
> 



^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH v3 06/15] sev: Update launch_update_data functions to use Error handling
  2024-06-27 12:48   ` Stefano Garzarella
@ 2024-06-28 11:20     ` Roy Hopkins
  0 siblings, 0 replies; 39+ messages in thread
From: Roy Hopkins @ 2024-06-28 11:20 UTC (permalink / raw)
  To: Stefano Garzarella
  Cc: qemu-devel, Paolo Bonzini, Daniel P . Berrangé,
	Marcelo Tosatti, Michael S . Tsirkin, Cornelia Huck,
	Marcel Apfelbaum, Sergio Lopez, Eduardo Habkost, Alistair Francis,
	Peter Xu, David Hildenbrand, Igor Mammedov, Tom Lendacky,
	Michael Roth, Ani Sinha, Jörg Roedel

On Thu, 2024-06-27 at 14:48 +0200, Stefano Garzarella wrote:
> On Fri, Jun 21, 2024 at 03:29:09PM GMT, Roy Hopkins wrote:
> > The class function and implementations for updating launch data return
> > a code in case of error. In some cases an error message is generated and
> > in other cases, just the error return value is used.
> > 
> > This small refactor adds an 'Error **errp' parameter to all functions
> > which consistently set an error condition if a non-zero value is
> > returned.
> > 
> > Signed-off-by: Roy Hopkins <roy.hopkins@suse.com>
> > ---
> > target/i386/sev.c | 67 +++++++++++++++++++++++++----------------------
> > 1 file changed, 35 insertions(+), 32 deletions(-)
> > 
> > diff --git a/target/i386/sev.c b/target/i386/sev.c
> > index 30b83f1d77..1900c3d9b4 100644
> > --- a/target/i386/sev.c
> > +++ b/target/i386/sev.c
> > @@ -121,7 +121,8 @@ struct SevCommonStateClass {
> >                                        Error **errp);
> >     int (*launch_start)(SevCommonState *sev_common);
> >     void (*launch_finish)(SevCommonState *sev_common);
> > -    int (*launch_update_data)(SevCommonState *sev_common, hwaddr gpa,
> > uint8_t *ptr, uint64_t len);
> > +    int (*launch_update_data)(SevCommonState *sev_common, hwaddr gpa,
> > +                              uint8_t *ptr, uint64_t len, Error **errp);
> >     int (*kvm_init)(ConfidentialGuestSupport *cgs, Error **errp);
> > };
> > 
> > @@ -942,14 +943,17 @@ out:
> >     return ret;
> > }
> > 
> > -static int
> > -sev_launch_update_data(SevCommonState *sev_common, hwaddr gpa, uint8_t
> > *addr, uint64_t len)
> > +static int sev_launch_update_data(SevCommonState *sev_common, hwaddr gpa,
> > +                                  uint8_t *addr, uint64_t len, Error
> > **errp)
> > {
> >     int ret, fw_error;
> >     struct kvm_sev_launch_update_data update;
> > 
> >     if (!addr || !len) {
> > -        return 1;
> 
> Why were we returning 1 before? Was that a mistake?
> Maybe we should mention it in the patch or fix it in another patch.
> 
> > +        error_setg(errp,
> > +                   "%s: Invalid parameters provided for updating 
> > launch data.",
> > +                   __func__);
> > +        return -1;
> >     }
> > 
> >     update.uaddr = (uintptr_t)addr;
> > @@ -958,8 +962,8 @@ sev_launch_update_data(SevCommonState *sev_common,
> > hwaddr gpa, uint8_t *addr, ui
> >     ret = sev_ioctl(sev_common->sev_fd, KVM_SEV_LAUNCH_UPDATE_DATA,
> >                     &update, &fw_error);
> >     if (ret) {
> > -        error_report("%s: LAUNCH_UPDATE ret=%d fw_error=%d '%s'",
> > -                __func__, ret, fw_error, fw_error_to_str(fw_error));
> > +        error_setg(errp, "%s: LAUNCH_UPDATE ret=%d fw_error=%d '%s'",
> > __func__,
> > +                   ret, fw_error, fw_error_to_str(fw_error));
> >     }
> > 
> >     return ret;
> > @@ -1087,9 +1091,8 @@ sev_launch_finish(SevCommonState *sev_common)
> >     migrate_add_blocker(&sev_mig_blocker, &error_fatal);
> > }
> > 
> > -static int
> > -snp_launch_update_data(uint64_t gpa, void *hva,
> > -                       uint32_t len, int type)
> > +static int snp_launch_update_data(uint64_t gpa, void *hva, uint32_t len,
> > +                                  int type, Error **errp)
> > {
> >     SevLaunchUpdateData *data;
> > 
> > @@ -1104,13 +1107,12 @@ snp_launch_update_data(uint64_t gpa, void *hva,
> >     return 0;
> > }
> > 
> > -static int
> > -sev_snp_launch_update_data(SevCommonState *sev_common, hwaddr gpa,
> > -                           uint8_t *ptr, uint64_t len)
> > +static int sev_snp_launch_update_data(SevCommonState *sev_common, hwaddr
> > gpa,
> > +                                      uint8_t *ptr, uint64_t len, Error
> > **errp)
> > {
> > -       int ret = snp_launch_update_data(gpa, ptr, len,
> > -                                         KVM_SEV_SNP_PAGE_TYPE_NORMAL);
> > -       return ret;
> > +    int ret = snp_launch_update_data(gpa, ptr, len,
> > +                                     KVM_SEV_SNP_PAGE_TYPE_NORMAL, errp);
> > +    return ret;
> 
> Pre-existing, but while we're at it maybe we can remove ret.
> 
> > }
> > 
> > static int
> > @@ -1162,8 +1164,8 @@ sev_snp_cpuid_info_fill(SnpCpuidInfo *snp_cpuid_info,
> >     return 0;
> > }
> > 
> > -static int
> > -snp_launch_update_cpuid(uint32_t cpuid_addr, void *hva, uint32_t cpuid_len)
> > +static int snp_launch_update_cpuid(uint32_t cpuid_addr, void *hva,
> > +                                   uint32_t cpuid_len, Error **errp)
> > {
> >     KvmCpuidInfo kvm_cpuid_info = {0};
> >     SnpCpuidInfo snp_cpuid_info;
> > @@ -1180,26 +1182,26 @@ snp_launch_update_cpuid(uint32_t cpuid_addr, void
> > *hva, uint32_t cpuid_len)
> >     } while (ret == -E2BIG);
> > 
> >     if (ret) {
> > -        error_report("SEV-SNP: unable to query CPUID values for CPU: '%s'",
> > -                     strerror(-ret));
> > -        return 1;
> > +        error_setg(errp, "SEV-SNP: unable to query CPUID values for CPU:
> > '%s'",
> > +                   strerror(-ret));
> > +        return -1;
> >     }
> > 
> >     ret = sev_snp_cpuid_info_fill(&snp_cpuid_info, &kvm_cpuid_info);
> >     if (ret) {
> > -        error_report("SEV-SNP: failed to generate CPUID table
> > information");
> > -        return 1;
> > +        error_setg(errp, "SEV-SNP: failed to generate CPUID table
> > information");
> > +        return -1;
> 
> Ditto for the 2 changes, although IIUC we never check the return value 
> of snp_launch_update_cpuid().

The error returns were changed to -1 for consistency, but on reflection, without
changing every return in this file it had the reverse effect. I'll revert these
back to the original return values.

I did however spot a pre-existing bug when reverting this though.
sev_encrypt_firmware() only considers a negative result from launch_update() as
an error, but the function returns 1 on some errors. I've added a new patch in
v4 that fixes this error.

> 
> >     }
> > 
> >     memcpy(hva, &snp_cpuid_info, sizeof(snp_cpuid_info));
> > 
> >     return snp_launch_update_data(cpuid_addr, hva, cpuid_len,
> > -                                  KVM_SEV_SNP_PAGE_TYPE_CPUID);
> > +                                  KVM_SEV_SNP_PAGE_TYPE_CPUID, errp);
> > }
> > 
> > -static int
> > -snp_launch_update_kernel_hashes(SevSnpGuestState *sev_snp, uint32_t addr,
> > -                                void *hva, uint32_t len)
> > +static int snp_launch_update_kernel_hashes(SevSnpGuestState *sev_snp,
> > +                                           uint32_t addr, void *hva,
> > +                                           uint32_t len, Error **errp)
> > {
> >     int type = KVM_SEV_SNP_PAGE_TYPE_ZERO;
> >     if (sev_snp->parent_obj.kernel_hashes) {
> > @@ -1211,7 +1213,7 @@ snp_launch_update_kernel_hashes(SevSnpGuestState 
> > *sev_snp, uint32_t addr,
> >                sizeof(*sev_snp->kernel_hashes_data));
> >         type = KVM_SEV_SNP_PAGE_TYPE_NORMAL;
> >     }
> > -    return snp_launch_update_data(addr, hva, len, type);
> > +    return snp_launch_update_data(addr, hva, len, type, errp);
> > }
> > 
> > static int
> > @@ -1249,12 +1251,14 @@ snp_populate_metadata_pages(SevSnpGuestState 
> > *sev_snp,
> >         }
> > 
> >         if (type == KVM_SEV_SNP_PAGE_TYPE_CPUID) {
> > -            ret = snp_launch_update_cpuid(desc->base, hva, desc->len);
> > +            ret = snp_launch_update_cpuid(desc->base, hva, desc->len,
> > +                                          &error_fatal);
> >         } else if (desc->type == SEV_DESC_TYPE_SNP_KERNEL_HASHES) {
> >             ret = snp_launch_update_kernel_hashes(sev_snp, desc->base, hva,
> > -                                                  desc->len);
> > +                                                  desc->len, &error_fatal);
> >         } else {
> > -            ret = snp_launch_update_data(desc->base, hva, desc->len, type);
> > +            ret = snp_launch_update_data(desc->base, hva, desc->len, type,
> > +                                         &error_fatal);
> >         }
> > 
> >         if (ret) {
> > @@ -1541,9 +1545,8 @@ sev_encrypt_flash(hwaddr gpa, uint8_t *ptr, uint64_t
> > len, Error **errp)
> >     if (sev_check_state(sev_common, SEV_STATE_LAUNCH_UPDATE)) {
> >         int ret;
> > 
> > -        ret = klass->launch_update_data(sev_common, gpa, ptr, len);
> > +        ret = klass->launch_update_data(sev_common, gpa, ptr, len, errp);
> >         if (ret < 0) {
> > -            error_setg(errp, "SEV: Failed to encrypt pflash rom");
> >             return ret;
> >         }
> >     }
> > -- 
> > 2.43.0
> > 
> 


^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH v3 04/15] hw/core/machine: Add igvm-cfg object and processing for IGVM files
  2024-06-28 11:09     ` Roy Hopkins
@ 2024-06-28 11:23       ` Daniel P. Berrangé
  2024-07-01 11:59         ` Roy Hopkins
  0 siblings, 1 reply; 39+ messages in thread
From: Daniel P. Berrangé @ 2024-06-28 11:23 UTC (permalink / raw)
  To: Roy Hopkins
  Cc: qemu-devel, Paolo Bonzini, Stefano Garzarella, Marcelo Tosatti,
	Michael S . Tsirkin, Cornelia Huck, Marcel Apfelbaum,
	Sergio Lopez, Eduardo Habkost, Alistair Francis, Peter Xu,
	David Hildenbrand, Igor Mammedov, Tom Lendacky, Michael Roth,
	Ani Sinha, Jörg Roedel

On Fri, Jun 28, 2024 at 12:09:59PM +0100, Roy Hopkins wrote:
> On Mon, 2024-06-24 at 15:00 +0100, Daniel P. Berrangé wrote:
> > On Fri, Jun 21, 2024 at 03:29:07PM +0100, Roy Hopkins wrote:
> > > An IGVM file contains configuration of guest state that should be
> > > applied during configuration of the guest, before the guest is started.
> > > 
> > > This patch allows the user to add an igvm-cfg object to the machine
> > > configuration that allows an IGVM file to be configured that will be
> > > applied to the guest before it is started.
> > > 
> > > If an IGVM configuration is provided then the IGVM file is processed at
> > > the end of the board initialization, before the state transition to
> > > PHASE_MACHINE_INITIALIZED.
> > > 
> > > Signed-off-by: Roy Hopkins <roy.hopkins@suse.com>
> > > ---
> > >  include/hw/boards.h |  2 ++
> > >  hw/core/machine.c   | 20 ++++++++++++++++++++
> > >  qemu-options.hx     | 25 +++++++++++++++++++++++++
> > >  3 files changed, 47 insertions(+)

snip

> > This adds igvm-cfg for all machines, regardless of architecture target.
> > 
> > Are igvm files fully cross-platform portable, or should we just put
> > this into the TYPE_X86_MACHINE base class to limit it ?
> > 
> > It at least reports errors if I try to load an IGVM file with
> > qemu-system-aarch64 + virt type
> > 
> > $ ./build/qemu-system-aarch64 -object igvm-cfg,file=../buildigvm/ovmf-
> > sev.igvm,id=igvm -machine virt,igvm-cfg=igvm
> > qemu-system-aarch64: IGVM file does not describe a compatible supported
> > platform
> > 
> > so that's good.
> 
> The IGVM specification is designed to support non X86 platforms, hence its
> inclusion for all machines. Support for non-X86 is likely to result in changes
> to the specification though that will impact the library we depend on.
> 
> There would obviously need to be some further implementation to support non-X86
> machines in QEMU, in the same way that further implementation is required to
> support other X86 confidential computing platforms such as TDX.
> 
> So, this poses the question: should we move it to TYPE_X86_MACHINE as the
> current supported platforms are all on X86? Or should we leave it where it is
> with a view to adding non X86 platform support with less impact later? I'd
> appreciate your views on this.

The pro of putting it in the base machine class is that we don't need to
repeat the property creation in each machine as we broaden support to other
arches, I presume aarch64 is probably most likely future candidate.

The downside of putting it in the base machine class is that it limits
mgmt app ability to probe QEMU for support. ie it wouldn't be possible
to probe whether individual machines support it or not, as we broaden
QEMU support.

Then again, we will already face that limited ability to probe even on
x86, as we won't be able to query whether IGVM is SNP only, or has been
extended to TDX too.

With my libvirt hat on, probing is still probably the more important
factor, so would push towards putting the property just to individual
machine classes that definitely have been wired up to be able to use
it.

With regards,
Daniel
-- 
|: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org         -o-            https://fstop138.berrange.com :|
|: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|



^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH v3 04/15] hw/core/machine: Add igvm-cfg object and processing for IGVM files
  2024-06-28 11:23       ` Daniel P. Berrangé
@ 2024-07-01 11:59         ` Roy Hopkins
  0 siblings, 0 replies; 39+ messages in thread
From: Roy Hopkins @ 2024-07-01 11:59 UTC (permalink / raw)
  To: Daniel P. Berrangé
  Cc: qemu-devel, Paolo Bonzini, Stefano Garzarella, Marcelo Tosatti,
	Michael S . Tsirkin, Cornelia Huck, Marcel Apfelbaum,
	Sergio Lopez, Eduardo Habkost, Alistair Francis, Peter Xu,
	David Hildenbrand, Igor Mammedov, Tom Lendacky, Michael Roth,
	Ani Sinha, Jörg Roedel

On Fri, 2024-06-28 at 12:23 +0100, Daniel P. Berrangé wrote:
> On Fri, Jun 28, 2024 at 12:09:59PM +0100, Roy Hopkins wrote:
> > On Mon, 2024-06-24 at 15:00 +0100, Daniel P. Berrangé wrote:
> > > On Fri, Jun 21, 2024 at 03:29:07PM +0100, Roy Hopkins wrote:
> > > > An IGVM file contains configuration of guest state that should be
> > > > applied during configuration of the guest, before the guest is started.
> > > > 
> > > > This patch allows the user to add an igvm-cfg object to the machine
> > > > configuration that allows an IGVM file to be configured that will be
> > > > applied to the guest before it is started.
> > > > 
> > > > If an IGVM configuration is provided then the IGVM file is processed at
> > > > the end of the board initialization, before the state transition to
> > > > PHASE_MACHINE_INITIALIZED.
> > > > 
> > > > Signed-off-by: Roy Hopkins <roy.hopkins@suse.com>
> > > > ---
> > > >  include/hw/boards.h |  2 ++
> > > >  hw/core/machine.c   | 20 ++++++++++++++++++++
> > > >  qemu-options.hx     | 25 +++++++++++++++++++++++++
> > > >  3 files changed, 47 insertions(+)
> 
> snip
> 
> > > This adds igvm-cfg for all machines, regardless of architecture target.
> > > 
> > > Are igvm files fully cross-platform portable, or should we just put
> > > this into the TYPE_X86_MACHINE base class to limit it ?
> > > 
> > > It at least reports errors if I try to load an IGVM file with
> > > qemu-system-aarch64 + virt type
> > > 
> > > $ ./build/qemu-system-aarch64 -object igvm-cfg,file=../buildigvm/ovmf-
> > > sev.igvm,id=igvm -machine virt,igvm-cfg=igvm
> > > qemu-system-aarch64: IGVM file does not describe a compatible supported
> > > platform
> > > 
> > > so that's good.
> > 
> > The IGVM specification is designed to support non X86 platforms, hence its
> > inclusion for all machines. Support for non-X86 is likely to result in
> > changes
> > to the specification though that will impact the library we depend on.
> > 
> > There would obviously need to be some further implementation to support non-
> > X86
> > machines in QEMU, in the same way that further implementation is required to
> > support other X86 confidential computing platforms such as TDX.
> > 
> > So, this poses the question: should we move it to TYPE_X86_MACHINE as the
> > current supported platforms are all on X86? Or should we leave it where it
> > is
> > with a view to adding non X86 platform support with less impact later? I'd
> > appreciate your views on this.
> 
> The pro of putting it in the base machine class is that we don't need to
> repeat the property creation in each machine as we broaden support to other
> arches, I presume aarch64 is probably most likely future candidate.
> 
> The downside of putting it in the base machine class is that it limits
> mgmt app ability to probe QEMU for support. ie it wouldn't be possible
> to probe whether individual machines support it or not, as we broaden
> QEMU support.
> 
> Then again, we will already face that limited ability to probe even on
> x86, as we won't be able to query whether IGVM is SNP only, or has been
> extended to TDX too.
> 
> With my libvirt hat on, probing is still probably the more important
> factor, so would push towards putting the property just to individual
> machine classes that definitely have been wired up to be able to use
> it.
> 
> With regards,
> Daniel

Ok, thanks. I'll move the instance to 'X86MachineState' and the call to 
'igvm->process()' into 'pc_q35_init()' and 'pc_init1()'.

Regards,
Roy



^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH v3 15/15] sev: Provide sev_features flags from IGVM VMSA to KVM_SEV_INIT2
  2024-06-24 14:14   ` Daniel P. Berrangé
@ 2024-07-01 13:50     ` Roy Hopkins
  0 siblings, 0 replies; 39+ messages in thread
From: Roy Hopkins @ 2024-07-01 13:50 UTC (permalink / raw)
  To: Daniel P. Berrangé
  Cc: qemu-devel, Paolo Bonzini, Stefano Garzarella, Marcelo Tosatti,
	Michael S . Tsirkin, Cornelia Huck, Marcel Apfelbaum,
	Sergio Lopez, Eduardo Habkost, Alistair Francis, Peter Xu,
	David Hildenbrand, Igor Mammedov, Tom Lendacky, Michael Roth,
	Ani Sinha, Jörg Roedel

On Mon, 2024-06-24 at 15:14 +0100, Daniel P. Berrangé wrote:
> On Fri, Jun 21, 2024 at 03:29:18PM +0100, Roy Hopkins wrote:
> > IGVM files can contain an initial VMSA that should be applied to each
> > vcpu as part of the initial guest state. The sev_features flags are
> > provided as part of the VMSA structure. However, KVM only allows
> > sev_features to be set during initialization and not as the guest is
> > being prepared for launch.
> > 
> > This patch queries KVM for the supported set of sev_features flags and
> > processes the IGVM file during kvm_init to determine any sev_features
> > flags set in the IGVM file. These are then provided in the call to
> > KVM_SEV_INIT2 to ensure the guest state matches that specified in the
> > IGVM file.
> > 
> > This does cause the IGVM file to be processed twice. Firstly to extract
> > the sev_features then secondly to actually configure the guest. However,
> > the first pass is largely ignored meaning the overhead is minimal.
> > 
> > Signed-off-by: Roy Hopkins <roy.hopkins@suse.com>
> > ---
> >  target/i386/sev.c | 145 ++++++++++++++++++++++++++++++++++++++++------
> >  1 file changed, 126 insertions(+), 19 deletions(-)
> > 
> 
> >  static int sev_common_kvm_init(ConfidentialGuestSupport *cgs, Error **errp)
> >  {
> >      char *devname;
> > @@ -1743,6 +1783,10 @@ static int
> > sev_common_kvm_init(ConfidentialGuestSupport *cgs, Error **errp)
> >          }
> >      }
> >  
> > +    if (sev_init_supported_features(sev_common, errp) < 0) {
> > +        return -1;
> > +    }
> > +
> >      trace_kvm_sev_init();
> >      if (x86_klass->kvm_type(X86_CONFIDENTIAL_GUEST(sev_common)) ==
> > KVM_X86_DEFAULT_VM) {
> >          cmd = sev_es_enabled() ? KVM_SEV_ES_INIT : KVM_SEV_INIT;
> > @@ -1750,6 +1794,38 @@ static int
> > sev_common_kvm_init(ConfidentialGuestSupport *cgs, Error **errp)
> >          ret = sev_ioctl(sev_common->sev_fd, cmd, NULL, &fw_error);
> >      } else {
> >          struct kvm_sev_init args = { 0 };
> > +        MachineState *machine = MACHINE(qdev_get_machine());
> > +
> > +        /*
> > +         * If configuration is provided via an IGVM file then the IGVM file
> > +         * might contain configuration of the initial vcpu context. For SEV
> > +         * the vcpu context includes the sev_features which should be
> > applied
> > +         * to the vcpu.
> > +         *
> > +         * KVM does not synchronize sev_features from CPU state. Instead it
> > +         * requires sev_features to be provided as part of this
> > initialization
> > +         * call which is subsequently automatically applied to the VMSA of
> > +         * each vcpu.
> > +         *
> > +         * The IGVM file is normally processed after initialization.
> > Therefore
> > +         * we need to pre-process it here to extract sev_features in order
> > to
> > +         * provide it to KVM_SEV_INIT2. Each cgs_* function that is called
> > by
> > +         * the IGVM processor detects this pre-process by observing the
> > state
> > +         * as SEV_STATE_UNINIT.
> > +         */
> > +        if (machine->igvm) {
> > +            if (IGVM_CFG_GET_CLASS(machine->igvm)
> > +                    ->process(machine->igvm, machine->cgs, errp) == -1) {
> > +                return -1;
> > +            }
> > +            /*
> > +             * KVM maintains a bitmask of allowed sev_features. This does
> > not
> > +             * include SVM_SEV_FEAT_SNP_ACTIVE which is set accordingly by
> > KVM
> > +             * itself. Therefore we need to clear this flag.
> > +             */
> > +            args.vmsa_features = sev_common->sev_features &
> > +                                 ~SVM_SEV_FEAT_SNP_ACTIVE;
> > +        }
> >  
> >          ret = sev_ioctl(sev_common->sev_fd, KVM_SEV_INIT2, &args,
> > &fw_error);
> >      }
> 
> What happens if the code path takes us down the KVM_SEV_INIT
> route, rather than KVM_SEV_INIT2 ?  Should we be reporting an
> error indicating that IGVM usage is incompatible with the legacy
> KVM_SEV_INIT path ?
> 
> 
> With regards,
> Daniel

The idea is that sev_common->supported_sev_features is initialised to 0 for the
legacy path meaning that the call to check_sev_features() that occurs when the
IGVM file is parsed will flag an error stating 'VMSA contains unsupported
sev_features' if any features are set. This should ensure the IGVM file matches
the settings in the legacy kernel.

However, I've just run this against an older kernel and found an error: the
kvm_ioctl in sev_init_supported_features() is only supported in newer kernels
meaning that the launch is aborted on older kernels. I'll update this patch to
fix this and ensure supported_sev_features is 0 for the KVM_SEV_INIT case.

Regards,
Roy



^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH v3 09/15] docs/system: Add documentation on support for IGVM
  2024-06-24 14:09   ` Daniel P. Berrangé
@ 2024-07-01 14:28     ` Roy Hopkins
  0 siblings, 0 replies; 39+ messages in thread
From: Roy Hopkins @ 2024-07-01 14:28 UTC (permalink / raw)
  To: Daniel P. Berrangé
  Cc: qemu-devel, Paolo Bonzini, Stefano Garzarella, Marcelo Tosatti,
	Michael S . Tsirkin, Cornelia Huck, Marcel Apfelbaum,
	Sergio Lopez, Eduardo Habkost, Alistair Francis, Peter Xu,
	David Hildenbrand, Igor Mammedov, Tom Lendacky, Michael Roth,
	Ani Sinha, Jörg Roedel

On Mon, 2024-06-24 at 15:09 +0100, Daniel P. Berrangé wrote:
> On Fri, Jun 21, 2024 at 03:29:12PM +0100, Roy Hopkins wrote:
> > IGVM support has been implemented for Confidential Guests that support
> > AMD SEV and AMD SEV-ES. Add some documentation that gives some
> > background on the IGVM format and how to use it to configure a
> > confidential guest.
> > 
> > Signed-off-by: Roy Hopkins <roy.hopkins@suse.com>
> > ---
> >  docs/system/i386/amd-memory-encryption.rst |   2 +
> >  docs/system/igvm.rst                       | 157 +++++++++++++++++++++
> >  docs/system/index.rst                      |   1 +
> >  3 files changed, 160 insertions(+)
> >  create mode 100644 docs/system/igvm.rst
> 
> > diff --git a/docs/system/igvm.rst b/docs/system/igvm.rst
> > new file mode 100644
> > index 0000000000..b6e544a508
> > --- /dev/null
> > +++ b/docs/system/igvm.rst
> 
> > +Running a Confidential Guest configured using IGVM
> > +--------------------------------------------------
> > +
> > +To run a confidential guest configured with IGVM you need to add an
> > +``igvm-cfg`` object and refer to it from the ``-machine`` parameter:
> > +
> > +Example (for AMD SEV)::
> > +
> > +    qemu-system-x86_64 \
> > +        <other parameters> \
> > +        -machine ...,confidential-guest-support=sev0,igvm-cfg=igvm0 \
> > +        -object sev-guest,id=sev0,cbitpos=47,reduced-phys-bits=1 \
> > +        -object igvm-cfg,id=igvm0,file=/path/to/guest.igvm
> 
> Perhaps also illustrate use of your 'buildigvm' tool for creating
> the igvm file first, assuming that's the tool users are most likely
> to end up needing ?
> 
> 
> With regards,
> Daniel

I'm not sure it is the tool that _most_ people will end up using, but it is a
good example and I'm not aware of many other examples of IGVM build tools.

IGVM is likely to be used to package guests with custom configuration
requirements, such as the COCONUT-SVSM usage where the SVSM kernel is packaged
along with OVMF, configuration and other data required to launch a guest running
with a range of privilege levels in a confidential virtual machine.

But for the purposes of testing and examples it makes sense to update the
documentation to describe how to use the 'buildigvm' tool so I'll update the
docs.

Regards,
Roy


^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH v3 10/15] docs/interop/firmware.json: Add igvm to FirmwareDevice
  2024-06-27 12:53   ` Stefano Garzarella
@ 2024-07-02 10:36     ` Roy Hopkins
  0 siblings, 0 replies; 39+ messages in thread
From: Roy Hopkins @ 2024-07-02 10:36 UTC (permalink / raw)
  To: Stefano Garzarella
  Cc: qemu-devel, Paolo Bonzini, Daniel P . Berrangé,
	Marcelo Tosatti, Michael S . Tsirkin, Cornelia Huck,
	Marcel Apfelbaum, Sergio Lopez, Eduardo Habkost, Alistair Francis,
	Peter Xu, David Hildenbrand, Igor Mammedov, Tom Lendacky,
	Michael Roth, Ani Sinha, Jörg Roedel

On Thu, 2024-06-27 at 14:53 +0200, Stefano Garzarella wrote:
> On Fri, Jun 21, 2024 at 03:29:13PM GMT, Roy Hopkins wrote:
> > Create an enum entry within FirmwareDevice for 'igvm' to describe that
> > an IGVM file can be used to map firmware into memory as an alternative
> > to pre-existing firmware devices.
> > 
> > Signed-off-by: Roy Hopkins <roy.hopkins@suse.com>
> > ---
> > docs/interop/firmware.json | 9 ++++++++-
> > 1 file changed, 8 insertions(+), 1 deletion(-)
> > 
> > diff --git a/docs/interop/firmware.json b/docs/interop/firmware.json
> > index 54a1fc6c10..9a9178606e 100644
> > --- a/docs/interop/firmware.json
> > +++ b/docs/interop/firmware.json
> > @@ -55,10 +55,17 @@
> > #
> > # @memory: The firmware is to be mapped into memory.
> > #
> > +# @igvm: The firmware is defined by a file conforming to the IGVM
> > +#        specification and mapped into memory according to directives
> > +#        defined in the file. This is similar to @memory but may
> > +#        include additional processing defined by the IGVM file
> > +#        including initial CPU state or population of metadata into
> > +#        the guest address space.
> 
> Should we add (Since: 9.1) ?
> 
> I'm not sure about that, since I don't see it used much in docs/interop/
> 
> Thanks,
> Stefano
> 

I'll add it to the end of the @igvm description. Thanks.

> > +#
> > # Since: 3.0
> > ##
> > { 'enum' : 'FirmwareDevice',
> > -  'data' : [ 'flash', 'kernel', 'memory' ] }
> > +  'data' : [ 'flash', 'kernel', 'memory', 'igvm' ] }
> > 
> > ##
> > # @FirmwareTarget:
> > -- 
> > 2.43.0
> > 
> 



^ permalink raw reply	[flat|nested] 39+ messages in thread

end of thread, other threads:[~2024-07-02 10:37 UTC | newest]

Thread overview: 39+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-06-21 14:29 [PATCH v3 00/15] Introduce support for IGVM files Roy Hopkins
2024-06-21 14:29 ` [PATCH v3 01/15] meson: Add optional dependency on IGVM library Roy Hopkins
2024-06-21 14:29 ` [PATCH v3 02/15] backends/confidential-guest-support: Add functions to support IGVM Roy Hopkins
2024-06-21 14:29 ` [PATCH v3 03/15] backends/igvm: Add IGVM loader and configuration Roy Hopkins
2024-06-24 13:29   ` Daniel P. Berrangé
2024-06-28 10:59     ` Roy Hopkins
2024-06-27  9:06   ` Stefano Garzarella
2024-06-27  9:14     ` Daniel P. Berrangé
2024-06-28 11:00       ` Roy Hopkins
2024-06-21 14:29 ` [PATCH v3 04/15] hw/core/machine: Add igvm-cfg object and processing for IGVM files Roy Hopkins
2024-06-24 14:00   ` Daniel P. Berrangé
2024-06-28 11:09     ` Roy Hopkins
2024-06-28 11:23       ` Daniel P. Berrangé
2024-07-01 11:59         ` Roy Hopkins
2024-06-21 14:29 ` [PATCH v3 05/15] i386/pc_sysfw: Ensure sysfw flash configuration does not conflict with IGVM Roy Hopkins
2024-06-27 12:38   ` Stefano Garzarella
2024-06-28 11:10     ` Roy Hopkins
2024-06-21 14:29 ` [PATCH v3 06/15] sev: Update launch_update_data functions to use Error handling Roy Hopkins
2024-06-27 12:48   ` Stefano Garzarella
2024-06-28 11:20     ` Roy Hopkins
2024-06-21 14:29 ` [PATCH v3 07/15] i386/sev: Refactor setting of reset vector and initial CPU state Roy Hopkins
2024-06-21 14:29 ` [PATCH v3 08/15] i386/sev: Implement ConfidentialGuestSupport functions for SEV Roy Hopkins
2024-06-21 14:29 ` [PATCH v3 09/15] docs/system: Add documentation on support for IGVM Roy Hopkins
2024-06-24 14:09   ` Daniel P. Berrangé
2024-07-01 14:28     ` Roy Hopkins
2024-06-21 14:29 ` [PATCH v3 10/15] docs/interop/firmware.json: Add igvm to FirmwareDevice Roy Hopkins
2024-06-27 12:53   ` Stefano Garzarella
2024-07-02 10:36     ` Roy Hopkins
2024-06-21 14:29 ` [PATCH v3 11/15] backends/confidential-guest-support: Add set_guest_policy() function Roy Hopkins
2024-06-21 14:29 ` [PATCH v3 12/15] backends/igvm: Process initialization sections in IGVM file Roy Hopkins
2024-06-21 14:29 ` [PATCH v3 13/15] backends/igvm: Handle policy for SEV guests Roy Hopkins
2024-06-24 14:56   ` Daniel P. Berrangé
2024-06-21 14:29 ` [PATCH v3 14/15] i386/sev: Add implementation of CGS set_guest_policy() Roy Hopkins
2024-06-24 14:53   ` Daniel P. Berrangé
2024-06-21 14:29 ` [PATCH v3 15/15] sev: Provide sev_features flags from IGVM VMSA to KVM_SEV_INIT2 Roy Hopkins
2024-06-24 14:14   ` Daniel P. Berrangé
2024-07-01 13:50     ` Roy Hopkins
2024-06-24 13:50 ` [PATCH v3 00/15] Introduce support for IGVM files Daniel P. Berrangé
2024-06-28 10:56   ` Roy Hopkins

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).