Skip to content

Commit 22836e1

Browse files
yamahatabonzini
authored andcommitted
KVM: TDX: Handle TLB tracking for TDX
Handle TLB tracking for TDX by introducing function tdx_track() for private memory TLB tracking and implementing flush_tlb* hooks to flush TLBs for shared memory. Introduce function tdx_track() to do TLB tracking on private memory, which basically does two things: calling TDH.MEM.TRACK to increase TD epoch and kicking off all vCPUs. The private EPT will then be flushed when each vCPU re-enters the TD. This function is unused temporarily in this patch and will be called on a page-by-page basis on removal of private guest page in a later patch. In earlier revisions, tdx_track() relied on an atomic counter to coordinate the synchronization between the actions of kicking off vCPUs, incrementing the TD epoch, and the vCPUs waiting for the incremented TD epoch after being kicked off. However, the core MMU only actually needs to call tdx_track() while aleady under a write mmu_lock. So this sychnonization can be made to be unneeded. vCPUs are kicked off only after the successful execution of TDH.MEM.TRACK, eliminating the need for vCPUs to wait for TDH.MEM.TRACK completion after being kicked off. tdx_track() is therefore able to send requests KVM_REQ_OUTSIDE_GUEST_MODE rather than KVM_REQ_TLB_FLUSH. Hooks for flush_remote_tlb and flush_remote_tlbs_range are not necessary for TDX, as tdx_track() will handle TLB tracking of private memory on page-by-page basis when private guest pages are removed. There is no need to invoke tdx_track() again in kvm_flush_remote_tlbs() even after changes to the mirrored page table. For hooks flush_tlb_current and flush_tlb_all, which are invoked during kvm_mmu_load() and vcpu load for normal VMs, let VMM to flush all EPTs in the two hooks for simplicity, since TDX does not depend on the two hooks to notify TDX module to flush private EPT in those cases. Signed-off-by: Isaku Yamahata <isaku.yamahata@intel.com> Co-developed-by: Rick Edgecombe <rick.p.edgecombe@intel.com> Signed-off-by: Rick Edgecombe <rick.p.edgecombe@intel.com> Co-developed-by: Yan Zhao <yan.y.zhao@intel.com> Signed-off-by: Yan Zhao <yan.y.zhao@intel.com> Message-ID: <20241112073753.22228-1-yan.y.zhao@intel.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
1 parent 7d10ffb commit 22836e1

File tree

3 files changed

+125
-4
lines changed

3 files changed

+125
-4
lines changed

arch/x86/kvm/vmx/main.c

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,42 @@ static void vt_vcpu_reset(struct kvm_vcpu *vcpu, bool init_event)
9999
vmx_vcpu_reset(vcpu, init_event);
100100
}
101101

