Skip to content

Commit fabaa76

Browse files
yamahatabonzini
authored andcommitted
KVM: x86/tdp_mmu: Support mirror root for TDP MMU
Add the ability for the TDP MMU to maintain a mirror of a separate mapping. Like other Coco technologies, TDX has the concept of private and shared memory. For TDX the private and shared mappings are managed on separate EPT roots. The private half is managed indirectly through calls into a protected runtime environment called the TDX module, where the shared half is managed within KVM in normal page tables. In order to handle both shared and private memory, KVM needs to learn to handle faults and other operations on the correct root for the operation. KVM could learn the concept of private roots, and operate on them by calling out to operations that call into the TDX module. But there are two problems with that: 1. Calls into the TDX module are relatively slow compared to the simple accesses required to read a PTE managed directly by KVM. 2. Other Coco technologies deal with private memory completely differently and it will make the code confusing when being read from their perspective. Special operations added for TDX that set private or zap private memory will have nothing to do with these other private memory technologies. (SEV, etc). To handle these, instead teach the TDP MMU about a new concept "mirror roots". Such roots maintain page tables that are not actually mapped, and are just used to traverse quickly to determine if the mid level page tables need to be installed. When the memory be mirrored needs to actually be changed, calls can be made to via x86_ops. private KVM page fault | | | V | private GPA | CPU protected EPTP | | | V | V mirror PT root | external PT root | | | V | V mirror PT --hook to propagate-->external PT | | | \--------------------+------\ | | | | | V V | private guest page | | non-encrypted memory | encrypted memory | Leave calling out to actually update the private page tables that are being mirrored for later changes. Just implement the handling of MMU operations on to mirrored roots. In order to direct operations to correct root, add root types KVM_DIRECT_ROOTS and KVM_MIRROR_ROOTS. Tie the usage of mirrored/direct roots to private/shared with conditionals. It could also be implemented by making the kvm_tdp_mmu_root_types and kvm_gfn_range_filter enum bits line up such that conversion could be a direct assignment with a case. Don't do this because the mapping of private to mirrored is confusing enough. So it is worth not hiding the logic in type casting. Cleanup the mirror root in kvm_mmu_destroy() instead of the normal place in kvm_mmu_free_roots(), because the private root that is being cannot be rebuilt like a normal root. It needs to persist for the lifetime of the VM. The TDX module will also need to be provided with page tables to use for the actual mapping being mirrored by the mirrored page tables. Allocate these in the mapping path using the recently added kvm_mmu_alloc_external_spt(). Don't support 2M page for now. This is avoided by forcing 4k pages in the fault. Add a KVM_BUG_ON() to verify. Signed-off-by: Isaku Yamahata <isaku.yamahata@intel.com> Co-developed-by: Kai Huang <kai.huang@intel.com> Signed-off-by: Kai Huang <kai.huang@intel.com> Co-developed-by: Yan Zhao <yan.y.zhao@intel.com> Signed-off-by: Yan Zhao <yan.y.zhao@intel.com> Co-developed-by: Rick Edgecombe <rick.p.edgecombe@intel.com> Signed-off-by: Rick Edgecombe <rick.p.edgecombe@intel.com> Message-ID: <20240718211230.1492011-13-rick.p.edgecombe@intel.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
1 parent 00d98dd commit fabaa76

File tree

5 files changed

+91
-12
lines changed

5 files changed

+91
-12
lines changed

