Skip to content

Commit 3a4eb36

Browse files
yamahatabonzini
authored andcommitted
KVM: x86/mmu: Add an external pointer to struct kvm_mmu_page
Add an external pointer to struct kvm_mmu_page for TDX's private page table and add helper functions to allocate/initialize/free a private page table page. TDX will only be supported with the TDP MMU. Because KVM TDP MMU doesn't use unsync_children and write_flooding_count, pack them to have room for a pointer and use a union to avoid memory overhead. For private GPA, CPU refers to a private page table whose contents are encrypted. The dedicated APIs to operate on it (e.g. updating/reading its PTE entry) are used, and their cost is expensive. When KVM resolves the KVM page fault, it walks the page tables. To reuse the existing KVM MMU code and mitigate the heavy cost of directly walking the private page table allocate two sets of page tables for the private half of the GPA space. For the page tables that KVM will walk, allocate them like normal and refer to them as mirror page tables. Additionally allocate one more page for the page tables the CPU will walk, and call them external page tables. Resolve the KVM page fault with the existing code, and do additional operations necessary for modifying the external page table in future patches. The relationship of the types of page tables in this scheme is depicted below: KVM page fault | | | V | -------------+---------- | | | | V V | shared GPA private GPA | | | | V V | shared PT root mirror PT root | private PT root | | | | V V | V shared PT mirror PT --propagate--> external PT | | | | | \-----------------+------\ | | | | | V | V V shared guest page | private guest page | non-encrypted memory | encrypted memory | PT - Page table Shared PT - Visible to KVM, and the CPU uses it for shared mappings. External PT - The CPU uses it, but it is invisible to KVM. TDX module updates this table to map private guest pages. Mirror PT - It is visible to KVM, but the CPU doesn't use it. KVM uses it to propagate PT change to the actual private PT. Add a helper kvm_has_mirrored_tdp() to trigger this behavior and wire it to the TDX vm type. Co-developed-by: Yan Zhao <[email protected]> Signed-off-by: Yan Zhao <[email protected]> Signed-off-by: Isaku Yamahata <[email protected]> Signed-off-by: Rick Edgecombe <[email protected]> Reviewed-by: Binbin Wu <[email protected]> Message-ID: <[email protected]> Signed-off-by: Paolo Bonzini <[email protected]>
1 parent 9364789 commit 3a4eb36

File tree

5 files changed

+45
-4
lines changed

5 files changed

+45
-4
lines changed

arch/x86/include/asm/kvm_host.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -813,6 +813,11 @@ struct kvm_vcpu_arch {
813813
struct kvm_mmu_memory_cache mmu_shadow_page_cache;
814814
struct kvm_mmu_memory_cache mmu_shadowed_info_cache;
815815
struct kvm_mmu_memory_cache mmu_page_header_cache;
816+
/*
817+
* This cache is to allocate external page table. E.g. private EPT used
818+
* by the TDX module.
819+
*/
820+
struct kvm_mmu_memory_cache mmu_external_spt_cache;
816821

817822
/*
818823
* QEMU userspace and the guest each have their own FPU state.

arch/x86/kvm/mmu.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,4 +287,9 @@ static inline gpa_t kvm_translate_gpa(struct kvm_vcpu *vcpu,
287287
return gpa;
288288
return translate_nested_gpa(vcpu, gpa, access, exception);
289289
}
290+
291+
static inline bool kvm_has_mirrored_tdp(const struct kvm *kvm)
292+
{
293+
return kvm->arch.vm_type == KVM_X86_TDX_VM;
294+
}
290295
#endif

arch/x86/kvm/mmu/mmu.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -599,6 +599,12 @@ static int mmu_topup_memory_caches(struct kvm_vcpu *vcpu, bool maybe_indirect)
599599
1 + PT64_ROOT_MAX_LEVEL + PTE_PREFETCH_NUM);
600600
if (r)
601601
return r;
602+
if (kvm_has_mirrored_tdp(vcpu->kvm)) {
603+
r = kvm_mmu_topup_memory_cache(&vcpu->arch.mmu_external_spt_cache,
604+
PT64_ROOT_MAX_LEVEL);
605+
if (r)
606+
return r;
607+
}
602608
r = kvm_mmu_topup_memory_cache(&vcpu->arch.mmu_shadow_page_cache,
603609
PT64_ROOT_MAX_LEVEL);
604610
if (r)
@@ -618,6 +624,7 @@ static void mmu_free_memory_caches(struct kvm_vcpu *vcpu)
618624
kvm_mmu_free_memory_cache(&vcpu->arch.mmu_pte_list_desc_cache);
619625
kvm_mmu_free_memory_cache(&vcpu->arch.mmu_shadow_page_cache);
620626
kvm_mmu_free_memory_cache(&vcpu->arch.mmu_shadowed_info_cache);
627+
kvm_mmu_free_memory_cache(&vcpu->arch.mmu_external_spt_cache);
621628
kvm_mmu_free_memory_cache(&vcpu->arch.mmu_page_header_cache);
622629
}
623630

arch/x86/kvm/mmu/mmu_internal.h

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,22 @@ struct kvm_mmu_page {
101101
int root_count;
102102
refcount_t tdp_mmu_root_count;
103103
};
104-
unsigned int unsync_children;
104+
union {
105+
/* These two members aren't used for TDP MMU */
106+
struct {
107+
unsigned int unsync_children;
108+
/*
109+
* Number of writes since the last time traversal
110+
* visited this page.
111+
*/
112+
atomic_t write_flooding_count;
113+
};
114+
/*
115+
* Page table page of external PT.
116+
* Passed to TDX module, not accessed by KVM.
117+
*/
118+
void *external_spt;
119+
};
105120
union {
106121
struct kvm_rmap_head parent_ptes; /* rmap pointers to parent sptes */
107122
tdp_ptep_t ptep;
@@ -124,9 +139,6 @@ struct kvm_mmu_page {
124139
int clear_spte_count;
125140
#endif
126141

127-
/* Number of writes since the last time traversal visited this page. */
128-
atomic_t write_flooding_count;
129-
130142
#ifdef CONFIG_X86_64
131143
/* Used for freeing the page asynchronously if it is a TDP MMU page. */
132144
struct rcu_head rcu_head;
@@ -145,6 +157,17 @@ static inline int kvm_mmu_page_as_id(struct kvm_mmu_page *sp)
145157
return kvm_mmu_role_as_id(sp->role);
146158
}
147159

160+
static inline void kvm_mmu_alloc_external_spt(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp)
161+
{
162+
/*
163+
* external_spt is allocated for TDX module to hold private EPT mappings,
164+
* TDX module will initialize the page by itself.
165+
* Therefore, KVM does not need to initialize or access external_spt.
166+
* KVM only interacts with sp->spt for private EPT operations.
167+
*/
168+
sp->external_spt = kvm_mmu_memory_cache_alloc(&vcpu->arch.mmu_external_spt_cache);
169+
}
170+
148171
static inline bool kvm_mmu_page_ad_need_write_protect(struct kvm_mmu_page *sp)
149172
{
150173
/*

arch/x86/kvm/mmu/tdp_mmu.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ void kvm_mmu_uninit_tdp_mmu(struct kvm *kvm)
5353

5454
static void tdp_mmu_free_sp(struct kvm_mmu_page *sp)
5555
{
56+
free_page((unsigned long)sp->external_spt);
5657
free_page((unsigned long)sp->spt);
5758
kmem_cache_free(mmu_page_header_cache, sp);
5859
}

0 commit comments

Comments
 (0)