mirror of
https://github.com/xen-project/xen.git
synced 2026-10-28 02:01:24 +01:00
Neither current nor curr_vcpu per-CPU fields accurately track which
page-tables are loaded. There are corner cases when dealing with shadow
paging failures that switch to the idle vCPU page-tables without changing
current or curr_vcpu per-CPU fields.
Introduce a new per-CPU field that attempts to track which vCPU page-tables
are loaded. Update such tracking when cr3 is changed, and do so in a
region with interrupts disabled, as to avoid handling interrupts with a
mismatch between the vCPU tracking field and the loaded page-tables.
As a result of this newly more accurate tracking the mapcache override
functionality can be removed: the dom0 PV builder was the only user of it,
and it's updated here to properly signal which vCPU page-tables are loaded
in the calls to switch_cr3_cr4().
Note the EFI page-tables have the Xen owned L4 slots copied from the idle
page-tables, so for the effects of the mapcache the EFI page-tables could
use the idle mapcache if it had one. Pass the idle vCPU in the
switch_cr3_cr4() call that switches to the runtime EFI page-tables.
There are known issues with the use of mapcache in NMI context. This patch
does not alter the behaviour.
This is CVE-2026-42488 / XSA-494.
Fixes: fb0ff49fe9 ("x86/shadow: defer releasing of PV's top-level shadow reference")
Signed-off-by: Roger Pau Monné <roger.pau@citrix.com>
Acked-by: Andrew Cooper <andrew.cooper3@citrix.com>
197 lines
6.4 KiB
C
197 lines
6.4 KiB
C
/******************************************************************************
|
|
* flushtlb.h
|
|
*
|
|
* TLB flushes are timestamped using a global virtual 'clock' which ticks
|
|
* on any TLB flush on any processor.
|
|
*
|
|
* Copyright (c) 2003-2004, K A Fraser
|
|
*/
|
|
|
|
#ifndef __FLUSHTLB_H__
|
|
#define __FLUSHTLB_H__
|
|
|
|
#include <xen/mm.h>
|
|
#include <xen/percpu.h>
|
|
#include <xen/smp.h>
|
|
#include <xen/types.h>
|
|
|
|
/* The current time as shown by the virtual TLB clock. */
|
|
extern u32 tlbflush_clock;
|
|
|
|
/* Time at which each CPU's TLB was last flushed. */
|
|
DECLARE_PER_CPU(u32, tlbflush_time);
|
|
|
|
/* TLB clock is in use. */
|
|
extern bool tlb_clk_enabled;
|
|
|
|
static inline uint32_t tlbflush_current_time(void)
|
|
{
|
|
/* Returning 0 from tlbflush_current_time will always force a flush. */
|
|
return tlb_clk_enabled ? tlbflush_clock : 0;
|
|
}
|
|
|
|
static inline void page_set_tlbflush_timestamp(struct page_info *page)
|
|
{
|
|
/* Avoid the write if the TLB clock is disabled. */
|
|
if ( !tlb_clk_enabled )
|
|
return;
|
|
|
|
/*
|
|
* Prevent storing a stale time stamp, which could happen if an update
|
|
* to tlbflush_clock plus a subsequent flush IPI happen between the
|
|
* reading of tlbflush_clock and the writing of the struct page_info
|
|
* field.
|
|
*/
|
|
ASSERT(local_irq_is_enabled());
|
|
local_irq_disable();
|
|
page->tlbflush_timestamp = tlbflush_current_time();
|
|
local_irq_enable();
|
|
}
|
|
|
|
/*
|
|
* @cpu_stamp is the timestamp at last TLB flush for the CPU we are testing.
|
|
* @lastuse_stamp is a timestamp taken when the PFN we are testing was last
|
|
* used for a purpose that may have caused the CPU's TLB to become tainted.
|
|
*/
|
|
static inline bool NEED_FLUSH(u32 cpu_stamp, u32 lastuse_stamp)
|
|
{
|
|
u32 curr_time = tlbflush_current_time();
|
|
/*
|
|
* Two cases:
|
|
* 1. During a wrap, the clock ticks over to 0 while CPUs catch up. For
|
|
* safety during this period, we force a flush if @curr_time == 0.
|
|
* 2. Otherwise, we look to see if @cpu_stamp <= @lastuse_stamp.
|
|
* To detect false positives because @cpu_stamp has wrapped, we
|
|
* also check @curr_time. If less than @lastuse_stamp we definitely
|
|
* wrapped, so there's no need for a flush (one is forced every wrap).
|
|
*/
|
|
return ((curr_time == 0) ||
|
|
((cpu_stamp <= lastuse_stamp) &&
|
|
(lastuse_stamp <= curr_time)));
|
|
}
|
|
|
|
/*
|
|
* Filter the given set of CPUs, removing those that definitely flushed their
|
|
* TLB since @page_timestamp.
|
|
*/
|
|
static inline void tlbflush_filter(cpumask_t *mask, uint32_t page_timestamp)
|
|
{
|
|
unsigned int cpu;
|
|
|
|
/* Short-circuit: there's no need to iterate if the clock is disabled. */
|
|
if ( !tlb_clk_enabled )
|
|
return;
|
|
|
|
for_each_cpu ( cpu, mask )
|
|
if ( !NEED_FLUSH(per_cpu(tlbflush_time, cpu), page_timestamp) )
|
|
__cpumask_clear_cpu(cpu, mask);
|
|
}
|
|
|
|
void cf_check new_tlbflush_clock_period(void);
|
|
|
|
/* Read pagetable base. */
|
|
static inline unsigned long read_cr3(void)
|
|
{
|
|
unsigned long cr3;
|
|
__asm__ __volatile__ (
|
|
"mov %%cr3, %0" : "=r" (cr3) : );
|
|
return cr3;
|
|
}
|
|
|
|
static inline void invlpg(const void *p)
|
|
{
|
|
asm volatile ( "invlpg %0" :: "m" (*(const char *)p) : "memory" );
|
|
}
|
|
|
|
/* Write pagetable base and implicitly tick the tlbflush clock. */
|
|
void switch_cr3_cr4(struct vcpu *v, unsigned long cr3, unsigned long cr4);
|
|
|
|
/* flush_* flag fields: */
|
|
/*
|
|
* Area to flush: 2^flush_order pages. Default is flush entire address space.
|
|
* NB. Multi-page areas do not need to have been mapped with a superpage.
|
|
*/
|
|
#define FLUSH_ORDER_MASK 0xff
|
|
#define FLUSH_ORDER(x) ((x)+1)
|
|
/* Flush TLBs (or parts thereof) */
|
|
#define FLUSH_TLB 0x100
|
|
/* Flush TLBs (or parts thereof) including global mappings */
|
|
#define FLUSH_TLB_GLOBAL 0x200
|
|
/* Flush data caches */
|
|
#define FLUSH_CACHE_EVICT 0x400
|
|
/* VA for the flush has a valid mapping */
|
|
#define FLUSH_VA_VALID 0x800
|
|
/* Flush CPU state */
|
|
#define FLUSH_VCPU_STATE 0x1000
|
|
/* Flush the per-cpu root page table */
|
|
#define FLUSH_ROOT_PGTBL 0x2000
|
|
#if CONFIG_HVM
|
|
/* Flush all HVM guests linear TLB (using ASID/VPID) */
|
|
#define FLUSH_HVM_ASID_CORE 0x4000
|
|
#else
|
|
#define FLUSH_HVM_ASID_CORE 0
|
|
#endif
|
|
#if defined(CONFIG_PV) || defined(CONFIG_SHADOW_PAGING)
|
|
/*
|
|
* Adding this to the flags passed to flush_area_mask will prevent using the
|
|
* assisted flush without having any other side effect.
|
|
*/
|
|
# define FLUSH_NO_ASSIST 0x8000
|
|
#else
|
|
# define FLUSH_NO_ASSIST 0
|
|
#endif
|
|
/* Write back data cache contents */
|
|
#define FLUSH_CACHE_WRITEBACK 0x10000
|
|
|
|
/* Flush local TLBs/caches. */
|
|
unsigned int flush_area_local(const void *va, unsigned int flags);
|
|
#define flush_local(flags) flush_area_local(NULL, flags)
|
|
|
|
/* Flush specified CPUs' TLBs/caches */
|
|
void flush_area_mask(const cpumask_t *mask, const void *va,
|
|
unsigned int flags);
|
|
#define flush_mask(mask, flags) flush_area_mask(mask, NULL, flags)
|
|
|
|
/* Flush all CPUs' TLBs/caches */
|
|
#define flush_area_all(va, flags) flush_area_mask(&cpu_online_map, va, flags)
|
|
#define flush_all(flags) flush_mask(&cpu_online_map, flags)
|
|
|
|
/* Flush local TLBs */
|
|
#define flush_tlb_local() \
|
|
flush_local(FLUSH_TLB)
|
|
#define flush_tlb_one_local(v) \
|
|
flush_area_local((const void *)(v), FLUSH_TLB|FLUSH_ORDER(0))
|
|
|
|
/* Flush specified CPUs' TLBs */
|
|
#define flush_tlb_mask(mask) \
|
|
flush_mask(mask, FLUSH_TLB)
|
|
#define flush_tlb_one_mask(mask,v) \
|
|
flush_area_mask(mask, (const void *)(v), FLUSH_TLB|FLUSH_ORDER(0))
|
|
|
|
/*
|
|
* Make the common code TLB flush helper disallow the usage of any flush
|
|
* assistance in order to be on the safe side and interrupt remote processors
|
|
* requiring a flush. Note that not all calls from common code strictly require
|
|
* this.
|
|
*/
|
|
#define arch_flush_tlb_mask(mask) flush_mask(mask, FLUSH_TLB | FLUSH_NO_ASSIST)
|
|
|
|
/* Flush all CPUs' TLBs */
|
|
#define flush_tlb_all() \
|
|
flush_tlb_mask(&cpu_online_map)
|
|
#define flush_tlb_one_all(v) \
|
|
flush_tlb_one_mask(&cpu_online_map, v)
|
|
|
|
#define flush_root_pgtbl_domain(d) \
|
|
{ \
|
|
if ( is_pv_domain(d) && (d)->arch.pv.xpti ) \
|
|
flush_mask((d)->dirty_cpumask, FLUSH_ROOT_PGTBL); \
|
|
}
|
|
|
|
static inline void flush_page_to_ram(unsigned long mfn, bool sync_icache) {}
|
|
|
|
unsigned int guest_flush_tlb_flags(const struct domain *d);
|
|
void guest_flush_tlb_mask(const struct domain *d, const cpumask_t *mask);
|
|
|
|
#endif /* __FLUSHTLB_H__ */
|