--- vrfc
+++ v4
@@ -1,91 +1,271 @@
-The SEV-SNP VMs may call the page state change VMGEXIT to add the GPA
-as private or shared in the RMP table. The page state change VMGEXIT
-will contain the RMP page level to be used in the RMP entry. If the
-page level between the TDP and RMP does not match then, it will result
-in nested-page-fault (RMP violation).
-
-The SEV-SNP VMGEXIT handler will use the kvm_mmu_get_tdp_walk() to get
-the current page-level in the TDP for the given GPA and calculate a
-workable page level. If a GPA is mapped as a 4K-page in the TDP, but
-the guest requested to add the GPA as a 2M in the RMP entry then the
-2M request will be broken into 4K-pages to keep the RMP and TDP
-page-levels in sync.
-
-Cc: Thomas Gleixner <tglx@linutronix.de>
-Cc: Ingo Molnar <mingo@redhat.com>
-Cc: Borislav Petkov <bp@alien8.de>
-Cc: Joerg Roedel <jroedel@suse.de>
-Cc: "H. Peter Anvin" <hpa@zytor.com>
-Cc: Tony Luck <tony.luck@intel.com>
-Cc: Dave Hansen <dave.hansen@intel.com>
-Cc: "Peter Zijlstra (Intel)" <peterz@infradead.org>
-Cc: Paolo Bonzini <pbonzini@redhat.com>
-Cc: Tom Lendacky <thomas.lendacky@amd.com>
-Cc: David Rientjes <rientjes@google.com>
-Cc: Sean Christopherson <seanjc@google.com>
-Cc: Vitaly Kuznetsov <vkuznets@redhat.com>
-Cc: Wanpeng Li <wanpengli@tencent.com>
-Cc: Jim Mattson <jmattson@google.com>
-Cc: x86@kernel.org
-Cc: kvm@vger.kernel.org
+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>
---
- arch/x86/kvm/mmu.h | 1 +
- arch/x86/kvm/mmu/mmu.c | 29 +++++++++++++++++++++++++++++
- 2 files changed, 30 insertions(+)
-
-diff --git a/arch/x86/kvm/mmu.h b/arch/x86/kvm/mmu.h
-index 70dce26a5882..e7c4e55215bf 100644
---- a/arch/x86/kvm/mmu.h
-+++ b/arch/x86/kvm/mmu.h
-@@ -110,6 +110,7 @@ int kvm_tdp_page_fault(struct kvm_vcpu *vcpu, gpa_t gpa, u32 error_code,
- bool prefault);
-
- int kvm_mmu_map_tdp_page(struct kvm_vcpu *vcpu, gpa_t gpa, u32 error_code, int max_level);
-+bool kvm_mmu_get_tdp_walk(struct kvm_vcpu *vcpu, gpa_t gpa, kvm_pfn_t *pfn, int *level);
-
- static inline int kvm_mmu_do_page_fault(struct kvm_vcpu *vcpu, gpa_t cr2_or_gpa,
- u32 err, bool prefault)
-diff --git a/arch/x86/kvm/mmu/mmu.c b/arch/x86/kvm/mmu/mmu.c
-index 33104943904b..147f22bda6e7 100644
---- a/arch/x86/kvm/mmu/mmu.c
-+++ b/arch/x86/kvm/mmu/mmu.c
-@@ -3828,6 +3828,35 @@ int kvm_mmu_map_tdp_page(struct kvm_vcpu *vcpu, gpa_t gpa, u32 error_code, int m
+ .../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 75ca60b6d40a..8620383d405a 100644
+--- a/Documentation/virt/kvm/amd-memory-encryption.rst
++++ b/Documentation/virt/kvm/amd-memory-encryption.rst
+@@ -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_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 */
++ };
++
++See the SEV-SNP specification for further detail on the launch input.
++
+ References
+ ==========
+
+diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c
+index be31221f0a47..f44a657e8912 100644
+--- a/arch/x86/kvm/svm/sev.c
++++ b/arch/x86/kvm/svm/sev.c
+@@ -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);
}
- EXPORT_SYMBOL_GPL(kvm_mmu_map_tdp_page);
-
-+bool kvm_mmu_get_tdp_walk(struct kvm_vcpu *vcpu, gpa_t gpa, kvm_pfn_t *pfn, int *level)
-+{
-+ u64 sptes[PT64_ROOT_MAX_LEVEL + 1];
-+ int leaf, root;
-+
-+ if (is_tdp_mmu_root(vcpu->kvm, vcpu->arch.mmu->root_hpa))
-+ leaf = kvm_tdp_mmu_get_walk(vcpu, gpa, sptes, &root);
-+ else
-+ leaf = get_walk(vcpu, gpa, sptes, &root);
-+
-+ if (unlikely(leaf < 0))
-+ return false;
-+
-+ /* Check if the leaf SPTE is present */
-+ if (!is_shadow_present_pte(sptes[leaf]))
-+ return false;
-+
-+ *pfn = spte_to_pfn(sptes[leaf]);
-+ if (leaf > PG_LEVEL_4K) {
-+ u64 page_mask = KVM_PAGES_PER_HPAGE(leaf) - KVM_PAGES_PER_HPAGE(leaf - 1);
-+ *pfn |= (gpa_to_gfn(gpa) & page_mask);
+
++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;
+ }
+
-+ *level = leaf;
-+
-+ return true;
-+}
-+EXPORT_SYMBOL_GPL(kvm_mmu_get_tdp_walk);
-+
- static void nonpaging_init_context(struct kvm_vcpu *vcpu,
- struct kvm_mmu *context)
++ 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;
++
++ /* 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)
{
+ 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;
+@@ -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 {
++ sev_unbind_asid(kvm, sev->handle);
++ }
++
+ sev_asid_free(sev);
+ }
+
+diff --git a/arch/x86/kvm/svm/svm.h b/arch/x86/kvm/svm/svm.h
+index b9ea99f8579e..bc5582b44356 100644
+--- a/arch/x86/kvm/svm/svm.h
++++ b/arch/x86/kvm/svm/svm.h
+@@ -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 */
++ 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 989a64aa1ae5..dbd05179d8fa 100644
+--- a/include/uapi/linux/kvm.h
++++ b/include/uapi/linux/kvm.h
+@@ -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,
+ };
+@@ -1781,6 +1782,14 @@ 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)
+ #define KVM_DEV_ASSIGN_PCI_2_3 (1 << 1)
+ #define KVM_DEV_ASSIGN_MASK_INTX (1 << 2)
--
2.17.1