mirror of
https://github.com/xen-project/xen.git
synced 2026-10-29 10:40:03 +01:00
x86/vmx: Properly flush the TLB when an altp2m is modified
Modifications to an altp2m mark the p2m as needing flushing, but this was never wired up in the return-to-guest path. As a result, stale TLB entries can remain after resuming the guest. In practice, this manifests as a missing EPT_VIOLATION or #VE exception when the guest subsequently accesses a page which has had its permissions reduced. vmx_vmenter_helper() now has 11 p2ms to potentially invalidate, but issuing 11 INVEPT instructions isn't clever. Instead, count how many contexts need invalidating, and use INVEPT_ALL_CONTEXT if two or more are in need of flushing. This doesn't have an XSA because altp2m is not yet a security-supported feature. Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com> Reviewed-by: Razvan Cojocaru <rcojocaru@bitdefender.com> Reviewed-by: Jan Beulich <jbeulich@suse.com> Release-acked-by: Juergen Gross <jgross@suse.com> Acked-by: Kevin Tian <kevin.tian@intel.com>
This commit is contained in:
1 parent
0ec9b4ef31
commit
69f7643df6
1 file changed
+31
-5
@@ -4273,6 +4273,7 @@ static void lbr_fixup(void)
|
||||
bool vmx_vmenter_helper(const struct cpu_user_regs *regs)
|
||||
{
|
||||
struct vcpu *curr = current;
|
||||
struct domain *currd = curr->domain;
|
||||
u32 new_asid, old_asid;
|
||||
struct hvm_vcpu_asid *p_asid;
|
||||
bool_t need_flush;
|
||||
@@ -4319,17 +4320,42 @@ bool vmx_vmenter_helper(const struct cpu_user_regs *regs)
|
||||
|
||||
if ( paging_mode_hap(curr->domain) )
|
||||
{
|
||||
struct ept_data *ept = &p2m_get_hostp2m(curr->domain)->ept;
|
||||
struct ept_data *ept = &p2m_get_hostp2m(currd)->ept;
|
||||
unsigned int cpu = smp_processor_id();
|
||||
unsigned int inv = 0; /* None => Single => All */
|
||||
struct ept_data *single = NULL; /* Single eptp, iff inv == 1 */
|
||||
|
||||
if ( cpumask_test_cpu(cpu, ept->invalidate) )
|
||||
{
|
||||
cpumask_clear_cpu(cpu, ept->invalidate);
|
||||
if ( nestedhvm_enabled(curr->domain) )
|
||||
__invept(INVEPT_ALL_CONTEXT, 0);
|
||||
else
|
||||
__invept(INVEPT_SINGLE_CONTEXT, ept->eptp);
|
||||
|
||||
/* Automatically invalidate all contexts if nested. */
|
||||
inv += 1 + nestedhvm_enabled(currd);
|
||||
single = ept;
|
||||
}
|
||||
|
||||
if ( altp2m_active(currd) )
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for ( i = 0; i < MAX_ALTP2M; ++i )
|
||||
{
|
||||
if ( currd->arch.altp2m_eptp[i] == mfn_x(INVALID_MFN) )
|
||||
continue;
|
||||
|
||||
ept = &currd->arch.altp2m_p2m[i]->ept;
|
||||
if ( cpumask_test_cpu(cpu, ept->invalidate) )
|
||||
{
|
||||
cpumask_clear_cpu(cpu, ept->invalidate);
|
||||
inv++;
|
||||
single = ept;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( inv )
|
||||
__invept(inv == 1 ? INVEPT_SINGLE_CONTEXT : INVEPT_ALL_CONTEXT,
|
||||
inv == 1 ? single->eptp : 0);
|
||||
}
|
||||
|
||||
out:
|
||||
|
||||