[RFC PATCH 04/25] KVM: arm64: Introduce struct id_reg_info
Andrew Jones
drjones at redhat.com
Fri Oct 15 06:47:41 PDT 2021
On Mon, Oct 11, 2021 at 09:35:14PM -0700, Reiji Watanabe wrote:
> This patch lays the groundwork to make ID registers writable.
>
> Introduce struct id_reg_info for an ID register to manage the
> register specific control of its value for the guest.
> It is used to do register specific initialization, validation
> of the ID register and etc. Not all ID registers must have
> the id_reg_info. ID registers that don't have the id_reg_info
> are handled in a common way that is applied to all other
> ID registers.
>
> At present, changing an ID register from userspace is allowed only
> if the ID register has the id_reg_info, but that will be changed
> by the following patches.
>
> No ID register has the structure yet and the following patches
> will add the id_reg_info for some ID registers.
>
> Signed-off-by: Reiji Watanabe <reijiw at google.com>
> ---
> arch/arm64/kvm/sys_regs.c | 120 +++++++++++++++++++++++++++++++++++---
> 1 file changed, 111 insertions(+), 9 deletions(-)
>
> diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
> index 72ca518e7944..8a0b88f9a975 100644
> --- a/arch/arm64/kvm/sys_regs.c
> +++ b/arch/arm64/kvm/sys_regs.c
> @@ -263,6 +263,76 @@ static bool trap_raz_wi(struct kvm_vcpu *vcpu,
> return read_zero(vcpu, p);
> }
>
> +struct id_reg_info {
> + u32 sys_reg; /* Register ID */
> + u64 sys_val; /* Sanitized system value */
> +
> + /*
> + * Limit value of the register for a vcpu. The value is sys_val
> + * with bits cleared for unsupported features for the guest.
> + */
> + u64 vcpu_limit_val;
Maybe I'll see a need for both later, but at the moment I'd think we only
need sys_val with the bits cleared for disabled features.
> +
> + /* Bits that are not validated when userspace sets the register. */
> + u64 ignore_mask;
> +
> + /* Initialization function of the id_reg_info */
> + void (*init)(struct id_reg_info *id_reg);
> +
> + /* Register specific validation function */
> + int (*validate)(struct kvm_vcpu *vcpu, u64 val);
> +
> + /* Return the reset value of the register for the vCPU */
> + u64 (*get_reset_val)(struct kvm_vcpu *vcpu, struct id_reg_info *id_reg);
> +};
> +
> +static void id_reg_info_init(struct id_reg_info *id_reg)
> +{
> + id_reg->sys_val = read_sanitised_ftr_reg(id_reg->sys_reg);
> + id_reg->vcpu_limit_val = id_reg->sys_val;
> +}
> +
> +/*
> + * An ID register that needs special handling to control the value for the
> + * guest must have its own id_reg_info in id_reg_info_table.
> + * (i.e. the reset value is different from the host's sanitized value,
> + * the value is affected by opt-in features, some fields needs specific
> + * validation, etc.)
> + */
> +#define GET_ID_REG_INFO(id) (id_reg_info_table[IDREG_IDX(id)])
> +static struct id_reg_info *id_reg_info_table[KVM_ARM_ID_REG_MAX_NUM] = {};
> +
> +static int validate_id_reg(struct kvm_vcpu *vcpu,
> + const struct sys_reg_desc *rd, u64 val)
> +{
> + u32 id = reg_to_encoding(rd);
> + const struct id_reg_info *id_reg = GET_ID_REG_INFO(id);
> + u64 limit;
> + u64 check_val = val;
> + int err;
> +
> + if (id_reg) {
> + /*
> + * Clear bits with ignore_mask, which we don't want
> + * arm64_check_features to check.
> + */
> + check_val &= ~id_reg->ignore_mask;
> + limit = id_reg->vcpu_limit_val;
> + } else
> + limit = read_sanitised_ftr_reg(id);
> +
> + /* Check if the value indicates any feature that is not in the limit. */
> + err = arm64_check_features(id, check_val, limit);
> + if (err)
> + return err;
> +
> + if (id_reg && id_reg->validate)
> + /* Run the ID register specific validity check. */
> + err = id_reg->validate(vcpu, val);
> +
> + return err;
> +}
> +
> /*
> * ARMv8.1 mandates at least a trivial LORegion implementation, where all the
> * RW registers are RES0 (which we can implement as RAZ/WI). On an ARMv8.0
> @@ -1176,11 +1246,19 @@ static unsigned int sve_visibility(const struct kvm_vcpu *vcpu,
> static void reset_id_reg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd)
> {
> u32 id = reg_to_encoding(rd);
> + struct id_reg_info *id_reg;
> + u64 val;
>
> if (vcpu_has_reset_once(vcpu))
> return;
>
> - __vcpu_sys_reg(vcpu, IDREG_SYS_IDX(id)) = read_sanitised_ftr_reg(id);
> + id_reg = GET_ID_REG_INFO(id);
> + if (id_reg && id_reg->get_reset_val)
> + val = id_reg->get_reset_val(vcpu, id_reg);
> + else
> + val = read_sanitised_ftr_reg(id);
> +
> + __vcpu_sys_reg(vcpu, IDREG_SYS_IDX(id)) = val;
> }
>
> static int set_id_aa64pfr0_el1(struct kvm_vcpu *vcpu,
> @@ -1225,11 +1303,7 @@ static int set_id_aa64pfr0_el1(struct kvm_vcpu *vcpu,
> return 0;
> }
>
> -/*
> - * cpufeature ID register user accessors
> - *
> - * We don't allow the effective value to be changed.
> - */
> +/* cpufeature ID register user accessors */
> static int __get_id_reg(const struct kvm_vcpu *vcpu,
> const struct sys_reg_desc *rd, void __user *uaddr,
> bool raz)
> @@ -1240,11 +1314,12 @@ static int __get_id_reg(const struct kvm_vcpu *vcpu,
> return reg_to_user(uaddr, &val, id);
> }
>
> -static int __set_id_reg(const struct kvm_vcpu *vcpu,
> +static int __set_id_reg(struct kvm_vcpu *vcpu,
> const struct sys_reg_desc *rd, void __user *uaddr,
> bool raz)
> {
> const u64 id = sys_reg_to_index(rd);
> + u32 encoding = reg_to_encoding(rd);
> int err;
> u64 val;
>
> @@ -1252,10 +1327,18 @@ static int __set_id_reg(const struct kvm_vcpu *vcpu,
> if (err)
> return err;
>
> - /* This is what we mean by invariant: you can't change it. */
> - if (val != read_id_reg(vcpu, rd, raz))
> + /* Don't allow to change the reg unless the reg has id_reg_info */
> + if (val != read_id_reg(vcpu, rd, raz) && !GET_ID_REG_INFO(encoding))
> return -EINVAL;
>
> + if (raz)
> + return (val == 0) ? 0 : -EINVAL;
This is already covered by the val != read_id_reg(vcpu, rd, raz) check.
> +
> + err = validate_id_reg(vcpu, rd, val);
> + if (err)
> + return err;
> +
> + __vcpu_sys_reg(vcpu, IDREG_SYS_IDX(encoding)) = val;
> return 0;
> }
>
> @@ -2818,6 +2901,23 @@ int kvm_arm_copy_sys_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices)
> return write_demux_regids(uindices);
> }
>
> +static void id_reg_info_init_all(void)
> +{
> + int i;
> + struct id_reg_info *id_reg;
> +
> + for (i = 0; i < ARRAY_SIZE(id_reg_info_table); i++) {
> + id_reg = (struct id_reg_info *)id_reg_info_table[i];
> + if (!id_reg)
> + continue;
> +
> + if (id_reg->init)
> + id_reg->init(id_reg);
> + else
> + id_reg_info_init(id_reg);
Maybe call id_reg->init(id_reg) from within id_reg_info_init() in case we
wanted to apply some common id register initialization at some point?
> + }
> +}
> +
> void kvm_sys_reg_table_init(void)
> {
> unsigned int i;
> @@ -2852,4 +2952,6 @@ void kvm_sys_reg_table_init(void)
> break;
> /* Clear all higher bits. */
> cache_levels &= (1 << (i*3))-1;
> +
> + id_reg_info_init_all();
> }
> --
> 2.33.0.882.g93a45727a2-goog
>
Thanks,
drew
More information about the linux-arm-kernel
mailing list