[PATCH v4 1/6] KVM: arm64: Move CPU ID feature registers emulation into a separate file

Jing Zhang jingzhangos at google.com
Tue Mar 28 10:16:57 PDT 2023


Hi Marc,

On Mon, Mar 27, 2023 at 3:14 AM Marc Zyngier <maz at kernel.org> wrote:
>
> On Fri, 17 Mar 2023 05:06:32 +0000,
> Jing Zhang <jingzhangos at google.com> wrote:
> >
> > Create a new file id_regs.c for CPU ID feature registers emulation code,
> > which are moved from sys_regs.c and tweak sys_regs code accordingly.
> >
> > No functional change intended.
> >
> > Signed-off-by: Jing Zhang <jingzhangos at google.com>
> > ---
> >  arch/arm64/kvm/Makefile   |   2 +-
> >  arch/arm64/kvm/id_regs.c  | 506 ++++++++++++++++++++++++++++++++++++++
> >  arch/arm64/kvm/sys_regs.c | 464 ++--------------------------------
> >  arch/arm64/kvm/sys_regs.h |  41 +++
> >  4 files changed, 575 insertions(+), 438 deletions(-)
> >  create mode 100644 arch/arm64/kvm/id_regs.c
> >
> > diff --git a/arch/arm64/kvm/Makefile b/arch/arm64/kvm/Makefile
> > index c0c050e53157..a6a315fcd81e 100644
> > --- a/arch/arm64/kvm/Makefile
> > +++ b/arch/arm64/kvm/Makefile
> > @@ -13,7 +13,7 @@ obj-$(CONFIG_KVM) += hyp/
> >  kvm-y += arm.o mmu.o mmio.o psci.o hypercalls.o pvtime.o \
> >        inject_fault.o va_layout.o handle_exit.o \
> >        guest.o debug.o reset.o sys_regs.o stacktrace.o \
> > -      vgic-sys-reg-v3.o fpsimd.o pkvm.o \
> > +      vgic-sys-reg-v3.o fpsimd.o pkvm.o id_regs.o \
> >        arch_timer.o trng.o vmid.o emulate-nested.o nested.o \
> >        vgic/vgic.o vgic/vgic-init.o \
> >        vgic/vgic-irqfd.o vgic/vgic-v2.o \
> > diff --git a/arch/arm64/kvm/id_regs.c b/arch/arm64/kvm/id_regs.c
> > new file mode 100644
> > index 000000000000..08b738852955
> > --- /dev/null
> > +++ b/arch/arm64/kvm/id_regs.c
>
> [...]
>
> > +/**
> > + * emulate_id_reg - Emulate a guest access to an AArch64 CPU ID feature register
> > + * @vcpu: The VCPU pointer
> > + * @params: Decoded system register parameters
> > + *
> > + * Return: true if the ID register access was successful, false otherwise.
> > + */
> > +int emulate_id_reg(struct kvm_vcpu *vcpu, struct sys_reg_params *params)
> > +{
> > +     const struct sys_reg_desc *r;
> > +
> > +     r = find_reg(params, id_reg_descs, ARRAY_SIZE(id_reg_descs));
> > +
> > +     if (likely(r)) {
> > +             perform_access(vcpu, params, r);
> > +     } else {
> > +             print_sys_reg_msg(params,
> > +                               "Unsupported guest id_reg access at: %lx [%08lx]\n",
> > +                               *vcpu_pc(vcpu), *vcpu_cpsr(vcpu));
> > +             kvm_inject_undefined(vcpu);
> > +     }
> > +
> > +     return 1;
> > +}
> > +
> > +
> > +void kvm_arm_reset_id_regs(struct kvm_vcpu *vcpu)
> > +{
> > +     unsigned long i;
> > +
> > +     for (i = 0; i < ARRAY_SIZE(id_reg_descs); i++)
> > +             if (id_reg_descs[i].reset)
> > +                     id_reg_descs[i].reset(vcpu, &id_reg_descs[i]);
> > +}
>
> What does this mean? None of the idregs have a reset function, given
> that they are global. Maybe this will make sense in the following
> patches, but definitely not here.
>
You are right. It actually does nothing for idregs which have no reset function.
Will remove this.
> > +
> > +int kvm_arm_get_id_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
> > +{
> > +     return kvm_sys_reg_get_user(vcpu, reg,
> > +                                 id_reg_descs, ARRAY_SIZE(id_reg_descs));
> > +}
> > +
> > +int kvm_arm_set_id_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
> > +{
> > +     return kvm_sys_reg_set_user(vcpu, reg,
> > +                                 id_reg_descs, ARRAY_SIZE(id_reg_descs));
> > +}
> > +
> > +bool kvm_arm_check_idreg_table(void)
> > +{
> > +     return check_sysreg_table(id_reg_descs, ARRAY_SIZE(id_reg_descs), false);
> > +}
>
> All these helpers are called from sys_regs.c and directly call back
> into it. Why not simply have a helper that gets the base and size of
> the array, and stick to pure common code?
>
As you know from the later patches in this series, a per VM idregs
array and an idregs specific structure are used. All these functions
would be implemented based on that.
> > +
> > +int kvm_arm_walk_id_regs(struct kvm_vcpu *vcpu, u64 __user *uind)
> > +{
> > +     const struct sys_reg_desc *i2, *end2;
> > +     unsigned int total = 0;
> > +     int err;
> > +
> > +     i2 = id_reg_descs;
> > +     end2 = id_reg_descs + ARRAY_SIZE(id_reg_descs);
> > +
> > +     while (i2 != end2) {
> > +             err = walk_one_sys_reg(vcpu, i2++, &uind, &total);
> > +             if (err)
> > +                     return err;
> > +     }
> > +     return total;
> > +}
>
> This is an exact copy of walk_sys_regs. Surely this can be made common
> code.
The reason for not using common code is the same as last comment. An
idregs specific data structure would be used.
>
> [...]
>
> > @@ -2912,6 +2482,8 @@ void kvm_reset_sys_regs(struct kvm_vcpu *vcpu)
> >  {
> >       unsigned long i;
> >
> > +     kvm_arm_reset_id_regs(vcpu);
> > +
> >       for (i = 0; i < ARRAY_SIZE(sys_reg_descs); i++)
> >               if (sys_reg_descs[i].reset)
> >                       sys_reg_descs[i].reset(vcpu, &sys_reg_descs[i]);
> > @@ -2932,6 +2504,9 @@ int kvm_handle_sys_reg(struct kvm_vcpu *vcpu)
> >       params = esr_sys64_to_params(esr);
> >       params.regval = vcpu_get_reg(vcpu, Rt);
> >
> > +     if (is_id_reg(reg_to_encoding(&params)))
> > +             return emulate_id_reg(vcpu, &params);
> > +
> >       if (!emulate_sys_reg(vcpu, &params))
> >               return 1;
> >
> > @@ -3160,6 +2735,10 @@ int kvm_arm_sys_reg_get_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg
> >       if (err != -ENOENT)
> >               return err;
> >
> > +     err = kvm_arm_get_id_reg(vcpu, reg);
>
> Why not check for the encoding here? or in the helpers? It feels that
> this is an overhead that would be easy to reduce, given that we have
> fewer idregs than normal sysregs.
Sure, will move the encoding check here.
>
> > +     if (err != -ENOENT)
> > +             return err;
> > +
> >       return kvm_sys_reg_get_user(vcpu, reg,
> >                                   sys_reg_descs, ARRAY_SIZE(sys_reg_descs));
> >  }
> > @@ -3204,6 +2783,10 @@ int kvm_arm_sys_reg_set_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg
> >       if (err != -ENOENT)
> >               return err;
> >
> > +     err = kvm_arm_set_id_reg(vcpu, reg);
>
> Same here.
Agreed.
>
> > +     if (err != -ENOENT)
> > +             return err;
> > +
> >       return kvm_sys_reg_set_user(vcpu, reg,
> >                                   sys_reg_descs, ARRAY_SIZE(sys_reg_descs));
> >  }
> > @@ -3250,10 +2833,10 @@ static bool copy_reg_to_user(const struct sys_reg_desc *reg, u64 __user **uind)
> >       return true;
> >  }
> >
> > -static int walk_one_sys_reg(const struct kvm_vcpu *vcpu,
> > -                         const struct sys_reg_desc *rd,
> > -                         u64 __user **uind,
> > -                         unsigned int *total)
> > +int walk_one_sys_reg(const struct kvm_vcpu *vcpu,
> > +                  const struct sys_reg_desc *rd,
> > +                  u64 __user **uind,
> > +                  unsigned int *total)
> >  {
> >       /*
> >        * Ignore registers we trap but don't save,
> > @@ -3294,6 +2877,7 @@ unsigned long kvm_arm_num_sys_reg_descs(struct kvm_vcpu *vcpu)
> >  {
> >       return ARRAY_SIZE(invariant_sys_regs)
> >               + num_demux_regs()
> > +             + kvm_arm_walk_id_regs(vcpu, (u64 __user *)NULL)
> >               + walk_sys_regs(vcpu, (u64 __user *)NULL);
> >  }
> >
> > @@ -3309,6 +2893,11 @@ int kvm_arm_copy_sys_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices)
> >               uindices++;
> >       }
> >
> > +     err = kvm_arm_walk_id_regs(vcpu, uindices);
> > +     if (err < 0)
> > +             return err;
> > +     uindices += err;
> > +
> >       err = walk_sys_regs(vcpu, uindices);
> >       if (err < 0)
> >               return err;
> > @@ -3323,6 +2912,7 @@ int __init kvm_sys_reg_table_init(void)
> >       unsigned int i;
> >
> >       /* Make sure tables are unique and in order. */
> > +     valid &= kvm_arm_check_idreg_table();
> >       valid &= check_sysreg_table(sys_reg_descs, ARRAY_SIZE(sys_reg_descs), false);
> >       valid &= check_sysreg_table(cp14_regs, ARRAY_SIZE(cp14_regs), true);
> >       valid &= check_sysreg_table(cp14_64_regs, ARRAY_SIZE(cp14_64_regs), true);
> > diff --git a/arch/arm64/kvm/sys_regs.h b/arch/arm64/kvm/sys_regs.h
> > index 6b11f2cc7146..ad41305348f7 100644
> > --- a/arch/arm64/kvm/sys_regs.h
> > +++ b/arch/arm64/kvm/sys_regs.h
> > @@ -210,6 +210,35 @@ find_reg(const struct sys_reg_params *params, const struct sys_reg_desc table[],
> >       return __inline_bsearch((void *)pval, table, num, sizeof(table[0]), match_sys_reg);
> >  }
> >
> > +static inline unsigned int raz_visibility(const struct kvm_vcpu *vcpu,
> > +                                       const struct sys_reg_desc *r)
> > +{
> > +     return REG_RAZ;
> > +}
>
> No, please. This is used as a function pointer. You now potentially
> force the compiler to emit as many copy of this as there are pointers.
>
Thanks, will fix this.
> > +
> > +static inline bool write_to_read_only(struct kvm_vcpu *vcpu,
> > +                                   struct sys_reg_params *params,
> > +                                   const struct sys_reg_desc *r)
> > +{
> > +     WARN_ONCE(1, "Unexpected sys_reg write to read-only register\n");
> > +     print_sys_reg_instr(params);
> > +     kvm_inject_undefined(vcpu);
> > +     return false;
> > +}
>
> Please make this common code, and not an inline function.
Sure, will do.
>
> Thanks,
>
>         M.
>
> --
> Without deviation from the norm, progress is not possible.
Thanks,
Jing



More information about the linux-arm-kernel mailing list