arch/x86/include/asm/kvm_host.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -459,6 +459,7 @@ struct kvm_mmu {
459459
int (*sync_spte)(struct kvm_vcpu *vcpu,
460460
struct kvm_mmu_page *sp, int i);
461461
struct kvm_mmu_root_info root;
462+
hpa_t mirror_root_hpa;
462463
union kvm_cpu_role cpu_role;
463464
union kvm_mmu_page_role root_role;
464465

arch/x86/kvm/mmu.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,15 @@ void kvm_mmu_track_write(struct kvm_vcpu *vcpu, gpa_t gpa, const u8 *new,
104104

105105
static inline int kvm_mmu_reload(struct kvm_vcpu *vcpu)
106106
{
107+
/*
108+
* Checking root.hpa is sufficient even when KVM has mirror root.
109+
* We can have either:
110+
* (1) mirror_root_hpa = INVALID_PAGE, root.hpa = INVALID_PAGE
111+
* (2) mirror_root_hpa = root, root.hpa = INVALID_PAGE
112+
* (3) mirror_root_hpa = root1, root.hpa = root2
113+
* We don't ever have:
114+
* mirror_root_hpa = INVALID_PAGE, root.hpa = root
115+
*/
107116
if (likely(vcpu->arch.mmu->root.hpa != INVALID_PAGE))
108117
return 0;
109118

@@ -297,4 +306,11 @@ static inline gfn_t kvm_gfn_direct_bits(const struct kvm *kvm)
297306
{
298307
return kvm->arch.gfn_direct_bits;
299308
}
309+
310+
static inline bool kvm_is_addr_direct(struct kvm *kvm, gpa_t gpa)
311+
{
312+
gpa_t gpa_direct_bits = gfn_to_gpa(kvm_gfn_direct_bits(kvm));
313+
314+
return !gpa_direct_bits || (gpa & gpa_direct_bits);
315+
}
300316
#endif

arch/x86/kvm/mmu/mmu.c

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3664,7 +3664,10 @@ static int mmu_alloc_direct_roots(struct kvm_vcpu *vcpu)
36643664
int r;
36653665

36663666
if (tdp_mmu_enabled) {
3667-
kvm_tdp_mmu_alloc_root(vcpu);
3667+
if (kvm_has_mirrored_tdp(vcpu->kvm) &&
3668+
!VALID_PAGE(mmu->mirror_root_hpa))
3669+
kvm_tdp_mmu_alloc_root(vcpu, true);
3670+
kvm_tdp_mmu_alloc_root(vcpu, false);
36683671
return 0;
36693672
}
36703673

@@ -6281,6 +6284,7 @@ static int __kvm_mmu_create(struct kvm_vcpu *vcpu, struct kvm_mmu *mmu)
62816284

62826285
mmu->root.hpa = INVALID_PAGE;
62836286
mmu->root.pgd = 0;
6287+
mmu->mirror_root_hpa = INVALID_PAGE;
62846288
for (i = 0; i < KVM_MMU_NUM_PREV_ROOTS; i++)
62856289
mmu->prev_roots[i] = KVM_MMU_ROOT_INFO_INVALID;
62866290

@@ -7229,6 +7233,12 @@ int kvm_mmu_vendor_module_init(void)
72297233
void kvm_mmu_destroy(struct kvm_vcpu *vcpu)
72307234
{
72317235
kvm_mmu_unload(vcpu);
7236+
if (tdp_mmu_enabled) {
7237+
read_lock(&vcpu->kvm->mmu_lock);
7238+
mmu_free_root_page(vcpu->kvm, &vcpu->arch.mmu->mirror_root_hpa,
7239+
NULL);
7240+
read_unlock(&vcpu->kvm->mmu_lock);
7241+
}
72327242
free_mmu_pages(&vcpu->arch.root_mmu);
72337243
free_mmu_pages(&vcpu->arch.guest_mmu);
72347244
mmu_free_memory_caches(vcpu);

arch/x86/kvm/mmu/tdp_mmu.c

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,9 @@ static bool tdp_mmu_root_match(struct kvm_mmu_page *root,
101101
if (root->role.invalid && !(types & KVM_INVALID_ROOTS))
102102
return false;
103103

104-
return true;
104+
if (likely(!is_mirror_sp(root)))
105+
return types & KVM_DIRECT_ROOTS;
106+
return types & KVM_MIRROR_ROOTS;
105107
}
106108

107109
/*
@@ -236,14 +238,17 @@ static void tdp_mmu_init_child_sp(struct kvm_mmu_page *child_sp,
236238
tdp_mmu_init_sp(child_sp, iter->sptep, iter->gfn, role);
237239
}
238240

239-
void kvm_tdp_mmu_alloc_root(struct kvm_vcpu *vcpu)
241+
void kvm_tdp_mmu_alloc_root(struct kvm_vcpu *vcpu, bool mirror)
240242
{
241243
struct kvm_mmu *mmu = vcpu->arch.mmu;
242244
union kvm_mmu_page_role role = mmu->root_role;
243245
int as_id = kvm_mmu_role_as_id(role);
244246
struct kvm *kvm = vcpu->kvm;
245247
struct kvm_mmu_page *root;
246248

249+
if (mirror)
250+
role.is_mirror = true;
251+
247252
/*
248253
* Check for an existing root before acquiring the pages lock to avoid
249254
* unnecessary serialization if multiple vCPUs are loading a new root.
@@ -295,8 +300,12 @@ void kvm_tdp_mmu_alloc_root(struct kvm_vcpu *vcpu)
295300
* and actually consuming the root if it's invalidated after dropping
296301
* mmu_lock, and the root can't be freed as this vCPU holds a reference.
297302
*/
298-
mmu->root.hpa = __pa(root->spt);
299-
mmu->root.pgd = 0;
303+
if (mirror) {
304+
mmu->mirror_root_hpa = __pa(root->spt);
305+
} else {
306+
mmu->root.hpa = __pa(root->spt);
307+
mmu->root.pgd = 0;
308+
}
300309
}
301310

302311
static void handle_changed_spte(struct kvm *kvm, int as_id, gfn_t gfn,
@@ -1083,8 +1092,8 @@ static int tdp_mmu_split_huge_page(struct kvm *kvm, struct tdp_iter *iter,
10831092
*/
10841093
int kvm_tdp_mmu_map(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault)
10851094
{
1095+
struct kvm_mmu_page *root = tdp_mmu_get_root_for_fault(vcpu, fault);
10861096
struct kvm *kvm = vcpu->kvm;
1087-
struct kvm_mmu_page *root = root_to_sp(vcpu->arch.mmu->root.hpa);
10881097
struct tdp_iter iter;
10891098
struct kvm_mmu_page *sp;
10901099
int ret = RET_PF_RETRY;
@@ -1122,13 +1131,18 @@ int kvm_tdp_mmu_map(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault)
11221131
*/
11231132
sp = tdp_mmu_alloc_sp(vcpu);
11241133
tdp_mmu_init_child_sp(sp, &iter);
1134+
if (is_mirror_sp(sp))
1135+
kvm_mmu_alloc_external_spt(vcpu, sp);
11251136

11261137
sp->nx_huge_page_disallowed = fault->huge_page_disallowed;
11271138

1128-
if (is_shadow_present_pte(iter.old_spte))
1139+
if (is_shadow_present_pte(iter.old_spte)) {
1140+
/* Don't support large page for mirrored roots (TDX) */
1141+
KVM_BUG_ON(is_mirror_sptep(iter.sptep), vcpu->kvm);
11291142
r = tdp_mmu_split_huge_page(kvm, &iter, sp, true);
1130-
else
1143+
} else {
11311144
r = tdp_mmu_link_sp(kvm, &iter, sp, true);
1145+
}
11321146

11331147
/*
11341148
* Force the guest to retry if installing an upper level SPTE
@@ -1773,7 +1787,8 @@ int kvm_tdp_mmu_get_walk(struct kvm_vcpu *vcpu, u64 addr, u64 *sptes,
17731787
u64 *kvm_tdp_mmu_fast_pf_get_last_sptep(struct kvm_vcpu *vcpu, gfn_t gfn,
17741788
u64 *spte)
17751789
{
1776-
struct kvm_mmu_page *root = root_to_sp(vcpu->arch.mmu->root.hpa);
1790+
/* Fast pf is not supported for mirrored roots */
1791+
struct kvm_mmu_page *root = tdp_mmu_get_root(vcpu, KVM_DIRECT_ROOTS);
17771792
struct tdp_iter iter;
17781793
tdp_ptep_t sptep = NULL;
17791794

arch/x86/kvm/mmu/tdp_mmu.h

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
void kvm_mmu_init_tdp_mmu(struct kvm *kvm);
1111
void kvm_mmu_uninit_tdp_mmu(struct kvm *kvm);
1212

13-
void kvm_tdp_mmu_alloc_root(struct kvm_vcpu *vcpu);
13+
void kvm_tdp_mmu_alloc_root(struct kvm_vcpu *vcpu, bool private);
1414

1515
__must_check static inline bool kvm_tdp_mmu_get_root(struct kvm_mmu_page *root)
1616
{
@@ -21,11 +21,48 @@ void kvm_tdp_mmu_put_root(struct kvm *kvm, struct kvm_mmu_page *root);
2121

2222
enum kvm_tdp_mmu_root_types {
2323
KVM_INVALID_ROOTS = BIT(0),
24-
25-
KVM_VALID_ROOTS = BIT(1),
24+
KVM_DIRECT_ROOTS = BIT(1),
25+
KVM_MIRROR_ROOTS = BIT(2),
26+
KVM_VALID_ROOTS = KVM_DIRECT_ROOTS | KVM_MIRROR_ROOTS,
2627
KVM_ALL_ROOTS = KVM_VALID_ROOTS | KVM_INVALID_ROOTS,
2728
};
2829

30+
static inline enum kvm_tdp_mmu_root_types kvm_gfn_range_filter_to_root_types(struct kvm *kvm,
31+
enum kvm_gfn_range_filter process)
32+
{
33+
enum kvm_tdp_mmu_root_types ret = 0;
34+
35+
if (!kvm_has_mirrored_tdp(kvm))
36+
return KVM_DIRECT_ROOTS;
37+
38+
if (process & KVM_FILTER_PRIVATE)
39+
ret |= KVM_MIRROR_ROOTS;
40+
if (process & KVM_FILTER_SHARED)
41+
ret |= KVM_DIRECT_ROOTS;
42+
43+
WARN_ON_ONCE(!ret);
44+
45+
return ret;
46+
}
47+
48+
static inline struct kvm_mmu_page *tdp_mmu_get_root_for_fault(struct kvm_vcpu *vcpu,
49+
struct kvm_page_fault *fault)
50+
{
51+
if (unlikely(!kvm_is_addr_direct(vcpu->kvm, fault->addr)))
52+
return root_to_sp(vcpu->arch.mmu->mirror_root_hpa);
53+
54+
return root_to_sp(vcpu->arch.mmu->root.hpa);
55+
}
56+
57+
static inline struct kvm_mmu_page *tdp_mmu_get_root(struct kvm_vcpu *vcpu,
58+
enum kvm_tdp_mmu_root_types type)
59+
{
60+
if (unlikely(type == KVM_MIRROR_ROOTS))
61+
return root_to_sp(vcpu->arch.mmu->mirror_root_hpa);
62+
63+
return root_to_sp(vcpu->arch.mmu->root.hpa);
64+
}
65+
2966
bool kvm_tdp_mmu_zap_leafs(struct kvm *kvm, gfn_t start, gfn_t end, bool flush);
3067
bool kvm_tdp_mmu_zap_sp(struct kvm *kvm, struct kvm_mmu_page *sp);
3168
void kvm_tdp_mmu_zap_all(struct kvm *kvm);

0 commit comments

Comments
 (0)