[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[PATCH v5 09/16] i386/sev: Implement ConfidentialGuestSupport functions
From: |
Roy Hopkins |
Subject: |
[PATCH v5 09/16] i386/sev: Implement ConfidentialGuestSupport functions for SEV |
Date: |
Tue, 13 Aug 2024 16:01:11 +0100 |
The ConfidentialGuestSupport object defines a number of virtual
functions that are called during processing of IGVM directives to query
or configure initial guest state. In order to support processing of IGVM
files, these functions need to be implemented by relevant isolation
hardware support code such as SEV.
This commit implements the required functions for SEV-ES and adds
support for processing IGVM files for configuring the guest.
Signed-off-by: Roy Hopkins <roy.hopkins@suse.com>
Acked-by: Michael S. Tsirkin <mst@redhat.com>
---
target/i386/sev.c | 254 ++++++++++++++++++++++++++++++++++++++++++++--
target/i386/sev.h | 2 +
2 files changed, 246 insertions(+), 10 deletions(-)
diff --git a/target/i386/sev.c b/target/i386/sev.c
index 65c0509210..6db76b0c94 100644
--- a/target/i386/sev.c
+++ b/target/i386/sev.c
@@ -39,8 +39,10 @@
#include "qapi/qapi-commands-misc-target.h"
#include "confidential-guest.h"
#include "hw/i386/pc.h"
+#include "hw/i386/e820_memory_layout.h"
#include "exec/address-spaces.h"
#include "qemu/queue.h"
+#include "qemu/cutils.h"
OBJECT_DECLARE_TYPE(SevCommonState, SevCommonStateClass, SEV_COMMON)
OBJECT_DECLARE_TYPE(SevGuestState, SevCommonStateClass, SEV_GUEST)
@@ -49,6 +51,9 @@ OBJECT_DECLARE_TYPE(SevSnpGuestState, SevCommonStateClass,
SEV_SNP_GUEST)
/* hard code sha256 digest size */
#define HASH_SIZE 32
+/* Hard coded GPA that KVM uses for the VMSA */
+#define KVM_VMSA_GPA 0xFFFFFFFFF000
+
/* Convert between SEV-ES VMSA and SegmentCache flags/attributes */
#define FLAGS_VMSA_TO_SEGCACHE(flags) \
((((flags) & 0xff00) << 12) | (((flags) & 0xff) << 8))
@@ -487,6 +492,103 @@ static void sev_apply_cpu_context(CPUState *cpu)
}
}
+static int check_vmsa_supported(hwaddr gpa, const struct sev_es_save_area
*vmsa,
+ Error **errp)
+{
+ struct sev_es_save_area vmsa_check;
+
+ /*
+ * KVM always populates the VMSA at a fixed GPA which cannot be modified
+ * from userspace. Specifying a different GPA will not prevent the guest
+ * from starting but will cause the launch measurement to be different
+ * from expected. Therefore check that the provided GPA matches the KVM
+ * hardcoded value.
+ */
+ if (gpa != KVM_VMSA_GPA) {
+ error_setg(errp,
+ "%s: The VMSA GPA must be %lX but is specified as %lX",
+ __func__, KVM_VMSA_GPA, gpa);
+ return -1;
+ }
+
+ /*
+ * Clear all supported fields so we can then check the entire structure
+ * is zero.
+ */
+ memcpy(&vmsa_check, vmsa, sizeof(struct sev_es_save_area));
+ memset(&vmsa_check.es, 0, sizeof(vmsa_check.es));
+ memset(&vmsa_check.cs, 0, sizeof(vmsa_check.cs));
+ memset(&vmsa_check.ss, 0, sizeof(vmsa_check.ss));
+ memset(&vmsa_check.ds, 0, sizeof(vmsa_check.ds));
+ memset(&vmsa_check.fs, 0, sizeof(vmsa_check.fs));
+ memset(&vmsa_check.gs, 0, sizeof(vmsa_check.gs));
+ memset(&vmsa_check.gdtr, 0, sizeof(vmsa_check.gdtr));
+ memset(&vmsa_check.idtr, 0, sizeof(vmsa_check.idtr));
+ memset(&vmsa_check.ldtr, 0, sizeof(vmsa_check.ldtr));
+ memset(&vmsa_check.tr, 0, sizeof(vmsa_check.tr));
+ vmsa_check.efer = 0;
+ vmsa_check.cr0 = 0;
+ vmsa_check.cr3 = 0;
+ vmsa_check.cr4 = 0;
+ vmsa_check.xcr0 = 0;
+ vmsa_check.dr6 = 0;
+ vmsa_check.dr7 = 0;
+ vmsa_check.rax = 0;
+ vmsa_check.rcx = 0;
+ vmsa_check.rdx = 0;
+ vmsa_check.rbx = 0;
+ vmsa_check.rsp = 0;
+ vmsa_check.rbp = 0;
+ vmsa_check.rsi = 0;
+ vmsa_check.rdi = 0;
+ vmsa_check.r8 = 0;
+ vmsa_check.r9 = 0;
+ vmsa_check.r10 = 0;
+ vmsa_check.r11 = 0;
+ vmsa_check.r12 = 0;
+ vmsa_check.r13 = 0;
+ vmsa_check.r14 = 0;
+ vmsa_check.r15 = 0;
+ vmsa_check.rip = 0;
+ vmsa_check.rflags = 0;
+
+ vmsa_check.g_pat = 0;
+ vmsa_check.xcr0 = 0;
+
+ vmsa_check.x87_fcw = 0;
+ vmsa_check.mxcsr = 0;
+
+ if (sev_snp_enabled()) {
+ if (vmsa_check.sev_features != SVM_SEV_FEAT_SNP_ACTIVE) {
+ error_setg(errp,
+ "%s: sev_features in the VMSA contains an unsupported "
+ "value. For SEV-SNP, sev_features must be set to %x.",
+ __func__, SVM_SEV_FEAT_SNP_ACTIVE);
+ return -1;
+ }
+ vmsa_check.sev_features = 0;
+ } else {
+ if (vmsa_check.sev_features != 0) {
+ error_setg(errp,
+ "%s: sev_features in the VMSA contains an unsupported "
+ "value. For SEV-ES and SEV, sev_features must be "
+ "set to 0.", __func__);
+ return -1;
+ }
+ }
+
+ if (!buffer_is_zero(&vmsa_check, sizeof(vmsa_check))) {
+ error_setg(errp,
+ "%s: The VMSA contains fields that are not "
+ "synchronized with KVM. Continuing would result in "
+ "either unpredictable guest behavior, or a "
+ "mismatched launch measurement.",
+ __func__);
+ return -1;
+ }
+ return 0;
+}
+
static int sev_set_cpu_context(uint16_t cpu_index, const void *ctx,
uint32_t ctx_len, hwaddr gpa, Error **errp)
{
@@ -1498,18 +1600,26 @@ sev_snp_launch_finish(SevCommonState *sev_common)
struct kvm_sev_snp_launch_finish *finish = &sev_snp->kvm_finish_conf;
/*
- * To boot the SNP guest, the hypervisor is required to populate the CPUID
- * and Secrets page before finalizing the launch flow. The location of
- * the secrets and CPUID page is available through the OVMF metadata GUID.
+ * Populate all the metadata pages if not using an IGVM file. In the case
+ * where an IGVM file is provided it will be used to configure the metadata
+ * pages directly.
*/
- metadata = pc_system_get_ovmf_sev_metadata_ptr();
- if (metadata == NULL) {
- error_report("%s: Failed to locate SEV metadata header", __func__);
- exit(1);
- }
+ if (!X86_MACHINE(qdev_get_machine())->igvm) {
+ /*
+ * To boot the SNP guest, the hypervisor is required to populate the
+ * CPUID and Secrets page before finalizing the launch flow. The
+ * location of the secrets and CPUID page is available through the
+ * OVMF metadata GUID.
+ */
+ metadata = pc_system_get_ovmf_sev_metadata_ptr();
+ if (metadata == NULL) {
+ error_report("%s: Failed to locate SEV metadata header", __func__);
+ exit(1);
+ }
- /* Populate all the metadata pages */
- snp_populate_metadata_pages(sev_snp, metadata);
+ /* Populate all the metadata pages */
+ snp_populate_metadata_pages(sev_snp, metadata);
+ }
QTAILQ_FOREACH(data, &launch_update, next) {
ret = sev_snp_launch_update(sev_snp, data);
@@ -2298,6 +2408,124 @@ static void sev_common_set_kernel_hashes(Object *obj,
bool value, Error **errp)
SEV_COMMON(obj)->kernel_hashes = value;
}
+static int cgs_check_support(ConfidentialGuestPlatformType platform,
+ uint16_t platform_version, uint8_t highest_vtl,
+ uint64_t shared_gpa_boundary)
+{
+ return (((platform == CGS_PLATFORM_SEV_SNP) && sev_snp_enabled()) ||
+ ((platform == CGS_PLATFORM_SEV_ES) && sev_es_enabled()) ||
+ ((platform == CGS_PLATFORM_SEV) && sev_enabled())) ? 1 : 0;
+}
+
+static int cgs_set_guest_state(hwaddr gpa, uint8_t *ptr, uint64_t len,
+ ConfidentialGuestPageType memory_type,
+ uint16_t cpu_index, Error **errp)
+{
+ SevCommonState *sev_common = SEV_COMMON(MACHINE(qdev_get_machine())->cgs);
+ SevCommonStateClass *klass = SEV_COMMON_GET_CLASS(sev_common);
+
+ if (!sev_enabled()) {
+ error_setg(errp, "%s: attempt to configure guest memory, but SEV "
+ "is not enabled", __func__);
+ return -1;
+ }
+
+ switch (memory_type) {
+ case CGS_PAGE_TYPE_NORMAL:
+ case CGS_PAGE_TYPE_ZERO:
+ return klass->launch_update_data(sev_common, gpa, ptr, len, errp);
+
+ case CGS_PAGE_TYPE_VMSA:
+ if (!sev_es_enabled()) {
+ error_setg(errp,
+ "%s: attempt to configure initial VMSA, but SEV-ES "
+ "is not supported",
+ __func__);
+ return -1;
+ }
+ if (check_vmsa_supported(gpa, (const struct sev_es_save_area *)ptr,
+ errp) < 0) {
+ return -1;
+ }
+ return sev_set_cpu_context(cpu_index, ptr, len, gpa, errp);
+
+ case CGS_PAGE_TYPE_UNMEASURED:
+ if (sev_snp_enabled()) {
+ return snp_launch_update_data(
+ gpa, ptr, len, KVM_SEV_SNP_PAGE_TYPE_UNMEASURED, errp);
+ }
+ /* No action required if not SEV-SNP */
+ return 0;
+
+ case CGS_PAGE_TYPE_SECRETS:
+ if (!sev_snp_enabled()) {
+ error_setg(errp,
+ "%s: attempt to configure secrets page, but SEV-SNP "
+ "is not supported",
+ __func__);
+ return -1;
+ }
+ return snp_launch_update_data(gpa, ptr, len,
+ KVM_SEV_SNP_PAGE_TYPE_SECRETS, errp);
+
+ case CGS_PAGE_TYPE_REQUIRED_MEMORY:
+ if (kvm_convert_memory(gpa, len, true) < 0) {
+ error_setg(
+ errp,
+ "%s: failed to configure required memory. gpa: %lX, type: %d",
+ __func__, gpa, memory_type);
+ return -1;
+ }
+ return 0;
+
+ case CGS_PAGE_TYPE_CPUID:
+ if (!sev_snp_enabled()) {
+ error_setg(errp,
+ "%s: attempt to configure CPUID page, but SEV-SNP "
+ "is not supported",
+ __func__);
+ return -1;
+ }
+ return snp_launch_update_cpuid(gpa, ptr, len, errp);
+ }
+ error_setg(errp, "%s: failed to update guest. gpa: %lX, type: %d",
__func__,
+ gpa, memory_type);
+ return -1;
+}
+
+static int cgs_get_mem_map_entry(int index,
+ ConfidentialGuestMemoryMapEntry *entry,
+ Error **errp)
+{
+ struct e820_entry *table;
+ int num_entries;
+
+ num_entries = e820_get_table(&table);
+ if ((index < 0) || (index >= num_entries)) {
+ return 1;
+ }
+ entry->gpa = table[index].address;
+ entry->size = table[index].length;
+ switch (table[index].type) {
+ case E820_RAM:
+ entry->type = CGS_MEM_RAM;
+ break;
+ case E820_RESERVED:
+ entry->type = CGS_MEM_RESERVED;
+ break;
+ case E820_ACPI:
+ entry->type = CGS_MEM_ACPI;
+ break;
+ case E820_NVS:
+ entry->type = CGS_MEM_NVS;
+ break;
+ case E820_UNUSABLE:
+ entry->type = CGS_MEM_UNUSABLE;
+ break;
+ }
+ return 0;
+}
+
static void
sev_common_class_init(ObjectClass *oc, void *data)
{
@@ -2321,6 +2549,8 @@ static void
sev_common_instance_init(Object *obj)
{
SevCommonState *sev_common = SEV_COMMON(obj);
+ ConfidentialGuestSupportClass *cgs =
+ CONFIDENTIAL_GUEST_SUPPORT_GET_CLASS(obj);
sev_common->kvm_type = -1;
@@ -2331,6 +2561,10 @@ sev_common_instance_init(Object *obj)
object_property_add_uint32_ptr(obj, "reduced-phys-bits",
&sev_common->reduced_phys_bits,
OBJ_PROP_FLAG_READWRITE);
+ cgs->check_support = cgs_check_support;
+ cgs->set_guest_state = cgs_set_guest_state;
+ cgs->get_mem_map_entry = cgs_get_mem_map_entry;
+
QTAILQ_INIT(&sev_common->launch_vmsa);
}
diff --git a/target/i386/sev.h b/target/i386/sev.h
index 167dd154d6..2ccd6fe1e8 100644
--- a/target/i386/sev.h
+++ b/target/i386/sev.h
@@ -34,6 +34,8 @@
#define SEV_SNP_POLICY_SMT 0x10000
#define SEV_SNP_POLICY_DBG 0x80000
+#define SVM_SEV_FEAT_SNP_ACTIVE 1
+
typedef struct SevKernelLoaderContext {
char *setup_data;
size_t setup_size;
--
2.43.0
- [PATCH v5 01/16] meson: Add optional dependency on IGVM library, (continued)
- [PATCH v5 01/16] meson: Add optional dependency on IGVM library, Roy Hopkins, 2024/08/13
- [PATCH v5 05/16] i386/pc_sysfw: Ensure sysfw flash configuration does not conflict with IGVM, Roy Hopkins, 2024/08/13
- [PATCH v5 06/16] sev: Update launch_update_data functions to use Error handling, Roy Hopkins, 2024/08/13
- [PATCH v5 03/16] backends/igvm: Add IGVM loader and configuration, Roy Hopkins, 2024/08/13
- [PATCH v5 07/16] target/i386: Allow setting of R_LDTR and R_TR with cpu_x86_load_seg_cache(), Roy Hopkins, 2024/08/13
- [PATCH v5 02/16] backends/confidential-guest-support: Add functions to support IGVM, Roy Hopkins, 2024/08/13
- [PATCH v5 04/16] hw/i386: Add igvm-cfg object and processing for IGVM files, Roy Hopkins, 2024/08/13
- [PATCH v5 08/16] i386/sev: Refactor setting of reset vector and initial CPU state, Roy Hopkins, 2024/08/13
- [PATCH v5 09/16] i386/sev: Implement ConfidentialGuestSupport functions for SEV,
Roy Hopkins <=
- [PATCH v5 13/16] backends/igvm: Process initialization sections in IGVM file, Roy Hopkins, 2024/08/13
- [PATCH v5 14/16] backends/igvm: Handle policy for SEV guests, Roy Hopkins, 2024/08/13
- [PATCH v5 11/16] docs/interop/firmware.json: Add igvm to FirmwareDevice, Roy Hopkins, 2024/08/13
- [PATCH v5 15/16] i386/sev: Add implementation of CGS set_guest_policy(), Roy Hopkins, 2024/08/13
- [PATCH v5 12/16] backends/confidential-guest-support: Add set_guest_policy() function, Roy Hopkins, 2024/08/13
- [PATCH v5 16/16] sev: Provide sev_features flags from IGVM VMSA to KVM_SEV_INIT2, Roy Hopkins, 2024/08/13
- [PATCH v5 10/16] docs/system: Add documentation on support for IGVM, Roy Hopkins, 2024/08/13