--- v3
+++ v5
@@ -1,43 +1,232 @@
-When SEV-SNP is enabled globally, the hardware places restrictions on all
-memory accesses based on the RMP entry, whether the hyperviso or a VM,
-performs the accesses. When hardware encounters an RMP access violation
-during a guest access, it will cause a #VMEXIT(NPF).
-
-See APM2 section 16.36.10 for more details.
+The KVM_SEV_SNP_LAUNCH_FINISH finalize the cryptographic digest and stores
+it as the measurement of the guest at launch.
+
+While finalizing the launch flow, it also issues the LAUNCH_UPDATE command
+to encrypt the VMSA pages.
Signed-off-by: Brijesh Singh <brijesh.singh@amd.com>
---
- arch/x86/include/asm/kvm_host.h | 8 ++++++++
- 1 file changed, 8 insertions(+)
-
-diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
-index cd2e19e1d323..59185b6bc82a 100644
---- a/arch/x86/include/asm/kvm_host.h
-+++ b/arch/x86/include/asm/kvm_host.h
-@@ -239,8 +239,12 @@ enum x86_intercept_stage;
- #define PFERR_FETCH_BIT 4
- #define PFERR_PK_BIT 5
- #define PFERR_SGX_BIT 15
-+#define PFERR_GUEST_RMP_BIT 31
- #define PFERR_GUEST_FINAL_BIT 32
- #define PFERR_GUEST_PAGE_BIT 33
-+#define PFERR_GUEST_ENC_BIT 34
-+#define PFERR_GUEST_SIZEM_BIT 35
-+#define PFERR_GUEST_VMPL_BIT 36
-
- #define PFERR_PRESENT_MASK (1U << PFERR_PRESENT_BIT)
- #define PFERR_WRITE_MASK (1U << PFERR_WRITE_BIT)
-@@ -251,6 +255,10 @@ enum x86_intercept_stage;
- #define PFERR_SGX_MASK (1U << PFERR_SGX_BIT)
- #define PFERR_GUEST_FINAL_MASK (1ULL << PFERR_GUEST_FINAL_BIT)
- #define PFERR_GUEST_PAGE_MASK (1ULL << PFERR_GUEST_PAGE_BIT)
-+#define PFERR_GUEST_RMP_MASK (1ULL << PFERR_GUEST_RMP_BIT)
-+#define PFERR_GUEST_ENC_MASK (1ULL << PFERR_GUEST_ENC_BIT)
-+#define PFERR_GUEST_SIZEM_MASK (1ULL << PFERR_GUEST_SIZEM_BIT)
-+#define PFERR_GUEST_VMPL_MASK (1ULL << PFERR_GUEST_VMPL_BIT)
-
- #define PFERR_NESTED_GUEST_PAGE (PFERR_GUEST_PAGE_MASK | \
- PFERR_WRITE_MASK | \
+ .../virt/kvm/amd-memory-encryption.rst | 22 ++++
+ arch/x86/kvm/svm/sev.c | 116 ++++++++++++++++++
+ include/uapi/linux/kvm.h | 14 +++
+ 3 files changed, 152 insertions(+)
+
+diff --git a/Documentation/virt/kvm/amd-memory-encryption.rst b/Documentation/virt/kvm/amd-memory-encryption.rst
+index ddcd94e9ffed..c7332e0e0baa 100644
+--- a/Documentation/virt/kvm/amd-memory-encryption.rst
++++ b/Documentation/virt/kvm/amd-memory-encryption.rst
+@@ -506,6 +506,28 @@ Returns: 0 on success, -negative on error
+ See the SEV-SNP spec for further details on how to build the VMPL permission
+ mask and page type.
+
++21. KVM_SNP_LAUNCH_FINISH
++-------------------------
++
++After completion of the SNP guest launch flow, the KVM_SNP_LAUNCH_FINISH command can be
++issued to make the guest ready for the execution.
++
++Parameters (in): struct kvm_sev_snp_launch_finish
++
++Returns: 0 on success, -negative on error
++
++::
++
++ struct kvm_sev_snp_launch_finish {
++ __u64 id_block_uaddr;
++ __u64 id_auth_uaddr;
++ __u8 id_block_en;
++ __u8 auth_key_en;
++ __u8 host_data[32];
++ };
++
++
++See SEV-SNP specification for further details on launch finish input parameters.
+
+ References
+ ==========
+diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c
+index dcef0ae5f8e4..248096a5c307 100644
+--- a/arch/x86/kvm/svm/sev.c
++++ b/arch/x86/kvm/svm/sev.c
+@@ -1813,6 +1813,106 @@ static int snp_launch_update(struct kvm *kvm, struct kvm_sev_cmd *argp)
+ return ret;
+ }
+
++static int snp_launch_update_vmsa(struct kvm *kvm, struct kvm_sev_cmd *argp)
++{
++ struct kvm_sev_info *sev = &to_kvm_svm(kvm)->sev_info;
++ struct sev_data_snp_launch_update data = {};
++ int i, ret;
++
++ data.gctx_paddr = __psp_pa(sev->snp_context);
++ data.page_type = SNP_PAGE_TYPE_VMSA;
++
++ for (i = 0; i < kvm->created_vcpus; i++) {
++ struct vcpu_svm *svm = to_svm(kvm->vcpus[i]);
++ u64 pfn = __pa(svm->vmsa) >> PAGE_SHIFT;
++
++ /* Perform some pre-encryption checks against the VMSA */
++ ret = sev_es_sync_vmsa(svm);
++ if (ret)
++ return ret;
++
++ /* Transition the VMSA page to a firmware state. */
++ ret = rmp_make_private(pfn, -1, PG_LEVEL_4K, sev->asid, true);
++ if (ret)
++ return ret;
++
++ /* Issue the SNP command to encrypt the VMSA */
++ data.address = __sme_pa(svm->vmsa);
++ ret = __sev_issue_cmd(argp->sev_fd, SEV_CMD_SNP_LAUNCH_UPDATE,
++ &data, &argp->error);
++ if (ret) {
++ snp_page_reclaim(pfn);
++ return ret;
++ }
++
++ svm->vcpu.arch.guest_state_protected = true;
++ }
++
++ return 0;
++}
++
++static int snp_launch_finish(struct kvm *kvm, struct kvm_sev_cmd *argp)
++{
++ struct kvm_sev_info *sev = &to_kvm_svm(kvm)->sev_info;
++ struct sev_data_snp_launch_finish *data;
++ void *id_block = NULL, *id_auth = NULL;
++ struct kvm_sev_snp_launch_finish params;
++ int ret;
++
++ if (!sev_snp_guest(kvm))
++ return -ENOTTY;
++
++ if (!sev->snp_context)
++ return -EINVAL;
++
++ if (copy_from_user(¶ms, (void __user *)(uintptr_t)argp->data, sizeof(params)))
++ return -EFAULT;
++
++ /* Measure all vCPUs using LAUNCH_UPDATE before we finalize the launch flow. */
++ ret = snp_launch_update_vmsa(kvm, argp);
++ if (ret)
++ return ret;
++
++ data = kzalloc(sizeof(*data), GFP_KERNEL_ACCOUNT);
++ if (!data)
++ return -ENOMEM;
++
++ if (params.id_block_en) {
++ id_block = psp_copy_user_blob(params.id_block_uaddr, KVM_SEV_SNP_ID_BLOCK_SIZE);
++ if (IS_ERR(id_block)) {
++ ret = PTR_ERR(id_block);
++ goto e_free;
++ }
++
++ data->id_block_en = 1;
++ data->id_block_paddr = __sme_pa(id_block);
++ }
++
++ if (params.auth_key_en) {
++ id_auth = psp_copy_user_blob(params.id_auth_uaddr, KVM_SEV_SNP_ID_AUTH_SIZE);
++ if (IS_ERR(id_auth)) {
++ ret = PTR_ERR(id_auth);
++ goto e_free_id_block;
++ }
++
++ data->auth_key_en = 1;
++ data->id_auth_paddr = __sme_pa(id_auth);
++ }
++
++ data->gctx_paddr = __psp_pa(sev->snp_context);
++ ret = sev_issue_cmd(kvm, SEV_CMD_SNP_LAUNCH_FINISH, data, &argp->error);
++
++ kfree(id_auth);
++
++e_free_id_block:
++ kfree(id_block);
++
++e_free:
++ kfree(data);
++
++ return ret;
++}
++
+ int svm_mem_enc_op(struct kvm *kvm, void __user *argp)
+ {
+ struct kvm_sev_cmd sev_cmd;
+@@ -1908,6 +2008,9 @@ int svm_mem_enc_op(struct kvm *kvm, void __user *argp)
+ case KVM_SEV_SNP_LAUNCH_UPDATE:
+ r = snp_launch_update(kvm, &sev_cmd);
+ break;
++ case KVM_SEV_SNP_LAUNCH_FINISH:
++ r = snp_launch_finish(kvm, &sev_cmd);
++ break;
+ default:
+ r = -EINVAL;
+ goto out;
+@@ -2364,16 +2467,29 @@ static void sev_flush_guest_memory(struct vcpu_svm *svm, void *va,
+ void sev_free_vcpu(struct kvm_vcpu *vcpu)
+ {
+ struct vcpu_svm *svm;
++ u64 pfn;
+
+ if (!sev_es_guest(vcpu->kvm))
+ return;
+
+ svm = to_svm(vcpu);
++ pfn = __pa(svm->vmsa) >> PAGE_SHIFT;
+
+ if (vcpu->arch.guest_state_protected)
+ sev_flush_guest_memory(svm, svm->vmsa, PAGE_SIZE);
++
++ /*
++ * If its an SNP guest, then VMSA was added in the RMP entry as
++ * a guest owned page. Transition the page to hyperivosr state
++ * before releasing it back to the system.
++ */
++ if (sev_snp_guest(vcpu->kvm) &&
++ host_rmp_make_shared(pfn, PG_LEVEL_4K, false))
++ goto skip_vmsa_free;
++
+ __free_page(virt_to_page(svm->vmsa));
+
++skip_vmsa_free:
+ if (svm->ghcb_sa_free)
+ kfree(svm->ghcb_sa);
+ }
+diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h
+index 0681be4bdfdf..ab9b1c82b0ee 100644
+--- a/include/uapi/linux/kvm.h
++++ b/include/uapi/linux/kvm.h
+@@ -1716,6 +1716,7 @@ enum sev_cmd_id {
+ KVM_SEV_SNP_INIT,
+ KVM_SEV_SNP_LAUNCH_START,
+ KVM_SEV_SNP_LAUNCH_UPDATE,
++ KVM_SEV_SNP_LAUNCH_FINISH,
+
+ KVM_SEV_NR_MAX,
+ };
+@@ -1850,6 +1851,19 @@ struct kvm_sev_snp_launch_update {
+ __u8 vmpl1_perms;
+ };
+
++#define KVM_SEV_SNP_ID_BLOCK_SIZE 96
++#define KVM_SEV_SNP_ID_AUTH_SIZE 4096
++#define KVM_SEV_SNP_FINISH_DATA_SIZE 32
++
++struct kvm_sev_snp_launch_finish {
++ __u64 id_block_uaddr;
++ __u64 id_auth_uaddr;
++ __u8 id_block_en;
++ __u8 auth_key_en;
++ __u8 host_data[KVM_SEV_SNP_FINISH_DATA_SIZE];
++ __u8 pad[6];
++};
++
+ #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