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 <[email protected]>
> ---
>  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 

_______________________________________________
kvmarm mailing list
[email protected]
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

Reply via email to