All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC PATCH v2 00/12] Add AMD Secure Nested Paging (SEV-SNP) support
@ 2021-08-26 22:26 Michael Roth
  2021-08-26 22:26 ` [RFC PATCH v2 01/12] i386/sev: introduce "sev-common" type to encapsulate common SEV state Michael Roth
                   ` (12 more replies)
  0 siblings, 13 replies; 61+ messages in thread
From: Michael Roth @ 2021-08-26 22:26 UTC (permalink / raw)
  To: qemu-devel
  Cc: Connor Kuehl, Philippe Mathieu-Daudé,
	Michael S . Tsirkin, James Bottomley, Dr . David Alan Gilbert,
	Tom Lendacky, Paolo Bonzini, Dov Murik, David Gibson,
	Daniel P . Berrangé,
	kvm, Eduardo Habkost, Brijesh Singh, Markus Armbruster,
	Eric Blake

These patches implement SEV-SNP along with CPUID enforcement support for QEMU,
and are also available at:

  https://github.com/mdroth/qemu/commits/snp-rfc-v2-upstream

They are based on the initial RFC submitted by Brijesh:

  https://lore.kernel.org/qemu-devel/20210722000259.ykepl7t6ptua7im5@amd.com/T/

Changes since RFC v1:

 - rebased onto latest master
 - drop SNP config file in favor of a new 'sev-snp-guest' object where all
   SNP-related params are passed as strings/integers via command-line
 - report specific error if BIOS reports invalid address/len for
   reserved/pre-validated regions (Connor)
 - use Range helpers for handling validated region overlaps (Dave)
 - simplify error handling in sev_snp_launch_start, and report the correct
   return code when handling LAUNCH_START failures (Dov)
 - add SEV-SNP bit to CPUID 0x8000001f when SNP enabled
 - updated query-sev to handle differences between SEV and SEV-SNP
 - updated to work against v5 of SEV-SNP host kernel / hypervisor patches

Overview
--------

SEV-SNP builds upon existing SEV and SEV-ES functionality while adding
new hardware-based memory protections. SEV-SNP adds strong memory integrity
protection to help prevent malicious hypervisor-based attacks like data
replay, memory re-mapping and more in order to create an isolated memory
encryption environment.

This series depends on the following patches to support SEV-SNP in Linux
kernel and OVMF:

  guest kernel (v5, part 1):
  https://lore.kernel.org/kvm/20210820151933.22401-1-brijesh.singh@amd.com/T/
  
  host kernel (v5, part 2):
  https://lore.kernel.org/lkml/20210820155918.7518-1-brijesh.singh@amd.com/
  
  OVMF (v5):
  https://edk2.groups.io/g/devel/message/77335?p=,,,20,0,0,0::Created,,posterid%3A5969970,20,2,20,83891508

The Qemu patches uses the command id added by the SEV-SNP hypervisor
patches to bootstrap the SEV-SNP VMs.

Additional resources
--------------------
SEV-SNP whitepaper
https://www.amd.com/system/files/TechDocs/SEV-SNP-strengthening-vm-isolation-with-integrity-protection-and-more.pdf

APM 2: https://www.amd.com/system/files/TechDocs/24593.pdf (section 15.36)

GHCB spec:
https://developer.amd.com/wp-content/resources/56421.pdf

SEV-SNP firmware specification:
https://www.amd.com/system/files/TechDocs/56860.pdf

----------------------------------------------------------------
Brijesh Singh (6):
      linux-header: add the SNP specific command
      i386/sev: introduce 'sev-snp-guest' object
      i386/sev: initialize SNP context
      i386/sev: add the SNP launch start context
      i386/sev: add support to encrypt BIOS when SEV-SNP is enabled
      i386/sev: populate secrets and cpuid page and finalize the SNP launch

Michael Roth (6):
      i386/sev: introduce "sev-common" type to encapsulate common SEV state
      target/i386: set SEV-SNP CPUID bit when SNP enabled
      target/i386: allow versioned CPUs to specify new cache_info
      target/i386: add new EPYC CPU versions with updated cache_info
      i386/sev: sev-snp: add support for CPUID validation
      i386/sev: update query-sev QAPI format to handle SEV-SNP

 docs/amd-memory-encryption.txt |  77 +++-
 hw/i386/pc_sysfw.c             |   7 +-
 include/sysemu/sev.h           |   2 +-
 linux-headers/linux/kvm.h      |  50 +++
 qapi/misc-target.json          |  71 ++-
 qapi/qom.json                  |  94 +++-
 target/i386/cpu.c              | 221 ++++++++-
 target/i386/monitor.c          |  29 +-
 target/i386/sev-stub.c         |   8 +-
 target/i386/sev.c              | 989 +++++++++++++++++++++++++++++++++++------
 target/i386/sev_i386.h         |   4 +
 target/i386/trace-events       |   4 +
 12 files changed, 1374 insertions(+), 182 deletions(-)



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

* [RFC PATCH v2 01/12] i386/sev: introduce "sev-common" type to encapsulate common SEV state
  2021-08-26 22:26 [RFC PATCH v2 00/12] Add AMD Secure Nested Paging (SEV-SNP) support Michael Roth
@ 2021-08-26 22:26 ` Michael Roth
  2021-09-01 14:18   ` Markus Armbruster
  2021-08-26 22:26 ` [RFC PATCH v2 02/12] linux-header: add the SNP specific command Michael Roth
                   ` (11 subsequent siblings)
  12 siblings, 1 reply; 61+ messages in thread
From: Michael Roth @ 2021-08-26 22:26 UTC (permalink / raw)
  To: qemu-devel
  Cc: Connor Kuehl, Philippe Mathieu-Daudé,
	Michael S . Tsirkin, James Bottomley, Dr . David Alan Gilbert,
	Tom Lendacky, Paolo Bonzini, Dov Murik, David Gibson,
	Daniel P . Berrangé,
	kvm, Eduardo Habkost, Brijesh Singh, Markus Armbruster,
	Eric Blake

Currently all SEV/SEV-ES functionality is managed through a single
'sev-guest' QOM type. With upcoming support for SEV-SNP, taking this
same approach won't work well since some of the properties/state
managed by 'sev-guest' is not applicable to SEV-SNP, which will instead
rely on a new QOM type with its own set of properties/state.

To prepare for this, this patch moves common state into an abstract
'sev-common' parent type to encapsulate properties/state that is
common to both SEV/SEV-ES and SEV-SNP, leaving only SEV/SEV-ES-specific
properties/state in the current 'sev-guest' type. This should not
affect current behavior or command-line options.

As part of this patch, some related changes are also made:

  - a static 'sev_guest' variable is currently used to keep track of
    the 'sev-guest' instance. SEV-SNP would similarly introduce an
    'sev_snp_guest' static variable. But these instances are now
    available via qdev_get_machine()->cgs, so switch to using that
    instead and drop the static variable.

  - 'sev_guest' is currently used as the name for the static variable
    holding a pointer to the 'sev-guest' instance. Re-purpose the name
    as a local variable referring the 'sev-guest' instance, and use
    that consistently throughout the code so it can be easily
    distinguished from sev-common/sev-snp-guest instances.

  - 'sev' is generally used as the name for local variables holding a
    pointer to the 'sev-guest' instance. In cases where that now points
    to common state, use the name 'sev_common'; in cases where that now
    points to state specific to 'sev-guest' instance, use the name
    'sev_guest'

Signed-off-by: Michael Roth <michael.roth@amd.com>
---
 qapi/qom.json     |  34 +++--
 target/i386/sev.c | 329 +++++++++++++++++++++++++++-------------------
 2 files changed, 214 insertions(+), 149 deletions(-)

diff --git a/qapi/qom.json b/qapi/qom.json
index a25616bc7a..211e083727 100644
--- a/qapi/qom.json
+++ b/qapi/qom.json
@@ -735,12 +735,29 @@
   'data': { '*filename': 'str' } }
 
 ##
-# @SevGuestProperties:
+# @SevCommonProperties:
 #
-# Properties for sev-guest objects.
+# Properties common to objects that are derivatives of sev-common.
 #
 # @sev-device: SEV device to use (default: "/dev/sev")
 #
+# @cbitpos: C-bit location in page table entry (default: 0)
+#
+# @reduced-phys-bits: number of bits in physical addresses that become
+#                     unavailable when SEV is enabled
+#
+# Since: 2.12
+##
+{ 'struct': 'SevCommonProperties',
+  'data': { '*sev-device': 'str',
+            '*cbitpos': 'uint32',
+            'reduced-phys-bits': 'uint32' } }
+
+##
+# @SevGuestProperties:
+#
+# Properties for sev-guest objects.
+#
 # @dh-cert-file: guest owners DH certificate (encoded with base64)
 #
 # @session-file: guest owners session parameters (encoded with base64)
@@ -749,21 +766,14 @@
 #
 # @handle: SEV firmware handle (default: 0)
 #
-# @cbitpos: C-bit location in page table entry (default: 0)
-#
-# @reduced-phys-bits: number of bits in physical addresses that become
-#                     unavailable when SEV is enabled
-#
 # Since: 2.12
 ##
 { 'struct': 'SevGuestProperties',
-  'data': { '*sev-device': 'str',
-            '*dh-cert-file': 'str',
+  'base': 'SevCommonProperties',
+  'data': { '*dh-cert-file': 'str',
             '*session-file': 'str',
             '*policy': 'uint32',
-            '*handle': 'uint32',
-            '*cbitpos': 'uint32',
-            'reduced-phys-bits': 'uint32' } }
+            '*handle': 'uint32' } }
 
 ##
 # @ObjectType:
diff --git a/target/i386/sev.c b/target/i386/sev.c
index 83df8c09f6..6acebfbd53 100644
--- a/target/i386/sev.c
+++ b/target/i386/sev.c
@@ -34,6 +34,8 @@
 #include "exec/confidential-guest-support.h"
 #include "hw/i386/pc.h"
 
+#define TYPE_SEV_COMMON "sev-common"
+OBJECT_DECLARE_SIMPLE_TYPE(SevCommonState, SEV_COMMON)
 #define TYPE_SEV_GUEST "sev-guest"
 OBJECT_DECLARE_SIMPLE_TYPE(SevGuestState, SEV_GUEST)
 
@@ -48,32 +50,38 @@ OBJECT_DECLARE_SIMPLE_TYPE(SevGuestState, SEV_GUEST)
  *         -object sev-guest,id=sev0 \
  *         -machine ...,memory-encryption=sev0
  */
-struct SevGuestState {
+struct SevCommonState {
     ConfidentialGuestSupport parent_obj;
 
     /* configuration parameters */
     char *sev_device;
-    uint32_t policy;
-    char *dh_cert_file;
-    char *session_file;
     uint32_t cbitpos;
     uint32_t reduced_phys_bits;
 
     /* runtime state */
-    uint32_t handle;
     uint8_t api_major;
     uint8_t api_minor;
     uint8_t build_id;
     uint64_t me_mask;
     int sev_fd;
     SevState state;
-    gchar *measurement;
 
     uint32_t reset_cs;
     uint32_t reset_ip;
     bool reset_data_valid;
 };
 
+struct SevGuestState {
+    SevCommonState sev_common;
+    gchar *measurement;
+
+    /* configuration parameters */
+    uint32_t handle;
+    uint32_t policy;
+    char *dh_cert_file;
+    char *session_file;
+};
+
 #define DEFAULT_GUEST_POLICY    0x1 /* disable debug */
 #define DEFAULT_SEV_DEVICE      "/dev/sev"
 
@@ -83,7 +91,6 @@ typedef struct __attribute__((__packed__)) SevInfoBlock {
     uint32_t reset_addr;
 } SevInfoBlock;
 
-static SevGuestState *sev_guest;
 static Error *sev_mig_blocker;
 
 static const char *const sev_fw_errlist[] = {
@@ -164,21 +171,21 @@ fw_error_to_str(int code)
 }
 
 static bool
-sev_check_state(const SevGuestState *sev, SevState state)
+sev_check_state(const SevCommonState *sev_common, SevState state)
 {
-    assert(sev);
-    return sev->state == state ? true : false;
+    assert(sev_common);
+    return sev_common->state == state ? true : false;
 }
 
 static void
-sev_set_guest_state(SevGuestState *sev, SevState new_state)
+sev_set_guest_state(SevCommonState *sev_common, SevState new_state)
 {
     assert(new_state < SEV_STATE__MAX);
-    assert(sev);
+    assert(sev_common);
 
-    trace_kvm_sev_change_state(SevState_str(sev->state),
+    trace_kvm_sev_change_state(SevState_str(sev_common->state),
                                SevState_str(new_state));
-    sev->state = new_state;
+    sev_common->state = new_state;
 }
 
 static void
@@ -245,67 +252,85 @@ static struct RAMBlockNotifier sev_ram_notifier = {
     .ram_block_removed = sev_ram_block_removed,
 };
 
-static void
-sev_guest_finalize(Object *obj)
+static char *
+sev_common_get_sev_device(Object *obj, Error **errp)
 {
+    return g_strdup(SEV_COMMON(obj)->sev_device);
 }
 
-static char *
-sev_guest_get_session_file(Object *obj, Error **errp)
+static void
+sev_common_set_sev_device(Object *obj, const char *value, Error **errp)
 {
-    SevGuestState *s = SEV_GUEST(obj);
+    SEV_COMMON(obj)->sev_device = g_strdup(value);
+}
 
-    return s->session_file ? g_strdup(s->session_file) : NULL;
+static void
+sev_common_class_init(ObjectClass *oc, void *data)
+{
+    object_class_property_add_str(oc, "sev-device",
+                                  sev_common_get_sev_device,
+                                  sev_common_set_sev_device);
+    object_class_property_set_description(oc, "sev-device",
+            "SEV device to use");
 }
 
 static void
-sev_guest_set_session_file(Object *obj, const char *value, Error **errp)
+sev_common_instance_init(Object *obj)
 {
-    SevGuestState *s = SEV_GUEST(obj);
+    SevCommonState *sev_common = SEV_COMMON(obj);
+
+    sev_common->sev_device = g_strdup(DEFAULT_SEV_DEVICE);
 
-    s->session_file = g_strdup(value);
+    object_property_add_uint32_ptr(obj, "cbitpos", &sev_common->cbitpos,
+                                   OBJ_PROP_FLAG_READWRITE);
+    object_property_add_uint32_ptr(obj, "reduced-phys-bits",
+                                   &sev_common->reduced_phys_bits,
+                                   OBJ_PROP_FLAG_READWRITE);
 }
 
+/* sev guest info common to sev/sev-es/sev-snp */
+static const TypeInfo sev_common_info = {
+    .parent = TYPE_CONFIDENTIAL_GUEST_SUPPORT,
+    .name = TYPE_SEV_COMMON,
+    .instance_size = sizeof(SevCommonState),
+    .class_init = sev_common_class_init,
+    .instance_init = sev_common_instance_init,
+    .abstract = true,
+    .interfaces = (InterfaceInfo[]) {
+        { TYPE_USER_CREATABLE },
+        { }
+    }
+};
+
 static char *
 sev_guest_get_dh_cert_file(Object *obj, Error **errp)
 {
-    SevGuestState *s = SEV_GUEST(obj);
-
-    return g_strdup(s->dh_cert_file);
+    return g_strdup(SEV_GUEST(obj)->dh_cert_file);
 }
 
 static void
 sev_guest_set_dh_cert_file(Object *obj, const char *value, Error **errp)
 {
-    SevGuestState *s = SEV_GUEST(obj);
-
-    s->dh_cert_file = g_strdup(value);
+    SEV_GUEST(obj)->dh_cert_file = g_strdup(value);
 }
 
 static char *
-sev_guest_get_sev_device(Object *obj, Error **errp)
+sev_guest_get_session_file(Object *obj, Error **errp)
 {
-    SevGuestState *sev = SEV_GUEST(obj);
+    SevGuestState *sev_guest = SEV_GUEST(obj);
 
-    return g_strdup(sev->sev_device);
+    return sev_guest->session_file ? g_strdup(sev_guest->session_file) : NULL;
 }
 
 static void
-sev_guest_set_sev_device(Object *obj, const char *value, Error **errp)
+sev_guest_set_session_file(Object *obj, const char *value, Error **errp)
 {
-    SevGuestState *sev = SEV_GUEST(obj);
-
-    sev->sev_device = g_strdup(value);
+    SEV_GUEST(obj)->session_file = g_strdup(value);
 }
 
 static void
 sev_guest_class_init(ObjectClass *oc, void *data)
 {
-    object_class_property_add_str(oc, "sev-device",
-                                  sev_guest_get_sev_device,
-                                  sev_guest_set_sev_device);
-    object_class_property_set_description(oc, "sev-device",
-            "SEV device to use");
     object_class_property_add_str(oc, "dh-cert-file",
                                   sev_guest_get_dh_cert_file,
                                   sev_guest_set_dh_cert_file);
@@ -321,80 +346,88 @@ sev_guest_class_init(ObjectClass *oc, void *data)
 static void
 sev_guest_instance_init(Object *obj)
 {
-    SevGuestState *sev = SEV_GUEST(obj);
+    SevGuestState *sev_guest = SEV_GUEST(obj);
 
-    sev->sev_device = g_strdup(DEFAULT_SEV_DEVICE);
-    sev->policy = DEFAULT_GUEST_POLICY;
-    object_property_add_uint32_ptr(obj, "policy", &sev->policy,
-                                   OBJ_PROP_FLAG_READWRITE);
-    object_property_add_uint32_ptr(obj, "handle", &sev->handle,
+    sev_guest->policy = DEFAULT_GUEST_POLICY;
+    object_property_add_uint32_ptr(obj, "handle", &sev_guest->handle,
                                    OBJ_PROP_FLAG_READWRITE);
-    object_property_add_uint32_ptr(obj, "cbitpos", &sev->cbitpos,
-                                   OBJ_PROP_FLAG_READWRITE);
-    object_property_add_uint32_ptr(obj, "reduced-phys-bits",
-                                   &sev->reduced_phys_bits,
+    object_property_add_uint32_ptr(obj, "policy", &sev_guest->policy,
                                    OBJ_PROP_FLAG_READWRITE);
 }
 
-/* sev guest info */
+/* guest info specific sev/sev-es */
 static const TypeInfo sev_guest_info = {
-    .parent = TYPE_CONFIDENTIAL_GUEST_SUPPORT,
+    .parent = TYPE_SEV_COMMON,
     .name = TYPE_SEV_GUEST,
     .instance_size = sizeof(SevGuestState),
-    .instance_finalize = sev_guest_finalize,
-    .class_init = sev_guest_class_init,
     .instance_init = sev_guest_instance_init,
-    .interfaces = (InterfaceInfo[]) {
-        { TYPE_USER_CREATABLE },
-        { }
-    }
+    .class_init = sev_guest_class_init,
 };
 
 bool
 sev_enabled(void)
 {
-    return !!sev_guest;
+    ConfidentialGuestSupport *cgs = MACHINE(qdev_get_machine())->cgs;
+
+    return !!object_dynamic_cast(OBJECT(cgs), TYPE_SEV_COMMON);
 }
 
 bool
 sev_es_enabled(void)
 {
-    return sev_enabled() && (sev_guest->policy & SEV_POLICY_ES);
+    ConfidentialGuestSupport *cgs = MACHINE(qdev_get_machine())->cgs;
+
+    return sev_enabled() && (SEV_GUEST(cgs)->policy & SEV_POLICY_ES);
 }
 
 uint64_t
 sev_get_me_mask(void)
 {
-    return sev_guest ? sev_guest->me_mask : ~0;
+    SevCommonState *sev_common = SEV_COMMON(MACHINE(qdev_get_machine())->cgs);
+
+    return sev_common ? sev_common->me_mask : ~0;
 }
 
 uint32_t
 sev_get_cbit_position(void)
 {
-    return sev_guest ? sev_guest->cbitpos : 0;
+    SevCommonState *sev_common = SEV_COMMON(MACHINE(qdev_get_machine())->cgs);
+
+    return sev_common ? sev_common->cbitpos : 0;
 }
 
 uint32_t
 sev_get_reduced_phys_bits(void)
 {
-    return sev_guest ? sev_guest->reduced_phys_bits : 0;
+    SevCommonState *sev_common = SEV_COMMON(MACHINE(qdev_get_machine())->cgs);
+
+    return sev_common ? sev_common->reduced_phys_bits : 0;
 }
 
 SevInfo *
 sev_get_info(void)
 {
     SevInfo *info;
+    SevCommonState *sev_common = SEV_COMMON(MACHINE(qdev_get_machine())->cgs);
+    SevGuestState *sev_guest =
+        (SevGuestState *)object_dynamic_cast(OBJECT(sev_common),
+                                             TYPE_SEV_GUEST);
 
     info = g_new0(SevInfo, 1);
     info->enabled = sev_enabled();
 
     if (info->enabled) {
-        info->api_major = sev_guest->api_major;
-        info->api_minor = sev_guest->api_minor;
-        info->build_id = sev_guest->build_id;
-        info->policy = sev_guest->policy;
-        info->state = sev_guest->state;
-        info->handle = sev_guest->handle;
+        if (sev_guest) {
+            info->handle = sev_guest->handle;
+        }
+        info->api_major = sev_common->api_major;
+        info->api_minor = sev_common->api_minor;
+        info->build_id = sev_common->build_id;
+        info->state = sev_common->state;
+        /* we only report the lower 32-bits of policy for SNP, ok for now... */
+        info->policy =
+            (uint32_t)object_property_get_uint(OBJECT(sev_common),
+                                               "policy", NULL);
     }
 
     return info;
@@ -452,6 +485,8 @@ sev_get_capabilities(Error **errp)
     size_t pdh_len = 0, cert_chain_len = 0;
     uint32_t ebx;
     int fd;
+    SevCommonState *sev_common;
+    char *sev_device;
 
     if (!kvm_enabled()) {
         error_setg(errp, "KVM not enabled");
@@ -462,12 +497,21 @@ sev_get_capabilities(Error **errp)
         return NULL;
     }
 
-    fd = open(DEFAULT_SEV_DEVICE, O_RDWR);
+    sev_common = SEV_COMMON(MACHINE(qdev_get_machine())->cgs);
+    if (!sev_common) {
+        error_setg(errp, "SEV is not configured");
+    }
+
+    sev_device = object_property_get_str(OBJECT(sev_common), "sev-device",
+                                         &error_abort);
+    fd = open(sev_device, O_RDWR);
     if (fd < 0) {
         error_setg_errno(errp, errno, "Failed to open %s",
                          DEFAULT_SEV_DEVICE);
+        g_free(sev_device);
         return NULL;
     }
+    g_free(sev_device);
 
     if (sev_get_pdh_info(fd, &pdh_data, &pdh_len,
                          &cert_chain_data, &cert_chain_len, errp)) {
@@ -499,7 +543,7 @@ sev_get_attestation_report(const char *mnonce, Error **errp)
 {
     struct kvm_sev_attestation_report input = {};
     SevAttestationReport *report = NULL;
-    SevGuestState *sev = sev_guest;
+    SevCommonState *sev_common;
     guchar *data;
     guchar *buf;
     gsize len;
@@ -525,8 +569,10 @@ sev_get_attestation_report(const char *mnonce, Error **errp)
         return NULL;
     }
 
+    sev_common = SEV_COMMON(MACHINE(qdev_get_machine())->cgs);
+
     /* Query the report length */
-    ret = sev_ioctl(sev->sev_fd, KVM_SEV_GET_ATTESTATION_REPORT,
+    ret = sev_ioctl(sev_common->sev_fd, KVM_SEV_GET_ATTESTATION_REPORT,
             &input, &err);
     if (ret < 0) {
         if (err != SEV_RET_INVALID_LEN) {
@@ -542,7 +588,7 @@ sev_get_attestation_report(const char *mnonce, Error **errp)
     memcpy(input.mnonce, buf, sizeof(input.mnonce));
 
     /* Query the report */
-    ret = sev_ioctl(sev->sev_fd, KVM_SEV_GET_ATTESTATION_REPORT,
+    ret = sev_ioctl(sev_common->sev_fd, KVM_SEV_GET_ATTESTATION_REPORT,
             &input, &err);
     if (ret) {
         error_setg_errno(errp, errno, "Failed to get attestation report"
@@ -579,28 +625,29 @@ sev_read_file_base64(const char *filename, guchar **data, gsize *len)
 }
 
 static int
-sev_launch_start(SevGuestState *sev)
+sev_launch_start(SevGuestState *sev_guest)
 {
     gsize sz;
     int ret = 1;
     int fw_error, rc;
     struct kvm_sev_launch_start *start;
     guchar *session = NULL, *dh_cert = NULL;
+    SevCommonState *sev_common = SEV_COMMON(sev_guest);
 
     start = g_new0(struct kvm_sev_launch_start, 1);
 
-    start->handle = sev->handle;
-    start->policy = sev->policy;
-    if (sev->session_file) {
-        if (sev_read_file_base64(sev->session_file, &session, &sz) < 0) {
+    start->handle = sev_guest->handle;
+    start->policy = sev_guest->policy;
+    if (sev_guest->session_file) {
+        if (sev_read_file_base64(sev_guest->session_file, &session, &sz) < 0) {
             goto out;
         }
         start->session_uaddr = (unsigned long)session;
         start->session_len = sz;
     }
 
-    if (sev->dh_cert_file) {
-        if (sev_read_file_base64(sev->dh_cert_file, &dh_cert, &sz) < 0) {
+    if (sev_guest->dh_cert_file) {
+        if (sev_read_file_base64(sev_guest->dh_cert_file, &dh_cert, &sz) < 0) {
             goto out;
         }
         start->dh_uaddr = (unsigned long)dh_cert;
@@ -608,15 +655,15 @@ sev_launch_start(SevGuestState *sev)
     }
 
     trace_kvm_sev_launch_start(start->policy, session, dh_cert);
-    rc = sev_ioctl(sev->sev_fd, KVM_SEV_LAUNCH_START, start, &fw_error);
+    rc = sev_ioctl(sev_common->sev_fd, KVM_SEV_LAUNCH_START, start, &fw_error);
     if (rc < 0) {
         error_report("%s: LAUNCH_START ret=%d fw_error=%d '%s'",
                 __func__, ret, fw_error, fw_error_to_str(fw_error));
         goto out;
     }
 
-    sev_set_guest_state(sev, SEV_STATE_LAUNCH_UPDATE);
-    sev->handle = start->handle;
+    sev_set_guest_state(sev_common, SEV_STATE_LAUNCH_UPDATE);
+    sev_guest->handle = start->handle;
     ret = 0;
 
 out:
@@ -627,7 +674,7 @@ out:
 }
 
 static int
-sev_launch_update_data(SevGuestState *sev, uint8_t *addr, uint64_t len)
+sev_launch_update_data(SevGuestState *sev_guest, uint8_t *addr, uint64_t len)
 {
     int ret, fw_error;
     struct kvm_sev_launch_update_data update;
@@ -639,7 +686,7 @@ sev_launch_update_data(SevGuestState *sev, uint8_t *addr, uint64_t len)
     update.uaddr = (__u64)(unsigned long)addr;
     update.len = len;
     trace_kvm_sev_launch_update_data(addr, len);
-    ret = sev_ioctl(sev->sev_fd, KVM_SEV_LAUNCH_UPDATE_DATA,
+    ret = sev_ioctl(SEV_COMMON(sev_guest)->sev_fd, KVM_SEV_LAUNCH_UPDATE_DATA,
                     &update, &fw_error);
     if (ret) {
         error_report("%s: LAUNCH_UPDATE ret=%d fw_error=%d '%s'",
@@ -650,11 +697,12 @@ sev_launch_update_data(SevGuestState *sev, uint8_t *addr, uint64_t len)
 }
 
 static int
-sev_launch_update_vmsa(SevGuestState *sev)
+sev_launch_update_vmsa(SevGuestState *sev_guest)
 {
     int ret, fw_error;
 
-    ret = sev_ioctl(sev->sev_fd, KVM_SEV_LAUNCH_UPDATE_VMSA, NULL, &fw_error);
+    ret = sev_ioctl(SEV_COMMON(sev_guest)->sev_fd, KVM_SEV_LAUNCH_UPDATE_VMSA,
+                    NULL, &fw_error);
     if (ret) {
         error_report("%s: LAUNCH_UPDATE_VMSA ret=%d fw_error=%d '%s'",
                 __func__, ret, fw_error, fw_error_to_str(fw_error));
@@ -666,18 +714,19 @@ sev_launch_update_vmsa(SevGuestState *sev)
 static void
 sev_launch_get_measure(Notifier *notifier, void *unused)
 {
-    SevGuestState *sev = sev_guest;
+    SevCommonState *sev_common = SEV_COMMON(MACHINE(qdev_get_machine())->cgs);
+    SevGuestState *sev_guest = SEV_GUEST(sev_common);
     int ret, error;
     guchar *data;
     struct kvm_sev_launch_measure *measurement;
 
-    if (!sev_check_state(sev, SEV_STATE_LAUNCH_UPDATE)) {
+    if (!sev_check_state(sev_common, SEV_STATE_LAUNCH_UPDATE)) {
         return;
     }
 
     if (sev_es_enabled()) {
         /* measure all the VM save areas before getting launch_measure */
-        ret = sev_launch_update_vmsa(sev);
+        ret = sev_launch_update_vmsa(sev_guest);
         if (ret) {
             exit(1);
         }
@@ -686,7 +735,7 @@ sev_launch_get_measure(Notifier *notifier, void *unused)
     measurement = g_new0(struct kvm_sev_launch_measure, 1);
 
     /* query the measurement blob length */
-    ret = sev_ioctl(sev->sev_fd, KVM_SEV_LAUNCH_MEASURE,
+    ret = sev_ioctl(sev_common->sev_fd, KVM_SEV_LAUNCH_MEASURE,
                     measurement, &error);
     if (!measurement->len) {
         error_report("%s: LAUNCH_MEASURE ret=%d fw_error=%d '%s'",
@@ -698,7 +747,7 @@ sev_launch_get_measure(Notifier *notifier, void *unused)
     measurement->uaddr = (unsigned long)data;
 
     /* get the measurement blob */
-    ret = sev_ioctl(sev->sev_fd, KVM_SEV_LAUNCH_MEASURE,
+    ret = sev_ioctl(sev_common->sev_fd, KVM_SEV_LAUNCH_MEASURE,
                     measurement, &error);
     if (ret) {
         error_report("%s: LAUNCH_MEASURE ret=%d fw_error=%d '%s'",
@@ -706,11 +755,11 @@ sev_launch_get_measure(Notifier *notifier, void *unused)
         goto free_data;
     }
 
-    sev_set_guest_state(sev, SEV_STATE_LAUNCH_SECRET);
+    sev_set_guest_state(sev_common, SEV_STATE_LAUNCH_SECRET);
 
     /* encode the measurement value and emit the event */
-    sev->measurement = g_base64_encode(data, measurement->len);
-    trace_kvm_sev_launch_measurement(sev->measurement);
+    sev_guest->measurement = g_base64_encode(data, measurement->len);
+    trace_kvm_sev_launch_measurement(sev_guest->measurement);
 
 free_data:
     g_free(data);
@@ -721,8 +770,10 @@ free_measurement:
 char *
 sev_get_launch_measurement(void)
 {
+    SevGuestState *sev_guest = SEV_GUEST(MACHINE(qdev_get_machine())->cgs);
+
     if (sev_guest &&
-        sev_guest->state >= SEV_STATE_LAUNCH_SECRET) {
+        SEV_COMMON(sev_guest)->state >= SEV_STATE_LAUNCH_SECRET) {
         return g_strdup(sev_guest->measurement);
     }
 
@@ -734,20 +785,21 @@ static Notifier sev_machine_done_notify = {
 };
 
 static void
-sev_launch_finish(SevGuestState *sev)
+sev_launch_finish(SevGuestState *sev_guest)
 {
     int ret, error;
     Error *local_err = NULL;
 
     trace_kvm_sev_launch_finish();
-    ret = sev_ioctl(sev->sev_fd, KVM_SEV_LAUNCH_FINISH, 0, &error);
+    ret = sev_ioctl(SEV_COMMON(sev_guest)->sev_fd, KVM_SEV_LAUNCH_FINISH, 0,
+                    &error);
     if (ret) {
         error_report("%s: LAUNCH_FINISH ret=%d fw_error=%d '%s'",
                      __func__, ret, error, fw_error_to_str(error));
         exit(1);
     }
 
-    sev_set_guest_state(sev, SEV_STATE_RUNNING);
+    sev_set_guest_state(SEV_COMMON(sev_guest), SEV_STATE_RUNNING);
 
     /* add migration blocker */
     error_setg(&sev_mig_blocker,
@@ -763,26 +815,25 @@ sev_launch_finish(SevGuestState *sev)
 static void
 sev_vm_state_change(void *opaque, bool running, RunState state)
 {
-    SevGuestState *sev = opaque;
+    SevCommonState *sev_common = opaque;
 
     if (running) {
-        if (!sev_check_state(sev, SEV_STATE_RUNNING)) {
-            sev_launch_finish(sev);
+        if (!sev_check_state(sev_common, SEV_STATE_RUNNING)) {
+            sev_launch_finish(SEV_GUEST(sev_common));
         }
     }
 }
 
 int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp)
 {
-    SevGuestState *sev
-        = (SevGuestState *)object_dynamic_cast(OBJECT(cgs), TYPE_SEV_GUEST);
+    SevCommonState *sev_common = SEV_COMMON(cgs);
     char *devname;
     int ret, fw_error, cmd;
     uint32_t ebx;
     uint32_t host_cbitpos;
     struct sev_user_data_status status = {};
 
-    if (!sev) {
+    if (!sev_common) {
         return 0;
     }
 
@@ -792,29 +843,28 @@ int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp)
         return -1;
     }
 
-    sev_guest = sev;
-    sev->state = SEV_STATE_UNINIT;
+    sev_common->state = SEV_STATE_UNINIT;
 
     host_cpuid(0x8000001F, 0, NULL, &ebx, NULL, NULL);
     host_cbitpos = ebx & 0x3f;
 
-    if (host_cbitpos != sev->cbitpos) {
+    if (host_cbitpos != sev_common->cbitpos) {
         error_setg(errp, "%s: cbitpos check failed, host '%d' requested '%d'",
-                   __func__, host_cbitpos, sev->cbitpos);
+                   __func__, host_cbitpos, sev_common->cbitpos);
         goto err;
     }
 
-    if (sev->reduced_phys_bits < 1) {
+    if (sev_common->reduced_phys_bits < 1) {
         error_setg(errp, "%s: reduced_phys_bits check failed, it should be >=1,"
-                   " requested '%d'", __func__, sev->reduced_phys_bits);
+                   " requested '%d'", __func__, sev_common->reduced_phys_bits);
         goto err;
     }
 
-    sev->me_mask = ~(1UL << sev->cbitpos);
+    sev_common->me_mask = ~(1UL << sev_common->cbitpos);
 
-    devname = object_property_get_str(OBJECT(sev), "sev-device", NULL);
-    sev->sev_fd = open(devname, O_RDWR);
-    if (sev->sev_fd < 0) {
+    devname = object_property_get_str(OBJECT(sev_common), "sev-device", NULL);
+    sev_common->sev_fd = open(devname, O_RDWR);
+    if (sev_common->sev_fd < 0) {
         error_setg(errp, "%s: Failed to open %s '%s'", __func__,
                    devname, strerror(errno));
         g_free(devname);
@@ -822,7 +872,7 @@ int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp)
     }
     g_free(devname);
 
-    ret = sev_platform_ioctl(sev->sev_fd, SEV_PLATFORM_STATUS, &status,
+    ret = sev_platform_ioctl(sev_common->sev_fd, SEV_PLATFORM_STATUS, &status,
                              &fw_error);
     if (ret) {
         error_setg(errp, "%s: failed to get platform status ret=%d "
@@ -830,9 +880,9 @@ int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp)
                    fw_error_to_str(fw_error));
         goto err;
     }
-    sev->build_id = status.build;
-    sev->api_major = status.api_major;
-    sev->api_minor = status.api_minor;
+    sev_common->build_id = status.build;
+    sev_common->api_major = status.api_major;
+    sev_common->api_minor = status.api_minor;
 
     if (sev_es_enabled()) {
         if (!kvm_kernel_irqchip_allowed()) {
@@ -853,14 +903,14 @@ int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp)
     }
 
     trace_kvm_sev_init();
-    ret = sev_ioctl(sev->sev_fd, cmd, NULL, &fw_error);
+    ret = sev_ioctl(sev_common->sev_fd, cmd, NULL, &fw_error);
     if (ret) {
         error_setg(errp, "%s: failed to initialize ret=%d fw_error=%d '%s'",
                    __func__, ret, fw_error, fw_error_to_str(fw_error));
         goto err;
     }
 
-    ret = sev_launch_start(sev);
+    ret = sev_launch_start(SEV_GUEST(sev_common));
     if (ret) {
         error_setg(errp, "%s: failed to create encryption context", __func__);
         goto err;
@@ -868,13 +918,12 @@ int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp)
 
     ram_block_notifier_add(&sev_ram_notifier);
     qemu_add_machine_init_done_notifier(&sev_machine_done_notify);
-    qemu_add_vm_change_state_handler(sev_vm_state_change, sev);
+    qemu_add_vm_change_state_handler(sev_vm_state_change, sev_common);
 
     cgs->ready = true;
 
     return 0;
 err:
-    sev_guest = NULL;
     ram_block_discard_disable(false);
     return -1;
 }
@@ -882,13 +931,15 @@ err:
 int
 sev_encrypt_flash(uint8_t *ptr, uint64_t len, Error **errp)
 {
-    if (!sev_guest) {
+    SevCommonState *sev_common = SEV_COMMON(MACHINE(qdev_get_machine())->cgs);
+
+    if (!sev_common) {
         return 0;
     }
 
     /* if SEV is in update state then encrypt the data else do nothing */
-    if (sev_check_state(sev_guest, SEV_STATE_LAUNCH_UPDATE)) {
-        int ret = sev_launch_update_data(sev_guest, ptr, len);
+    if (sev_check_state(sev_common, SEV_STATE_LAUNCH_UPDATE)) {
+        int ret = sev_launch_update_data(SEV_GUEST(sev_common), ptr, len);
         if (ret < 0) {
             error_setg(errp, "failed to encrypt pflash rom");
             return ret;
@@ -907,16 +958,17 @@ int sev_inject_launch_secret(const char *packet_hdr, const char *secret,
     void *hva;
     gsize hdr_sz = 0, data_sz = 0;
     MemoryRegion *mr = NULL;
+    SevCommonState *sev_common = SEV_COMMON(MACHINE(qdev_get_machine())->cgs);
 
-    if (!sev_guest) {
+    if (!sev_common) {
         error_setg(errp, "SEV: SEV not enabled.");
         return 1;
     }
 
     /* secret can be injected only in this state */
-    if (!sev_check_state(sev_guest, SEV_STATE_LAUNCH_SECRET)) {
+    if (!sev_check_state(sev_common, SEV_STATE_LAUNCH_SECRET)) {
         error_setg(errp, "SEV: Not in correct state. (LSECRET) %x",
-                     sev_guest->state);
+                   sev_common->state);
         return 1;
     }
 
@@ -950,7 +1002,7 @@ int sev_inject_launch_secret(const char *packet_hdr, const char *secret,
     trace_kvm_sev_launch_secret(gpa, input.guest_uaddr,
                                 input.trans_uaddr, input.trans_len);
 
-    ret = sev_ioctl(sev_guest->sev_fd, KVM_SEV_LAUNCH_SECRET,
+    ret = sev_ioctl(sev_common->sev_fd, KVM_SEV_LAUNCH_SECRET,
                     &input, &error);
     if (ret) {
         error_setg(errp, "SEV: failed to inject secret ret=%d fw_error=%d '%s'",
@@ -1026,9 +1078,10 @@ void sev_es_set_reset_vector(CPUState *cpu)
 {
     X86CPU *x86;
     CPUX86State *env;
+    SevCommonState *sev_common = SEV_COMMON(MACHINE(qdev_get_machine())->cgs);
 
     /* Only update if we have valid reset information */
-    if (!sev_guest || !sev_guest->reset_data_valid) {
+    if (!sev_common || !sev_common->reset_data_valid) {
         return;
     }
 
@@ -1040,11 +1093,11 @@ void sev_es_set_reset_vector(CPUState *cpu)
     x86 = X86_CPU(cpu);
     env = &x86->env;
 
-    cpu_x86_load_seg_cache(env, R_CS, 0xf000, sev_guest->reset_cs, 0xffff,
+    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_guest->reset_ip;
+    env->eip = sev_common->reset_ip;
 }
 
 int sev_es_save_reset_vector(void *flash_ptr, uint64_t flash_size)
@@ -1052,6 +1105,7 @@ 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;
@@ -1065,9 +1119,9 @@ int sev_es_save_reset_vector(void *flash_ptr, uint64_t flash_size)
     }
 
     if (addr) {
-        sev_guest->reset_cs = addr & 0xffff0000;
-        sev_guest->reset_ip = addr & 0x0000ffff;
-        sev_guest->reset_data_valid = true;
+        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);
@@ -1080,6 +1134,7 @@ int sev_es_save_reset_vector(void *flash_ptr, uint64_t flash_size)
 static void
 sev_register_types(void)
 {
+    type_register_static(&sev_common_info);
     type_register_static(&sev_guest_info);
 }
 
-- 
2.25.1


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

* [RFC PATCH v2 02/12] linux-header: add the SNP specific command
  2021-08-26 22:26 [RFC PATCH v2 00/12] Add AMD Secure Nested Paging (SEV-SNP) support Michael Roth
  2021-08-26 22:26 ` [RFC PATCH v2 01/12] i386/sev: introduce "sev-common" type to encapsulate common SEV state Michael Roth
@ 2021-08-26 22:26 ` Michael Roth
  2021-09-03 20:36     ` Dov Murik
  2021-08-26 22:26 ` [RFC PATCH v2 03/12] i386/sev: introduce 'sev-snp-guest' object Michael Roth
                   ` (10 subsequent siblings)
  12 siblings, 1 reply; 61+ messages in thread
From: Michael Roth @ 2021-08-26 22:26 UTC (permalink / raw)
  To: qemu-devel
  Cc: Connor Kuehl, Philippe Mathieu-Daudé,
	Michael S . Tsirkin, James Bottomley, Dr . David Alan Gilbert,
	Tom Lendacky, Paolo Bonzini, Dov Murik, David Gibson,
	Daniel P . Berrangé,
	kvm, Eduardo Habkost, Brijesh Singh, Markus Armbruster,
	Eric Blake

From: Brijesh Singh <brijesh.singh@amd.com>

Sync the kvm.h with the kernel to include the SNP specific commands.

Signed-off-by: Brijesh Singh <brijesh.singh@amd.com>
Signed-off-by: Michael Roth <michael.roth@amd.com>
---
 linux-headers/linux/kvm.h | 50 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 50 insertions(+)

diff --git a/linux-headers/linux/kvm.h b/linux-headers/linux/kvm.h
index bcaf66cc4d..486c12b4f7 100644
--- a/linux-headers/linux/kvm.h
+++ b/linux-headers/linux/kvm.h
@@ -1712,6 +1712,12 @@ enum sev_cmd_id {
 	/* Guest Migration Extension */
 	KVM_SEV_SEND_CANCEL,
 
+	/* SNP specific commands */
+	KVM_SEV_SNP_INIT,
+	KVM_SEV_SNP_LAUNCH_START,
+	KVM_SEV_SNP_LAUNCH_UPDATE,
+	KVM_SEV_SNP_LAUNCH_FINISH,
+
 	KVM_SEV_NR_MAX,
 };
 
@@ -1808,6 +1814,50 @@ struct kvm_sev_receive_update_data {
 	__u32 trans_len;
 };
 
+struct kvm_snp_init {
+	__u64 flags;
+};
+
+struct kvm_sev_snp_launch_start {
+	__u64 policy;
+	__u64 ma_uaddr;
+	__u8 ma_en;
+	__u8 imi_en;
+	__u8 gosvw[16];
+	__u8 pad[6];
+};
+
+#define KVM_SEV_SNP_PAGE_TYPE_NORMAL		0x1
+#define KVM_SEV_SNP_PAGE_TYPE_VMSA		0x2
+#define KVM_SEV_SNP_PAGE_TYPE_ZERO		0x3
+#define KVM_SEV_SNP_PAGE_TYPE_UNMEASURED	0x4
+#define KVM_SEV_SNP_PAGE_TYPE_SECRETS		0x5
+#define KVM_SEV_SNP_PAGE_TYPE_CPUID		0x6
+
+struct kvm_sev_snp_launch_update {
+        __u64 start_gfn;
+	__u64 uaddr;
+	__u32 len;
+	__u8 imi_page;
+	__u8 page_type;
+	__u8 vmpl3_perms;
+	__u8 vmpl2_perms;
+	__u8 vmpl1_perms;
+};
+
+#define KVM_SEV_SNP_ID_BLOCK_SIZE	96
+#define KVM_SEV_SNP_ID_AUTH_SIZE	4096
+#define KVM_SEV_SNP_FINISH_DATA_SIZE	32
+
+struct kvm_sev_snp_launch_finish {
+	__u64 id_block_uaddr;
+	__u64 id_auth_uaddr;
+	__u8 id_block_en;
+	__u8 auth_key_en;
+	__u8 host_data[KVM_SEV_SNP_FINISH_DATA_SIZE];
+	__u8 pad[6];
+};
+
 #define KVM_DEV_ASSIGN_ENABLE_IOMMU	(1 << 0)
 #define KVM_DEV_ASSIGN_PCI_2_3		(1 << 1)
 #define KVM_DEV_ASSIGN_MASK_INTX	(1 << 2)
-- 
2.25.1


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

* [RFC PATCH v2 03/12] i386/sev: introduce 'sev-snp-guest' object
  2021-08-26 22:26 [RFC PATCH v2 00/12] Add AMD Secure Nested Paging (SEV-SNP) support Michael Roth
  2021-08-26 22:26 ` [RFC PATCH v2 01/12] i386/sev: introduce "sev-common" type to encapsulate common SEV state Michael Roth
  2021-08-26 22:26 ` [RFC PATCH v2 02/12] linux-header: add the SNP specific command Michael Roth
@ 2021-08-26 22:26 ` Michael Roth
  2021-09-01 14:29   ` Markus Armbruster
  2021-09-03 21:12     ` Dov Murik
  2021-08-26 22:26 ` [RFC PATCH v2 04/12] i386/sev: initialize SNP context Michael Roth
                   ` (9 subsequent siblings)
  12 siblings, 2 replies; 61+ messages in thread
From: Michael Roth @ 2021-08-26 22:26 UTC (permalink / raw)
  To: qemu-devel
  Cc: Connor Kuehl, Philippe Mathieu-Daudé,
	Michael S . Tsirkin, James Bottomley, Dr . David Alan Gilbert,
	Tom Lendacky, Paolo Bonzini, Dov Murik, David Gibson,
	Daniel P . Berrangé,
	kvm, Eduardo Habkost, Brijesh Singh, Markus Armbruster,
	Eric Blake

From: Brijesh Singh <brijesh.singh@amd.com>

SEV-SNP support relies on a different set of properties/state than the
existing 'sev-guest' object. This patch introduces the 'sev-snp-guest'
object, which can be used to configure an SEV-SNP guest. For example,
a default-configured SEV-SNP guest with no additional information
passed in for use with attestation:

  -object sev-snp-guest,id=sev0

or a fully-specified SEV-SNP guest where all spec-defined binary
blobs are passed in as base64-encoded strings:

  -object sev-snp-guest,id=sev0, \
    policy=0x30000, \
    init-flags=0, \
    id-block=YWFhYWFhYWFhYWFhYWFhCg==, \
    id-auth=CxHK/OKLkXGn/KpAC7Wl1FSiisWDbGTEKz..., \
    auth-key-enabled=on, \
    host-data=LNkCWBRC5CcdGXirbNUV1OrsR28s..., \
    guest-visible-workarounds=AA==, \

See the QAPI schema updates included in this patch for more usage
details.

In some cases these blobs may be up to 4096 characters, but this is
generally well below the default limit for linux hosts where
command-line sizes are defined by the sysconf-configurable ARG_MAX
value, which defaults to 2097152 characters for Ubuntu hosts, for
example.

Co-developed-by: Michael Roth <michael.roth@amd.com>
Signed-off-by: Brijesh Singh <brijesh.singh@amd.com>
Signed-off-by: Michael Roth <michael.roth@amd.com>
---
 docs/amd-memory-encryption.txt |  77 ++++++++++-
 qapi/qom.json                  |  60 ++++++++
 target/i386/sev.c              | 245 ++++++++++++++++++++++++++++++++-
 3 files changed, 379 insertions(+), 3 deletions(-)

diff --git a/docs/amd-memory-encryption.txt b/docs/amd-memory-encryption.txt
index ffca382b5f..0d82e67fa1 100644
--- a/docs/amd-memory-encryption.txt
+++ b/docs/amd-memory-encryption.txt
@@ -22,8 +22,8 @@ support for notifying a guest's operating system when certain types of VMEXITs
 are about to occur. This allows the guest to selectively share information with
 the hypervisor to satisfy the requested function.
 
-Launching
----------
+Launching (SEV and SEV-ES)
+--------------------------
 Boot images (such as bios) must be encrypted before a guest can be booted. The
 MEMORY_ENCRYPT_OP ioctl provides commands to encrypt the images: LAUNCH_START,
 LAUNCH_UPDATE_DATA, LAUNCH_MEASURE and LAUNCH_FINISH. These four commands
@@ -113,6 +113,79 @@ a SEV-ES guest:
  - Requires in-kernel irqchip - the burden is placed on the hypervisor to
    manage booting APs.
 
+Launching (SEV-SNP)
+-------------------
+Boot images (such as bios) must be encrypted before a guest can be booted. The
+MEMORY_ENCRYPT_OP ioctl provides commands to encrypt the images:
+KVM_SNP_INIT, SNP_LAUNCH_START, SNP_LAUNCH_UPDATE, and SNP_LAUNCH_FINISH. These
+four commands together generate a fresh memory encryption key for the VM,
+encrypt the boot images for a successful launch.
+
+KVM_SNP_INIT is called first to initialize the SEV-SNP firmware and SNP
+features in the KVM. The feature flags value can be provided through the
+'init-flags' property of the 'sev-snp-guest' object.
+
++------------+-------+----------+---------------------------------+
+| key        | type  | default  | meaning                         |
++------------+-------+----------+---------------------------------+
+| init_flags | hex   | 0        | SNP feature flags               |
++-----------------------------------------------------------------+
+
+Note: currently the init_flags must be zero.
+
+SNP_LAUNCH_START is called first to create a cryptographic launch context
+within the firmware. To create this context, guest owner must provide a guest
+policy and other parameters as described in the SEV-SNP firmware
+specification. The launch parameters should be specified as described in the
+QAPI schema for the 'sev-snp-guest' object.
+
+The SNP_LAUNCH_START uses the following parameters (see the SEV-SNP
+specification for more details):
+
++--------+-------+----------+----------------------------------------------+
+| key    | type  | default  | meaning                                      |
++--------+-------+----------+----------------------------------------------+
+| policy | hex   | 0x30000  | a 64-bit guest policy                        |
+| imi_en | bool  | 0        | 1 when IMI is enabled                        |
+| ma_end | bool  | 0        | 1 when migration agent is used               |
+| gosvw  | string| 0        | 16-byte base64 encoded string for the guest  |
+|        |       |          | OS visible workaround.                       |
++--------+-------+----------+----------------------------------------------+
+
+SNP_LAUNCH_UPDATE encrypts the memory region using the cryptographic context
+created via the SNP_LAUNCH_START command. If required, this command can be called
+multiple times to encrypt different memory regions. The command also calculates
+the measurement of the memory contents as it encrypts.
+
+SNP_LAUNCH_FINISH finalizes the guest launch flow. Optionally, while finalizing
+the launch the firmware can perform checks on the launch digest computing
+through the SNP_LAUNCH_UPDATE. To perform the check the user must supply
+the id block, authentication blob and host data that should be included in the
+attestation report. See the SEV-SNP spec for further details.
+
+The SNP_LAUNCH_FINISH uses the following parameters, which can be configured
+by the corresponding parameters documented in the QAPI schema for the
+'sev-snp-guest' object.
+
++------------+-------+----------+----------------------------------------------+
+| key        | type  | default  | meaning                                      |
++------------+-------+----------+----------------------------------------------+
+| id_block   | string| none     | base64 encoded ID block                      |
++------------+-------+----------+----------------------------------------------+
+| id_auth    | string| none     | base64 encoded authentication information    |
++------------+-------+----------+----------------------------------------------+
+| auth_key_en| bool  | 0        | auth block contains author key               |
++------------+-------+----------+----------------------------------------------+
+| host_data  | string| none     | host provided data                           |
++------------+-------+----------+----------------------------------------------+
+
+To launch a SEV-SNP guest (additional parameters are documented in the QAPI
+schema for the 'sev-snp-guest' object):
+
+# ${QEMU} \
+    -machine ...,confidential-guest-support=sev0 \
+    -object sev-snp-guest,id=sev0,cbitpos=51,reduced-phys-bits=1
+
 Debugging
 -----------
 Since the memory contents of a SEV guest are encrypted, hypervisor access to
diff --git a/qapi/qom.json b/qapi/qom.json
index 211e083727..ea39585026 100644
--- a/qapi/qom.json
+++ b/qapi/qom.json
@@ -775,6 +775,64 @@
             '*policy': 'uint32',
             '*handle': 'uint32' } }
 
+##
+# @SevSnpGuestProperties:
+#
+# Properties for sev-snp-guest objects. Many of these are direct arguments
+# for the SEV-SNP KVM interfaces documented in the linux kernel source
+# documentation under 'amd-memory-encryption.rst'. Additional documentation
+# is also available in the QEMU source tree under
+# 'amd-memory-encryption.rst'.
+#
+# In addition to those files, please see the SEV-SNP Firmware Specification
+# (Rev 0.9) documentation for the SNP_INIT and
+# SNP_LAUNCH_{START,UPDATE,FINISH} firmware interfaces, which the KVM
+# interfaces are written against.
+#
+# @init-flags: as documented for the 'flags' parameter of the
+#              KVM_SNP_INIT KVM command (default: 0)
+#
+# @policy: as documented for the 'policy' parameter of the
+#          KVM_SNP_LAUNCH_START KVM command (default: 0x30000)
+#
+# @guest-visible-workarounds: 16-byte, base64-encoded blob to report
+#                             hypervisor-defined workarounds, as documented
+#                             for the 'gosvm' parameter of the
+#                             KVM_SNP_LAUNCH_START KVM command.
+#                             (default: all-zero)
+#
+# @id-block: 8-byte, base64-encoded blob to provide the ID Block
+#            structure documented in SEV-SNP spec, as documented for the
+#            'id_block_uaddr' parameter of the KVM_SNP_LAUNCH_FINISH
+#            command (default: all-zero)
+#
+# @id-auth: 4096-byte, base64-encoded blob to provide the ID Authentication
+#           Information Structure documented in SEV-SNP spec, as documented
+#           for the 'id_auth_uaddr' parameter of the KVM_SNP_LAUNCH_FINISH
+#           command (default: all-zero)
+#
+# @auth-key-enabled: true if 'id-auth' blob contains the Author Key
+#                    documented in the SEV-SNP spec, as documented for the
+#                    'auth_key_en' parameter of the KVM_SNP_LAUNCH_FINISH
+#                    command (default: false)
+#
+# @host-data: 32-byte, base64-encoded user-defined blob to provide to the
+#             guest, as documented for the 'host_data' parameter of the
+#             KVM_SNP_LAUNCH_FINISH command (default: all-zero)
+#
+# Since: 6.2
+##
+{ 'struct': 'SevSnpGuestProperties',
+  'base': 'SevCommonProperties',
+  'data': {
+            '*init-flags': 'uint64',
+            '*policy': 'uint64',
+            '*guest-visible-workarounds': 'str',
+            '*id-block': 'str',
+            '*id-auth': 'str',
+            '*auth-key-enabled': 'bool',
+            '*host-data': 'str' } }
+
 ##
 # @ObjectType:
 #
@@ -816,6 +874,7 @@
     'secret',
     'secret_keyring',
     'sev-guest',
+    'sev-snp-guest',
     's390-pv-guest',
     'throttle-group',
     'tls-creds-anon',
@@ -873,6 +932,7 @@
       'secret':                     'SecretProperties',
       'secret_keyring':             'SecretKeyringProperties',
       'sev-guest':                  'SevGuestProperties',
+      'sev-snp-guest':              'SevSnpGuestProperties',
       'throttle-group':             'ThrottleGroupProperties',
       'tls-creds-anon':             'TlsCredsAnonProperties',
       'tls-creds-psk':              'TlsCredsPskProperties',
diff --git a/target/i386/sev.c b/target/i386/sev.c
index 6acebfbd53..ba08b7d3ab 100644
--- a/target/i386/sev.c
+++ b/target/i386/sev.c
@@ -38,7 +38,8 @@
 OBJECT_DECLARE_SIMPLE_TYPE(SevCommonState, SEV_COMMON)
 #define TYPE_SEV_GUEST "sev-guest"
 OBJECT_DECLARE_SIMPLE_TYPE(SevGuestState, SEV_GUEST)
-
+#define TYPE_SEV_SNP_GUEST "sev-snp-guest"
+OBJECT_DECLARE_SIMPLE_TYPE(SevSnpGuestState, SEV_SNP_GUEST)
 
 /**
  * SevGuestState:
@@ -82,8 +83,23 @@ struct SevGuestState {
     char *session_file;
 };
 
+struct SevSnpGuestState {
+    SevCommonState sev_common;
+
+    /* configuration parameters */
+    char *guest_visible_workarounds;
+    char *id_block;
+    char *id_auth;
+    char *host_data;
+
+    struct kvm_snp_init kvm_init_conf;
+    struct kvm_sev_snp_launch_start kvm_start_conf;
+    struct kvm_sev_snp_launch_finish kvm_finish_conf;
+};
+
 #define DEFAULT_GUEST_POLICY    0x1 /* disable debug */
 #define DEFAULT_SEV_DEVICE      "/dev/sev"
+#define DEFAULT_SEV_SNP_POLICY  0x30000
 
 #define SEV_INFO_BLOCK_GUID     "00f771de-1a7e-4fcb-890e-68c77e2fb44e"
 typedef struct __attribute__((__packed__)) SevInfoBlock {
@@ -364,6 +380,232 @@ static const TypeInfo sev_guest_info = {
     .class_init = sev_guest_class_init,
 };
 
+static void
+sev_snp_guest_get_init_flags(Object *obj, Visitor *v, const char *name,
+                             void *opaque, Error **errp)
+{
+    visit_type_uint64(v, name,
+                      (uint64_t *)&SEV_SNP_GUEST(obj)->kvm_init_conf.flags,
+                      errp);
+}
+
+static void
+sev_snp_guest_set_init_flags(Object *obj, Visitor *v, const char *name,
+                             void *opaque, Error **errp)
+{
+    visit_type_uint64(v, name,
+                      (uint64_t *)&SEV_SNP_GUEST(obj)->kvm_init_conf.flags,
+                      errp);
+}
+
+static void
+sev_snp_guest_get_policy(Object *obj, Visitor *v, const char *name,
+                         void *opaque, Error **errp)
+{
+    visit_type_uint64(v, name,
+                      (uint64_t *)&SEV_SNP_GUEST(obj)->kvm_start_conf.policy,
+                      errp);
+}
+
+static void
+sev_snp_guest_set_policy(Object *obj, Visitor *v, const char *name,
+                         void *opaque, Error **errp)
+{
+    visit_type_uint64(v, name,
+                      (uint64_t *)&SEV_SNP_GUEST(obj)->kvm_start_conf.policy,
+                      errp);
+}
+
+static char *
+sev_snp_guest_get_guest_visible_workarounds(Object *obj, Error **errp)
+{
+    return g_strdup(SEV_SNP_GUEST(obj)->guest_visible_workarounds);
+}
+
+static void
+sev_snp_guest_set_guest_visible_workarounds(Object *obj, const char *value,
+                                            Error **errp)
+{
+    SevSnpGuestState *sev_snp_guest = SEV_SNP_GUEST(obj);
+    struct kvm_sev_snp_launch_start *start = &sev_snp_guest->kvm_start_conf;
+    g_autofree guchar *blob;
+    gsize len;
+
+    if (sev_snp_guest->guest_visible_workarounds) {
+        g_free(sev_snp_guest->guest_visible_workarounds);
+    }
+
+    /* store the base64 str so we don't need to re-encode in getter */
+    sev_snp_guest->guest_visible_workarounds = g_strdup(value);
+
+    blob = g_base64_decode(sev_snp_guest->guest_visible_workarounds, &len);
+    if (len > sizeof(start->gosvw)) {
+        error_setg(errp, "parameter length of %lu exceeds max of %lu",
+                   len, sizeof(start->gosvw));
+        return;
+    }
+
+    memcpy(start->gosvw, blob, len);
+}
+
+static char *
+sev_snp_guest_get_id_block(Object *obj, Error **errp)
+{
+    SevSnpGuestState *sev_snp_guest = SEV_SNP_GUEST(obj);
+
+    return g_strdup(sev_snp_guest->id_block);
+}
+
+static void
+sev_snp_guest_set_id_block(Object *obj, const char *value, Error **errp)
+{
+    SevSnpGuestState *sev_snp_guest = SEV_SNP_GUEST(obj);
+    struct kvm_sev_snp_launch_finish *finish = &sev_snp_guest->kvm_finish_conf;
+    gsize len;
+
+    if (sev_snp_guest->id_block) {
+        g_free(sev_snp_guest->id_block);
+        g_free((guchar *)finish->id_block_uaddr);
+    }
+
+    /* store the base64 str so we don't need to re-encode in getter */
+    sev_snp_guest->id_block = g_strdup(value);
+
+    finish->id_block_uaddr = (uint64_t)g_base64_decode(sev_snp_guest->id_block, &len);
+    if (len > KVM_SEV_SNP_ID_BLOCK_SIZE) {
+        error_setg(errp, "parameter length of %lu exceeds max of %u",
+                   len, KVM_SEV_SNP_ID_BLOCK_SIZE);
+        return;
+    }
+    finish->id_block_en = 1;
+}
+
+static char *
+sev_snp_guest_get_id_auth(Object *obj, Error **errp)
+{
+    SevSnpGuestState *sev_snp_guest = SEV_SNP_GUEST(obj);
+
+    return g_strdup(sev_snp_guest->id_auth);
+}
+
+static void
+sev_snp_guest_set_id_auth(Object *obj, const char *value, Error **errp)
+{
+    SevSnpGuestState *sev_snp_guest = SEV_SNP_GUEST(obj);
+    struct kvm_sev_snp_launch_finish *finish = &sev_snp_guest->kvm_finish_conf;
+    gsize len;
+
+    if (sev_snp_guest->id_auth) {
+        g_free(sev_snp_guest->id_auth);
+        g_free((guchar *)finish->id_auth_uaddr);
+    }
+
+    /* store the base64 str so we don't need to re-encode in getter */
+    sev_snp_guest->id_auth = g_strdup(value);
+
+    finish->id_auth_uaddr = (uint64_t)g_base64_decode(sev_snp_guest->id_auth, &len);
+    if (len > KVM_SEV_SNP_ID_AUTH_SIZE) {
+        error_setg(errp, "parameter length of %lu exceeds max of %u",
+                   len, KVM_SEV_SNP_ID_AUTH_SIZE);
+        return;
+    }
+}
+
+static bool
+sev_snp_guest_get_auth_key_en(Object *obj, Error **errp)
+{
+    SevSnpGuestState *sev_snp_guest = SEV_SNP_GUEST(obj);
+
+    return !!sev_snp_guest->kvm_finish_conf.auth_key_en;
+}
+
+static void
+sev_snp_guest_set_auth_key_en(Object *obj, bool value, Error **errp)
+{
+    SevSnpGuestState *sev_snp_guest = SEV_SNP_GUEST(obj);
+
+    sev_snp_guest->kvm_finish_conf.auth_key_en = value;
+}
+
+static char *
+sev_snp_guest_get_host_data(Object *obj, Error **errp)
+{
+    SevSnpGuestState *sev_snp_guest = SEV_SNP_GUEST(obj);
+
+    return g_strdup(sev_snp_guest->host_data);
+}
+
+static void
+sev_snp_guest_set_host_data(Object *obj, const char *value, Error **errp)
+{
+    SevSnpGuestState *sev_snp_guest = SEV_SNP_GUEST(obj);
+    struct kvm_sev_snp_launch_finish *finish = &sev_snp_guest->kvm_finish_conf;
+    g_autofree guchar *blob;
+    gsize len;
+
+    if (sev_snp_guest->host_data) {
+        g_free(sev_snp_guest->host_data);
+    }
+
+    /* store the base64 str so we don't need to re-encode in getter */
+    sev_snp_guest->host_data = g_strdup(value);
+
+    blob = g_base64_decode(sev_snp_guest->host_data, &len);
+    if (len > sizeof(finish->host_data)) {
+        error_setg(errp, "parameter length of %lu exceeds max of %lu",
+                   len, sizeof(finish->host_data));
+        return;
+    }
+
+    memcpy(finish->host_data, blob, len);
+}
+
+static void
+sev_snp_guest_class_init(ObjectClass *oc, void *data)
+{
+    object_class_property_add(oc, "init-flags", "uint64",
+                              sev_snp_guest_get_init_flags,
+                              sev_snp_guest_set_init_flags, NULL, NULL);
+    object_class_property_set_description(oc, "init-flags",
+        "guest initialization flags");
+    object_class_property_add(oc, "policy", "uint64",
+                              sev_snp_guest_get_policy,
+                              sev_snp_guest_set_policy, NULL, NULL);
+    object_class_property_add_str(oc, "guest-visible-workarounds",
+                                  sev_snp_guest_get_guest_visible_workarounds,
+                                  sev_snp_guest_set_guest_visible_workarounds);
+    object_class_property_add_str(oc, "id-block",
+                                  sev_snp_guest_get_id_block,
+                                  sev_snp_guest_set_id_block);
+    object_class_property_add_str(oc, "id-auth",
+                                  sev_snp_guest_get_id_auth,
+                                  sev_snp_guest_set_id_auth);
+    object_class_property_add_bool(oc, "auth-key-enabled",
+                                   sev_snp_guest_get_auth_key_en,
+                                   sev_snp_guest_set_auth_key_en);
+    object_class_property_add_str(oc, "host-data",
+                                  sev_snp_guest_get_host_data,
+                                  sev_snp_guest_set_host_data);
+}
+
+static void
+sev_snp_guest_instance_init(Object *obj)
+{
+    SevSnpGuestState *sev_snp_guest = SEV_SNP_GUEST(obj);
+
+    /* default init/start/finish params for kvm */
+    sev_snp_guest->kvm_start_conf.policy = DEFAULT_SEV_SNP_POLICY;
+}
+
+/* guest info specific to sev-snp */
+static const TypeInfo sev_snp_guest_info = {
+    .parent = TYPE_SEV_COMMON,
+    .name = TYPE_SEV_SNP_GUEST,
+    .instance_size = sizeof(SevSnpGuestState),
+    .class_init = sev_snp_guest_class_init,
+    .instance_init = sev_snp_guest_instance_init,
+};
+
 bool
 sev_enabled(void)
 {
@@ -1136,6 +1378,7 @@ sev_register_types(void)
 {
     type_register_static(&sev_common_info);
     type_register_static(&sev_guest_info);
+    type_register_static(&sev_snp_guest_info);
 }
 
 type_init(sev_register_types);
-- 
2.25.1


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

* [RFC PATCH v2 04/12] i386/sev: initialize SNP context
  2021-08-26 22:26 [RFC PATCH v2 00/12] Add AMD Secure Nested Paging (SEV-SNP) support Michael Roth
                   ` (2 preceding siblings ...)
  2021-08-26 22:26 ` [RFC PATCH v2 03/12] i386/sev: introduce 'sev-snp-guest' object Michael Roth
@ 2021-08-26 22:26 ` Michael Roth
  2021-09-05  7:07     ` Dov Murik
  2021-09-05  9:19     ` Dov Murik
  2021-08-26 22:26 ` [RFC PATCH v2 05/12] i386/sev: add the SNP launch start context Michael Roth
                   ` (8 subsequent siblings)
  12 siblings, 2 replies; 61+ messages in thread
From: Michael Roth @ 2021-08-26 22:26 UTC (permalink / raw)
  To: qemu-devel
  Cc: Connor Kuehl, Philippe Mathieu-Daudé,
	Michael S . Tsirkin, James Bottomley, Dr . David Alan Gilbert,
	Tom Lendacky, Paolo Bonzini, Dov Murik, David Gibson,
	Daniel P . Berrangé,
	kvm, Eduardo Habkost, Brijesh Singh, Markus Armbruster,
	Eric Blake

From: Brijesh Singh <brijesh.singh@amd.com>

When SEV-SNP is enabled, the KVM_SNP_INIT command is used to initialize
the platform. The command checks whether SNP is enabled in the KVM, if
enabled then it allocates a new ASID from the SNP pool and calls the
firmware to initialize the all the resources.

Signed-off-by: Brijesh Singh <brijesh.singh@amd.com>
Signed-off-by: Michael Roth <michael.roth@amd.com>
---
 target/i386/sev-stub.c |  6 ++++++
 target/i386/sev.c      | 27 ++++++++++++++++++++++++---
 target/i386/sev_i386.h |  1 +
 3 files changed, 31 insertions(+), 3 deletions(-)

diff --git a/target/i386/sev-stub.c b/target/i386/sev-stub.c
index 0227cb5177..e4fb8e882e 100644
--- a/target/i386/sev-stub.c
+++ b/target/i386/sev-stub.c
@@ -81,3 +81,9 @@ sev_get_attestation_report(const char *mnonce, Error **errp)
     error_setg(errp, "SEV is not available in this QEMU");
     return NULL;
 }
+
+bool
+sev_snp_enabled(void)
+{
+    return false;
+}
diff --git a/target/i386/sev.c b/target/i386/sev.c
index ba08b7d3ab..b8bd6ed9ea 100644
--- a/target/i386/sev.c
+++ b/target/i386/sev.c
@@ -614,12 +614,21 @@ sev_enabled(void)
     return !!object_dynamic_cast(OBJECT(cgs), TYPE_SEV_COMMON);
 }
 
+bool
+sev_snp_enabled(void)
+{
+    ConfidentialGuestSupport *cgs = MACHINE(qdev_get_machine())->cgs;
+
+    return !!object_dynamic_cast(OBJECT(cgs), TYPE_SEV_SNP_GUEST);
+}
+
 bool
 sev_es_enabled(void)
 {
     ConfidentialGuestSupport *cgs = MACHINE(qdev_get_machine())->cgs;
 
-    return sev_enabled() && (SEV_GUEST(cgs)->policy & SEV_POLICY_ES);
+    return sev_snp_enabled() ||
+            (sev_enabled() && SEV_GUEST(cgs)->policy & SEV_POLICY_ES);
 }
 
 uint64_t
@@ -1074,6 +1083,7 @@ int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp)
     uint32_t ebx;
     uint32_t host_cbitpos;
     struct sev_user_data_status status = {};
+    void *init_args = NULL;
 
     if (!sev_common) {
         return 0;
@@ -1126,7 +1136,18 @@ int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp)
     sev_common->api_major = status.api_major;
     sev_common->api_minor = status.api_minor;
 
-    if (sev_es_enabled()) {
+    if (sev_snp_enabled()) {
+        SevSnpGuestState *sev_snp_guest = SEV_SNP_GUEST(sev_common);
+        if (!kvm_kernel_irqchip_allowed()) {
+            error_report("%s: SEV-SNP guests require in-kernel irqchip support",
+                         __func__);
+            goto err;
+        }
+
+        cmd = KVM_SEV_SNP_INIT;
+        init_args = (void *)&sev_snp_guest->kvm_init_conf;
+
+    } else if (sev_es_enabled()) {
         if (!kvm_kernel_irqchip_allowed()) {
             error_report("%s: SEV-ES guests require in-kernel irqchip support",
                          __func__);
@@ -1145,7 +1166,7 @@ int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp)
     }
 
     trace_kvm_sev_init();
-    ret = sev_ioctl(sev_common->sev_fd, cmd, NULL, &fw_error);
+    ret = sev_ioctl(sev_common->sev_fd, cmd, init_args, &fw_error);
     if (ret) {
         error_setg(errp, "%s: failed to initialize ret=%d fw_error=%d '%s'",
                    __func__, ret, fw_error, fw_error_to_str(fw_error));
diff --git a/target/i386/sev_i386.h b/target/i386/sev_i386.h
index ae6d840478..e0e1a599be 100644
--- a/target/i386/sev_i386.h
+++ b/target/i386/sev_i386.h
@@ -29,6 +29,7 @@
 #define SEV_POLICY_SEV          0x20
 
 extern bool sev_es_enabled(void);
+extern bool sev_snp_enabled(void);
 extern uint64_t sev_get_me_mask(void);
 extern SevInfo *sev_get_info(void);
 extern uint32_t sev_get_cbit_position(void);
-- 
2.25.1


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

* [RFC PATCH v2 05/12] i386/sev: add the SNP launch start context
  2021-08-26 22:26 [RFC PATCH v2 00/12] Add AMD Secure Nested Paging (SEV-SNP) support Michael Roth
                   ` (3 preceding siblings ...)
  2021-08-26 22:26 ` [RFC PATCH v2 04/12] i386/sev: initialize SNP context Michael Roth
@ 2021-08-26 22:26 ` Michael Roth
  2021-08-26 22:26 ` [RFC PATCH v2 06/12] i386/sev: add support to encrypt BIOS when SEV-SNP is enabled Michael Roth
                   ` (7 subsequent siblings)
  12 siblings, 0 replies; 61+ messages in thread
From: Michael Roth @ 2021-08-26 22:26 UTC (permalink / raw)
  To: qemu-devel
  Cc: Connor Kuehl, Philippe Mathieu-Daudé,
	Michael S . Tsirkin, James Bottomley, Dr . David Alan Gilbert,
	Tom Lendacky, Paolo Bonzini, Dov Murik, David Gibson,
	Daniel P . Berrangé,
	kvm, Eduardo Habkost, Brijesh Singh, Markus Armbruster,
	Eric Blake

From: Brijesh Singh <brijesh.singh@amd.com>

The SNP_LAUNCH_START is called first to create a cryptographic launch
context within the firmware.

Signed-off-by: Brijesh Singh <brijesh.singh@amd.com>
Signed-off-by: Michael Roth <michael.roth@amd.com>
---
 target/i386/sev.c        | 29 ++++++++++++++++++++++++++++-
 target/i386/trace-events |  1 +
 2 files changed, 29 insertions(+), 1 deletion(-)

diff --git a/target/i386/sev.c b/target/i386/sev.c
index b8bd6ed9ea..51689d4fa4 100644
--- a/target/i386/sev.c
+++ b/target/i386/sev.c
@@ -875,6 +875,28 @@ sev_read_file_base64(const char *filename, guchar **data, gsize *len)
     return 0;
 }
 
+static int
+sev_snp_launch_start(SevSnpGuestState *sev_snp_guest)
+{
+    int fw_error, rc;
+    SevCommonState *sev_common = SEV_COMMON(sev_snp_guest);
+    struct kvm_sev_snp_launch_start *start = &sev_snp_guest->kvm_start_conf;
+
+    trace_kvm_sev_snp_launch_start(start->policy);
+
+    rc = sev_ioctl(sev_common->sev_fd, KVM_SEV_SNP_LAUNCH_START,
+                   start, &fw_error);
+    if (rc < 0) {
+        error_report("%s: SNP_LAUNCH_START ret=%d fw_error=%d '%s'",
+                __func__, rc, fw_error, fw_error_to_str(fw_error));
+        return 1;
+    }
+
+    sev_set_guest_state(sev_common, SEV_STATE_LAUNCH_UPDATE);
+
+    return 0;
+}
+
 static int
 sev_launch_start(SevGuestState *sev_guest)
 {
@@ -1173,7 +1195,12 @@ int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp)
         goto err;
     }
 
-    ret = sev_launch_start(SEV_GUEST(sev_common));
+    if (sev_snp_enabled()) {
+        ret = sev_snp_launch_start(SEV_SNP_GUEST(sev_common));
+    } else {
+        ret = sev_launch_start(SEV_GUEST(sev_common));
+    }
+
     if (ret) {
         error_setg(errp, "%s: failed to create encryption context", __func__);
         goto err;
diff --git a/target/i386/trace-events b/target/i386/trace-events
index 2cd8726eeb..18cc14b956 100644
--- a/target/i386/trace-events
+++ b/target/i386/trace-events
@@ -11,3 +11,4 @@ kvm_sev_launch_measurement(const char *value) "data %s"
 kvm_sev_launch_finish(void) ""
 kvm_sev_launch_secret(uint64_t hpa, uint64_t hva, uint64_t secret, int len) "hpa 0x%" PRIx64 " hva 0x%" PRIx64 " data 0x%" PRIx64 " len %d"
 kvm_sev_attestation_report(const char *mnonce, const char *data) "mnonce %s data %s"
+kvm_sev_snp_launch_start(uint64_t policy) "policy 0x%" PRIx64
-- 
2.25.1


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

* [RFC PATCH v2 06/12] i386/sev: add support to encrypt BIOS when SEV-SNP is enabled
  2021-08-26 22:26 [RFC PATCH v2 00/12] Add AMD Secure Nested Paging (SEV-SNP) support Michael Roth
                   ` (4 preceding siblings ...)
  2021-08-26 22:26 ` [RFC PATCH v2 05/12] i386/sev: add the SNP launch start context Michael Roth
@ 2021-08-26 22:26 ` Michael Roth
  2021-08-26 22:26 ` [RFC PATCH v2 07/12] i386/sev: populate secrets and cpuid page and finalize the SNP launch Michael Roth
                   ` (6 subsequent siblings)
  12 siblings, 0 replies; 61+ messages in thread
From: Michael Roth @ 2021-08-26 22:26 UTC (permalink / raw)
  To: qemu-devel
  Cc: Connor Kuehl, Philippe Mathieu-Daudé,
	Michael S . Tsirkin, James Bottomley, Dr . David Alan Gilbert,
	Tom Lendacky, Paolo Bonzini, Dov Murik, David Gibson,
	Daniel P . Berrangé,
	kvm, Eduardo Habkost, Brijesh Singh, Markus Armbruster,
	Eric Blake

From: Brijesh Singh <brijesh.singh@amd.com>

The KVM_SEV_SNP_LAUNCH_UPDATE command is used for encrypting the bios
image used for booting the SEV-SNP guest.

Signed-off-by: Brijesh Singh <brijesh.singh@amd.com>
Signed-off-by: Michael Roth <michael.roth@amd.com>
---
 hw/i386/pc_sysfw.c       |  7 ++++---
 include/sysemu/sev.h     |  2 +-
 target/i386/sev-stub.c   |  2 +-
 target/i386/sev.c        | 40 ++++++++++++++++++++++++++++++++++++++--
 target/i386/trace-events |  1 +
 5 files changed, 45 insertions(+), 7 deletions(-)

diff --git a/hw/i386/pc_sysfw.c b/hw/i386/pc_sysfw.c
index 68d6b1f783..54ccf13c0e 100644
--- a/hw/i386/pc_sysfw.c
+++ b/hw/i386/pc_sysfw.c
@@ -149,6 +149,7 @@ static void pc_system_flash_map(PCMachineState *pcms,
     void *flash_ptr;
     int flash_size;
     int ret;
+    hwaddr gpa;
 
     assert(PC_MACHINE_GET_CLASS(pcms)->pci_enabled);
 
@@ -182,11 +183,11 @@ static void pc_system_flash_map(PCMachineState *pcms,
         }
 
         total_size += size;
+        gpa = 0x100000000ULL - total_size; /* where the flash is mapped */
         qdev_prop_set_uint32(DEVICE(system_flash), "num-blocks",
                              size / FLASH_SECTOR_SIZE);
         sysbus_realize_and_unref(SYS_BUS_DEVICE(system_flash), &error_fatal);
-        sysbus_mmio_map(SYS_BUS_DEVICE(system_flash), 0,
-                        0x100000000ULL - total_size);
+        sysbus_mmio_map(SYS_BUS_DEVICE(system_flash), 0, gpa);
 
         if (i == 0) {
             flash_mem = pflash_cfi01_get_memory(system_flash);
@@ -208,7 +209,7 @@ static void pc_system_flash_map(PCMachineState *pcms,
                     exit(1);
                 }
 
-                sev_encrypt_flash(flash_ptr, flash_size, &error_fatal);
+                sev_encrypt_flash(gpa, flash_ptr, flash_size, &error_fatal);
             }
         }
     }
diff --git a/include/sysemu/sev.h b/include/sysemu/sev.h
index 94d821d737..78e3bf97e8 100644
--- a/include/sysemu/sev.h
+++ b/include/sysemu/sev.h
@@ -18,7 +18,7 @@
 
 bool sev_enabled(void);
 int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp);
-int sev_encrypt_flash(uint8_t *ptr, uint64_t len, Error **errp);
+int sev_encrypt_flash(hwaddr gpa, uint8_t *ptr, uint64_t len, Error **errp);
 int sev_inject_launch_secret(const char *hdr, const char *secret,
                              uint64_t gpa, Error **errp);
 
diff --git a/target/i386/sev-stub.c b/target/i386/sev-stub.c
index e4fb8e882e..8b35704937 100644
--- a/target/i386/sev-stub.c
+++ b/target/i386/sev-stub.c
@@ -56,7 +56,7 @@ int sev_inject_launch_secret(const char *hdr, const char *secret,
     return 1;
 }
 
-int sev_encrypt_flash(uint8_t *ptr, uint64_t len, Error **errp)
+int sev_encrypt_flash(hwaddr gpa, uint8_t *ptr, uint64_t len, Error **errp)
 {
     return 0;
 }
diff --git a/target/i386/sev.c b/target/i386/sev.c
index 51689d4fa4..867c0cb457 100644
--- a/target/i386/sev.c
+++ b/target/i386/sev.c
@@ -946,6 +946,35 @@ out:
     return ret;
 }
 
+static int
+sev_snp_launch_update(SevSnpGuestState *sev_snp_guest, hwaddr gpa, uint8_t *addr,
+                      uint64_t len, int type)
+{
+    int ret, fw_error;
+    struct kvm_sev_snp_launch_update update = {0};
+
+    if (!addr || !len) {
+        error_report("%s: SNP_LAUNCH_UPDATE called with invalid address / length: %lx / %lx",
+                __func__, gpa, len);
+        return 1;
+    }
+
+    update.uaddr = (__u64)(unsigned long)addr;
+    update.start_gfn = gpa >> TARGET_PAGE_BITS;
+    update.len = len;
+    update.page_type = type;
+    trace_kvm_sev_snp_launch_update(addr, len, type);
+    ret = sev_ioctl(SEV_COMMON(sev_snp_guest)->sev_fd,
+                    KVM_SEV_SNP_LAUNCH_UPDATE,
+                    &update, &fw_error);
+    if (ret) {
+        error_report("%s: SNP_LAUNCH_UPDATE ret=%d fw_error=%d '%s'",
+                __func__, ret, fw_error, fw_error_to_str(fw_error));
+    }
+
+    return ret;
+}
+
 static int
 sev_launch_update_data(SevGuestState *sev_guest, uint8_t *addr, uint64_t len)
 {
@@ -1219,7 +1248,7 @@ err:
 }
 
 int
-sev_encrypt_flash(uint8_t *ptr, uint64_t len, Error **errp)
+sev_encrypt_flash(hwaddr gpa, uint8_t *ptr, uint64_t len, Error **errp)
 {
     SevCommonState *sev_common = SEV_COMMON(MACHINE(qdev_get_machine())->cgs);
 
@@ -1229,7 +1258,14 @@ sev_encrypt_flash(uint8_t *ptr, uint64_t len, Error **errp)
 
     /* if SEV is in update state then encrypt the data else do nothing */
     if (sev_check_state(sev_common, SEV_STATE_LAUNCH_UPDATE)) {
-        int ret = sev_launch_update_data(SEV_GUEST(sev_common), ptr, len);
+        int ret;
+
+        if (sev_snp_enabled()) {
+            ret = sev_snp_launch_update(SEV_SNP_GUEST(sev_common), gpa, ptr,
+                                        len, KVM_SEV_SNP_PAGE_TYPE_NORMAL);
+        } else {
+            ret = sev_launch_update_data(SEV_GUEST(sev_common), ptr, len);
+        }
         if (ret < 0) {
             error_setg(errp, "failed to encrypt pflash rom");
             return ret;
diff --git a/target/i386/trace-events b/target/i386/trace-events
index 18cc14b956..0c2d250206 100644
--- a/target/i386/trace-events
+++ b/target/i386/trace-events
@@ -12,3 +12,4 @@ kvm_sev_launch_finish(void) ""
 kvm_sev_launch_secret(uint64_t hpa, uint64_t hva, uint64_t secret, int len) "hpa 0x%" PRIx64 " hva 0x%" PRIx64 " data 0x%" PRIx64 " len %d"
 kvm_sev_attestation_report(const char *mnonce, const char *data) "mnonce %s data %s"
 kvm_sev_snp_launch_start(uint64_t policy) "policy 0x%" PRIx64
+kvm_sev_snp_launch_update(void *addr, uint64_t len, int type) "addr %p len 0x%" PRIx64 " type %d"
-- 
2.25.1


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

* [RFC PATCH v2 07/12] i386/sev: populate secrets and cpuid page and finalize the SNP launch
  2021-08-26 22:26 [RFC PATCH v2 00/12] Add AMD Secure Nested Paging (SEV-SNP) support Michael Roth
                   ` (5 preceding siblings ...)
  2021-08-26 22:26 ` [RFC PATCH v2 06/12] i386/sev: add support to encrypt BIOS when SEV-SNP is enabled Michael Roth
@ 2021-08-26 22:26 ` Michael Roth
  2021-09-03 20:24     ` Dov Murik
  2021-08-26 22:26 ` [RFC PATCH v2 08/12] target/i386: set SEV-SNP CPUID bit when SNP enabled Michael Roth
                   ` (5 subsequent siblings)
  12 siblings, 1 reply; 61+ messages in thread
From: Michael Roth @ 2021-08-26 22:26 UTC (permalink / raw)
  To: qemu-devel
  Cc: Connor Kuehl, Philippe Mathieu-Daudé,
	Michael S . Tsirkin, James Bottomley, Dr . David Alan Gilbert,
	Tom Lendacky, Paolo Bonzini, Dov Murik, David Gibson,
	Daniel P . Berrangé,
	kvm, Eduardo Habkost, Brijesh Singh, Markus Armbruster,
	Eric Blake

From: Brijesh Singh <brijesh.singh@amd.com>

During the SNP guest launch sequence, a special secrets and cpuid page
needs to be populated by the SEV-SNP firmware. The secrets page contains
the VM Platform Communication Key (VMPCKs) used by the guest to send and
receive secure messages to the PSP. And CPUID page will contain the CPUID
value filtered through the PSP.

The guest BIOS (OVMF) reserves these pages in MEMFD and location of it
is available through the SNP boot block GUID. While finalizing the guest
boot flow, lookup for the boot block and call the SNP_LAUNCH_UPDATE
command to populate secrets and cpuid pages.

In order to support early boot code, the OVMF may ask hypervisor to
request the pre-validation of certain memory range. If such range is
present the call SNP_LAUNCH_UPDATE command to validate those address
range without affecting the measurement. See the SEV-SNP specification
for further details.

Finally, call the SNP_LAUNCH_FINISH to finalize the guest boot.

Signed-off-by: Brijesh Singh <brijesh.singh@amd.com>
Signed-off-by: Michael Roth <michael.roth@amd.com>
---
 target/i386/sev.c        | 189 ++++++++++++++++++++++++++++++++++++++-
 target/i386/trace-events |   2 +
 2 files changed, 189 insertions(+), 2 deletions(-)

diff --git a/target/i386/sev.c b/target/i386/sev.c
index 867c0cb457..0009c93d28 100644
--- a/target/i386/sev.c
+++ b/target/i386/sev.c
@@ -33,6 +33,7 @@
 #include "monitor/monitor.h"
 #include "exec/confidential-guest-support.h"
 #include "hw/i386/pc.h"
+#include "qemu/range.h"
 
 #define TYPE_SEV_COMMON "sev-common"
 OBJECT_DECLARE_SIMPLE_TYPE(SevCommonState, SEV_COMMON)
@@ -107,6 +108,19 @@ typedef struct __attribute__((__packed__)) SevInfoBlock {
     uint32_t reset_addr;
 } SevInfoBlock;
 
+#define SEV_SNP_BOOT_BLOCK_GUID "bd39c0c2-2f8e-4243-83e8-1b74cebcb7d9"
+typedef struct __attribute__((__packed__)) SevSnpBootInfoBlock {
+    /* Prevalidate range address */
+    uint32_t pre_validated_start;
+    uint32_t pre_validated_end;
+    /* Secrets page address */
+    uint32_t secrets_addr;
+    uint32_t secrets_len;
+    /* CPUID page address */
+    uint32_t cpuid_addr;
+    uint32_t cpuid_len;
+} SevSnpBootInfoBlock;
+
 static Error *sev_mig_blocker;
 
 static const char *const sev_fw_errlist[] = {
@@ -1086,6 +1100,162 @@ static Notifier sev_machine_done_notify = {
     .notify = sev_launch_get_measure,
 };
 
+static int
+sev_snp_launch_update_gpa(uint32_t hwaddr, uint32_t size, uint8_t type)
+{
+    void *hva;
+    MemoryRegion *mr = NULL;
+    SevSnpGuestState *sev_snp_guest =
+        SEV_SNP_GUEST(MACHINE(qdev_get_machine())->cgs);
+
+    hva = gpa2hva(&mr, hwaddr, size, NULL);
+    if (!hva) {
+        error_report("SEV-SNP failed to get HVA for GPA 0x%x", hwaddr);
+        return 1;
+    }
+
+    return sev_snp_launch_update(sev_snp_guest, hwaddr, hva, size, type);
+}
+
+static bool
+detect_first_overlap(uint64_t start, uint64_t end, Range *range_list,
+                     size_t range_count, Range *overlap_range)
+{
+    int i;
+    bool overlap = false;
+    Range new;
+
+    assert(overlap_range);
+    range_make_empty(overlap_range);
+    range_init_nofail(&new, start, end - start + 1);
+
+    for (i = 0; i < range_count; i++) {
+        if (range_overlaps_range(&new, &range_list[i]) &&
+            (range_is_empty(overlap_range) ||
+             range_lob(&range_list[i]) < range_lob(overlap_range))) {
+            *overlap_range = range_list[i];
+            overlap = true;
+        }
+    }
+
+    return overlap;
+}
+
+static void snp_ovmf_boot_block_setup(void)
+{
+    SevSnpBootInfoBlock *info;
+    uint32_t start, end, sz;
+    int ret;
+    Range validated_ranges[2];
+
+    /*
+     * Extract the SNP boot block for the SEV-SNP guests by locating the
+     * SNP_BOOT GUID. The boot block contains the information such as location
+     * of secrets and CPUID page, additionaly it may contain the range of
+     * memory that need to be pre-validated for the boot.
+     */
+    if (!pc_system_ovmf_table_find(SEV_SNP_BOOT_BLOCK_GUID,
+        (uint8_t **)&info, NULL)) {
+        error_report("SEV-SNP: failed to find the SNP boot block");
+        exit(1);
+    }
+
+    trace_kvm_sev_snp_ovmf_boot_block_info(info->secrets_addr,
+                                           info->secrets_len, info->cpuid_addr,
+                                           info->cpuid_len,
+                                           info->pre_validated_start,
+                                           info->pre_validated_end);
+
+    /* Populate the secrets page */
+    ret = sev_snp_launch_update_gpa(info->secrets_addr, info->secrets_len,
+                                    KVM_SEV_SNP_PAGE_TYPE_SECRETS);
+    if (ret) {
+        error_report("SEV-SNP: failed to insert secret page GPA 0x%x",
+                     info->secrets_addr);
+        exit(1);
+    }
+
+    /* Populate the cpuid page */
+    ret = sev_snp_launch_update_gpa(info->cpuid_addr, info->cpuid_len,
+                                    KVM_SEV_SNP_PAGE_TYPE_CPUID);
+    if (ret) {
+        error_report("SEV-SNP: failed to insert cpuid page GPA 0x%x",
+                     info->cpuid_addr);
+        exit(1);
+    }
+
+    /*
+     * Pre-validate the range using the LAUNCH_UPDATE_DATA, if the
+     * pre-validation range contains the CPUID and Secret page GPA then skip
+     * it. This is because SEV-SNP firmware pre-validates those pages as part
+     * of adding secrets and cpuid LAUNCH_UPDATE type.
+     */
+    range_init_nofail(&validated_ranges[0], info->secrets_addr, info->secrets_len);
+    range_init_nofail(&validated_ranges[1], info->cpuid_addr, info->cpuid_len);
+    start = info->pre_validated_start;
+    end = info->pre_validated_end;
+
+    while (start < end) {
+        Range overlap_range;
+
+        /* Check if the requested range overlaps with Secrets and CPUID page */
+        if (detect_first_overlap(start, end, validated_ranges, 2,
+                                 &overlap_range)) {
+            if (start < range_lob(&overlap_range)) {
+                sz = range_lob(&overlap_range) - start;
+                if (sev_snp_launch_update_gpa(start, sz,
+                    KVM_SEV_SNP_PAGE_TYPE_UNMEASURED)) {
+                    error_report("SEV-SNP: failed to validate gpa 0x%x sz %d",
+                                 start, sz);
+                    exit(1);
+                }
+            }
+
+            start = range_upb(&overlap_range) + 1;
+            continue;
+        }
+
+        /* Validate the remaining range */
+        if (sev_snp_launch_update_gpa(start, end - start,
+            KVM_SEV_SNP_PAGE_TYPE_UNMEASURED)) {
+            error_report("SEV-SNP: failed to validate gpa 0x%x sz %d",
+                         start, end - start);
+            exit(1);
+        }
+
+        start = end;
+    }
+}
+
+static void
+sev_snp_launch_finish(SevSnpGuestState *sev_snp)
+{
+    int ret, error;
+    Error *local_err = NULL;
+    struct kvm_sev_snp_launch_finish *finish = &sev_snp->kvm_finish_conf;
+
+    trace_kvm_sev_snp_launch_finish();
+    ret = sev_ioctl(SEV_COMMON(sev_snp)->sev_fd, KVM_SEV_SNP_LAUNCH_FINISH, finish, &error);
+    if (ret) {
+        error_report("%s: SNP_LAUNCH_FINISH ret=%d fw_error=%d '%s'",
+                     __func__, ret, error, fw_error_to_str(error));
+        exit(1);
+    }
+
+    sev_set_guest_state(SEV_COMMON(sev_snp), SEV_STATE_RUNNING);
+
+    /* add migration blocker */
+    error_setg(&sev_mig_blocker,
+               "SEV: Migration is not implemented");
+    ret = migrate_add_blocker(sev_mig_blocker, &local_err);
+    if (local_err) {
+        error_report_err(local_err);
+        error_free(sev_mig_blocker);
+        exit(1);
+    }
+}
+
+
 static void
 sev_launch_finish(SevGuestState *sev_guest)
 {
@@ -1121,7 +1291,12 @@ sev_vm_state_change(void *opaque, bool running, RunState state)
 
     if (running) {
         if (!sev_check_state(sev_common, SEV_STATE_RUNNING)) {
-            sev_launch_finish(SEV_GUEST(sev_common));
+            if (sev_snp_enabled()) {
+                snp_ovmf_boot_block_setup();
+                sev_snp_launch_finish(SEV_SNP_GUEST(sev_common));
+            } else {
+                sev_launch_finish(SEV_GUEST(sev_common));
+            }
         }
     }
 }
@@ -1236,7 +1411,17 @@ int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp)
     }
 
     ram_block_notifier_add(&sev_ram_notifier);
-    qemu_add_machine_init_done_notifier(&sev_machine_done_notify);
+
+    /*
+     * The machine done notify event is used by the SEV guest to get the
+     * measurement of the encrypted images. When SEV-SNP is enabled then
+     * measurement is part of the attestation report and the measurement
+     * command does not exist. So skip registering the notifier.
+     */
+    if (!sev_snp_enabled()) {
+        qemu_add_machine_init_done_notifier(&sev_machine_done_notify);
+    }
+
     qemu_add_vm_change_state_handler(sev_vm_state_change, sev_common);
 
     cgs->ready = true;
diff --git a/target/i386/trace-events b/target/i386/trace-events
index 0c2d250206..db91287439 100644
--- a/target/i386/trace-events
+++ b/target/i386/trace-events
@@ -13,3 +13,5 @@ kvm_sev_launch_secret(uint64_t hpa, uint64_t hva, uint64_t secret, int len) "hpa
 kvm_sev_attestation_report(const char *mnonce, const char *data) "mnonce %s data %s"
 kvm_sev_snp_launch_start(uint64_t policy) "policy 0x%" PRIx64
 kvm_sev_snp_launch_update(void *addr, uint64_t len, int type) "addr %p len 0x%" PRIx64 " type %d"
+kvm_sev_snp_launch_finish(void) ""
+kvm_sev_snp_ovmf_boot_block_info(uint32_t secrets_gpa, uint32_t slen, uint32_t cpuid_gpa, uint32_t clen, uint32_t s, uint32_t e) "secrets 0x%x+0x%x cpuid 0x%x+0x%x pre-validate 0x%x+0x%x"
-- 
2.25.1


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

* [RFC PATCH v2 08/12] target/i386: set SEV-SNP CPUID bit when SNP enabled
  2021-08-26 22:26 [RFC PATCH v2 00/12] Add AMD Secure Nested Paging (SEV-SNP) support Michael Roth
                   ` (6 preceding siblings ...)
  2021-08-26 22:26 ` [RFC PATCH v2 07/12] i386/sev: populate secrets and cpuid page and finalize the SNP launch Michael Roth
@ 2021-08-26 22:26 ` Michael Roth
  2021-08-26 22:26 ` [RFC PATCH v2 09/12] target/i386: allow versioned CPUs to specify new cache_info Michael Roth
                   ` (4 subsequent siblings)
  12 siblings, 0 replies; 61+ messages in thread
From: Michael Roth @ 2021-08-26 22:26 UTC (permalink / raw)
  To: qemu-devel
  Cc: Connor Kuehl, Philippe Mathieu-Daudé,
	Michael S . Tsirkin, James Bottomley, Dr . David Alan Gilbert,
	Tom Lendacky, Paolo Bonzini, Dov Murik, David Gibson,
	Daniel P . Berrangé,
	kvm, Eduardo Habkost, Brijesh Singh, Markus Armbruster,
	Eric Blake

SNP guests will rely on this bit to determine certain feature support.

Signed-off-by: Michael Roth <michael.roth@amd.com>
---
 target/i386/cpu.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/target/i386/cpu.c b/target/i386/cpu.c
index 97e250e876..f0b441f692 100644
--- a/target/i386/cpu.c
+++ b/target/i386/cpu.c
@@ -5619,6 +5619,7 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count,
     case 0x8000001F:
         *eax = sev_enabled() ? 0x2 : 0;
         *eax |= sev_es_enabled() ? 0x8 : 0;
+        *eax |= sev_snp_enabled() ? 0x10 : 0;
         *ebx = sev_get_cbit_position();
         *ebx |= sev_get_reduced_phys_bits() << 6;
         *ecx = 0;
-- 
2.25.1


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

* [RFC PATCH v2 09/12] target/i386: allow versioned CPUs to specify new cache_info
  2021-08-26 22:26 [RFC PATCH v2 00/12] Add AMD Secure Nested Paging (SEV-SNP) support Michael Roth
                   ` (7 preceding siblings ...)
  2021-08-26 22:26 ` [RFC PATCH v2 08/12] target/i386: set SEV-SNP CPUID bit when SNP enabled Michael Roth
@ 2021-08-26 22:26 ` Michael Roth
  2021-08-26 22:26 ` [RFC PATCH v2 10/12] target/i386: add new EPYC CPU versions with updated cache_info Michael Roth
                   ` (3 subsequent siblings)
  12 siblings, 0 replies; 61+ messages in thread
From: Michael Roth @ 2021-08-26 22:26 UTC (permalink / raw)
  To: qemu-devel
  Cc: Connor Kuehl, Philippe Mathieu-Daudé,
	Michael S . Tsirkin, James Bottomley, Dr . David Alan Gilbert,
	Tom Lendacky, Paolo Bonzini, Dov Murik, David Gibson,
	Daniel P . Berrangé,
	kvm, Eduardo Habkost, Brijesh Singh, Markus Armbruster,
	Eric Blake

New EPYC CPUs versions require small changes to their cache_info's.
Because current QEMU x86 CPU definition does not support cache
versions, we would have to declare a new CPU type for each such case.
To avoid this duplication, the patch allows new cache_info pointers to
be specificed for a new CPU version.

Co-developed-by: Wei Huang <wei.huang2@amd.com>
Signed-off-by: Wei Huang <wei.huang2@amd.com>
Signed-off-by: Michael Roth <michael.roth@amd.com>
---
 target/i386/cpu.c | 36 +++++++++++++++++++++++++++++++++---
 1 file changed, 33 insertions(+), 3 deletions(-)

diff --git a/target/i386/cpu.c b/target/i386/cpu.c
index f0b441f692..85d387163a 100644
--- a/target/i386/cpu.c
+++ b/target/i386/cpu.c
@@ -1458,6 +1458,7 @@ typedef struct X86CPUVersionDefinition {
     const char *alias;
     const char *note;
     PropValue *props;
+    const CPUCaches *const cache_info;
 } X86CPUVersionDefinition;
 
 /* Base definition for a CPU model */
@@ -4975,6 +4976,32 @@ static void x86_cpu_apply_version_props(X86CPU *cpu, X86CPUModel *model)
     assert(vdef->version == version);
 }
 
+/* Apply properties for the CPU model version specified in model */
+static const CPUCaches *x86_cpu_get_version_cache_info(X86CPU *cpu,
+                                                       X86CPUModel *model)
+{
+    const X86CPUVersionDefinition *vdef;
+    X86CPUVersion version = x86_cpu_model_resolve_version(model);
+    const CPUCaches *cache_info = model->cpudef->cache_info;
+
+    if (version == CPU_VERSION_LEGACY) {
+        return cache_info;
+    }
+
+    for (vdef = x86_cpu_def_get_versions(model->cpudef); vdef->version; vdef++) {
+        if (vdef->cache_info) {
+            cache_info = vdef->cache_info;
+        }
+
+        if (vdef->version == version) {
+            break;
+        }
+    }
+
+    assert(vdef->version == version);
+    return cache_info;
+}
+
 /*
  * Load data from X86CPUDefinition into a X86CPU object.
  * Only for builtin_x86_defs models initialized with x86_register_cpudef_types.
@@ -5007,7 +5034,7 @@ static void x86_cpu_load_model(X86CPU *cpu, X86CPUModel *model)
     }
 
     /* legacy-cache defaults to 'off' if CPU model provides cache info */
-    cpu->legacy_cache = !def->cache_info;
+    cpu->legacy_cache = !x86_cpu_get_version_cache_info(cpu, model);
 
     env->features[FEAT_1_ECX] |= CPUID_EXT_HYPERVISOR;
 
@@ -6234,14 +6261,17 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp)
 
     /* Cache information initialization */
     if (!cpu->legacy_cache) {
-        if (!xcc->model || !xcc->model->cpudef->cache_info) {
+        const CPUCaches *cache_info =
+            x86_cpu_get_version_cache_info(cpu, xcc->model);
+
+        if (!xcc->model || !cache_info) {
             g_autofree char *name = x86_cpu_class_get_model_name(xcc);
             error_setg(errp,
                        "CPU model '%s' doesn't support legacy-cache=off", name);
             return;
         }
         env->cache_info_cpuid2 = env->cache_info_cpuid4 = env->cache_info_amd =
-            *xcc->model->cpudef->cache_info;
+            *cache_info;
     } else {
         /* Build legacy cache information */
         env->cache_info_cpuid2.l1d_cache = &legacy_l1d_cache;
-- 
2.25.1


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

* [RFC PATCH v2 10/12] target/i386: add new EPYC CPU versions with updated cache_info
  2021-08-26 22:26 [RFC PATCH v2 00/12] Add AMD Secure Nested Paging (SEV-SNP) support Michael Roth
                   ` (8 preceding siblings ...)
  2021-08-26 22:26 ` [RFC PATCH v2 09/12] target/i386: allow versioned CPUs to specify new cache_info Michael Roth
@ 2021-08-26 22:26 ` Michael Roth
  2021-08-26 22:26 ` [RFC PATCH v2 11/12] i386/sev: sev-snp: add support for CPUID validation Michael Roth
                   ` (2 subsequent siblings)
  12 siblings, 0 replies; 61+ messages in thread
From: Michael Roth @ 2021-08-26 22:26 UTC (permalink / raw)
  To: qemu-devel
  Cc: Connor Kuehl, Philippe Mathieu-Daudé,
	Michael S . Tsirkin, James Bottomley, Dr . David Alan Gilbert,
	Tom Lendacky, Paolo Bonzini, Dov Murik, David Gibson,
	Daniel P . Berrangé,
	kvm, Eduardo Habkost, Brijesh Singh, Markus Armbruster,
	Eric Blake

This patch introduces new EPYC cpu versions: EPYC-v4, EPYC-Rome-v3,
and EPYC-Milan-v2. The only difference vs. older models is an updated
cache_info with the 'complex_indexing' bit unset, since this bit is
not currently defined for AMD and may cause problems should it be used
for something else in the future. Setting this bit will also cause
CPUID validation failures when running SEV-SNP guests.

Signed-off-by: Michael Roth <michael.roth@amd.com>
---
 target/i386/cpu.c | 184 ++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 184 insertions(+)

diff --git a/target/i386/cpu.c b/target/i386/cpu.c
index 85d387163a..45e456b557 100644
--- a/target/i386/cpu.c
+++ b/target/i386/cpu.c
@@ -1567,6 +1567,56 @@ static const CPUCaches epyc_cache_info = {
     },
 };
 
+static CPUCaches epyc_v4_cache_info = {
+    .l1d_cache = &(CPUCacheInfo) {
+        .type = DATA_CACHE,
+        .level = 1,
+        .size = 32 * KiB,
+        .line_size = 64,
+        .associativity = 8,
+        .partitions = 1,
+        .sets = 64,
+        .lines_per_tag = 1,
+        .self_init = 1,
+        .no_invd_sharing = true,
+    },
+    .l1i_cache = &(CPUCacheInfo) {
+        .type = INSTRUCTION_CACHE,
+        .level = 1,
+        .size = 64 * KiB,
+        .line_size = 64,
+        .associativity = 4,
+        .partitions = 1,
+        .sets = 256,
+        .lines_per_tag = 1,
+        .self_init = 1,
+        .no_invd_sharing = true,
+    },
+    .l2_cache = &(CPUCacheInfo) {
+        .type = UNIFIED_CACHE,
+        .level = 2,
+        .size = 512 * KiB,
+        .line_size = 64,
+        .associativity = 8,
+        .partitions = 1,
+        .sets = 1024,
+        .lines_per_tag = 1,
+    },
+    .l3_cache = &(CPUCacheInfo) {
+        .type = UNIFIED_CACHE,
+        .level = 3,
+        .size = 8 * MiB,
+        .line_size = 64,
+        .associativity = 16,
+        .partitions = 1,
+        .sets = 8192,
+        .lines_per_tag = 1,
+        .self_init = true,
+        .inclusive = true,
+        .complex_indexing = false,
+    },
+};
+
 static const CPUCaches epyc_rome_cache_info = {
     .l1d_cache = &(CPUCacheInfo) {
         .type = DATA_CACHE,
@@ -1617,6 +1667,56 @@ static const CPUCaches epyc_rome_cache_info = {
     },
 };
 
+static const CPUCaches epyc_rome_v3_cache_info = {
+    .l1d_cache = &(CPUCacheInfo) {
+        .type = DATA_CACHE,
+        .level = 1,
+        .size = 32 * KiB,
+        .line_size = 64,
+        .associativity = 8,
+        .partitions = 1,
+        .sets = 64,
+        .lines_per_tag = 1,
+        .self_init = 1,
+        .no_invd_sharing = true,
+    },
+    .l1i_cache = &(CPUCacheInfo) {
+        .type = INSTRUCTION_CACHE,
+        .level = 1,
+        .size = 32 * KiB,
+        .line_size = 64,
+        .associativity = 8,
+        .partitions = 1,
+        .sets = 64,
+        .lines_per_tag = 1,
+        .self_init = 1,
+        .no_invd_sharing = true,
+    },
+    .l2_cache = &(CPUCacheInfo) {
+        .type = UNIFIED_CACHE,
+        .level = 2,
+        .size = 512 * KiB,
+        .line_size = 64,
+        .associativity = 8,
+        .partitions = 1,
+        .sets = 1024,
+        .lines_per_tag = 1,
+    },
+    .l3_cache = &(CPUCacheInfo) {
+        .type = UNIFIED_CACHE,
+        .level = 3,
+        .size = 16 * MiB,
+        .line_size = 64,
+        .associativity = 16,
+        .partitions = 1,
+        .sets = 16384,
+        .lines_per_tag = 1,
+        .self_init = true,
+        .inclusive = true,
+        .complex_indexing = false,
+    },
+};
+
 static const CPUCaches epyc_milan_cache_info = {
     .l1d_cache = &(CPUCacheInfo) {
         .type = DATA_CACHE,
@@ -1667,6 +1767,56 @@ static const CPUCaches epyc_milan_cache_info = {
     },
 };
 
+static const CPUCaches epyc_milan_v2_cache_info = {
+    .l1d_cache = &(CPUCacheInfo) {
+        .type = DATA_CACHE,
+        .level = 1,
+        .size = 32 * KiB,
+        .line_size = 64,
+        .associativity = 8,
+        .partitions = 1,
+        .sets = 64,
+        .lines_per_tag = 1,
+        .self_init = 1,
+        .no_invd_sharing = true,
+    },
+    .l1i_cache = &(CPUCacheInfo) {
+        .type = INSTRUCTION_CACHE,
+        .level = 1,
+        .size = 32 * KiB,
+        .line_size = 64,
+        .associativity = 8,
+        .partitions = 1,
+        .sets = 64,
+        .lines_per_tag = 1,
+        .self_init = 1,
+        .no_invd_sharing = true,
+    },
+    .l2_cache = &(CPUCacheInfo) {
+        .type = UNIFIED_CACHE,
+        .level = 2,
+        .size = 512 * KiB,
+        .line_size = 64,
+        .associativity = 8,
+        .partitions = 1,
+        .sets = 1024,
+        .lines_per_tag = 1,
+    },
+    .l3_cache = &(CPUCacheInfo) {
+        .type = UNIFIED_CACHE,
+        .level = 3,
+        .size = 32 * MiB,
+        .line_size = 64,
+        .associativity = 16,
+        .partitions = 1,
+        .sets = 32768,
+        .lines_per_tag = 1,
+        .self_init = true,
+        .inclusive = true,
+        .complex_indexing = false,
+    },
+};
+
 /* The following VMX features are not supported by KVM and are left out in the
  * CPU definitions:
  *
@@ -3935,6 +4085,16 @@ static const X86CPUDefinition builtin_x86_defs[] = {
                     { /* end of list */ }
                 }
             },
+            {
+                .version = 4,
+                .note = "compatible with SEV-SNP CPUID enforcement",
+                .props = (PropValue[]) {
+                    { "model-id",
+                      "AMD EPYC-v4 Processor" },
+                    { /* end of list */ }
+                },
+                .cache_info = &epyc_v4_cache_info
+            },
             { /* end of list */ }
         }
     },
@@ -4054,6 +4214,16 @@ static const X86CPUDefinition builtin_x86_defs[] = {
                     { /* end of list */ }
                 }
             },
+            {
+                .version = 3,
+                .note = "compatible with SEV-SNP CPUID enforcement",
+                .props = (PropValue[]) {
+                    { "model-id",
+                      "AMD EPYC-Rome-v3 Processor" },
+                    { /* end of list */ }
+                },
+                .cache_info = &epyc_rome_v3_cache_info
+            },
             { /* end of list */ }
         }
     },
@@ -4111,6 +4281,20 @@ static const X86CPUDefinition builtin_x86_defs[] = {
         .xlevel = 0x8000001E,
         .model_id = "AMD EPYC-Milan Processor",
         .cache_info = &epyc_milan_cache_info,
+        .versions = (X86CPUVersionDefinition[]) {
+            {   .version = 1 },
+            {
+                .version = 2,
+                .note = "compatible with SEV-SNP CPUID enforcement",
+                .props = (PropValue[]) {
+                    { "model-id",
+                      "AMD EPYC-Milan-v2 Processor" },
+                    { /* end of list */ }
+                },
+                .cache_info = &epyc_milan_v2_cache_info
+            },
+            { /* end of list */ }
+        }
     },
 };
 
-- 
2.25.1


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

* [RFC PATCH v2 11/12] i386/sev: sev-snp: add support for CPUID validation
  2021-08-26 22:26 [RFC PATCH v2 00/12] Add AMD Secure Nested Paging (SEV-SNP) support Michael Roth
                   ` (9 preceding siblings ...)
  2021-08-26 22:26 ` [RFC PATCH v2 10/12] target/i386: add new EPYC CPU versions with updated cache_info Michael Roth
@ 2021-08-26 22:26 ` Michael Roth
  2021-09-05 10:02     ` Dov Murik
  2021-08-26 22:26 ` [RFC PATCH v2 12/12] i386/sev: update query-sev QAPI format to handle SEV-SNP Michael Roth
  2021-11-16  9:23   ` Daniel P. Berrangé
  12 siblings, 1 reply; 61+ messages in thread
From: Michael Roth @ 2021-08-26 22:26 UTC (permalink / raw)
  To: qemu-devel
  Cc: Connor Kuehl, Philippe Mathieu-Daudé,
	Michael S . Tsirkin, James Bottomley, Dr . David Alan Gilbert,
	Tom Lendacky, Paolo Bonzini, Dov Murik, David Gibson,
	Daniel P . Berrangé,
	kvm, Eduardo Habkost, Brijesh Singh, Markus Armbruster,
	Eric Blake

SEV-SNP firmware allows a special guest page to be populated with a
table of guest CPUID values so that they can be validated through
firmware before being loaded into encrypted guest memory where they can
be used in place of hypervisor-provided values[1].

As part of SEV-SNP guest initialization, use this process to validate
the CPUID entries reported by KVM_GET_CPUID2 prior to initial guest
start.

[1]: SEV SNP Firmware ABI Specification, Rev. 0.8, 8.13.2.6

Signed-off-by: Michael Roth <michael.roth@amd.com>
---
 target/i386/sev.c | 146 +++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 143 insertions(+), 3 deletions(-)

diff --git a/target/i386/sev.c b/target/i386/sev.c
index 0009c93d28..72a6146295 100644
--- a/target/i386/sev.c
+++ b/target/i386/sev.c
@@ -153,6 +153,36 @@ static const char *const sev_fw_errlist[] = {
 
 #define SEV_FW_MAX_ERROR      ARRAY_SIZE(sev_fw_errlist)
 
+/* <linux/kvm.h> doesn't expose this, so re-use the max from kvm.c */
+#define KVM_MAX_CPUID_ENTRIES 100
+
+typedef struct KvmCpuidInfo {
+    struct kvm_cpuid2 cpuid;
+    struct kvm_cpuid_entry2 entries[KVM_MAX_CPUID_ENTRIES];
+} KvmCpuidInfo;
+
+#define SNP_CPUID_FUNCTION_MAXCOUNT 64
+#define SNP_CPUID_FUNCTION_UNKNOWN 0xFFFFFFFF
+
+typedef struct {
+    uint32_t eax_in;
+    uint32_t ecx_in;
+    uint64_t xcr0_in;
+    uint64_t xss_in;
+    uint32_t eax;
+    uint32_t ebx;
+    uint32_t ecx;
+    uint32_t edx;
+    uint64_t reserved;
+} __attribute__((packed)) SnpCpuidFunc;
+
+typedef struct {
+    uint32_t count;
+    uint32_t reserved1;
+    uint64_t reserved2;
+    SnpCpuidFunc entries[SNP_CPUID_FUNCTION_MAXCOUNT];
+} __attribute__((packed)) SnpCpuidInfo;
+
 static int
 sev_ioctl(int fd, int cmd, void *data, int *error)
 {
@@ -1141,6 +1171,117 @@ detect_first_overlap(uint64_t start, uint64_t end, Range *range_list,
     return overlap;
 }
 
+static int
+sev_snp_cpuid_info_fill(SnpCpuidInfo *snp_cpuid_info,
+                        const KvmCpuidInfo *kvm_cpuid_info)
+{
+    size_t i;
+
+    memset(snp_cpuid_info, 0, sizeof(*snp_cpuid_info));
+
+    for (i = 0; kvm_cpuid_info->entries[i].function != 0xFFFFFFFF; i++) {
+        const struct kvm_cpuid_entry2 *kvm_cpuid_entry;
+        SnpCpuidFunc *snp_cpuid_entry;
+
+        kvm_cpuid_entry = &kvm_cpuid_info->entries[i];
+        snp_cpuid_entry = &snp_cpuid_info->entries[i];
+
+        snp_cpuid_entry->eax_in = kvm_cpuid_entry->function;
+        if (kvm_cpuid_entry->flags == KVM_CPUID_FLAG_SIGNIFCANT_INDEX) {
+            snp_cpuid_entry->ecx_in = kvm_cpuid_entry->index;
+        }
+        snp_cpuid_entry->eax = kvm_cpuid_entry->eax;
+        snp_cpuid_entry->ebx = kvm_cpuid_entry->ebx;
+        snp_cpuid_entry->ecx = kvm_cpuid_entry->ecx;
+        snp_cpuid_entry->edx = kvm_cpuid_entry->edx;
+
+        if (snp_cpuid_entry->eax_in == 0xD &&
+            (snp_cpuid_entry->ecx_in == 0x0 || snp_cpuid_entry->ecx_in == 0x1)) {
+            snp_cpuid_entry->ebx = 0x240;
+        }
+    }
+
+    if (i > SNP_CPUID_FUNCTION_MAXCOUNT) {
+        error_report("SEV-SNP: CPUID count '%lu' exceeds max '%u'",
+                     i, SNP_CPUID_FUNCTION_MAXCOUNT);
+        return -1;
+    }
+
+    snp_cpuid_info->count = i;
+
+    return 0;
+}
+
+static void
+sev_snp_cpuid_report_mismatches(SnpCpuidInfo *old,
+                                SnpCpuidInfo *new)
+{
+    size_t i;
+
+    for (i = 0; i < old->count; i++) {
+        SnpCpuidFunc *old_func, *new_func;
+
+        old_func = &old->entries[i];
+        new_func = &new->entries[i];
+
+        if (memcmp(old_func, new_func, sizeof(SnpCpuidFunc))) {
+            error_report("SEV-SNP: CPUID validation failed for function %x, index: %x.\n"
+                         "provided: eax:0x%08x, ebx: 0x%08x, ecx: 0x%08x, edx: 0x%08x\n"
+                         "expected: eax:0x%08x, ebx: 0x%08x, ecx: 0x%08x, edx: 0x%08x",
+                         old_func->eax_in, old_func->ecx_in,
+                         old_func->eax, old_func->ebx, old_func->ecx, old_func->edx,
+                         new_func->eax, new_func->ebx, new_func->ecx, new_func->edx);
+        }
+    }
+}
+
+static int
+sev_snp_launch_update_cpuid(uint32_t cpuid_addr, uint32_t cpuid_len)
+{
+    KvmCpuidInfo kvm_cpuid_info;
+    SnpCpuidInfo snp_cpuid_info;
+    CPUState *cs = first_cpu;
+    MemoryRegion *mr = NULL;
+    void *snp_cpuid_hva;
+    int ret;
+
+    snp_cpuid_hva = gpa2hva(&mr, cpuid_addr, cpuid_len, NULL);
+    if (!snp_cpuid_hva) {
+        error_report("SEV-SNP: unable to access CPUID memory range at GPA %d",
+                     cpuid_addr);
+        return 1;
+    }
+
+    /* get the cpuid list from KVM */
+    memset(&kvm_cpuid_info.entries, 0xFF,
+           KVM_MAX_CPUID_ENTRIES * sizeof(struct kvm_cpuid_entry2));
+    kvm_cpuid_info.cpuid.nent = KVM_MAX_CPUID_ENTRIES;
+
+    ret = kvm_vcpu_ioctl(cs, KVM_GET_CPUID2, &kvm_cpuid_info);
+    if (ret) {
+        error_report("SEV-SNP: unable to query CPUID values for CPU: '%s'",
+                     strerror(-ret));
+    }
+
+    ret = sev_snp_cpuid_info_fill(&snp_cpuid_info, &kvm_cpuid_info);
+    if (ret) {
+        error_report("SEV-SNP: failed to generate CPUID table information");
+        exit(1);
+    }
+
+    memcpy(snp_cpuid_hva, &snp_cpuid_info, sizeof(snp_cpuid_info));
+
+    ret = sev_snp_launch_update_gpa(cpuid_addr, cpuid_len,
+                                    KVM_SEV_SNP_PAGE_TYPE_CPUID);
+    if (ret) {
+        sev_snp_cpuid_report_mismatches(&snp_cpuid_info, snp_cpuid_hva);
+        error_report("SEV-SNP: failed update CPUID page");
+        exit(1);
+    }
+
+    return 0;
+}
+
 static void snp_ovmf_boot_block_setup(void)
 {
     SevSnpBootInfoBlock *info;
@@ -1176,10 +1317,9 @@ static void snp_ovmf_boot_block_setup(void)
     }
 
     /* Populate the cpuid page */
-    ret = sev_snp_launch_update_gpa(info->cpuid_addr, info->cpuid_len,
-                                    KVM_SEV_SNP_PAGE_TYPE_CPUID);
+    ret = sev_snp_launch_update_cpuid(info->cpuid_addr, info->cpuid_len);
     if (ret) {
-        error_report("SEV-SNP: failed to insert cpuid page GPA 0x%x",
+        error_report("SEV-SNP: failed to populate cpuid tables GPA 0x%x",
                      info->cpuid_addr);
         exit(1);
     }
-- 
2.25.1


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

* [RFC PATCH v2 12/12] i386/sev: update query-sev QAPI format to handle SEV-SNP
  2021-08-26 22:26 [RFC PATCH v2 00/12] Add AMD Secure Nested Paging (SEV-SNP) support Michael Roth
                   ` (10 preceding siblings ...)
  2021-08-26 22:26 ` [RFC PATCH v2 11/12] i386/sev: sev-snp: add support for CPUID validation Michael Roth
@ 2021-08-26 22:26 ` Michael Roth
  2021-09-01 14:14   ` Markus Armbruster
  2021-09-03 15:27     ` Daniel P. Berrangé
  2021-11-16  9:23   ` Daniel P. Berrangé
  12 siblings, 2 replies; 61+ messages in thread
From: Michael Roth @ 2021-08-26 22:26 UTC (permalink / raw)
  To: qemu-devel
  Cc: Connor Kuehl, Philippe Mathieu-Daudé,
	Michael S . Tsirkin, James Bottomley, Dr . David Alan Gilbert,
	Tom Lendacky, Paolo Bonzini, Dov Murik, David Gibson,
	Daniel P . Berrangé,
	kvm, Eduardo Habkost, Brijesh Singh, Markus Armbruster,
	Eric Blake

Most of the current 'query-sev' command is relevant to both legacy
SEV/SEV-ES guests and SEV-SNP guests, with 2 exceptions:

  - 'policy' is a 64-bit field for SEV-SNP, not 32-bit, and
    the meaning of the bit positions has changed
  - 'handle' is not relevant to SEV-SNP

To address this, this patch adds a new 'sev-type' field that can be
used as a discriminator to select between SEV and SEV-SNP-specific
fields/formats without breaking compatibility for existing management
tools (so long as management tools that add support for launching
SEV-SNP guest update their handling of query-sev appropriately).

The corresponding HMP command has also been fixed up similarly.

Signed-off-by: Michael Roth <michael.roth@amd.com>
---
 qapi/misc-target.json  | 71 +++++++++++++++++++++++++++++++++---------
 target/i386/monitor.c  | 29 +++++++++++++----
 target/i386/sev.c      | 22 +++++++------
 target/i386/sev_i386.h |  3 ++
 4 files changed, 95 insertions(+), 30 deletions(-)

diff --git a/qapi/misc-target.json b/qapi/misc-target.json
index 3b05ad3dbf..80f994ff9b 100644
--- a/qapi/misc-target.json
+++ b/qapi/misc-target.json
@@ -81,6 +81,49 @@
            'send-update', 'receive-update' ],
   'if': 'TARGET_I386' }
 
+##
+# @SevGuestType:
+#
+# An enumeration indicating the type of SEV guest being run.
+#
+# @sev:     The guest is a legacy SEV or SEV-ES guest.
+# @sev-snp: The guest is an SEV-SNP guest.
+#
+# Since: 6.2
+##
+{ 'enum': 'SevGuestType',
+  'data': [ 'sev', 'sev-snp' ],
+  'if': 'TARGET_I386' }
+
+##
+# @SevGuestInfo:
+#
+# Information specific to legacy SEV/SEV-ES guests.
+#
+# @policy: SEV policy value
+#
+# @handle: SEV firmware handle
+#
+# Since: 2.12
+##
+{ 'struct': 'SevGuestInfo',
+  'data': { 'policy': 'uint32',
+            'handle': 'uint32' },
+  'if': 'TARGET_I386' }
+
+##
+# @SevSnpGuestInfo:
+#
+# Information specific to SEV-SNP guests.
+#
+# @policy: SEV-SNP policy value
+#
+# Since: 6.2
+##
+{ 'struct': 'SevSnpGuestInfo',
+  'data': { 'policy': 'uint64' },
+  'if': 'TARGET_I386' }
+
 ##
 # @SevInfo:
 #
@@ -94,25 +137,25 @@
 #
 # @build-id: SEV FW build id
 #
-# @policy: SEV policy value
-#
 # @state: SEV guest state
 #
-# @handle: SEV firmware handle
+# @sev-type: Type of SEV guest being run
 #
 # Since: 2.12
 ##
-{ 'struct': 'SevInfo',
-    'data': { 'enabled': 'bool',
-              'api-major': 'uint8',
-              'api-minor' : 'uint8',
-              'build-id' : 'uint8',
-              'policy' : 'uint32',
-              'state' : 'SevState',
-              'handle' : 'uint32'
-            },
-  'if': 'TARGET_I386'
-}
+{ 'union': 'SevInfo',
+  'base': { 'enabled': 'bool',
+            'api-major': 'uint8',
+            'api-minor' : 'uint8',
+            'build-id' : 'uint8',
+            'state' : 'SevState',
+            'sev-type' : 'SevGuestType' },
+  'discriminator': 'sev-type',
+  'data': {
+      'sev': 'SevGuestInfo',
+      'sev-snp': 'SevSnpGuestInfo' },
+  'if': 'TARGET_I386' }
+
 
 ##
 # @query-sev:
diff --git a/target/i386/monitor.c b/target/i386/monitor.c
index 119211f0b0..85a8bc2bef 100644
--- a/target/i386/monitor.c
+++ b/target/i386/monitor.c
@@ -692,20 +692,37 @@ void hmp_info_sev(Monitor *mon, const QDict *qdict)
 {
     SevInfo *info = sev_get_info();
 
-    if (info && info->enabled) {
-        monitor_printf(mon, "handle: %d\n", info->handle);
+    if (!info || !info->enabled) {
+        monitor_printf(mon, "SEV is not enabled\n");
+        goto out;
+    }
+
+    if (sev_snp_enabled()) {
         monitor_printf(mon, "state: %s\n", SevState_str(info->state));
         monitor_printf(mon, "build: %d\n", info->build_id);
         monitor_printf(mon, "api version: %d.%d\n",
                        info->api_major, info->api_minor);
         monitor_printf(mon, "debug: %s\n",
-                       info->policy & SEV_POLICY_NODBG ? "off" : "on");
-        monitor_printf(mon, "key-sharing: %s\n",
-                       info->policy & SEV_POLICY_NOKS ? "off" : "on");
+                       info->u.sev_snp.policy & SEV_SNP_POLICY_DBG ? "on"
+                                                                   : "off");
+        monitor_printf(mon, "SMT allowed: %s\n",
+                       info->u.sev_snp.policy & SEV_SNP_POLICY_SMT ? "on"
+                                                                   : "off");
+        monitor_printf(mon, "SEV type: %s\n", SevGuestType_str(info->sev_type));
     } else {
-        monitor_printf(mon, "SEV is not enabled\n");
+        monitor_printf(mon, "handle: %d\n", info->u.sev.handle);
+        monitor_printf(mon, "state: %s\n", SevState_str(info->state));
+        monitor_printf(mon, "build: %d\n", info->build_id);
+        monitor_printf(mon, "api version: %d.%d\n",
+                       info->api_major, info->api_minor);
+        monitor_printf(mon, "debug: %s\n",
+                       info->u.sev.policy & SEV_POLICY_NODBG ? "off" : "on");
+        monitor_printf(mon, "key-sharing: %s\n",
+                       info->u.sev.policy & SEV_POLICY_NOKS ? "off" : "on");
+        monitor_printf(mon, "SEV type: %s\n", SevGuestType_str(info->sev_type));
     }
 
+out:
     qapi_free_SevInfo(info);
 }
 
diff --git a/target/i386/sev.c b/target/i386/sev.c
index 72a6146295..fac2755e68 100644
--- a/target/i386/sev.c
+++ b/target/i386/sev.c
@@ -704,25 +704,27 @@ sev_get_info(void)
 {
     SevInfo *info;
     SevCommonState *sev_common = SEV_COMMON(MACHINE(qdev_get_machine())->cgs);
-    SevGuestState *sev_guest =
-        (SevGuestState *)object_dynamic_cast(OBJECT(sev_common),
-                                             TYPE_SEV_GUEST);
 
     info = g_new0(SevInfo, 1);
     info->enabled = sev_enabled();
 
     if (info->enabled) {
-        if (sev_guest) {
-            info->handle = sev_guest->handle;
-        }
         info->api_major = sev_common->api_major;
         info->api_minor = sev_common->api_minor;
         info->build_id = sev_common->build_id;
         info->state = sev_common->state;
-        /* we only report the lower 32-bits of policy for SNP, ok for now... */
-        info->policy =
-            (uint32_t)object_property_get_uint(OBJECT(sev_common),
-                                               "policy", NULL);
+
+        if (sev_snp_enabled()) {
+            info->sev_type = SEV_GUEST_TYPE_SEV_SNP;
+            info->u.sev_snp.policy =
+                object_property_get_uint(OBJECT(sev_common), "policy", NULL);
+        } else {
+            info->sev_type = SEV_GUEST_TYPE_SEV;
+            info->u.sev.handle = SEV_GUEST(sev_common)->handle;
+            info->u.sev.policy =
+                (uint32_t)object_property_get_uint(OBJECT(sev_common),
+                                                   "policy", NULL);
+        }
     }
 
     return info;
diff --git a/target/i386/sev_i386.h b/target/i386/sev_i386.h
index e0e1a599be..948d8f1079 100644
--- a/target/i386/sev_i386.h
+++ b/target/i386/sev_i386.h
@@ -28,6 +28,9 @@
 #define SEV_POLICY_DOMAIN       0x10
 #define SEV_POLICY_SEV          0x20
 
+#define SEV_SNP_POLICY_SMT      0x10000
+#define SEV_SNP_POLICY_DBG      0x80000
+
 extern bool sev_es_enabled(void);
 extern bool sev_snp_enabled(void);
 extern uint64_t sev_get_me_mask(void);
-- 
2.25.1


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

* Re: [RFC PATCH v2 12/12] i386/sev: update query-sev QAPI format to handle SEV-SNP
  2021-08-26 22:26 ` [RFC PATCH v2 12/12] i386/sev: update query-sev QAPI format to handle SEV-SNP Michael Roth
@ 2021-09-01 14:14   ` Markus Armbruster
  2021-09-03 15:13     ` Michael Roth
  2021-09-03 16:01       ` Daniel P. Berrangé
  2021-09-03 15:27     ` Daniel P. Berrangé
  1 sibling, 2 replies; 61+ messages in thread
From: Markus Armbruster @ 2021-09-01 14:14 UTC (permalink / raw)
  To: Michael Roth
  Cc: Tom Lendacky, Daniel P . Berrangé,
	Eduardo Habkost, kvm, Michael S . Tsirkin, Connor Kuehl,
	Eric Blake, James Bottomley, qemu-devel, Dr . David Alan Gilbert,
	Dov Murik, Brijesh Singh, Paolo Bonzini,
	Philippe Mathieu-Daudé,
	David Gibson

Michael Roth <michael.roth@amd.com> writes:

> Most of the current 'query-sev' command is relevant to both legacy
> SEV/SEV-ES guests and SEV-SNP guests, with 2 exceptions:
>
>   - 'policy' is a 64-bit field for SEV-SNP, not 32-bit, and
>     the meaning of the bit positions has changed
>   - 'handle' is not relevant to SEV-SNP
>
> To address this, this patch adds a new 'sev-type' field that can be
> used as a discriminator to select between SEV and SEV-SNP-specific
> fields/formats without breaking compatibility for existing management
> tools (so long as management tools that add support for launching
> SEV-SNP guest update their handling of query-sev appropriately).

Technically a compatibility break: query-sev can now return an object
that whose member @policy has different meaning, and also lacks @handle.

Matrix:

                            Old mgmt app    New mgmt app
    Old QEMU, SEV/SEV-ES       good            good(1)
    New QEMU, SEV/SEV-ES       good(2)         good
    New QEMU, SEV-SNP           bad(3)         good

Notes:

(1) As long as the management application can cope with absent member
@sev-type.

(2) As long as the management application ignores unknown member
@sev-type.

(3) Management application may choke on missing member @handle, or
worse, misinterpret member @policy.  Can only happen when something
other than the management application created the SEV-SNP guest (or the
user somehow made the management application create one even though it
doesn't know how, say with CLI option passthrough, but that's always
fragile, and I wouldn't worry about it here).

I think (1) and (2) are reasonable.  (3) is an issue for management
applications that support attaching to existing guests.  Thoughts?

>
> The corresponding HMP command has also been fixed up similarly.
>
> Signed-off-by: Michael Roth <michael.roth@amd.com>
> ---
>  qapi/misc-target.json  | 71 +++++++++++++++++++++++++++++++++---------
>  target/i386/monitor.c  | 29 +++++++++++++----
>  target/i386/sev.c      | 22 +++++++------
>  target/i386/sev_i386.h |  3 ++
>  4 files changed, 95 insertions(+), 30 deletions(-)
>
> diff --git a/qapi/misc-target.json b/qapi/misc-target.json
> index 3b05ad3dbf..80f994ff9b 100644
> --- a/qapi/misc-target.json
> +++ b/qapi/misc-target.json
> @@ -81,6 +81,49 @@
>             'send-update', 'receive-update' ],
>    'if': 'TARGET_I386' }
>  
> +##
> +# @SevGuestType:
> +#
> +# An enumeration indicating the type of SEV guest being run.
> +#
> +# @sev:     The guest is a legacy SEV or SEV-ES guest.
> +# @sev-snp: The guest is an SEV-SNP guest.
> +#
> +# Since: 6.2
> +##
> +{ 'enum': 'SevGuestType',
> +  'data': [ 'sev', 'sev-snp' ],
> +  'if': 'TARGET_I386' }
> +
> +##
> +# @SevGuestInfo:
> +#
> +# Information specific to legacy SEV/SEV-ES guests.
> +#
> +# @policy: SEV policy value
> +#
> +# @handle: SEV firmware handle
> +#
> +# Since: 2.12
> +##
> +{ 'struct': 'SevGuestInfo',
> +  'data': { 'policy': 'uint32',
> +            'handle': 'uint32' },
> +  'if': 'TARGET_I386' }
> +
> +##
> +# @SevSnpGuestInfo:
> +#
> +# Information specific to SEV-SNP guests.
> +#
> +# @policy: SEV-SNP policy value
> +#
> +# Since: 6.2
> +##
> +{ 'struct': 'SevSnpGuestInfo',
> +  'data': { 'policy': 'uint64' },
> +  'if': 'TARGET_I386' }
> +
>  ##
>  # @SevInfo:
>  #
> @@ -94,25 +137,25 @@
>  #
>  # @build-id: SEV FW build id
>  #
> -# @policy: SEV policy value
> -#
>  # @state: SEV guest state
>  #
> -# @handle: SEV firmware handle
> +# @sev-type: Type of SEV guest being run
>  #
>  # Since: 2.12
>  ##
> -{ 'struct': 'SevInfo',
> -    'data': { 'enabled': 'bool',
> -              'api-major': 'uint8',
> -              'api-minor' : 'uint8',
> -              'build-id' : 'uint8',
> -              'policy' : 'uint32',
> -              'state' : 'SevState',
> -              'handle' : 'uint32'
> -            },
> -  'if': 'TARGET_I386'
> -}
> +{ 'union': 'SevInfo',
> +  'base': { 'enabled': 'bool',
> +            'api-major': 'uint8',
> +            'api-minor' : 'uint8',
> +            'build-id' : 'uint8',
> +            'state' : 'SevState',
> +            'sev-type' : 'SevGuestType' },
> +  'discriminator': 'sev-type',
> +  'data': {
> +      'sev': 'SevGuestInfo',
> +      'sev-snp': 'SevSnpGuestInfo' },
> +  'if': 'TARGET_I386' }
> +
>  
>  ##
>  # @query-sev:

[...]



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

* Re: [RFC PATCH v2 01/12] i386/sev: introduce "sev-common" type to encapsulate common SEV state
  2021-08-26 22:26 ` [RFC PATCH v2 01/12] i386/sev: introduce "sev-common" type to encapsulate common SEV state Michael Roth
@ 2021-09-01 14:18   ` Markus Armbruster
  2021-09-03 15:11     ` Michael Roth
  0 siblings, 1 reply; 61+ messages in thread
From: Markus Armbruster @ 2021-09-01 14:18 UTC (permalink / raw)
  To: Michael Roth
  Cc: Tom Lendacky, Daniel P . Berrangé,
	Eduardo Habkost, kvm, Michael S . Tsirkin, Connor Kuehl,
	Eric Blake, James Bottomley, qemu-devel, Dr . David Alan Gilbert,
	Dov Murik, Brijesh Singh, Paolo Bonzini,
	Philippe Mathieu-Daudé,
	David Gibson

Michael Roth <michael.roth@amd.com> writes:

> Currently all SEV/SEV-ES functionality is managed through a single
> 'sev-guest' QOM type. With upcoming support for SEV-SNP, taking this
> same approach won't work well since some of the properties/state
> managed by 'sev-guest' is not applicable to SEV-SNP, which will instead
> rely on a new QOM type with its own set of properties/state.
>
> To prepare for this, this patch moves common state into an abstract
> 'sev-common' parent type to encapsulate properties/state that is
> common to both SEV/SEV-ES and SEV-SNP, leaving only SEV/SEV-ES-specific
> properties/state in the current 'sev-guest' type. This should not
> affect current behavior or command-line options.
>
> As part of this patch, some related changes are also made:
>
>   - a static 'sev_guest' variable is currently used to keep track of
>     the 'sev-guest' instance. SEV-SNP would similarly introduce an
>     'sev_snp_guest' static variable. But these instances are now
>     available via qdev_get_machine()->cgs, so switch to using that
>     instead and drop the static variable.
>
>   - 'sev_guest' is currently used as the name for the static variable
>     holding a pointer to the 'sev-guest' instance. Re-purpose the name
>     as a local variable referring the 'sev-guest' instance, and use
>     that consistently throughout the code so it can be easily
>     distinguished from sev-common/sev-snp-guest instances.
>
>   - 'sev' is generally used as the name for local variables holding a
>     pointer to the 'sev-guest' instance. In cases where that now points
>     to common state, use the name 'sev_common'; in cases where that now
>     points to state specific to 'sev-guest' instance, use the name
>     'sev_guest'
>
> Signed-off-by: Michael Roth <michael.roth@amd.com>
> ---
>  qapi/qom.json     |  34 +++--
>  target/i386/sev.c | 329 +++++++++++++++++++++++++++-------------------
>  2 files changed, 214 insertions(+), 149 deletions(-)
>
> diff --git a/qapi/qom.json b/qapi/qom.json
> index a25616bc7a..211e083727 100644
> --- a/qapi/qom.json
> +++ b/qapi/qom.json
> @@ -735,12 +735,29 @@
>    'data': { '*filename': 'str' } }
>  
>  ##
> -# @SevGuestProperties:
> +# @SevCommonProperties:
>  #
> -# Properties for sev-guest objects.
> +# Properties common to objects that are derivatives of sev-common.
>  #
>  # @sev-device: SEV device to use (default: "/dev/sev")
>  #
> +# @cbitpos: C-bit location in page table entry (default: 0)
> +#
> +# @reduced-phys-bits: number of bits in physical addresses that become
> +#                     unavailable when SEV is enabled
> +#
> +# Since: 2.12
> +##
> +{ 'struct': 'SevCommonProperties',
> +  'data': { '*sev-device': 'str',
> +            '*cbitpos': 'uint32',
> +            'reduced-phys-bits': 'uint32' } }
> +
> +##
> +# @SevGuestProperties:
> +#
> +# Properties for sev-guest objects.
> +#
>  # @dh-cert-file: guest owners DH certificate (encoded with base64)
>  #
>  # @session-file: guest owners session parameters (encoded with base64)
> @@ -749,21 +766,14 @@
>  #
>  # @handle: SEV firmware handle (default: 0)
>  #
> -# @cbitpos: C-bit location in page table entry (default: 0)
> -#
> -# @reduced-phys-bits: number of bits in physical addresses that become
> -#                     unavailable when SEV is enabled
> -#
>  # Since: 2.12
>  ##
>  { 'struct': 'SevGuestProperties',
> -  'data': { '*sev-device': 'str',
> -            '*dh-cert-file': 'str',
> +  'base': 'SevCommonProperties',
> +  'data': { '*dh-cert-file': 'str',
>              '*session-file': 'str',
>              '*policy': 'uint32',
> -            '*handle': 'uint32',
> -            '*cbitpos': 'uint32',
> -            'reduced-phys-bits': 'uint32' } }
> +            '*handle': 'uint32' } }
>  
>  ##
>  # @ObjectType:

External interface remains unchanged, as far as I can tell.

For the QAPI schema:
Acked-by: Markus Armbruster <armbru@redhat.com>



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

* Re: [RFC PATCH v2 03/12] i386/sev: introduce 'sev-snp-guest' object
  2021-08-26 22:26 ` [RFC PATCH v2 03/12] i386/sev: introduce 'sev-snp-guest' object Michael Roth
@ 2021-09-01 14:29   ` Markus Armbruster
  2021-09-03 15:15     ` Michael Roth
  2021-09-03 21:12     ` Dov Murik
  1 sibling, 1 reply; 61+ messages in thread
From: Markus Armbruster @ 2021-09-01 14:29 UTC (permalink / raw)
  To: Michael Roth
  Cc: Tom Lendacky, Daniel P . Berrangé,
	Eduardo Habkost, kvm, Michael S . Tsirkin, Connor Kuehl,
	Eric Blake, James Bottomley, qemu-devel, Dr . David Alan Gilbert,
	Dov Murik, Brijesh Singh, Paolo Bonzini,
	Philippe Mathieu-Daudé,
	David Gibson

Michael Roth <michael.roth@amd.com> writes:

> From: Brijesh Singh <brijesh.singh@amd.com>
>
> SEV-SNP support relies on a different set of properties/state than the
> existing 'sev-guest' object. This patch introduces the 'sev-snp-guest'
> object, which can be used to configure an SEV-SNP guest. For example,
> a default-configured SEV-SNP guest with no additional information
> passed in for use with attestation:
>
>   -object sev-snp-guest,id=sev0
>
> or a fully-specified SEV-SNP guest where all spec-defined binary
> blobs are passed in as base64-encoded strings:
>
>   -object sev-snp-guest,id=sev0, \
>     policy=0x30000, \
>     init-flags=0, \
>     id-block=YWFhYWFhYWFhYWFhYWFhCg==, \
>     id-auth=CxHK/OKLkXGn/KpAC7Wl1FSiisWDbGTEKz..., \
>     auth-key-enabled=on, \
>     host-data=LNkCWBRC5CcdGXirbNUV1OrsR28s..., \
>     guest-visible-workarounds=AA==, \
>
> See the QAPI schema updates included in this patch for more usage
> details.
>
> In some cases these blobs may be up to 4096 characters, but this is
> generally well below the default limit for linux hosts where
> command-line sizes are defined by the sysconf-configurable ARG_MAX
> value, which defaults to 2097152 characters for Ubuntu hosts, for
> example.
>
> Co-developed-by: Michael Roth <michael.roth@amd.com>
> Signed-off-by: Brijesh Singh <brijesh.singh@amd.com>
> Signed-off-by: Michael Roth <michael.roth@amd.com>
> ---
>  docs/amd-memory-encryption.txt |  77 ++++++++++-
>  qapi/qom.json                  |  60 ++++++++
>  target/i386/sev.c              | 245 ++++++++++++++++++++++++++++++++-
>  3 files changed, 379 insertions(+), 3 deletions(-)
>
> diff --git a/docs/amd-memory-encryption.txt b/docs/amd-memory-encryption.txt
> index ffca382b5f..0d82e67fa1 100644
> --- a/docs/amd-memory-encryption.txt
> +++ b/docs/amd-memory-encryption.txt
> @@ -22,8 +22,8 @@ support for notifying a guest's operating system when certain types of VMEXITs
>  are about to occur. This allows the guest to selectively share information with
>  the hypervisor to satisfy the requested function.
>  
> -Launching
> ----------
> +Launching (SEV and SEV-ES)
> +--------------------------
>  Boot images (such as bios) must be encrypted before a guest can be booted. The
>  MEMORY_ENCRYPT_OP ioctl provides commands to encrypt the images: LAUNCH_START,
>  LAUNCH_UPDATE_DATA, LAUNCH_MEASURE and LAUNCH_FINISH. These four commands
> @@ -113,6 +113,79 @@ a SEV-ES guest:
>   - Requires in-kernel irqchip - the burden is placed on the hypervisor to
>     manage booting APs.
>  
> +Launching (SEV-SNP)
> +-------------------
> +Boot images (such as bios) must be encrypted before a guest can be booted. The
> +MEMORY_ENCRYPT_OP ioctl provides commands to encrypt the images:
> +KVM_SNP_INIT, SNP_LAUNCH_START, SNP_LAUNCH_UPDATE, and SNP_LAUNCH_FINISH. These
> +four commands together generate a fresh memory encryption key for the VM,
> +encrypt the boot images for a successful launch.
> +
> +KVM_SNP_INIT is called first to initialize the SEV-SNP firmware and SNP
> +features in the KVM. The feature flags value can be provided through the
> +'init-flags' property of the 'sev-snp-guest' object.
> +
> ++------------+-------+----------+---------------------------------+
> +| key        | type  | default  | meaning                         |
> ++------------+-------+----------+---------------------------------+
> +| init_flags | hex   | 0        | SNP feature flags               |
> ++-----------------------------------------------------------------+
> +
> +Note: currently the init_flags must be zero.
> +
> +SNP_LAUNCH_START is called first to create a cryptographic launch context
> +within the firmware. To create this context, guest owner must provide a guest
> +policy and other parameters as described in the SEV-SNP firmware
> +specification. The launch parameters should be specified as described in the
> +QAPI schema for the 'sev-snp-guest' object.
> +
> +The SNP_LAUNCH_START uses the following parameters (see the SEV-SNP
> +specification for more details):
> +
> ++--------+-------+----------+----------------------------------------------+
> +| key    | type  | default  | meaning                                      |
> ++--------+-------+----------+----------------------------------------------+
> +| policy | hex   | 0x30000  | a 64-bit guest policy                        |
> +| imi_en | bool  | 0        | 1 when IMI is enabled                        |
> +| ma_end | bool  | 0        | 1 when migration agent is used               |
> +| gosvw  | string| 0        | 16-byte base64 encoded string for the guest  |
> +|        |       |          | OS visible workaround.                       |
> ++--------+-------+----------+----------------------------------------------+
> +
> +SNP_LAUNCH_UPDATE encrypts the memory region using the cryptographic context
> +created via the SNP_LAUNCH_START command. If required, this command can be called
> +multiple times to encrypt different memory regions. The command also calculates
> +the measurement of the memory contents as it encrypts.
> +
> +SNP_LAUNCH_FINISH finalizes the guest launch flow. Optionally, while finalizing
> +the launch the firmware can perform checks on the launch digest computing
> +through the SNP_LAUNCH_UPDATE. To perform the check the user must supply
> +the id block, authentication blob and host data that should be included in the
> +attestation report. See the SEV-SNP spec for further details.
> +
> +The SNP_LAUNCH_FINISH uses the following parameters, which can be configured
> +by the corresponding parameters documented in the QAPI schema for the
> +'sev-snp-guest' object.
> +
> ++------------+-------+----------+----------------------------------------------+
> +| key        | type  | default  | meaning                                      |
> ++------------+-------+----------+----------------------------------------------+
> +| id_block   | string| none     | base64 encoded ID block                      |
> ++------------+-------+----------+----------------------------------------------+
> +| id_auth    | string| none     | base64 encoded authentication information    |
> ++------------+-------+----------+----------------------------------------------+
> +| auth_key_en| bool  | 0        | auth block contains author key               |
> ++------------+-------+----------+----------------------------------------------+
> +| host_data  | string| none     | host provided data                           |
> ++------------+-------+----------+----------------------------------------------+
> +
> +To launch a SEV-SNP guest (additional parameters are documented in the QAPI
> +schema for the 'sev-snp-guest' object):
> +
> +# ${QEMU} \
> +    -machine ...,confidential-guest-support=sev0 \
> +    -object sev-snp-guest,id=sev0,cbitpos=51,reduced-phys-bits=1
> +
>  Debugging
>  -----------
>  Since the memory contents of a SEV guest are encrypted, hypervisor access to
> diff --git a/qapi/qom.json b/qapi/qom.json
> index 211e083727..ea39585026 100644
> --- a/qapi/qom.json
> +++ b/qapi/qom.json
> @@ -775,6 +775,64 @@
>              '*policy': 'uint32',
>              '*handle': 'uint32' } }
>  
> +##
> +# @SevSnpGuestProperties:
> +#
> +# Properties for sev-snp-guest objects. Many of these are direct arguments
> +# for the SEV-SNP KVM interfaces documented in the linux kernel source
> +# documentation under 'amd-memory-encryption.rst'. Additional documentation
> +# is also available in the QEMU source tree under
> +# 'amd-memory-encryption.rst'.
> +#
> +# In addition to those files, please see the SEV-SNP Firmware Specification
> +# (Rev 0.9) documentation for the SNP_INIT and
> +# SNP_LAUNCH_{START,UPDATE,FINISH} firmware interfaces, which the KVM
> +# interfaces are written against.
> +#
> +# @init-flags: as documented for the 'flags' parameter of the
> +#              KVM_SNP_INIT KVM command (default: 0)
> +#
> +# @policy: as documented for the 'policy' parameter of the
> +#          KVM_SNP_LAUNCH_START KVM command (default: 0x30000)

These expose the host kernel's numerical encoding of over QMP.  I'm not
sure that's a good idea.

> +#
> +# @guest-visible-workarounds: 16-byte, base64-encoded blob to report
> +#                             hypervisor-defined workarounds, as documented
> +#                             for the 'gosvm' parameter of the
> +#                             KVM_SNP_LAUNCH_START KVM command.
> +#                             (default: all-zero)
> +#
> +# @id-block: 8-byte, base64-encoded blob to provide the ID Block
> +#            structure documented in SEV-SNP spec, as documented for the
> +#            'id_block_uaddr' parameter of the KVM_SNP_LAUNCH_FINISH
> +#            command (default: all-zero)
> +#
> +# @id-auth: 4096-byte, base64-encoded blob to provide the ID Authentication
> +#           Information Structure documented in SEV-SNP spec, as documented
> +#           for the 'id_auth_uaddr' parameter of the KVM_SNP_LAUNCH_FINISH
> +#           command (default: all-zero)
> +#
> +# @auth-key-enabled: true if 'id-auth' blob contains the Author Key
> +#                    documented in the SEV-SNP spec, as documented for the
> +#                    'auth_key_en' parameter of the KVM_SNP_LAUNCH_FINISH
> +#                    command (default: false)
> +#
> +# @host-data: 32-byte, base64-encoded user-defined blob to provide to the
> +#             guest, as documented for the 'host_data' parameter of the
> +#             KVM_SNP_LAUNCH_FINISH command (default: all-zero)
> +#
> +# Since: 6.2
> +##
> +{ 'struct': 'SevSnpGuestProperties',
> +  'base': 'SevCommonProperties',
> +  'data': {
> +            '*init-flags': 'uint64',
> +            '*policy': 'uint64',
> +            '*guest-visible-workarounds': 'str',
> +            '*id-block': 'str',
> +            '*id-auth': 'str',
> +            '*auth-key-enabled': 'bool',
> +            '*host-data': 'str' } }
> +
>  ##
>  # @ObjectType:
>  #
> @@ -816,6 +874,7 @@
>      'secret',
>      'secret_keyring',
>      'sev-guest',
> +    'sev-snp-guest',
>      's390-pv-guest',
>      'throttle-group',
>      'tls-creds-anon',
> @@ -873,6 +932,7 @@
>        'secret':                     'SecretProperties',
>        'secret_keyring':             'SecretKeyringProperties',
>        'sev-guest':                  'SevGuestProperties',
> +      'sev-snp-guest':              'SevSnpGuestProperties',
>        'throttle-group':             'ThrottleGroupProperties',
>        'tls-creds-anon':             'TlsCredsAnonProperties',
>        'tls-creds-psk':              'TlsCredsPskProperties',

Pretty much all Greek to me, but there are no obvious QAPI schema
no-nos, so

For the QAPI schema
Acked-by: Markus Armbruster <armbru@redhat.com>

[...]



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

* Re: [RFC PATCH v2 01/12] i386/sev: introduce "sev-common" type to encapsulate common SEV state
  2021-09-01 14:18   ` Markus Armbruster
@ 2021-09-03 15:11     ` Michael Roth
  0 siblings, 0 replies; 61+ messages in thread
From: Michael Roth @ 2021-09-03 15:11 UTC (permalink / raw)
  To: Markus Armbruster
  Cc: Tom Lendacky, Daniel P . Berrangé,
	Eduardo Habkost, kvm, Michael S . Tsirkin, Connor Kuehl,
	Eric Blake, James Bottomley, qemu-devel, Dr . David Alan Gilbert,
	Dov Murik, Brijesh Singh, Paolo Bonzini,
	Philippe Mathieu-Daudé,
	David Gibson

On Wed, Sep 01, 2021 at 04:18:21PM +0200, Markus Armbruster wrote:
> Michael Roth <michael.roth@amd.com> writes:
> 
> > Currently all SEV/SEV-ES functionality is managed through a single
> > 'sev-guest' QOM type. With upcoming support for SEV-SNP, taking this
> > same approach won't work well since some of the properties/state
> > managed by 'sev-guest' is not applicable to SEV-SNP, which will instead
> > rely on a new QOM type with its own set of properties/state.
> >
> > To prepare for this, this patch moves common state into an abstract
> > 'sev-common' parent type to encapsulate properties/state that is
> > common to both SEV/SEV-ES and SEV-SNP, leaving only SEV/SEV-ES-specific
> > properties/state in the current 'sev-guest' type. This should not
> > affect current behavior or command-line options.
> >
> > As part of this patch, some related changes are also made:
> >
> >   - a static 'sev_guest' variable is currently used to keep track of
> >     the 'sev-guest' instance. SEV-SNP would similarly introduce an
> >     'sev_snp_guest' static variable. But these instances are now
> >     available via qdev_get_machine()->cgs, so switch to using that
> >     instead and drop the static variable.
> >
> >   - 'sev_guest' is currently used as the name for the static variable
> >     holding a pointer to the 'sev-guest' instance. Re-purpose the name
> >     as a local variable referring the 'sev-guest' instance, and use
> >     that consistently throughout the code so it can be easily
> >     distinguished from sev-common/sev-snp-guest instances.
> >
> >   - 'sev' is generally used as the name for local variables holding a
> >     pointer to the 'sev-guest' instance. In cases where that now points
> >     to common state, use the name 'sev_common'; in cases where that now
> >     points to state specific to 'sev-guest' instance, use the name
> >     'sev_guest'
> >
> > Signed-off-by: Michael Roth <michael.roth@amd.com>
> > ---
> >  qapi/qom.json     |  34 +++--
> >  target/i386/sev.c | 329 +++++++++++++++++++++++++++-------------------
> >  2 files changed, 214 insertions(+), 149 deletions(-)
> >
> > diff --git a/qapi/qom.json b/qapi/qom.json
> > index a25616bc7a..211e083727 100644
> > --- a/qapi/qom.json
> > +++ b/qapi/qom.json
> > @@ -735,12 +735,29 @@
> >    'data': { '*filename': 'str' } }
> >  
> >  ##
> > -# @SevGuestProperties:
> > +# @SevCommonProperties:
> >  #
> > -# Properties for sev-guest objects.
> > +# Properties common to objects that are derivatives of sev-common.
> >  #
> >  # @sev-device: SEV device to use (default: "/dev/sev")
> >  #
> > +# @cbitpos: C-bit location in page table entry (default: 0)
> > +#
> > +# @reduced-phys-bits: number of bits in physical addresses that become
> > +#                     unavailable when SEV is enabled
> > +#
> > +# Since: 2.12
> > +##
> > +{ 'struct': 'SevCommonProperties',
> > +  'data': { '*sev-device': 'str',
> > +            '*cbitpos': 'uint32',
> > +            'reduced-phys-bits': 'uint32' } }
> > +
> > +##
> > +# @SevGuestProperties:
> > +#
> > +# Properties for sev-guest objects.
> > +#
> >  # @dh-cert-file: guest owners DH certificate (encoded with base64)
> >  #
> >  # @session-file: guest owners session parameters (encoded with base64)
> > @@ -749,21 +766,14 @@
> >  #
> >  # @handle: SEV firmware handle (default: 0)
> >  #
> > -# @cbitpos: C-bit location in page table entry (default: 0)
> > -#
> > -# @reduced-phys-bits: number of bits in physical addresses that become
> > -#                     unavailable when SEV is enabled
> > -#
> >  # Since: 2.12
> >  ##
> >  { 'struct': 'SevGuestProperties',
> > -  'data': { '*sev-device': 'str',
> > -            '*dh-cert-file': 'str',
> > +  'base': 'SevCommonProperties',
> > +  'data': { '*dh-cert-file': 'str',
> >              '*session-file': 'str',
> >              '*policy': 'uint32',
> > -            '*handle': 'uint32',
> > -            '*cbitpos': 'uint32',
> > -            'reduced-phys-bits': 'uint32' } }
> > +            '*handle': 'uint32' } }
> >  
> >  ##
> >  # @ObjectType:
> 
> External interface remains unchanged, as far as I can tell.
> 
> For the QAPI schema:
> Acked-by: Markus Armbruster <armbru@redhat.com>

Thanks!

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

* Re: [RFC PATCH v2 12/12] i386/sev: update query-sev QAPI format to handle SEV-SNP
  2021-09-01 14:14   ` Markus Armbruster
@ 2021-09-03 15:13     ` Michael Roth
  2021-09-03 15:30         ` Daniel P. Berrangé
  2021-09-03 16:01       ` Daniel P. Berrangé
  1 sibling, 1 reply; 61+ messages in thread
From: Michael Roth @ 2021-09-03 15:13 UTC (permalink / raw)
  To: Markus Armbruster
  Cc: Tom Lendacky, Daniel P . Berrangé,
	Eduardo Habkost, kvm, Michael S . Tsirkin, Connor Kuehl,
	Eric Blake, James Bottomley, qemu-devel, Dr . David Alan Gilbert,
	Dov Murik, Brijesh Singh, Paolo Bonzini,
	Philippe Mathieu-Daudé,
	David Gibson

On Wed, Sep 01, 2021 at 04:14:10PM +0200, Markus Armbruster wrote:
> Michael Roth <michael.roth@amd.com> writes:
> 
> > Most of the current 'query-sev' command is relevant to both legacy
> > SEV/SEV-ES guests and SEV-SNP guests, with 2 exceptions:
> >
> >   - 'policy' is a 64-bit field for SEV-SNP, not 32-bit, and
> >     the meaning of the bit positions has changed
> >   - 'handle' is not relevant to SEV-SNP
> >
> > To address this, this patch adds a new 'sev-type' field that can be
> > used as a discriminator to select between SEV and SEV-SNP-specific
> > fields/formats without breaking compatibility for existing management
> > tools (so long as management tools that add support for launching
> > SEV-SNP guest update their handling of query-sev appropriately).
> 
> Technically a compatibility break: query-sev can now return an object
> that whose member @policy has different meaning, and also lacks @handle.
> 
> Matrix:
> 
>                             Old mgmt app    New mgmt app
>     Old QEMU, SEV/SEV-ES       good            good(1)
>     New QEMU, SEV/SEV-ES       good(2)         good
>     New QEMU, SEV-SNP           bad(3)         good
> 
> Notes:
> 
> (1) As long as the management application can cope with absent member
> @sev-type.
> 
> (2) As long as the management application ignores unknown member
> @sev-type.
> 
> (3) Management application may choke on missing member @handle, or
> worse, misinterpret member @policy.  Can only happen when something
> other than the management application created the SEV-SNP guest (or the
> user somehow made the management application create one even though it
> doesn't know how, say with CLI option passthrough, but that's always
> fragile, and I wouldn't worry about it here).
> 
> I think (1) and (2) are reasonable.  (3) is an issue for management
> applications that support attaching to existing guests.  Thoughts?

Hmm... yah I hadn't considering 'old mgmt' trying to interact with a SNP
guest started through some other means.

Don't really see an alternative other than introducing a new
'query-sev-snp', but that would still leave 'old mgmt' broken, since
it might still call do weird stuff like try to interpret the SNP policy
as an SEV/SEV-ES and end up with some very unexpected results. So if I
did go this route, I would need to have QMP begin returning an error if
query-sev is run against an SNP guest. But currently for non-SEV guests
it already does:

  error_setg(errp, "SEV feature is not available")

so 'old mgmt' should be able to handle the error just fine.

Would that approach be reasonable?

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

* Re: [RFC PATCH v2 03/12] i386/sev: introduce 'sev-snp-guest' object
  2021-09-01 14:29   ` Markus Armbruster
@ 2021-09-03 15:15     ` Michael Roth
  0 siblings, 0 replies; 61+ messages in thread
From: Michael Roth @ 2021-09-03 15:15 UTC (permalink / raw)
  To: Markus Armbruster
  Cc: Tom Lendacky, Daniel P . Berrangé,
	Eduardo Habkost, kvm, Michael S . Tsirkin, Connor Kuehl,
	Eric Blake, James Bottomley, qemu-devel, Dr . David Alan Gilbert,
	Dov Murik, Brijesh Singh, Paolo Bonzini,
	Philippe Mathieu-Daudé,
	David Gibson

On Wed, Sep 01, 2021 at 04:29:03PM +0200, Markus Armbruster wrote:
> Michael Roth <michael.roth@amd.com> writes:
> 
> > From: Brijesh Singh <brijesh.singh@amd.com>
> >
> > SEV-SNP support relies on a different set of properties/state than the
> > existing 'sev-guest' object. This patch introduces the 'sev-snp-guest'
> > object, which can be used to configure an SEV-SNP guest. For example,
> > a default-configured SEV-SNP guest with no additional information
> > passed in for use with attestation:
> >
> >   -object sev-snp-guest,id=sev0
> >
> > or a fully-specified SEV-SNP guest where all spec-defined binary
> > blobs are passed in as base64-encoded strings:
> >
> >   -object sev-snp-guest,id=sev0, \
> >     policy=0x30000, \
> >     init-flags=0, \
> >     id-block=YWFhYWFhYWFhYWFhYWFhCg==, \
> >     id-auth=CxHK/OKLkXGn/KpAC7Wl1FSiisWDbGTEKz..., \
> >     auth-key-enabled=on, \
> >     host-data=LNkCWBRC5CcdGXirbNUV1OrsR28s..., \
> >     guest-visible-workarounds=AA==, \
> >
> > See the QAPI schema updates included in this patch for more usage
> > details.
> >
> > In some cases these blobs may be up to 4096 characters, but this is
> > generally well below the default limit for linux hosts where
> > command-line sizes are defined by the sysconf-configurable ARG_MAX
> > value, which defaults to 2097152 characters for Ubuntu hosts, for
> > example.
> >
> > Co-developed-by: Michael Roth <michael.roth@amd.com>
> > Signed-off-by: Brijesh Singh <brijesh.singh@amd.com>
> > Signed-off-by: Michael Roth <michael.roth@amd.com>
> > ---
> >  docs/amd-memory-encryption.txt |  77 ++++++++++-
> >  qapi/qom.json                  |  60 ++++++++
> >  target/i386/sev.c              | 245 ++++++++++++++++++++++++++++++++-
> >  3 files changed, 379 insertions(+), 3 deletions(-)
> >
> > diff --git a/docs/amd-memory-encryption.txt b/docs/amd-memory-encryption.txt
> > index ffca382b5f..0d82e67fa1 100644
> > --- a/docs/amd-memory-encryption.txt
> > +++ b/docs/amd-memory-encryption.txt
> > @@ -22,8 +22,8 @@ support for notifying a guest's operating system when certain types of VMEXITs
> >  are about to occur. This allows the guest to selectively share information with
> >  the hypervisor to satisfy the requested function.
> >  
> > -Launching
> > ----------
> > +Launching (SEV and SEV-ES)
> > +--------------------------
> >  Boot images (such as bios) must be encrypted before a guest can be booted. The
> >  MEMORY_ENCRYPT_OP ioctl provides commands to encrypt the images: LAUNCH_START,
> >  LAUNCH_UPDATE_DATA, LAUNCH_MEASURE and LAUNCH_FINISH. These four commands
> > @@ -113,6 +113,79 @@ a SEV-ES guest:
> >   - Requires in-kernel irqchip - the burden is placed on the hypervisor to
> >     manage booting APs.
> >  
> > +Launching (SEV-SNP)
> > +-------------------
> > +Boot images (such as bios) must be encrypted before a guest can be booted. The
> > +MEMORY_ENCRYPT_OP ioctl provides commands to encrypt the images:
> > +KVM_SNP_INIT, SNP_LAUNCH_START, SNP_LAUNCH_UPDATE, and SNP_LAUNCH_FINISH. These
> > +four commands together generate a fresh memory encryption key for the VM,
> > +encrypt the boot images for a successful launch.
> > +
> > +KVM_SNP_INIT is called first to initialize the SEV-SNP firmware and SNP
> > +features in the KVM. The feature flags value can be provided through the
> > +'init-flags' property of the 'sev-snp-guest' object.
> > +
> > ++------------+-------+----------+---------------------------------+
> > +| key        | type  | default  | meaning                         |
> > ++------------+-------+----------+---------------------------------+
> > +| init_flags | hex   | 0        | SNP feature flags               |
> > ++-----------------------------------------------------------------+
> > +
> > +Note: currently the init_flags must be zero.
> > +
> > +SNP_LAUNCH_START is called first to create a cryptographic launch context
> > +within the firmware. To create this context, guest owner must provide a guest
> > +policy and other parameters as described in the SEV-SNP firmware
> > +specification. The launch parameters should be specified as described in the
> > +QAPI schema for the 'sev-snp-guest' object.
> > +
> > +The SNP_LAUNCH_START uses the following parameters (see the SEV-SNP
> > +specification for more details):
> > +
> > ++--------+-------+----------+----------------------------------------------+
> > +| key    | type  | default  | meaning                                      |
> > ++--------+-------+----------+----------------------------------------------+
> > +| policy | hex   | 0x30000  | a 64-bit guest policy                        |
> > +| imi_en | bool  | 0        | 1 when IMI is enabled                        |
> > +| ma_end | bool  | 0        | 1 when migration agent is used               |
> > +| gosvw  | string| 0        | 16-byte base64 encoded string for the guest  |
> > +|        |       |          | OS visible workaround.                       |
> > ++--------+-------+----------+----------------------------------------------+
> > +
> > +SNP_LAUNCH_UPDATE encrypts the memory region using the cryptographic context
> > +created via the SNP_LAUNCH_START command. If required, this command can be called
> > +multiple times to encrypt different memory regions. The command also calculates
> > +the measurement of the memory contents as it encrypts.
> > +
> > +SNP_LAUNCH_FINISH finalizes the guest launch flow. Optionally, while finalizing
> > +the launch the firmware can perform checks on the launch digest computing
> > +through the SNP_LAUNCH_UPDATE. To perform the check the user must supply
> > +the id block, authentication blob and host data that should be included in the
> > +attestation report. See the SEV-SNP spec for further details.
> > +
> > +The SNP_LAUNCH_FINISH uses the following parameters, which can be configured
> > +by the corresponding parameters documented in the QAPI schema for the
> > +'sev-snp-guest' object.
> > +
> > ++------------+-------+----------+----------------------------------------------+
> > +| key        | type  | default  | meaning                                      |
> > ++------------+-------+----------+----------------------------------------------+
> > +| id_block   | string| none     | base64 encoded ID block                      |
> > ++------------+-------+----------+----------------------------------------------+
> > +| id_auth    | string| none     | base64 encoded authentication information    |
> > ++------------+-------+----------+----------------------------------------------+
> > +| auth_key_en| bool  | 0        | auth block contains author key               |
> > ++------------+-------+----------+----------------------------------------------+
> > +| host_data  | string| none     | host provided data                           |
> > ++------------+-------+----------+----------------------------------------------+
> > +
> > +To launch a SEV-SNP guest (additional parameters are documented in the QAPI
> > +schema for the 'sev-snp-guest' object):
> > +
> > +# ${QEMU} \
> > +    -machine ...,confidential-guest-support=sev0 \
> > +    -object sev-snp-guest,id=sev0,cbitpos=51,reduced-phys-bits=1
> > +
> >  Debugging
> >  -----------
> >  Since the memory contents of a SEV guest are encrypted, hypervisor access to
> > diff --git a/qapi/qom.json b/qapi/qom.json
> > index 211e083727..ea39585026 100644
> > --- a/qapi/qom.json
> > +++ b/qapi/qom.json
> > @@ -775,6 +775,64 @@
> >              '*policy': 'uint32',
> >              '*handle': 'uint32' } }
> >  
> > +##
> > +# @SevSnpGuestProperties:
> > +#
> > +# Properties for sev-snp-guest objects. Many of these are direct arguments
> > +# for the SEV-SNP KVM interfaces documented in the linux kernel source
> > +# documentation under 'amd-memory-encryption.rst'. Additional documentation
> > +# is also available in the QEMU source tree under
> > +# 'amd-memory-encryption.rst'.
> > +#
> > +# In addition to those files, please see the SEV-SNP Firmware Specification
> > +# (Rev 0.9) documentation for the SNP_INIT and
> > +# SNP_LAUNCH_{START,UPDATE,FINISH} firmware interfaces, which the KVM
> > +# interfaces are written against.
> > +#
> > +# @init-flags: as documented for the 'flags' parameter of the
> > +#              KVM_SNP_INIT KVM command (default: 0)
> > +#
> > +# @policy: as documented for the 'policy' parameter of the
> > +#          KVM_SNP_LAUNCH_START KVM command (default: 0x30000)
> 
> These expose the host kernel's numerical encoding of over QMP.  I'm not
> sure that's a good idea.

Most of these are the same as the actual arguments to firmware as defined by
the SNP spec, but I'll see if I can make the documentation here more
agnostic to the kernel interfaces and try to stick more to the SNP/firmware
spec.

> 
> > +#
> > +# @guest-visible-workarounds: 16-byte, base64-encoded blob to report
> > +#                             hypervisor-defined workarounds, as documented
> > +#                             for the 'gosvm' parameter of the
> > +#                             KVM_SNP_LAUNCH_START KVM command.
> > +#                             (default: all-zero)
> > +#
> > +# @id-block: 8-byte, base64-encoded blob to provide the ID Block
> > +#            structure documented in SEV-SNP spec, as documented for the
> > +#            'id_block_uaddr' parameter of the KVM_SNP_LAUNCH_FINISH
> > +#            command (default: all-zero)
> > +#
> > +# @id-auth: 4096-byte, base64-encoded blob to provide the ID Authentication
> > +#           Information Structure documented in SEV-SNP spec, as documented
> > +#           for the 'id_auth_uaddr' parameter of the KVM_SNP_LAUNCH_FINISH
> > +#           command (default: all-zero)
> > +#
> > +# @auth-key-enabled: true if 'id-auth' blob contains the Author Key
> > +#                    documented in the SEV-SNP spec, as documented for the
> > +#                    'auth_key_en' parameter of the KVM_SNP_LAUNCH_FINISH
> > +#                    command (default: false)
> > +#
> > +# @host-data: 32-byte, base64-encoded user-defined blob to provide to the
> > +#             guest, as documented for the 'host_data' parameter of the
> > +#             KVM_SNP_LAUNCH_FINISH command (default: all-zero)
> > +#
> > +# Since: 6.2
> > +##
> > +{ 'struct': 'SevSnpGuestProperties',
> > +  'base': 'SevCommonProperties',
> > +  'data': {
> > +            '*init-flags': 'uint64',
> > +            '*policy': 'uint64',
> > +            '*guest-visible-workarounds': 'str',
> > +            '*id-block': 'str',
> > +            '*id-auth': 'str',
> > +            '*auth-key-enabled': 'bool',
> > +            '*host-data': 'str' } }
> > +
> >  ##
> >  # @ObjectType:
> >  #
> > @@ -816,6 +874,7 @@
> >      'secret',
> >      'secret_keyring',
> >      'sev-guest',
> > +    'sev-snp-guest',
> >      's390-pv-guest',
> >      'throttle-group',
> >      'tls-creds-anon',
> > @@ -873,6 +932,7 @@
> >        'secret':                     'SecretProperties',
> >        'secret_keyring':             'SecretKeyringProperties',
> >        'sev-guest':                  'SevGuestProperties',
> > +      'sev-snp-guest':              'SevSnpGuestProperties',
> >        'throttle-group':             'ThrottleGroupProperties',
> >        'tls-creds-anon':             'TlsCredsAnonProperties',
> >        'tls-creds-psk':              'TlsCredsPskProperties',
> 
> Pretty much all Greek to me, but there are no obvious QAPI schema
> no-nos, so
> 
> For the QAPI schema
> Acked-by: Markus Armbruster <armbru@redhat.com>
> 
> [...]

Thanks!

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

* Re: [RFC PATCH v2 12/12] i386/sev: update query-sev QAPI format to handle SEV-SNP
  2021-08-26 22:26 ` [RFC PATCH v2 12/12] i386/sev: update query-sev QAPI format to handle SEV-SNP Michael Roth
@ 2021-09-03 15:27     ` Daniel P. Berrangé
  2021-09-03 15:27     ` Daniel P. Berrangé
  1 sibling, 0 replies; 61+ messages in thread
From: Daniel P. Berrangé @ 2021-09-03 15:27 UTC (permalink / raw)
  To: Michael Roth
  Cc: qemu-devel, Connor Kuehl, Philippe Mathieu-Daudé,
	Michael S . Tsirkin, James Bottomley, Dr . David Alan Gilbert,
	Tom Lendacky, Paolo Bonzini, Dov Murik, David Gibson, kvm,
	Eduardo Habkost, Brijesh Singh, Markus Armbruster, Eric Blake

On Thu, Aug 26, 2021 at 05:26:27PM -0500, Michael Roth wrote:
> Most of the current 'query-sev' command is relevant to both legacy
> SEV/SEV-ES guests and SEV-SNP guests, with 2 exceptions:
> 
>   - 'policy' is a 64-bit field for SEV-SNP, not 32-bit, and
>     the meaning of the bit positions has changed
>   - 'handle' is not relevant to SEV-SNP

If the host supports SEV-SNP guests, is it still possible for mgmt
app to create guests using the  "legacy" SEV/SEV-ES approach ? ie
is the hardware backwards compatible, or is it strictly required
to always create SEV-SNP guests when the hardware is capable ?

The code here seems to imply a non-backwards compatible approach,
mandating use of SEV-SNP guests on such capable kernel/hardware.

> To address this, this patch adds a new 'sev-type' field that can be
> used as a discriminator to select between SEV and SEV-SNP-specific
> fields/formats without breaking compatibility for existing management
> tools (so long as management tools that add support for launching
> SEV-SNP guest update their handling of query-sev appropriately).
> 
> The corresponding HMP command has also been fixed up similarly.
> 
> Signed-off-by: Michael Roth <michael.roth@amd.com>
> ---
>  qapi/misc-target.json  | 71 +++++++++++++++++++++++++++++++++---------
>  target/i386/monitor.c  | 29 +++++++++++++----
>  target/i386/sev.c      | 22 +++++++------
>  target/i386/sev_i386.h |  3 ++
>  4 files changed, 95 insertions(+), 30 deletions(-)
> 
> diff --git a/qapi/misc-target.json b/qapi/misc-target.json
> index 3b05ad3dbf..80f994ff9b 100644
> --- a/qapi/misc-target.json
> +++ b/qapi/misc-target.json
> @@ -81,6 +81,49 @@
>             'send-update', 'receive-update' ],
>    'if': 'TARGET_I386' }
>  
> +##
> +# @SevGuestType:
> +#
> +# An enumeration indicating the type of SEV guest being run.
> +#
> +# @sev:     The guest is a legacy SEV or SEV-ES guest.
> +# @sev-snp: The guest is an SEV-SNP guest.
> +#
> +# Since: 6.2
> +##
> +{ 'enum': 'SevGuestType',
> +  'data': [ 'sev', 'sev-snp' ],
> +  'if': 'TARGET_I386' }
> +
> +##
> +# @SevGuestInfo:
> +#
> +# Information specific to legacy SEV/SEV-ES guests.
> +#
> +# @policy: SEV policy value
> +#
> +# @handle: SEV firmware handle
> +#
> +# Since: 2.12
> +##
> +{ 'struct': 'SevGuestInfo',
> +  'data': { 'policy': 'uint32',
> +            'handle': 'uint32' },
> +  'if': 'TARGET_I386' }
> +
> +##
> +# @SevSnpGuestInfo:
> +#
> +# Information specific to SEV-SNP guests.
> +#
> +# @policy: SEV-SNP policy value
> +#
> +# Since: 6.2
> +##
> +{ 'struct': 'SevSnpGuestInfo',
> +  'data': { 'policy': 'uint64' },
> +  'if': 'TARGET_I386' }
> +
>  ##
>  # @SevInfo:
>  #
> @@ -94,25 +137,25 @@
>  #
>  # @build-id: SEV FW build id
>  #
> -# @policy: SEV policy value
> -#
>  # @state: SEV guest state
>  #
> -# @handle: SEV firmware handle
> +# @sev-type: Type of SEV guest being run
>  #
>  # Since: 2.12
>  ##
> -{ 'struct': 'SevInfo',
> -    'data': { 'enabled': 'bool',
> -              'api-major': 'uint8',
> -              'api-minor' : 'uint8',
> -              'build-id' : 'uint8',
> -              'policy' : 'uint32',
> -              'state' : 'SevState',
> -              'handle' : 'uint32'
> -            },
> -  'if': 'TARGET_I386'
> -}
> +{ 'union': 'SevInfo',
> +  'base': { 'enabled': 'bool',
> +            'api-major': 'uint8',
> +            'api-minor' : 'uint8',
> +            'build-id' : 'uint8',
> +            'state' : 'SevState',
> +            'sev-type' : 'SevGuestType' },
> +  'discriminator': 'sev-type',
> +  'data': {
> +      'sev': 'SevGuestInfo',
> +      'sev-snp': 'SevSnpGuestInfo' },
> +  'if': 'TARGET_I386' }
> +
>  
>  ##
>  # @query-sev:
> diff --git a/target/i386/monitor.c b/target/i386/monitor.c
> index 119211f0b0..85a8bc2bef 100644
> --- a/target/i386/monitor.c
> +++ b/target/i386/monitor.c
> @@ -692,20 +692,37 @@ void hmp_info_sev(Monitor *mon, const QDict *qdict)
>  {
>      SevInfo *info = sev_get_info();
>  
> -    if (info && info->enabled) {
> -        monitor_printf(mon, "handle: %d\n", info->handle);
> +    if (!info || !info->enabled) {
> +        monitor_printf(mon, "SEV is not enabled\n");
> +        goto out;
> +    }
> +
> +    if (sev_snp_enabled()) {
>          monitor_printf(mon, "state: %s\n", SevState_str(info->state));
>          monitor_printf(mon, "build: %d\n", info->build_id);
>          monitor_printf(mon, "api version: %d.%d\n",
>                         info->api_major, info->api_minor);
>          monitor_printf(mon, "debug: %s\n",
> -                       info->policy & SEV_POLICY_NODBG ? "off" : "on");
> -        monitor_printf(mon, "key-sharing: %s\n",
> -                       info->policy & SEV_POLICY_NOKS ? "off" : "on");
> +                       info->u.sev_snp.policy & SEV_SNP_POLICY_DBG ? "on"
> +                                                                   : "off");
> +        monitor_printf(mon, "SMT allowed: %s\n",
> +                       info->u.sev_snp.policy & SEV_SNP_POLICY_SMT ? "on"
> +                                                                   : "off");
> +        monitor_printf(mon, "SEV type: %s\n", SevGuestType_str(info->sev_type));
>      } else {
> -        monitor_printf(mon, "SEV is not enabled\n");
> +        monitor_printf(mon, "handle: %d\n", info->u.sev.handle);
> +        monitor_printf(mon, "state: %s\n", SevState_str(info->state));
> +        monitor_printf(mon, "build: %d\n", info->build_id);
> +        monitor_printf(mon, "api version: %d.%d\n",
> +                       info->api_major, info->api_minor);
> +        monitor_printf(mon, "debug: %s\n",
> +                       info->u.sev.policy & SEV_POLICY_NODBG ? "off" : "on");
> +        monitor_printf(mon, "key-sharing: %s\n",
> +                       info->u.sev.policy & SEV_POLICY_NOKS ? "off" : "on");
> +        monitor_printf(mon, "SEV type: %s\n", SevGuestType_str(info->sev_type));
>      }
>  
> +out:
>      qapi_free_SevInfo(info);
>  }
>  
> diff --git a/target/i386/sev.c b/target/i386/sev.c
> index 72a6146295..fac2755e68 100644
> --- a/target/i386/sev.c
> +++ b/target/i386/sev.c
> @@ -704,25 +704,27 @@ sev_get_info(void)
>  {
>      SevInfo *info;
>      SevCommonState *sev_common = SEV_COMMON(MACHINE(qdev_get_machine())->cgs);
> -    SevGuestState *sev_guest =
> -        (SevGuestState *)object_dynamic_cast(OBJECT(sev_common),
> -                                             TYPE_SEV_GUEST);
>  
>      info = g_new0(SevInfo, 1);
>      info->enabled = sev_enabled();
>  
>      if (info->enabled) {
> -        if (sev_guest) {
> -            info->handle = sev_guest->handle;
> -        }
>          info->api_major = sev_common->api_major;
>          info->api_minor = sev_common->api_minor;
>          info->build_id = sev_common->build_id;
>          info->state = sev_common->state;
> -        /* we only report the lower 32-bits of policy for SNP, ok for now... */
> -        info->policy =
> -            (uint32_t)object_property_get_uint(OBJECT(sev_common),
> -                                               "policy", NULL);
> +
> +        if (sev_snp_enabled()) {
> +            info->sev_type = SEV_GUEST_TYPE_SEV_SNP;
> +            info->u.sev_snp.policy =
> +                object_property_get_uint(OBJECT(sev_common), "policy", NULL);
> +        } else {
> +            info->sev_type = SEV_GUEST_TYPE_SEV;
> +            info->u.sev.handle = SEV_GUEST(sev_common)->handle;
> +            info->u.sev.policy =
> +                (uint32_t)object_property_get_uint(OBJECT(sev_common),
> +                                                   "policy", NULL);
> +        }
>      }
>  
>      return info;
> diff --git a/target/i386/sev_i386.h b/target/i386/sev_i386.h
> index e0e1a599be..948d8f1079 100644
> --- a/target/i386/sev_i386.h
> +++ b/target/i386/sev_i386.h
> @@ -28,6 +28,9 @@
>  #define SEV_POLICY_DOMAIN       0x10
>  #define SEV_POLICY_SEV          0x20
>  
> +#define SEV_SNP_POLICY_SMT      0x10000
> +#define SEV_SNP_POLICY_DBG      0x80000
> +
>  extern bool sev_es_enabled(void);
>  extern bool sev_snp_enabled(void);
>  extern uint64_t sev_get_me_mask(void);
> -- 
> 2.25.1
> 

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] 61+ messages in thread

* Re: [RFC PATCH v2 12/12] i386/sev: update query-sev QAPI format to handle SEV-SNP
@ 2021-09-03 15:27     ` Daniel P. Berrangé
  0 siblings, 0 replies; 61+ messages in thread
From: Daniel P. Berrangé @ 2021-09-03 15:27 UTC (permalink / raw)
  To: Michael Roth
  Cc: Tom Lendacky, Brijesh Singh, Eduardo Habkost, kvm,
	Michael S . Tsirkin, Connor Kuehl, Markus Armbruster,
	James Bottomley, qemu-devel, Eric Blake, Dr . David Alan Gilbert,
	Dov Murik, Paolo Bonzini, Philippe Mathieu-Daudé,
	David Gibson

On Thu, Aug 26, 2021 at 05:26:27PM -0500, Michael Roth wrote:
> Most of the current 'query-sev' command is relevant to both legacy
> SEV/SEV-ES guests and SEV-SNP guests, with 2 exceptions:
> 
>   - 'policy' is a 64-bit field for SEV-SNP, not 32-bit, and
>     the meaning of the bit positions has changed
>   - 'handle' is not relevant to SEV-SNP

If the host supports SEV-SNP guests, is it still possible for mgmt
app to create guests using the  "legacy" SEV/SEV-ES approach ? ie
is the hardware backwards compatible, or is it strictly required
to always create SEV-SNP guests when the hardware is capable ?

The code here seems to imply a non-backwards compatible approach,
mandating use of SEV-SNP guests on such capable kernel/hardware.

> To address this, this patch adds a new 'sev-type' field that can be
> used as a discriminator to select between SEV and SEV-SNP-specific
> fields/formats without breaking compatibility for existing management
> tools (so long as management tools that add support for launching
> SEV-SNP guest update their handling of query-sev appropriately).
> 
> The corresponding HMP command has also been fixed up similarly.
> 
> Signed-off-by: Michael Roth <michael.roth@amd.com>
> ---
>  qapi/misc-target.json  | 71 +++++++++++++++++++++++++++++++++---------
>  target/i386/monitor.c  | 29 +++++++++++++----
>  target/i386/sev.c      | 22 +++++++------
>  target/i386/sev_i386.h |  3 ++
>  4 files changed, 95 insertions(+), 30 deletions(-)
> 
> diff --git a/qapi/misc-target.json b/qapi/misc-target.json
> index 3b05ad3dbf..80f994ff9b 100644
> --- a/qapi/misc-target.json
> +++ b/qapi/misc-target.json
> @@ -81,6 +81,49 @@
>             'send-update', 'receive-update' ],
>    'if': 'TARGET_I386' }
>  
> +##
> +# @SevGuestType:
> +#
> +# An enumeration indicating the type of SEV guest being run.
> +#
> +# @sev:     The guest is a legacy SEV or SEV-ES guest.
> +# @sev-snp: The guest is an SEV-SNP guest.
> +#
> +# Since: 6.2
> +##
> +{ 'enum': 'SevGuestType',
> +  'data': [ 'sev', 'sev-snp' ],
> +  'if': 'TARGET_I386' }
> +
> +##
> +# @SevGuestInfo:
> +#
> +# Information specific to legacy SEV/SEV-ES guests.
> +#
> +# @policy: SEV policy value
> +#
> +# @handle: SEV firmware handle
> +#
> +# Since: 2.12
> +##
> +{ 'struct': 'SevGuestInfo',
> +  'data': { 'policy': 'uint32',
> +            'handle': 'uint32' },
> +  'if': 'TARGET_I386' }
> +
> +##
> +# @SevSnpGuestInfo:
> +#
> +# Information specific to SEV-SNP guests.
> +#
> +# @policy: SEV-SNP policy value
> +#
> +# Since: 6.2
> +##
> +{ 'struct': 'SevSnpGuestInfo',
> +  'data': { 'policy': 'uint64' },
> +  'if': 'TARGET_I386' }
> +
>  ##
>  # @SevInfo:
>  #
> @@ -94,25 +137,25 @@
>  #
>  # @build-id: SEV FW build id
>  #
> -# @policy: SEV policy value
> -#
>  # @state: SEV guest state
>  #
> -# @handle: SEV firmware handle
> +# @sev-type: Type of SEV guest being run
>  #
>  # Since: 2.12
>  ##
> -{ 'struct': 'SevInfo',
> -    'data': { 'enabled': 'bool',
> -              'api-major': 'uint8',
> -              'api-minor' : 'uint8',
> -              'build-id' : 'uint8',
> -              'policy' : 'uint32',
> -              'state' : 'SevState',
> -              'handle' : 'uint32'
> -            },
> -  'if': 'TARGET_I386'
> -}
> +{ 'union': 'SevInfo',
> +  'base': { 'enabled': 'bool',
> +            'api-major': 'uint8',
> +            'api-minor' : 'uint8',
> +            'build-id' : 'uint8',
> +            'state' : 'SevState',
> +            'sev-type' : 'SevGuestType' },
> +  'discriminator': 'sev-type',
> +  'data': {
> +      'sev': 'SevGuestInfo',
> +      'sev-snp': 'SevSnpGuestInfo' },
> +  'if': 'TARGET_I386' }
> +
>  
>  ##
>  # @query-sev:
> diff --git a/target/i386/monitor.c b/target/i386/monitor.c
> index 119211f0b0..85a8bc2bef 100644
> --- a/target/i386/monitor.c
> +++ b/target/i386/monitor.c
> @@ -692,20 +692,37 @@ void hmp_info_sev(Monitor *mon, const QDict *qdict)
>  {
>      SevInfo *info = sev_get_info();
>  
> -    if (info && info->enabled) {
> -        monitor_printf(mon, "handle: %d\n", info->handle);
> +    if (!info || !info->enabled) {
> +        monitor_printf(mon, "SEV is not enabled\n");
> +        goto out;
> +    }
> +
> +    if (sev_snp_enabled()) {
>          monitor_printf(mon, "state: %s\n", SevState_str(info->state));
>          monitor_printf(mon, "build: %d\n", info->build_id);
>          monitor_printf(mon, "api version: %d.%d\n",
>                         info->api_major, info->api_minor);
>          monitor_printf(mon, "debug: %s\n",
> -                       info->policy & SEV_POLICY_NODBG ? "off" : "on");
> -        monitor_printf(mon, "key-sharing: %s\n",
> -                       info->policy & SEV_POLICY_NOKS ? "off" : "on");
> +                       info->u.sev_snp.policy & SEV_SNP_POLICY_DBG ? "on"
> +                                                                   : "off");
> +        monitor_printf(mon, "SMT allowed: %s\n",
> +                       info->u.sev_snp.policy & SEV_SNP_POLICY_SMT ? "on"
> +                                                                   : "off");
> +        monitor_printf(mon, "SEV type: %s\n", SevGuestType_str(info->sev_type));
>      } else {
> -        monitor_printf(mon, "SEV is not enabled\n");
> +        monitor_printf(mon, "handle: %d\n", info->u.sev.handle);
> +        monitor_printf(mon, "state: %s\n", SevState_str(info->state));
> +        monitor_printf(mon, "build: %d\n", info->build_id);
> +        monitor_printf(mon, "api version: %d.%d\n",
> +                       info->api_major, info->api_minor);
> +        monitor_printf(mon, "debug: %s\n",
> +                       info->u.sev.policy & SEV_POLICY_NODBG ? "off" : "on");
> +        monitor_printf(mon, "key-sharing: %s\n",
> +                       info->u.sev.policy & SEV_POLICY_NOKS ? "off" : "on");
> +        monitor_printf(mon, "SEV type: %s\n", SevGuestType_str(info->sev_type));
>      }
>  
> +out:
>      qapi_free_SevInfo(info);
>  }
>  
> diff --git a/target/i386/sev.c b/target/i386/sev.c
> index 72a6146295..fac2755e68 100644
> --- a/target/i386/sev.c
> +++ b/target/i386/sev.c
> @@ -704,25 +704,27 @@ sev_get_info(void)
>  {
>      SevInfo *info;
>      SevCommonState *sev_common = SEV_COMMON(MACHINE(qdev_get_machine())->cgs);
> -    SevGuestState *sev_guest =
> -        (SevGuestState *)object_dynamic_cast(OBJECT(sev_common),
> -                                             TYPE_SEV_GUEST);
>  
>      info = g_new0(SevInfo, 1);
>      info->enabled = sev_enabled();
>  
>      if (info->enabled) {
> -        if (sev_guest) {
> -            info->handle = sev_guest->handle;
> -        }
>          info->api_major = sev_common->api_major;
>          info->api_minor = sev_common->api_minor;
>          info->build_id = sev_common->build_id;
>          info->state = sev_common->state;
> -        /* we only report the lower 32-bits of policy for SNP, ok for now... */
> -        info->policy =
> -            (uint32_t)object_property_get_uint(OBJECT(sev_common),
> -                                               "policy", NULL);
> +
> +        if (sev_snp_enabled()) {
> +            info->sev_type = SEV_GUEST_TYPE_SEV_SNP;
> +            info->u.sev_snp.policy =
> +                object_property_get_uint(OBJECT(sev_common), "policy", NULL);
> +        } else {
> +            info->sev_type = SEV_GUEST_TYPE_SEV;
> +            info->u.sev.handle = SEV_GUEST(sev_common)->handle;
> +            info->u.sev.policy =
> +                (uint32_t)object_property_get_uint(OBJECT(sev_common),
> +                                                   "policy", NULL);
> +        }
>      }
>  
>      return info;
> diff --git a/target/i386/sev_i386.h b/target/i386/sev_i386.h
> index e0e1a599be..948d8f1079 100644
> --- a/target/i386/sev_i386.h
> +++ b/target/i386/sev_i386.h
> @@ -28,6 +28,9 @@
>  #define SEV_POLICY_DOMAIN       0x10
>  #define SEV_POLICY_SEV          0x20
>  
> +#define SEV_SNP_POLICY_SMT      0x10000
> +#define SEV_SNP_POLICY_DBG      0x80000
> +
>  extern bool sev_es_enabled(void);
>  extern bool sev_snp_enabled(void);
>  extern uint64_t sev_get_me_mask(void);
> -- 
> 2.25.1
> 

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] 61+ messages in thread

* Re: [RFC PATCH v2 12/12] i386/sev: update query-sev QAPI format to handle SEV-SNP
  2021-09-03 15:13     ` Michael Roth
@ 2021-09-03 15:30         ` Daniel P. Berrangé
  0 siblings, 0 replies; 61+ messages in thread
From: Daniel P. Berrangé @ 2021-09-03 15:30 UTC (permalink / raw)
  To: Michael Roth
  Cc: Markus Armbruster, Tom Lendacky, Eduardo Habkost, kvm,
	Michael S . Tsirkin, Connor Kuehl, Eric Blake, James Bottomley,
	qemu-devel, Dr . David Alan Gilbert, Dov Murik, Brijesh Singh,
	Paolo Bonzini, Philippe Mathieu-Daudé,
	David Gibson

On Fri, Sep 03, 2021 at 10:13:16AM -0500, Michael Roth wrote:
> On Wed, Sep 01, 2021 at 04:14:10PM +0200, Markus Armbruster wrote:
> > Michael Roth <michael.roth@amd.com> writes:
> > 
> > > Most of the current 'query-sev' command is relevant to both legacy
> > > SEV/SEV-ES guests and SEV-SNP guests, with 2 exceptions:
> > >
> > >   - 'policy' is a 64-bit field for SEV-SNP, not 32-bit, and
> > >     the meaning of the bit positions has changed
> > >   - 'handle' is not relevant to SEV-SNP
> > >
> > > To address this, this patch adds a new 'sev-type' field that can be
> > > used as a discriminator to select between SEV and SEV-SNP-specific
> > > fields/formats without breaking compatibility for existing management
> > > tools (so long as management tools that add support for launching
> > > SEV-SNP guest update their handling of query-sev appropriately).
> > 
> > Technically a compatibility break: query-sev can now return an object
> > that whose member @policy has different meaning, and also lacks @handle.
> > 
> > Matrix:
> > 
> >                             Old mgmt app    New mgmt app
> >     Old QEMU, SEV/SEV-ES       good            good(1)
> >     New QEMU, SEV/SEV-ES       good(2)         good
> >     New QEMU, SEV-SNP           bad(3)         good
> > 
> > Notes:
> > 
> > (1) As long as the management application can cope with absent member
> > @sev-type.
> > 
> > (2) As long as the management application ignores unknown member
> > @sev-type.
> > 
> > (3) Management application may choke on missing member @handle, or
> > worse, misinterpret member @policy.  Can only happen when something
> > other than the management application created the SEV-SNP guest (or the
> > user somehow made the management application create one even though it
> > doesn't know how, say with CLI option passthrough, but that's always
> > fragile, and I wouldn't worry about it here).
> > 
> > I think (1) and (2) are reasonable.  (3) is an issue for management
> > applications that support attaching to existing guests.  Thoughts?
> 
> Hmm... yah I hadn't considering 'old mgmt' trying to interact with a SNP
> guest started through some other means.
> 
> Don't really see an alternative other than introducing a new
> 'query-sev-snp', but that would still leave 'old mgmt' broken, since
> it might still call do weird stuff like try to interpret the SNP policy
> as an SEV/SEV-ES and end up with some very unexpected results. So if I
> did go this route, I would need to have QMP begin returning an error if
> query-sev is run against an SNP guest. But currently for non-SEV guests
> it already does:
> 
>   error_setg(errp, "SEV feature is not available")
> 
> so 'old mgmt' should be able to handle the error just fine.
> 
> Would that approach be reasonable?

This ties into the question I've just sent in my other mail.

If the hardware strictly requires that guest are created in SEV-SNP
mode only, and will not support SEV/SEV-ES mode, then we need to
ensure "query-sev" reports the feature as not-available, so that
existing mgmt apps don't try to use SEV/SEV-ES.

If the SEV-SNP hardware is functionally back-compatible with a gues
configured in SEV/SEV-ES mode, then we souldn't need a new command,
just augment th eexisting command with additional field(s), to
indicate existance of SEV-SNP features.

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] 61+ messages in thread

* Re: [RFC PATCH v2 12/12] i386/sev: update query-sev QAPI format to handle SEV-SNP
@ 2021-09-03 15:30         ` Daniel P. Berrangé
  0 siblings, 0 replies; 61+ messages in thread
From: Daniel P. Berrangé @ 2021-09-03 15:30 UTC (permalink / raw)
  To: Michael Roth
  Cc: Tom Lendacky, Brijesh Singh, Eduardo Habkost, kvm,
	Michael S . Tsirkin, Connor Kuehl, Philippe Mathieu-Daudé,
	James Bottomley, Markus Armbruster, qemu-devel, Dov Murik,
	Paolo Bonzini, Eric Blake, Dr . David Alan Gilbert, David Gibson

On Fri, Sep 03, 2021 at 10:13:16AM -0500, Michael Roth wrote:
> On Wed, Sep 01, 2021 at 04:14:10PM +0200, Markus Armbruster wrote:
> > Michael Roth <michael.roth@amd.com> writes:
> > 
> > > Most of the current 'query-sev' command is relevant to both legacy
> > > SEV/SEV-ES guests and SEV-SNP guests, with 2 exceptions:
> > >
> > >   - 'policy' is a 64-bit field for SEV-SNP, not 32-bit, and
> > >     the meaning of the bit positions has changed
> > >   - 'handle' is not relevant to SEV-SNP
> > >
> > > To address this, this patch adds a new 'sev-type' field that can be
> > > used as a discriminator to select between SEV and SEV-SNP-specific
> > > fields/formats without breaking compatibility for existing management
> > > tools (so long as management tools that add support for launching
> > > SEV-SNP guest update their handling of query-sev appropriately).
> > 
> > Technically a compatibility break: query-sev can now return an object
> > that whose member @policy has different meaning, and also lacks @handle.
> > 
> > Matrix:
> > 
> >                             Old mgmt app    New mgmt app
> >     Old QEMU, SEV/SEV-ES       good            good(1)
> >     New QEMU, SEV/SEV-ES       good(2)         good
> >     New QEMU, SEV-SNP           bad(3)         good
> > 
> > Notes:
> > 
> > (1) As long as the management application can cope with absent member
> > @sev-type.
> > 
> > (2) As long as the management application ignores unknown member
> > @sev-type.
> > 
> > (3) Management application may choke on missing member @handle, or
> > worse, misinterpret member @policy.  Can only happen when something
> > other than the management application created the SEV-SNP guest (or the
> > user somehow made the management application create one even though it
> > doesn't know how, say with CLI option passthrough, but that's always
> > fragile, and I wouldn't worry about it here).
> > 
> > I think (1) and (2) are reasonable.  (3) is an issue for management
> > applications that support attaching to existing guests.  Thoughts?
> 
> Hmm... yah I hadn't considering 'old mgmt' trying to interact with a SNP
> guest started through some other means.
> 
> Don't really see an alternative other than introducing a new
> 'query-sev-snp', but that would still leave 'old mgmt' broken, since
> it might still call do weird stuff like try to interpret the SNP policy
> as an SEV/SEV-ES and end up with some very unexpected results. So if I
> did go this route, I would need to have QMP begin returning an error if
> query-sev is run against an SNP guest. But currently for non-SEV guests
> it already does:
> 
>   error_setg(errp, "SEV feature is not available")
> 
> so 'old mgmt' should be able to handle the error just fine.
> 
> Would that approach be reasonable?

This ties into the question I've just sent in my other mail.

If the hardware strictly requires that guest are created in SEV-SNP
mode only, and will not support SEV/SEV-ES mode, then we need to
ensure "query-sev" reports the feature as not-available, so that
existing mgmt apps don't try to use SEV/SEV-ES.

If the SEV-SNP hardware is functionally back-compatible with a gues
configured in SEV/SEV-ES mode, then we souldn't need a new command,
just augment th eexisting command with additional field(s), to
indicate existance of SEV-SNP features.

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] 61+ messages in thread

* Re: [RFC PATCH v2 12/12] i386/sev: update query-sev QAPI format to handle SEV-SNP
  2021-09-03 15:30         ` Daniel P. Berrangé
@ 2021-09-03 15:43           ` Michael Roth via
  -1 siblings, 0 replies; 61+ messages in thread
From: Michael Roth @ 2021-09-03 15:43 UTC (permalink / raw)
  To: Daniel P. Berrangé
  Cc: Markus Armbruster, Tom Lendacky, Eduardo Habkost, kvm,
	Michael S . Tsirkin, Connor Kuehl, Eric Blake, James Bottomley,
	qemu-devel, Dr . David Alan Gilbert, Dov Murik, Brijesh Singh,
	Paolo Bonzini, Philippe Mathieu-Daudé,
	David Gibson

On Fri, Sep 03, 2021 at 04:30:48PM +0100, Daniel P. Berrangé wrote:
> On Fri, Sep 03, 2021 at 10:13:16AM -0500, Michael Roth wrote:
> > On Wed, Sep 01, 2021 at 04:14:10PM +0200, Markus Armbruster wrote:
> > > Michael Roth <michael.roth@amd.com> writes:
> > > 
> > > > Most of the current 'query-sev' command is relevant to both legacy
> > > > SEV/SEV-ES guests and SEV-SNP guests, with 2 exceptions:
> > > >
> > > >   - 'policy' is a 64-bit field for SEV-SNP, not 32-bit, and
> > > >     the meaning of the bit positions has changed
> > > >   - 'handle' is not relevant to SEV-SNP
> > > >
> > > > To address this, this patch adds a new 'sev-type' field that can be
> > > > used as a discriminator to select between SEV and SEV-SNP-specific
> > > > fields/formats without breaking compatibility for existing management
> > > > tools (so long as management tools that add support for launching
> > > > SEV-SNP guest update their handling of query-sev appropriately).
> > > 
> > > Technically a compatibility break: query-sev can now return an object
> > > that whose member @policy has different meaning, and also lacks @handle.
> > > 
> > > Matrix:
> > > 
> > >                             Old mgmt app    New mgmt app
> > >     Old QEMU, SEV/SEV-ES       good            good(1)
> > >     New QEMU, SEV/SEV-ES       good(2)         good
> > >     New QEMU, SEV-SNP           bad(3)         good
> > > 
> > > Notes:
> > > 
> > > (1) As long as the management application can cope with absent member
> > > @sev-type.
> > > 
> > > (2) As long as the management application ignores unknown member
> > > @sev-type.
> > > 
> > > (3) Management application may choke on missing member @handle, or
> > > worse, misinterpret member @policy.  Can only happen when something
> > > other than the management application created the SEV-SNP guest (or the
> > > user somehow made the management application create one even though it
> > > doesn't know how, say with CLI option passthrough, but that's always
> > > fragile, and I wouldn't worry about it here).
> > > 
> > > I think (1) and (2) are reasonable.  (3) is an issue for management
> > > applications that support attaching to existing guests.  Thoughts?
> > 
> > Hmm... yah I hadn't considering 'old mgmt' trying to interact with a SNP
> > guest started through some other means.
> > 
> > Don't really see an alternative other than introducing a new
> > 'query-sev-snp', but that would still leave 'old mgmt' broken, since
> > it might still call do weird stuff like try to interpret the SNP policy
> > as an SEV/SEV-ES and end up with some very unexpected results. So if I
> > did go this route, I would need to have QMP begin returning an error if
> > query-sev is run against an SNP guest. But currently for non-SEV guests
> > it already does:
> > 
> >   error_setg(errp, "SEV feature is not available")
> > 
> > so 'old mgmt' should be able to handle the error just fine.
> > 
> > Would that approach be reasonable?
> 
> This ties into the question I've just sent in my other mail.
> 
> If the hardware strictly requires that guest are created in SEV-SNP
> mode only, and will not support SEV/SEV-ES mode, then we need to
> ensure "query-sev" reports the feature as not-available, so that
> existing mgmt apps don't try to use SEV/SEV-ES.

An SEV-SNP-capable host can run both 'legacy' SEV/SEV-ES, as well as
SEV-SNP guests, at the same time. But as far as QEMU goes, we need
to specify one or the other explicitly at launch time, via existing
'sev-guest', or the new 'sev-snp-guest' ConfidentialGuestSupport type.

> 
> If the SEV-SNP hardware is functionally back-compatible with a gues
> configured in SEV/SEV-ES mode, then we souldn't need a new command,
> just augment th eexisting command with additional field(s), to
> indicate existance of SEV-SNP features.

query-sev-info provides information specific to the guest instance,
like the configured policy. Are you thinking of query-sev-capabilities,
which reports some host-wide information and should indeed remain
available for either case. (and maybe should also be updated to report
on SEV-SNP availability for the host?)

> 
> Regards,
> Daniel
> -- 
> |: https://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%2Fberrange.com%2F&amp;data=04%7C01%7Cmichael.roth%40amd.com%7C86e4ceb7f55f4cf839e708d96eefd768%7C3dd8961fe4884e608e11a82d994e183d%7C0%7C0%7C637662798671249591%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&amp;sdata=Fo%2FCcCEG7OrpVNj2ij2CzYJCMFXs30YUnRaClz17Okc%3D&amp;reserved=0      -o-    https://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%2Fwww.flickr.com%2Fphotos%2Fdberrange&amp;data=04%7C01%7Cmichael.roth%40amd.com%7C86e4ceb7f55f4cf839e708d96eefd768%7C3dd8961fe4884e608e11a82d994e183d%7C0%7C0%7C637662798671249591%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&amp;sdata=ypMyoiFFTmgWnDD3UwJrKIcHGzDaKQ8nXnFASw7gyRE%3D&amp;reserved=0 :|
> |: https://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%2Flibvirt.org%2F&amp;data=04%7C01%7Cmichael.roth%40amd.com%7C86e4ceb7f55f4cf839e708d96eefd768%7C3dd8961fe4884e608e11a82d994e183d%7C0%7C0%7C637662798671249591%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&amp;sdata=viWBuUPauLi41pEKT143wh0Ds%2FwJqIG%2BDfraIO5uxNE%3D&amp;reserved=0         -o-            https://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%2Ffstop138.berrange.com%2F&amp;data=04%7C01%7Cmichael.roth%40amd.com%7C86e4ceb7f55f4cf839e708d96eefd768%7C3dd8961fe4884e608e11a82d994e183d%7C0%7C0%7C637662798671249591%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&amp;sdata=H%2BTFPox5kDO4osMWicvwUrb8PvNfMBPLwCWEiEfcIOw%3D&amp;reserved=0 :|
> |: https://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%2Fentangle-photo.org%2F&amp;data=04%7C01%7Cmichael.roth%40amd.com%7C86e4ceb7f55f4cf839e708d96eefd768%7C3dd8961fe4884e608e11a82d994e183d%7C0%7C0%7C637662798671259556%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&amp;sdata=9mp0c7dJ07EIdYYNZ73DQ6ax%2Bw0ORKTpScI6p5gNpVo%3D&amp;reserved=0    -o-    https://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%2Fwww.instagram.com%2Fdberrange&amp;data=04%7C01%7Cmichael.roth%40amd.com%7C86e4ceb7f55f4cf839e708d96eefd768%7C3dd8961fe4884e608e11a82d994e183d%7C0%7C0%7C637662798671259556%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&amp;sdata=%2FL1f9LxzAqXxVNbUhWa4w2Sibb6f0vLcRzaM2%2F9NY90%3D&amp;reserved=0 :|
> 

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

* Re: [RFC PATCH v2 12/12] i386/sev: update query-sev QAPI format to handle SEV-SNP
@ 2021-09-03 15:43           ` Michael Roth via
  0 siblings, 0 replies; 61+ messages in thread
From: Michael Roth via @ 2021-09-03 15:43 UTC (permalink / raw)
  To: Daniel P. Berrangé
  Cc: Markus Armbruster, Tom Lendacky, Eduardo Habkost, kvm,
	Michael S . Tsirkin, Connor Kuehl, Eric Blake, James Bottomley,
	qemu-devel, Dr . David Alan Gilbert, Dov Murik, Brijesh Singh,
	Paolo Bonzini, Philippe Mathieu-Daudé,
	David Gibson

On Fri, Sep 03, 2021 at 04:30:48PM +0100, Daniel P. Berrangé wrote:
> On Fri, Sep 03, 2021 at 10:13:16AM -0500, Michael Roth wrote:
> > On Wed, Sep 01, 2021 at 04:14:10PM +0200, Markus Armbruster wrote:
> > > Michael Roth <michael.roth@amd.com> writes:
> > > 
> > > > Most of the current 'query-sev' command is relevant to both legacy
> > > > SEV/SEV-ES guests and SEV-SNP guests, with 2 exceptions:
> > > >
> > > >   - 'policy' is a 64-bit field for SEV-SNP, not 32-bit, and
> > > >     the meaning of the bit positions has changed
> > > >   - 'handle' is not relevant to SEV-SNP
> > > >
> > > > To address this, this patch adds a new 'sev-type' field that can be
> > > > used as a discriminator to select between SEV and SEV-SNP-specific
> > > > fields/formats without breaking compatibility for existing management
> > > > tools (so long as management tools that add support for launching
> > > > SEV-SNP guest update their handling of query-sev appropriately).
> > > 
> > > Technically a compatibility break: query-sev can now return an object
> > > that whose member @policy has different meaning, and also lacks @handle.
> > > 
> > > Matrix:
> > > 
> > >                             Old mgmt app    New mgmt app
> > >     Old QEMU, SEV/SEV-ES       good            good(1)
> > >     New QEMU, SEV/SEV-ES       good(2)         good
> > >     New QEMU, SEV-SNP           bad(3)         good
> > > 
> > > Notes:
> > > 
> > > (1) As long as the management application can cope with absent member
> > > @sev-type.
> > > 
> > > (2) As long as the management application ignores unknown member
> > > @sev-type.
> > > 
> > > (3) Management application may choke on missing member @handle, or
> > > worse, misinterpret member @policy.  Can only happen when something
> > > other than the management application created the SEV-SNP guest (or the
> > > user somehow made the management application create one even though it
> > > doesn't know how, say with CLI option passthrough, but that's always
> > > fragile, and I wouldn't worry about it here).
> > > 
> > > I think (1) and (2) are reasonable.  (3) is an issue for management
> > > applications that support attaching to existing guests.  Thoughts?
> > 
> > Hmm... yah I hadn't considering 'old mgmt' trying to interact with a SNP
> > guest started through some other means.
> > 
> > Don't really see an alternative other than introducing a new
> > 'query-sev-snp', but that would still leave 'old mgmt' broken, since
> > it might still call do weird stuff like try to interpret the SNP policy
> > as an SEV/SEV-ES and end up with some very unexpected results. So if I
> > did go this route, I would need to have QMP begin returning an error if
> > query-sev is run against an SNP guest. But currently for non-SEV guests
> > it already does:
> > 
> >   error_setg(errp, "SEV feature is not available")
> > 
> > so 'old mgmt' should be able to handle the error just fine.
> > 
> > Would that approach be reasonable?
> 
> This ties into the question I've just sent in my other mail.
> 
> If the hardware strictly requires that guest are created in SEV-SNP
> mode only, and will not support SEV/SEV-ES mode, then we need to
> ensure "query-sev" reports the feature as not-available, so that
> existing mgmt apps don't try to use SEV/SEV-ES.

An SEV-SNP-capable host can run both 'legacy' SEV/SEV-ES, as well as
SEV-SNP guests, at the same time. But as far as QEMU goes, we need
to specify one or the other explicitly at launch time, via existing
'sev-guest', or the new 'sev-snp-guest' ConfidentialGuestSupport type.

> 
> If the SEV-SNP hardware is functionally back-compatible with a gues
> configured in SEV/SEV-ES mode, then we souldn't need a new command,
> just augment th eexisting command with additional field(s), to
> indicate existance of SEV-SNP features.

query-sev-info provides information specific to the guest instance,
like the configured policy. Are you thinking of query-sev-capabilities,
which reports some host-wide information and should indeed remain
available for either case. (and maybe should also be updated to report
on SEV-SNP availability for the host?)

> 
> Regards,
> Daniel
> -- 
> |: https://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%2Fberrange.com%2F&amp;data=04%7C01%7Cmichael.roth%40amd.com%7C86e4ceb7f55f4cf839e708d96eefd768%7C3dd8961fe4884e608e11a82d994e183d%7C0%7C0%7C637662798671249591%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&amp;sdata=Fo%2FCcCEG7OrpVNj2ij2CzYJCMFXs30YUnRaClz17Okc%3D&amp;reserved=0      -o-    https://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%2Fwww.flickr.com%2Fphotos%2Fdberrange&amp;data=04%7C01%7Cmichael.roth%40amd.com%7C86e4ceb7f55f4cf839e708d96eefd768%7C3dd8961fe4884e608e11a82d994e183d%7C0%7C0%7C637662798671249591%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&amp;sdata=ypMyoiFFTmgWnDD3UwJrKIcHGzDaKQ8nXnFASw7gyRE%3D&amp;reserved=0 :|
> |: https://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%2Flibvirt.org%2F&amp;data=04%7C01%7Cmichael.roth%40amd.com%7C86e4ceb7f55f4cf839e708d96eefd768%7C3dd8961fe4884e608e11a82d994e183d%7C0%7C0%7C637662798671249591%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&amp;sdata=viWBuUPauLi41pEKT143wh0Ds%2FwJqIG%2BDfraIO5uxNE%3D&amp;reserved=0         -o-            https://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%2Ffstop138.berrange.com%2F&amp;data=04%7C01%7Cmichael.roth%40amd.com%7C86e4ceb7f55f4cf839e708d96eefd768%7C3dd8961fe4884e608e11a82d994e183d%7C0%7C0%7C637662798671249591%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&amp;sdata=H%2BTFPox5kDO4osMWicvwUrb8PvNfMBPLwCWEiEfcIOw%3D&amp;reserved=0 :|
> |: https://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%2Fentangle-photo.org%2F&amp;data=04%7C01%7Cmichael.roth%40amd.com%7C86e4ceb7f55f4cf839e708d96eefd768%7C3dd8961fe4884e608e11a82d994e183d%7C0%7C0%7C637662798671259556%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&amp;sdata=9mp0c7dJ07EIdYYNZ73DQ6ax%2Bw0ORKTpScI6p5gNpVo%3D&amp;reserved=0    -o-    https://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%2Fwww.instagram.com%2Fdberrange&amp;data=04%7C01%7Cmichael.roth%40amd.com%7C86e4ceb7f55f4cf839e708d96eefd768%7C3dd8961fe4884e608e11a82d994e183d%7C0%7C0%7C637662798671259556%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&amp;sdata=%2FL1f9LxzAqXxVNbUhWa4w2Sibb6f0vLcRzaM2%2F9NY90%3D&amp;reserved=0 :|
> 


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

* Re: [RFC PATCH v2 12/12] i386/sev: update query-sev QAPI format to handle SEV-SNP
  2021-09-03 15:43           ` Michael Roth via
@ 2021-09-03 15:58             ` Daniel P. Berrangé
  -1 siblings, 0 replies; 61+ messages in thread
From: Daniel P. Berrangé @ 2021-09-03 15:58 UTC (permalink / raw)
  To: Michael Roth
  Cc: Markus Armbruster, Tom Lendacky, Eduardo Habkost, kvm,
	Michael S . Tsirkin, Connor Kuehl, Eric Blake, James Bottomley,
	qemu-devel, Dr . David Alan Gilbert, Dov Murik, Brijesh Singh,
	Paolo Bonzini, Philippe Mathieu-Daudé,
	David Gibson

On Fri, Sep 03, 2021 at 10:43:19AM -0500, Michael Roth wrote:
> On Fri, Sep 03, 2021 at 04:30:48PM +0100, Daniel P. Berrangé wrote:
> > On Fri, Sep 03, 2021 at 10:13:16AM -0500, Michael Roth wrote:
> > > On Wed, Sep 01, 2021 at 04:14:10PM +0200, Markus Armbruster wrote:
> > > > Michael Roth <michael.roth@amd.com> writes:
> > > > 
> > > > > Most of the current 'query-sev' command is relevant to both legacy
> > > > > SEV/SEV-ES guests and SEV-SNP guests, with 2 exceptions:
> > > > >
> > > > >   - 'policy' is a 64-bit field for SEV-SNP, not 32-bit, and
> > > > >     the meaning of the bit positions has changed
> > > > >   - 'handle' is not relevant to SEV-SNP
> > > > >
> > > > > To address this, this patch adds a new 'sev-type' field that can be
> > > > > used as a discriminator to select between SEV and SEV-SNP-specific
> > > > > fields/formats without breaking compatibility for existing management
> > > > > tools (so long as management tools that add support for launching
> > > > > SEV-SNP guest update their handling of query-sev appropriately).
> > > > 
> > > > Technically a compatibility break: query-sev can now return an object
> > > > that whose member @policy has different meaning, and also lacks @handle.
> > > > 
> > > > Matrix:
> > > > 
> > > >                             Old mgmt app    New mgmt app
> > > >     Old QEMU, SEV/SEV-ES       good            good(1)
> > > >     New QEMU, SEV/SEV-ES       good(2)         good
> > > >     New QEMU, SEV-SNP           bad(3)         good
> > > > 
> > > > Notes:
> > > > 
> > > > (1) As long as the management application can cope with absent member
> > > > @sev-type.
> > > > 
> > > > (2) As long as the management application ignores unknown member
> > > > @sev-type.
> > > > 
> > > > (3) Management application may choke on missing member @handle, or
> > > > worse, misinterpret member @policy.  Can only happen when something
> > > > other than the management application created the SEV-SNP guest (or the
> > > > user somehow made the management application create one even though it
> > > > doesn't know how, say with CLI option passthrough, but that's always
> > > > fragile, and I wouldn't worry about it here).
> > > > 
> > > > I think (1) and (2) are reasonable.  (3) is an issue for management
> > > > applications that support attaching to existing guests.  Thoughts?
> > > 
> > > Hmm... yah I hadn't considering 'old mgmt' trying to interact with a SNP
> > > guest started through some other means.
> > > 
> > > Don't really see an alternative other than introducing a new
> > > 'query-sev-snp', but that would still leave 'old mgmt' broken, since
> > > it might still call do weird stuff like try to interpret the SNP policy
> > > as an SEV/SEV-ES and end up with some very unexpected results. So if I
> > > did go this route, I would need to have QMP begin returning an error if
> > > query-sev is run against an SNP guest. But currently for non-SEV guests
> > > it already does:
> > > 
> > >   error_setg(errp, "SEV feature is not available")
> > > 
> > > so 'old mgmt' should be able to handle the error just fine.
> > > 
> > > Would that approach be reasonable?
> > 
> > This ties into the question I've just sent in my other mail.
> > 
> > If the hardware strictly requires that guest are created in SEV-SNP
> > mode only, and will not support SEV/SEV-ES mode, then we need to
> > ensure "query-sev" reports the feature as not-available, so that
> > existing mgmt apps don't try to use SEV/SEV-ES.
> 
> An SEV-SNP-capable host can run both 'legacy' SEV/SEV-ES, as well as
> SEV-SNP guests, at the same time. But as far as QEMU goes, we need
> to specify one or the other explicitly at launch time, via existing
> 'sev-guest', or the new 'sev-snp-guest' ConfidentialGuestSupport type.
> 
> > 
> > If the SEV-SNP hardware is functionally back-compatible with a gues
> > configured in SEV/SEV-ES mode, then we souldn't need a new command,
> > just augment th eexisting command with additional field(s), to
> > indicate existance of SEV-SNP features.
> 
> query-sev-info provides information specific to the guest instance,
> like the configured policy. Are you thinking of query-sev-capabilities,
> which reports some host-wide information and should indeed remain
> available for either case. (and maybe should also be updated to report
> on SEV-SNP availability for the host?)

Oh right, yes, I am getting confused with query-sev-capabilities.

I think this means everything is OK with your query-sev-info
changes proposed here, I'll reply direct to Markus' point though.

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] 61+ messages in thread

* Re: [RFC PATCH v2 12/12] i386/sev: update query-sev QAPI format to handle SEV-SNP
@ 2021-09-03 15:58             ` Daniel P. Berrangé
  0 siblings, 0 replies; 61+ messages in thread
From: Daniel P. Berrangé @ 2021-09-03 15:58 UTC (permalink / raw)
  To: Michael Roth
  Cc: Tom Lendacky, Brijesh Singh, Eduardo Habkost, kvm,
	Michael S . Tsirkin, Connor Kuehl, Philippe Mathieu-Daudé,
	James Bottomley, Markus Armbruster, qemu-devel, Dov Murik,
	Paolo Bonzini, Eric Blake, Dr . David Alan Gilbert, David Gibson

On Fri, Sep 03, 2021 at 10:43:19AM -0500, Michael Roth wrote:
> On Fri, Sep 03, 2021 at 04:30:48PM +0100, Daniel P. Berrangé wrote:
> > On Fri, Sep 03, 2021 at 10:13:16AM -0500, Michael Roth wrote:
> > > On Wed, Sep 01, 2021 at 04:14:10PM +0200, Markus Armbruster wrote:
> > > > Michael Roth <michael.roth@amd.com> writes:
> > > > 
> > > > > Most of the current 'query-sev' command is relevant to both legacy
> > > > > SEV/SEV-ES guests and SEV-SNP guests, with 2 exceptions:
> > > > >
> > > > >   - 'policy' is a 64-bit field for SEV-SNP, not 32-bit, and
> > > > >     the meaning of the bit positions has changed
> > > > >   - 'handle' is not relevant to SEV-SNP
> > > > >
> > > > > To address this, this patch adds a new 'sev-type' field that can be
> > > > > used as a discriminator to select between SEV and SEV-SNP-specific
> > > > > fields/formats without breaking compatibility for existing management
> > > > > tools (so long as management tools that add support for launching
> > > > > SEV-SNP guest update their handling of query-sev appropriately).
> > > > 
> > > > Technically a compatibility break: query-sev can now return an object
> > > > that whose member @policy has different meaning, and also lacks @handle.
> > > > 
> > > > Matrix:
> > > > 
> > > >                             Old mgmt app    New mgmt app
> > > >     Old QEMU, SEV/SEV-ES       good            good(1)
> > > >     New QEMU, SEV/SEV-ES       good(2)         good
> > > >     New QEMU, SEV-SNP           bad(3)         good
> > > > 
> > > > Notes:
> > > > 
> > > > (1) As long as the management application can cope with absent member
> > > > @sev-type.
> > > > 
> > > > (2) As long as the management application ignores unknown member
> > > > @sev-type.
> > > > 
> > > > (3) Management application may choke on missing member @handle, or
> > > > worse, misinterpret member @policy.  Can only happen when something
> > > > other than the management application created the SEV-SNP guest (or the
> > > > user somehow made the management application create one even though it
> > > > doesn't know how, say with CLI option passthrough, but that's always
> > > > fragile, and I wouldn't worry about it here).
> > > > 
> > > > I think (1) and (2) are reasonable.  (3) is an issue for management
> > > > applications that support attaching to existing guests.  Thoughts?
> > > 
> > > Hmm... yah I hadn't considering 'old mgmt' trying to interact with a SNP
> > > guest started through some other means.
> > > 
> > > Don't really see an alternative other than introducing a new
> > > 'query-sev-snp', but that would still leave 'old mgmt' broken, since
> > > it might still call do weird stuff like try to interpret the SNP policy
> > > as an SEV/SEV-ES and end up with some very unexpected results. So if I
> > > did go this route, I would need to have QMP begin returning an error if
> > > query-sev is run against an SNP guest. But currently for non-SEV guests
> > > it already does:
> > > 
> > >   error_setg(errp, "SEV feature is not available")
> > > 
> > > so 'old mgmt' should be able to handle the error just fine.
> > > 
> > > Would that approach be reasonable?
> > 
> > This ties into the question I've just sent in my other mail.
> > 
> > If the hardware strictly requires that guest are created in SEV-SNP
> > mode only, and will not support SEV/SEV-ES mode, then we need to
> > ensure "query-sev" reports the feature as not-available, so that
> > existing mgmt apps don't try to use SEV/SEV-ES.
> 
> An SEV-SNP-capable host can run both 'legacy' SEV/SEV-ES, as well as
> SEV-SNP guests, at the same time. But as far as QEMU goes, we need
> to specify one or the other explicitly at launch time, via existing
> 'sev-guest', or the new 'sev-snp-guest' ConfidentialGuestSupport type.
> 
> > 
> > If the SEV-SNP hardware is functionally back-compatible with a gues
> > configured in SEV/SEV-ES mode, then we souldn't need a new command,
> > just augment th eexisting command with additional field(s), to
> > indicate existance of SEV-SNP features.
> 
> query-sev-info provides information specific to the guest instance,
> like the configured policy. Are you thinking of query-sev-capabilities,
> which reports some host-wide information and should indeed remain
> available for either case. (and maybe should also be updated to report
> on SEV-SNP availability for the host?)

Oh right, yes, I am getting confused with query-sev-capabilities.

I think this means everything is OK with your query-sev-info
changes proposed here, I'll reply direct to Markus' point though.

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] 61+ messages in thread

* Re: [RFC PATCH v2 12/12] i386/sev: update query-sev QAPI format to handle SEV-SNP
  2021-09-01 14:14   ` Markus Armbruster
@ 2021-09-03 16:01       ` Daniel P. Berrangé
  2021-09-03 16:01       ` Daniel P. Berrangé
  1 sibling, 0 replies; 61+ messages in thread
From: Daniel P. Berrangé @ 2021-09-03 16:01 UTC (permalink / raw)
  To: Markus Armbruster
  Cc: Michael Roth, qemu-devel, Connor Kuehl,
	Philippe Mathieu-Daudé,
	Michael S . Tsirkin, James Bottomley, Dr . David Alan Gilbert,
	Tom Lendacky, Paolo Bonzini, Dov Murik, David Gibson, kvm,
	Eduardo Habkost, Brijesh Singh, Eric Blake

On Wed, Sep 01, 2021 at 04:14:10PM +0200, Markus Armbruster wrote:
> Michael Roth <michael.roth@amd.com> writes:
> 
> > Most of the current 'query-sev' command is relevant to both legacy
> > SEV/SEV-ES guests and SEV-SNP guests, with 2 exceptions:
> >
> >   - 'policy' is a 64-bit field for SEV-SNP, not 32-bit, and
> >     the meaning of the bit positions has changed
> >   - 'handle' is not relevant to SEV-SNP
> >
> > To address this, this patch adds a new 'sev-type' field that can be
> > used as a discriminator to select between SEV and SEV-SNP-specific
> > fields/formats without breaking compatibility for existing management
> > tools (so long as management tools that add support for launching
> > SEV-SNP guest update their handling of query-sev appropriately).
> 
> Technically a compatibility break: query-sev can now return an object
> that whose member @policy has different meaning, and also lacks @handle.
> 
> Matrix:
> 
>                             Old mgmt app    New mgmt app
>     Old QEMU, SEV/SEV-ES       good            good(1)
>     New QEMU, SEV/SEV-ES       good(2)         good
>     New QEMU, SEV-SNP           bad(3)         good
> 
> Notes:
> 
> (1) As long as the management application can cope with absent member
> @sev-type.
> 
> (2) As long as the management application ignores unknown member
> @sev-type.
> 
> (3) Management application may choke on missing member @handle, or
> worse, misinterpret member @policy.  Can only happen when something
> other than the management application created the SEV-SNP guest (or the
> user somehow made the management application create one even though it
> doesn't know how, say with CLI option passthrough, but that's always
> fragile, and I wouldn't worry about it here).
> 
> I think (1) and (2) are reasonable.  (3) is an issue for management
> applications that support attaching to existing guests.  Thoughts?

IIUC you can only reach scenario (3) if you have created a guest
using '-object sev-snp-guest', which is a new feature introduced
in patch 2.

IOW, scenario (3)  old mgmt app + new QEMU + sev-snp guest does
not exist as a combination. Thus the (bad) field is actually (n/a)

So I believe this proposed change is acceptable in all scenarios
with existing deployed usage, as well as all newly introduced
scenarios.

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] 61+ messages in thread

* Re: [RFC PATCH v2 12/12] i386/sev: update query-sev QAPI format to handle SEV-SNP
@ 2021-09-03 16:01       ` Daniel P. Berrangé
  0 siblings, 0 replies; 61+ messages in thread
From: Daniel P. Berrangé @ 2021-09-03 16:01 UTC (permalink / raw)
  To: Markus Armbruster
  Cc: Tom Lendacky, Brijesh Singh, Eduardo Habkost, kvm,
	Michael S . Tsirkin, Connor Kuehl, Michael Roth, James Bottomley,
	qemu-devel, Eric Blake, Dr . David Alan Gilbert, Dov Murik,
	Paolo Bonzini, Philippe Mathieu-Daudé,
	David Gibson

On Wed, Sep 01, 2021 at 04:14:10PM +0200, Markus Armbruster wrote:
> Michael Roth <michael.roth@amd.com> writes:
> 
> > Most of the current 'query-sev' command is relevant to both legacy
> > SEV/SEV-ES guests and SEV-SNP guests, with 2 exceptions:
> >
> >   - 'policy' is a 64-bit field for SEV-SNP, not 32-bit, and
> >     the meaning of the bit positions has changed
> >   - 'handle' is not relevant to SEV-SNP
> >
> > To address this, this patch adds a new 'sev-type' field that can be
> > used as a discriminator to select between SEV and SEV-SNP-specific
> > fields/formats without breaking compatibility for existing management
> > tools (so long as management tools that add support for launching
> > SEV-SNP guest update their handling of query-sev appropriately).
> 
> Technically a compatibility break: query-sev can now return an object
> that whose member @policy has different meaning, and also lacks @handle.
> 
> Matrix:
> 
>                             Old mgmt app    New mgmt app
>     Old QEMU, SEV/SEV-ES       good            good(1)
>     New QEMU, SEV/SEV-ES       good(2)         good
>     New QEMU, SEV-SNP           bad(3)         good
> 
> Notes:
> 
> (1) As long as the management application can cope with absent member
> @sev-type.
> 
> (2) As long as the management application ignores unknown member
> @sev-type.
> 
> (3) Management application may choke on missing member @handle, or
> worse, misinterpret member @policy.  Can only happen when something
> other than the management application created the SEV-SNP guest (or the
> user somehow made the management application create one even though it
> doesn't know how, say with CLI option passthrough, but that's always
> fragile, and I wouldn't worry about it here).
> 
> I think (1) and (2) are reasonable.  (3) is an issue for management
> applications that support attaching to existing guests.  Thoughts?

IIUC you can only reach scenario (3) if you have created a guest
using '-object sev-snp-guest', which is a new feature introduced
in patch 2.

IOW, scenario (3)  old mgmt app + new QEMU + sev-snp guest does
not exist as a combination. Thus the (bad) field is actually (n/a)

So I believe this proposed change is acceptable in all scenarios
with existing deployed usage, as well as all newly introduced
scenarios.

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] 61+ messages in thread

* Re: [RFC PATCH v2 07/12] i386/sev: populate secrets and cpuid page and finalize the SNP launch
  2021-08-26 22:26 ` [RFC PATCH v2 07/12] i386/sev: populate secrets and cpuid page and finalize the SNP launch Michael Roth
@ 2021-09-03 20:24     ` Dov Murik
  0 siblings, 0 replies; 61+ messages in thread
From: Dov Murik @ 2021-09-03 20:24 UTC (permalink / raw)
  To: Michael Roth, qemu-devel
  Cc: Connor Kuehl, Philippe Mathieu-Daudé,
	Michael S . Tsirkin, James Bottomley, Dr . David Alan Gilbert,
	Tom Lendacky, Paolo Bonzini, David Gibson,
	Daniel P . Berrangé,
	kvm, Eduardo Habkost, Brijesh Singh, Markus Armbruster,
	Eric Blake, Dov Murik

Hi Michael,

On 27/08/2021 1:26, Michael Roth wrote:
> From: Brijesh Singh <brijesh.singh@amd.com>
> 
> During the SNP guest launch sequence, a special secrets and cpuid page
> needs to be populated by the SEV-SNP firmware. 

Just to be clear: these are two distinct pages.  I suggest rephrasing to
"... a special secrets page and a special cpuid page need to be
populated ..." (or something along these lines).

> The secrets page contains
> the VM Platform Communication Key (VMPCKs) used by the guest to send and
> receive secure messages to the PSP. And CPUID page will contain the CPUID
> value filtered through the PSP.
> 
> The guest BIOS (OVMF) reserves these pages in MEMFD and location of it
> is available through the SNP boot block GUID. While finalizing the guest
> boot flow, lookup for the boot block and call the SNP_LAUNCH_UPDATE
> command to populate secrets and cpuid pages.
> 
> In order to support early boot code, the OVMF may ask hypervisor to
> request the pre-validation of certain memory range. If such range is
> present the call SNP_LAUNCH_UPDATE command to validate those address
> range without affecting the measurement. See the SEV-SNP specification
> for further details.
> 
> Finally, call the SNP_LAUNCH_FINISH to finalize the guest boot.
> 
> Signed-off-by: Brijesh Singh <brijesh.singh@amd.com>
> Signed-off-by: Michael Roth <michael.roth@amd.com>
> ---
>  target/i386/sev.c        | 189 ++++++++++++++++++++++++++++++++++++++-
>  target/i386/trace-events |   2 +
>  2 files changed, 189 insertions(+), 2 deletions(-)
> 
> diff --git a/target/i386/sev.c b/target/i386/sev.c
> index 867c0cb457..0009c93d28 100644
> --- a/target/i386/sev.c
> +++ b/target/i386/sev.c
> @@ -33,6 +33,7 @@
>  #include "monitor/monitor.h"
>  #include "exec/confidential-guest-support.h"
>  #include "hw/i386/pc.h"
> +#include "qemu/range.h"
>  
>  #define TYPE_SEV_COMMON "sev-common"
>  OBJECT_DECLARE_SIMPLE_TYPE(SevCommonState, SEV_COMMON)
> @@ -107,6 +108,19 @@ typedef struct __attribute__((__packed__)) SevInfoBlock {
>      uint32_t reset_addr;
>  } SevInfoBlock;
>  
> +#define SEV_SNP_BOOT_BLOCK_GUID "bd39c0c2-2f8e-4243-83e8-1b74cebcb7d9"
> +typedef struct __attribute__((__packed__)) SevSnpBootInfoBlock {
> +    /* Prevalidate range address */
> +    uint32_t pre_validated_start;
> +    uint32_t pre_validated_end;
> +    /* Secrets page address */
> +    uint32_t secrets_addr;
> +    uint32_t secrets_len;

Just curious: isn't secrets_len always 4096? (same for cpuid_len)
Though it might be a good future proofing to have a length field.


> +    /* CPUID page address */
> +    uint32_t cpuid_addr;
> +    uint32_t cpuid_len;
> +} SevSnpBootInfoBlock;
> +
>  static Error *sev_mig_blocker;
>  
>  static const char *const sev_fw_errlist[] = {
> @@ -1086,6 +1100,162 @@ static Notifier sev_machine_done_notify = {
>      .notify = sev_launch_get_measure,
>  };
>  
> +static int
> +sev_snp_launch_update_gpa(uint32_t hwaddr, uint32_t size, uint8_t type)
> +{
> +    void *hva;
> +    MemoryRegion *mr = NULL;
> +    SevSnpGuestState *sev_snp_guest =
> +        SEV_SNP_GUEST(MACHINE(qdev_get_machine())->cgs);
> +
> +    hva = gpa2hva(&mr, hwaddr, size, NULL);
> +    if (!hva) {
> +        error_report("SEV-SNP failed to get HVA for GPA 0x%x", hwaddr);
> +        return 1;
> +    }
> +
> +    return sev_snp_launch_update(sev_snp_guest, hwaddr, hva, size, type);
> +}
> +
> +static bool
> +detect_first_overlap(uint64_t start, uint64_t end, Range *range_list,
> +                     size_t range_count, Range *overlap_range)
> +{
> +    int i;
> +    bool overlap = false;
> +    Range new;
> +
> +    assert(overlap_range);

Also:
assert(end >= start)
assert(range_count == 0 || range_list)

> +    range_make_empty(overlap_range);
> +    range_init_nofail(&new, start, end - start + 1);
> +
> +    for (i = 0; i < range_count; i++) {
> +        if (range_overlaps_range(&new, &range_list[i]) &&
> +            (range_is_empty(overlap_range) ||
> +             range_lob(&range_list[i]) < range_lob(overlap_range))) {
> +            *overlap_range = range_list[i];
> +            overlap = true;
> +        }
> +    }
> +
> +    return overlap;
> +}
> +
> +static void snp_ovmf_boot_block_setup(void)
> +{
> +    SevSnpBootInfoBlock *info;
> +    uint32_t start, end, sz;
> +    int ret;
> +    Range validated_ranges[2];
> +
> +    /*
> +     * Extract the SNP boot block for the SEV-SNP guests by locating the
> +     * SNP_BOOT GUID. The boot block contains the information such as location
> +     * of secrets and CPUID page, additionaly it may contain the range of
> +     * memory that need to be pre-validated for the boot.
> +     */
> +    if (!pc_system_ovmf_table_find(SEV_SNP_BOOT_BLOCK_GUID,
> +        (uint8_t **)&info, NULL)) {

Fix indent of arguments.


> +        error_report("SEV-SNP: failed to find the SNP boot block");
> +        exit(1);
> +    }
> +
> +    trace_kvm_sev_snp_ovmf_boot_block_info(info->secrets_addr,
> +                                           info->secrets_len, info->cpuid_addr,
> +                                           info->cpuid_len,
> +                                           info->pre_validated_start,
> +                                           info->pre_validated_end);
> +
> +    /* Populate the secrets page */
> +    ret = sev_snp_launch_update_gpa(info->secrets_addr, info->secrets_len,
> +                                    KVM_SEV_SNP_PAGE_TYPE_SECRETS);
> +    if (ret) {
> +        error_report("SEV-SNP: failed to insert secret page GPA 0x%x",
> +                     info->secrets_addr);
> +        exit(1);
> +    }
> +
> +    /* Populate the cpuid page */
> +    ret = sev_snp_launch_update_gpa(info->cpuid_addr, info->cpuid_len,
> +                                    KVM_SEV_SNP_PAGE_TYPE_CPUID);
> +    if (ret) {
> +        error_report("SEV-SNP: failed to insert cpuid page GPA 0x%x",
> +                     info->cpuid_addr);
> +        exit(1);
> +    }
> +
> +    /*
> +     * Pre-validate the range using the LAUNCH_UPDATE_DATA, if the
> +     * pre-validation range contains the CPUID and Secret page GPA then skip
> +     * it. This is because SEV-SNP firmware pre-validates those pages as part
> +     * of adding secrets and cpuid LAUNCH_UPDATE type.
> +     */
> +    range_init_nofail(&validated_ranges[0], info->secrets_addr, info->secrets_len);
> +    range_init_nofail(&validated_ranges[1], info->cpuid_addr, info->cpuid_len);
> +    start = info->pre_validated_start;
> +    end = info->pre_validated_end;
> +
> +    while (start < end) {
> +        Range overlap_range;
> +
> +        /* Check if the requested range overlaps with Secrets and CPUID page */
> +        if (detect_first_overlap(start, end, validated_ranges, 2,

Replace the literal 2 with ARRAY_SIZE(validated_ranges).


> +                                 &overlap_range)) {
> +            if (start < range_lob(&overlap_range)) {
> +                sz = range_lob(&overlap_range) - start;
> +                if (sev_snp_launch_update_gpa(start, sz,
> +                    KVM_SEV_SNP_PAGE_TYPE_UNMEASURED)) {

Fix indent of arguments (if possible).

> +                    error_report("SEV-SNP: failed to validate gpa 0x%x sz %d",
> +                                 start, sz);
> +                    exit(1);
> +                }
> +            }
> +
> +            start = range_upb(&overlap_range) + 1;
> +            continue;
> +        }
> +
> +        /* Validate the remaining range */
> +        if (sev_snp_launch_update_gpa(start, end - start,

I think the second argument should be:    end - start + 1 .

Consider start=0x8000 end=0x8fff (inclusive). In this case you want to
validate exactly 0x1000 bytes starting at 0x8000.  So the size should be
0x8fff - 0x8000 + 1.

I assume all this doesn't matter for the underlying calls which operate
at whole pages anyway (are there proper asserts in sev_snp_launch_update
(or in KVM) that verify that start and sz are page-size-aligned?)



> +            KVM_SEV_SNP_PAGE_TYPE_UNMEASURED)) {

Fix indent of arguments.


> +            error_report("SEV-SNP: failed to validate gpa 0x%x sz %d",
> +                         start, end - start);
> +            exit(1);
> +        }
> +
> +        start = end;
> +    }
> +}
> +
> +static void
> +sev_snp_launch_finish(SevSnpGuestState *sev_snp)
> +{
> +    int ret, error;
> +    Error *local_err = NULL;
> +    struct kvm_sev_snp_launch_finish *finish = &sev_snp->kvm_finish_conf;
> +
> +    trace_kvm_sev_snp_launch_finish();
> +    ret = sev_ioctl(SEV_COMMON(sev_snp)->sev_fd, KVM_SEV_SNP_LAUNCH_FINISH, finish, &error);
> +    if (ret) {
> +        error_report("%s: SNP_LAUNCH_FINISH ret=%d fw_error=%d '%s'",
> +                     __func__, ret, error, fw_error_to_str(error));
> +        exit(1);
> +    }
> +
> +    sev_set_guest_state(SEV_COMMON(sev_snp), SEV_STATE_RUNNING);
> +
> +    /* add migration blocker */
> +    error_setg(&sev_mig_blocker,
> +               "SEV: Migration is not implemented");
> +    ret = migrate_add_blocker(sev_mig_blocker, &local_err);
> +    if (local_err) {
> +        error_report_err(local_err);
> +        error_free(sev_mig_blocker);
> +        exit(1);
> +    }
> +}
> +
> +
>  static void
>  sev_launch_finish(SevGuestState *sev_guest)
>  {
> @@ -1121,7 +1291,12 @@ sev_vm_state_change(void *opaque, bool running, RunState state)
>  
>      if (running) {
>          if (!sev_check_state(sev_common, SEV_STATE_RUNNING)) {
> -            sev_launch_finish(SEV_GUEST(sev_common));
> +            if (sev_snp_enabled()) {
> +                snp_ovmf_boot_block_setup();
> +                sev_snp_launch_finish(SEV_SNP_GUEST(sev_common));
> +            } else {
> +                sev_launch_finish(SEV_GUEST(sev_common));
> +            }
>          }
>      }
>  }
> @@ -1236,7 +1411,17 @@ int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp)
>      }
>  
>      ram_block_notifier_add(&sev_ram_notifier);
> -    qemu_add_machine_init_done_notifier(&sev_machine_done_notify);
> +
> +    /*
> +     * The machine done notify event is used by the SEV guest to get the
> +     * measurement of the encrypted images. When SEV-SNP is enabled then
> +     * measurement is part of the attestation report and the measurement
> +     * command does not exist. So skip registering the notifier.
> +     */
> +    if (!sev_snp_enabled()) {
> +        qemu_add_machine_init_done_notifier(&sev_machine_done_notify);
> +    }
> +
>      qemu_add_vm_change_state_handler(sev_vm_state_change, sev_common);
>  
>      cgs->ready = true;
> diff --git a/target/i386/trace-events b/target/i386/trace-events
> index 0c2d250206..db91287439 100644
> --- a/target/i386/trace-events
> +++ b/target/i386/trace-events
> @@ -13,3 +13,5 @@ kvm_sev_launch_secret(uint64_t hpa, uint64_t hva, uint64_t secret, int len) "hpa
>  kvm_sev_attestation_report(const char *mnonce, const char *data) "mnonce %s data %s"
>  kvm_sev_snp_launch_start(uint64_t policy) "policy 0x%" PRIx64
>  kvm_sev_snp_launch_update(void *addr, uint64_t len, int type) "addr %p len 0x%" PRIx64 " type %d"
> +kvm_sev_snp_launch_finish(void) ""
> +kvm_sev_snp_ovmf_boot_block_info(uint32_t secrets_gpa, uint32_t slen, uint32_t cpuid_gpa, uint32_t clen, uint32_t s, uint32_t e) "secrets 0x%x+0x%x cpuid 0x%x+0x%x pre-validate 0x%x+0x%x"

In this trace format string you use the notation A+B to indicate addr=A
 len=B.  But for the pre-validated range the arguments are 'start' and
'end' (and not 'addr' and 'len'), so I suggest choosing a different
notation to log that range.

-Dov

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

* Re: [RFC PATCH v2 07/12] i386/sev: populate secrets and cpuid page and finalize the SNP launch
@ 2021-09-03 20:24     ` Dov Murik
  0 siblings, 0 replies; 61+ messages in thread
From: Dov Murik @ 2021-09-03 20:24 UTC (permalink / raw)
  To: Michael Roth, qemu-devel
  Cc: Tom Lendacky, Daniel P . Berrangé,
	Eduardo Habkost, kvm, Michael S . Tsirkin, Connor Kuehl,
	Eric Blake, James Bottomley, Dr . David Alan Gilbert,
	Markus Armbruster, Dov Murik, Brijesh Singh, Paolo Bonzini,
	Philippe Mathieu-Daudé,
	David Gibson

Hi Michael,

On 27/08/2021 1:26, Michael Roth wrote:
> From: Brijesh Singh <brijesh.singh@amd.com>
> 
> During the SNP guest launch sequence, a special secrets and cpuid page
> needs to be populated by the SEV-SNP firmware. 

Just to be clear: these are two distinct pages.  I suggest rephrasing to
"... a special secrets page and a special cpuid page need to be
populated ..." (or something along these lines).

> The secrets page contains
> the VM Platform Communication Key (VMPCKs) used by the guest to send and
> receive secure messages to the PSP. And CPUID page will contain the CPUID
> value filtered through the PSP.
> 
> The guest BIOS (OVMF) reserves these pages in MEMFD and location of it
> is available through the SNP boot block GUID. While finalizing the guest
> boot flow, lookup for the boot block and call the SNP_LAUNCH_UPDATE
> command to populate secrets and cpuid pages.
> 
> In order to support early boot code, the OVMF may ask hypervisor to
> request the pre-validation of certain memory range. If such range is
> present the call SNP_LAUNCH_UPDATE command to validate those address
> range without affecting the measurement. See the SEV-SNP specification
> for further details.
> 
> Finally, call the SNP_LAUNCH_FINISH to finalize the guest boot.
> 
> Signed-off-by: Brijesh Singh <brijesh.singh@amd.com>
> Signed-off-by: Michael Roth <michael.roth@amd.com>
> ---
>  target/i386/sev.c        | 189 ++++++++++++++++++++++++++++++++++++++-
>  target/i386/trace-events |   2 +
>  2 files changed, 189 insertions(+), 2 deletions(-)
> 
> diff --git a/target/i386/sev.c b/target/i386/sev.c
> index 867c0cb457..0009c93d28 100644
> --- a/target/i386/sev.c
> +++ b/target/i386/sev.c
> @@ -33,6 +33,7 @@
>  #include "monitor/monitor.h"
>  #include "exec/confidential-guest-support.h"
>  #include "hw/i386/pc.h"
> +#include "qemu/range.h"
>  
>  #define TYPE_SEV_COMMON "sev-common"
>  OBJECT_DECLARE_SIMPLE_TYPE(SevCommonState, SEV_COMMON)
> @@ -107,6 +108,19 @@ typedef struct __attribute__((__packed__)) SevInfoBlock {
>      uint32_t reset_addr;
>  } SevInfoBlock;
>  
> +#define SEV_SNP_BOOT_BLOCK_GUID "bd39c0c2-2f8e-4243-83e8-1b74cebcb7d9"
> +typedef struct __attribute__((__packed__)) SevSnpBootInfoBlock {
> +    /* Prevalidate range address */
> +    uint32_t pre_validated_start;
> +    uint32_t pre_validated_end;
> +    /* Secrets page address */
> +    uint32_t secrets_addr;
> +    uint32_t secrets_len;

Just curious: isn't secrets_len always 4096? (same for cpuid_len)
Though it might be a good future proofing to have a length field.


> +    /* CPUID page address */
> +    uint32_t cpuid_addr;
> +    uint32_t cpuid_len;
> +} SevSnpBootInfoBlock;
> +
>  static Error *sev_mig_blocker;
>  
>  static const char *const sev_fw_errlist[] = {
> @@ -1086,6 +1100,162 @@ static Notifier sev_machine_done_notify = {
>      .notify = sev_launch_get_measure,
>  };
>  
> +static int
> +sev_snp_launch_update_gpa(uint32_t hwaddr, uint32_t size, uint8_t type)
> +{
> +    void *hva;
> +    MemoryRegion *mr = NULL;
> +    SevSnpGuestState *sev_snp_guest =
> +        SEV_SNP_GUEST(MACHINE(qdev_get_machine())->cgs);
> +
> +    hva = gpa2hva(&mr, hwaddr, size, NULL);
> +    if (!hva) {
> +        error_report("SEV-SNP failed to get HVA for GPA 0x%x", hwaddr);
> +        return 1;
> +    }
> +
> +    return sev_snp_launch_update(sev_snp_guest, hwaddr, hva, size, type);
> +}
> +
> +static bool
> +detect_first_overlap(uint64_t start, uint64_t end, Range *range_list,
> +                     size_t range_count, Range *overlap_range)
> +{
> +    int i;
> +    bool overlap = false;
> +    Range new;
> +
> +    assert(overlap_range);

Also:
assert(end >= start)
assert(range_count == 0 || range_list)

> +    range_make_empty(overlap_range);
> +    range_init_nofail(&new, start, end - start + 1);
> +
> +    for (i = 0; i < range_count; i++) {
> +        if (range_overlaps_range(&new, &range_list[i]) &&
> +            (range_is_empty(overlap_range) ||
> +             range_lob(&range_list[i]) < range_lob(overlap_range))) {
> +            *overlap_range = range_list[i];
> +            overlap = true;
> +        }
> +    }
> +
> +    return overlap;
> +}
> +
> +static void snp_ovmf_boot_block_setup(void)
> +{
> +    SevSnpBootInfoBlock *info;
> +    uint32_t start, end, sz;
> +    int ret;
> +    Range validated_ranges[2];
> +
> +    /*
> +     * Extract the SNP boot block for the SEV-SNP guests by locating the
> +     * SNP_BOOT GUID. The boot block contains the information such as location
> +     * of secrets and CPUID page, additionaly it may contain the range of
> +     * memory that need to be pre-validated for the boot.
> +     */
> +    if (!pc_system_ovmf_table_find(SEV_SNP_BOOT_BLOCK_GUID,
> +        (uint8_t **)&info, NULL)) {

Fix indent of arguments.


> +        error_report("SEV-SNP: failed to find the SNP boot block");
> +        exit(1);
> +    }
> +
> +    trace_kvm_sev_snp_ovmf_boot_block_info(info->secrets_addr,
> +                                           info->secrets_len, info->cpuid_addr,
> +                                           info->cpuid_len,
> +                                           info->pre_validated_start,
> +                                           info->pre_validated_end);
> +
> +    /* Populate the secrets page */
> +    ret = sev_snp_launch_update_gpa(info->secrets_addr, info->secrets_len,
> +                                    KVM_SEV_SNP_PAGE_TYPE_SECRETS);
> +    if (ret) {
> +        error_report("SEV-SNP: failed to insert secret page GPA 0x%x",
> +                     info->secrets_addr);
> +        exit(1);
> +    }
> +
> +    /* Populate the cpuid page */
> +    ret = sev_snp_launch_update_gpa(info->cpuid_addr, info->cpuid_len,
> +                                    KVM_SEV_SNP_PAGE_TYPE_CPUID);
> +    if (ret) {
> +        error_report("SEV-SNP: failed to insert cpuid page GPA 0x%x",
> +                     info->cpuid_addr);
> +        exit(1);
> +    }
> +
> +    /*
> +     * Pre-validate the range using the LAUNCH_UPDATE_DATA, if the
> +     * pre-validation range contains the CPUID and Secret page GPA then skip
> +     * it. This is because SEV-SNP firmware pre-validates those pages as part
> +     * of adding secrets and cpuid LAUNCH_UPDATE type.
> +     */
> +    range_init_nofail(&validated_ranges[0], info->secrets_addr, info->secrets_len);
> +    range_init_nofail(&validated_ranges[1], info->cpuid_addr, info->cpuid_len);
> +    start = info->pre_validated_start;
> +    end = info->pre_validated_end;
> +
> +    while (start < end) {
> +        Range overlap_range;
> +
> +        /* Check if the requested range overlaps with Secrets and CPUID page */
> +        if (detect_first_overlap(start, end, validated_ranges, 2,

Replace the literal 2 with ARRAY_SIZE(validated_ranges).


> +                                 &overlap_range)) {
> +            if (start < range_lob(&overlap_range)) {
> +                sz = range_lob(&overlap_range) - start;
> +                if (sev_snp_launch_update_gpa(start, sz,
> +                    KVM_SEV_SNP_PAGE_TYPE_UNMEASURED)) {

Fix indent of arguments (if possible).

> +                    error_report("SEV-SNP: failed to validate gpa 0x%x sz %d",
> +                                 start, sz);
> +                    exit(1);
> +                }
> +            }
> +
> +            start = range_upb(&overlap_range) + 1;
> +            continue;
> +        }
> +
> +        /* Validate the remaining range */
> +        if (sev_snp_launch_update_gpa(start, end - start,

I think the second argument should be:    end - start + 1 .

Consider start=0x8000 end=0x8fff (inclusive). In this case you want to
validate exactly 0x1000 bytes starting at 0x8000.  So the size should be
0x8fff - 0x8000 + 1.

I assume all this doesn't matter for the underlying calls which operate
at whole pages anyway (are there proper asserts in sev_snp_launch_update
(or in KVM) that verify that start and sz are page-size-aligned?)



> +            KVM_SEV_SNP_PAGE_TYPE_UNMEASURED)) {

Fix indent of arguments.


> +            error_report("SEV-SNP: failed to validate gpa 0x%x sz %d",
> +                         start, end - start);
> +            exit(1);
> +        }
> +
> +        start = end;
> +    }
> +}
> +
> +static void
> +sev_snp_launch_finish(SevSnpGuestState *sev_snp)
> +{
> +    int ret, error;
> +    Error *local_err = NULL;
> +    struct kvm_sev_snp_launch_finish *finish = &sev_snp->kvm_finish_conf;
> +
> +    trace_kvm_sev_snp_launch_finish();
> +    ret = sev_ioctl(SEV_COMMON(sev_snp)->sev_fd, KVM_SEV_SNP_LAUNCH_FINISH, finish, &error);
> +    if (ret) {
> +        error_report("%s: SNP_LAUNCH_FINISH ret=%d fw_error=%d '%s'",
> +                     __func__, ret, error, fw_error_to_str(error));
> +        exit(1);
> +    }
> +
> +    sev_set_guest_state(SEV_COMMON(sev_snp), SEV_STATE_RUNNING);
> +
> +    /* add migration blocker */
> +    error_setg(&sev_mig_blocker,
> +               "SEV: Migration is not implemented");
> +    ret = migrate_add_blocker(sev_mig_blocker, &local_err);
> +    if (local_err) {
> +        error_report_err(local_err);
> +        error_free(sev_mig_blocker);
> +        exit(1);
> +    }
> +}
> +
> +
>  static void
>  sev_launch_finish(SevGuestState *sev_guest)
>  {
> @@ -1121,7 +1291,12 @@ sev_vm_state_change(void *opaque, bool running, RunState state)
>  
>      if (running) {
>          if (!sev_check_state(sev_common, SEV_STATE_RUNNING)) {
> -            sev_launch_finish(SEV_GUEST(sev_common));
> +            if (sev_snp_enabled()) {
> +                snp_ovmf_boot_block_setup();
> +                sev_snp_launch_finish(SEV_SNP_GUEST(sev_common));
> +            } else {
> +                sev_launch_finish(SEV_GUEST(sev_common));
> +            }
>          }
>      }
>  }
> @@ -1236,7 +1411,17 @@ int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp)
>      }
>  
>      ram_block_notifier_add(&sev_ram_notifier);
> -    qemu_add_machine_init_done_notifier(&sev_machine_done_notify);
> +
> +    /*
> +     * The machine done notify event is used by the SEV guest to get the
> +     * measurement of the encrypted images. When SEV-SNP is enabled then
> +     * measurement is part of the attestation report and the measurement
> +     * command does not exist. So skip registering the notifier.
> +     */
> +    if (!sev_snp_enabled()) {
> +        qemu_add_machine_init_done_notifier(&sev_machine_done_notify);
> +    }
> +
>      qemu_add_vm_change_state_handler(sev_vm_state_change, sev_common);
>  
>      cgs->ready = true;
> diff --git a/target/i386/trace-events b/target/i386/trace-events
> index 0c2d250206..db91287439 100644
> --- a/target/i386/trace-events
> +++ b/target/i386/trace-events
> @@ -13,3 +13,5 @@ kvm_sev_launch_secret(uint64_t hpa, uint64_t hva, uint64_t secret, int len) "hpa
>  kvm_sev_attestation_report(const char *mnonce, const char *data) "mnonce %s data %s"
>  kvm_sev_snp_launch_start(uint64_t policy) "policy 0x%" PRIx64
>  kvm_sev_snp_launch_update(void *addr, uint64_t len, int type) "addr %p len 0x%" PRIx64 " type %d"
> +kvm_sev_snp_launch_finish(void) ""
> +kvm_sev_snp_ovmf_boot_block_info(uint32_t secrets_gpa, uint32_t slen, uint32_t cpuid_gpa, uint32_t clen, uint32_t s, uint32_t e) "secrets 0x%x+0x%x cpuid 0x%x+0x%x pre-validate 0x%x+0x%x"

In this trace format string you use the notation A+B to indicate addr=A
 len=B.  But for the pre-validated range the arguments are 'start' and
'end' (and not 'addr' and 'len'), so I suggest choosing a different
notation to log that range.

-Dov


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

* Re: [RFC PATCH v2 02/12] linux-header: add the SNP specific command
  2021-08-26 22:26 ` [RFC PATCH v2 02/12] linux-header: add the SNP specific command Michael Roth
@ 2021-09-03 20:36     ` Dov Murik
  0 siblings, 0 replies; 61+ messages in thread
From: Dov Murik @ 2021-09-03 20:36 UTC (permalink / raw)
  To: Michael Roth, qemu-devel
  Cc: Connor Kuehl, Philippe Mathieu-Daudé,
	Michael S . Tsirkin, James Bottomley, Dr . David Alan Gilbert,
	Tom Lendacky, Paolo Bonzini, David Gibson,
	Daniel P . Berrangé,
	kvm, Eduardo Habkost, Brijesh Singh, Markus Armbruster,
	Eric Blake

Hi Michael,

On 27/08/2021 1:26, Michael Roth wrote:
> From: Brijesh Singh <brijesh.singh@amd.com>
> 
> Sync the kvm.h with the kernel to include the SNP specific commands.
> 
> Signed-off-by: Brijesh Singh <brijesh.singh@amd.com>
> Signed-off-by: Michael Roth <michael.roth@amd.com>
> ---
>  linux-headers/linux/kvm.h | 50 +++++++++++++++++++++++++++++++++++++++
>  1 file changed, 50 insertions(+)
> 

In previous review round I commented:

------
What about psp-sev.h ? I see that kernel patch "[PATCH Part2 RFC v4
11/40] crypto:ccp: Define the SEV-SNP commands" adds some new PSP return
codes.

The QEMU user-friendly string list sev_fw_errlist (in sev.c) should be
updated accordingly.
-------


-Dov

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

* Re: [RFC PATCH v2 02/12] linux-header: add the SNP specific command
@ 2021-09-03 20:36     ` Dov Murik
  0 siblings, 0 replies; 61+ messages in thread
From: Dov Murik @ 2021-09-03 20:36 UTC (permalink / raw)
  To: Michael Roth, qemu-devel
  Cc: Tom Lendacky, Daniel P . Berrangé,
	Eduardo Habkost, kvm, Michael S . Tsirkin, Connor Kuehl,
	Eric Blake, James Bottomley, Dr . David Alan Gilbert,
	Markus Armbruster, Brijesh Singh, Paolo Bonzini,
	Philippe Mathieu-Daudé,
	David Gibson

Hi Michael,

On 27/08/2021 1:26, Michael Roth wrote:
> From: Brijesh Singh <brijesh.singh@amd.com>
> 
> Sync the kvm.h with the kernel to include the SNP specific commands.
> 
> Signed-off-by: Brijesh Singh <brijesh.singh@amd.com>
> Signed-off-by: Michael Roth <michael.roth@amd.com>
> ---
>  linux-headers/linux/kvm.h | 50 +++++++++++++++++++++++++++++++++++++++
>  1 file changed, 50 insertions(+)
> 

In previous review round I commented:

------
What about psp-sev.h ? I see that kernel patch "[PATCH Part2 RFC v4
11/40] crypto:ccp: Define the SEV-SNP commands" adds some new PSP return
codes.

The QEMU user-friendly string list sev_fw_errlist (in sev.c) should be
updated accordingly.
-------


-Dov


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

* Re: [RFC PATCH v2 03/12] i386/sev: introduce 'sev-snp-guest' object
  2021-08-26 22:26 ` [RFC PATCH v2 03/12] i386/sev: introduce 'sev-snp-guest' object Michael Roth
@ 2021-09-03 21:12     ` Dov Murik
  2021-09-03 21:12     ` Dov Murik
  1 sibling, 0 replies; 61+ messages in thread
From: Dov Murik @ 2021-09-03 21:12 UTC (permalink / raw)
  To: Michael Roth, qemu-devel
  Cc: Connor Kuehl, Philippe Mathieu-Daudé,
	Michael S . Tsirkin, James Bottomley, Dr . David Alan Gilbert,
	Tom Lendacky, Paolo Bonzini, David Gibson,
	Daniel P . Berrangé,
	kvm, Eduardo Habkost, Brijesh Singh, Markus Armbruster,
	Eric Blake, Dov Murik



On 27/08/2021 1:26, Michael Roth wrote:
> From: Brijesh Singh <brijesh.singh@amd.com>
> 
> SEV-SNP support relies on a different set of properties/state than the
> existing 'sev-guest' object. This patch introduces the 'sev-snp-guest'
> object, which can be used to configure an SEV-SNP guest. For example,
> a default-configured SEV-SNP guest with no additional information
> passed in for use with attestation:
> 
>   -object sev-snp-guest,id=sev0
> 
> or a fully-specified SEV-SNP guest where all spec-defined binary
> blobs are passed in as base64-encoded strings:
> 
>   -object sev-snp-guest,id=sev0, \
>     policy=0x30000, \
>     init-flags=0, \
>     id-block=YWFhYWFhYWFhYWFhYWFhCg==, \
>     id-auth=CxHK/OKLkXGn/KpAC7Wl1FSiisWDbGTEKz..., \
>     auth-key-enabled=on, \
>     host-data=LNkCWBRC5CcdGXirbNUV1OrsR28s..., \
>     guest-visible-workarounds=AA==, \
> 
> See the QAPI schema updates included in this patch for more usage
> details.
> 
> In some cases these blobs may be up to 4096 characters, but this is
> generally well below the default limit for linux hosts where
> command-line sizes are defined by the sysconf-configurable ARG_MAX
> value, which defaults to 2097152 characters for Ubuntu hosts, for
> example.
> 
> Co-developed-by: Michael Roth <michael.roth@amd.com>
> Signed-off-by: Brijesh Singh <brijesh.singh@amd.com>
> Signed-off-by: Michael Roth <michael.roth@amd.com>
> ---
>  docs/amd-memory-encryption.txt |  77 ++++++++++-
>  qapi/qom.json                  |  60 ++++++++
>  target/i386/sev.c              | 245 ++++++++++++++++++++++++++++++++-
>  3 files changed, 379 insertions(+), 3 deletions(-)
> 
> diff --git a/docs/amd-memory-encryption.txt b/docs/amd-memory-encryption.txt
> index ffca382b5f..0d82e67fa1 100644
> --- a/docs/amd-memory-encryption.txt
> +++ b/docs/amd-memory-encryption.txt
> @@ -22,8 +22,8 @@ support for notifying a guest's operating system when certain types of VMEXITs
>  are about to occur. This allows the guest to selectively share information with
>  the hypervisor to satisfy the requested function.
>  
> -Launching
> ----------
> +Launching (SEV and SEV-ES)
> +--------------------------
>  Boot images (such as bios) must be encrypted before a guest can be booted. The
>  MEMORY_ENCRYPT_OP ioctl provides commands to encrypt the images: LAUNCH_START,
>  LAUNCH_UPDATE_DATA, LAUNCH_MEASURE and LAUNCH_FINISH. These four commands
> @@ -113,6 +113,79 @@ a SEV-ES guest:
>   - Requires in-kernel irqchip - the burden is placed on the hypervisor to
>     manage booting APs.
>  
> +Launching (SEV-SNP)
> +-------------------
> +Boot images (such as bios) must be encrypted before a guest can be booted. The
> +MEMORY_ENCRYPT_OP ioctl provides commands to encrypt the images:
> +KVM_SNP_INIT, SNP_LAUNCH_START, SNP_LAUNCH_UPDATE, and SNP_LAUNCH_FINISH. These
> +four commands together generate a fresh memory encryption key for the VM,
> +encrypt the boot images for a successful launch.
> +
> +KVM_SNP_INIT is called first to initialize the SEV-SNP firmware and SNP
> +features in the KVM. The feature flags value can be provided through the
> +'init-flags' property of the 'sev-snp-guest' object.
> +
> ++------------+-------+----------+---------------------------------+
> +| key        | type  | default  | meaning                         |
> ++------------+-------+----------+---------------------------------+
> +| init_flags | hex   | 0        | SNP feature flags               |
> ++-----------------------------------------------------------------+
> +
> +Note: currently the init_flags must be zero.
> +
> +SNP_LAUNCH_START is called first to create a cryptographic launch context
> +within the firmware. To create this context, guest owner must provide a guest
> +policy and other parameters as described in the SEV-SNP firmware
> +specification. The launch parameters should be specified as described in the
> +QAPI schema for the 'sev-snp-guest' object.
> +
> +The SNP_LAUNCH_START uses the following parameters (see the SEV-SNP
> +specification for more details):
> +
> ++--------+-------+----------+----------------------------------------------+
> +| key    | type  | default  | meaning                                      |
> ++--------+-------+----------+----------------------------------------------+
> +| policy | hex   | 0x30000  | a 64-bit guest policy                        |
> +| imi_en | bool  | 0        | 1 when IMI is enabled                        |
> +| ma_end | bool  | 0        | 1 when migration agent is used               |
> +| gosvw  | string| 0        | 16-byte base64 encoded string for the guest  |
> +|        |       |          | OS visible workaround.                       |
> ++--------+-------+----------+----------------------------------------------+
> +
> +SNP_LAUNCH_UPDATE encrypts the memory region using the cryptographic context
> +created via the SNP_LAUNCH_START command. If required, this command can be called
> +multiple times to encrypt different memory regions. The command also calculates
> +the measurement of the memory contents as it encrypts.
> +
> +SNP_LAUNCH_FINISH finalizes the guest launch flow. Optionally, while finalizing
> +the launch the firmware can perform checks on the launch digest computing
> +through the SNP_LAUNCH_UPDATE. To perform the check the user must supply
> +the id block, authentication blob and host data that should be included in the
> +attestation report. See the SEV-SNP spec for further details.
> +
> +The SNP_LAUNCH_FINISH uses the following parameters, which can be configured
> +by the corresponding parameters documented in the QAPI schema for the
> +'sev-snp-guest' object.
> +
> ++------------+-------+----------+----------------------------------------------+
> +| key        | type  | default  | meaning                                      |
> ++------------+-------+----------+----------------------------------------------+
> +| id_block   | string| none     | base64 encoded ID block                      |
> ++------------+-------+----------+----------------------------------------------+
> +| id_auth    | string| none     | base64 encoded authentication information    |
> ++------------+-------+----------+----------------------------------------------+
> +| auth_key_en| bool  | 0        | auth block contains author key               |
> ++------------+-------+----------+----------------------------------------------+
> +| host_data  | string| none     | host provided data                           |
> ++------------+-------+----------+----------------------------------------------+
> +
> +To launch a SEV-SNP guest (additional parameters are documented in the QAPI
> +schema for the 'sev-snp-guest' object):
> +
> +# ${QEMU} \
> +    -machine ...,confidential-guest-support=sev0 \
> +    -object sev-snp-guest,id=sev0,cbitpos=51,reduced-phys-bits=1
> +
>  Debugging
>  -----------
>  Since the memory contents of a SEV guest are encrypted, hypervisor access to
> diff --git a/qapi/qom.json b/qapi/qom.json
> index 211e083727..ea39585026 100644
> --- a/qapi/qom.json
> +++ b/qapi/qom.json
> @@ -775,6 +775,64 @@
>              '*policy': 'uint32',
>              '*handle': 'uint32' } }
>  
> +##
> +# @SevSnpGuestProperties:
> +#
> +# Properties for sev-snp-guest objects. Many of these are direct arguments
> +# for the SEV-SNP KVM interfaces documented in the linux kernel source
> +# documentation under 'amd-memory-encryption.rst'. Additional documentation
> +# is also available in the QEMU source tree under
> +# 'amd-memory-encryption.rst'.
> +#
> +# In addition to those files, please see the SEV-SNP Firmware Specification
> +# (Rev 0.9) documentation for the SNP_INIT and
> +# SNP_LAUNCH_{START,UPDATE,FINISH} firmware interfaces, which the KVM
> +# interfaces are written against.
> +#
> +# @init-flags: as documented for the 'flags' parameter of the
> +#              KVM_SNP_INIT KVM command (default: 0)
> +#
> +# @policy: as documented for the 'policy' parameter of the
> +#          KVM_SNP_LAUNCH_START KVM command (default: 0x30000)
> +#
> +# @guest-visible-workarounds: 16-byte, base64-encoded blob to report
> +#                             hypervisor-defined workarounds, as documented
> +#                             for the 'gosvm' parameter of the

typo: s/gosvm/gosvw/


> +#                             KVM_SNP_LAUNCH_START KVM command.
> +#                             (default: all-zero)
> +#
> +# @id-block: 8-byte, base64-encoded blob to provide the ID Block
> +#            structure documented in SEV-SNP spec, as documented for the
> +#            'id_block_uaddr' parameter of the KVM_SNP_LAUNCH_FINISH
> +#            command (default: all-zero)

The documentation says the ID Block is 96 bytes long (Table 65 in
section 8.15 of the SNP FW ABI document).


> +#
> +# @id-auth: 4096-byte, base64-encoded blob to provide the ID Authentication
> +#           Information Structure documented in SEV-SNP spec, as documented
> +#           for the 'id_auth_uaddr' parameter of the KVM_SNP_LAUNCH_FINISH
> +#           command (default: all-zero)
> +#
> +# @auth-key-enabled: true if 'id-auth' blob contains the Author Key
> +#                    documented in the SEV-SNP spec, as documented for the
> +#                    'auth_key_en' parameter of the KVM_SNP_LAUNCH_FINISH
> +#                    command (default: false)
> +#
> +# @host-data: 32-byte, base64-encoded user-defined blob to provide to the
> +#             guest, as documented for the 'host_data' parameter of the
> +#             KVM_SNP_LAUNCH_FINISH command (default: all-zero)
> +#
> +# Since: 6.2
> +##
> +{ 'struct': 'SevSnpGuestProperties',
> +  'base': 'SevCommonProperties',
> +  'data': {
> +            '*init-flags': 'uint64',
> +            '*policy': 'uint64',
> +            '*guest-visible-workarounds': 'str',
> +            '*id-block': 'str',
> +            '*id-auth': 'str',
> +            '*auth-key-enabled': 'bool',
> +            '*host-data': 'str' } }
> +
>  ##
>  # @ObjectType:
>  #
> @@ -816,6 +874,7 @@
>      'secret',
>      'secret_keyring',
>      'sev-guest',
> +    'sev-snp-guest',
>      's390-pv-guest',
>      'throttle-group',
>      'tls-creds-anon',
> @@ -873,6 +932,7 @@
>        'secret':                     'SecretProperties',
>        'secret_keyring':             'SecretKeyringProperties',
>        'sev-guest':                  'SevGuestProperties',
> +      'sev-snp-guest':              'SevSnpGuestProperties',
>        'throttle-group':             'ThrottleGroupProperties',
>        'tls-creds-anon':             'TlsCredsAnonProperties',
>        'tls-creds-psk':              'TlsCredsPskProperties',
> diff --git a/target/i386/sev.c b/target/i386/sev.c
> index 6acebfbd53..ba08b7d3ab 100644
> --- a/target/i386/sev.c
> +++ b/target/i386/sev.c
> @@ -38,7 +38,8 @@
>  OBJECT_DECLARE_SIMPLE_TYPE(SevCommonState, SEV_COMMON)
>  #define TYPE_SEV_GUEST "sev-guest"
>  OBJECT_DECLARE_SIMPLE_TYPE(SevGuestState, SEV_GUEST)
> -
> +#define TYPE_SEV_SNP_GUEST "sev-snp-guest"
> +OBJECT_DECLARE_SIMPLE_TYPE(SevSnpGuestState, SEV_SNP_GUEST)
>  
>  /**
>   * SevGuestState:
> @@ -82,8 +83,23 @@ struct SevGuestState {
>      char *session_file;
>  };
>  
> +struct SevSnpGuestState {
> +    SevCommonState sev_common;
> +
> +    /* configuration parameters */
> +    char *guest_visible_workarounds;
> +    char *id_block;
> +    char *id_auth;
> +    char *host_data;
> +
> +    struct kvm_snp_init kvm_init_conf;
> +    struct kvm_sev_snp_launch_start kvm_start_conf;
> +    struct kvm_sev_snp_launch_finish kvm_finish_conf;
> +};
> +
>  #define DEFAULT_GUEST_POLICY    0x1 /* disable debug */
>  #define DEFAULT_SEV_DEVICE      "/dev/sev"
> +#define DEFAULT_SEV_SNP_POLICY  0x30000
>  
>  #define SEV_INFO_BLOCK_GUID     "00f771de-1a7e-4fcb-890e-68c77e2fb44e"
>  typedef struct __attribute__((__packed__)) SevInfoBlock {
> @@ -364,6 +380,232 @@ static const TypeInfo sev_guest_info = {
>      .class_init = sev_guest_class_init,
>  };
>  
> +static void
> +sev_snp_guest_get_init_flags(Object *obj, Visitor *v, const char *name,
> +                             void *opaque, Error **errp)
> +{
> +    visit_type_uint64(v, name,
> +                      (uint64_t *)&SEV_SNP_GUEST(obj)->kvm_init_conf.flags,
> +                      errp);
> +}
> +
> +static void
> +sev_snp_guest_set_init_flags(Object *obj, Visitor *v, const char *name,
> +                             void *opaque, Error **errp)
> +{
> +    visit_type_uint64(v, name,
> +                      (uint64_t *)&SEV_SNP_GUEST(obj)->kvm_init_conf.flags,
> +                      errp);
> +}
> +
> +static void
> +sev_snp_guest_get_policy(Object *obj, Visitor *v, const char *name,
> +                         void *opaque, Error **errp)
> +{
> +    visit_type_uint64(v, name,
> +                      (uint64_t *)&SEV_SNP_GUEST(obj)->kvm_start_conf.policy,
> +                      errp);
> +}
> +
> +static void
> +sev_snp_guest_set_policy(Object *obj, Visitor *v, const char *name,
> +                         void *opaque, Error **errp)
> +{
> +    visit_type_uint64(v, name,
> +                      (uint64_t *)&SEV_SNP_GUEST(obj)->kvm_start_conf.policy,
> +                      errp);
> +}
> +
> +static char *
> +sev_snp_guest_get_guest_visible_workarounds(Object *obj, Error **errp)
> +{
> +    return g_strdup(SEV_SNP_GUEST(obj)->guest_visible_workarounds);
> +}
> +
> +static void
> +sev_snp_guest_set_guest_visible_workarounds(Object *obj, const char *value,
> +                                            Error **errp)
> +{
> +    SevSnpGuestState *sev_snp_guest = SEV_SNP_GUEST(obj);
> +    struct kvm_sev_snp_launch_start *start = &sev_snp_guest->kvm_start_conf;
> +    g_autofree guchar *blob;
> +    gsize len;
> +
> +    if (sev_snp_guest->guest_visible_workarounds) {
> +        g_free(sev_snp_guest->guest_visible_workarounds);
> +    }
> +
> +    /* store the base64 str so we don't need to re-encode in getter */
> +    sev_snp_guest->guest_visible_workarounds = g_strdup(value);
> +
> +    blob = g_base64_decode(sev_snp_guest->guest_visible_workarounds, &len);

I see there's a qbase64_decode which performs some checks and then calls
g_base64_decode.  It might detect illegal chars in the value?

Also I think you should verify this decode succeeds by checking that
blob is not NULL.

(similar comments for all base64_decode calls in this file.)


> +    if (len > sizeof(start->gosvw)) {
> +        error_setg(errp, "parameter length of %lu exceeds max of %lu",
> +                   len, sizeof(start->gosvw));
> +        return;
> +    }
> +
> +    memcpy(start->gosvw, blob, len);
> +}
> +
> +static char *
> +sev_snp_guest_get_id_block(Object *obj, Error **errp)
> +{
> +    SevSnpGuestState *sev_snp_guest = SEV_SNP_GUEST(obj);
> +
> +    return g_strdup(sev_snp_guest->id_block);
> +}
> +
> +static void
> +sev_snp_guest_set_id_block(Object *obj, const char *value, Error **errp)
> +{
> +    SevSnpGuestState *sev_snp_guest = SEV_SNP_GUEST(obj);
> +    struct kvm_sev_snp_launch_finish *finish = &sev_snp_guest->kvm_finish_conf;
> +    gsize len;
> +
> +    if (sev_snp_guest->id_block) {
> +        g_free(sev_snp_guest->id_block);
> +        g_free((guchar *)finish->id_block_uaddr);
> +    }
> +
> +    /* store the base64 str so we don't need to re-encode in getter */
> +    sev_snp_guest->id_block = g_strdup(value);
> +
> +    finish->id_block_uaddr = (uint64_t)g_base64_decode(sev_snp_guest->id_block, &len);
> +    if (len > KVM_SEV_SNP_ID_BLOCK_SIZE) {
> +        error_setg(errp, "parameter length of %lu exceeds max of %u",
> +                   len, KVM_SEV_SNP_ID_BLOCK_SIZE);
> +        return;
> +    }
> +    finish->id_block_en = 1;

There's no way to set the id_block to a "don't want an ID block", except
for not giving this option to the sev-snp-guest object.  I'm not sure if
this is a problem (for example, if you dump one VM's config and try to
load it elsewhere).

Maybe if strlen(value)==0 you should set finish->id_block_en = 0.



> +}
> +
> +static char *
> +sev_snp_guest_get_id_auth(Object *obj, Error **errp)
> +{
> +    SevSnpGuestState *sev_snp_guest = SEV_SNP_GUEST(obj);
> +
> +    return g_strdup(sev_snp_guest->id_auth);
> +}
> +
> +static void
> +sev_snp_guest_set_id_auth(Object *obj, const char *value, Error **errp)
> +{
> +    SevSnpGuestState *sev_snp_guest = SEV_SNP_GUEST(obj);
> +    struct kvm_sev_snp_launch_finish *finish = &sev_snp_guest->kvm_finish_conf;
> +    gsize len;
> +
> +    if (sev_snp_guest->id_auth) {
> +        g_free(sev_snp_guest->id_auth);
> +        g_free((guchar *)finish->id_auth_uaddr);
> +    }
> +
> +    /* store the base64 str so we don't need to re-encode in getter */
> +    sev_snp_guest->id_auth = g_strdup(value);
> +
> +    finish->id_auth_uaddr = (uint64_t)g_base64_decode(sev_snp_guest->id_auth, &len);
> +    if (len > KVM_SEV_SNP_ID_AUTH_SIZE) {
> +        error_setg(errp, "parameter length of %lu exceeds max of %u",
> +                   len, KVM_SEV_SNP_ID_AUTH_SIZE);
> +        return;
> +    }
> +}
> +
> +static bool
> +sev_snp_guest_get_auth_key_en(Object *obj, Error **errp)
> +{
> +    SevSnpGuestState *sev_snp_guest = SEV_SNP_GUEST(obj);
> +
> +    return !!sev_snp_guest->kvm_finish_conf.auth_key_en;
> +}
> +
> +static void
> +sev_snp_guest_set_auth_key_en(Object *obj, bool value, Error **errp)
> +{
> +    SevSnpGuestState *sev_snp_guest = SEV_SNP_GUEST(obj);
> +
> +    sev_snp_guest->kvm_finish_conf.auth_key_en = value;
> +}
> +
> +static char *
> +sev_snp_guest_get_host_data(Object *obj, Error **errp)
> +{
> +    SevSnpGuestState *sev_snp_guest = SEV_SNP_GUEST(obj);
> +
> +    return g_strdup(sev_snp_guest->host_data);
> +}
> +
> +static void
> +sev_snp_guest_set_host_data(Object *obj, const char *value, Error **errp)
> +{
> +    SevSnpGuestState *sev_snp_guest = SEV_SNP_GUEST(obj);
> +    struct kvm_sev_snp_launch_finish *finish = &sev_snp_guest->kvm_finish_conf;
> +    g_autofree guchar *blob;
> +    gsize len;
> +
> +    if (sev_snp_guest->host_data) {
> +        g_free(sev_snp_guest->host_data);
> +    }
> +
> +    /* store the base64 str so we don't need to re-encode in getter */
> +    sev_snp_guest->host_data = g_strdup(value);
> +
> +    blob = g_base64_decode(sev_snp_guest->host_data, &len);
> +    if (len > sizeof(finish->host_data)) {
> +        error_setg(errp, "parameter length of %lu exceeds max of %lu",
> +                   len, sizeof(finish->host_data));
> +        return;
> +    }
> +
> +    memcpy(finish->host_data, blob, len);
> +}
> +
> +static void
> +sev_snp_guest_class_init(ObjectClass *oc, void *data)
> +{
> +    object_class_property_add(oc, "init-flags", "uint64",
> +                              sev_snp_guest_get_init_flags,
> +                              sev_snp_guest_set_init_flags, NULL, NULL);
> +    object_class_property_set_description(oc, "init-flags",
> +        "guest initialization flags");
> +    object_class_property_add(oc, "policy", "uint64",
> +                              sev_snp_guest_get_policy,
> +                              sev_snp_guest_set_policy, NULL, NULL);
> +    object_class_property_add_str(oc, "guest-visible-workarounds",
> +                                  sev_snp_guest_get_guest_visible_workarounds,
> +                                  sev_snp_guest_set_guest_visible_workarounds);
> +    object_class_property_add_str(oc, "id-block",
> +                                  sev_snp_guest_get_id_block,
> +                                  sev_snp_guest_set_id_block);
> +    object_class_property_add_str(oc, "id-auth",
> +                                  sev_snp_guest_get_id_auth,
> +                                  sev_snp_guest_set_id_auth);
> +    object_class_property_add_bool(oc, "auth-key-enabled",
> +                                   sev_snp_guest_get_auth_key_en,
> +                                   sev_snp_guest_set_auth_key_en);
> +    object_class_property_add_str(oc, "host-data",
> +                                  sev_snp_guest_get_host_data,
> +                                  sev_snp_guest_set_host_data);
> +}
> +
> +static void
> +sev_snp_guest_instance_init(Object *obj)
> +{
> +    SevSnpGuestState *sev_snp_guest = SEV_SNP_GUEST(obj);
> +
> +    /* default init/start/finish params for kvm */
> +    sev_snp_guest->kvm_start_conf.policy = DEFAULT_SEV_SNP_POLICY;
> +}
> +
> +/* guest info specific to sev-snp */
> +static const TypeInfo sev_snp_guest_info = {
> +    .parent = TYPE_SEV_COMMON,
> +    .name = TYPE_SEV_SNP_GUEST,
> +    .instance_size = sizeof(SevSnpGuestState),
> +    .class_init = sev_snp_guest_class_init,
> +    .instance_init = sev_snp_guest_instance_init,
> +};
> +
>  bool
>  sev_enabled(void)
>  {
> @@ -1136,6 +1378,7 @@ sev_register_types(void)
>  {
>      type_register_static(&sev_common_info);
>      type_register_static(&sev_guest_info);
> +    type_register_static(&sev_snp_guest_info);
>  }
>  
>  type_init(sev_register_types);
> 

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

* Re: [RFC PATCH v2 03/12] i386/sev: introduce 'sev-snp-guest' object
@ 2021-09-03 21:12     ` Dov Murik
  0 siblings, 0 replies; 61+ messages in thread
From: Dov Murik @ 2021-09-03 21:12 UTC (permalink / raw)
  To: Michael Roth, qemu-devel
  Cc: Tom Lendacky, Daniel P . Berrangé,
	Eduardo Habkost, kvm, Michael S . Tsirkin, Connor Kuehl,
	Eric Blake, James Bottomley, Dr . David Alan Gilbert,
	Markus Armbruster, Dov Murik, Brijesh Singh, Paolo Bonzini,
	Philippe Mathieu-Daudé,
	David Gibson



On 27/08/2021 1:26, Michael Roth wrote:
> From: Brijesh Singh <brijesh.singh@amd.com>
> 
> SEV-SNP support relies on a different set of properties/state than the
> existing 'sev-guest' object. This patch introduces the 'sev-snp-guest'
> object, which can be used to configure an SEV-SNP guest. For example,
> a default-configured SEV-SNP guest with no additional information
> passed in for use with attestation:
> 
>   -object sev-snp-guest,id=sev0
> 
> or a fully-specified SEV-SNP guest where all spec-defined binary
> blobs are passed in as base64-encoded strings:
> 
>   -object sev-snp-guest,id=sev0, \
>     policy=0x30000, \
>     init-flags=0, \
>     id-block=YWFhYWFhYWFhYWFhYWFhCg==, \
>     id-auth=CxHK/OKLkXGn/KpAC7Wl1FSiisWDbGTEKz..., \
>     auth-key-enabled=on, \
>     host-data=LNkCWBRC5CcdGXirbNUV1OrsR28s..., \
>     guest-visible-workarounds=AA==, \
> 
> See the QAPI schema updates included in this patch for more usage
> details.
> 
> In some cases these blobs may be up to 4096 characters, but this is
> generally well below the default limit for linux hosts where
> command-line sizes are defined by the sysconf-configurable ARG_MAX
> value, which defaults to 2097152 characters for Ubuntu hosts, for
> example.
> 
> Co-developed-by: Michael Roth <michael.roth@amd.com>
> Signed-off-by: Brijesh Singh <brijesh.singh@amd.com>
> Signed-off-by: Michael Roth <michael.roth@amd.com>
> ---
>  docs/amd-memory-encryption.txt |  77 ++++++++++-
>  qapi/qom.json                  |  60 ++++++++
>  target/i386/sev.c              | 245 ++++++++++++++++++++++++++++++++-
>  3 files changed, 379 insertions(+), 3 deletions(-)
> 
> diff --git a/docs/amd-memory-encryption.txt b/docs/amd-memory-encryption.txt
> index ffca382b5f..0d82e67fa1 100644
> --- a/docs/amd-memory-encryption.txt
> +++ b/docs/amd-memory-encryption.txt
> @@ -22,8 +22,8 @@ support for notifying a guest's operating system when certain types of VMEXITs
>  are about to occur. This allows the guest to selectively share information with
>  the hypervisor to satisfy the requested function.
>  
> -Launching
> ----------
> +Launching (SEV and SEV-ES)
> +--------------------------
>  Boot images (such as bios) must be encrypted before a guest can be booted. The
>  MEMORY_ENCRYPT_OP ioctl provides commands to encrypt the images: LAUNCH_START,
>  LAUNCH_UPDATE_DATA, LAUNCH_MEASURE and LAUNCH_FINISH. These four commands
> @@ -113,6 +113,79 @@ a SEV-ES guest:
>   - Requires in-kernel irqchip - the burden is placed on the hypervisor to
>     manage booting APs.
>  
> +Launching (SEV-SNP)
> +-------------------
> +Boot images (such as bios) must be encrypted before a guest can be booted. The
> +MEMORY_ENCRYPT_OP ioctl provides commands to encrypt the images:
> +KVM_SNP_INIT, SNP_LAUNCH_START, SNP_LAUNCH_UPDATE, and SNP_LAUNCH_FINISH. These
> +four commands together generate a fresh memory encryption key for the VM,
> +encrypt the boot images for a successful launch.
> +
> +KVM_SNP_INIT is called first to initialize the SEV-SNP firmware and SNP
> +features in the KVM. The feature flags value can be provided through the
> +'init-flags' property of the 'sev-snp-guest' object.
> +
> ++------------+-------+----------+---------------------------------+
> +| key        | type  | default  | meaning                         |
> ++------------+-------+----------+---------------------------------+
> +| init_flags | hex   | 0        | SNP feature flags               |
> ++-----------------------------------------------------------------+
> +
> +Note: currently the init_flags must be zero.
> +
> +SNP_LAUNCH_START is called first to create a cryptographic launch context
> +within the firmware. To create this context, guest owner must provide a guest
> +policy and other parameters as described in the SEV-SNP firmware
> +specification. The launch parameters should be specified as described in the
> +QAPI schema for the 'sev-snp-guest' object.
> +
> +The SNP_LAUNCH_START uses the following parameters (see the SEV-SNP
> +specification for more details):
> +
> ++--------+-------+----------+----------------------------------------------+
> +| key    | type  | default  | meaning                                      |
> ++--------+-------+----------+----------------------------------------------+
> +| policy | hex   | 0x30000  | a 64-bit guest policy                        |
> +| imi_en | bool  | 0        | 1 when IMI is enabled                        |
> +| ma_end | bool  | 0        | 1 when migration agent is used               |
> +| gosvw  | string| 0        | 16-byte base64 encoded string for the guest  |
> +|        |       |          | OS visible workaround.                       |
> ++--------+-------+----------+----------------------------------------------+
> +
> +SNP_LAUNCH_UPDATE encrypts the memory region using the cryptographic context
> +created via the SNP_LAUNCH_START command. If required, this command can be called
> +multiple times to encrypt different memory regions. The command also calculates
> +the measurement of the memory contents as it encrypts.
> +
> +SNP_LAUNCH_FINISH finalizes the guest launch flow. Optionally, while finalizing
> +the launch the firmware can perform checks on the launch digest computing
> +through the SNP_LAUNCH_UPDATE. To perform the check the user must supply
> +the id block, authentication blob and host data that should be included in the
> +attestation report. See the SEV-SNP spec for further details.
> +
> +The SNP_LAUNCH_FINISH uses the following parameters, which can be configured
> +by the corresponding parameters documented in the QAPI schema for the
> +'sev-snp-guest' object.
> +
> ++------------+-------+----------+----------------------------------------------+
> +| key        | type  | default  | meaning                                      |
> ++------------+-------+----------+----------------------------------------------+
> +| id_block   | string| none     | base64 encoded ID block                      |
> ++------------+-------+----------+----------------------------------------------+
> +| id_auth    | string| none     | base64 encoded authentication information    |
> ++------------+-------+----------+----------------------------------------------+
> +| auth_key_en| bool  | 0        | auth block contains author key               |
> ++------------+-------+----------+----------------------------------------------+
> +| host_data  | string| none     | host provided data                           |
> ++------------+-------+----------+----------------------------------------------+
> +
> +To launch a SEV-SNP guest (additional parameters are documented in the QAPI
> +schema for the 'sev-snp-guest' object):
> +
> +# ${QEMU} \
> +    -machine ...,confidential-guest-support=sev0 \
> +    -object sev-snp-guest,id=sev0,cbitpos=51,reduced-phys-bits=1
> +
>  Debugging
>  -----------
>  Since the memory contents of a SEV guest are encrypted, hypervisor access to
> diff --git a/qapi/qom.json b/qapi/qom.json
> index 211e083727..ea39585026 100644
> --- a/qapi/qom.json
> +++ b/qapi/qom.json
> @@ -775,6 +775,64 @@
>              '*policy': 'uint32',
>              '*handle': 'uint32' } }
>  
> +##
> +# @SevSnpGuestProperties:
> +#
> +# Properties for sev-snp-guest objects. Many of these are direct arguments
> +# for the SEV-SNP KVM interfaces documented in the linux kernel source
> +# documentation under 'amd-memory-encryption.rst'. Additional documentation
> +# is also available in the QEMU source tree under
> +# 'amd-memory-encryption.rst'.
> +#
> +# In addition to those files, please see the SEV-SNP Firmware Specification
> +# (Rev 0.9) documentation for the SNP_INIT and
> +# SNP_LAUNCH_{START,UPDATE,FINISH} firmware interfaces, which the KVM
> +# interfaces are written against.
> +#
> +# @init-flags: as documented for the 'flags' parameter of the
> +#              KVM_SNP_INIT KVM command (default: 0)
> +#
> +# @policy: as documented for the 'policy' parameter of the
> +#          KVM_SNP_LAUNCH_START KVM command (default: 0x30000)
> +#
> +# @guest-visible-workarounds: 16-byte, base64-encoded blob to report
> +#                             hypervisor-defined workarounds, as documented
> +#                             for the 'gosvm' parameter of the

typo: s/gosvm/gosvw/


> +#                             KVM_SNP_LAUNCH_START KVM command.
> +#                             (default: all-zero)
> +#
> +# @id-block: 8-byte, base64-encoded blob to provide the ID Block
> +#            structure documented in SEV-SNP spec, as documented for the
> +#            'id_block_uaddr' parameter of the KVM_SNP_LAUNCH_FINISH
> +#            command (default: all-zero)

The documentation says the ID Block is 96 bytes long (Table 65 in
section 8.15 of the SNP FW ABI document).


> +#
> +# @id-auth: 4096-byte, base64-encoded blob to provide the ID Authentication
> +#           Information Structure documented in SEV-SNP spec, as documented
> +#           for the 'id_auth_uaddr' parameter of the KVM_SNP_LAUNCH_FINISH
> +#           command (default: all-zero)
> +#
> +# @auth-key-enabled: true if 'id-auth' blob contains the Author Key
> +#                    documented in the SEV-SNP spec, as documented for the
> +#                    'auth_key_en' parameter of the KVM_SNP_LAUNCH_FINISH
> +#                    command (default: false)
> +#
> +# @host-data: 32-byte, base64-encoded user-defined blob to provide to the
> +#             guest, as documented for the 'host_data' parameter of the
> +#             KVM_SNP_LAUNCH_FINISH command (default: all-zero)
> +#
> +# Since: 6.2
> +##
> +{ 'struct': 'SevSnpGuestProperties',
> +  'base': 'SevCommonProperties',
> +  'data': {
> +            '*init-flags': 'uint64',
> +            '*policy': 'uint64',
> +            '*guest-visible-workarounds': 'str',
> +            '*id-block': 'str',
> +            '*id-auth': 'str',
> +            '*auth-key-enabled': 'bool',
> +            '*host-data': 'str' } }
> +
>  ##
>  # @ObjectType:
>  #
> @@ -816,6 +874,7 @@
>      'secret',
>      'secret_keyring',
>      'sev-guest',
> +    'sev-snp-guest',
>      's390-pv-guest',
>      'throttle-group',
>      'tls-creds-anon',
> @@ -873,6 +932,7 @@
>        'secret':                     'SecretProperties',
>        'secret_keyring':             'SecretKeyringProperties',
>        'sev-guest':                  'SevGuestProperties',
> +      'sev-snp-guest':              'SevSnpGuestProperties',
>        'throttle-group':             'ThrottleGroupProperties',
>        'tls-creds-anon':             'TlsCredsAnonProperties',
>        'tls-creds-psk':              'TlsCredsPskProperties',
> diff --git a/target/i386/sev.c b/target/i386/sev.c
> index 6acebfbd53..ba08b7d3ab 100644
> --- a/target/i386/sev.c
> +++ b/target/i386/sev.c
> @@ -38,7 +38,8 @@
>  OBJECT_DECLARE_SIMPLE_TYPE(SevCommonState, SEV_COMMON)
>  #define TYPE_SEV_GUEST "sev-guest"
>  OBJECT_DECLARE_SIMPLE_TYPE(SevGuestState, SEV_GUEST)
> -
> +#define TYPE_SEV_SNP_GUEST "sev-snp-guest"
> +OBJECT_DECLARE_SIMPLE_TYPE(SevSnpGuestState, SEV_SNP_GUEST)
>  
>  /**
>   * SevGuestState:
> @@ -82,8 +83,23 @@ struct SevGuestState {
>      char *session_file;
>  };
>  
> +struct SevSnpGuestState {
> +    SevCommonState sev_common;
> +
> +    /* configuration parameters */
> +    char *guest_visible_workarounds;
> +    char *id_block;
> +    char *id_auth;
> +    char *host_data;
> +
> +    struct kvm_snp_init kvm_init_conf;
> +    struct kvm_sev_snp_launch_start kvm_start_conf;
> +    struct kvm_sev_snp_launch_finish kvm_finish_conf;
> +};
> +
>  #define DEFAULT_GUEST_POLICY    0x1 /* disable debug */
>  #define DEFAULT_SEV_DEVICE      "/dev/sev"
> +#define DEFAULT_SEV_SNP_POLICY  0x30000
>  
>  #define SEV_INFO_BLOCK_GUID     "00f771de-1a7e-4fcb-890e-68c77e2fb44e"
>  typedef struct __attribute__((__packed__)) SevInfoBlock {
> @@ -364,6 +380,232 @@ static const TypeInfo sev_guest_info = {
>      .class_init = sev_guest_class_init,
>  };
>  
> +static void
> +sev_snp_guest_get_init_flags(Object *obj, Visitor *v, const char *name,
> +                             void *opaque, Error **errp)
> +{
> +    visit_type_uint64(v, name,
> +                      (uint64_t *)&SEV_SNP_GUEST(obj)->kvm_init_conf.flags,
> +                      errp);
> +}
> +
> +static void
> +sev_snp_guest_set_init_flags(Object *obj, Visitor *v, const char *name,
> +                             void *opaque, Error **errp)
> +{
> +    visit_type_uint64(v, name,
> +                      (uint64_t *)&SEV_SNP_GUEST(obj)->kvm_init_conf.flags,
> +                      errp);
> +}
> +
> +static void
> +sev_snp_guest_get_policy(Object *obj, Visitor *v, const char *name,
> +                         void *opaque, Error **errp)
> +{
> +    visit_type_uint64(v, name,
> +                      (uint64_t *)&SEV_SNP_GUEST(obj)->kvm_start_conf.policy,
> +                      errp);
> +}
> +
> +static void
> +sev_snp_guest_set_policy(Object *obj, Visitor *v, const char *name,
> +                         void *opaque, Error **errp)
> +{
> +    visit_type_uint64(v, name,
> +                      (uint64_t *)&SEV_SNP_GUEST(obj)->kvm_start_conf.policy,
> +                      errp);
> +}
> +
> +static char *
> +sev_snp_guest_get_guest_visible_workarounds(Object *obj, Error **errp)
> +{
> +    return g_strdup(SEV_SNP_GUEST(obj)->guest_visible_workarounds);
> +}
> +
> +static void
> +sev_snp_guest_set_guest_visible_workarounds(Object *obj, const char *value,
> +                                            Error **errp)
> +{
> +    SevSnpGuestState *sev_snp_guest = SEV_SNP_GUEST(obj);
> +    struct kvm_sev_snp_launch_start *start = &sev_snp_guest->kvm_start_conf;
> +    g_autofree guchar *blob;
> +    gsize len;
> +
> +    if (sev_snp_guest->guest_visible_workarounds) {
> +        g_free(sev_snp_guest->guest_visible_workarounds);
> +    }
> +
> +    /* store the base64 str so we don't need to re-encode in getter */
> +    sev_snp_guest->guest_visible_workarounds = g_strdup(value);
> +
> +    blob = g_base64_decode(sev_snp_guest->guest_visible_workarounds, &len);

I see there's a qbase64_decode which performs some checks and then calls
g_base64_decode.  It might detect illegal chars in the value?

Also I think you should verify this decode succeeds by checking that
blob is not NULL.

(similar comments for all base64_decode calls in this file.)


> +    if (len > sizeof(start->gosvw)) {
> +        error_setg(errp, "parameter length of %lu exceeds max of %lu",
> +                   len, sizeof(start->gosvw));
> +        return;
> +    }
> +
> +    memcpy(start->gosvw, blob, len);
> +}
> +
> +static char *
> +sev_snp_guest_get_id_block(Object *obj, Error **errp)
> +{
> +    SevSnpGuestState *sev_snp_guest = SEV_SNP_GUEST(obj);
> +
> +    return g_strdup(sev_snp_guest->id_block);
> +}
> +
> +static void
> +sev_snp_guest_set_id_block(Object *obj, const char *value, Error **errp)
> +{
> +    SevSnpGuestState *sev_snp_guest = SEV_SNP_GUEST(obj);
> +    struct kvm_sev_snp_launch_finish *finish = &sev_snp_guest->kvm_finish_conf;
> +    gsize len;
> +
> +    if (sev_snp_guest->id_block) {
> +        g_free(sev_snp_guest->id_block);
> +        g_free((guchar *)finish->id_block_uaddr);
> +    }
> +
> +    /* store the base64 str so we don't need to re-encode in getter */
> +    sev_snp_guest->id_block = g_strdup(value);
> +
> +    finish->id_block_uaddr = (uint64_t)g_base64_decode(sev_snp_guest->id_block, &len);
> +    if (len > KVM_SEV_SNP_ID_BLOCK_SIZE) {
> +        error_setg(errp, "parameter length of %lu exceeds max of %u",
> +                   len, KVM_SEV_SNP_ID_BLOCK_SIZE);
> +        return;
> +    }
> +    finish->id_block_en = 1;

There's no way to set the id_block to a "don't want an ID block", except
for not giving this option to the sev-snp-guest object.  I'm not sure if
this is a problem (for example, if you dump one VM's config and try to
load it elsewhere).

Maybe if strlen(value)==0 you should set finish->id_block_en = 0.



> +}
> +
> +static char *
> +sev_snp_guest_get_id_auth(Object *obj, Error **errp)
> +{
> +    SevSnpGuestState *sev_snp_guest = SEV_SNP_GUEST(obj);
> +
> +    return g_strdup(sev_snp_guest->id_auth);
> +}
> +
> +static void
> +sev_snp_guest_set_id_auth(Object *obj, const char *value, Error **errp)
> +{
> +    SevSnpGuestState *sev_snp_guest = SEV_SNP_GUEST(obj);
> +    struct kvm_sev_snp_launch_finish *finish = &sev_snp_guest->kvm_finish_conf;
> +    gsize len;
> +
> +    if (sev_snp_guest->id_auth) {
> +        g_free(sev_snp_guest->id_auth);
> +        g_free((guchar *)finish->id_auth_uaddr);
> +    }
> +
> +    /* store the base64 str so we don't need to re-encode in getter */
> +    sev_snp_guest->id_auth = g_strdup(value);
> +
> +    finish->id_auth_uaddr = (uint64_t)g_base64_decode(sev_snp_guest->id_auth, &len);
> +    if (len > KVM_SEV_SNP_ID_AUTH_SIZE) {
> +        error_setg(errp, "parameter length of %lu exceeds max of %u",
> +                   len, KVM_SEV_SNP_ID_AUTH_SIZE);
> +        return;
> +    }
> +}
> +
> +static bool
> +sev_snp_guest_get_auth_key_en(Object *obj, Error **errp)
> +{
> +    SevSnpGuestState *sev_snp_guest = SEV_SNP_GUEST(obj);
> +
> +    return !!sev_snp_guest->kvm_finish_conf.auth_key_en;
> +}
> +
> +static void
> +sev_snp_guest_set_auth_key_en(Object *obj, bool value, Error **errp)
> +{
> +    SevSnpGuestState *sev_snp_guest = SEV_SNP_GUEST(obj);
> +
> +    sev_snp_guest->kvm_finish_conf.auth_key_en = value;
> +}
> +
> +static char *
> +sev_snp_guest_get_host_data(Object *obj, Error **errp)
> +{
> +    SevSnpGuestState *sev_snp_guest = SEV_SNP_GUEST(obj);
> +
> +    return g_strdup(sev_snp_guest->host_data);
> +}
> +
> +static void
> +sev_snp_guest_set_host_data(Object *obj, const char *value, Error **errp)
> +{
> +    SevSnpGuestState *sev_snp_guest = SEV_SNP_GUEST(obj);
> +    struct kvm_sev_snp_launch_finish *finish = &sev_snp_guest->kvm_finish_conf;
> +    g_autofree guchar *blob;
> +    gsize len;
> +
> +    if (sev_snp_guest->host_data) {
> +        g_free(sev_snp_guest->host_data);
> +    }
> +
> +    /* store the base64 str so we don't need to re-encode in getter */
> +    sev_snp_guest->host_data = g_strdup(value);
> +
> +    blob = g_base64_decode(sev_snp_guest->host_data, &len);
> +    if (len > sizeof(finish->host_data)) {
> +        error_setg(errp, "parameter length of %lu exceeds max of %lu",
> +                   len, sizeof(finish->host_data));
> +        return;
> +    }
> +
> +    memcpy(finish->host_data, blob, len);
> +}
> +
> +static void
> +sev_snp_guest_class_init(ObjectClass *oc, void *data)
> +{
> +    object_class_property_add(oc, "init-flags", "uint64",
> +                              sev_snp_guest_get_init_flags,
> +                              sev_snp_guest_set_init_flags, NULL, NULL);
> +    object_class_property_set_description(oc, "init-flags",
> +        "guest initialization flags");
> +    object_class_property_add(oc, "policy", "uint64",
> +                              sev_snp_guest_get_policy,
> +                              sev_snp_guest_set_policy, NULL, NULL);
> +    object_class_property_add_str(oc, "guest-visible-workarounds",
> +                                  sev_snp_guest_get_guest_visible_workarounds,
> +                                  sev_snp_guest_set_guest_visible_workarounds);
> +    object_class_property_add_str(oc, "id-block",
> +                                  sev_snp_guest_get_id_block,
> +                                  sev_snp_guest_set_id_block);
> +    object_class_property_add_str(oc, "id-auth",
> +                                  sev_snp_guest_get_id_auth,
> +                                  sev_snp_guest_set_id_auth);
> +    object_class_property_add_bool(oc, "auth-key-enabled",
> +                                   sev_snp_guest_get_auth_key_en,
> +                                   sev_snp_guest_set_auth_key_en);
> +    object_class_property_add_str(oc, "host-data",
> +                                  sev_snp_guest_get_host_data,
> +                                  sev_snp_guest_set_host_data);
> +}
> +
> +static void
> +sev_snp_guest_instance_init(Object *obj)
> +{
> +    SevSnpGuestState *sev_snp_guest = SEV_SNP_GUEST(obj);
> +
> +    /* default init/start/finish params for kvm */
> +    sev_snp_guest->kvm_start_conf.policy = DEFAULT_SEV_SNP_POLICY;
> +}
> +
> +/* guest info specific to sev-snp */
> +static const TypeInfo sev_snp_guest_info = {
> +    .parent = TYPE_SEV_COMMON,
> +    .name = TYPE_SEV_SNP_GUEST,
> +    .instance_size = sizeof(SevSnpGuestState),
> +    .class_init = sev_snp_guest_class_init,
> +    .instance_init = sev_snp_guest_instance_init,
> +};
> +
>  bool
>  sev_enabled(void)
>  {
> @@ -1136,6 +1378,7 @@ sev_register_types(void)
>  {
>      type_register_static(&sev_common_info);
>      type_register_static(&sev_guest_info);
> +    type_register_static(&sev_snp_guest_info);
>  }
>  
>  type_init(sev_register_types);
> 


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

* Re: [RFC PATCH v2 12/12] i386/sev: update query-sev QAPI format to handle SEV-SNP
  2021-09-03 16:01       ` Daniel P. Berrangé
  (?)
@ 2021-09-04  5:41       ` Markus Armbruster
  -1 siblings, 0 replies; 61+ messages in thread
From: Markus Armbruster @ 2021-09-04  5:41 UTC (permalink / raw)
  To: Daniel P. Berrangé
  Cc: Tom Lendacky, Brijesh Singh, Eduardo Habkost, kvm,
	Michael S . Tsirkin, Connor Kuehl, Michael Roth, James Bottomley,
	qemu-devel, Eric Blake, Dr . David Alan Gilbert, Dov Murik,
	Paolo Bonzini, Philippe Mathieu-Daudé,
	David Gibson

Daniel P. Berrangé <berrange@redhat.com> writes:

> On Wed, Sep 01, 2021 at 04:14:10PM +0200, Markus Armbruster wrote:
>> Michael Roth <michael.roth@amd.com> writes:
>> 
>> > Most of the current 'query-sev' command is relevant to both legacy
>> > SEV/SEV-ES guests and SEV-SNP guests, with 2 exceptions:
>> >
>> >   - 'policy' is a 64-bit field for SEV-SNP, not 32-bit, and
>> >     the meaning of the bit positions has changed
>> >   - 'handle' is not relevant to SEV-SNP
>> >
>> > To address this, this patch adds a new 'sev-type' field that can be
>> > used as a discriminator to select between SEV and SEV-SNP-specific
>> > fields/formats without breaking compatibility for existing management
>> > tools (so long as management tools that add support for launching
>> > SEV-SNP guest update their handling of query-sev appropriately).
>> 
>> Technically a compatibility break: query-sev can now return an object
>> that whose member @policy has different meaning, and also lacks @handle.
>> 
>> Matrix:
>> 
>>                             Old mgmt app    New mgmt app
>>     Old QEMU, SEV/SEV-ES       good            good(1)
>>     New QEMU, SEV/SEV-ES       good(2)         good
>>     New QEMU, SEV-SNP           bad(3)         good
>> 
>> Notes:
>> 
>> (1) As long as the management application can cope with absent member
>> @sev-type.
>> 
>> (2) As long as the management application ignores unknown member
>> @sev-type.
>> 
>> (3) Management application may choke on missing member @handle, or
>> worse, misinterpret member @policy.  Can only happen when something
>> other than the management application created the SEV-SNP guest (or the
>> user somehow made the management application create one even though it
>> doesn't know how, say with CLI option passthrough, but that's always
>> fragile, and I wouldn't worry about it here).
>> 
>> I think (1) and (2) are reasonable.  (3) is an issue for management
>> applications that support attaching to existing guests.  Thoughts?
>
> IIUC you can only reach scenario (3) if you have created a guest
> using '-object sev-snp-guest', which is a new feature introduced
> in patch 2.
>
> IOW, scenario (3)  old mgmt app + new QEMU + sev-snp guest does
> not exist as a combination. Thus the (bad) field is actually (n/a)
>
> So I believe this proposed change is acceptable in all scenarios
> with existing deployed usage, as well as all newly introduced
> scenarios.

Let's work this into the commit message.



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

* Re: [RFC PATCH v2 04/12] i386/sev: initialize SNP context
  2021-08-26 22:26 ` [RFC PATCH v2 04/12] i386/sev: initialize SNP context Michael Roth
@ 2021-09-05  7:07     ` Dov Murik
  2021-09-05  9:19     ` Dov Murik
  1 sibling, 0 replies; 61+ messages in thread
From: Dov Murik @ 2021-09-05  7:07 UTC (permalink / raw)
  To: Michael Roth, qemu-devel
  Cc: Connor Kuehl, Philippe Mathieu-Daudé,
	Michael S . Tsirkin, James Bottomley, Dr . David Alan Gilbert,
	Tom Lendacky, Paolo Bonzini, David Gibson,
	Daniel P . Berrangé,
	kvm, Eduardo Habkost, Brijesh Singh, Markus Armbruster,
	Eric Blake, Dov Murik

Hi Michael,

On 27/08/2021 1:26, Michael Roth wrote:
> From: Brijesh Singh <brijesh.singh@amd.com>
> 
> When SEV-SNP is enabled, the KVM_SNP_INIT command is used to initialize
> the platform. The command checks whether SNP is enabled in the KVM, if
> enabled then it allocates a new ASID from the SNP pool and calls the
> firmware to initialize the all the resources.
> 
> Signed-off-by: Brijesh Singh <brijesh.singh@amd.com>
> Signed-off-by: Michael Roth <michael.roth@amd.com>
> ---
>  target/i386/sev-stub.c |  6 ++++++
>  target/i386/sev.c      | 27 ++++++++++++++++++++++++---
>  target/i386/sev_i386.h |  1 +
>  3 files changed, 31 insertions(+), 3 deletions(-)
> 
> diff --git a/target/i386/sev-stub.c b/target/i386/sev-stub.c
> index 0227cb5177..e4fb8e882e 100644
> --- a/target/i386/sev-stub.c
> +++ b/target/i386/sev-stub.c
> @@ -81,3 +81,9 @@ sev_get_attestation_report(const char *mnonce, Error **errp)
>      error_setg(errp, "SEV is not available in this QEMU");
>      return NULL;
>  }
> +
> +bool
> +sev_snp_enabled(void)
> +{
> +    return false;
> +}
> diff --git a/target/i386/sev.c b/target/i386/sev.c
> index ba08b7d3ab..b8bd6ed9ea 100644
> --- a/target/i386/sev.c
> +++ b/target/i386/sev.c
> @@ -614,12 +614,21 @@ sev_enabled(void)
>      return !!object_dynamic_cast(OBJECT(cgs), TYPE_SEV_COMMON);
>  }
>  
> +bool
> +sev_snp_enabled(void)
> +{
> +    ConfidentialGuestSupport *cgs = MACHINE(qdev_get_machine())->cgs;
> +
> +    return !!object_dynamic_cast(OBJECT(cgs), TYPE_SEV_SNP_GUEST);
> +}
> +
>  bool
>  sev_es_enabled(void)
>  {
>      ConfidentialGuestSupport *cgs = MACHINE(qdev_get_machine())->cgs;
>  
> -    return sev_enabled() && (SEV_GUEST(cgs)->policy & SEV_POLICY_ES);
> +    return sev_snp_enabled() ||
> +            (sev_enabled() && SEV_GUEST(cgs)->policy & SEV_POLICY_ES);
>  }
>  
>  uint64_t
> @@ -1074,6 +1083,7 @@ int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp)
>      uint32_t ebx;
>      uint32_t host_cbitpos;
>      struct sev_user_data_status status = {};
> +    void *init_args = NULL;
>  
>      if (!sev_common) {
>          return 0;
> @@ -1126,7 +1136,18 @@ int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp)
>      sev_common->api_major = status.api_major;
>      sev_common->api_minor = status.api_minor;

Not visible here in the context: the code here is using the
SEV_PLATFORM_STATUS command to get the build_id, api_major, and api_minor.

I see that SNP has a new command SNP_PLATFORM_STATUS, which fills a
struct sev_data_snp_platform_status (hmmm, I can't find the struct's
definition; I assume it should look like Table 38 in 8.3.2 in SNP FW ABI
document).

My questions are:

1. Is it OK to call the "legacy" SEV_PLATFORM_STATUS when about to init
an SNP guest?
2. Do we want to save some info like installed TCB version and reported
TCB version, and maybe other fields from SNP platform status?
3. Should we check the state field in the platform status?



>  
> -    if (sev_es_enabled()) {
> +    if (sev_snp_enabled()) {
> +        SevSnpGuestState *sev_snp_guest = SEV_SNP_GUEST(sev_common);
> +        if (!kvm_kernel_irqchip_allowed()) {
> +            error_report("%s: SEV-SNP guests require in-kernel irqchip support",
> +                         __func__);

Most errors in this function use error_setg(errp, ...).  This should follow.


> +            goto err;
> +        }
> +
> +        cmd = KVM_SEV_SNP_INIT;
> +        init_args = (void *)&sev_snp_guest->kvm_init_conf;
> +
> +    } else if (sev_es_enabled()) {
>          if (!kvm_kernel_irqchip_allowed()) {
>              error_report("%s: SEV-ES guests require in-kernel irqchip support",
>                           __func__);

Not part of this patch, but this error_report (and another one in the
SEV-ES case) should be converted to error_setg similarly.  Maybe add a
separate patch for fixing this for SEV-ES.



> @@ -1145,7 +1166,7 @@ int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp)
>      }
>  
>      trace_kvm_sev_init();

Suggestions:

1. log the guest type (SEV / SEV-ES / SEV-SNP)
2. log the SNP init flags value when initializing an SNP guest


-Dov

> -    ret = sev_ioctl(sev_common->sev_fd, cmd, NULL, &fw_error);
> +    ret = sev_ioctl(sev_common->sev_fd, cmd, init_args, &fw_error);
>      if (ret) {
>          error_setg(errp, "%s: failed to initialize ret=%d fw_error=%d '%s'",
>                     __func__, ret, fw_error, fw_error_to_str(fw_error));
> diff --git a/target/i386/sev_i386.h b/target/i386/sev_i386.h
> index ae6d840478..e0e1a599be 100644
> --- a/target/i386/sev_i386.h
> +++ b/target/i386/sev_i386.h
> @@ -29,6 +29,7 @@
>  #define SEV_POLICY_SEV          0x20
>  
>  extern bool sev_es_enabled(void);
> +extern bool sev_snp_enabled(void);
>  extern uint64_t sev_get_me_mask(void);
>  extern SevInfo *sev_get_info(void);
>  extern uint32_t sev_get_cbit_position(void);
> 

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

* Re: [RFC PATCH v2 04/12] i386/sev: initialize SNP context
@ 2021-09-05  7:07     ` Dov Murik
  0 siblings, 0 replies; 61+ messages in thread
From: Dov Murik @ 2021-09-05  7:07 UTC (permalink / raw)
  To: Michael Roth, qemu-devel
  Cc: Tom Lendacky, Daniel P . Berrangé,
	Eduardo Habkost, kvm, Michael S . Tsirkin, Connor Kuehl,
	Eric Blake, James Bottomley, Dr . David Alan Gilbert,
	Markus Armbruster, Dov Murik, Brijesh Singh, Paolo Bonzini,
	Philippe Mathieu-Daudé,
	David Gibson

Hi Michael,

On 27/08/2021 1:26, Michael Roth wrote:
> From: Brijesh Singh <brijesh.singh@amd.com>
> 
> When SEV-SNP is enabled, the KVM_SNP_INIT command is used to initialize
> the platform. The command checks whether SNP is enabled in the KVM, if
> enabled then it allocates a new ASID from the SNP pool and calls the
> firmware to initialize the all the resources.
> 
> Signed-off-by: Brijesh Singh <brijesh.singh@amd.com>
> Signed-off-by: Michael Roth <michael.roth@amd.com>
> ---
>  target/i386/sev-stub.c |  6 ++++++
>  target/i386/sev.c      | 27 ++++++++++++++++++++++++---
>  target/i386/sev_i386.h |  1 +
>  3 files changed, 31 insertions(+), 3 deletions(-)
> 
> diff --git a/target/i386/sev-stub.c b/target/i386/sev-stub.c
> index 0227cb5177..e4fb8e882e 100644
> --- a/target/i386/sev-stub.c
> +++ b/target/i386/sev-stub.c
> @@ -81,3 +81,9 @@ sev_get_attestation_report(const char *mnonce, Error **errp)
>      error_setg(errp, "SEV is not available in this QEMU");
>      return NULL;
>  }
> +
> +bool
> +sev_snp_enabled(void)
> +{
> +    return false;
> +}
> diff --git a/target/i386/sev.c b/target/i386/sev.c
> index ba08b7d3ab..b8bd6ed9ea 100644
> --- a/target/i386/sev.c
> +++ b/target/i386/sev.c
> @@ -614,12 +614,21 @@ sev_enabled(void)
>      return !!object_dynamic_cast(OBJECT(cgs), TYPE_SEV_COMMON);
>  }
>  
> +bool
> +sev_snp_enabled(void)
> +{
> +    ConfidentialGuestSupport *cgs = MACHINE(qdev_get_machine())->cgs;
> +
> +    return !!object_dynamic_cast(OBJECT(cgs), TYPE_SEV_SNP_GUEST);
> +}
> +
>  bool
>  sev_es_enabled(void)
>  {
>      ConfidentialGuestSupport *cgs = MACHINE(qdev_get_machine())->cgs;
>  
> -    return sev_enabled() && (SEV_GUEST(cgs)->policy & SEV_POLICY_ES);
> +    return sev_snp_enabled() ||
> +            (sev_enabled() && SEV_GUEST(cgs)->policy & SEV_POLICY_ES);
>  }
>  
>  uint64_t
> @@ -1074,6 +1083,7 @@ int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp)
>      uint32_t ebx;
>      uint32_t host_cbitpos;
>      struct sev_user_data_status status = {};
> +    void *init_args = NULL;
>  
>      if (!sev_common) {
>          return 0;
> @@ -1126,7 +1136,18 @@ int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp)
>      sev_common->api_major = status.api_major;
>      sev_common->api_minor = status.api_minor;

Not visible here in the context: the code here is using the
SEV_PLATFORM_STATUS command to get the build_id, api_major, and api_minor.

I see that SNP has a new command SNP_PLATFORM_STATUS, which fills a
struct sev_data_snp_platform_status (hmmm, I can't find the struct's
definition; I assume it should look like Table 38 in 8.3.2 in SNP FW ABI
document).

My questions are:

1. Is it OK to call the "legacy" SEV_PLATFORM_STATUS when about to init
an SNP guest?
2. Do we want to save some info like installed TCB version and reported
TCB version, and maybe other fields from SNP platform status?
3. Should we check the state field in the platform status?



>  
> -    if (sev_es_enabled()) {
> +    if (sev_snp_enabled()) {
> +        SevSnpGuestState *sev_snp_guest = SEV_SNP_GUEST(sev_common);
> +        if (!kvm_kernel_irqchip_allowed()) {
> +            error_report("%s: SEV-SNP guests require in-kernel irqchip support",
> +                         __func__);

Most errors in this function use error_setg(errp, ...).  This should follow.


> +            goto err;
> +        }
> +
> +        cmd = KVM_SEV_SNP_INIT;
> +        init_args = (void *)&sev_snp_guest->kvm_init_conf;
> +
> +    } else if (sev_es_enabled()) {
>          if (!kvm_kernel_irqchip_allowed()) {
>              error_report("%s: SEV-ES guests require in-kernel irqchip support",
>                           __func__);

Not part of this patch, but this error_report (and another one in the
SEV-ES case) should be converted to error_setg similarly.  Maybe add a
separate patch for fixing this for SEV-ES.



> @@ -1145,7 +1166,7 @@ int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp)
>      }
>  
>      trace_kvm_sev_init();

Suggestions:

1. log the guest type (SEV / SEV-ES / SEV-SNP)
2. log the SNP init flags value when initializing an SNP guest


-Dov

> -    ret = sev_ioctl(sev_common->sev_fd, cmd, NULL, &fw_error);
> +    ret = sev_ioctl(sev_common->sev_fd, cmd, init_args, &fw_error);
>      if (ret) {
>          error_setg(errp, "%s: failed to initialize ret=%d fw_error=%d '%s'",
>                     __func__, ret, fw_error, fw_error_to_str(fw_error));
> diff --git a/target/i386/sev_i386.h b/target/i386/sev_i386.h
> index ae6d840478..e0e1a599be 100644
> --- a/target/i386/sev_i386.h
> +++ b/target/i386/sev_i386.h
> @@ -29,6 +29,7 @@
>  #define SEV_POLICY_SEV          0x20
>  
>  extern bool sev_es_enabled(void);
> +extern bool sev_snp_enabled(void);
>  extern uint64_t sev_get_me_mask(void);
>  extern SevInfo *sev_get_info(void);
>  extern uint32_t sev_get_cbit_position(void);
> 


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

* Re: [RFC PATCH v2 04/12] i386/sev: initialize SNP context
  2021-08-26 22:26 ` [RFC PATCH v2 04/12] i386/sev: initialize SNP context Michael Roth
@ 2021-09-05  9:19     ` Dov Murik
  2021-09-05  9:19     ` Dov Murik
  1 sibling, 0 replies; 61+ messages in thread
From: Dov Murik @ 2021-09-05  9:19 UTC (permalink / raw)
  To: Michael Roth, qemu-devel
  Cc: Connor Kuehl, Philippe Mathieu-Daudé,
	Michael S . Tsirkin, James Bottomley, Dr . David Alan Gilbert,
	Tom Lendacky, Paolo Bonzini, David Gibson,
	Daniel P . Berrangé,
	kvm, Eduardo Habkost, Brijesh Singh, Markus Armbruster,
	Eric Blake, Dov Murik



On 27/08/2021 1:26, Michael Roth wrote:
> From: Brijesh Singh <brijesh.singh@amd.com>
> 
> When SEV-SNP is enabled, the KVM_SNP_INIT command is used to initialize
> the platform. The command checks whether SNP is enabled in the KVM, if
> enabled then it allocates a new ASID from the SNP pool and calls the
> firmware to initialize the all the resources.
> 


From the KVM code ("[PATCH Part2 v5 24/45] KVM: SVM: Add
KVM_SEV_SNP_LAUNCH_START command") it seems that KVM_SNP_INIT does *not*
allocate the ASID; actually this is done in KVM_SEV_SNP_LAUNCH_START.

If that is indeed the case, I suggest removing this sentence here and
adding it in the appropriate QEMU step (patch 5?).

-Dov



> Signed-off-by: Brijesh Singh <brijesh.singh@amd.com>
> Signed-off-by: Michael Roth <michael.roth@amd.com>
> ---
>  target/i386/sev-stub.c |  6 ++++++
>  target/i386/sev.c      | 27 ++++++++++++++++++++++++---
>  target/i386/sev_i386.h |  1 +
>  3 files changed, 31 insertions(+), 3 deletions(-)
> 

[...]

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

* Re: [RFC PATCH v2 04/12] i386/sev: initialize SNP context
@ 2021-09-05  9:19     ` Dov Murik
  0 siblings, 0 replies; 61+ messages in thread
From: Dov Murik @ 2021-09-05  9:19 UTC (permalink / raw)
  To: Michael Roth, qemu-devel
  Cc: Tom Lendacky, Daniel P . Berrangé,
	Eduardo Habkost, kvm, Michael S . Tsirkin, Connor Kuehl,
	Eric Blake, James Bottomley, Dr . David Alan Gilbert,
	Markus Armbruster, Dov Murik, Brijesh Singh, Paolo Bonzini,
	Philippe Mathieu-Daudé,
	David Gibson



On 27/08/2021 1:26, Michael Roth wrote:
> From: Brijesh Singh <brijesh.singh@amd.com>
> 
> When SEV-SNP is enabled, the KVM_SNP_INIT command is used to initialize
> the platform. The command checks whether SNP is enabled in the KVM, if
> enabled then it allocates a new ASID from the SNP pool and calls the
> firmware to initialize the all the resources.
> 


From the KVM code ("[PATCH Part2 v5 24/45] KVM: SVM: Add
KVM_SEV_SNP_LAUNCH_START command") it seems that KVM_SNP_INIT does *not*
allocate the ASID; actually this is done in KVM_SEV_SNP_LAUNCH_START.

If that is indeed the case, I suggest removing this sentence here and
adding it in the appropriate QEMU step (patch 5?).

-Dov



> Signed-off-by: Brijesh Singh <brijesh.singh@amd.com>
> Signed-off-by: Michael Roth <michael.roth@amd.com>
> ---
>  target/i386/sev-stub.c |  6 ++++++
>  target/i386/sev.c      | 27 ++++++++++++++++++++++++---
>  target/i386/sev_i386.h |  1 +
>  3 files changed, 31 insertions(+), 3 deletions(-)
> 

[...]


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

* Re: [RFC PATCH v2 11/12] i386/sev: sev-snp: add support for CPUID validation
  2021-08-26 22:26 ` [RFC PATCH v2 11/12] i386/sev: sev-snp: add support for CPUID validation Michael Roth
@ 2021-09-05 10:02     ` Dov Murik
  0 siblings, 0 replies; 61+ messages in thread
From: Dov Murik @ 2021-09-05 10:02 UTC (permalink / raw)
  To: Michael Roth, qemu-devel
  Cc: Connor Kuehl, Philippe Mathieu-Daudé,
	Michael S . Tsirkin, James Bottomley, Dr . David Alan Gilbert,
	Tom Lendacky, Paolo Bonzini, David Gibson,
	Daniel P . Berrangé,
	kvm, Eduardo Habkost, Brijesh Singh, Markus Armbruster,
	Eric Blake, Dov Murik

Hi Michael,

On 27/08/2021 1:26, Michael Roth wrote:
> SEV-SNP firmware allows a special guest page to be populated with a
> table of guest CPUID values so that they can be validated through
> firmware before being loaded into encrypted guest memory where they can
> be used in place of hypervisor-provided values[1].
> 
> As part of SEV-SNP guest initialization, use this process to validate
> the CPUID entries reported by KVM_GET_CPUID2 prior to initial guest
> start.
> 
> [1]: SEV SNP Firmware ABI Specification, Rev. 0.8, 8.13.2.6
> 
> Signed-off-by: Michael Roth <michael.roth@amd.com>
> ---
>  target/i386/sev.c | 146 +++++++++++++++++++++++++++++++++++++++++++++-
>  1 file changed, 143 insertions(+), 3 deletions(-)
> 
> diff --git a/target/i386/sev.c b/target/i386/sev.c
> index 0009c93d28..72a6146295 100644
> --- a/target/i386/sev.c
> +++ b/target/i386/sev.c
> @@ -153,6 +153,36 @@ static const char *const sev_fw_errlist[] = {
>  
>  #define SEV_FW_MAX_ERROR      ARRAY_SIZE(sev_fw_errlist)
>  
> +/* <linux/kvm.h> doesn't expose this, so re-use the max from kvm.c */
> +#define KVM_MAX_CPUID_ENTRIES 100
> +
> +typedef struct KvmCpuidInfo {
> +    struct kvm_cpuid2 cpuid;
> +    struct kvm_cpuid_entry2 entries[KVM_MAX_CPUID_ENTRIES];
> +} KvmCpuidInfo;
> +
> +#define SNP_CPUID_FUNCTION_MAXCOUNT 64
> +#define SNP_CPUID_FUNCTION_UNKNOWN 0xFFFFFFFF
> +
> +typedef struct {
> +    uint32_t eax_in;
> +    uint32_t ecx_in;
> +    uint64_t xcr0_in;
> +    uint64_t xss_in;
> +    uint32_t eax;
> +    uint32_t ebx;
> +    uint32_t ecx;
> +    uint32_t edx;
> +    uint64_t reserved;
> +} __attribute__((packed)) SnpCpuidFunc;
> +
> +typedef struct {
> +    uint32_t count;
> +    uint32_t reserved1;
> +    uint64_t reserved2;
> +    SnpCpuidFunc entries[SNP_CPUID_FUNCTION_MAXCOUNT];
> +} __attribute__((packed)) SnpCpuidInfo;
> +
>  static int
>  sev_ioctl(int fd, int cmd, void *data, int *error)
>  {
> @@ -1141,6 +1171,117 @@ detect_first_overlap(uint64_t start, uint64_t end, Range *range_list,
>      return overlap;
>  }
>  
> +static int
> +sev_snp_cpuid_info_fill(SnpCpuidInfo *snp_cpuid_info,
> +                        const KvmCpuidInfo *kvm_cpuid_info)
> +{
> +    size_t i;
> +
> +    memset(snp_cpuid_info, 0, sizeof(*snp_cpuid_info));
> +
> +    for (i = 0; kvm_cpuid_info->entries[i].function != 0xFFFFFFFF; i++) {

Maybe iterate only while i < kvm_cpuid_info.cpuid.nent ?

> +        const struct kvm_cpuid_entry2 *kvm_cpuid_entry;
> +        SnpCpuidFunc *snp_cpuid_entry;
> +
> +        kvm_cpuid_entry = &kvm_cpuid_info->entries[i];
> +        snp_cpuid_entry = &snp_cpuid_info->entries[i];

There's no explicit check that i < KVM_MAX_CPUID_ENTRIES and i <
SNP_CPUID_FUNCTION_MAXCOUNT.  The !=0xFFFFFFFF condition might protect
against this but this is not really clear (the memset to 0xFF is done in
another function).

Since KVM_MAX_CPUID_ENTRIES is 100 and SNP_CPUID_FUNCTION_MAXCOUNT is
64, it seems possible that i will be 65 for example and then
snp_cpuid_info->entries[i] is an out-of-bounds read access.




> +
> +        snp_cpuid_entry->eax_in = kvm_cpuid_entry->function;
> +        if (kvm_cpuid_entry->flags == KVM_CPUID_FLAG_SIGNIFCANT_INDEX) {
> +            snp_cpuid_entry->ecx_in = kvm_cpuid_entry->index;
> +        }
> +        snp_cpuid_entry->eax = kvm_cpuid_entry->eax;
> +        snp_cpuid_entry->ebx = kvm_cpuid_entry->ebx;
> +        snp_cpuid_entry->ecx = kvm_cpuid_entry->ecx;
> +        snp_cpuid_entry->edx = kvm_cpuid_entry->edx;
> +
> +        if (snp_cpuid_entry->eax_in == 0xD &&
> +            (snp_cpuid_entry->ecx_in == 0x0 || snp_cpuid_entry->ecx_in == 0x1)) {
> +            snp_cpuid_entry->ebx = 0x240;
> +        }

Can you please add a comment explaining this special case?




> +    }
> +
> +    if (i > SNP_CPUID_FUNCTION_MAXCOUNT) {

This can be checked at the top (before the for loop): compare
kvm_cpuid_info.cpuid.nent with SNP_CPUID_FUNCTION_MAXCOUNT.


> +        error_report("SEV-SNP: CPUID count '%lu' exceeds max '%u'",
> +                     i, SNP_CPUID_FUNCTION_MAXCOUNT);
> +        return -1;
> +    }
> +
> +    snp_cpuid_info->count = i;
> +
> +    return 0;
> +}
> +
> +static void
> +sev_snp_cpuid_report_mismatches(SnpCpuidInfo *old,
> +                                SnpCpuidInfo *new)
> +{
> +    size_t i;
> +

Add check that new->count == old->count.


> +    for (i = 0; i < old->count; i++) {
> +        SnpCpuidFunc *old_func, *new_func;
> +
> +        old_func = &old->entries[i];
> +        new_func = &new->entries[i];
> +
> +        if (memcmp(old_func, new_func, sizeof(SnpCpuidFunc))) {

Maybe clearer:

    if (*old_func != *new_func) ...


> +            error_report("SEV-SNP: CPUID validation failed for function %x, index: %x.\n"

Add "0x" prefixes before printing hex values (%x), otherwise we might
have confusing outputs such as "failed for function 13, index: 25" which
is unclear whether it's decimal or hex.


> +                         "provided: eax:0x%08x, ebx: 0x%08x, ecx: 0x%08x, edx: 0x%08x\n"
> +                         "expected: eax:0x%08x, ebx: 0x%08x, ecx: 0x%08x, edx: 0x%08x",
> +                         old_func->eax_in, old_func->ecx_in,
> +                         old_func->eax, old_func->ebx, old_func->ecx, old_func->edx,
> +                         new_func->eax, new_func->ebx, new_func->ecx, new_func->edx);
> +        }
> +    }
> +}
> +
> +static int
> +sev_snp_launch_update_cpuid(uint32_t cpuid_addr, uint32_t cpuid_len)
> +{
> +    KvmCpuidInfo kvm_cpuid_info;
> +    SnpCpuidInfo snp_cpuid_info;
> +    CPUState *cs = first_cpu;
> +    MemoryRegion *mr = NULL;
> +    void *snp_cpuid_hva;
> +    int ret;
> +
> +    snp_cpuid_hva = gpa2hva(&mr, cpuid_addr, cpuid_len, NULL);
> +    if (!snp_cpuid_hva) {
> +        error_report("SEV-SNP: unable to access CPUID memory range at GPA %d",
> +                     cpuid_addr);
> +        return 1;
> +    }

I think that moving this section just before the memcpy(snp_cpuid_hva,
...) below would make the flow of this function clearer to the reader
(no functional difference, I believe).


> +
> +    /* get the cpuid list from KVM */
> +    memset(&kvm_cpuid_info.entries, 0xFF,
> +           KVM_MAX_CPUID_ENTRIES * sizeof(struct kvm_cpuid_entry2));

The third argument can be:  sizeof(kvm_cpuid_info.entries)


> +    kvm_cpuid_info.cpuid.nent = KVM_MAX_CPUID_ENTRIES;
> +
> +    ret = kvm_vcpu_ioctl(cs, KVM_GET_CPUID2, &kvm_cpuid_info);
> +    if (ret) {
> +        error_report("SEV-SNP: unable to query CPUID values for CPU: '%s'",
> +                     strerror(-ret));

Missing return 1 or exit(1) here?


-Dov

> +    }
> +
> +    ret = sev_snp_cpuid_info_fill(&snp_cpuid_info, &kvm_cpuid_info);
> +    if (ret) {
> +        error_report("SEV-SNP: failed to generate CPUID table information");
> +        exit(1);
> +    }
> +
> +    memcpy(snp_cpuid_hva, &snp_cpuid_info, sizeof(snp_cpuid_info));

Before memcpy, maybe add sanity test (assert?) that
sizeof(snp_cpuid_info) <= cpuid_len .


> +
> +    ret = sev_snp_launch_update_gpa(cpuid_addr, cpuid_len,
> +                                    KVM_SEV_SNP_PAGE_TYPE_CPUID);
> +    if (ret) {
> +        sev_snp_cpuid_report_mismatches(&snp_cpuid_info, snp_cpuid_hva);
> +        error_report("SEV-SNP: failed update CPUID page");
> +        exit(1);
> +    }
> +
> +    return 0;
> +}
> +
>  static void snp_ovmf_boot_block_setup(void)
>  {
>      SevSnpBootInfoBlock *info;
> @@ -1176,10 +1317,9 @@ static void snp_ovmf_boot_block_setup(void)
>      }
>  
>      /* Populate the cpuid page */
> -    ret = sev_snp_launch_update_gpa(info->cpuid_addr, info->cpuid_len,
> -                                    KVM_SEV_SNP_PAGE_TYPE_CPUID);
> +    ret = sev_snp_launch_update_cpuid(info->cpuid_addr, info->cpuid_len);
>      if (ret) {
> -        error_report("SEV-SNP: failed to insert cpuid page GPA 0x%x",
> +        error_report("SEV-SNP: failed to populate cpuid tables GPA 0x%x",
>                       info->cpuid_addr);
>          exit(1);
>      }
> 

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

* Re: [RFC PATCH v2 11/12] i386/sev: sev-snp: add support for CPUID validation
@ 2021-09-05 10:02     ` Dov Murik
  0 siblings, 0 replies; 61+ messages in thread
From: Dov Murik @ 2021-09-05 10:02 UTC (permalink / raw)
  To: Michael Roth, qemu-devel
  Cc: Tom Lendacky, Daniel P . Berrangé,
	Eduardo Habkost, kvm, Michael S . Tsirkin, Connor Kuehl,
	Eric Blake, James Bottomley, Dr . David Alan Gilbert,
	Markus Armbruster, Dov Murik, Brijesh Singh, Paolo Bonzini,
	Philippe Mathieu-Daudé,
	David Gibson

Hi Michael,

On 27/08/2021 1:26, Michael Roth wrote:
> SEV-SNP firmware allows a special guest page to be populated with a
> table of guest CPUID values so that they can be validated through
> firmware before being loaded into encrypted guest memory where they can
> be used in place of hypervisor-provided values[1].
> 
> As part of SEV-SNP guest initialization, use this process to validate
> the CPUID entries reported by KVM_GET_CPUID2 prior to initial guest
> start.
> 
> [1]: SEV SNP Firmware ABI Specification, Rev. 0.8, 8.13.2.6
> 
> Signed-off-by: Michael Roth <michael.roth@amd.com>
> ---
>  target/i386/sev.c | 146 +++++++++++++++++++++++++++++++++++++++++++++-
>  1 file changed, 143 insertions(+), 3 deletions(-)
> 
> diff --git a/target/i386/sev.c b/target/i386/sev.c
> index 0009c93d28..72a6146295 100644
> --- a/target/i386/sev.c
> +++ b/target/i386/sev.c
> @@ -153,6 +153,36 @@ static const char *const sev_fw_errlist[] = {
>  
>  #define SEV_FW_MAX_ERROR      ARRAY_SIZE(sev_fw_errlist)
>  
> +/* <linux/kvm.h> doesn't expose this, so re-use the max from kvm.c */
> +#define KVM_MAX_CPUID_ENTRIES 100
> +
> +typedef struct KvmCpuidInfo {
> +    struct kvm_cpuid2 cpuid;
> +    struct kvm_cpuid_entry2 entries[KVM_MAX_CPUID_ENTRIES];
> +} KvmCpuidInfo;
> +
> +#define SNP_CPUID_FUNCTION_MAXCOUNT 64
> +#define SNP_CPUID_FUNCTION_UNKNOWN 0xFFFFFFFF
> +
> +typedef struct {
> +    uint32_t eax_in;
> +    uint32_t ecx_in;
> +    uint64_t xcr0_in;
> +    uint64_t xss_in;
> +    uint32_t eax;
> +    uint32_t ebx;
> +    uint32_t ecx;
> +    uint32_t edx;
> +    uint64_t reserved;
> +} __attribute__((packed)) SnpCpuidFunc;
> +
> +typedef struct {
> +    uint32_t count;
> +    uint32_t reserved1;
> +    uint64_t reserved2;
> +    SnpCpuidFunc entries[SNP_CPUID_FUNCTION_MAXCOUNT];
> +} __attribute__((packed)) SnpCpuidInfo;
> +
>  static int
>  sev_ioctl(int fd, int cmd, void *data, int *error)
>  {
> @@ -1141,6 +1171,117 @@ detect_first_overlap(uint64_t start, uint64_t end, Range *range_list,
>      return overlap;
>  }
>  
> +static int
> +sev_snp_cpuid_info_fill(SnpCpuidInfo *snp_cpuid_info,
> +                        const KvmCpuidInfo *kvm_cpuid_info)
> +{
> +    size_t i;
> +
> +    memset(snp_cpuid_info, 0, sizeof(*snp_cpuid_info));
> +
> +    for (i = 0; kvm_cpuid_info->entries[i].function != 0xFFFFFFFF; i++) {

Maybe iterate only while i < kvm_cpuid_info.cpuid.nent ?

> +        const struct kvm_cpuid_entry2 *kvm_cpuid_entry;
> +        SnpCpuidFunc *snp_cpuid_entry;
> +
> +        kvm_cpuid_entry = &kvm_cpuid_info->entries[i];
> +        snp_cpuid_entry = &snp_cpuid_info->entries[i];

There's no explicit check that i < KVM_MAX_CPUID_ENTRIES and i <
SNP_CPUID_FUNCTION_MAXCOUNT.  The !=0xFFFFFFFF condition might protect
against this but this is not really clear (the memset to 0xFF is done in
another function).

Since KVM_MAX_CPUID_ENTRIES is 100 and SNP_CPUID_FUNCTION_MAXCOUNT is
64, it seems possible that i will be 65 for example and then
snp_cpuid_info->entries[i] is an out-of-bounds read access.




> +
> +        snp_cpuid_entry->eax_in = kvm_cpuid_entry->function;
> +        if (kvm_cpuid_entry->flags == KVM_CPUID_FLAG_SIGNIFCANT_INDEX) {
> +            snp_cpuid_entry->ecx_in = kvm_cpuid_entry->index;
> +        }
> +        snp_cpuid_entry->eax = kvm_cpuid_entry->eax;
> +        snp_cpuid_entry->ebx = kvm_cpuid_entry->ebx;
> +        snp_cpuid_entry->ecx = kvm_cpuid_entry->ecx;
> +        snp_cpuid_entry->edx = kvm_cpuid_entry->edx;
> +
> +        if (snp_cpuid_entry->eax_in == 0xD &&
> +            (snp_cpuid_entry->ecx_in == 0x0 || snp_cpuid_entry->ecx_in == 0x1)) {
> +            snp_cpuid_entry->ebx = 0x240;
> +        }

Can you please add a comment explaining this special case?




> +    }
> +
> +    if (i > SNP_CPUID_FUNCTION_MAXCOUNT) {

This can be checked at the top (before the for loop): compare
kvm_cpuid_info.cpuid.nent with SNP_CPUID_FUNCTION_MAXCOUNT.


> +        error_report("SEV-SNP: CPUID count '%lu' exceeds max '%u'",
> +                     i, SNP_CPUID_FUNCTION_MAXCOUNT);
> +        return -1;
> +    }
> +
> +    snp_cpuid_info->count = i;
> +
> +    return 0;
> +}
> +
> +static void
> +sev_snp_cpuid_report_mismatches(SnpCpuidInfo *old,
> +                                SnpCpuidInfo *new)
> +{
> +    size_t i;
> +

Add check that new->count == old->count.


> +    for (i = 0; i < old->count; i++) {
> +        SnpCpuidFunc *old_func, *new_func;
> +
> +        old_func = &old->entries[i];
> +        new_func = &new->entries[i];
> +
> +        if (memcmp(old_func, new_func, sizeof(SnpCpuidFunc))) {

Maybe clearer:

    if (*old_func != *new_func) ...


> +            error_report("SEV-SNP: CPUID validation failed for function %x, index: %x.\n"

Add "0x" prefixes before printing hex values (%x), otherwise we might
have confusing outputs such as "failed for function 13, index: 25" which
is unclear whether it's decimal or hex.


> +                         "provided: eax:0x%08x, ebx: 0x%08x, ecx: 0x%08x, edx: 0x%08x\n"
> +                         "expected: eax:0x%08x, ebx: 0x%08x, ecx: 0x%08x, edx: 0x%08x",
> +                         old_func->eax_in, old_func->ecx_in,
> +                         old_func->eax, old_func->ebx, old_func->ecx, old_func->edx,
> +                         new_func->eax, new_func->ebx, new_func->ecx, new_func->edx);
> +        }
> +    }
> +}
> +
> +static int
> +sev_snp_launch_update_cpuid(uint32_t cpuid_addr, uint32_t cpuid_len)
> +{
> +    KvmCpuidInfo kvm_cpuid_info;
> +    SnpCpuidInfo snp_cpuid_info;
> +    CPUState *cs = first_cpu;
> +    MemoryRegion *mr = NULL;
> +    void *snp_cpuid_hva;
> +    int ret;
> +
> +    snp_cpuid_hva = gpa2hva(&mr, cpuid_addr, cpuid_len, NULL);
> +    if (!snp_cpuid_hva) {
> +        error_report("SEV-SNP: unable to access CPUID memory range at GPA %d",
> +                     cpuid_addr);
> +        return 1;
> +    }

I think that moving this section just before the memcpy(snp_cpuid_hva,
...) below would make the flow of this function clearer to the reader
(no functional difference, I believe).


> +
> +    /* get the cpuid list from KVM */
> +    memset(&kvm_cpuid_info.entries, 0xFF,
> +           KVM_MAX_CPUID_ENTRIES * sizeof(struct kvm_cpuid_entry2));

The third argument can be:  sizeof(kvm_cpuid_info.entries)


> +    kvm_cpuid_info.cpuid.nent = KVM_MAX_CPUID_ENTRIES;
> +
> +    ret = kvm_vcpu_ioctl(cs, KVM_GET_CPUID2, &kvm_cpuid_info);
> +    if (ret) {
> +        error_report("SEV-SNP: unable to query CPUID values for CPU: '%s'",
> +                     strerror(-ret));

Missing return 1 or exit(1) here?


-Dov

> +    }
> +
> +    ret = sev_snp_cpuid_info_fill(&snp_cpuid_info, &kvm_cpuid_info);
> +    if (ret) {
> +        error_report("SEV-SNP: failed to generate CPUID table information");
> +        exit(1);
> +    }
> +
> +    memcpy(snp_cpuid_hva, &snp_cpuid_info, sizeof(snp_cpuid_info));

Before memcpy, maybe add sanity test (assert?) that
sizeof(snp_cpuid_info) <= cpuid_len .


> +
> +    ret = sev_snp_launch_update_gpa(cpuid_addr, cpuid_len,
> +                                    KVM_SEV_SNP_PAGE_TYPE_CPUID);
> +    if (ret) {
> +        sev_snp_cpuid_report_mismatches(&snp_cpuid_info, snp_cpuid_hva);
> +        error_report("SEV-SNP: failed update CPUID page");
> +        exit(1);
> +    }
> +
> +    return 0;
> +}
> +
>  static void snp_ovmf_boot_block_setup(void)
>  {
>      SevSnpBootInfoBlock *info;
> @@ -1176,10 +1317,9 @@ static void snp_ovmf_boot_block_setup(void)
>      }
>  
>      /* Populate the cpuid page */
> -    ret = sev_snp_launch_update_gpa(info->cpuid_addr, info->cpuid_len,
> -                                    KVM_SEV_SNP_PAGE_TYPE_CPUID);
> +    ret = sev_snp_launch_update_cpuid(info->cpuid_addr, info->cpuid_len);
>      if (ret) {
> -        error_report("SEV-SNP: failed to insert cpuid page GPA 0x%x",
> +        error_report("SEV-SNP: failed to populate cpuid tables GPA 0x%x",
>                       info->cpuid_addr);
>          exit(1);
>      }
> 


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

* Re: [RFC PATCH v2 04/12] i386/sev: initialize SNP context
  2021-09-05  7:07     ` Dov Murik
  (?)
@ 2021-09-05 13:58     ` Brijesh Singh
  2021-09-05 17:09         ` Dov Murik
  -1 siblings, 1 reply; 61+ messages in thread
From: Brijesh Singh @ 2021-09-05 13:58 UTC (permalink / raw)
  To: Dov Murik, Michael Roth, qemu-devel
  Cc: Connor Kuehl, Philippe Mathieu-Daudé,
	Michael S . Tsirkin, James Bottomley, Dr . David Alan Gilbert,
	Tom Lendacky, Paolo Bonzini, David Gibson,
	Daniel P . Berrangé,
	kvm, Eduardo Habkost, Markus Armbruster, Eric Blake

Hi Dov,

On 9/5/21 2:07 AM, Dov Murik wrote:
...
>
>>  
>>  uint64_t
>> @@ -1074,6 +1083,7 @@ int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp)
>>      uint32_t ebx;
>>      uint32_t host_cbitpos;
>>      struct sev_user_data_status status = {};
>> +    void *init_args = NULL;
>>  
>>      if (!sev_common) {
>>          return 0;
>> @@ -1126,7 +1136,18 @@ int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp)
>>      sev_common->api_major = status.api_major;
>>      sev_common->api_minor = status.api_minor;
> Not visible here in the context: the code here is using the
> SEV_PLATFORM_STATUS command to get the build_id, api_major, and api_minor.
>
> I see that SNP has a new command SNP_PLATFORM_STATUS, which fills a
> struct sev_data_snp_platform_status (hmmm, I can't find the struct's
> definition; I assume it should look like Table 38 in 8.3.2 in SNP FW ABI
> document).

The API version can be queries either through the SNP_PLATFORM_STATUS or
SEV_PLATFORM_STATUS and they both report the same info. As the
definition of the sev_data_platform_status is concerned it should be
defined in the kernel include/linux/psp-sev.h.


> My questions are:
>
> 1. Is it OK to call the "legacy" SEV_PLATFORM_STATUS when about to init
> an SNP guest?

Yes, the legacy platform status command can be called on the SNP
initialized host.

I choose not to new command because we only care about the verison
string and that is available through either of these commands (SNP or
SEV platform status).

> 2. Do we want to save some info like installed TCB version and reported
> TCB version, and maybe other fields from SNP platform status?

If we decide to add a new QMP (query-sev-snp) then it makes sense to
export those fields so that a hypervisor console can give additional
information; But note that for the guest, all these are available in the
attestation report.


> 3. Should we check the state field in the platform status?
>
>
Good point, we could use the SNP platform status. I don't expect the
state to be different between the SNP platform_status and SEV
platform_status.


>>  
>> -    if (sev_es_enabled()) {
>> +    if (sev_snp_enabled()) {
>> +        SevSnpGuestState *sev_snp_guest = SEV_SNP_GUEST(sev_common);
>> +        if (!kvm_kernel_irqchip_allowed()) {
>> +            error_report("%s: SEV-SNP guests require in-kernel irqchip support",
>> +                         __func__);
> Most errors in this function use error_setg(errp, ...).  This should follow.
>
>
>> +            goto err;
>> +        }
>> +
>> +        cmd = KVM_SEV_SNP_INIT;
>> +        init_args = (void *)&sev_snp_guest->kvm_init_conf;
>> +
>> +    } else if (sev_es_enabled()) {
>>          if (!kvm_kernel_irqchip_allowed()) {
>>              error_report("%s: SEV-ES guests require in-kernel irqchip support",
>>                           __func__);
> Not part of this patch, but this error_report (and another one in the
> SEV-ES case) should be converted to error_setg similarly.  Maybe add a
> separate patch for fixing this for SEV-ES.
>
>
>
>> @@ -1145,7 +1166,7 @@ int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp)
>>      }
>>  
>>      trace_kvm_sev_init();
> Suggestions:
>
> 1. log the guest type (SEV / SEV-ES / SEV-SNP)
> 2. log the SNP init flags value when initializing an SNP guest

Noted.

thanks

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

* Re: [RFC PATCH v2 04/12] i386/sev: initialize SNP context
  2021-09-05  9:19     ` Dov Murik
  (?)
@ 2021-09-05 14:05     ` Brijesh Singh
  2021-09-05 17:03         ` Dov Murik
  -1 siblings, 1 reply; 61+ messages in thread
From: Brijesh Singh @ 2021-09-05 14:05 UTC (permalink / raw)
  To: Dov Murik, Michael Roth, qemu-devel
  Cc: Connor Kuehl, Philippe Mathieu-Daudé,
	Michael S . Tsirkin, James Bottomley, Dr . David Alan Gilbert,
	Tom Lendacky, Paolo Bonzini, David Gibson,
	Daniel P . Berrangé,
	kvm, Eduardo Habkost, Markus Armbruster, Eric Blake


On 9/5/21 4:19 AM, Dov Murik wrote:
>
> On 27/08/2021 1:26, Michael Roth wrote:
>> From: Brijesh Singh <brijesh.singh@amd.com>
>>
>> When SEV-SNP is enabled, the KVM_SNP_INIT command is used to initialize
>> the platform. The command checks whether SNP is enabled in the KVM, if
>> enabled then it allocates a new ASID from the SNP pool and calls the
>> firmware to initialize the all the resources.
>>
>
> From the KVM code ("[PATCH Part2 v5 24/45] KVM: SVM: Add
> KVM_SEV_SNP_LAUNCH_START command") it seems that KVM_SNP_INIT does *not*
> allocate the ASID; actually this is done in KVM_SEV_SNP_LAUNCH_START.

Actually, the KVM_SNP_INIT does allocate the ASID. If you look at the
driver code then in switch state, the SNP_INIT fallthrough to SEV_INIT
which will call sev_guest_init(). The sev_guest_init() allocates a new
ASID.
https://github.com/AMDESE/linux/blob/bb9ba49cd9b749d5551aae295c091d8757153dd7/arch/x86/kvm/svm/sev.c#L255

The LAUNCH_START simply binds the ASID to a guest.

thanks

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

* Re: [RFC PATCH v2 04/12] i386/sev: initialize SNP context
  2021-09-05 14:05     ` Brijesh Singh
@ 2021-09-05 17:03         ` Dov Murik
  0 siblings, 0 replies; 61+ messages in thread
From: Dov Murik @ 2021-09-05 17:03 UTC (permalink / raw)
  To: Brijesh Singh, Michael Roth, qemu-devel
  Cc: Connor Kuehl, Philippe Mathieu-Daudé,
	Michael S . Tsirkin, James Bottomley, Dr . David Alan Gilbert,
	Tom Lendacky, Paolo Bonzini, David Gibson,
	Daniel P . Berrangé,
	kvm, Eduardo Habkost, Markus Armbruster, Eric Blake, Dov Murik



On 05/09/2021 17:05, Brijesh Singh wrote:
> 
> On 9/5/21 4:19 AM, Dov Murik wrote:
>>
>> On 27/08/2021 1:26, Michael Roth wrote:
>>> From: Brijesh Singh <brijesh.singh@amd.com>
>>>
>>> When SEV-SNP is enabled, the KVM_SNP_INIT command is used to initialize
>>> the platform. The command checks whether SNP is enabled in the KVM, if
>>> enabled then it allocates a new ASID from the SNP pool and calls the
>>> firmware to initialize the all the resources.
>>>
>>
>> From the KVM code ("[PATCH Part2 v5 24/45] KVM: SVM: Add
>> KVM_SEV_SNP_LAUNCH_START command") it seems that KVM_SNP_INIT does *not*
>> allocate the ASID; actually this is done in KVM_SEV_SNP_LAUNCH_START.
> 
> Actually, the KVM_SNP_INIT does allocate the ASID. If you look at the
> driver code then in switch state, the SNP_INIT fallthrough to SEV_INIT
> which will call sev_guest_init(). The sev_guest_init() allocates a new
> ASID.
> https://github.com/AMDESE/linux/blob/bb9ba49cd9b749d5551aae295c091d8757153dd7/arch/x86/kvm/svm/sev.c#L255
> 
> The LAUNCH_START simply binds the ASID to a guest.

OK thank you for clearing this up.  So the kernel is choosing the new
ASID during the KVM_SNP_INIT ioctl, but doesn't "tell" the firmware
about it.  Then later in SNP_LAUNCH_START that integer (saved in the
kernel sev structure) is given to the firmware as an argument of the
SNP_LAUNCH_START (binding?).  Is this description correct?


-Dov

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

* Re: [RFC PATCH v2 04/12] i386/sev: initialize SNP context
@ 2021-09-05 17:03         ` Dov Murik
  0 siblings, 0 replies; 61+ messages in thread
From: Dov Murik @ 2021-09-05 17:03 UTC (permalink / raw)
  To: Brijesh Singh, Michael Roth, qemu-devel
  Cc: Tom Lendacky, Daniel P . Berrangé,
	Eduardo Habkost, kvm, Michael S . Tsirkin, Connor Kuehl,
	Eric Blake, James Bottomley, Dr . David Alan Gilbert,
	Markus Armbruster, Dov Murik, Paolo Bonzini,
	Philippe Mathieu-Daudé,
	David Gibson



On 05/09/2021 17:05, Brijesh Singh wrote:
> 
> On 9/5/21 4:19 AM, Dov Murik wrote:
>>
>> On 27/08/2021 1:26, Michael Roth wrote:
>>> From: Brijesh Singh <brijesh.singh@amd.com>
>>>
>>> When SEV-SNP is enabled, the KVM_SNP_INIT command is used to initialize
>>> the platform. The command checks whether SNP is enabled in the KVM, if
>>> enabled then it allocates a new ASID from the SNP pool and calls the
>>> firmware to initialize the all the resources.
>>>
>>
>> From the KVM code ("[PATCH Part2 v5 24/45] KVM: SVM: Add
>> KVM_SEV_SNP_LAUNCH_START command") it seems that KVM_SNP_INIT does *not*
>> allocate the ASID; actually this is done in KVM_SEV_SNP_LAUNCH_START.
> 
> Actually, the KVM_SNP_INIT does allocate the ASID. If you look at the
> driver code then in switch state, the SNP_INIT fallthrough to SEV_INIT
> which will call sev_guest_init(). The sev_guest_init() allocates a new
> ASID.
> https://github.com/AMDESE/linux/blob/bb9ba49cd9b749d5551aae295c091d8757153dd7/arch/x86/kvm/svm/sev.c#L255
> 
> The LAUNCH_START simply binds the ASID to a guest.

OK thank you for clearing this up.  So the kernel is choosing the new
ASID during the KVM_SNP_INIT ioctl, but doesn't "tell" the firmware
about it.  Then later in SNP_LAUNCH_START that integer (saved in the
kernel sev structure) is given to the firmware as an argument of the
SNP_LAUNCH_START (binding?).  Is this description correct?


-Dov


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

* Re: [RFC PATCH v2 04/12] i386/sev: initialize SNP context
  2021-09-05 13:58     ` Brijesh Singh
@ 2021-09-05 17:09         ` Dov Murik
  0 siblings, 0 replies; 61+ messages in thread
From: Dov Murik @ 2021-09-05 17:09 UTC (permalink / raw)
  To: Brijesh Singh, Michael Roth, qemu-devel
  Cc: Connor Kuehl, Philippe Mathieu-Daudé,
	Michael S . Tsirkin, James Bottomley, Dr . David Alan Gilbert,
	Tom Lendacky, Paolo Bonzini, David Gibson,
	Daniel P . Berrangé,
	kvm, Eduardo Habkost, Markus Armbruster, Eric Blake, Dov Murik



On 05/09/2021 16:58, Brijesh Singh wrote:
> Hi Dov,
> 
> On 9/5/21 2:07 AM, Dov Murik wrote:
> ...
>>
>>>  
>>>  uint64_t
>>> @@ -1074,6 +1083,7 @@ int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp)
>>>      uint32_t ebx;
>>>      uint32_t host_cbitpos;
>>>      struct sev_user_data_status status = {};
>>> +    void *init_args = NULL;
>>>  
>>>      if (!sev_common) {
>>>          return 0;
>>> @@ -1126,7 +1136,18 @@ int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp)
>>>      sev_common->api_major = status.api_major;
>>>      sev_common->api_minor = status.api_minor;
>> Not visible here in the context: the code here is using the
>> SEV_PLATFORM_STATUS command to get the build_id, api_major, and api_minor.
>>
>> I see that SNP has a new command SNP_PLATFORM_STATUS, which fills a
>> struct sev_data_snp_platform_status (hmmm, I can't find the struct's
>> definition; I assume it should look like Table 38 in 8.3.2 in SNP FW ABI
>> document).
> 
> The API version can be queries either through the SNP_PLATFORM_STATUS or
> SEV_PLATFORM_STATUS and they both report the same info. As the
> definition of the sev_data_platform_status is concerned it should be
> defined in the kernel include/linux/psp-sev.h.
> 
> 
>> My questions are:
>>
>> 1. Is it OK to call the "legacy" SEV_PLATFORM_STATUS when about to init
>> an SNP guest?
> 
> Yes, the legacy platform status command can be called on the SNP
> initialized host.
> 
> I choose not to new command because we only care about the verison
> string and that is available through either of these commands (SNP or
> SEV platform status).
> 
>> 2. Do we want to save some info like installed TCB version and reported
>> TCB version, and maybe other fields from SNP platform status?
> 
> If we decide to add a new QMP (query-sev-snp) then it makes sense to
> export those fields so that a hypervisor console can give additional
> information; But note that for the guest, all these are available in the
> attestation report.
> 

We have new QMP response for SNP guests (SevSnpGuestProperties, patch 3
in this series).  I think it would make sense to add the
installed+reported TCB versions there (read-only properties), for
debugging/observability purposes.


-Dov


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

* Re: [RFC PATCH v2 04/12] i386/sev: initialize SNP context
@ 2021-09-05 17:09         ` Dov Murik
  0 siblings, 0 replies; 61+ messages in thread
From: Dov Murik @ 2021-09-05 17:09 UTC (permalink / raw)
  To: Brijesh Singh, Michael Roth, qemu-devel
  Cc: Tom Lendacky, Daniel P . Berrangé,
	Eduardo Habkost, kvm, Michael S . Tsirkin, Connor Kuehl,
	Eric Blake, James Bottomley, Dr . David Alan Gilbert,
	Markus Armbruster, Dov Murik, Paolo Bonzini,
	Philippe Mathieu-Daudé,
	David Gibson



On 05/09/2021 16:58, Brijesh Singh wrote:
> Hi Dov,
> 
> On 9/5/21 2:07 AM, Dov Murik wrote:
> ...
>>
>>>  
>>>  uint64_t
>>> @@ -1074,6 +1083,7 @@ int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp)
>>>      uint32_t ebx;
>>>      uint32_t host_cbitpos;
>>>      struct sev_user_data_status status = {};
>>> +    void *init_args = NULL;
>>>  
>>>      if (!sev_common) {
>>>          return 0;
>>> @@ -1126,7 +1136,18 @@ int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp)
>>>      sev_common->api_major = status.api_major;
>>>      sev_common->api_minor = status.api_minor;
>> Not visible here in the context: the code here is using the
>> SEV_PLATFORM_STATUS command to get the build_id, api_major, and api_minor.
>>
>> I see that SNP has a new command SNP_PLATFORM_STATUS, which fills a
>> struct sev_data_snp_platform_status (hmmm, I can't find the struct's
>> definition; I assume it should look like Table 38 in 8.3.2 in SNP FW ABI
>> document).
> 
> The API version can be queries either through the SNP_PLATFORM_STATUS or
> SEV_PLATFORM_STATUS and they both report the same info. As the
> definition of the sev_data_platform_status is concerned it should be
> defined in the kernel include/linux/psp-sev.h.
> 
> 
>> My questions are:
>>
>> 1. Is it OK to call the "legacy" SEV_PLATFORM_STATUS when about to init
>> an SNP guest?
> 
> Yes, the legacy platform status command can be called on the SNP
> initialized host.
> 
> I choose not to new command because we only care about the verison
> string and that is available through either of these commands (SNP or
> SEV platform status).
> 
>> 2. Do we want to save some info like installed TCB version and reported
>> TCB version, and maybe other fields from SNP platform status?
> 
> If we decide to add a new QMP (query-sev-snp) then it makes sense to
> export those fields so that a hypervisor console can give additional
> information; But note that for the guest, all these are available in the
> attestation report.
> 

We have new QMP response for SNP guests (SevSnpGuestProperties, patch 3
in this series).  I think it would make sense to add the
installed+reported TCB versions there (read-only properties), for
debugging/observability purposes.


-Dov



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

* Re: [RFC PATCH v2 12/12] i386/sev: update query-sev QAPI format to handle SEV-SNP
  2021-09-03 16:01       ` Daniel P. Berrangé
@ 2021-09-07 11:52         ` Dr. David Alan Gilbert
  -1 siblings, 0 replies; 61+ messages in thread
From: Dr. David Alan Gilbert @ 2021-09-07 11:52 UTC (permalink / raw)
  To: Daniel P. Berrangé
  Cc: Markus Armbruster, Michael Roth, qemu-devel, Connor Kuehl,
	Philippe Mathieu-Daudé,
	Michael S . Tsirkin, James Bottomley, Tom Lendacky,
	Paolo Bonzini, Dov Murik, David Gibson, kvm, Eduardo Habkost,
	Brijesh Singh, Eric Blake

* Daniel P. Berrangé (berrange@redhat.com) wrote:
> On Wed, Sep 01, 2021 at 04:14:10PM +0200, Markus Armbruster wrote:
> > Michael Roth <michael.roth@amd.com> writes:
> > 
> > > Most of the current 'query-sev' command is relevant to both legacy
> > > SEV/SEV-ES guests and SEV-SNP guests, with 2 exceptions:
> > >
> > >   - 'policy' is a 64-bit field for SEV-SNP, not 32-bit, and
> > >     the meaning of the bit positions has changed
> > >   - 'handle' is not relevant to SEV-SNP
> > >
> > > To address this, this patch adds a new 'sev-type' field that can be
> > > used as a discriminator to select between SEV and SEV-SNP-specific
> > > fields/formats without breaking compatibility for existing management
> > > tools (so long as management tools that add support for launching
> > > SEV-SNP guest update their handling of query-sev appropriately).
> > 
> > Technically a compatibility break: query-sev can now return an object
> > that whose member @policy has different meaning, and also lacks @handle.
> > 
> > Matrix:
> > 
> >                             Old mgmt app    New mgmt app
> >     Old QEMU, SEV/SEV-ES       good            good(1)
> >     New QEMU, SEV/SEV-ES       good(2)         good
> >     New QEMU, SEV-SNP           bad(3)         good
> > 
> > Notes:
> > 
> > (1) As long as the management application can cope with absent member
> > @sev-type.
> > 
> > (2) As long as the management application ignores unknown member
> > @sev-type.
> > 
> > (3) Management application may choke on missing member @handle, or
> > worse, misinterpret member @policy.  Can only happen when something
> > other than the management application created the SEV-SNP guest (or the
> > user somehow made the management application create one even though it
> > doesn't know how, say with CLI option passthrough, but that's always
> > fragile, and I wouldn't worry about it here).
> > 
> > I think (1) and (2) are reasonable.  (3) is an issue for management
> > applications that support attaching to existing guests.  Thoughts?
> 
> IIUC you can only reach scenario (3) if you have created a guest
> using '-object sev-snp-guest', which is a new feature introduced
> in patch 2.
> 
> IOW, scenario (3)  old mgmt app + new QEMU + sev-snp guest does
> not exist as a combination. Thus the (bad) field is actually (n/a)
> 
> So I believe this proposed change is acceptable in all scenarios
> with existing deployed usage, as well as all newly introduced
> scenarios.

I wonder if it's worth going firther and renaming 'policy' in the 
SNP world to 'snppolicy' just to reduce the risk of accidentally
specifying the wrong one.

Dave

> 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 :|
> 
-- 
Dr. David Alan Gilbert / dgilbert@redhat.com / Manchester, UK


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

* Re: [RFC PATCH v2 12/12] i386/sev: update query-sev QAPI format to handle SEV-SNP
@ 2021-09-07 11:52         ` Dr. David Alan Gilbert
  0 siblings, 0 replies; 61+ messages in thread
From: Dr. David Alan Gilbert @ 2021-09-07 11:52 UTC (permalink / raw)
  To: Daniel P. Berrangé
  Cc: Tom Lendacky, Brijesh Singh, Eduardo Habkost, kvm,
	Michael S . Tsirkin, Connor Kuehl, Michael Roth, James Bottomley,
	Markus Armbruster, Eric Blake, qemu-devel, Dov Murik,
	Paolo Bonzini, Philippe Mathieu-Daudé,
	David Gibson

* Daniel P. Berrangé (berrange@redhat.com) wrote:
> On Wed, Sep 01, 2021 at 04:14:10PM +0200, Markus Armbruster wrote:
> > Michael Roth <michael.roth@amd.com> writes:
> > 
> > > Most of the current 'query-sev' command is relevant to both legacy
> > > SEV/SEV-ES guests and SEV-SNP guests, with 2 exceptions:
> > >
> > >   - 'policy' is a 64-bit field for SEV-SNP, not 32-bit, and
> > >     the meaning of the bit positions has changed
> > >   - 'handle' is not relevant to SEV-SNP
> > >
> > > To address this, this patch adds a new 'sev-type' field that can be
> > > used as a discriminator to select between SEV and SEV-SNP-specific
> > > fields/formats without breaking compatibility for existing management
> > > tools (so long as management tools that add support for launching
> > > SEV-SNP guest update their handling of query-sev appropriately).
> > 
> > Technically a compatibility break: query-sev can now return an object
> > that whose member @policy has different meaning, and also lacks @handle.
> > 
> > Matrix:
> > 
> >                             Old mgmt app    New mgmt app
> >     Old QEMU, SEV/SEV-ES       good            good(1)
> >     New QEMU, SEV/SEV-ES       good(2)         good
> >     New QEMU, SEV-SNP           bad(3)         good
> > 
> > Notes:
> > 
> > (1) As long as the management application can cope with absent member
> > @sev-type.
> > 
> > (2) As long as the management application ignores unknown member
> > @sev-type.
> > 
> > (3) Management application may choke on missing member @handle, or
> > worse, misinterpret member @policy.  Can only happen when something
> > other than the management application created the SEV-SNP guest (or the
> > user somehow made the management application create one even though it
> > doesn't know how, say with CLI option passthrough, but that's always
> > fragile, and I wouldn't worry about it here).
> > 
> > I think (1) and (2) are reasonable.  (3) is an issue for management
> > applications that support attaching to existing guests.  Thoughts?
> 
> IIUC you can only reach scenario (3) if you have created a guest
> using '-object sev-snp-guest', which is a new feature introduced
> in patch 2.
> 
> IOW, scenario (3)  old mgmt app + new QEMU + sev-snp guest does
> not exist as a combination. Thus the (bad) field is actually (n/a)
> 
> So I believe this proposed change is acceptable in all scenarios
> with existing deployed usage, as well as all newly introduced
> scenarios.

I wonder if it's worth going firther and renaming 'policy' in the 
SNP world to 'snppolicy' just to reduce the risk of accidentally
specifying the wrong one.

Dave

> 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 :|
> 
-- 
Dr. David Alan Gilbert / dgilbert@redhat.com / Manchester, UK



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

* Re: [RFC PATCH v2 03/12] i386/sev: introduce 'sev-snp-guest' object
  2021-09-03 21:12     ` Dov Murik
  (?)
@ 2021-09-07 14:20     ` Michael Roth
  -1 siblings, 0 replies; 61+ messages in thread
From: Michael Roth @ 2021-09-07 14:20 UTC (permalink / raw)
  To: Dov Murik
  Cc: qemu-devel, Connor Kuehl, Philippe Mathieu-Daudé,
	Michael S . Tsirkin, James Bottomley, Dr . David Alan Gilbert,
	Tom Lendacky, Paolo Bonzini, David Gibson,
	Daniel P . Berrangé,
	kvm, Eduardo Habkost, Brijesh Singh, Markus Armbruster,
	Eric Blake

On Sat, Sep 04, 2021 at 12:12:19AM +0300, Dov Murik wrote:
> 
> 
> On 27/08/2021 1:26, Michael Roth wrote:
> > From: Brijesh Singh <brijesh.singh@amd.com>
> > 
> > SEV-SNP support relies on a different set of properties/state than the
> > existing 'sev-guest' object. This patch introduces the 'sev-snp-guest'
> > object, which can be used to configure an SEV-SNP guest. For example,
> > a default-configured SEV-SNP guest with no additional information
> > passed in for use with attestation:
> > 
> >   -object sev-snp-guest,id=sev0
> > 
> > or a fully-specified SEV-SNP guest where all spec-defined binary
> > blobs are passed in as base64-encoded strings:
> > 
> >   -object sev-snp-guest,id=sev0, \
> >     policy=0x30000, \
> >     init-flags=0, \
> >     id-block=YWFhYWFhYWFhYWFhYWFhCg==, \
> >     id-auth=CxHK/OKLkXGn/KpAC7Wl1FSiisWDbGTEKz..., \
> >     auth-key-enabled=on, \
> >     host-data=LNkCWBRC5CcdGXirbNUV1OrsR28s..., \
> >     guest-visible-workarounds=AA==, \
> > 
> > See the QAPI schema updates included in this patch for more usage
> > details.
> > 
> > In some cases these blobs may be up to 4096 characters, but this is
> > generally well below the default limit for linux hosts where
> > command-line sizes are defined by the sysconf-configurable ARG_MAX
> > value, which defaults to 2097152 characters for Ubuntu hosts, for
> > example.
> > 
> > Co-developed-by: Michael Roth <michael.roth@amd.com>
> > Signed-off-by: Brijesh Singh <brijesh.singh@amd.com>
> > Signed-off-by: Michael Roth <michael.roth@amd.com>
> > ---
> >  docs/amd-memory-encryption.txt |  77 ++++++++++-
> >  qapi/qom.json                  |  60 ++++++++
> >  target/i386/sev.c              | 245 ++++++++++++++++++++++++++++++++-
> >  3 files changed, 379 insertions(+), 3 deletions(-)
> > 
> > diff --git a/docs/amd-memory-encryption.txt b/docs/amd-memory-encryption.txt
> > index ffca382b5f..0d82e67fa1 100644
> > --- a/docs/amd-memory-encryption.txt
> > +++ b/docs/amd-memory-encryption.txt
> > @@ -22,8 +22,8 @@ support for notifying a guest's operating system when certain types of VMEXITs
> >  are about to occur. This allows the guest to selectively share information with
> >  the hypervisor to satisfy the requested function.
> >  
> > -Launching
> > ----------
> > +Launching (SEV and SEV-ES)
> > +--------------------------
> >  Boot images (such as bios) must be encrypted before a guest can be booted. The
> >  MEMORY_ENCRYPT_OP ioctl provides commands to encrypt the images: LAUNCH_START,
> >  LAUNCH_UPDATE_DATA, LAUNCH_MEASURE and LAUNCH_FINISH. These four commands
> > @@ -113,6 +113,79 @@ a SEV-ES guest:
> >   - Requires in-kernel irqchip - the burden is placed on the hypervisor to
> >     manage booting APs.
> >  
> > +Launching (SEV-SNP)
> > +-------------------
> > +Boot images (such as bios) must be encrypted before a guest can be booted. The
> > +MEMORY_ENCRYPT_OP ioctl provides commands to encrypt the images:
> > +KVM_SNP_INIT, SNP_LAUNCH_START, SNP_LAUNCH_UPDATE, and SNP_LAUNCH_FINISH. These
> > +four commands together generate a fresh memory encryption key for the VM,
> > +encrypt the boot images for a successful launch.
> > +
> > +KVM_SNP_INIT is called first to initialize the SEV-SNP firmware and SNP
> > +features in the KVM. The feature flags value can be provided through the
> > +'init-flags' property of the 'sev-snp-guest' object.
> > +
> > ++------------+-------+----------+---------------------------------+
> > +| key        | type  | default  | meaning                         |
> > ++------------+-------+----------+---------------------------------+
> > +| init_flags | hex   | 0        | SNP feature flags               |
> > ++-----------------------------------------------------------------+
> > +
> > +Note: currently the init_flags must be zero.
> > +
> > +SNP_LAUNCH_START is called first to create a cryptographic launch context
> > +within the firmware. To create this context, guest owner must provide a guest
> > +policy and other parameters as described in the SEV-SNP firmware
> > +specification. The launch parameters should be specified as described in the
> > +QAPI schema for the 'sev-snp-guest' object.
> > +
> > +The SNP_LAUNCH_START uses the following parameters (see the SEV-SNP
> > +specification for more details):
> > +
> > ++--------+-------+----------+----------------------------------------------+
> > +| key    | type  | default  | meaning                                      |
> > ++--------+-------+----------+----------------------------------------------+
> > +| policy | hex   | 0x30000  | a 64-bit guest policy                        |
> > +| imi_en | bool  | 0        | 1 when IMI is enabled                        |
> > +| ma_end | bool  | 0        | 1 when migration agent is used               |
> > +| gosvw  | string| 0        | 16-byte base64 encoded string for the guest  |
> > +|        |       |          | OS visible workaround.                       |
> > ++--------+-------+----------+----------------------------------------------+
> > +
> > +SNP_LAUNCH_UPDATE encrypts the memory region using the cryptographic context
> > +created via the SNP_LAUNCH_START command. If required, this command can be called
> > +multiple times to encrypt different memory regions. The command also calculates
> > +the measurement of the memory contents as it encrypts.
> > +
> > +SNP_LAUNCH_FINISH finalizes the guest launch flow. Optionally, while finalizing
> > +the launch the firmware can perform checks on the launch digest computing
> > +through the SNP_LAUNCH_UPDATE. To perform the check the user must supply
> > +the id block, authentication blob and host data that should be included in the
> > +attestation report. See the SEV-SNP spec for further details.
> > +
> > +The SNP_LAUNCH_FINISH uses the following parameters, which can be configured
> > +by the corresponding parameters documented in the QAPI schema for the
> > +'sev-snp-guest' object.
> > +
> > ++------------+-------+----------+----------------------------------------------+
> > +| key        | type  | default  | meaning                                      |
> > ++------------+-------+----------+----------------------------------------------+
> > +| id_block   | string| none     | base64 encoded ID block                      |
> > ++------------+-------+----------+----------------------------------------------+
> > +| id_auth    | string| none     | base64 encoded authentication information    |
> > ++------------+-------+----------+----------------------------------------------+
> > +| auth_key_en| bool  | 0        | auth block contains author key               |
> > ++------------+-------+----------+----------------------------------------------+
> > +| host_data  | string| none     | host provided data                           |
> > ++------------+-------+----------+----------------------------------------------+
> > +
> > +To launch a SEV-SNP guest (additional parameters are documented in the QAPI
> > +schema for the 'sev-snp-guest' object):
> > +
> > +# ${QEMU} \
> > +    -machine ...,confidential-guest-support=sev0 \
> > +    -object sev-snp-guest,id=sev0,cbitpos=51,reduced-phys-bits=1
> > +
> >  Debugging
> >  -----------
> >  Since the memory contents of a SEV guest are encrypted, hypervisor access to
> > diff --git a/qapi/qom.json b/qapi/qom.json
> > index 211e083727..ea39585026 100644
> > --- a/qapi/qom.json
> > +++ b/qapi/qom.json
> > @@ -775,6 +775,64 @@
> >              '*policy': 'uint32',
> >              '*handle': 'uint32' } }
> >  
> > +##
> > +# @SevSnpGuestProperties:
> > +#
> > +# Properties for sev-snp-guest objects. Many of these are direct arguments
> > +# for the SEV-SNP KVM interfaces documented in the linux kernel source
> > +# documentation under 'amd-memory-encryption.rst'. Additional documentation
> > +# is also available in the QEMU source tree under
> > +# 'amd-memory-encryption.rst'.
> > +#
> > +# In addition to those files, please see the SEV-SNP Firmware Specification
> > +# (Rev 0.9) documentation for the SNP_INIT and
> > +# SNP_LAUNCH_{START,UPDATE,FINISH} firmware interfaces, which the KVM
> > +# interfaces are written against.
> > +#
> > +# @init-flags: as documented for the 'flags' parameter of the
> > +#              KVM_SNP_INIT KVM command (default: 0)
> > +#
> > +# @policy: as documented for the 'policy' parameter of the
> > +#          KVM_SNP_LAUNCH_START KVM command (default: 0x30000)
> > +#
> > +# @guest-visible-workarounds: 16-byte, base64-encoded blob to report
> > +#                             hypervisor-defined workarounds, as documented
> > +#                             for the 'gosvm' parameter of the
> 
> typo: s/gosvm/gosvw/

Argh, can't seem to get that one right!

> 
> 
> > +#                             KVM_SNP_LAUNCH_START KVM command.
> > +#                             (default: all-zero)
> > +#
> > +# @id-block: 8-byte, base64-encoded blob to provide the ID Block
> > +#            structure documented in SEV-SNP spec, as documented for the
> > +#            'id_block_uaddr' parameter of the KVM_SNP_LAUNCH_FINISH
> > +#            command (default: all-zero)
> 
> The documentation says the ID Block is 96 bytes long (Table 65 in
> section 8.15 of the SNP FW ABI document).

Thanks for the catch, I think I grabbed the value from the wrong column here.

> 
> 
> > +#
> > +# @id-auth: 4096-byte, base64-encoded blob to provide the ID Authentication
> > +#           Information Structure documented in SEV-SNP spec, as documented
> > +#           for the 'id_auth_uaddr' parameter of the KVM_SNP_LAUNCH_FINISH
> > +#           command (default: all-zero)
> > +#
> > +# @auth-key-enabled: true if 'id-auth' blob contains the Author Key
> > +#                    documented in the SEV-SNP spec, as documented for the
> > +#                    'auth_key_en' parameter of the KVM_SNP_LAUNCH_FINISH
> > +#                    command (default: false)
> > +#
> > +# @host-data: 32-byte, base64-encoded user-defined blob to provide to the
> > +#             guest, as documented for the 'host_data' parameter of the
> > +#             KVM_SNP_LAUNCH_FINISH command (default: all-zero)
> > +#
> > +# Since: 6.2
> > +##
> > +{ 'struct': 'SevSnpGuestProperties',
> > +  'base': 'SevCommonProperties',
> > +  'data': {
> > +            '*init-flags': 'uint64',
> > +            '*policy': 'uint64',
> > +            '*guest-visible-workarounds': 'str',
> > +            '*id-block': 'str',
> > +            '*id-auth': 'str',
> > +            '*auth-key-enabled': 'bool',
> > +            '*host-data': 'str' } }
> > +
> >  ##
> >  # @ObjectType:
> >  #
> > @@ -816,6 +874,7 @@
> >      'secret',
> >      'secret_keyring',
> >      'sev-guest',
> > +    'sev-snp-guest',
> >      's390-pv-guest',
> >      'throttle-group',
> >      'tls-creds-anon',
> > @@ -873,6 +932,7 @@
> >        'secret':                     'SecretProperties',
> >        'secret_keyring':             'SecretKeyringProperties',
> >        'sev-guest':                  'SevGuestProperties',
> > +      'sev-snp-guest':              'SevSnpGuestProperties',
> >        'throttle-group':             'ThrottleGroupProperties',
> >        'tls-creds-anon':             'TlsCredsAnonProperties',
> >        'tls-creds-psk':              'TlsCredsPskProperties',
> > diff --git a/target/i386/sev.c b/target/i386/sev.c
> > index 6acebfbd53..ba08b7d3ab 100644
> > --- a/target/i386/sev.c
> > +++ b/target/i386/sev.c
> > @@ -38,7 +38,8 @@
> >  OBJECT_DECLARE_SIMPLE_TYPE(SevCommonState, SEV_COMMON)
> >  #define TYPE_SEV_GUEST "sev-guest"
> >  OBJECT_DECLARE_SIMPLE_TYPE(SevGuestState, SEV_GUEST)
> > -
> > +#define TYPE_SEV_SNP_GUEST "sev-snp-guest"
> > +OBJECT_DECLARE_SIMPLE_TYPE(SevSnpGuestState, SEV_SNP_GUEST)
> >  
> >  /**
> >   * SevGuestState:
> > @@ -82,8 +83,23 @@ struct SevGuestState {
> >      char *session_file;
> >  };
> >  
> > +struct SevSnpGuestState {
> > +    SevCommonState sev_common;
> > +
> > +    /* configuration parameters */
> > +    char *guest_visible_workarounds;
> > +    char *id_block;
> > +    char *id_auth;
> > +    char *host_data;
> > +
> > +    struct kvm_snp_init kvm_init_conf;
> > +    struct kvm_sev_snp_launch_start kvm_start_conf;
> > +    struct kvm_sev_snp_launch_finish kvm_finish_conf;
> > +};
> > +
> >  #define DEFAULT_GUEST_POLICY    0x1 /* disable debug */
> >  #define DEFAULT_SEV_DEVICE      "/dev/sev"
> > +#define DEFAULT_SEV_SNP_POLICY  0x30000
> >  
> >  #define SEV_INFO_BLOCK_GUID     "00f771de-1a7e-4fcb-890e-68c77e2fb44e"
> >  typedef struct __attribute__((__packed__)) SevInfoBlock {
> > @@ -364,6 +380,232 @@ static const TypeInfo sev_guest_info = {
> >      .class_init = sev_guest_class_init,
> >  };
> >  
> > +static void
> > +sev_snp_guest_get_init_flags(Object *obj, Visitor *v, const char *name,
> > +                             void *opaque, Error **errp)
> > +{
> > +    visit_type_uint64(v, name,
> > +                      (uint64_t *)&SEV_SNP_GUEST(obj)->kvm_init_conf.flags,
> > +                      errp);
> > +}
> > +
> > +static void
> > +sev_snp_guest_set_init_flags(Object *obj, Visitor *v, const char *name,
> > +                             void *opaque, Error **errp)
> > +{
> > +    visit_type_uint64(v, name,
> > +                      (uint64_t *)&SEV_SNP_GUEST(obj)->kvm_init_conf.flags,
> > +                      errp);
> > +}
> > +
> > +static void
> > +sev_snp_guest_get_policy(Object *obj, Visitor *v, const char *name,
> > +                         void *opaque, Error **errp)
> > +{
> > +    visit_type_uint64(v, name,
> > +                      (uint64_t *)&SEV_SNP_GUEST(obj)->kvm_start_conf.policy,
> > +                      errp);
> > +}
> > +
> > +static void
> > +sev_snp_guest_set_policy(Object *obj, Visitor *v, const char *name,
> > +                         void *opaque, Error **errp)
> > +{
> > +    visit_type_uint64(v, name,
> > +                      (uint64_t *)&SEV_SNP_GUEST(obj)->kvm_start_conf.policy,
> > +                      errp);
> > +}
> > +
> > +static char *
> > +sev_snp_guest_get_guest_visible_workarounds(Object *obj, Error **errp)
> > +{
> > +    return g_strdup(SEV_SNP_GUEST(obj)->guest_visible_workarounds);
> > +}
> > +
> > +static void
> > +sev_snp_guest_set_guest_visible_workarounds(Object *obj, const char *value,
> > +                                            Error **errp)
> > +{
> > +    SevSnpGuestState *sev_snp_guest = SEV_SNP_GUEST(obj);
> > +    struct kvm_sev_snp_launch_start *start = &sev_snp_guest->kvm_start_conf;
> > +    g_autofree guchar *blob;
> > +    gsize len;
> > +
> > +    if (sev_snp_guest->guest_visible_workarounds) {
> > +        g_free(sev_snp_guest->guest_visible_workarounds);
> > +    }
> > +
> > +    /* store the base64 str so we don't need to re-encode in getter */
> > +    sev_snp_guest->guest_visible_workarounds = g_strdup(value);
> > +
> > +    blob = g_base64_decode(sev_snp_guest->guest_visible_workarounds, &len);
> 
> I see there's a qbase64_decode which performs some checks and then calls
> g_base64_decode.  It might detect illegal chars in the value?

That does seems to be the preferred approach, I'll switch to that for
the series.

> 
> Also I think you should verify this decode succeeds by checking that
> blob is not NULL.
> 
> (similar comments for all base64_decode calls in this file.)

I'll add those checks as well.

> 
> 
> > +    if (len > sizeof(start->gosvw)) {
> > +        error_setg(errp, "parameter length of %lu exceeds max of %lu",
> > +                   len, sizeof(start->gosvw));
> > +        return;
> > +    }
> > +
> > +    memcpy(start->gosvw, blob, len);
> > +}
> > +
> > +static char *
> > +sev_snp_guest_get_id_block(Object *obj, Error **errp)
> > +{
> > +    SevSnpGuestState *sev_snp_guest = SEV_SNP_GUEST(obj);
> > +
> > +    return g_strdup(sev_snp_guest->id_block);
> > +}
> > +
> > +static void
> > +sev_snp_guest_set_id_block(Object *obj, const char *value, Error **errp)
> > +{
> > +    SevSnpGuestState *sev_snp_guest = SEV_SNP_GUEST(obj);
> > +    struct kvm_sev_snp_launch_finish *finish = &sev_snp_guest->kvm_finish_conf;
> > +    gsize len;
> > +
> > +    if (sev_snp_guest->id_block) {
> > +        g_free(sev_snp_guest->id_block);
> > +        g_free((guchar *)finish->id_block_uaddr);
> > +    }
> > +
> > +    /* store the base64 str so we don't need to re-encode in getter */
> > +    sev_snp_guest->id_block = g_strdup(value);
> > +
> > +    finish->id_block_uaddr = (uint64_t)g_base64_decode(sev_snp_guest->id_block, &len);
> > +    if (len > KVM_SEV_SNP_ID_BLOCK_SIZE) {
> > +        error_setg(errp, "parameter length of %lu exceeds max of %u",
> > +                   len, KVM_SEV_SNP_ID_BLOCK_SIZE);
> > +        return;
> > +    }
> > +    finish->id_block_en = 1;
> 
> There's no way to set the id_block to a "don't want an ID block", except
> for not giving this option to the sev-snp-guest object.  I'm not sure if
> this is a problem (for example, if you dump one VM's config and try to
> load it elsewhere).
> 
> Maybe if strlen(value)==0 you should set finish->id_block_en = 0.

That makes sense, I'll add the handling for this.

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

* Re: [RFC PATCH v2 02/12] linux-header: add the SNP specific command
  2021-09-03 20:36     ` Dov Murik
  (?)
@ 2021-09-07 14:27     ` Michael Roth
  -1 siblings, 0 replies; 61+ messages in thread
From: Michael Roth @ 2021-09-07 14:27 UTC (permalink / raw)
  To: Dov Murik
  Cc: qemu-devel, Connor Kuehl, Philippe Mathieu-Daudé,
	Michael S . Tsirkin, James Bottomley, Dr . David Alan Gilbert,
	Tom Lendacky, Paolo Bonzini, David Gibson,
	Daniel P . Berrangé,
	kvm, Eduardo Habkost, Brijesh Singh, Markus Armbruster,
	Eric Blake

On Fri, Sep 03, 2021 at 11:36:43PM +0300, Dov Murik wrote:
> Hi Michael,
> 
> On 27/08/2021 1:26, Michael Roth wrote:
> > From: Brijesh Singh <brijesh.singh@amd.com>
> > 
> > Sync the kvm.h with the kernel to include the SNP specific commands.
> > 
> > Signed-off-by: Brijesh Singh <brijesh.singh@amd.com>
> > Signed-off-by: Michael Roth <michael.roth@amd.com>
> > ---
> >  linux-headers/linux/kvm.h | 50 +++++++++++++++++++++++++++++++++++++++
> >  1 file changed, 50 insertions(+)
> > 
> 
> In previous review round I commented:
> 
> ------
> What about psp-sev.h ? I see that kernel patch "[PATCH Part2 RFC v4
> 11/40] crypto:ccp: Define the SEV-SNP commands" adds some new PSP return
> codes.
> 
> The QEMU user-friendly string list sev_fw_errlist (in sev.c) should be
> updated accordingly.
> -------

Sorry I missed that one, will include the header in the next round and update
the QEMU bits as suggested.

> 
> 
> -Dov

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

* Re: [RFC PATCH v2 12/12] i386/sev: update query-sev QAPI format to handle SEV-SNP
  2021-09-07 11:52         ` Dr. David Alan Gilbert
@ 2021-09-07 14:33           ` Michael Roth via
  -1 siblings, 0 replies; 61+ messages in thread
From: Michael Roth @ 2021-09-07 14:33 UTC (permalink / raw)
  To: Dr. David Alan Gilbert
  Cc: Daniel P. Berrangé,
	Markus Armbruster, qemu-devel, Connor Kuehl,
	Philippe Mathieu-Daudé,
	Michael S . Tsirkin, James Bottomley, Tom Lendacky,
	Paolo Bonzini, Dov Murik, David Gibson, kvm, Eduardo Habkost,
	Brijesh Singh, Eric Blake

On Tue, Sep 07, 2021 at 12:52:54PM +0100, Dr. David Alan Gilbert wrote:
> * Daniel P. Berrangé (berrange@redhat.com) wrote:
> > On Wed, Sep 01, 2021 at 04:14:10PM +0200, Markus Armbruster wrote:
> > > Michael Roth <michael.roth@amd.com> writes:
> > > 
> > > > Most of the current 'query-sev' command is relevant to both legacy
> > > > SEV/SEV-ES guests and SEV-SNP guests, with 2 exceptions:
> > > >
> > > >   - 'policy' is a 64-bit field for SEV-SNP, not 32-bit, and
> > > >     the meaning of the bit positions has changed
> > > >   - 'handle' is not relevant to SEV-SNP
> > > >
> > > > To address this, this patch adds a new 'sev-type' field that can be
> > > > used as a discriminator to select between SEV and SEV-SNP-specific
> > > > fields/formats without breaking compatibility for existing management
> > > > tools (so long as management tools that add support for launching
> > > > SEV-SNP guest update their handling of query-sev appropriately).
> > > 
> > > Technically a compatibility break: query-sev can now return an object
> > > that whose member @policy has different meaning, and also lacks @handle.
> > > 
> > > Matrix:
> > > 
> > >                             Old mgmt app    New mgmt app
> > >     Old QEMU, SEV/SEV-ES       good            good(1)
> > >     New QEMU, SEV/SEV-ES       good(2)         good
> > >     New QEMU, SEV-SNP           bad(3)         good
> > > 
> > > Notes:
> > > 
> > > (1) As long as the management application can cope with absent member
> > > @sev-type.
> > > 
> > > (2) As long as the management application ignores unknown member
> > > @sev-type.
> > > 
> > > (3) Management application may choke on missing member @handle, or
> > > worse, misinterpret member @policy.  Can only happen when something
> > > other than the management application created the SEV-SNP guest (or the
> > > user somehow made the management application create one even though it
> > > doesn't know how, say with CLI option passthrough, but that's always
> > > fragile, and I wouldn't worry about it here).
> > > 
> > > I think (1) and (2) are reasonable.  (3) is an issue for management
> > > applications that support attaching to existing guests.  Thoughts?
> > 
> > IIUC you can only reach scenario (3) if you have created a guest
> > using '-object sev-snp-guest', which is a new feature introduced
> > in patch 2.
> > 
> > IOW, scenario (3)  old mgmt app + new QEMU + sev-snp guest does
> > not exist as a combination. Thus the (bad) field is actually (n/a)
> > 
> > So I believe this proposed change is acceptable in all scenarios
> > with existing deployed usage, as well as all newly introduced
> > scenarios.
> 
> I wonder if it's worth going firther and renaming 'policy' in the 
> SNP world to 'snppolicy' just to reduce the risk of accidentally
> specifying the wrong one.

Seems reasonable. I'll plan on renaming to 'snp-policy' if there are no
objections.

> 
> Dave
> 
> > Regards,
> > Daniel
> > -- 
> > |: https://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%2Fberrange.com%2F&amp;data=04%7C01%7Cmichael.roth%40amd.com%7Cb9a484cd5d4f484b542908d971f61073%7C3dd8961fe4884e608e11a82d994e183d%7C0%7C0%7C637666123947391605%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&amp;sdata=D56oHIuVk%2FmAJaKYtpJ3ZEZpKZpDPWZXydV3tpYjcM4%3D&amp;reserved=0      -o-    https://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%2Fwww.flickr.com%2Fphotos%2Fdberrange&amp;data=04%7C01%7Cmichael.roth%40amd.com%7Cb9a484cd5d4f484b542908d971f61073%7C3dd8961fe4884e608e11a82d994e183d%7C0%7C0%7C637666123947401567%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&amp;sdata=A9YA65nj6En3f3E2wm%2BVZE%2F6DpdbDKyHSWN9VXHAk8U%3D&amp;reserved=0 :|
> > |: https://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%2Flibvirt.org%2F&amp;data=04%7C01%7Cmichael.roth%40amd.com%7Cb9a484cd5d4f484b542908d971f61073%7C3dd8961fe4884e608e11a82d994e183d%7C0%7C0%7C637666123947401567%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&amp;sdata=yf%2FV3f3%2FNxEDwmYESp7D0ZOn74aM6cXskVJrvHLvXRE%3D&amp;reserved=0         -o-            https://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%2Ffstop138.berrange.com%2F&amp;data=04%7C01%7Cmichael.roth%40amd.com%7Cb9a484cd5d4f484b542908d971f61073%7C3dd8961fe4884e608e11a82d994e183d%7C0%7C0%7C637666123947401567%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&amp;sdata=pUYNVu6WWgPtwjwrjvz3YCCY7S1Qli%2FfvQKmkaRu3gc%3D&amp;reserved=0 :|
> > |: https://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%2Fentangle-photo.org%2F&amp;data=04%7C01%7Cmichael.roth%40amd.com%7Cb9a484cd5d4f484b542908d971f61073%7C3dd8961fe4884e608e11a82d994e183d%7C0%7C0%7C637666123947401567%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&amp;sdata=opdXI%2BlyzxWhUbNNgka6sMKMiLmMHfk8WuZY6cMy7yE%3D&amp;reserved=0    -o-    https://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%2Fwww.instagram.com%2Fdberrange&amp;data=04%7C01%7Cmichael.roth%40amd.com%7Cb9a484cd5d4f484b542908d971f61073%7C3dd8961fe4884e608e11a82d994e183d%7C0%7C0%7C637666123947401567%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&amp;sdata=8cV6tsleO5nDBVKR3WX74a%2BKch5RdRdmPciv%2F6T9nOg%3D&amp;reserved=0 :|
> > 
> -- 
> Dr. David Alan Gilbert / dgilbert@redhat.com / Manchester, UK
> 

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

* Re: [RFC PATCH v2 12/12] i386/sev: update query-sev QAPI format to handle SEV-SNP
@ 2021-09-07 14:33           ` Michael Roth via
  0 siblings, 0 replies; 61+ messages in thread
From: Michael Roth via @ 2021-09-07 14:33 UTC (permalink / raw)
  To: Dr. David Alan Gilbert
  Cc: Daniel P. Berrangé,
	Markus Armbruster, qemu-devel, Connor Kuehl,
	Philippe Mathieu-Daudé,
	Michael S . Tsirkin, James Bottomley, Tom Lendacky,
	Paolo Bonzini, Dov Murik, David Gibson, kvm, Eduardo Habkost,
	Brijesh Singh, Eric Blake

On Tue, Sep 07, 2021 at 12:52:54PM +0100, Dr. David Alan Gilbert wrote:
> * Daniel P. Berrangé (berrange@redhat.com) wrote:
> > On Wed, Sep 01, 2021 at 04:14:10PM +0200, Markus Armbruster wrote:
> > > Michael Roth <michael.roth@amd.com> writes:
> > > 
> > > > Most of the current 'query-sev' command is relevant to both legacy
> > > > SEV/SEV-ES guests and SEV-SNP guests, with 2 exceptions:
> > > >
> > > >   - 'policy' is a 64-bit field for SEV-SNP, not 32-bit, and
> > > >     the meaning of the bit positions has changed
> > > >   - 'handle' is not relevant to SEV-SNP
> > > >
> > > > To address this, this patch adds a new 'sev-type' field that can be
> > > > used as a discriminator to select between SEV and SEV-SNP-specific
> > > > fields/formats without breaking compatibility for existing management
> > > > tools (so long as management tools that add support for launching
> > > > SEV-SNP guest update their handling of query-sev appropriately).
> > > 
> > > Technically a compatibility break: query-sev can now return an object
> > > that whose member @policy has different meaning, and also lacks @handle.
> > > 
> > > Matrix:
> > > 
> > >                             Old mgmt app    New mgmt app
> > >     Old QEMU, SEV/SEV-ES       good            good(1)
> > >     New QEMU, SEV/SEV-ES       good(2)         good
> > >     New QEMU, SEV-SNP           bad(3)         good
> > > 
> > > Notes:
> > > 
> > > (1) As long as the management application can cope with absent member
> > > @sev-type.
> > > 
> > > (2) As long as the management application ignores unknown member
> > > @sev-type.
> > > 
> > > (3) Management application may choke on missing member @handle, or
> > > worse, misinterpret member @policy.  Can only happen when something
> > > other than the management application created the SEV-SNP guest (or the
> > > user somehow made the management application create one even though it
> > > doesn't know how, say with CLI option passthrough, but that's always
> > > fragile, and I wouldn't worry about it here).
> > > 
> > > I think (1) and (2) are reasonable.  (3) is an issue for management
> > > applications that support attaching to existing guests.  Thoughts?
> > 
> > IIUC you can only reach scenario (3) if you have created a guest
> > using '-object sev-snp-guest', which is a new feature introduced
> > in patch 2.
> > 
> > IOW, scenario (3)  old mgmt app + new QEMU + sev-snp guest does
> > not exist as a combination. Thus the (bad) field is actually (n/a)
> > 
> > So I believe this proposed change is acceptable in all scenarios
> > with existing deployed usage, as well as all newly introduced
> > scenarios.
> 
> I wonder if it's worth going firther and renaming 'policy' in the 
> SNP world to 'snppolicy' just to reduce the risk of accidentally
> specifying the wrong one.

Seems reasonable. I'll plan on renaming to 'snp-policy' if there are no
objections.

> 
> Dave
> 
> > Regards,
> > Daniel
> > -- 
> > |: https://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%2Fberrange.com%2F&amp;data=04%7C01%7Cmichael.roth%40amd.com%7Cb9a484cd5d4f484b542908d971f61073%7C3dd8961fe4884e608e11a82d994e183d%7C0%7C0%7C637666123947391605%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&amp;sdata=D56oHIuVk%2FmAJaKYtpJ3ZEZpKZpDPWZXydV3tpYjcM4%3D&amp;reserved=0      -o-    https://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%2Fwww.flickr.com%2Fphotos%2Fdberrange&amp;data=04%7C01%7Cmichael.roth%40amd.com%7Cb9a484cd5d4f484b542908d971f61073%7C3dd8961fe4884e608e11a82d994e183d%7C0%7C0%7C637666123947401567%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&amp;sdata=A9YA65nj6En3f3E2wm%2BVZE%2F6DpdbDKyHSWN9VXHAk8U%3D&amp;reserved=0 :|
> > |: https://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%2Flibvirt.org%2F&amp;data=04%7C01%7Cmichael.roth%40amd.com%7Cb9a484cd5d4f484b542908d971f61073%7C3dd8961fe4884e608e11a82d994e183d%7C0%7C0%7C637666123947401567%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&amp;sdata=yf%2FV3f3%2FNxEDwmYESp7D0ZOn74aM6cXskVJrvHLvXRE%3D&amp;reserved=0         -o-            https://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%2Ffstop138.berrange.com%2F&amp;data=04%7C01%7Cmichael.roth%40amd.com%7Cb9a484cd5d4f484b542908d971f61073%7C3dd8961fe4884e608e11a82d994e183d%7C0%7C0%7C637666123947401567%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&amp;sdata=pUYNVu6WWgPtwjwrjvz3YCCY7S1Qli%2FfvQKmkaRu3gc%3D&amp;reserved=0 :|
> > |: https://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%2Fentangle-photo.org%2F&amp;data=04%7C01%7Cmichael.roth%40amd.com%7Cb9a484cd5d4f484b542908d971f61073%7C3dd8961fe4884e608e11a82d994e183d%7C0%7C0%7C637666123947401567%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&amp;sdata=opdXI%2BlyzxWhUbNNgka6sMKMiLmMHfk8WuZY6cMy7yE%3D&amp;reserved=0    -o-    https://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%2Fwww.instagram.com%2Fdberrange&amp;data=04%7C01%7Cmichael.roth%40amd.com%7Cb9a484cd5d4f484b542908d971f61073%7C3dd8961fe4884e608e11a82d994e183d%7C0%7C0%7C637666123947401567%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&amp;sdata=8cV6tsleO5nDBVKR3WX74a%2BKch5RdRdmPciv%2F6T9nOg%3D&amp;reserved=0 :|
> > 
> -- 
> Dr. David Alan Gilbert / dgilbert@redhat.com / Manchester, UK
> 


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

* Re: [RFC PATCH v2 07/12] i386/sev: populate secrets and cpuid page and finalize the SNP launch
  2021-09-03 20:24     ` Dov Murik
  (?)
@ 2021-09-07 16:18     ` Michael Roth
  -1 siblings, 0 replies; 61+ messages in thread
From: Michael Roth @ 2021-09-07 16:18 UTC (permalink / raw)
  To: Dov Murik
  Cc: qemu-devel, Connor Kuehl, Philippe Mathieu-Daudé,
	Michael S . Tsirkin, James Bottomley, Dr . David Alan Gilbert,
	Tom Lendacky, Paolo Bonzini, David Gibson,
	Daniel P . Berrangé,
	kvm, Eduardo Habkost, Brijesh Singh, Markus Armbruster,
	Eric Blake

On Fri, Sep 03, 2021 at 11:24:20PM +0300, Dov Murik wrote:
> Hi Michael,

Hi Dov,

Thanks for reviewing!

> 
> On 27/08/2021 1:26, Michael Roth wrote:
> > From: Brijesh Singh <brijesh.singh@amd.com>
> > 
> > During the SNP guest launch sequence, a special secrets and cpuid page
> > needs to be populated by the SEV-SNP firmware. 
> 
> Just to be clear: these are two distinct pages.  I suggest rephrasing to
> "... a special secrets page and a special cpuid page need to be
> populated ..." (or something along these lines).

Will do.

> 
> > The secrets page contains
> > the VM Platform Communication Key (VMPCKs) used by the guest to send and
> > receive secure messages to the PSP. And CPUID page will contain the CPUID
> > value filtered through the PSP.
> > 
> > The guest BIOS (OVMF) reserves these pages in MEMFD and location of it
> > is available through the SNP boot block GUID. While finalizing the guest
> > boot flow, lookup for the boot block and call the SNP_LAUNCH_UPDATE
> > command to populate secrets and cpuid pages.
> > 
> > In order to support early boot code, the OVMF may ask hypervisor to
> > request the pre-validation of certain memory range. If such range is
> > present the call SNP_LAUNCH_UPDATE command to validate those address
> > range without affecting the measurement. See the SEV-SNP specification
> > for further details.
> > 
> > Finally, call the SNP_LAUNCH_FINISH to finalize the guest boot.
> > 
> > Signed-off-by: Brijesh Singh <brijesh.singh@amd.com>
> > Signed-off-by: Michael Roth <michael.roth@amd.com>
> > ---
> >  target/i386/sev.c        | 189 ++++++++++++++++++++++++++++++++++++++-
> >  target/i386/trace-events |   2 +
> >  2 files changed, 189 insertions(+), 2 deletions(-)
> > 
> > diff --git a/target/i386/sev.c b/target/i386/sev.c
> > index 867c0cb457..0009c93d28 100644
> > --- a/target/i386/sev.c
> > +++ b/target/i386/sev.c
> > @@ -33,6 +33,7 @@
> >  #include "monitor/monitor.h"
> >  #include "exec/confidential-guest-support.h"
> >  #include "hw/i386/pc.h"
> > +#include "qemu/range.h"
> >  
> >  #define TYPE_SEV_COMMON "sev-common"
> >  OBJECT_DECLARE_SIMPLE_TYPE(SevCommonState, SEV_COMMON)
> > @@ -107,6 +108,19 @@ typedef struct __attribute__((__packed__)) SevInfoBlock {
> >      uint32_t reset_addr;
> >  } SevInfoBlock;
> >  
> > +#define SEV_SNP_BOOT_BLOCK_GUID "bd39c0c2-2f8e-4243-83e8-1b74cebcb7d9"
> > +typedef struct __attribute__((__packed__)) SevSnpBootInfoBlock {
> > +    /* Prevalidate range address */
> > +    uint32_t pre_validated_start;
> > +    uint32_t pre_validated_end;
> > +    /* Secrets page address */
> > +    uint32_t secrets_addr;
> > +    uint32_t secrets_len;
> 
> Just curious: isn't secrets_len always 4096? (same for cpuid_len)
> Though it might be a good future proofing to have a length field.

I believe you can specify a contiguous range of pages for cpuid_len (in
which case you'll get multiple self-contained CPUID tables), but I'm not
sure about the secrets page. I've tried to avoid the need for multiple
CPUID tables/pages in the initial implementation by allowing 0 entries
to be left out, like how KVM_SET_CPUID2 does it; guest kernels will then
check the CPUID ranges to determine if a 0 entry is a valid all-0 entry
vs. an invalid one.

> 
> 
> > +    /* CPUID page address */
> > +    uint32_t cpuid_addr;
> > +    uint32_t cpuid_len;
> > +} SevSnpBootInfoBlock;
> > +
> >  static Error *sev_mig_blocker;
> >  
> >  static const char *const sev_fw_errlist[] = {
> > @@ -1086,6 +1100,162 @@ static Notifier sev_machine_done_notify = {
> >      .notify = sev_launch_get_measure,
> >  };
> >  
> > +static int
> > +sev_snp_launch_update_gpa(uint32_t hwaddr, uint32_t size, uint8_t type)
> > +{
> > +    void *hva;
> > +    MemoryRegion *mr = NULL;
> > +    SevSnpGuestState *sev_snp_guest =
> > +        SEV_SNP_GUEST(MACHINE(qdev_get_machine())->cgs);
> > +
> > +    hva = gpa2hva(&mr, hwaddr, size, NULL);
> > +    if (!hva) {
> > +        error_report("SEV-SNP failed to get HVA for GPA 0x%x", hwaddr);
> > +        return 1;
> > +    }
> > +
> > +    return sev_snp_launch_update(sev_snp_guest, hwaddr, hva, size, type);
> > +}
> > +
> > +static bool
> > +detect_first_overlap(uint64_t start, uint64_t end, Range *range_list,
> > +                     size_t range_count, Range *overlap_range)
> > +{
> > +    int i;
> > +    bool overlap = false;
> > +    Range new;
> > +
> > +    assert(overlap_range);
> 
> Also:
> assert(end >= start)
> assert(range_count == 0 || range_list)
> 
> > +    range_make_empty(overlap_range);
> > +    range_init_nofail(&new, start, end - start + 1);
> > +
> > +    for (i = 0; i < range_count; i++) {
> > +        if (range_overlaps_range(&new, &range_list[i]) &&
> > +            (range_is_empty(overlap_range) ||
> > +             range_lob(&range_list[i]) < range_lob(overlap_range))) {
> > +            *overlap_range = range_list[i];
> > +            overlap = true;
> > +        }
> > +    }
> > +
> > +    return overlap;
> > +}
> > +
> > +static void snp_ovmf_boot_block_setup(void)
> > +{
> > +    SevSnpBootInfoBlock *info;
> > +    uint32_t start, end, sz;
> > +    int ret;
> > +    Range validated_ranges[2];
> > +
> > +    /*
> > +     * Extract the SNP boot block for the SEV-SNP guests by locating the
> > +     * SNP_BOOT GUID. The boot block contains the information such as location
> > +     * of secrets and CPUID page, additionaly it may contain the range of
> > +     * memory that need to be pre-validated for the boot.
> > +     */
> > +    if (!pc_system_ovmf_table_find(SEV_SNP_BOOT_BLOCK_GUID,
> > +        (uint8_t **)&info, NULL)) {
> 
> Fix indent of arguments.
> 
> 
> > +        error_report("SEV-SNP: failed to find the SNP boot block");
> > +        exit(1);
> > +    }
> > +
> > +    trace_kvm_sev_snp_ovmf_boot_block_info(info->secrets_addr,
> > +                                           info->secrets_len, info->cpuid_addr,
> > +                                           info->cpuid_len,
> > +                                           info->pre_validated_start,
> > +                                           info->pre_validated_end);
> > +
> > +    /* Populate the secrets page */
> > +    ret = sev_snp_launch_update_gpa(info->secrets_addr, info->secrets_len,
> > +                                    KVM_SEV_SNP_PAGE_TYPE_SECRETS);
> > +    if (ret) {
> > +        error_report("SEV-SNP: failed to insert secret page GPA 0x%x",
> > +                     info->secrets_addr);
> > +        exit(1);
> > +    }
> > +
> > +    /* Populate the cpuid page */
> > +    ret = sev_snp_launch_update_gpa(info->cpuid_addr, info->cpuid_len,
> > +                                    KVM_SEV_SNP_PAGE_TYPE_CPUID);
> > +    if (ret) {
> > +        error_report("SEV-SNP: failed to insert cpuid page GPA 0x%x",
> > +                     info->cpuid_addr);
> > +        exit(1);
> > +    }
> > +
> > +    /*
> > +     * Pre-validate the range using the LAUNCH_UPDATE_DATA, if the
> > +     * pre-validation range contains the CPUID and Secret page GPA then skip
> > +     * it. This is because SEV-SNP firmware pre-validates those pages as part
> > +     * of adding secrets and cpuid LAUNCH_UPDATE type.
> > +     */
> > +    range_init_nofail(&validated_ranges[0], info->secrets_addr, info->secrets_len);
> > +    range_init_nofail(&validated_ranges[1], info->cpuid_addr, info->cpuid_len);
> > +    start = info->pre_validated_start;
> > +    end = info->pre_validated_end;
> > +
> > +    while (start < end) {
> > +        Range overlap_range;
> > +
> > +        /* Check if the requested range overlaps with Secrets and CPUID page */
> > +        if (detect_first_overlap(start, end, validated_ranges, 2,
> 
> Replace the literal 2 with ARRAY_SIZE(validated_ranges).

Will do. I was a little concerned with the case where validated_ranges
might have variable number of entries in future (say a new optional page
was added/scanned for via ovmf so extra space was allocated for it). I
think some comments should be enough to avoid any issues should that
change be made in the future though.

> 
> 
> > +                                 &overlap_range)) {
> > +            if (start < range_lob(&overlap_range)) {
> > +                sz = range_lob(&overlap_range) - start;
> > +                if (sev_snp_launch_update_gpa(start, sz,
> > +                    KVM_SEV_SNP_PAGE_TYPE_UNMEASURED)) {
> 
> Fix indent of arguments (if possible).

Not sure it is :( Not without changing the loop logic anyway, I'll see if
that can be reworked a bit.

> 
> > +                    error_report("SEV-SNP: failed to validate gpa 0x%x sz %d",
> > +                                 start, sz);
> > +                    exit(1);
> > +                }
> > +            }
> > +
> > +            start = range_upb(&overlap_range) + 1;
> > +            continue;
> > +        }
> > +
> > +        /* Validate the remaining range */
> > +        if (sev_snp_launch_update_gpa(start, end - start,
> 
> I think the second argument should be:    end - start + 1 .
> 
> Consider start=0x8000 end=0x8fff (inclusive). In this case you want to
> validate exactly 0x1000 bytes starting at 0x8000.  So the size should be
> 0x8fff - 0x8000 + 1.

Good catch, I'll get this fixed up.

> 
> I assume all this doesn't matter for the underlying calls which operate
> at whole pages anyway (are there proper asserts in sev_snp_launch_update
> (or in KVM) that verify that start and sz are page-size-aligned?)

That's correct, snp_launch_update() mostly operates on the underlying
pages, so the actual size doesn't matter much. I'm not sure if that
granularity is meant to be transparent to userspace or explicitly enforced
though, I'll need to check with Brijesh on that.

> 
> 
> 
> > +            KVM_SEV_SNP_PAGE_TYPE_UNMEASURED)) {
> 
> Fix indent of arguments.
> 
> 
> > +            error_report("SEV-SNP: failed to validate gpa 0x%x sz %d",
> > +                         start, end - start);
> > +            exit(1);
> > +        }
> > +
> > +        start = end;
> > +    }
> > +}
> > +
> > +static void
> > +sev_snp_launch_finish(SevSnpGuestState *sev_snp)
> > +{
> > +    int ret, error;
> > +    Error *local_err = NULL;
> > +    struct kvm_sev_snp_launch_finish *finish = &sev_snp->kvm_finish_conf;
> > +
> > +    trace_kvm_sev_snp_launch_finish();
> > +    ret = sev_ioctl(SEV_COMMON(sev_snp)->sev_fd, KVM_SEV_SNP_LAUNCH_FINISH, finish, &error);
> > +    if (ret) {
> > +        error_report("%s: SNP_LAUNCH_FINISH ret=%d fw_error=%d '%s'",
> > +                     __func__, ret, error, fw_error_to_str(error));
> > +        exit(1);
> > +    }
> > +
> > +    sev_set_guest_state(SEV_COMMON(sev_snp), SEV_STATE_RUNNING);
> > +
> > +    /* add migration blocker */
> > +    error_setg(&sev_mig_blocker,
> > +               "SEV: Migration is not implemented");
> > +    ret = migrate_add_blocker(sev_mig_blocker, &local_err);
> > +    if (local_err) {
> > +        error_report_err(local_err);
> > +        error_free(sev_mig_blocker);
> > +        exit(1);
> > +    }
> > +}
> > +
> > +
> >  static void
> >  sev_launch_finish(SevGuestState *sev_guest)
> >  {
> > @@ -1121,7 +1291,12 @@ sev_vm_state_change(void *opaque, bool running, RunState state)
> >  
> >      if (running) {
> >          if (!sev_check_state(sev_common, SEV_STATE_RUNNING)) {
> > -            sev_launch_finish(SEV_GUEST(sev_common));
> > +            if (sev_snp_enabled()) {
> > +                snp_ovmf_boot_block_setup();
> > +                sev_snp_launch_finish(SEV_SNP_GUEST(sev_common));
> > +            } else {
> > +                sev_launch_finish(SEV_GUEST(sev_common));
> > +            }
> >          }
> >      }
> >  }
> > @@ -1236,7 +1411,17 @@ int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp)
> >      }
> >  
> >      ram_block_notifier_add(&sev_ram_notifier);
> > -    qemu_add_machine_init_done_notifier(&sev_machine_done_notify);
> > +
> > +    /*
> > +     * The machine done notify event is used by the SEV guest to get the
> > +     * measurement of the encrypted images. When SEV-SNP is enabled then
> > +     * measurement is part of the attestation report and the measurement
> > +     * command does not exist. So skip registering the notifier.
> > +     */
> > +    if (!sev_snp_enabled()) {
> > +        qemu_add_machine_init_done_notifier(&sev_machine_done_notify);
> > +    }
> > +
> >      qemu_add_vm_change_state_handler(sev_vm_state_change, sev_common);
> >  
> >      cgs->ready = true;
> > diff --git a/target/i386/trace-events b/target/i386/trace-events
> > index 0c2d250206..db91287439 100644
> > --- a/target/i386/trace-events
> > +++ b/target/i386/trace-events
> > @@ -13,3 +13,5 @@ kvm_sev_launch_secret(uint64_t hpa, uint64_t hva, uint64_t secret, int len) "hpa
> >  kvm_sev_attestation_report(const char *mnonce, const char *data) "mnonce %s data %s"
> >  kvm_sev_snp_launch_start(uint64_t policy) "policy 0x%" PRIx64
> >  kvm_sev_snp_launch_update(void *addr, uint64_t len, int type) "addr %p len 0x%" PRIx64 " type %d"
> > +kvm_sev_snp_launch_finish(void) ""
> > +kvm_sev_snp_ovmf_boot_block_info(uint32_t secrets_gpa, uint32_t slen, uint32_t cpuid_gpa, uint32_t clen, uint32_t s, uint32_t e) "secrets 0x%x+0x%x cpuid 0x%x+0x%x pre-validate 0x%x+0x%x"
> 
> In this trace format string you use the notation A+B to indicate addr=A
>  len=B.  But for the pre-validated range the arguments are 'start' and
> 'end' (and not 'addr' and 'len'), so I suggest choosing a different
> notation to log that range.

Indeed, will get that fixed up.

> 
> -Dov

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

* Re: [RFC PATCH v2 11/12] i386/sev: sev-snp: add support for CPUID validation
  2021-09-05 10:02     ` Dov Murik
  (?)
@ 2021-09-07 16:50     ` Michael Roth
  2021-09-07 17:44         ` Dov Murik
  -1 siblings, 1 reply; 61+ messages in thread
From: Michael Roth @ 2021-09-07 16:50 UTC (permalink / raw)
  To: Dov Murik
  Cc: qemu-devel, Connor Kuehl, Philippe Mathieu-Daudé,
	Michael S . Tsirkin, James Bottomley, Dr . David Alan Gilbert,
	Tom Lendacky, Paolo Bonzini, David Gibson,
	Daniel P . Berrangé,
	kvm, Eduardo Habkost, Brijesh Singh, Markus Armbruster,
	Eric Blake

On Sun, Sep 05, 2021 at 01:02:12PM +0300, Dov Murik wrote:
> Hi Michael,
> 
> On 27/08/2021 1:26, Michael Roth wrote:
> > SEV-SNP firmware allows a special guest page to be populated with a
> > table of guest CPUID values so that they can be validated through
> > firmware before being loaded into encrypted guest memory where they can
> > be used in place of hypervisor-provided values[1].
> > 
> > As part of SEV-SNP guest initialization, use this process to validate
> > the CPUID entries reported by KVM_GET_CPUID2 prior to initial guest
> > start.
> > 
> > [1]: SEV SNP Firmware ABI Specification, Rev. 0.8, 8.13.2.6
> > 
> > Signed-off-by: Michael Roth <michael.roth@amd.com>
> > ---
> >  target/i386/sev.c | 146 +++++++++++++++++++++++++++++++++++++++++++++-
> >  1 file changed, 143 insertions(+), 3 deletions(-)
> > 
> > diff --git a/target/i386/sev.c b/target/i386/sev.c
> > index 0009c93d28..72a6146295 100644
> > --- a/target/i386/sev.c
> > +++ b/target/i386/sev.c
> > @@ -153,6 +153,36 @@ static const char *const sev_fw_errlist[] = {
> >  
> >  #define SEV_FW_MAX_ERROR      ARRAY_SIZE(sev_fw_errlist)
> >  
> > +/* <linux/kvm.h> doesn't expose this, so re-use the max from kvm.c */
> > +#define KVM_MAX_CPUID_ENTRIES 100
> > +
> > +typedef struct KvmCpuidInfo {
> > +    struct kvm_cpuid2 cpuid;
> > +    struct kvm_cpuid_entry2 entries[KVM_MAX_CPUID_ENTRIES];
> > +} KvmCpuidInfo;
> > +
> > +#define SNP_CPUID_FUNCTION_MAXCOUNT 64
> > +#define SNP_CPUID_FUNCTION_UNKNOWN 0xFFFFFFFF
> > +
> > +typedef struct {
> > +    uint32_t eax_in;
> > +    uint32_t ecx_in;
> > +    uint64_t xcr0_in;
> > +    uint64_t xss_in;
> > +    uint32_t eax;
> > +    uint32_t ebx;
> > +    uint32_t ecx;
> > +    uint32_t edx;
> > +    uint64_t reserved;
> > +} __attribute__((packed)) SnpCpuidFunc;
> > +
> > +typedef struct {
> > +    uint32_t count;
> > +    uint32_t reserved1;
> > +    uint64_t reserved2;
> > +    SnpCpuidFunc entries[SNP_CPUID_FUNCTION_MAXCOUNT];
> > +} __attribute__((packed)) SnpCpuidInfo;
> > +
> >  static int
> >  sev_ioctl(int fd, int cmd, void *data, int *error)
> >  {
> > @@ -1141,6 +1171,117 @@ detect_first_overlap(uint64_t start, uint64_t end, Range *range_list,
> >      return overlap;
> >  }
> >  
> > +static int
> > +sev_snp_cpuid_info_fill(SnpCpuidInfo *snp_cpuid_info,
> > +                        const KvmCpuidInfo *kvm_cpuid_info)
> > +{
> > +    size_t i;
> > +
> > +    memset(snp_cpuid_info, 0, sizeof(*snp_cpuid_info));
> > +
> > +    for (i = 0; kvm_cpuid_info->entries[i].function != 0xFFFFFFFF; i++) {
> 
> Maybe iterate only while i < kvm_cpuid_info.cpuid.nent ?

Unfortunately kvm_vcpu_ioctl_get_cpuid2() in kernel only checks
kvm_cpuid_info.cpuid.nent as an input to verify there's enough room in
in kvm_cpuid_info.cpuid.entries array to copy the values over. It
doesn't update kvm_cpuid_info.cpuid.nent to indicate how many entries
are actually present, so I've been relying on the 0xFFFFFFFF marker
stuff.

An alternative approach is to keep incrementing cpuid.nent until the KVM
ioctl stops reporting E2BIG, I think the KVM selftests take this
approach as well so that's probably the way to go.

> 
> > +        const struct kvm_cpuid_entry2 *kvm_cpuid_entry;
> > +        SnpCpuidFunc *snp_cpuid_entry;
> > +
> > +        kvm_cpuid_entry = &kvm_cpuid_info->entries[i];
> > +        snp_cpuid_entry = &snp_cpuid_info->entries[i];
> 
> There's no explicit check that i < KVM_MAX_CPUID_ENTRIES and i <
> SNP_CPUID_FUNCTION_MAXCOUNT.  The !=0xFFFFFFFF condition might protect
> against this but this is not really clear (the memset to 0xFF is done in
> another function).
> 
> Since KVM_MAX_CPUID_ENTRIES is 100 and SNP_CPUID_FUNCTION_MAXCOUNT is
> 64, it seems possible that i will be 65 for example and then
> snp_cpuid_info->entries[i] is an out-of-bounds read access.

Indeed, and I don't think this is guarded against currently. I'll make sure
to add a check for this.

> 
> 
> 
> 
> > +
> > +        snp_cpuid_entry->eax_in = kvm_cpuid_entry->function;
> > +        if (kvm_cpuid_entry->flags == KVM_CPUID_FLAG_SIGNIFCANT_INDEX) {
> > +            snp_cpuid_entry->ecx_in = kvm_cpuid_entry->index;
> > +        }
> > +        snp_cpuid_entry->eax = kvm_cpuid_entry->eax;
> > +        snp_cpuid_entry->ebx = kvm_cpuid_entry->ebx;
> > +        snp_cpuid_entry->ecx = kvm_cpuid_entry->ecx;
> > +        snp_cpuid_entry->edx = kvm_cpuid_entry->edx;
> > +
> > +        if (snp_cpuid_entry->eax_in == 0xD &&
> > +            (snp_cpuid_entry->ecx_in == 0x0 || snp_cpuid_entry->ecx_in == 0x1)) {
> > +            snp_cpuid_entry->ebx = 0x240;
> > +        }
> 
> Can you please add a comment explaining this special case?

Will do, meant to add one previously.

> 
> 
> 
> 
> > +    }
> > +
> > +    if (i > SNP_CPUID_FUNCTION_MAXCOUNT) {
> 
> This can be checked at the top (before the for loop): compare
> kvm_cpuid_info.cpuid.nent with SNP_CPUID_FUNCTION_MAXCOUNT.

Was possible previously because cpuid.nent doesn't reflect the actual
entry count, but with the proposed change above I think I should be able
to handle this as suggested.

> 
> > +        error_report("SEV-SNP: CPUID count '%lu' exceeds max '%u'",
> > +                     i, SNP_CPUID_FUNCTION_MAXCOUNT);
> > +        return -1;
> > +    }
> > +
> > +    snp_cpuid_info->count = i;
> > +
> > +    return 0;
> > +}
> > +
> > +static void
> > +sev_snp_cpuid_report_mismatches(SnpCpuidInfo *old,
> > +                                SnpCpuidInfo *new)
> > +{
> > +    size_t i;
> > +
> 
> Add check that new->count == old->count.

Ah, of course.

> 
> 
> > +    for (i = 0; i < old->count; i++) {
> > +        SnpCpuidFunc *old_func, *new_func;
> > +
> > +        old_func = &old->entries[i];
> > +        new_func = &new->entries[i];
> > +
> > +        if (memcmp(old_func, new_func, sizeof(SnpCpuidFunc))) {
> 
> Maybe clearer:
> 
>     if (*old_func != *new_func) ...

Not 2 structs can be compared this way, maybe I'll just compare the
individual members.

> 
> 
> > +            error_report("SEV-SNP: CPUID validation failed for function %x, index: %x.\n"
> 
> Add "0x" prefixes before printing hex values (%x), otherwise we might
> have confusing outputs such as "failed for function 13, index: 25" which
> is unclear whether it's decimal or hex.

Of course, will fix.

> 
> 
> > +                         "provided: eax:0x%08x, ebx: 0x%08x, ecx: 0x%08x, edx: 0x%08x\n"
> > +                         "expected: eax:0x%08x, ebx: 0x%08x, ecx: 0x%08x, edx: 0x%08x",
> > +                         old_func->eax_in, old_func->ecx_in,
> > +                         old_func->eax, old_func->ebx, old_func->ecx, old_func->edx,
> > +                         new_func->eax, new_func->ebx, new_func->ecx, new_func->edx);
> > +        }
> > +    }
> > +}
> > +
> > +static int
> > +sev_snp_launch_update_cpuid(uint32_t cpuid_addr, uint32_t cpuid_len)
> > +{
> > +    KvmCpuidInfo kvm_cpuid_info;
> > +    SnpCpuidInfo snp_cpuid_info;
> > +    CPUState *cs = first_cpu;
> > +    MemoryRegion *mr = NULL;
> > +    void *snp_cpuid_hva;
> > +    int ret;
> > +
> > +    snp_cpuid_hva = gpa2hva(&mr, cpuid_addr, cpuid_len, NULL);
> > +    if (!snp_cpuid_hva) {
> > +        error_report("SEV-SNP: unable to access CPUID memory range at GPA %d",
> > +                     cpuid_addr);
> > +        return 1;
> > +    }
> 
> I think that moving this section just before the memcpy(snp_cpuid_hva,
> ...) below would make the flow of this function clearer to the reader
> (no functional difference, I believe).

I think I put this here to avoid the extra KVM ioctls if this case is
hit, but it shouldn't hurt to move it.

> 
> 
> > +
> > +    /* get the cpuid list from KVM */
> > +    memset(&kvm_cpuid_info.entries, 0xFF,
> > +           KVM_MAX_CPUID_ENTRIES * sizeof(struct kvm_cpuid_entry2));
> 
> The third argument can be:  sizeof(kvm_cpuid_info.entries)

Much nicer.

> 
> 
> > +    kvm_cpuid_info.cpuid.nent = KVM_MAX_CPUID_ENTRIES;
> > +
> > +    ret = kvm_vcpu_ioctl(cs, KVM_GET_CPUID2, &kvm_cpuid_info);
> > +    if (ret) {
> > +        error_report("SEV-SNP: unable to query CPUID values for CPU: '%s'",
> > +                     strerror(-ret));
> 
> Missing return 1 or exit(1) here?

Yes indeed, will get this fixed up.

> 
> 
> -Dov
> 
> > +    }
> > +
> > +    ret = sev_snp_cpuid_info_fill(&snp_cpuid_info, &kvm_cpuid_info);
> > +    if (ret) {
> > +        error_report("SEV-SNP: failed to generate CPUID table information");
> > +        exit(1);
> > +    }
> > +
> > +    memcpy(snp_cpuid_hva, &snp_cpuid_info, sizeof(snp_cpuid_info));
> 
> Before memcpy, maybe add sanity test (assert?) that
> sizeof(snp_cpuid_info) <= cpuid_len .

Makes sense.

Thanks!

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

* Re: [RFC PATCH v2 11/12] i386/sev: sev-snp: add support for CPUID validation
  2021-09-07 16:50     ` Michael Roth
@ 2021-09-07 17:44         ` Dov Murik
  0 siblings, 0 replies; 61+ messages in thread
From: Dov Murik @ 2021-09-07 17:44 UTC (permalink / raw)
  To: Michael Roth
  Cc: qemu-devel, Connor Kuehl, Philippe Mathieu-Daudé,
	Michael S . Tsirkin, James Bottomley, Dr . David Alan Gilbert,
	Tom Lendacky, Paolo Bonzini, David Gibson,
	Daniel P. Berrangé,
	kvm, Eduardo Habkost, Brijesh Singh, Markus Armbruster,
	Eric Blake, Dov Murik



On 07/09/2021 19:50, Michael Roth wrote:
> On Sun, Sep 05, 2021 at 01:02:12PM +0300, Dov Murik wrote:
>> On 27/08/2021 1:26, Michael Roth wrote:

[...]

>>> +    for (i = 0; i < old->count; i++) {
>>> +        SnpCpuidFunc *old_func, *new_func;
>>> +
>>> +        old_func = &old->entries[i];
>>> +        new_func = &new->entries[i];
>>> +
>>> +        if (memcmp(old_func, new_func, sizeof(SnpCpuidFunc))) {
>>
>> Maybe clearer:
>>
>>     if (*old_func != *new_func) ...
> 
> Not 2 structs can be compared this way, maybe I'll just compare the
> individual members.
> 

Oops, my bad; I was confusing it with struct assignment.  I guess memcmp
is fine as-is in this case.


-Dov


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

* Re: [RFC PATCH v2 11/12] i386/sev: sev-snp: add support for CPUID validation
@ 2021-09-07 17:44         ` Dov Murik
  0 siblings, 0 replies; 61+ messages in thread
From: Dov Murik @ 2021-09-07 17:44 UTC (permalink / raw)
  To: Michael Roth
  Cc: Tom Lendacky, Daniel P. Berrangé,
	Eduardo Habkost, kvm, Michael S . Tsirkin, Connor Kuehl,
	Markus Armbruster, James Bottomley, qemu-devel, Eric Blake,
	Dr . David Alan Gilbert, Dov Murik, Brijesh Singh, Paolo Bonzini,
	Philippe Mathieu-Daudé,
	David Gibson



On 07/09/2021 19:50, Michael Roth wrote:
> On Sun, Sep 05, 2021 at 01:02:12PM +0300, Dov Murik wrote:
>> On 27/08/2021 1:26, Michael Roth wrote:

[...]

>>> +    for (i = 0; i < old->count; i++) {
>>> +        SnpCpuidFunc *old_func, *new_func;
>>> +
>>> +        old_func = &old->entries[i];
>>> +        new_func = &new->entries[i];
>>> +
>>> +        if (memcmp(old_func, new_func, sizeof(SnpCpuidFunc))) {
>>
>> Maybe clearer:
>>
>>     if (*old_func != *new_func) ...
> 
> Not 2 structs can be compared this way, maybe I'll just compare the
> individual members.
> 

Oops, my bad; I was confusing it with struct assignment.  I guess memcmp
is fine as-is in this case.


-Dov



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

* Re: [RFC PATCH v2 00/12] Add AMD Secure Nested Paging (SEV-SNP) support
  2021-08-26 22:26 [RFC PATCH v2 00/12] Add AMD Secure Nested Paging (SEV-SNP) support Michael Roth
@ 2021-11-16  9:23   ` Daniel P. Berrangé
  2021-08-26 22:26 ` [RFC PATCH v2 02/12] linux-header: add the SNP specific command Michael Roth
                     ` (11 subsequent siblings)
  12 siblings, 0 replies; 61+ messages in thread
From: Daniel P. Berrangé @ 2021-11-16  9:23 UTC (permalink / raw)
  To: Michael Roth
  Cc: qemu-devel, Philippe Mathieu-Daudé,
	Michael S . Tsirkin, James Bottomley, Dr . David Alan Gilbert,
	Tom Lendacky, Paolo Bonzini, Dov Murik, David Gibson, kvm,
	Eduardo Habkost, Brijesh Singh, Markus Armbruster, Eric Blake

On Thu, Aug 26, 2021 at 05:26:15PM -0500, Michael Roth wrote:
> These patches implement SEV-SNP along with CPUID enforcement support for QEMU,
> and are also available at:
> 
>   https://github.com/mdroth/qemu/commits/snp-rfc-v2-upstream
> 
> They are based on the initial RFC submitted by Brijesh:
> 
>   https://lore.kernel.org/qemu-devel/20210722000259.ykepl7t6ptua7im5@amd.com/T/

What's the status of these patches ?  Is there going to be any non-RFC
version posted in the near future ?

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] 61+ messages in thread

* Re: [RFC PATCH v2 00/12] Add AMD Secure Nested Paging (SEV-SNP) support
@ 2021-11-16  9:23   ` Daniel P. Berrangé
  0 siblings, 0 replies; 61+ messages in thread
From: Daniel P. Berrangé @ 2021-11-16  9:23 UTC (permalink / raw)
  To: Michael Roth
  Cc: Tom Lendacky, Brijesh Singh, Eduardo Habkost, kvm,
	Michael S . Tsirkin, Markus Armbruster, James Bottomley,
	qemu-devel, Eric Blake, Dr . David Alan Gilbert, Dov Murik,
	Paolo Bonzini, Philippe Mathieu-Daudé,
	David Gibson

On Thu, Aug 26, 2021 at 05:26:15PM -0500, Michael Roth wrote:
> These patches implement SEV-SNP along with CPUID enforcement support for QEMU,
> and are also available at:
> 
>   https://github.com/mdroth/qemu/commits/snp-rfc-v2-upstream
> 
> They are based on the initial RFC submitted by Brijesh:
> 
>   https://lore.kernel.org/qemu-devel/20210722000259.ykepl7t6ptua7im5@amd.com/T/

What's the status of these patches ?  Is there going to be any non-RFC
version posted in the near future ?

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] 61+ messages in thread

* Re: [RFC PATCH v2 00/12] Add AMD Secure Nested Paging (SEV-SNP) support
  2021-11-16  9:23   ` Daniel P. Berrangé
  (?)
@ 2021-11-16 11:54   ` Brijesh Singh
  -1 siblings, 0 replies; 61+ messages in thread
From: Brijesh Singh @ 2021-11-16 11:54 UTC (permalink / raw)
  To: Daniel P. Berrangé, Michael Roth
  Cc: brijesh.singh, qemu-devel, Philippe Mathieu-Daudé,
	Michael S . Tsirkin, James Bottomley, Dr . David Alan Gilbert,
	Tom Lendacky, Paolo Bonzini, Dov Murik, David Gibson, kvm,
	Eduardo Habkost, Markus Armbruster, Eric Blake


On 11/16/21 3:23 AM, Daniel P. Berrangé wrote:
> On Thu, Aug 26, 2021 at 05:26:15PM -0500, Michael Roth wrote:
>> These patches implement SEV-SNP along with CPUID enforcement support for QEMU,
>> and are also available at:
>>
>>   https://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2Fmdroth%2Fqemu%2Fcommits%2Fsnp-rfc-v2-upstream&amp;data=04%7C01%7Cbrijesh.singh%40amd.com%7C3506c40b7121401945b108d9a8e2c8d0%7C3dd8961fe4884e608e11a82d994e183d%7C0%7C0%7C637726514264887241%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000&amp;sdata=HXdG4TmNY157Gz6qLXhAL8FufCTxe9VzSiTaQICGawo%3D&amp;reserved=0
>>
>> They are based on the initial RFC submitted by Brijesh:
>>
>>   https://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%2Flore.kernel.org%2Fqemu-devel%2F20210722000259.ykepl7t6ptua7im5%40amd.com%2FT%2F&amp;data=04%7C01%7Cbrijesh.singh%40amd.com%7C3506c40b7121401945b108d9a8e2c8d0%7C3dd8961fe4884e608e11a82d994e183d%7C0%7C0%7C637726514264887241%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000&amp;sdata=AhOI%2FoQFq4k%2B6uOqYQqos6FlxE4AD1FFYfIPPiSHioI%3D&amp;reserved=0
> What's the status of these patches ?  Is there going to be any non-RFC
> version posted in the near future ?


I am waiting for the KVM interface to be finalized before spinning the
qemu patch. With the recent discussion on KVM patch we may see some
change in the interfaces. I am hoping to post updated series after
posting the newer KVM series.

thanls


>
> Regards,
> Daniel

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

end of thread, other threads:[~2021-11-16 11:54 UTC | newest]

Thread overview: 61+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-08-26 22:26 [RFC PATCH v2 00/12] Add AMD Secure Nested Paging (SEV-SNP) support Michael Roth
2021-08-26 22:26 ` [RFC PATCH v2 01/12] i386/sev: introduce "sev-common" type to encapsulate common SEV state Michael Roth
2021-09-01 14:18   ` Markus Armbruster
2021-09-03 15:11     ` Michael Roth
2021-08-26 22:26 ` [RFC PATCH v2 02/12] linux-header: add the SNP specific command Michael Roth
2021-09-03 20:36   ` Dov Murik
2021-09-03 20:36     ` Dov Murik
2021-09-07 14:27     ` Michael Roth
2021-08-26 22:26 ` [RFC PATCH v2 03/12] i386/sev: introduce 'sev-snp-guest' object Michael Roth
2021-09-01 14:29   ` Markus Armbruster
2021-09-03 15:15     ` Michael Roth
2021-09-03 21:12   ` Dov Murik
2021-09-03 21:12     ` Dov Murik
2021-09-07 14:20     ` Michael Roth
2021-08-26 22:26 ` [RFC PATCH v2 04/12] i386/sev: initialize SNP context Michael Roth
2021-09-05  7:07   ` Dov Murik
2021-09-05  7:07     ` Dov Murik
2021-09-05 13:58     ` Brijesh Singh
2021-09-05 17:09       ` Dov Murik
2021-09-05 17:09         ` Dov Murik
2021-09-05  9:19   ` Dov Murik
2021-09-05  9:19     ` Dov Murik
2021-09-05 14:05     ` Brijesh Singh
2021-09-05 17:03       ` Dov Murik
2021-09-05 17:03         ` Dov Murik
2021-08-26 22:26 ` [RFC PATCH v2 05/12] i386/sev: add the SNP launch start context Michael Roth
2021-08-26 22:26 ` [RFC PATCH v2 06/12] i386/sev: add support to encrypt BIOS when SEV-SNP is enabled Michael Roth
2021-08-26 22:26 ` [RFC PATCH v2 07/12] i386/sev: populate secrets and cpuid page and finalize the SNP launch Michael Roth
2021-09-03 20:24   ` Dov Murik
2021-09-03 20:24     ` Dov Murik
2021-09-07 16:18     ` Michael Roth
2021-08-26 22:26 ` [RFC PATCH v2 08/12] target/i386: set SEV-SNP CPUID bit when SNP enabled Michael Roth
2021-08-26 22:26 ` [RFC PATCH v2 09/12] target/i386: allow versioned CPUs to specify new cache_info Michael Roth
2021-08-26 22:26 ` [RFC PATCH v2 10/12] target/i386: add new EPYC CPU versions with updated cache_info Michael Roth
2021-08-26 22:26 ` [RFC PATCH v2 11/12] i386/sev: sev-snp: add support for CPUID validation Michael Roth
2021-09-05 10:02   ` Dov Murik
2021-09-05 10:02     ` Dov Murik
2021-09-07 16:50     ` Michael Roth
2021-09-07 17:44       ` Dov Murik
2021-09-07 17:44         ` Dov Murik
2021-08-26 22:26 ` [RFC PATCH v2 12/12] i386/sev: update query-sev QAPI format to handle SEV-SNP Michael Roth
2021-09-01 14:14   ` Markus Armbruster
2021-09-03 15:13     ` Michael Roth
2021-09-03 15:30       ` Daniel P. Berrangé
2021-09-03 15:30         ` Daniel P. Berrangé
2021-09-03 15:43         ` Michael Roth
2021-09-03 15:43           ` Michael Roth via
2021-09-03 15:58           ` Daniel P. Berrangé
2021-09-03 15:58             ` Daniel P. Berrangé
2021-09-03 16:01     ` Daniel P. Berrangé
2021-09-03 16:01       ` Daniel P. Berrangé
2021-09-04  5:41       ` Markus Armbruster
2021-09-07 11:52       ` Dr. David Alan Gilbert
2021-09-07 11:52         ` Dr. David Alan Gilbert
2021-09-07 14:33         ` Michael Roth
2021-09-07 14:33           ` Michael Roth via
2021-09-03 15:27   ` Daniel P. Berrangé
2021-09-03 15:27     ` Daniel P. Berrangé
2021-11-16  9:23 ` [RFC PATCH v2 00/12] Add AMD Secure Nested Paging (SEV-SNP) support Daniel P. Berrangé
2021-11-16  9:23   ` Daniel P. Berrangé
2021-11-16 11:54   ` Brijesh Singh

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.