102+
static void vt_flush_tlb_all(struct kvm_vcpu *vcpu)
103+
{
104+
if (is_td_vcpu(vcpu)) {
105+
tdx_flush_tlb_all(vcpu);
106+
return;
107+
}
108+
109+
vmx_flush_tlb_all(vcpu);
110+
}
111+
112+
static void vt_flush_tlb_current(struct kvm_vcpu *vcpu)
113+
{
114+
if (is_td_vcpu(vcpu)) {
115+
tdx_flush_tlb_current(vcpu);
116+
return;
117+
}
118+
119+
vmx_flush_tlb_current(vcpu);
120+
}
121+
122+
static void vt_flush_tlb_gva(struct kvm_vcpu *vcpu, gva_t addr)
123+
{
124+
if (is_td_vcpu(vcpu))
125+
return;
126+
127+
vmx_flush_tlb_gva(vcpu, addr);
128+
}
129+
130+
static void vt_flush_tlb_guest(struct kvm_vcpu *vcpu)
131+
{
132+
if (is_td_vcpu(vcpu))
133+
return;
134+
135+
vmx_flush_tlb_guest(vcpu);
136+
}
137+
102138
static void vt_load_mmu_pgd(struct kvm_vcpu *vcpu, hpa_t root_hpa,
103139
int pgd_level)
104140
{
@@ -190,10 +226,10 @@ struct kvm_x86_ops vt_x86_ops __initdata = {
190226
.set_rflags = vmx_set_rflags,
191227
.get_if_flag = vmx_get_if_flag,
192228

193-
.flush_tlb_all = vmx_flush_tlb_all,
194-
.flush_tlb_current = vmx_flush_tlb_current,
195-
.flush_tlb_gva = vmx_flush_tlb_gva,
196-
.flush_tlb_guest = vmx_flush_tlb_guest,
229+
.flush_tlb_all = vt_flush_tlb_all,
230+
.flush_tlb_current = vt_flush_tlb_current,
231+
.flush_tlb_gva = vt_flush_tlb_gva,
232+
.flush_tlb_guest = vt_flush_tlb_guest,
197233

198234
.vcpu_pre_run = vmx_vcpu_pre_run,
199235
.vcpu_run = vmx_vcpu_run,

arch/x86/kvm/vmx/tdx.c

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include "x86_ops.h"
99
#include "lapic.h"
1010
#include "tdx.h"
11+
#include "vmx.h"
1112
#include "mmu/spte.h"
1213

1314
#pragma GCC poison to_vmx
@@ -524,6 +525,51 @@ void tdx_load_mmu_pgd(struct kvm_vcpu *vcpu, hpa_t root_hpa, int pgd_level)
524525
td_vmcs_write64(to_tdx(vcpu), SHARED_EPT_POINTER, root_hpa);
525526
}
526527

528+
/*
529+
* Ensure shared and private EPTs to be flushed on all vCPUs.
530+
* tdh_mem_track() is the only caller that increases TD epoch. An increase in
531+
* the TD epoch (e.g., to value "N + 1") is successful only if no vCPUs are
532+
* running in guest mode with the value "N - 1".
533+
*
534+
* A successful execution of tdh_mem_track() ensures that vCPUs can only run in
535+
* guest mode with TD epoch value "N" if no TD exit occurs after the TD epoch
536+
* being increased to "N + 1".
537+
*
538+
* Kicking off all vCPUs after that further results in no vCPUs can run in guest
539+
* mode with TD epoch value "N", which unblocks the next tdh_mem_track() (e.g.
540+
* to increase TD epoch to "N + 2").
541+
*
542+
* TDX module will flush EPT on the next TD enter and make vCPUs to run in
543+
* guest mode with TD epoch value "N + 1".
544+
*
545+
* kvm_make_all_cpus_request() guarantees all vCPUs are out of guest mode by
546+
* waiting empty IPI handler ack_kick().
547+
*
548+
* No action is required to the vCPUs being kicked off since the kicking off
549+
* occurs certainly after TD epoch increment and before the next
550+
* tdh_mem_track().
551+
*/
552+
static void __always_unused tdx_track(struct kvm *kvm)
553+
{
554+
struct kvm_tdx *kvm_tdx = to_kvm_tdx(kvm);
555+
u64 err;
556+
557+
/* If TD isn't finalized, it's before any vcpu running. */
558+
if (unlikely(kvm_tdx->state != TD_STATE_RUNNABLE))
559+
return;
560+
561+
lockdep_assert_held_write(&kvm->mmu_lock);
562+
563+
do {
564+
err = tdh_mem_track(&kvm_tdx->td);
565+
} while (unlikely((err & TDX_SEAMCALL_STATUS_MASK) == TDX_OPERAND_BUSY));
566+
567+
if (KVM_BUG_ON(err, kvm))
568+
pr_tdx_error(TDH_MEM_TRACK, err);
569+
570+
kvm_make_all_cpus_request(kvm, KVM_REQ_OUTSIDE_GUEST_MODE);
571+
}
572+
527573
static int tdx_get_capabilities(struct kvm_tdx_cmd *cmd)
528574
{
529575
const struct tdx_sys_info_td_conf *td_conf = &tdx_sysinfo->td_conf;
@@ -1079,6 +1125,41 @@ static int tdx_td_init(struct kvm *kvm, struct kvm_tdx_cmd *cmd)
10791125
return ret;
10801126
}
10811127

1128+
void tdx_flush_tlb_current(struct kvm_vcpu *vcpu)
1129+
{
1130+
/*
1131+
* flush_tlb_current() is invoked when the first time for the vcpu to
1132+
* run or when root of shared EPT is invalidated.
1133+
* KVM only needs to flush shared EPT because the TDX module handles TLB
1134+
* invalidation for private EPT in tdh_vp_enter();
1135+
*
1136+
* A single context invalidation for shared EPT can be performed here.
1137+
* However, this single context invalidation requires the private EPTP
1138+
* rather than the shared EPTP to flush shared EPT, as shared EPT uses
1139+
* private EPTP as its ASID for TLB invalidation.
1140+
*
1141+
* To avoid reading back private EPTP, perform a global invalidation for
1142+
* shared EPT instead to keep this function simple.
1143+
*/
1144+
ept_sync_global();
1145+
}
1146+
1147+
void tdx_flush_tlb_all(struct kvm_vcpu *vcpu)
1148+
{
1149+
/*
1150+
* TDX has called tdx_track() in tdx_sept_remove_private_spte() to
1151+
* ensure that private EPT will be flushed on the next TD enter. No need
1152+
* to call tdx_track() here again even when this callback is a result of
1153+
* zapping private EPT.
1154+
*
1155+
* Due to the lack of the context to determine which EPT has been
1156+
* affected by zapping, invoke invept() directly here for both shared
1157+
* EPT and private EPT for simplicity, though it's not necessary for
1158+
* private EPT.
1159+
*/
1160+
ept_sync_global();
1161+
}
1162+
10821163
int tdx_vm_ioctl(struct kvm *kvm, void __user *argp)
10831164
{
10841165
struct kvm_tdx_cmd tdx_cmd;

arch/x86/kvm/vmx/x86_ops.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,8 @@ void tdx_vcpu_free(struct kvm_vcpu *vcpu);
132132

133133
int tdx_vcpu_ioctl(struct kvm_vcpu *vcpu, void __user *argp);
134134

135+
void tdx_flush_tlb_current(struct kvm_vcpu *vcpu);
136+
void tdx_flush_tlb_all(struct kvm_vcpu *vcpu);
135137
void tdx_load_mmu_pgd(struct kvm_vcpu *vcpu, hpa_t root_hpa, int root_level);
136138
#else
137139
static inline int tdx_vm_init(struct kvm *kvm) { return -EOPNOTSUPP; }
@@ -144,6 +146,8 @@ static inline void tdx_vcpu_free(struct kvm_vcpu *vcpu) {}
144146

145147
static inline int tdx_vcpu_ioctl(struct kvm_vcpu *vcpu, void __user *argp) { return -EOPNOTSUPP; }
146148

149+
static inline void tdx_flush_tlb_current(struct kvm_vcpu *vcpu) {}
150+
static inline void tdx_flush_tlb_all(struct kvm_vcpu *vcpu) {}
147151
static inline void tdx_load_mmu_pgd(struct kvm_vcpu *vcpu, hpa_t root_hpa, int root_level) {}
148152
#endif
149153

0 commit comments

Comments
 (0)