[PATCH v2 4/6] KVM: arm64: Account for RES1 bits in DECLARE_FEAT_MAP() and co
Fuad Tabba
tabba at google.com
Thu Dec 11 06:30:03 PST 2025
On Wed, 10 Dec 2025 at 17:30, Marc Zyngier <maz at kernel.org> wrote:
>
> 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.
>
> Signed-off-by: Marc Zyngier <maz at kernel.org>
Reviewed-by: Fuad Tabba <tabba at google.com>
/fuad
> ---
> arch/arm64/include/asm/kvm_host.h | 1 +
> arch/arm64/kvm/config.c | 25 +++++++-------
> arch/arm64/kvm/emulate-nested.c | 55 +++++++++++++++++--------------
> 3 files changed, 45 insertions(+), 36 deletions(-)
>
> diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
> index ac7f970c78830..b552a1e03848c 100644
> --- a/arch/arm64/include/asm/kvm_host.h
> +++ b/arch/arm64/include/asm/kvm_host.h
> @@ -638,6 +638,7 @@ struct fgt_masks {
> u64 mask;
> u64 nmask;
> u64 res0;
> + u64 res1;
> };
>
> extern struct fgt_masks hfgrtr_masks;
> diff --git a/arch/arm64/kvm/config.c b/arch/arm64/kvm/config.c
> index 24bb3f36e9d59..3845b188551b6 100644
> --- a/arch/arm64/kvm/config.c
> +++ b/arch/arm64/kvm/config.c
> @@ -16,14 +16,14 @@
> */
> struct reg_bits_to_feat_map {
> union {
> - u64 bits;
> - u64 *res0p;
> + u64 bits;
> + struct fgt_masks *masks;
> };
>
> #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 */
> +#define MASKS_POINTER BIT(3) /* Pointer to fgt_masks struct instead of bits */
>
> unsigned long flags;
>
> @@ -92,8 +92,8 @@ struct reg_feat_map_desc {
> #define NEEDS_FEAT_FIXED(m, ...) \
> __NEEDS_FEAT_FLAG(m, FIXED_VALUE, bits, __VA_ARGS__, 0)
>
> -#define NEEDS_FEAT_RES0(p, ...) \
> - __NEEDS_FEAT_FLAG(p, RES0_POINTER, res0p, __VA_ARGS__)
> +#define NEEDS_FEAT_MASKS(p, ...) \
> + __NEEDS_FEAT_FLAG(p, MASKS_POINTER, masks, __VA_ARGS__)
>
> /*
> * 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) \
> struct reg_feat_map_desc n = { \
> .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_sz = ARRAY_SIZE(m), \
> }
>
> /*
> * 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) \
> struct reg_feat_map_desc n = { \
> .name = #msk, \
> - .feat_map = NEEDS_FEAT_RES0(&msk.res0, f),\
> + .feat_map = NEEDS_FEAT_MASKS(&msk, f), \
> .bit_feat_map = 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);
>
> 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;
>
> for (int i = 0; i < map_size; i++)
> mask |= map[i].bits;
>
> - if (mask != ~res0)
> + if (mask != ~resx)
> 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)
> {
> - 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)
> diff --git a/arch/arm64/kvm/emulate-nested.c b/arch/arm64/kvm/emulate-nested.c
> index 834f13fb1fb7d..75d49f83342a5 100644
> --- a/arch/arm64/kvm/emulate-nested.c
> +++ b/arch/arm64/kvm/emulate-nested.c
> @@ -2105,23 +2105,24 @@ static u32 encoding_next(u32 encoding)
> }
>
> #define FGT_MASKS(__n, __m) \
> - struct fgt_masks __n = { .str = #__m, .res0 = __m, }
> -
> -FGT_MASKS(hfgrtr_masks, HFGRTR_EL2_RES0);
> -FGT_MASKS(hfgwtr_masks, HFGWTR_EL2_RES0);
> -FGT_MASKS(hfgitr_masks, HFGITR_EL2_RES0);
> -FGT_MASKS(hdfgrtr_masks, HDFGRTR_EL2_RES0);
> -FGT_MASKS(hdfgwtr_masks, HDFGWTR_EL2_RES0);
> -FGT_MASKS(hafgrtr_masks, HAFGRTR_EL2_RES0);
> -FGT_MASKS(hfgrtr2_masks, HFGRTR2_EL2_RES0);
> -FGT_MASKS(hfgwtr2_masks, HFGWTR2_EL2_RES0);
> -FGT_MASKS(hfgitr2_masks, HFGITR2_EL2_RES0);
> -FGT_MASKS(hdfgrtr2_masks, HDFGRTR2_EL2_RES0);
> -FGT_MASKS(hdfgwtr2_masks, HDFGWTR2_EL2_RES0);
> + struct fgt_masks __n = { .str = #__m, .res0 = __m ## _RES0, .res1 = __m ## _RES1 }
> +
> +FGT_MASKS(hfgrtr_masks, HFGRTR_EL2);
> +FGT_MASKS(hfgwtr_masks, HFGWTR_EL2);
> +FGT_MASKS(hfgitr_masks, HFGITR_EL2);
> +FGT_MASKS(hdfgrtr_masks, HDFGRTR_EL2);
> +FGT_MASKS(hdfgwtr_masks, HDFGWTR_EL2);
> +FGT_MASKS(hafgrtr_masks, HAFGRTR_EL2);
> +FGT_MASKS(hfgrtr2_masks, HFGRTR2_EL2);
> +FGT_MASKS(hfgwtr2_masks, HFGWTR2_EL2);
> +FGT_MASKS(hfgitr2_masks, HFGITR2_EL2);
> +FGT_MASKS(hdfgrtr2_masks, HDFGRTR2_EL2);
> +FGT_MASKS(hdfgwtr2_masks, HDFGWTR2_EL2);
>
> static __init bool aggregate_fgt(union trap_config tc)
> {
> struct fgt_masks *rmasks, *wmasks;
> + u64 rresx, wresx;
>
> switch (tc.fgt) {
> case HFGRTR_GROUP:
> @@ -2154,24 +2155,27 @@ static __init bool aggregate_fgt(union trap_config tc)
> 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
> * not both.
> */
> - if ((BIT(tc.bit) & rmasks->res0) &&
> - (!wmasks || (BIT(tc.bit) & wmasks->res0)))
> + if ((BIT(tc.bit) & rresx) && (!wmasks || (BIT(tc.bit) & wresx)))
> return false;
>
> if (tc.pol)
> - rmasks->mask |= BIT(tc.bit) & ~rmasks->res0;
> + rmasks->mask |= BIT(tc.bit) & ~rresx;
> else
> - rmasks->nmask |= BIT(tc.bit) & ~rmasks->res0;
> + rmasks->nmask |= BIT(tc.bit) & ~rresx;
>
> if (wmasks) {
> if (tc.pol)
> - wmasks->mask |= BIT(tc.bit) & ~wmasks->res0;
> + wmasks->mask |= BIT(tc.bit) & ~wresx;
> else
> - wmasks->nmask |= BIT(tc.bit) & ~wmasks->res0;
> + wmasks->nmask |= BIT(tc.bit) & ~wresx;
> }
>
> 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)
> {
> unsigned long duplicate = masks->mask & masks->nmask;
> - u64 res0 = masks->res0;
> int ret = 0;
>
> if (duplicate) {
> @@ -2194,10 +2197,14 @@ static __init int check_fgt_masks(struct fgt_masks *masks)
> ret = -EINVAL;
> }
>
> - masks->res0 = ~(masks->mask | masks->nmask);
> - if (masks->res0 != res0)
> - kvm_info("Implicit %s = %016llx, expecting %016llx\n",
> - masks->str, masks->res0, res0);
> + if ((masks->res0 | masks->res1 | masks->mask | masks->nmask) != GENMASK(63, 0) ||
> + (masks->res0 & masks->res1) || (masks->res0 & masks->mask) ||
> + (masks->res0 & masks->nmask) || (masks->res1 & masks->mask) ||
> + (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;
> }
> --
> 2.47.3
>
More information about the linux-arm-kernel
mailing list