mirror of
https://github.com/torvalds/linux.git
synced 2026-06-01 19:13:47 +02:00
KVM: arm64: Account for RES1 bits in DECLARE_FEAT_MAP() and co
None of the registers we manage in the feature dependency infrastructure so far has any RES1 bit. This is about to change, as VTCR_EL2 has its bit 31 being RES1. In order to not fail the consistency checks by not describing a bit, add RES1 bits to the set of immutable bits. This requires some extra surgery for the FGT handling, as we now need to track RES1 bits there as well. There are no RES1 FGT bits *yet*. Watch this space. Reviewed-by: Fuad Tabba <tabba@google.com> Tested-by: Sascha Bischoff <sascha.bischoff@arm.com> Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com> Tested-by: Fuad Tabba <tabba@google.com> Link: https://patch.msgid.link/20251210173024.561160-5-maz@kernel.org Signed-off-by: Marc Zyngier <maz@kernel.org>
This commit is contained in:
parent
a035001dea
commit
c259d763e6
|
|
@ -638,6 +638,7 @@ struct fgt_masks {
|
||||||
u64 mask;
|
u64 mask;
|
||||||
u64 nmask;
|
u64 nmask;
|
||||||
u64 res0;
|
u64 res0;
|
||||||
|
u64 res1;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern struct fgt_masks hfgrtr_masks;
|
extern struct fgt_masks hfgrtr_masks;
|
||||||
|
|
|
||||||
|
|
@ -17,13 +17,13 @@
|
||||||
struct reg_bits_to_feat_map {
|
struct reg_bits_to_feat_map {
|
||||||
union {
|
union {
|
||||||
u64 bits;
|
u64 bits;
|
||||||
u64 *res0p;
|
struct fgt_masks *masks;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define NEVER_FGU BIT(0) /* Can trap, but never UNDEF */
|
#define NEVER_FGU BIT(0) /* Can trap, but never UNDEF */
|
||||||
#define CALL_FUNC BIT(1) /* Needs to evaluate tons of crap */
|
#define CALL_FUNC BIT(1) /* Needs to evaluate tons of crap */
|
||||||
#define FIXED_VALUE BIT(2) /* RAZ/WI or RAO/WI in KVM */
|
#define FIXED_VALUE BIT(2) /* RAZ/WI or RAO/WI in KVM */
|
||||||
#define RES0_POINTER BIT(3) /* Pointer to RES0 value instead of bits */
|
#define MASKS_POINTER BIT(3) /* Pointer to fgt_masks struct instead of bits */
|
||||||
|
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
|
||||||
|
|
@ -92,8 +92,8 @@ struct reg_feat_map_desc {
|
||||||
#define NEEDS_FEAT_FIXED(m, ...) \
|
#define NEEDS_FEAT_FIXED(m, ...) \
|
||||||
__NEEDS_FEAT_FLAG(m, FIXED_VALUE, bits, __VA_ARGS__, 0)
|
__NEEDS_FEAT_FLAG(m, FIXED_VALUE, bits, __VA_ARGS__, 0)
|
||||||
|
|
||||||
#define NEEDS_FEAT_RES0(p, ...) \
|
#define NEEDS_FEAT_MASKS(p, ...) \
|
||||||
__NEEDS_FEAT_FLAG(p, RES0_POINTER, res0p, __VA_ARGS__)
|
__NEEDS_FEAT_FLAG(p, MASKS_POINTER, masks, __VA_ARGS__)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Declare the dependency between a set of bits and a set of features,
|
* Declare the dependency between a set of bits and a set of features,
|
||||||
|
|
@ -109,19 +109,20 @@ struct reg_feat_map_desc {
|
||||||
#define DECLARE_FEAT_MAP(n, r, m, f) \
|
#define DECLARE_FEAT_MAP(n, r, m, f) \
|
||||||
struct reg_feat_map_desc n = { \
|
struct reg_feat_map_desc n = { \
|
||||||
.name = #r, \
|
.name = #r, \
|
||||||
.feat_map = NEEDS_FEAT(~r##_RES0, f), \
|
.feat_map = NEEDS_FEAT(~(r##_RES0 | \
|
||||||
|
r##_RES1), f), \
|
||||||
.bit_feat_map = m, \
|
.bit_feat_map = m, \
|
||||||
.bit_feat_map_sz = ARRAY_SIZE(m), \
|
.bit_feat_map_sz = ARRAY_SIZE(m), \
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Specialised version of the above for FGT registers that have their
|
* Specialised version of the above for FGT registers that have their
|
||||||
* RES0 masks described as struct fgt_masks.
|
* RESx masks described as struct fgt_masks.
|
||||||
*/
|
*/
|
||||||
#define DECLARE_FEAT_MAP_FGT(n, msk, m, f) \
|
#define DECLARE_FEAT_MAP_FGT(n, msk, m, f) \
|
||||||
struct reg_feat_map_desc n = { \
|
struct reg_feat_map_desc n = { \
|
||||||
.name = #msk, \
|
.name = #msk, \
|
||||||
.feat_map = NEEDS_FEAT_RES0(&msk.res0, f),\
|
.feat_map = NEEDS_FEAT_MASKS(&msk, f), \
|
||||||
.bit_feat_map = m, \
|
.bit_feat_map = m, \
|
||||||
.bit_feat_map_sz = ARRAY_SIZE(m), \
|
.bit_feat_map_sz = ARRAY_SIZE(m), \
|
||||||
}
|
}
|
||||||
|
|
@ -1168,21 +1169,21 @@ static const DECLARE_FEAT_MAP(mdcr_el2_desc, MDCR_EL2,
|
||||||
mdcr_el2_feat_map, FEAT_AA64EL2);
|
mdcr_el2_feat_map, FEAT_AA64EL2);
|
||||||
|
|
||||||
static void __init check_feat_map(const struct reg_bits_to_feat_map *map,
|
static void __init check_feat_map(const struct reg_bits_to_feat_map *map,
|
||||||
int map_size, u64 res0, const char *str)
|
int map_size, u64 resx, const char *str)
|
||||||
{
|
{
|
||||||
u64 mask = 0;
|
u64 mask = 0;
|
||||||
|
|
||||||
for (int i = 0; i < map_size; i++)
|
for (int i = 0; i < map_size; i++)
|
||||||
mask |= map[i].bits;
|
mask |= map[i].bits;
|
||||||
|
|
||||||
if (mask != ~res0)
|
if (mask != ~resx)
|
||||||
kvm_err("Undefined %s behaviour, bits %016llx\n",
|
kvm_err("Undefined %s behaviour, bits %016llx\n",
|
||||||
str, mask ^ ~res0);
|
str, mask ^ ~resx);
|
||||||
}
|
}
|
||||||
|
|
||||||
static u64 reg_feat_map_bits(const struct reg_bits_to_feat_map *map)
|
static u64 reg_feat_map_bits(const struct reg_bits_to_feat_map *map)
|
||||||
{
|
{
|
||||||
return map->flags & RES0_POINTER ? ~(*map->res0p) : map->bits;
|
return map->flags & MASKS_POINTER ? (map->masks->mask | map->masks->nmask) : map->bits;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void __init check_reg_desc(const struct reg_feat_map_desc *r)
|
static void __init check_reg_desc(const struct reg_feat_map_desc *r)
|
||||||
|
|
|
||||||
|
|
@ -2105,23 +2105,24 @@ static u32 encoding_next(u32 encoding)
|
||||||
}
|
}
|
||||||
|
|
||||||
#define FGT_MASKS(__n, __m) \
|
#define FGT_MASKS(__n, __m) \
|
||||||
struct fgt_masks __n = { .str = #__m, .res0 = __m, }
|
struct fgt_masks __n = { .str = #__m, .res0 = __m ## _RES0, .res1 = __m ## _RES1 }
|
||||||
|
|
||||||
FGT_MASKS(hfgrtr_masks, HFGRTR_EL2_RES0);
|
FGT_MASKS(hfgrtr_masks, HFGRTR_EL2);
|
||||||
FGT_MASKS(hfgwtr_masks, HFGWTR_EL2_RES0);
|
FGT_MASKS(hfgwtr_masks, HFGWTR_EL2);
|
||||||
FGT_MASKS(hfgitr_masks, HFGITR_EL2_RES0);
|
FGT_MASKS(hfgitr_masks, HFGITR_EL2);
|
||||||
FGT_MASKS(hdfgrtr_masks, HDFGRTR_EL2_RES0);
|
FGT_MASKS(hdfgrtr_masks, HDFGRTR_EL2);
|
||||||
FGT_MASKS(hdfgwtr_masks, HDFGWTR_EL2_RES0);
|
FGT_MASKS(hdfgwtr_masks, HDFGWTR_EL2);
|
||||||
FGT_MASKS(hafgrtr_masks, HAFGRTR_EL2_RES0);
|
FGT_MASKS(hafgrtr_masks, HAFGRTR_EL2);
|
||||||
FGT_MASKS(hfgrtr2_masks, HFGRTR2_EL2_RES0);
|
FGT_MASKS(hfgrtr2_masks, HFGRTR2_EL2);
|
||||||
FGT_MASKS(hfgwtr2_masks, HFGWTR2_EL2_RES0);
|
FGT_MASKS(hfgwtr2_masks, HFGWTR2_EL2);
|
||||||
FGT_MASKS(hfgitr2_masks, HFGITR2_EL2_RES0);
|
FGT_MASKS(hfgitr2_masks, HFGITR2_EL2);
|
||||||
FGT_MASKS(hdfgrtr2_masks, HDFGRTR2_EL2_RES0);
|
FGT_MASKS(hdfgrtr2_masks, HDFGRTR2_EL2);
|
||||||
FGT_MASKS(hdfgwtr2_masks, HDFGWTR2_EL2_RES0);
|
FGT_MASKS(hdfgwtr2_masks, HDFGWTR2_EL2);
|
||||||
|
|
||||||
static __init bool aggregate_fgt(union trap_config tc)
|
static __init bool aggregate_fgt(union trap_config tc)
|
||||||
{
|
{
|
||||||
struct fgt_masks *rmasks, *wmasks;
|
struct fgt_masks *rmasks, *wmasks;
|
||||||
|
u64 rresx, wresx;
|
||||||
|
|
||||||
switch (tc.fgt) {
|
switch (tc.fgt) {
|
||||||
case HFGRTR_GROUP:
|
case HFGRTR_GROUP:
|
||||||
|
|
@ -2154,24 +2155,27 @@ static __init bool aggregate_fgt(union trap_config tc)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rresx = rmasks->res0 | rmasks->res1;
|
||||||
|
if (wmasks)
|
||||||
|
wresx = wmasks->res0 | wmasks->res1;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* A bit can be reserved in either the R or W register, but
|
* A bit can be reserved in either the R or W register, but
|
||||||
* not both.
|
* not both.
|
||||||
*/
|
*/
|
||||||
if ((BIT(tc.bit) & rmasks->res0) &&
|
if ((BIT(tc.bit) & rresx) && (!wmasks || (BIT(tc.bit) & wresx)))
|
||||||
(!wmasks || (BIT(tc.bit) & wmasks->res0)))
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (tc.pol)
|
if (tc.pol)
|
||||||
rmasks->mask |= BIT(tc.bit) & ~rmasks->res0;
|
rmasks->mask |= BIT(tc.bit) & ~rresx;
|
||||||
else
|
else
|
||||||
rmasks->nmask |= BIT(tc.bit) & ~rmasks->res0;
|
rmasks->nmask |= BIT(tc.bit) & ~rresx;
|
||||||
|
|
||||||
if (wmasks) {
|
if (wmasks) {
|
||||||
if (tc.pol)
|
if (tc.pol)
|
||||||
wmasks->mask |= BIT(tc.bit) & ~wmasks->res0;
|
wmasks->mask |= BIT(tc.bit) & ~wresx;
|
||||||
else
|
else
|
||||||
wmasks->nmask |= BIT(tc.bit) & ~wmasks->res0;
|
wmasks->nmask |= BIT(tc.bit) & ~wresx;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -2180,7 +2184,6 @@ static __init bool aggregate_fgt(union trap_config tc)
|
||||||
static __init int check_fgt_masks(struct fgt_masks *masks)
|
static __init int check_fgt_masks(struct fgt_masks *masks)
|
||||||
{
|
{
|
||||||
unsigned long duplicate = masks->mask & masks->nmask;
|
unsigned long duplicate = masks->mask & masks->nmask;
|
||||||
u64 res0 = masks->res0;
|
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
if (duplicate) {
|
if (duplicate) {
|
||||||
|
|
@ -2194,10 +2197,14 @@ static __init int check_fgt_masks(struct fgt_masks *masks)
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
masks->res0 = ~(masks->mask | masks->nmask);
|
if ((masks->res0 | masks->res1 | masks->mask | masks->nmask) != GENMASK(63, 0) ||
|
||||||
if (masks->res0 != res0)
|
(masks->res0 & masks->res1) || (masks->res0 & masks->mask) ||
|
||||||
kvm_info("Implicit %s = %016llx, expecting %016llx\n",
|
(masks->res0 & masks->nmask) || (masks->res1 & masks->mask) ||
|
||||||
masks->str, masks->res0, res0);
|
(masks->res1 & masks->nmask) || (masks->mask & masks->nmask)) {
|
||||||
|
kvm_info("Inconsistent masks for %s (%016llx, %016llx, %016llx, %016llx)\n",
|
||||||
|
masks->str, masks->res0, masks->res1, masks->mask, masks->nmask);
|
||||||
|
masks->res0 = ~(masks->res1 | masks->mask | masks->nmask);
|
||||||
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user