Re: [PATCH v15 13/20] KVM: SEV: Implement gmem hook for initializing private pages
From: Huang, Kai
Date: Mon May 20 2024 - 06:17:14 EST
On Wed, 2024-05-01 at 03:52 -0500, Michael Roth wrote:
> This will handle the RMP table updates needed to put a page into a
> private state before mapping it into an SEV-SNP guest.
>
>
[...]
> +int sev_gmem_prepare(struct kvm *kvm, kvm_pfn_t pfn, gfn_t gfn, int max_order)
> +{
> + struct kvm_sev_info *sev = &to_kvm_svm(kvm)->sev_info;
> + kvm_pfn_t pfn_aligned;
> + gfn_t gfn_aligned;
> + int level, rc;
> + bool assigned;
> +
> + if (!sev_snp_guest(kvm))
> + return 0;
> +
> + rc = snp_lookup_rmpentry(pfn, &assigned, &level);
> + if (rc) {
> + pr_err_ratelimited("SEV: Failed to look up RMP entry: GFN %llx PFN %llx error %d\n",
> + gfn, pfn, rc);
> + return -ENOENT;
> + }
> +
> + if (assigned) {
> + pr_debug("%s: already assigned: gfn %llx pfn %llx max_order %d level %d\n",
> + __func__, gfn, pfn, max_order, level);
> + return 0;
> + }
> +
> + if (is_large_rmp_possible(kvm, pfn, max_order)) {
> + level = PG_LEVEL_2M;
> + pfn_aligned = ALIGN_DOWN(pfn, PTRS_PER_PMD);
> + gfn_aligned = ALIGN_DOWN(gfn, PTRS_PER_PMD);
> + } else {
> + level = PG_LEVEL_4K;
> + pfn_aligned = pfn;
> + gfn_aligned = gfn;
> + }
> +
> + rc = rmp_make_private(pfn_aligned, gfn_to_gpa(gfn_aligned), level, sev->asid, false);
> + if (rc) {
> + pr_err_ratelimited("SEV: Failed to update RMP entry: GFN %llx PFN %llx level %d error %d\n",
> + gfn, pfn, level, rc);
> + return -EINVAL;
> + }
> +
> + pr_debug("%s: updated: gfn %llx pfn %llx pfn_aligned %llx max_order %d level %d\n",
> + __func__, gfn, pfn, pfn_aligned, max_order, level);
> +
> + return 0;
> +}
> diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c
> index b70556608e8d..60783e9f2ae8 100644
> --- a/arch/x86/kvm/svm/svm.c
> +++ b/arch/x86/kvm/svm/svm.c
> @@ -5085,6 +5085,8 @@ static struct kvm_x86_ops svm_x86_ops __initdata = {
> .vcpu_deliver_sipi_vector = svm_vcpu_deliver_sipi_vector,
> .vcpu_get_apicv_inhibit_reasons = avic_vcpu_get_apicv_inhibit_reasons,
> .alloc_apic_backing_page = svm_alloc_apic_backing_page,
> +
> + .gmem_prepare = sev_gmem_prepare,
> };
>
>
+Rick, Isaku,
I am wondering whether this can be done in the KVM page fault handler?
The reason that I am asking is KVM will introduce several new
kvm_x86_ops::xx_private_spte() ops for TDX to handle setting up the
private mapping, and I am wondering whether SNP can just reuse some of
them so we can avoid having this .gmem_prepare():
/* Add a page as page table page into private page table */
int (*link_private_spt)(struct kvm *kvm, gfn_t gfn,
enum pg_level level, void *private_spt);
/*
* Free a page table page of private page table.
* ...
*/
int (*free_private_spt)(struct kvm *kvm, gfn_t gfn,
enum pg_level level, void *private_spt);
/* Add a guest private page into private page table */
int (*set_private_spte)(struct kvm *kvm, gfn_t gfn,
enum pg_level level, kvm_pfn_t pfn);
/* Remove a guest private page from private page table*/
int (*remove_private_spte)(struct kvm *kvm, gfn_t gfn,
enum pg_level level, kvm_pfn_t pfn);
/*
* Keep a guest private page mapped in private page table,
* but clear its present bit
*/
int (*zap_private_spte)(struct kvm *kvm, gfn_t gfn,
enum pg_level level);
The idea behind these is in the fault handler:
bool use_private_pt = fault->is_private &&
kvm_use_private_pt(kvm);
root_pt = use_private_pt ? mmu->private_root_hpa : mmu->root_hpa;
tdp_mmu_for_each_pte(&iter, root_pt, gfn, gfn+1, ..) {
if (use_private_pt)
kvm_x86_ops->xx_private_spte();
else
// normal TDP MMU ops
}
Which means: if the fault is for private GPA, _AND_ when the VM has a
separate private table, use the specific xx_private_spte() ops to handle
private mapping.
But I am thinking we can use those hooks for SNP too, because
"conceptually", SNP also has concept of "private GPA" and must at least
issue some command to update the RMP table when private mapping is
setup/torn down.
So if we change the above logic to use fault->is_private, but not
'use_private_pt' to decide whether to invoke the
kvm_x86_ops::xx_private_spte(), then we can also implement SNP commands in
those callbacks IIUC:
if (fault->is_private && kvm_x86_ops::xx_private_spte())
kvm_x86_ops::xx_private_spte();
else
// normal TDP MMU operation
For SNP, these callbacks will operate on normal page table using the
normal TDP MMU code, but can do additional things like issuing commands as
shown in this patch.
My understanding is SNP doesn't need specific handling for middle level
page table, but should be able to utilize the ops when setting up /
tearing down the leaf SPTE?