Inter-revision diff: patch 23

Comparing v5 (message) to v4 (message)

--- 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(&params, (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, &params, 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)
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help