Merge branch kvm-arm64/el2-feature-control into kvmarm-master/next
* kvm-arm64/el2-feature-control: (23 commits) : . : General rework of EL2 features that can be disabled to satisfy : the requirement of migration between heterogeneous hosts: : : - Handle effective RES0 behaviour of undefined registers, making sure : that disabling a feature affects full registeres, and not just : individual control bits. (20250918151402.1665315-1-maz@kernel.org) : : - Allow ID_AA64MMFR1_EL1.{TWED,HCX} to be disabled from userspace. : (20250911114621.3724469-1-yangjinqian1@huawei.com) : : - Turn the NV feature management into a deny-list, and expose : missing features to EL2 guests. : (20250912212258.407350-1-oliver.upton@linux.dev) : . KVM: arm64: nv: Expose up to FEAT_Debugv8p8 to NV-enabled VMs KVM: arm64: nv: Advertise FEAT_TIDCP1 to NV-enabled VMs KVM: arm64: nv: Advertise FEAT_SpecSEI to NV-enabled VMs KVM: arm64: nv: Expose FEAT_TWED to NV-enabled VMs KVM: arm64: nv: Exclude guest's TWED configuration when TWE isn't set KVM: arm64: nv: Expose FEAT_AFP to NV-enabled VMs KVM: arm64: nv: Expose FEAT_ECBHB to NV-enabled VMs KVM: arm64: nv: Expose FEAT_RASv1p1 via RAS_frac KVM: arm64: nv: Expose FEAT_DF2 to NV-enabled VMs KVM: arm64: nv: Don't erroneously claim FEAT_DoubleLock for NV VMs KVM: arm64: nv: Convert masks to denylists in limit_nv_id_reg() KVM: arm64: selftests: Test writes to ID_AA64MMFR1_EL1.{HCX, TWED} KVM: arm64: Make ID_AA64MMFR1_EL1.{HCX, TWED} writable from userspace KVM: arm64: Convert MDCR_EL2 RES0 handling to compute_reg_res0_bits() KVM: arm64: Convert SCTLR_EL1 RES0 handling to compute_reg_res0_bits() KVM: arm64: Enforce absence of FEAT_TCR2 on TCR2_EL2 KVM: arm64: Enforce absence of FEAT_SCTLR2 on SCTLR2_EL{1,2} KVM: arm64: Convert HCR_EL2 RES0 handling to compute_reg_res0_bits() KVM: arm64: Enforce absence of FEAT_HCX on HCRX_EL2 KVM: arm64: Enforce absence of FEAT_FGT2 on FGT2 registers ... Signed-off-by: Marc Zyngier <maz@kernel.org>
This commit is contained in:
commit
46bd74ef07
@ -7,12 +7,22 @@
|
||||
#include <linux/kvm_host.h>
|
||||
#include <asm/sysreg.h>
|
||||
|
||||
/*
|
||||
* Describes the dependencies between a set of bits (or the negation
|
||||
* of a set of RES0 bits) and a feature. The flags indicate how the
|
||||
* data is interpreted.
|
||||
*/
|
||||
struct reg_bits_to_feat_map {
|
||||
u64 bits;
|
||||
union {
|
||||
u64 bits;
|
||||
u64 *res0p;
|
||||
};
|
||||
|
||||
#define NEVER_FGU BIT(0) /* Can trap, but never UNDEF */
|
||||
#define CALL_FUNC BIT(1) /* Needs to evaluate tons of crap */
|
||||
#define FIXED_VALUE BIT(2) /* RAZ/WI or RAO/WI in KVM */
|
||||
#define RES0_POINTER BIT(3) /* Pointer to RES0 value instead of bits */
|
||||
|
||||
unsigned long flags;
|
||||
|
||||
union {
|
||||
@ -28,9 +38,27 @@ struct reg_bits_to_feat_map {
|
||||
};
|
||||
};
|
||||
|
||||
#define __NEEDS_FEAT_3(m, f, id, fld, lim) \
|
||||
/*
|
||||
* Describes the dependencies for a given register:
|
||||
*
|
||||
* @feat_map describes the dependency for the whole register. If the
|
||||
* features the register depends on are not present, the whole
|
||||
* register is effectively RES0.
|
||||
*
|
||||
* @bit_feat_map describes the dependencies for a set of bits in that
|
||||
* register. If the features these bits depend on are not present, the
|
||||
* bits are effectively RES0.
|
||||
*/
|
||||
struct reg_feat_map_desc {
|
||||
const char *name;
|
||||
const struct reg_bits_to_feat_map feat_map;
|
||||
const struct reg_bits_to_feat_map *bit_feat_map;
|
||||
const unsigned int bit_feat_map_sz;
|
||||
};
|
||||
|
||||
#define __NEEDS_FEAT_3(m, f, w, id, fld, lim) \
|
||||
{ \
|
||||
.bits = (m), \
|
||||
.w = (m), \
|
||||
.flags = (f), \
|
||||
.regidx = IDREG_IDX(SYS_ ## id), \
|
||||
.shift = id ##_## fld ## _SHIFT, \
|
||||
@ -39,28 +67,63 @@ struct reg_bits_to_feat_map {
|
||||
.lo_lim = id ##_## fld ##_## lim \
|
||||
}
|
||||
|
||||
#define __NEEDS_FEAT_2(m, f, fun, dummy) \
|
||||
#define __NEEDS_FEAT_2(m, f, w, fun, dummy) \
|
||||
{ \
|
||||
.bits = (m), \
|
||||
.w = (m), \
|
||||
.flags = (f) | CALL_FUNC, \
|
||||
.fval = (fun), \
|
||||
}
|
||||
|
||||
#define __NEEDS_FEAT_1(m, f, fun) \
|
||||
#define __NEEDS_FEAT_1(m, f, w, fun) \
|
||||
{ \
|
||||
.bits = (m), \
|
||||
.w = (m), \
|
||||
.flags = (f) | CALL_FUNC, \
|
||||
.match = (fun), \
|
||||
}
|
||||
|
||||
#define __NEEDS_FEAT_FLAG(m, f, w, ...) \
|
||||
CONCATENATE(__NEEDS_FEAT_, COUNT_ARGS(__VA_ARGS__))(m, f, w, __VA_ARGS__)
|
||||
|
||||
#define NEEDS_FEAT_FLAG(m, f, ...) \
|
||||
CONCATENATE(__NEEDS_FEAT_, COUNT_ARGS(__VA_ARGS__))(m, f, __VA_ARGS__)
|
||||
__NEEDS_FEAT_FLAG(m, f, bits, __VA_ARGS__)
|
||||
|
||||
#define NEEDS_FEAT_FIXED(m, ...) \
|
||||
NEEDS_FEAT_FLAG(m, FIXED_VALUE, __VA_ARGS__, 0)
|
||||
__NEEDS_FEAT_FLAG(m, FIXED_VALUE, bits, __VA_ARGS__, 0)
|
||||
|
||||
#define NEEDS_FEAT_RES0(p, ...) \
|
||||
__NEEDS_FEAT_FLAG(p, RES0_POINTER, res0p, __VA_ARGS__)
|
||||
|
||||
/*
|
||||
* Declare the dependency between a set of bits and a set of features,
|
||||
* generating a struct reg_bit_to_feat_map.
|
||||
*/
|
||||
#define NEEDS_FEAT(m, ...) NEEDS_FEAT_FLAG(m, 0, __VA_ARGS__)
|
||||
|
||||
/*
|
||||
* Declare the dependency between a non-FGT register, a set of
|
||||
* feature, and the set of individual bits it contains. This generates
|
||||
* a struct reg_feat_map_desc.
|
||||
*/
|
||||
#define DECLARE_FEAT_MAP(n, r, m, f) \
|
||||
struct reg_feat_map_desc n = { \
|
||||
.name = #r, \
|
||||
.feat_map = NEEDS_FEAT(~r##_RES0, f), \
|
||||
.bit_feat_map = m, \
|
||||
.bit_feat_map_sz = ARRAY_SIZE(m), \
|
||||
}
|
||||
|
||||
/*
|
||||
* Specialised version of the above for FGT registers that have their
|
||||
* RES0 masks described as struct fgt_masks.
|
||||
*/
|
||||
#define DECLARE_FEAT_MAP_FGT(n, msk, m, f) \
|
||||
struct reg_feat_map_desc n = { \
|
||||
.name = #msk, \
|
||||
.feat_map = NEEDS_FEAT_RES0(&msk.res0, f),\
|
||||
.bit_feat_map = m, \
|
||||
.bit_feat_map_sz = ARRAY_SIZE(m), \
|
||||
}
|
||||
|
||||
#define FEAT_SPE ID_AA64DFR0_EL1, PMSVer, IMP
|
||||
#define FEAT_SPE_FnE ID_AA64DFR0_EL1, PMSVer, V1P2
|
||||
#define FEAT_BRBE ID_AA64DFR0_EL1, BRBE, IMP
|
||||
@ -73,6 +136,7 @@ struct reg_bits_to_feat_map {
|
||||
#define FEAT_AA32EL0 ID_AA64PFR0_EL1, EL0, AARCH32
|
||||
#define FEAT_AA32EL1 ID_AA64PFR0_EL1, EL1, AARCH32
|
||||
#define FEAT_AA64EL1 ID_AA64PFR0_EL1, EL1, IMP
|
||||
#define FEAT_AA64EL2 ID_AA64PFR0_EL1, EL2, IMP
|
||||
#define FEAT_AA64EL3 ID_AA64PFR0_EL1, EL3, IMP
|
||||
#define FEAT_AIE ID_AA64MMFR3_EL1, AIE, IMP
|
||||
#define FEAT_S2POE ID_AA64MMFR3_EL1, S2POE, IMP
|
||||
@ -131,7 +195,6 @@ struct reg_bits_to_feat_map {
|
||||
#define FEAT_SPMU ID_AA64DFR1_EL1, SPMU, IMP
|
||||
#define FEAT_SPE_nVM ID_AA64DFR2_EL1, SPE_nVM, IMP
|
||||
#define FEAT_STEP2 ID_AA64DFR2_EL1, STEP, IMP
|
||||
#define FEAT_SYSREG128 ID_AA64ISAR2_EL1, SYSREG_128, IMP
|
||||
#define FEAT_CPA2 ID_AA64ISAR3_EL1, CPA, CPA2
|
||||
#define FEAT_ASID2 ID_AA64MMFR4_EL1, ASID2, IMP
|
||||
#define FEAT_MEC ID_AA64MMFR3_EL1, MEC, IMP
|
||||
@ -143,7 +206,6 @@ struct reg_bits_to_feat_map {
|
||||
#define FEAT_LSMAOC ID_AA64MMFR2_EL1, LSM, IMP
|
||||
#define FEAT_MixedEnd ID_AA64MMFR0_EL1, BIGEND, IMP
|
||||
#define FEAT_MixedEndEL0 ID_AA64MMFR0_EL1, BIGENDEL0, IMP
|
||||
#define FEAT_MTE2 ID_AA64PFR1_EL1, MTE, MTE2
|
||||
#define FEAT_MTE_ASYNC ID_AA64PFR1_EL1, MTE_frac, ASYNC
|
||||
#define FEAT_MTE_STORE_ONLY ID_AA64PFR2_EL1, MTESTOREONLY, IMP
|
||||
#define FEAT_PAN ID_AA64MMFR1_EL1, PAN, IMP
|
||||
@ -151,7 +213,9 @@ struct reg_bits_to_feat_map {
|
||||
#define FEAT_SSBS ID_AA64PFR1_EL1, SSBS, IMP
|
||||
#define FEAT_TIDCP1 ID_AA64MMFR1_EL1, TIDCP1, IMP
|
||||
#define FEAT_FGT ID_AA64MMFR0_EL1, FGT, IMP
|
||||
#define FEAT_FGT2 ID_AA64MMFR0_EL1, FGT, FGT2
|
||||
#define FEAT_MTPMU ID_AA64DFR0_EL1, MTPMU, IMP
|
||||
#define FEAT_HCX ID_AA64MMFR1_EL1, HCX, IMP
|
||||
|
||||
static bool not_feat_aa64el3(struct kvm *kvm)
|
||||
{
|
||||
@ -397,6 +461,10 @@ static const struct reg_bits_to_feat_map hfgrtr_feat_map[] = {
|
||||
NEVER_FGU, FEAT_AA64EL1),
|
||||
};
|
||||
|
||||
|
||||
static const DECLARE_FEAT_MAP_FGT(hfgrtr_desc, hfgrtr_masks,
|
||||
hfgrtr_feat_map, FEAT_FGT);
|
||||
|
||||
static const struct reg_bits_to_feat_map hfgwtr_feat_map[] = {
|
||||
NEEDS_FEAT(HFGWTR_EL2_nAMAIR2_EL1 |
|
||||
HFGWTR_EL2_nMAIR2_EL1,
|
||||
@ -461,6 +529,9 @@ static const struct reg_bits_to_feat_map hfgwtr_feat_map[] = {
|
||||
NEVER_FGU, FEAT_AA64EL1),
|
||||
};
|
||||
|
||||
static const DECLARE_FEAT_MAP_FGT(hfgwtr_desc, hfgwtr_masks,
|
||||
hfgwtr_feat_map, FEAT_FGT);
|
||||
|
||||
static const struct reg_bits_to_feat_map hdfgrtr_feat_map[] = {
|
||||
NEEDS_FEAT(HDFGRTR_EL2_PMBIDR_EL1 |
|
||||
HDFGRTR_EL2_PMSLATFR_EL1 |
|
||||
@ -528,6 +599,9 @@ static const struct reg_bits_to_feat_map hdfgrtr_feat_map[] = {
|
||||
NEVER_FGU, FEAT_AA64EL1)
|
||||
};
|
||||
|
||||
static const DECLARE_FEAT_MAP_FGT(hdfgrtr_desc, hdfgrtr_masks,
|
||||
hdfgrtr_feat_map, FEAT_FGT);
|
||||
|
||||
static const struct reg_bits_to_feat_map hdfgwtr_feat_map[] = {
|
||||
NEEDS_FEAT(HDFGWTR_EL2_PMSLATFR_EL1 |
|
||||
HDFGWTR_EL2_PMSIRR_EL1 |
|
||||
@ -588,6 +662,8 @@ static const struct reg_bits_to_feat_map hdfgwtr_feat_map[] = {
|
||||
NEEDS_FEAT(HDFGWTR_EL2_TRFCR_EL1, FEAT_TRF),
|
||||
};
|
||||
|
||||
static const DECLARE_FEAT_MAP_FGT(hdfgwtr_desc, hdfgwtr_masks,
|
||||
hdfgwtr_feat_map, FEAT_FGT);
|
||||
|
||||
static const struct reg_bits_to_feat_map hfgitr_feat_map[] = {
|
||||
NEEDS_FEAT(HFGITR_EL2_PSBCSYNC, FEAT_SPEv1p5),
|
||||
@ -662,6 +738,9 @@ static const struct reg_bits_to_feat_map hfgitr_feat_map[] = {
|
||||
NEVER_FGU, FEAT_AA64EL1),
|
||||
};
|
||||
|
||||
static const DECLARE_FEAT_MAP_FGT(hfgitr_desc, hfgitr_masks,
|
||||
hfgitr_feat_map, FEAT_FGT);
|
||||
|
||||
static const struct reg_bits_to_feat_map hafgrtr_feat_map[] = {
|
||||
NEEDS_FEAT(HAFGRTR_EL2_AMEVTYPER115_EL0 |
|
||||
HAFGRTR_EL2_AMEVTYPER114_EL0 |
|
||||
@ -704,11 +783,17 @@ static const struct reg_bits_to_feat_map hafgrtr_feat_map[] = {
|
||||
FEAT_AMUv1),
|
||||
};
|
||||
|
||||
static const DECLARE_FEAT_MAP_FGT(hafgrtr_desc, hafgrtr_masks,
|
||||
hafgrtr_feat_map, FEAT_FGT);
|
||||
|
||||
static const struct reg_bits_to_feat_map hfgitr2_feat_map[] = {
|
||||
NEEDS_FEAT(HFGITR2_EL2_nDCCIVAPS, FEAT_PoPS),
|
||||
NEEDS_FEAT(HFGITR2_EL2_TSBCSYNC, FEAT_TRBEv1p1)
|
||||
};
|
||||
|
||||
static const DECLARE_FEAT_MAP_FGT(hfgitr2_desc, hfgitr2_masks,
|
||||
hfgitr2_feat_map, FEAT_FGT2);
|
||||
|
||||
static const struct reg_bits_to_feat_map hfgrtr2_feat_map[] = {
|
||||
NEEDS_FEAT(HFGRTR2_EL2_nPFAR_EL1, FEAT_PFAR),
|
||||
NEEDS_FEAT(HFGRTR2_EL2_nERXGSR_EL1, FEAT_RASv2),
|
||||
@ -728,6 +813,9 @@ static const struct reg_bits_to_feat_map hfgrtr2_feat_map[] = {
|
||||
NEEDS_FEAT(HFGRTR2_EL2_nRCWSMASK_EL1, FEAT_THE),
|
||||
};
|
||||
|
||||
static const DECLARE_FEAT_MAP_FGT(hfgrtr2_desc, hfgrtr2_masks,
|
||||
hfgrtr2_feat_map, FEAT_FGT2);
|
||||
|
||||
static const struct reg_bits_to_feat_map hfgwtr2_feat_map[] = {
|
||||
NEEDS_FEAT(HFGWTR2_EL2_nPFAR_EL1, FEAT_PFAR),
|
||||
NEEDS_FEAT(HFGWTR2_EL2_nACTLRALIAS_EL1 |
|
||||
@ -746,6 +834,9 @@ static const struct reg_bits_to_feat_map hfgwtr2_feat_map[] = {
|
||||
NEEDS_FEAT(HFGWTR2_EL2_nRCWSMASK_EL1, FEAT_THE),
|
||||
};
|
||||
|
||||
static const DECLARE_FEAT_MAP_FGT(hfgwtr2_desc, hfgwtr2_masks,
|
||||
hfgwtr2_feat_map, FEAT_FGT2);
|
||||
|
||||
static const struct reg_bits_to_feat_map hdfgrtr2_feat_map[] = {
|
||||
NEEDS_FEAT(HDFGRTR2_EL2_nMDSELR_EL1, FEAT_Debugv8p9),
|
||||
NEEDS_FEAT(HDFGRTR2_EL2_nPMECR_EL1, feat_ebep_pmuv3_ss),
|
||||
@ -776,6 +867,9 @@ static const struct reg_bits_to_feat_map hdfgrtr2_feat_map[] = {
|
||||
NEEDS_FEAT(HDFGRTR2_EL2_nTRBMPAM_EL1, feat_trbe_mpam),
|
||||
};
|
||||
|
||||
static const DECLARE_FEAT_MAP_FGT(hdfgrtr2_desc, hdfgrtr2_masks,
|
||||
hdfgrtr2_feat_map, FEAT_FGT2);
|
||||
|
||||
static const struct reg_bits_to_feat_map hdfgwtr2_feat_map[] = {
|
||||
NEEDS_FEAT(HDFGWTR2_EL2_nMDSELR_EL1, FEAT_Debugv8p9),
|
||||
NEEDS_FEAT(HDFGWTR2_EL2_nPMECR_EL1, feat_ebep_pmuv3_ss),
|
||||
@ -804,6 +898,10 @@ static const struct reg_bits_to_feat_map hdfgwtr2_feat_map[] = {
|
||||
NEEDS_FEAT(HDFGWTR2_EL2_nTRBMPAM_EL1, feat_trbe_mpam),
|
||||
};
|
||||
|
||||
static const DECLARE_FEAT_MAP_FGT(hdfgwtr2_desc, hdfgwtr2_masks,
|
||||
hdfgwtr2_feat_map, FEAT_FGT2);
|
||||
|
||||
|
||||
static const struct reg_bits_to_feat_map hcrx_feat_map[] = {
|
||||
NEEDS_FEAT(HCRX_EL2_PACMEn, feat_pauth_lr),
|
||||
NEEDS_FEAT(HCRX_EL2_EnFPM, FEAT_FPMR),
|
||||
@ -833,6 +931,10 @@ static const struct reg_bits_to_feat_map hcrx_feat_map[] = {
|
||||
NEEDS_FEAT(HCRX_EL2_EnAS0, FEAT_LS64_ACCDATA),
|
||||
};
|
||||
|
||||
|
||||
static const DECLARE_FEAT_MAP(hcrx_desc, __HCRX_EL2,
|
||||
hcrx_feat_map, FEAT_HCX);
|
||||
|
||||
static const struct reg_bits_to_feat_map hcr_feat_map[] = {
|
||||
NEEDS_FEAT(HCR_EL2_TID0, FEAT_AA32EL0),
|
||||
NEEDS_FEAT_FIXED(HCR_EL2_RW, compute_hcr_rw),
|
||||
@ -904,6 +1006,9 @@ static const struct reg_bits_to_feat_map hcr_feat_map[] = {
|
||||
NEEDS_FEAT_FIXED(HCR_EL2_E2H, compute_hcr_e2h),
|
||||
};
|
||||
|
||||
static const DECLARE_FEAT_MAP(hcr_desc, HCR_EL2,
|
||||
hcr_feat_map, FEAT_AA64EL2);
|
||||
|
||||
static const struct reg_bits_to_feat_map sctlr2_feat_map[] = {
|
||||
NEEDS_FEAT(SCTLR2_EL1_NMEA |
|
||||
SCTLR2_EL1_EASE,
|
||||
@ -921,6 +1026,9 @@ static const struct reg_bits_to_feat_map sctlr2_feat_map[] = {
|
||||
FEAT_CPA2),
|
||||
};
|
||||
|
||||
static const DECLARE_FEAT_MAP(sctlr2_desc, SCTLR2_EL1,
|
||||
sctlr2_feat_map, FEAT_SCTLR2);
|
||||
|
||||
static const struct reg_bits_to_feat_map tcr2_el2_feat_map[] = {
|
||||
NEEDS_FEAT(TCR2_EL2_FNG1 |
|
||||
TCR2_EL2_FNG0 |
|
||||
@ -943,6 +1051,9 @@ static const struct reg_bits_to_feat_map tcr2_el2_feat_map[] = {
|
||||
NEEDS_FEAT(TCR2_EL2_PIE, FEAT_S1PIE),
|
||||
};
|
||||
|
||||
static const DECLARE_FEAT_MAP(tcr2_el2_desc, TCR2_EL2,
|
||||
tcr2_el2_feat_map, FEAT_TCR2);
|
||||
|
||||
static const struct reg_bits_to_feat_map sctlr_el1_feat_map[] = {
|
||||
NEEDS_FEAT(SCTLR_EL1_CP15BEN |
|
||||
SCTLR_EL1_ITD |
|
||||
@ -1017,6 +1128,9 @@ static const struct reg_bits_to_feat_map sctlr_el1_feat_map[] = {
|
||||
FEAT_AA64EL1),
|
||||
};
|
||||
|
||||
static const DECLARE_FEAT_MAP(sctlr_el1_desc, SCTLR_EL1,
|
||||
sctlr_el1_feat_map, FEAT_AA64EL1);
|
||||
|
||||
static const struct reg_bits_to_feat_map mdcr_el2_feat_map[] = {
|
||||
NEEDS_FEAT(MDCR_EL2_EBWE, FEAT_Debugv8p9),
|
||||
NEEDS_FEAT(MDCR_EL2_TDOSA, FEAT_DoubleLock),
|
||||
@ -1048,6 +1162,9 @@ static const struct reg_bits_to_feat_map mdcr_el2_feat_map[] = {
|
||||
FEAT_AA64EL1),
|
||||
};
|
||||
|
||||
static const DECLARE_FEAT_MAP(mdcr_el2_desc, MDCR_EL2,
|
||||
mdcr_el2_feat_map, FEAT_AA64EL2);
|
||||
|
||||
static void __init check_feat_map(const struct reg_bits_to_feat_map *map,
|
||||
int map_size, u64 res0, const char *str)
|
||||
{
|
||||
@ -1061,32 +1178,36 @@ static void __init check_feat_map(const struct reg_bits_to_feat_map *map,
|
||||
str, mask ^ ~res0);
|
||||
}
|
||||
|
||||
static u64 reg_feat_map_bits(const struct reg_bits_to_feat_map *map)
|
||||
{
|
||||
return map->flags & RES0_POINTER ? ~(*map->res0p) : map->bits;
|
||||
}
|
||||
|
||||
static void __init check_reg_desc(const struct reg_feat_map_desc *r)
|
||||
{
|
||||
check_feat_map(r->bit_feat_map, r->bit_feat_map_sz,
|
||||
~reg_feat_map_bits(&r->feat_map), r->name);
|
||||
}
|
||||
|
||||
void __init check_feature_map(void)
|
||||
{
|
||||
check_feat_map(hfgrtr_feat_map, ARRAY_SIZE(hfgrtr_feat_map),
|
||||
hfgrtr_masks.res0, hfgrtr_masks.str);
|
||||
check_feat_map(hfgwtr_feat_map, ARRAY_SIZE(hfgwtr_feat_map),
|
||||
hfgwtr_masks.res0, hfgwtr_masks.str);
|
||||
check_feat_map(hfgitr_feat_map, ARRAY_SIZE(hfgitr_feat_map),
|
||||
hfgitr_masks.res0, hfgitr_masks.str);
|
||||
check_feat_map(hdfgrtr_feat_map, ARRAY_SIZE(hdfgrtr_feat_map),
|
||||
hdfgrtr_masks.res0, hdfgrtr_masks.str);
|
||||
check_feat_map(hdfgwtr_feat_map, ARRAY_SIZE(hdfgwtr_feat_map),
|
||||
hdfgwtr_masks.res0, hdfgwtr_masks.str);
|
||||
check_feat_map(hafgrtr_feat_map, ARRAY_SIZE(hafgrtr_feat_map),
|
||||
hafgrtr_masks.res0, hafgrtr_masks.str);
|
||||
check_feat_map(hcrx_feat_map, ARRAY_SIZE(hcrx_feat_map),
|
||||
__HCRX_EL2_RES0, "HCRX_EL2");
|
||||
check_feat_map(hcr_feat_map, ARRAY_SIZE(hcr_feat_map),
|
||||
HCR_EL2_RES0, "HCR_EL2");
|
||||
check_feat_map(sctlr2_feat_map, ARRAY_SIZE(sctlr2_feat_map),
|
||||
SCTLR2_EL1_RES0, "SCTLR2_EL1");
|
||||
check_feat_map(tcr2_el2_feat_map, ARRAY_SIZE(tcr2_el2_feat_map),
|
||||
TCR2_EL2_RES0, "TCR2_EL2");
|
||||
check_feat_map(sctlr_el1_feat_map, ARRAY_SIZE(sctlr_el1_feat_map),
|
||||
SCTLR_EL1_RES0, "SCTLR_EL1");
|
||||
check_feat_map(mdcr_el2_feat_map, ARRAY_SIZE(mdcr_el2_feat_map),
|
||||
MDCR_EL2_RES0, "MDCR_EL2");
|
||||
check_reg_desc(&hfgrtr_desc);
|
||||
check_reg_desc(&hfgwtr_desc);
|
||||
check_reg_desc(&hfgitr_desc);
|
||||
check_reg_desc(&hdfgrtr_desc);
|
||||
check_reg_desc(&hdfgwtr_desc);
|
||||
check_reg_desc(&hafgrtr_desc);
|
||||
check_reg_desc(&hfgrtr2_desc);
|
||||
check_reg_desc(&hfgwtr2_desc);
|
||||
check_reg_desc(&hfgitr2_desc);
|
||||
check_reg_desc(&hdfgrtr2_desc);
|
||||
check_reg_desc(&hdfgwtr2_desc);
|
||||
check_reg_desc(&hcrx_desc);
|
||||
check_reg_desc(&hcr_desc);
|
||||
check_reg_desc(&sctlr2_desc);
|
||||
check_reg_desc(&tcr2_el2_desc);
|
||||
check_reg_desc(&sctlr_el1_desc);
|
||||
check_reg_desc(&mdcr_el2_desc);
|
||||
}
|
||||
|
||||
static bool idreg_feat_match(struct kvm *kvm, const struct reg_bits_to_feat_map *map)
|
||||
@ -1129,7 +1250,7 @@ static u64 __compute_fixed_bits(struct kvm *kvm,
|
||||
match = idreg_feat_match(kvm, &map[i]);
|
||||
|
||||
if (!match || (map[i].flags & FIXED_VALUE))
|
||||
val |= map[i].bits;
|
||||
val |= reg_feat_map_bits(&map[i]);
|
||||
}
|
||||
|
||||
return val;
|
||||
@ -1145,15 +1266,36 @@ static u64 compute_res0_bits(struct kvm *kvm,
|
||||
require, exclude | FIXED_VALUE);
|
||||
}
|
||||
|
||||
static u64 compute_fixed_bits(struct kvm *kvm,
|
||||
const struct reg_bits_to_feat_map *map,
|
||||
int map_size,
|
||||
u64 *fixed_bits,
|
||||
unsigned long require,
|
||||
unsigned long exclude)
|
||||
static u64 compute_reg_res0_bits(struct kvm *kvm,
|
||||
const struct reg_feat_map_desc *r,
|
||||
unsigned long require, unsigned long exclude)
|
||||
|
||||
{
|
||||
return __compute_fixed_bits(kvm, map, map_size, fixed_bits,
|
||||
require | FIXED_VALUE, exclude);
|
||||
u64 res0;
|
||||
|
||||
res0 = compute_res0_bits(kvm, r->bit_feat_map, r->bit_feat_map_sz,
|
||||
require, exclude);
|
||||
|
||||
/*
|
||||
* If computing FGUs, don't take RES0 or register existence
|
||||
* into account -- we're not computing bits for the register
|
||||
* itself.
|
||||
*/
|
||||
if (!(exclude & NEVER_FGU)) {
|
||||
res0 |= compute_res0_bits(kvm, &r->feat_map, 1, require, exclude);
|
||||
res0 |= ~reg_feat_map_bits(&r->feat_map);
|
||||
}
|
||||
|
||||
return res0;
|
||||
}
|
||||
|
||||
static u64 compute_reg_fixed_bits(struct kvm *kvm,
|
||||
const struct reg_feat_map_desc *r,
|
||||
u64 *fixed_bits, unsigned long require,
|
||||
unsigned long exclude)
|
||||
{
|
||||
return __compute_fixed_bits(kvm, r->bit_feat_map, r->bit_feat_map_sz,
|
||||
fixed_bits, require | FIXED_VALUE, exclude);
|
||||
}
|
||||
|
||||
void compute_fgu(struct kvm *kvm, enum fgt_group_id fgt)
|
||||
@ -1162,51 +1304,40 @@ void compute_fgu(struct kvm *kvm, enum fgt_group_id fgt)
|
||||
|
||||
switch (fgt) {
|
||||
case HFGRTR_GROUP:
|
||||
val |= compute_res0_bits(kvm, hfgrtr_feat_map,
|
||||
ARRAY_SIZE(hfgrtr_feat_map),
|
||||
0, NEVER_FGU);
|
||||
val |= compute_res0_bits(kvm, hfgwtr_feat_map,
|
||||
ARRAY_SIZE(hfgwtr_feat_map),
|
||||
0, NEVER_FGU);
|
||||
val |= compute_reg_res0_bits(kvm, &hfgrtr_desc,
|
||||
0, NEVER_FGU);
|
||||
val |= compute_reg_res0_bits(kvm, &hfgwtr_desc,
|
||||
0, NEVER_FGU);
|
||||
break;
|
||||
case HFGITR_GROUP:
|
||||
val |= compute_res0_bits(kvm, hfgitr_feat_map,
|
||||
ARRAY_SIZE(hfgitr_feat_map),
|
||||
0, NEVER_FGU);
|
||||
val |= compute_reg_res0_bits(kvm, &hfgitr_desc,
|
||||
0, NEVER_FGU);
|
||||
break;
|
||||
case HDFGRTR_GROUP:
|
||||
val |= compute_res0_bits(kvm, hdfgrtr_feat_map,
|
||||
ARRAY_SIZE(hdfgrtr_feat_map),
|
||||
0, NEVER_FGU);
|
||||
val |= compute_res0_bits(kvm, hdfgwtr_feat_map,
|
||||
ARRAY_SIZE(hdfgwtr_feat_map),
|
||||
0, NEVER_FGU);
|
||||
val |= compute_reg_res0_bits(kvm, &hdfgrtr_desc,
|
||||
0, NEVER_FGU);
|
||||
val |= compute_reg_res0_bits(kvm, &hdfgwtr_desc,
|
||||
0, NEVER_FGU);
|
||||
break;
|
||||
case HAFGRTR_GROUP:
|
||||
val |= compute_res0_bits(kvm, hafgrtr_feat_map,
|
||||
ARRAY_SIZE(hafgrtr_feat_map),
|
||||
0, NEVER_FGU);
|
||||
val |= compute_reg_res0_bits(kvm, &hafgrtr_desc,
|
||||
0, NEVER_FGU);
|
||||
break;
|
||||
case HFGRTR2_GROUP:
|
||||
val |= compute_res0_bits(kvm, hfgrtr2_feat_map,
|
||||
ARRAY_SIZE(hfgrtr2_feat_map),
|
||||
0, NEVER_FGU);
|
||||
val |= compute_res0_bits(kvm, hfgwtr2_feat_map,
|
||||
ARRAY_SIZE(hfgwtr2_feat_map),
|
||||
0, NEVER_FGU);
|
||||
val |= compute_reg_res0_bits(kvm, &hfgrtr2_desc,
|
||||
0, NEVER_FGU);
|
||||
val |= compute_reg_res0_bits(kvm, &hfgwtr2_desc,
|
||||
0, NEVER_FGU);
|
||||
break;
|
||||
case HFGITR2_GROUP:
|
||||
val |= compute_res0_bits(kvm, hfgitr2_feat_map,
|
||||
ARRAY_SIZE(hfgitr2_feat_map),
|
||||
0, NEVER_FGU);
|
||||
val |= compute_reg_res0_bits(kvm, &hfgitr2_desc,
|
||||
0, NEVER_FGU);
|
||||
break;
|
||||
case HDFGRTR2_GROUP:
|
||||
val |= compute_res0_bits(kvm, hdfgrtr2_feat_map,
|
||||
ARRAY_SIZE(hdfgrtr2_feat_map),
|
||||
0, NEVER_FGU);
|
||||
val |= compute_res0_bits(kvm, hdfgwtr2_feat_map,
|
||||
ARRAY_SIZE(hdfgwtr2_feat_map),
|
||||
0, NEVER_FGU);
|
||||
val |= compute_reg_res0_bits(kvm, &hdfgrtr2_desc,
|
||||
0, NEVER_FGU);
|
||||
val |= compute_reg_res0_bits(kvm, &hdfgwtr2_desc,
|
||||
0, NEVER_FGU);
|
||||
break;
|
||||
default:
|
||||
BUG();
|
||||
@ -1221,109 +1352,74 @@ void get_reg_fixed_bits(struct kvm *kvm, enum vcpu_sysreg reg, u64 *res0, u64 *r
|
||||
|
||||
switch (reg) {
|
||||
case HFGRTR_EL2:
|
||||
*res0 = compute_res0_bits(kvm, hfgrtr_feat_map,
|
||||
ARRAY_SIZE(hfgrtr_feat_map), 0, 0);
|
||||
*res0 |= hfgrtr_masks.res0;
|
||||
*res0 = compute_reg_res0_bits(kvm, &hfgrtr_desc, 0, 0);
|
||||
*res1 = HFGRTR_EL2_RES1;
|
||||
break;
|
||||
case HFGWTR_EL2:
|
||||
*res0 = compute_res0_bits(kvm, hfgwtr_feat_map,
|
||||
ARRAY_SIZE(hfgwtr_feat_map), 0, 0);
|
||||
*res0 |= hfgwtr_masks.res0;
|
||||
*res0 = compute_reg_res0_bits(kvm, &hfgwtr_desc, 0, 0);
|
||||
*res1 = HFGWTR_EL2_RES1;
|
||||
break;
|
||||
case HFGITR_EL2:
|
||||
*res0 = compute_res0_bits(kvm, hfgitr_feat_map,
|
||||
ARRAY_SIZE(hfgitr_feat_map), 0, 0);
|
||||
*res0 |= hfgitr_masks.res0;
|
||||
*res0 = compute_reg_res0_bits(kvm, &hfgitr_desc, 0, 0);
|
||||
*res1 = HFGITR_EL2_RES1;
|
||||
break;
|
||||
case HDFGRTR_EL2:
|
||||
*res0 = compute_res0_bits(kvm, hdfgrtr_feat_map,
|
||||
ARRAY_SIZE(hdfgrtr_feat_map), 0, 0);
|
||||
*res0 |= hdfgrtr_masks.res0;
|
||||
*res0 = compute_reg_res0_bits(kvm, &hdfgrtr_desc, 0, 0);
|
||||
*res1 = HDFGRTR_EL2_RES1;
|
||||
break;
|
||||
case HDFGWTR_EL2:
|
||||
*res0 = compute_res0_bits(kvm, hdfgwtr_feat_map,
|
||||
ARRAY_SIZE(hdfgwtr_feat_map), 0, 0);
|
||||
*res0 |= hdfgwtr_masks.res0;
|
||||
*res0 = compute_reg_res0_bits(kvm, &hdfgwtr_desc, 0, 0);
|
||||
*res1 = HDFGWTR_EL2_RES1;
|
||||
break;
|
||||
case HAFGRTR_EL2:
|
||||
*res0 = compute_res0_bits(kvm, hafgrtr_feat_map,
|
||||
ARRAY_SIZE(hafgrtr_feat_map), 0, 0);
|
||||
*res0 |= hafgrtr_masks.res0;
|
||||
*res0 = compute_reg_res0_bits(kvm, &hafgrtr_desc, 0, 0);
|
||||
*res1 = HAFGRTR_EL2_RES1;
|
||||
break;
|
||||
case HFGRTR2_EL2:
|
||||
*res0 = compute_res0_bits(kvm, hfgrtr2_feat_map,
|
||||
ARRAY_SIZE(hfgrtr2_feat_map), 0, 0);
|
||||
*res0 |= hfgrtr2_masks.res0;
|
||||
*res0 = compute_reg_res0_bits(kvm, &hfgrtr2_desc, 0, 0);
|
||||
*res1 = HFGRTR2_EL2_RES1;
|
||||
break;
|
||||
case HFGWTR2_EL2:
|
||||
*res0 = compute_res0_bits(kvm, hfgwtr2_feat_map,
|
||||
ARRAY_SIZE(hfgwtr2_feat_map), 0, 0);
|
||||
*res0 |= hfgwtr2_masks.res0;
|
||||
*res0 = compute_reg_res0_bits(kvm, &hfgwtr2_desc, 0, 0);
|
||||
*res1 = HFGWTR2_EL2_RES1;
|
||||
break;
|
||||
case HFGITR2_EL2:
|
||||
*res0 = compute_res0_bits(kvm, hfgitr2_feat_map,
|
||||
ARRAY_SIZE(hfgitr2_feat_map), 0, 0);
|
||||
*res0 |= hfgitr2_masks.res0;
|
||||
*res0 = compute_reg_res0_bits(kvm, &hfgitr2_desc, 0, 0);
|
||||
*res1 = HFGITR2_EL2_RES1;
|
||||
break;
|
||||
case HDFGRTR2_EL2:
|
||||
*res0 = compute_res0_bits(kvm, hdfgrtr2_feat_map,
|
||||
ARRAY_SIZE(hdfgrtr2_feat_map), 0, 0);
|
||||
*res0 |= hdfgrtr2_masks.res0;
|
||||
*res0 = compute_reg_res0_bits(kvm, &hdfgrtr2_desc, 0, 0);
|
||||
*res1 = HDFGRTR2_EL2_RES1;
|
||||
break;
|
||||
case HDFGWTR2_EL2:
|
||||
*res0 = compute_res0_bits(kvm, hdfgwtr2_feat_map,
|
||||
ARRAY_SIZE(hdfgwtr2_feat_map), 0, 0);
|
||||
*res0 |= hdfgwtr2_masks.res0;
|
||||
*res0 = compute_reg_res0_bits(kvm, &hdfgwtr2_desc, 0, 0);
|
||||
*res1 = HDFGWTR2_EL2_RES1;
|
||||
break;
|
||||
case HCRX_EL2:
|
||||
*res0 = compute_res0_bits(kvm, hcrx_feat_map,
|
||||
ARRAY_SIZE(hcrx_feat_map), 0, 0);
|
||||
*res0 |= __HCRX_EL2_RES0;
|
||||
*res0 = compute_reg_res0_bits(kvm, &hcrx_desc, 0, 0);
|
||||
*res1 = __HCRX_EL2_RES1;
|
||||
break;
|
||||
case HCR_EL2:
|
||||
mask = compute_fixed_bits(kvm, hcr_feat_map,
|
||||
ARRAY_SIZE(hcr_feat_map), &fixed,
|
||||
0, 0);
|
||||
*res0 = compute_res0_bits(kvm, hcr_feat_map,
|
||||
ARRAY_SIZE(hcr_feat_map), 0, 0);
|
||||
*res0 |= HCR_EL2_RES0 | (mask & ~fixed);
|
||||
mask = compute_reg_fixed_bits(kvm, &hcr_desc, &fixed, 0, 0);
|
||||
*res0 = compute_reg_res0_bits(kvm, &hcr_desc, 0, 0);
|
||||
*res0 |= (mask & ~fixed);
|
||||
*res1 = HCR_EL2_RES1 | (mask & fixed);
|
||||
break;
|
||||
case SCTLR2_EL1:
|
||||
case SCTLR2_EL2:
|
||||
*res0 = compute_res0_bits(kvm, sctlr2_feat_map,
|
||||
ARRAY_SIZE(sctlr2_feat_map), 0, 0);
|
||||
*res0 |= SCTLR2_EL1_RES0;
|
||||
*res0 = compute_reg_res0_bits(kvm, &sctlr2_desc, 0, 0);
|
||||
*res1 = SCTLR2_EL1_RES1;
|
||||
break;
|
||||
case TCR2_EL2:
|
||||
*res0 = compute_res0_bits(kvm, tcr2_el2_feat_map,
|
||||
ARRAY_SIZE(tcr2_el2_feat_map), 0, 0);
|
||||
*res0 |= TCR2_EL2_RES0;
|
||||
*res0 = compute_reg_res0_bits(kvm, &tcr2_el2_desc, 0, 0);
|
||||
*res1 = TCR2_EL2_RES1;
|
||||
break;
|
||||
case SCTLR_EL1:
|
||||
*res0 = compute_res0_bits(kvm, sctlr_el1_feat_map,
|
||||
ARRAY_SIZE(sctlr_el1_feat_map), 0, 0);
|
||||
*res0 |= SCTLR_EL1_RES0;
|
||||
*res0 = compute_reg_res0_bits(kvm, &sctlr_el1_desc, 0, 0);
|
||||
*res1 = SCTLR_EL1_RES1;
|
||||
break;
|
||||
case MDCR_EL2:
|
||||
*res0 = compute_res0_bits(kvm, mdcr_el2_feat_map,
|
||||
ARRAY_SIZE(mdcr_el2_feat_map), 0, 0);
|
||||
*res0 |= MDCR_EL2_RES0;
|
||||
*res0 = compute_reg_res0_bits(kvm, &mdcr_el2_desc, 0, 0);
|
||||
*res1 = MDCR_EL2_RES1;
|
||||
break;
|
||||
default:
|
||||
|
||||
@ -95,6 +95,13 @@ static u64 __compute_hcr(struct kvm_vcpu *vcpu)
|
||||
/* Force NV2 in case the guest is forgetful... */
|
||||
guest_hcr |= HCR_NV2;
|
||||
}
|
||||
|
||||
/*
|
||||
* Exclude the guest's TWED configuration if it hasn't set TWE
|
||||
* to avoid potentially delaying traps for the host.
|
||||
*/
|
||||
if (!(guest_hcr & HCR_TWE))
|
||||
guest_hcr &= ~(HCR_EL2_TWEDEn | HCR_EL2_TWEDEL);
|
||||
}
|
||||
|
||||
BUG_ON(host_data_test_flag(VCPU_IN_HYP_CONTEXT) &&
|
||||
|
||||
@ -1462,9 +1462,16 @@ u64 limit_nv_id_reg(struct kvm *kvm, u32 reg, u64 val)
|
||||
|
||||
case SYS_ID_AA64PFR1_EL1:
|
||||
/* Only support BTI, SSBS, CSV2_frac */
|
||||
val &= (ID_AA64PFR1_EL1_BT |
|
||||
ID_AA64PFR1_EL1_SSBS |
|
||||
ID_AA64PFR1_EL1_CSV2_frac);
|
||||
val &= ~(ID_AA64PFR1_EL1_PFAR |
|
||||
ID_AA64PFR1_EL1_MTEX |
|
||||
ID_AA64PFR1_EL1_THE |
|
||||
ID_AA64PFR1_EL1_GCS |
|
||||
ID_AA64PFR1_EL1_MTE_frac |
|
||||
ID_AA64PFR1_EL1_NMI |
|
||||
ID_AA64PFR1_EL1_SME |
|
||||
ID_AA64PFR1_EL1_RES0 |
|
||||
ID_AA64PFR1_EL1_MPAM_frac |
|
||||
ID_AA64PFR1_EL1_MTE);
|
||||
break;
|
||||
|
||||
case SYS_ID_AA64MMFR0_EL1:
|
||||
@ -1517,12 +1524,11 @@ u64 limit_nv_id_reg(struct kvm *kvm, u32 reg, u64 val)
|
||||
break;
|
||||
|
||||
case SYS_ID_AA64MMFR1_EL1:
|
||||
val &= (ID_AA64MMFR1_EL1_HCX |
|
||||
ID_AA64MMFR1_EL1_PAN |
|
||||
ID_AA64MMFR1_EL1_LO |
|
||||
ID_AA64MMFR1_EL1_HPDS |
|
||||
ID_AA64MMFR1_EL1_VH |
|
||||
ID_AA64MMFR1_EL1_VMIDBits);
|
||||
val &= ~(ID_AA64MMFR1_EL1_CMOW |
|
||||
ID_AA64MMFR1_EL1_nTLBPA |
|
||||
ID_AA64MMFR1_EL1_ETS |
|
||||
ID_AA64MMFR1_EL1_XNX |
|
||||
ID_AA64MMFR1_EL1_HAFDBS);
|
||||
/* FEAT_E2H0 implies no VHE */
|
||||
if (test_bit(KVM_ARM_VCPU_HAS_EL2_E2H0, kvm->arch.vcpu_features))
|
||||
val &= ~ID_AA64MMFR1_EL1_VH;
|
||||
@ -1564,14 +1570,22 @@ u64 limit_nv_id_reg(struct kvm *kvm, u32 reg, u64 val)
|
||||
|
||||
case SYS_ID_AA64DFR0_EL1:
|
||||
/* Only limited support for PMU, Debug, BPs, WPs, and HPMN0 */
|
||||
val &= (ID_AA64DFR0_EL1_PMUVer |
|
||||
ID_AA64DFR0_EL1_WRPs |
|
||||
ID_AA64DFR0_EL1_BRPs |
|
||||
ID_AA64DFR0_EL1_DebugVer|
|
||||
ID_AA64DFR0_EL1_HPMN0);
|
||||
val &= ~(ID_AA64DFR0_EL1_ExtTrcBuff |
|
||||
ID_AA64DFR0_EL1_BRBE |
|
||||
ID_AA64DFR0_EL1_MTPMU |
|
||||
ID_AA64DFR0_EL1_TraceBuffer |
|
||||
ID_AA64DFR0_EL1_TraceFilt |
|
||||
ID_AA64DFR0_EL1_PMSVer |
|
||||
ID_AA64DFR0_EL1_CTX_CMPs |
|
||||
ID_AA64DFR0_EL1_SEBEP |
|
||||
ID_AA64DFR0_EL1_PMSS |
|
||||
ID_AA64DFR0_EL1_TraceVer);
|
||||
|
||||
/* Cap Debug to ARMv8.1 */
|
||||
val = ID_REG_LIMIT_FIELD_ENUM(val, ID_AA64DFR0_EL1, DebugVer, VHE);
|
||||
/*
|
||||
* FEAT_Debugv8p9 requires support for extended breakpoints /
|
||||
* watchpoints.
|
||||
*/
|
||||
val = ID_REG_LIMIT_FIELD_ENUM(val, ID_AA64DFR0_EL1, DebugVer, V8P8);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@ -1997,6 +1997,26 @@ static u64 sanitise_id_aa64dfr0_el1(const struct kvm_vcpu *vcpu, u64 val)
|
||||
return val;
|
||||
}
|
||||
|
||||
/*
|
||||
* Older versions of KVM erroneously claim support for FEAT_DoubleLock with
|
||||
* NV-enabled VMs on unsupporting hardware. Silently ignore the incorrect
|
||||
* value if it is consistent with the bug.
|
||||
*/
|
||||
static bool ignore_feat_doublelock(struct kvm_vcpu *vcpu, u64 val)
|
||||
{
|
||||
u8 host, user;
|
||||
|
||||
if (!vcpu_has_nv(vcpu))
|
||||
return false;
|
||||
|
||||
host = SYS_FIELD_GET(ID_AA64DFR0_EL1, DoubleLock,
|
||||
read_sanitised_ftr_reg(SYS_ID_AA64DFR0_EL1));
|
||||
user = SYS_FIELD_GET(ID_AA64DFR0_EL1, DoubleLock, val);
|
||||
|
||||
return host == ID_AA64DFR0_EL1_DoubleLock_NI &&
|
||||
user == ID_AA64DFR0_EL1_DoubleLock_IMP;
|
||||
}
|
||||
|
||||
static int set_id_aa64dfr0_el1(struct kvm_vcpu *vcpu,
|
||||
const struct sys_reg_desc *rd,
|
||||
u64 val)
|
||||
@ -2028,6 +2048,11 @@ static int set_id_aa64dfr0_el1(struct kvm_vcpu *vcpu,
|
||||
if (debugver < ID_AA64DFR0_EL1_DebugVer_IMP)
|
||||
return -EINVAL;
|
||||
|
||||
if (ignore_feat_doublelock(vcpu, val)) {
|
||||
val &= ~ID_AA64DFR0_EL1_DoubleLock;
|
||||
val |= SYS_FIELD_PREP_ENUM(ID_AA64DFR0_EL1, DoubleLock, NI);
|
||||
}
|
||||
|
||||
return set_id_reg(vcpu, rd, val);
|
||||
}
|
||||
|
||||
@ -3152,8 +3177,6 @@ static const struct sys_reg_desc sys_reg_descs[] = {
|
||||
~(ID_AA64MMFR0_EL1_RES0 |
|
||||
ID_AA64MMFR0_EL1_ASIDBITS)),
|
||||
ID_WRITABLE(ID_AA64MMFR1_EL1, ~(ID_AA64MMFR1_EL1_RES0 |
|
||||
ID_AA64MMFR1_EL1_HCX |
|
||||
ID_AA64MMFR1_EL1_TWED |
|
||||
ID_AA64MMFR1_EL1_XNX |
|
||||
ID_AA64MMFR1_EL1_VH |
|
||||
ID_AA64MMFR1_EL1_VMIDBits)),
|
||||
|
||||
@ -165,7 +165,9 @@ static const struct reg_ftr_bits ftr_id_aa64mmfr0_el1[] = {
|
||||
static const struct reg_ftr_bits ftr_id_aa64mmfr1_el1[] = {
|
||||
REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64MMFR1_EL1, TIDCP1, 0),
|
||||
REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64MMFR1_EL1, AFP, 0),
|
||||
REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64MMFR1_EL1, HCX, 0),
|
||||
REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64MMFR1_EL1, ETS, 0),
|
||||
REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64MMFR1_EL1, TWED, 0),
|
||||
REG_FTR_BITS(FTR_HIGHER_SAFE, ID_AA64MMFR1_EL1, SpecSEI, 0),
|
||||
REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64MMFR1_EL1, PAN, 0),
|
||||
REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64MMFR1_EL1, LO, 0),
|
||||
|
||||
Loading…
Reference in New Issue
Block a user