--- v5
+++ v4
@@ -1,205 +1,266 @@
-The KVM_SNP_INIT command is used by the hypervisor to initialize the
-SEV-SNP platform context. In a typical workflow, this command should be the
-first command issued. When creating SEV-SNP guest, the VMM must use this
-command instead of the KVM_SEV_INIT or KVM_SEV_ES_INIT.
-
-The flags value must be zero, it will be extended in future SNP support to
-communicate the optional features (such as restricted INT injection etc).
-
-Co-developed-by: Pavan Kumar Paluri <papaluri@amd.com>
-Signed-off-by: Pavan Kumar Paluri <papaluri@amd.com>
+KVM_SEV_SNP_LAUNCH_START begins the launch process for an SEV-SNP guest.
+The command initializes a cryptographic digest context used to construct
+the measurement of the guest. If the guest is expected to be migrated,
+the command also binds a migration agent (MA) to the guest.
+
+For more information see the SEV-SNP specification.
+
Signed-off-by: Brijesh Singh <brijesh.singh@amd.com>
---
- .../virt/kvm/amd-memory-encryption.rst | 27 ++++++++++++
- arch/x86/include/asm/svm.h | 2 +
- arch/x86/kvm/svm/sev.c | 44 ++++++++++++++++++-
- arch/x86/kvm/svm/svm.h | 4 ++
- include/uapi/linux/kvm.h | 13 ++++++
- 5 files changed, 88 insertions(+), 2 deletions(-)
+ .../virt/kvm/amd-memory-encryption.rst | 25 ++++
+ arch/x86/kvm/svm/sev.c | 132 +++++++++++++++++-
+ arch/x86/kvm/svm/svm.h | 1 +
+ include/uapi/linux/kvm.h | 9 ++
+ 4 files changed, 166 insertions(+), 1 deletion(-)
diff --git a/Documentation/virt/kvm/amd-memory-encryption.rst b/Documentation/virt/kvm/amd-memory-encryption.rst
-index 5c081c8c7164..7b1d32fb99a8 100644
+index 75ca60b6d40a..8620383d405a 100644
--- a/Documentation/virt/kvm/amd-memory-encryption.rst
+++ b/Documentation/virt/kvm/amd-memory-encryption.rst
-@@ -427,6 +427,33 @@ issued by the hypervisor to make the guest ready for execution.
-
- Returns: 0 on success, -negative on error
-
-+18. KVM_SNP_INIT
-+----------------
-+
-+The KVM_SNP_INIT command can be used by the hypervisor to initialize SEV-SNP
-+context. In a typical workflow, this command should be the first command issued.
-+
-+Parameters (in/out): struct kvm_snp_init
+@@ -443,6 +443,31 @@ Returns: 0 on success, -negative on error
+ __u64 flags; /* must be zero */
+ };
+
++
++19. KVM_SNP_LAUNCH_START
++------------------------
++
++The KVM_SNP_LAUNCH_START command is used for creating the memory encryption
++context for the SEV-SNP guest. To create the encryption context, user must
++provide a guest policy, migration agent (if any) and guest OS visible
++workarounds value as defined SEV-SNP specification.
++
++Parameters (in): struct kvm_snp_launch_start
+
+Returns: 0 on success, -negative on error
+
+::
+
-+ struct kvm_snp_init {
-+ __u64 flags;
++ struct kvm_sev_snp_launch_start {
++ __u64 policy; /* Guest policy to use. */
++ __u64 ma_uaddr; /* userspace address of migration agent */
++ __u8 ma_en; /* 1 if the migtation agent is enabled */
++ __u8 imi_en; /* set IMI to 1. */
++ __u8 gosvw[16]; /* guest OS visible workarounds */
+ };
+
-+The flags bitmap is defined as::
-+
-+ /* enable the restricted injection */
-+ #define KVM_SEV_SNP_RESTRICTED_INJET (1<<0)
-+
-+ /* enable the restricted injection timer */
-+ #define KVM_SEV_SNP_RESTRICTED_TIMER_INJET (1<<1)
-+
-+If the specified flags is not supported then return -EOPNOTSUPP, and the supported
-+flags are returned.
++See the SEV-SNP specification for further detail on the launch input.
+
References
==========
-diff --git a/arch/x86/include/asm/svm.h b/arch/x86/include/asm/svm.h
-index 44a3f920f886..a39e31845a33 100644
---- a/arch/x86/include/asm/svm.h
-+++ b/arch/x86/include/asm/svm.h
-@@ -218,6 +218,8 @@ struct __attribute__ ((__packed__)) vmcb_control_area {
- #define SVM_NESTED_CTL_SEV_ENABLE BIT(1)
- #define SVM_NESTED_CTL_SEV_ES_ENABLE BIT(2)
-
-+#define SVM_SEV_FEAT_SNP_ACTIVE BIT(0)
-+
- struct vmcb_seg {
- u16 selector;
- u16 attrib;
diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c
-index 50fddbe56981..93da463545ef 100644
+index be31221f0a47..f44a657e8912 100644
--- a/arch/x86/kvm/svm/sev.c
+++ b/arch/x86/kvm/svm/sev.c
-@@ -235,10 +235,30 @@ static void sev_unbind_asid(struct kvm *kvm, unsigned int handle)
- sev_decommission(handle);
+@@ -20,6 +20,7 @@
+ #include <asm/fpu/internal.h>
+
+ #include <asm/trapnr.h>
++#include <asm/sev.h>
+
+ #include "x86.h"
+ #include "svm.h"
+@@ -75,6 +76,8 @@ static unsigned long sev_me_mask;
+ static unsigned long *sev_asid_bitmap;
+ static unsigned long *sev_reclaim_asid_bitmap;
+
++static int snp_decommission_context(struct kvm *kvm);
++
+ struct enc_region {
+ struct list_head list;
+ unsigned long npages;
+@@ -1527,6 +1530,100 @@ static int sev_receive_finish(struct kvm *kvm, struct kvm_sev_cmd *argp)
+ return sev_issue_cmd(kvm, SEV_CMD_RECEIVE_FINISH, &data, &argp->error);
}
-+static int verify_snp_init_flags(struct kvm *kvm, struct kvm_sev_cmd *argp)
-+{
-+ struct kvm_snp_init params;
-+ int ret = 0;
++static void *snp_context_create(struct kvm *kvm, struct kvm_sev_cmd *argp)
++{
++ struct sev_data_snp_gctx_create data = {};
++ void *context;
++ int rc;
++
++ /* Allocate memory for context page */
++ context = snp_alloc_firmware_page(GFP_KERNEL_ACCOUNT);
++ if (!context)
++ return NULL;
++
++ data.gctx_paddr = __psp_pa(context);
++ rc = __sev_issue_cmd(argp->sev_fd, SEV_CMD_SNP_GCTX_CREATE, &data, &argp->error);
++ if (rc) {
++ snp_free_firmware_page(context);
++ return NULL;
++ }
++
++ return context;
++}
++
++static int snp_bind_asid(struct kvm *kvm, int *error)
++{
++ struct kvm_sev_info *sev = &to_kvm_svm(kvm)->sev_info;
++ struct sev_data_snp_activate data = {};
++ int asid = sev_get_asid(kvm);
++ int ret, retry_count = 0;
++
++ /* Activate ASID on the given context */
++ data.gctx_paddr = __psp_pa(sev->snp_context);
++ data.asid = asid;
++again:
++ ret = sev_issue_cmd(kvm, SEV_CMD_SNP_ACTIVATE, &data, error);
++
++ /* Check if the DF_FLUSH is required, and try again */
++ if (ret && (*error == SEV_RET_DFFLUSH_REQUIRED) && (!retry_count)) {
++ /* Guard DEACTIVATE against WBINVD/DF_FLUSH used in ASID recycling */
++ down_read(&sev_deactivate_lock);
++ wbinvd_on_all_cpus();
++ ret = snp_guest_df_flush(error);
++ up_read(&sev_deactivate_lock);
++
++ if (ret)
++ return ret;
++
++ /* only one retry */
++ retry_count = 1;
++
++ goto again;
++ }
++
++ return ret;
++}
++
++static int snp_launch_start(struct kvm *kvm, struct kvm_sev_cmd *argp)
++{
++ struct kvm_sev_info *sev = &to_kvm_svm(kvm)->sev_info;
++ struct sev_data_snp_launch_start start = {};
++ struct kvm_sev_snp_launch_start params;
++ int rc;
++
++ if (!sev_snp_guest(kvm))
++ return -ENOTTY;
+
+ if (copy_from_user(¶ms, (void __user *)(uintptr_t)argp->data, sizeof(params)))
+ return -EFAULT;
+
-+ if (params.flags & ~SEV_SNP_SUPPORTED_FLAGS)
-+ ret = -EOPNOTSUPP;
-+
-+ params.flags = SEV_SNP_SUPPORTED_FLAGS;
-+
-+ if (copy_to_user((void __user *)(uintptr_t)argp->data, ¶ms, sizeof(params)))
-+ ret = -EFAULT;
-+
-+ return ret;
-+}
-+
- static int sev_guest_init(struct kvm *kvm, struct kvm_sev_cmd *argp)
++ /* Initialize the guest context */
++ sev->snp_context = snp_context_create(kvm, argp);
++ if (!sev->snp_context)
++ return -ENOTTY;
++
++ /* Issue the LAUNCH_START command */
++ start.gctx_paddr = __psp_pa(sev->snp_context);
++ start.policy = params.policy;
++ memcpy(start.gosvw, params.gosvw, sizeof(params.gosvw));
++ rc = __sev_issue_cmd(argp->sev_fd, SEV_CMD_SNP_LAUNCH_START, &start, &argp->error);
++ if (rc)
++ goto e_free_context;
++
++ /* Bind ASID to this guest */
++ sev->fd = argp->sev_fd;
++ rc = snp_bind_asid(kvm, &argp->error);
++ if (rc)
++ goto e_free_context;
++
++ return 0;
++
++e_free_context:
++ snp_decommission_context(kvm);
++
++ return rc;
++}
++
+ int svm_mem_enc_op(struct kvm *kvm, void __user *argp)
{
-+ bool es_active = (argp->id == KVM_SEV_ES_INIT || argp->id == KVM_SEV_SNP_INIT);
+ struct kvm_sev_cmd sev_cmd;
+@@ -1616,6 +1713,9 @@ int svm_mem_enc_op(struct kvm *kvm, void __user *argp)
+ case KVM_SEV_RECEIVE_FINISH:
+ r = sev_receive_finish(kvm, &sev_cmd);
+ break;
++ case KVM_SEV_SNP_LAUNCH_START:
++ r = snp_launch_start(kvm, &sev_cmd);
++ break;
+ default:
+ r = -EINVAL;
+ goto out;
+@@ -1809,6 +1909,28 @@ int svm_vm_copy_asid_from(struct kvm *kvm, unsigned int source_fd)
+ return ret;
+ }
+
++static int snp_decommission_context(struct kvm *kvm)
++{
++ struct kvm_sev_info *sev = &to_kvm_svm(kvm)->sev_info;
++ struct sev_data_snp_decommission data = {};
++ int ret;
++
++ /* If context is not created then do nothing */
++ if (!sev->snp_context)
++ return 0;
++
++ data.gctx_paddr = __sme_pa(sev->snp_context);
++ ret = snp_guest_decommission(&data, NULL);
++ if (ret)
++ return ret;
++
++ /* free the context page now */
++ snp_free_firmware_page(sev->snp_context);
++ sev->snp_context = NULL;
++
++ return 0;
++}
++
+ void sev_vm_destroy(struct kvm *kvm)
+ {
struct kvm_sev_info *sev = &to_kvm_svm(kvm)->sev_info;
-- bool es_active = argp->id == KVM_SEV_ES_INIT;
-+ bool snp_active = argp->id == KVM_SEV_SNP_INIT;
- int asid, ret;
-
- if (kvm->created_vcpus)
-@@ -249,12 +269,22 @@ static int sev_guest_init(struct kvm *kvm, struct kvm_sev_cmd *argp)
- return ret;
-
- sev->es_active = es_active;
-+ sev->snp_active = snp_active;
- asid = sev_asid_new(sev);
- if (asid < 0)
- goto e_no_asid;
- sev->asid = asid;
-
-- ret = sev_platform_init(&argp->error);
-+ if (snp_active) {
-+ ret = verify_snp_init_flags(kvm, argp);
-+ if (ret)
-+ goto e_free;
-+
-+ ret = sev_snp_init(&argp->error);
+@@ -1847,7 +1969,15 @@ void sev_vm_destroy(struct kvm *kvm)
+
+ mutex_unlock(&kvm->lock);
+
+- sev_unbind_asid(kvm, sev->handle);
++ if (sev_snp_guest(kvm)) {
++ if (snp_decommission_context(kvm)) {
++ pr_err("Failed to free SNP guest context, leaking asid!\n");
++ return;
++ }
+ } else {
-+ ret = sev_platform_init(&argp->error);
++ sev_unbind_asid(kvm, sev->handle);
+ }
+
- if (ret)
- goto e_free;
-
-@@ -600,6 +630,10 @@ static int sev_es_sync_vmsa(struct vcpu_svm *svm)
- save->pkru = svm->vcpu.arch.pkru;
- save->xss = svm->vcpu.arch.ia32_xss;
-
-+ /* Enable the SEV-SNP feature */
-+ if (sev_snp_guest(svm->vcpu.kvm))
-+ save->sev_features |= SVM_SEV_FEAT_SNP_ACTIVE;
-+
- return 0;
+ sev_asid_free(sev);
}
-@@ -1532,6 +1566,12 @@ int svm_mem_enc_op(struct kvm *kvm, void __user *argp)
- }
-
- switch (sev_cmd.id) {
-+ case KVM_SEV_SNP_INIT:
-+ if (!sev_snp_enabled) {
-+ r = -ENOTTY;
-+ goto out;
-+ }
-+ fallthrough;
- case KVM_SEV_ES_INIT:
- if (!sev_es_enabled) {
- r = -ENOTTY;
diff --git a/arch/x86/kvm/svm/svm.h b/arch/x86/kvm/svm/svm.h
-index 01953522097d..57c3c404b0b3 100644
+index b9ea99f8579e..bc5582b44356 100644
--- a/arch/x86/kvm/svm/svm.h
+++ b/arch/x86/kvm/svm/svm.h
-@@ -69,6 +69,9 @@ enum {
- /* TPR and CR2 are always written before VMRUN */
- #define VMCB_ALWAYS_DIRTY_MASK ((1U << VMCB_INTR) | (1U << VMCB_CR2))
-
-+/* Supported init feature flags */
-+#define SEV_SNP_SUPPORTED_FLAGS 0x0
-+
- struct kvm_sev_info {
- bool active; /* SEV enabled guest */
- bool es_active; /* SEV-ES enabled guest */
-@@ -81,6 +84,7 @@ struct kvm_sev_info {
+@@ -67,6 +67,7 @@ struct kvm_sev_info {
u64 ap_jump_table; /* SEV-ES AP Jump Table address */
struct kvm *enc_context_owner; /* Owner of copied encryption context */
struct misc_cg *misc_cg; /* For misc cgroup accounting */
-+ u64 snp_init_flags;
++ void *snp_context; /* SNP guest context page */
};
struct kvm_svm {
diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h
-index d9e4aabcb31a..944e2bf601fe 100644
+index 989a64aa1ae5..dbd05179d8fa 100644
--- a/include/uapi/linux/kvm.h
+++ b/include/uapi/linux/kvm.h
-@@ -1712,6 +1712,9 @@ enum sev_cmd_id {
- /* Guest Migration Extension */
- KVM_SEV_SEND_CANCEL,
-
-+ /* SNP specific commands */
-+ KVM_SEV_SNP_INIT,
-+
+@@ -1680,6 +1680,7 @@ enum sev_cmd_id {
+
+ /* SNP specific commands */
+ KVM_SEV_SNP_INIT = 256,
++ KVM_SEV_SNP_LAUNCH_START,
+
KVM_SEV_NR_MAX,
};
-
-@@ -1808,6 +1811,16 @@ struct kvm_sev_receive_update_data {
- __u32 trans_len;
+@@ -1781,6 +1782,14 @@ struct kvm_snp_init {
+ __u64 flags;
};
-+/* enable the restricted injection */
-+#define KVM_SEV_SNP_RESTRICTED_INJET (1 << 0)
-+
-+/* enable the restricted injection timer */
-+#define KVM_SEV_SNP_RESTRICTED_TIMER_INJET (1 << 1)
-+
-+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];
+};
+
#define KVM_DEV_ASSIGN_ENABLE_IOMMU (1 << 0)