|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [RFC PATCH 13/16] x86/coco: Introduce AMD-SEV support
From: Andrei Semenov <andrei.semenov@xxxxxxxxxx>
AMD-SEV is AMD implementation for confidential computing.
This patch introduces SEV initialization and HVM enablement logic.
Signed-off-by: Andrei Semenov <andrei.semenov@xxxxxxxxxx>
Signed-off-by: Teddy Astie <teddy.astie@xxxxxxxxxx>
---
Some possible improvement would be to slightly change the ASID allocation
logic under SEV :
With SEV support and usable :
- non-SEV guest : Use ASID > NumSevGuests if possible
- SEV guest : Use ASID in SEV range
Such as we don't waste SEV-supported ASIDs.
This currently lacks DF_FLUSH support, so SEV-enabled destroyed cannot
reuse their ASIDs. This is currently workaround with "coco: Leak ASID for coco
guests".
---
xen/arch/x86/Makefile | 1 +
xen/arch/x86/coco/Makefile | 1 +
xen/arch/x86/coco/sev.c | 262 +++++++++++++++++++++++++
xen/arch/x86/cpu/amd.c | 10 +
xen/arch/x86/cpuid.c | 5 +
xen/arch/x86/hvm/Kconfig | 10 +
xen/arch/x86/hvm/svm/svm.c | 6 +
xen/arch/x86/hvm/svm/vmcb.c | 17 +-
xen/arch/x86/include/asm/coco.h | 8 +
xen/arch/x86/include/asm/hvm/svm/sev.h | 14 ++
xen/arch/x86/include/asm/hvm/svm/svm.h | 16 ++
11 files changed, 344 insertions(+), 6 deletions(-)
create mode 100644 xen/arch/x86/coco/Makefile
create mode 100644 xen/arch/x86/coco/sev.c
create mode 100644 xen/arch/x86/include/asm/coco.h
create mode 100644 xen/arch/x86/include/asm/hvm/svm/sev.h
diff --git a/xen/arch/x86/Makefile b/xen/arch/x86/Makefile
index bedb97cbee..220bff5e0a 100644
--- a/xen/arch/x86/Makefile
+++ b/xen/arch/x86/Makefile
@@ -1,5 +1,6 @@
obj-y += acpi/
obj-y += boot/
+obj-$(CONFIG_COCO) += coco/
obj-y += cpu/
obj-y += efi/
obj-y += genapic/
diff --git a/xen/arch/x86/coco/Makefile b/xen/arch/x86/coco/Makefile
new file mode 100644
index 0000000000..59ab1c075f
--- /dev/null
+++ b/xen/arch/x86/coco/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_COCO_AMD_SEV) += sev.o
\ No newline at end of file
diff --git a/xen/arch/x86/coco/sev.c b/xen/arch/x86/coco/sev.c
new file mode 100644
index 0000000000..366ce42baa
--- /dev/null
+++ b/xen/arch/x86/coco/sev.c
@@ -0,0 +1,262 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * coco/sev.c: AMD SEV support
+ * Copyright (c) Vates SAS
+ */
+
+#include <asm/cpu-policy.h>
+#include <asm/cpufeature.h>
+#include <asm/p2m.h>
+#include <asm/hvm/asid.h>
+
+#include <public/hvm/coco.h>
+
+#include <xen/config.h>
+#include <xen/coco.h>
+#include <asm/psp-sev.h>
+
+static int sev_domain_initialise(struct domain *d)
+{
+ struct sev_data_launch_start sd_ls;
+ struct sev_data_activate sd_a;
+ int psp_ret;
+ long rc = 0;
+
+ sd_ls.handle = 0; /* generate new one */
+ sd_ls.policy = 0; /* NOKS policy */
+ sd_ls.dh_cert_address = 0; /* do not DH stuff */
+
+ rc = sev_do_cmd(SEV_CMD_LAUNCH_START, (void *)(&sd_ls), &psp_ret, true);
+ if ( rc )
+ {
+ printk(XENLOG_ERR "asp: failed to LAUNCH_START domain(%d): psp_ret
%d\n",
+ d->domain_id, psp_ret);
+ return rc;
+ }
+
+ sd_a.handle = sd_ls.handle;
+ sd_a.asid = d->arch.hvm.asid.asid;
+
+ rc = sev_do_cmd(SEV_CMD_ACTIVATE, (void *)(&sd_a), &psp_ret, true);
+ if ( rc )
+ {
+ printk(XENLOG_ERR "asp: failed to ACTIVATE domain(%d): psp_ret %d\n",
+ d->domain_id, psp_ret);
+ return rc;
+ }
+
+ d->arch.hvm.svm.sev.asp_handle = sd_ls.handle;
+ d->arch.hvm.svm.sev.asp_policy = 0;
+
+ return 0;
+}
+
+static int sev_domain_prepare_initial_mem(struct domain *d, gfn_t gfn, size_t
count)
+{
+ struct page_info *page;
+ int rc, psp_ret;
+ struct sev_data_launch_update_data sd_lud;
+
+ mfn_t mfn, mfn_base = INVALID_MFN;
+ size_t segment_size = 0;
+
+ do {
+ page = get_page_from_gfn(d, gfn_x(gfn), NULL, P2M_ALLOC);
+ if ( unlikely(!page) )
+ return rc;
+
+ mfn = page_to_mfn(page);
+ put_page(page);
+
+ if ( !mfn_valid(mfn_base) )
+ mfn_base = mfn;
+ else
+ {
+ // Check for a break.
+ if (mfn_x(mfn_base) + segment_size != mfn_x(mfn) || segment_size
== 512)
+ {
+ // Make launch update data.
+ printk(XENLOG_DEBUG
+ "asp: LAUNCH_UPDATE_DATA d%d: base=%"PRI_xen_pfn",
size=%zx\n",
+ d->domain_id, mfn_x(mfn_base), segment_size);
+
+ sd_lud.reserved = 0;
+ sd_lud.handle = d->arch.hvm.svm.sev.asp_handle;
+ sd_lud.address = mfn_x(mfn_base) << PAGE_SHIFT;
+ sd_lud.len = segment_size * PAGE_SIZE;
+ rc = sev_do_cmd(SEV_CMD_LAUNCH_UPDATE_DATA, (void *)(&sd_lud),
+ &psp_ret, true);
+ if (rc)
+ {
+ printk(XENLOG_ERR
+ "asp: failed to LAUNCH_UPDATE_DATA dom(%d): err
%d\n",
+ d->domain_id, psp_ret);
+ return rc;
+ }
+
+ mfn_base = mfn_x(mfn);
+ segment_size = 0;
+ }
+ }
+
+ gfn = gfn_add(gfn, 1);
+ segment_size++;
+ count--;
+ } while ( count );
+
+ // Last launch update data.
+ if ( segment_size )
+ {
+ sd_lud.reserved = 0;
+ sd_lud.handle = d->arch.hvm.svm.sev.asp_handle;
+ sd_lud.address = mfn_x(mfn_base) << PAGE_SHIFT;
+ sd_lud.len = segment_size * PAGE_SIZE;
+ rc = sev_do_cmd(SEV_CMD_LAUNCH_UPDATE_DATA, (void *)(&sd_lud),
+ &psp_ret, true);
+
+ if ( rc )
+ printk(XENLOG_ERR "asp: failed to LAUNCH_UPDATE_DATA dom(%d): err
%d\n",
+ d->domain_id, psp_ret);
+ }
+
+ return rc;
+}
+
+static int sev_domain_creation_finished(struct domain *d)
+{
+ struct sev_data_launch_measure sd_lm;
+ struct sev_data_launch_finish sd_lf;
+ int psp_ret;
+ long rc = 0;
+
+ sd_lm.handle = d->arch.hvm.svm.sev.asp_handle;
+ sd_lm.address = virt_to_maddr(d->arch.hvm.svm.sev.measure);
+ sd_lm.len = sizeof(d->arch.hvm.svm.sev.measure);
+ sd_lm.reserved = 0;
+
+ rc = sev_do_cmd(SEV_CMD_LAUNCH_MEASURE, (void *)(&sd_lm), &psp_ret, true);
+ if ( rc )
+ {
+ printk(XENLOG_ERR "asp: failed to LAUNCH_MEASURE for d%hu: psp_ret
%hu, rc %ld\n",
+ d->domain_id, psp_ret, rc);
+
+ if (psp_ret == SEV_RET_INVALID_LEN)
+ printk(XENLOG_ERR "asp: Expected %"PRIu32" bytes\n", sd_lm.len);
+ return rc;
+ }
+
+ sd_lf.handle = d->arch.hvm.svm.sev.asp_handle;
+
+ rc = sev_do_cmd(SEV_CMD_LAUNCH_FINISH, (void *)(&sd_lf), &psp_ret, true);
+ if ( rc )
+ {
+ printk(XENLOG_ERR "asp: failed to LAUNCH_FINISH for d%hu: psp_ret %d,
rc %ld\n",
+ d->domain_id, psp_ret, rc);
+ return rc;
+ }
+
+ d->arch.hvm.svm.sev.measure_len = sd_lm.len;
+ return 0;
+}
+
+static void sev_domain_destroy(struct domain *d)
+{
+ struct sev_data_deactivate sd_da;
+ struct sev_data_decommission sd_de;
+ int psp_ret;
+ long rc = 0;
+
+ sd_da.handle = d->arch.hvm.svm.sev.asp_handle;
+
+ rc = sev_do_cmd(SEV_CMD_DEACTIVATE, (void *)(&sd_da), &psp_ret, true);
+ if (rc)
+ {
+ printk(XENLOG_ERR "asp: failed to DEACTIVATE for d%hu: psp_ret %d\n",
+ d->domain_id, psp_ret);
+ return;
+ }
+
+ sd_de.handle = d->arch.hvm.svm.sev.asp_handle;
+
+ rc = sev_do_cmd(SEV_CMD_DECOMMISSION, (void *)(&sd_de), &psp_ret, true);
+ if (rc)
+ {
+ printk(XENLOG_ERR "asp: failed to DECOMMISSION for d%hu: psp_ret %d\n",
+ d->domain_id, psp_ret);
+ return;
+ }
+
+ d->arch.hvm.svm.sev.asp_handle = 0;
+}
+
+static int sev_asid_alloc(struct domain *d, struct hvm_asid *asid)
+{
+ /* TODO: SEV-ES/SNP */
+ unsigned long asid_min = raw_cpu_policy.extd.min_no_es_asid;
+ unsigned long asid_max = raw_cpu_policy.extd.max_sev_guests;
+
+ return hvm_asid_alloc_range(asid, asid_min, asid_max);
+}
+
+static struct coco_domain_ops sev_domain_ops = {
+ .prepare_initial_mem = sev_domain_prepare_initial_mem,
+ .domain_initialise = sev_domain_initialise,
+ .domain_creation_finished = sev_domain_creation_finished,
+ .domain_destroy = sev_domain_destroy,
+ .asid_alloc = sev_asid_alloc,
+};
+
+static int sev_init(void)
+{
+ unsigned long syscfg;
+
+ if ( WARN_ON(!cpu_has_sev) )
+ return -ENOSYS;
+
+ ASSERT(raw_cpu_policy.extd.c_bit_pos > 0);
+ ASSERT(raw_cpu_policy.extd.max_sev_guests > 0);
+
+ printk(XENLOG_INFO "sev: C-bit is %"PRIu32"\n",
raw_cpu_policy.extd.c_bit_pos);
+ printk(XENLOG_INFO "sev: Supports up to %"PRIu32" guests\n",
+ raw_cpu_policy.extd.max_sev_guests);
+
+ /* Enable AMD SME */
+ rdmsrl(MSR_K8_SYSCFG, syscfg);
+
+ if ( !(syscfg & SYSCFG_MEM_ENCRYPT) )
+ {
+ syscfg |= SYSCFG_MEM_ENCRYPT;
+ wrmsrl(MSR_K8_SYSCFG, syscfg);
+
+ printk(XENLOG_INFO "sev: Enabled AMD SME\n");
+ }
+
+ return 0;
+}
+
+static int sev_get_platform_status(struct coco_platform_status *status)
+{
+ status->platform = COCO_PLATFORM_amd_sev;
+
+ // if ( cpu_has_sev_es )
+ // status->platform_flags |= COCO_PLATFORM_FLAG_sev_es;
+
+ status->flags = COCO_STATUS_FLAG_supported;
+
+ return 0;
+}
+
+static struct coco_domain_ops *sev_get_domain_ops(struct domain *d)
+{
+ // TODO: SEV-ES and SEV-SNP support
+ return &sev_domain_ops;
+}
+
+struct coco_ops sev_coco_ops = {
+ .name = "SEV",
+ .init = sev_init,
+ .get_platform_status = sev_get_platform_status,
+ .get_domain_ops = sev_get_domain_ops,
+};
+
+
diff --git a/xen/arch/x86/cpu/amd.c b/xen/arch/x86/cpu/amd.c
index 37d67dd15c..28b5a0420d 100644
--- a/xen/arch/x86/cpu/amd.c
+++ b/xen/arch/x86/cpu/amd.c
@@ -1,4 +1,5 @@
#include <xen/cpu.h>
+#include <asm/cpu-policy.h>
#include <xen/init.h>
#include <xen/bitops.h>
#include <xen/mm.h>
@@ -19,6 +20,10 @@
#include "cpu.h"
+#ifdef CONFIG_COCO
+#include <asm/coco.h>
+#endif
+
/*
* Pre-canned values for overriding the CPUID features
* and extended features masks.
@@ -1333,6 +1338,11 @@ static void cf_check init_amd(struct cpuinfo_x86 *c)
check_syscfg_dram_mod_en();
amd_log_freq(c);
+
+#ifdef CONFIG_COCO_AMD_SEV
+ if ( cpu_has_sev )
+ coco_register_ops(&sev_coco_ops);
+#endif
}
const struct cpu_dev __initconst_cf_clobber amd_cpu_dev = {
diff --git a/xen/arch/x86/cpuid.c b/xen/arch/x86/cpuid.c
index e2d94619c2..e1d6db4ad8 100644
--- a/xen/arch/x86/cpuid.c
+++ b/xen/arch/x86/cpuid.c
@@ -8,6 +8,7 @@
#include <asm/cpu-policy.h>
#include <asm/cpuid.h>
#include <asm/hvm/viridian.h>
+#include <asm/hvm/svm/sev.h>
#include <asm/xstate.h>
#define EMPTY_LEAF ((struct cpuid_leaf){})
@@ -250,6 +251,10 @@ void guest_cpuid(const struct vcpu *v, uint32_t leaf,
return;
*res = array_access_nospec(p->extd.raw, leaf & 0xffff);
+
+ /* For a SEV guest, passthrough the host SEV leaf. */
+ if ( is_sev_domain(d) && leaf == 0x8000001fU )
+ *res = raw_cpu_policy.extd.raw[0x1f];
break;
default:
diff --git a/xen/arch/x86/hvm/Kconfig b/xen/arch/x86/hvm/Kconfig
index 2def0f98e2..a9332ab8ce 100644
--- a/xen/arch/x86/hvm/Kconfig
+++ b/xen/arch/x86/hvm/Kconfig
@@ -25,6 +25,16 @@ config AMD_SVM
If your system includes a processor with AMD-V support, say Y.
If in doubt, say Y.
+config COCO_AMD_SEV
+ bool "AMD SEV (UNSUPPORTED)" if AMD && AMD_SVM && COCO && UNSUPPORTED
+ default y
+ select AMD_SP
+ help
+ Enables support for AMD Secure Encrypted Virtualization
technology.
+ This option is needed if you want to run confidential guests on
a
+ AMD platform that supports it.
+ If in doubt, say N.
+
config INTEL_VMX
bool "Intel VT-x" if INTEL && EXPERT
default y
diff --git a/xen/arch/x86/hvm/svm/svm.c b/xen/arch/x86/hvm/svm/svm.c
index cc19d80fe1..63889bf803 100644
--- a/xen/arch/x86/hvm/svm/svm.c
+++ b/xen/arch/x86/hvm/svm/svm.c
@@ -27,6 +27,7 @@
#include <asm/hvm/nestedhvm.h>
#include <asm/hvm/support.h>
#include <asm/hvm/asid.h>
+#include <asm/hvm/svm/sev.h>
#include <asm/hvm/svm/svm.h>
#include <asm/hvm/svm/svmdebug.h>
#include <asm/hvm/svm/vmcb.h>
@@ -1865,6 +1866,11 @@ static int cf_check svm_msr_read_intercept(
break;
case MSR_K8_SYSCFG:
+ if ( is_sev_domain(d) )
+ {
+ *msr_content = SYSCFG_MEM_ENCRYPT;
+ break;
+ }
case MSR_K8_TOP_MEM1:
case MSR_K8_TOP_MEM2:
case MSR_K8_VM_CR:
diff --git a/xen/arch/x86/hvm/svm/vmcb.c b/xen/arch/x86/hvm/svm/vmcb.c
index 4e1f61dbe0..5157afe733 100644
--- a/xen/arch/x86/hvm/svm/vmcb.c
+++ b/xen/arch/x86/hvm/svm/vmcb.c
@@ -15,6 +15,7 @@
#include <asm/hvm/svm/vmcb.h>
#include <asm/msr-index.h>
#include <asm/p2m.h>
+#include <asm/hvm/svm/sev.h>
#include <asm/hvm/svm/svm.h>
#include <asm/hvm/svm/svmdebug.h>
#include <asm/spec_ctrl.h>
@@ -192,15 +193,19 @@ int svm_create_vmcb(struct vcpu *v)
svm->vmcb = nv->nv_n1vmcx;
rc = construct_vmcb(v);
if ( rc != 0 )
- {
- free_vmcb(nv->nv_n1vmcx);
- nv->nv_n1vmcx = NULL;
- svm->vmcb = NULL;
- return rc;
- }
+ goto err;
+
+ if ( is_sev_domain(v->domain) )
+ vmcb_set_np_ctrl(svm->vmcb, vmcb_get_np_ctrl(svm->vmcb) |
NPCTRL_SEV_ENABLE);
svm->vmcb_pa = nv->nv_n1vmcx_pa = virt_to_maddr(svm->vmcb);
return 0;
+
+err:
+ free_vmcb(nv->nv_n1vmcx);
+ nv->nv_n1vmcx = NULL;
+ svm->vmcb = NULL;
+ return rc;
}
void svm_destroy_vmcb(struct vcpu *v)
diff --git a/xen/arch/x86/include/asm/coco.h b/xen/arch/x86/include/asm/coco.h
new file mode 100644
index 0000000000..874ef56327
--- /dev/null
+++ b/xen/arch/x86/include/asm/coco.h
@@ -0,0 +1,8 @@
+#ifndef __ARCH_X86_COCO_H
+#define __ARCH_X86_COCO_H
+
+#include <xen/coco.h>
+
+extern struct coco_ops sev_coco_ops;
+
+#endif /* __ARCH_X86_CACHE_H */
\ No newline at end of file
diff --git a/xen/arch/x86/include/asm/hvm/svm/sev.h
b/xen/arch/x86/include/asm/hvm/svm/sev.h
new file mode 100644
index 0000000000..b7b5ab5591
--- /dev/null
+++ b/xen/arch/x86/include/asm/hvm/svm/sev.h
@@ -0,0 +1,14 @@
+#ifndef __XEN_HVM_SEV_H__
+#define __XEN_HVM_SEV_H__
+
+#include <asm/nospec.h>
+#include <asm/cpufeature.h>
+
+#include <xen/sched.h>
+
+static always_inline bool is_sev_domain(const struct domain *d)
+{
+ return cpu_has_sev && evaluate_nospec(d->options & XEN_DOMCTL_CDF_coco);
+}
+
+#endif /* __XEN_HVM_SEV_H__ */
diff --git a/xen/arch/x86/include/asm/hvm/svm/svm.h
b/xen/arch/x86/include/asm/hvm/svm/svm.h
index 1254e5f3ee..efd54511aa 100644
--- a/xen/arch/x86/include/asm/hvm/svm/svm.h
+++ b/xen/arch/x86/include/asm/hvm/svm/svm.h
@@ -9,6 +9,8 @@
#ifndef __ASM_X86_HVM_SVM_H__
#define __ASM_X86_HVM_SVM_H__
+#include <xen/stdint.h>
+
void svm_asid_init(void);
void svm_vcpu_assign_asid(struct vcpu *v);
void svm_vcpu_set_tlb_control(struct vcpu *v);
@@ -26,6 +28,16 @@ bool svm_load_segs(unsigned int ldt_ents, unsigned long
ldt_base,
unsigned long fs_base, unsigned long gs_base,
unsigned long gs_shadow);
+struct sev_state {
+ uint32_t asp_handle;
+ uint32_t asp_policy;
+ uint8_t measure[96];
+ uint32_t measure_len; /* 96 bytes */
+ uint8_t state;
+
+ unsigned long flags;
+};
+
struct svm_domain {
/* OSVW MSRs */
union {
@@ -35,6 +47,10 @@ struct svm_domain {
uint64_t status;
};
} osvw;
+
+ #ifdef CONFIG_COCO_AMD_SEV
+ struct sev_state sev;
+ #endif
};
extern u32 svm_feature_flags;
--
2.49.0
Teddy Astie | Vates XCP-ng Developer
XCP-ng & Xen Orchestra - Vates solutions
web: https://vates.tech
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |