KVM: x86/mmu: pull page format to a new struct

Structs kvm_mmu ("build page tables") and kvm_pagewalk ("walk page
tables") share a piece of common functionality, namely "looking at PTEs".
Both of them have code to check is a specific access (described by
PFERR_* constants) is allowed by a PTE, and both of them also validate
that reserved bits are zero on the respective page tables page tables;
for SPTEs the code is only there to check internal consistency, but in
this case it is indeed shared via struct rsvd_bits_validate.

In preparation for sharing more PTE parsing code between struct
kvm_pagewalk and struct kvm_mmu, create a new struct that contains all
precalculated tables, including the data that is extracted from the
CPU role.  For now only struct kvm_pagewalk uses it.

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
This commit is contained in:
Paolo Bonzini 2026-05-11 06:58:40 -04:00
parent 7ccb75f2bf
commit 72f734a435
4 changed files with 32 additions and 26 deletions

View File

@ -516,16 +516,7 @@ struct kvm_page_fault;
* and 2-level 32-bit). The kvm_pagewalk structure abstracts the details of the
* current mmu mode.
*/
struct kvm_pagewalk {
unsigned long (*get_guest_pgd)(struct kvm_vcpu *vcpu);
u64 (*get_pdptr)(struct kvm_vcpu *vcpu, int index);
void (*inject_page_fault)(struct kvm_vcpu *vcpu,
struct x86_exception *fault,
bool from_hardware);
gpa_t (*gva_to_gpa)(struct kvm_vcpu *vcpu, struct kvm_pagewalk *w,
gpa_t gva_or_gpa, u64 access,
struct x86_exception *exception);
union kvm_cpu_role cpu_role;
struct kvm_page_format {
struct rsvd_bits_validate guest_rsvd_check;
/*
@ -544,6 +535,20 @@ struct kvm_pagewalk {
u16 permissions[16];
};
struct kvm_pagewalk {
unsigned long (*get_guest_pgd)(struct kvm_vcpu *vcpu);
u64 (*get_pdptr)(struct kvm_vcpu *vcpu, int index);
void (*inject_page_fault)(struct kvm_vcpu *vcpu,
struct x86_exception *fault,
bool from_hardware);
gpa_t (*gva_to_gpa)(struct kvm_vcpu *vcpu, struct kvm_pagewalk *w,
gpa_t gva_or_gpa, u64 access,
struct x86_exception *exception);
union kvm_cpu_role cpu_role;
struct kvm_page_format fmt;
};
struct kvm_mmu {
int (*page_fault)(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault);
int (*sync_spte)(struct kvm_vcpu *vcpu,

View File

@ -294,15 +294,16 @@ static inline u8 permission_fault(struct kvm_vcpu *vcpu, struct kvm_pagewalk *w,
u64 implicit_access = access & PFERR_IMPLICIT_ACCESS;
bool not_smap = ((rflags & X86_EFLAGS_AC) | implicit_access) == X86_EFLAGS_AC;
int index = (pfec | (not_smap ? PFERR_RSVD_MASK : 0)) >> 1;
struct kvm_page_format *fmt = &w->fmt;
u32 errcode = PFERR_PRESENT_MASK;
bool fault;
kvm_mmu_refresh_passthrough_bits(vcpu, w);
fault = (w->permissions[index] >> pte_access) & 1;
fault = (fmt->permissions[index] >> pte_access) & 1;
WARN_ON_ONCE(pfec & (PFERR_PK_MASK | PFERR_SS_MASK | PFERR_RSVD_MASK));
if (unlikely(w->pkru_mask)) {
if (unlikely(fmt->pkru_mask)) {
u32 pkru_bits, offset;
/*
@ -316,7 +317,7 @@ static inline u8 permission_fault(struct kvm_vcpu *vcpu, struct kvm_pagewalk *w,
/* clear present bit, replace PFEC.RSVD with ACC_USER_MASK. */
offset = (pfec & ~1) | ((pte_access & PT_USER_MASK) ? PFERR_RSVD_MASK : 0);
pkru_bits &= w->pkru_mask >> offset;
pkru_bits &= fmt->pkru_mask >> offset;
errcode |= -pkru_bits & PFERR_PK_MASK;
fault |= (pkru_bits != 0);
}

View File

@ -5479,7 +5479,7 @@ static void __reset_rsvds_bits_mask(struct rsvd_bits_validate *rsvd_check,
static void reset_guest_rsvds_bits_mask(struct kvm_vcpu *vcpu,
struct kvm_pagewalk *w)
{
__reset_rsvds_bits_mask(&w->guest_rsvd_check,
__reset_rsvds_bits_mask(&w->fmt.guest_rsvd_check,
vcpu->arch.reserved_gpa_bits,
w->cpu_role.base.level, is_efer_nx(w),
guest_cpu_cap_has(vcpu, X86_FEATURE_GBPAGES),
@ -5528,7 +5528,7 @@ static void __reset_rsvds_bits_mask_ept(struct rsvd_bits_validate *rsvd_check,
static void reset_rsvds_bits_mask_ept(struct kvm_vcpu *vcpu,
bool execonly, int huge_page_level)
{
__reset_rsvds_bits_mask_ept(&vcpu->arch.ngpa_walk.guest_rsvd_check,
__reset_rsvds_bits_mask_ept(&vcpu->arch.ngpa_walk.fmt.guest_rsvd_check,
vcpu->arch.reserved_gpa_bits, execonly,
huge_page_level);
}
@ -5682,7 +5682,7 @@ static void update_permission_bitmask(struct kvm_pagewalk *pw, bool tdp, bool ep
* permission_fault() to indicate accesses that are *not* subject to
* SMAP restrictions.
*/
for (index = 0; index < ARRAY_SIZE(pw->permissions); ++index) {
for (index = 0; index < ARRAY_SIZE(pw->fmt.permissions); ++index) {
unsigned pfec = index << 1;
/*
@ -5756,7 +5756,7 @@ static void update_permission_bitmask(struct kvm_pagewalk *pw, bool tdp, bool ep
smapf = (pfec & (PFERR_RSVD_MASK|PFERR_FETCH_MASK)) ? 0 : kf;
}
pw->permissions[index] = ff | uf | wf | rf | smapf;
pw->fmt.permissions[index] = ff | uf | wf | rf | smapf;
}
}
@ -5789,14 +5789,14 @@ static void update_pkru_bitmask(struct kvm_pagewalk *w)
unsigned bit;
bool wp;
w->pkru_mask = 0;
w->fmt.pkru_mask = 0;
if (!is_cr4_pke(w))
return;
wp = is_cr0_wp(w);
for (bit = 0; bit < ARRAY_SIZE(w->permissions); ++bit) {
for (bit = 0; bit < ARRAY_SIZE(w->fmt.permissions); ++bit) {
unsigned pfec, pkey_bits;
bool check_pkey, check_write, ff, uf, wf, pte_user;
@ -5824,7 +5824,7 @@ static void update_pkru_bitmask(struct kvm_pagewalk *w)
/* PKRU.WD stops write access. */
pkey_bits |= (!!check_write) << 1;
w->pkru_mask |= (pkey_bits & 3) << pfec;
w->fmt.pkru_mask |= (pkey_bits & 3) << pfec;
}
}
@ -6113,7 +6113,7 @@ void kvm_init_shadow_ept_mmu(struct kvm_vcpu *vcpu, bool execonly,
context->sync_spte = ept_sync_spte;
update_permission_bitmask(ngpa_walk, true, true);
ngpa_walk->pkru_mask = 0;
ngpa_walk->fmt.pkru_mask = 0;
reset_rsvds_bits_mask_ept(vcpu, execonly, huge_page_level);
reset_ept_shadow_zero_bits_mask(context, execonly);
}

View File

@ -147,10 +147,10 @@ static bool FNAME(is_bad_mt_xwr)(struct rsvd_bits_validate *rsvd_check, u64 gpte
#endif
}
static bool FNAME(is_rsvd_bits_set)(struct kvm_pagewalk *w, u64 gpte, int level)
static bool FNAME(is_rsvd_bits_set)(struct kvm_page_format *fmt, u64 gpte, int level)
{
return __is_rsvd_bits_set(&w->guest_rsvd_check, gpte, level) ||
FNAME(is_bad_mt_xwr)(&w->guest_rsvd_check, gpte);
return __is_rsvd_bits_set(&fmt->guest_rsvd_check, gpte, level) ||
FNAME(is_bad_mt_xwr)(&fmt->guest_rsvd_check, gpte);
}
static bool FNAME(prefetch_invalid_gpte)(struct kvm_vcpu *vcpu,
@ -167,7 +167,7 @@ static bool FNAME(prefetch_invalid_gpte)(struct kvm_vcpu *vcpu,
!(gpte & PT_GUEST_ACCESSED_MASK))
goto no_present;
if (FNAME(is_rsvd_bits_set)(w, gpte, PG_LEVEL_4K))
if (FNAME(is_rsvd_bits_set)(&w->fmt, gpte, PG_LEVEL_4K))
goto no_present;
return false;
@ -427,7 +427,7 @@ static int FNAME(walk_addr_generic)(struct guest_walker *walker,
if (unlikely(!FNAME(is_present_gpte)(w, pte)))
goto error;
if (unlikely(FNAME(is_rsvd_bits_set)(w, pte, walker->level))) {
if (unlikely(FNAME(is_rsvd_bits_set)(&w->fmt, pte, walker->level))) {
errcode = PFERR_RSVD_MASK | PFERR_PRESENT_MASK;
goto error;
}