mirror of
https://github.com/torvalds/linux.git
synced 2026-09-23 13:14:02 +02:00
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:
parent
7ccb75f2bf
commit
72f734a435
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user