[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




 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.