Re: [PATCH 15/17] ARM: KVM: KVM_GET_MSR_INDEX_LIST/KVM_GET_MSRS/KVM_SET_MSRS for cp15 regs.

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Thu, Jul 12, 2012 at 7:54 AM, Rusty Russell <rusty.russell@xxxxxxxxxx> wrote:
> Now we have a table for all the cp15 registers, we can drive a generic
> API.  x86 already has one for sparse-numbered registers, so we simply
> reproduce that.  The only difference is that our KVM_GET_MSR_INDEX_LIST
> is a per-vcpu ioctl; we can't know the MSRs until we know the cpu type.
>
> The numbering for the indices for coprocesors is simple, if userspace
> cares (it might not for simple save and restore): the upper 16 bits
> are the coprocessor number.  If it's > 15, it's something else, for
> future expansion.
>
> Bit 15 indicates a 64-bit register.  For 64 bit registers the bottom 4
> bits are CRm, the next 4 are opc1 (just like the MCRR/MRCC instruction
> encoding).  For 32 bit registers, the bottom 4 bits are CRm, the next
> 3 are opc2, the next 4 CRn, and the next 3 opc1 (the same order as the
> MRC/MCR instruction encoding, but not the same bit positions).
>
> 64-bit coprocessor register:
>        ...|19 18 17 16|15|14 13 12 11 10  9  8| 7  6  5  4 |3  2  1  0|
>   ...0  0 |  cp num   | 1| 0  0  0  0  0  0  0|   opc1     |   CRm    |
>
> 32-bit coprocessor register:
>        ...|19 18 17 16|15|14|13 12 11|10  9  8  7 |6  5  4 |3  2  1  0|
>   ...0  0 |  cp num   | 0| 0|  opc1  |    CRn     | opc2   |   CRm    |
>
> Non-coprocessor register:
>
>    | 32 31 30 29 28 27 26 25 24 23 22 21 20|19 18 17 16 15 ...
>    |     < some non-zero value >           | ...
>
> Signed-off-by: Rusty Russell <rusty@xxxxxxxxxxxxxxx>
> ---
>  arch/arm/include/asm/kvm.h        |   31 +++++
>  arch/arm/include/asm/kvm_coproc.h |    7 ++
>  arch/arm/kvm/arm.c                |   29 +++++
>  arch/arm/kvm/coproc.c             |  237 +++++++++++++++++++++++++++++++++++++
>  4 files changed, 304 insertions(+)
>
> diff --git a/arch/arm/include/asm/kvm.h b/arch/arm/include/asm/kvm.h
> index 09b5be3..d6531e8 100644
> --- a/arch/arm/include/asm/kvm.h
> +++ b/arch/arm/include/asm/kvm.h
> @@ -92,4 +92,35 @@ struct kvm_sync_regs {
>  struct kvm_arch_memory_slot {
>  };
>
> +/* Exactly like x86. */
> +struct kvm_msr_entry {
> +       __u32 index;
> +       __u32 reserved;
> +       __u64 data;
> +};

makes sense to move this to include/linux/kvm.h ?

> +
> +/* for KVM_GET_MSRS and KVM_SET_MSRS */
> +struct kvm_msrs {
> +       __u32 nmsrs; /* number of msrs in entries */
> +       __u32 pad;
> +
> +       struct kvm_msr_entry entries[0];
> +};
> +
> +/* for KVM_GET_MSR_INDEX_LIST */
> +struct kvm_msr_list {
> +       __u32 nmsrs; /* number of msrs in entries */
> +       __u32 indices[0];
> +};
> +
> +/* If you need to interpret the index values, here's the key. */
> +#define KVM_ARM_MSR_COPROC_MASK                0xFFFF0000
> +#define KVM_ARM_MSR_64_BIT_MASK                0x00008000
> +#define KVM_ARM_MSR_64_OPC1_MASK       0x000000F0
> +#define KVM_ARM_MSR_64_CRM_MASK                0x0000000F
> +#define KVM_ARM_MSR_32_CRM_MASK                0x0000000F
> +#define KVM_ARM_MSR_32_OPC2_MASK       0x00000070
> +#define KVM_ARM_MSR_32_CRN_MASK                0x00000780
> +#define KVM_ARM_MSR_32_OPC1_MASK       0x00003800
> +
>  #endif /* __ARM_KVM_H__ */
> diff --git a/arch/arm/include/asm/kvm_coproc.h b/arch/arm/include/asm/kvm_coproc.h
> index 6bed190..31e0b12 100644
> --- a/arch/arm/include/asm/kvm_coproc.h
> +++ b/arch/arm/include/asm/kvm_coproc.h
> @@ -25,4 +25,11 @@ int kvm_handle_cp14_load_store(struct kvm_vcpu *vcpu, struct kvm_run *run);
>  int kvm_handle_cp14_access(struct kvm_vcpu *vcpu, struct kvm_run *run);
>  int kvm_handle_cp15_32(struct kvm_vcpu *vcpu, struct kvm_run *run);
>  int kvm_handle_cp15_64(struct kvm_vcpu *vcpu, struct kvm_run *run);
> +
> +int kvm_arm_get_msrs(struct kvm_vcpu *vcpu,
> +                    struct kvm_msr_entry __user *entries, u32 num);
> +int kvm_arm_set_msrs(struct kvm_vcpu *vcpu,
> +                    struct kvm_msr_entry __user *entries, u32 num);
> +unsigned long kvm_arm_num_guest_msrs(struct kvm_vcpu *vcpu);
> +int kvm_copy_msrindices(struct kvm_vcpu *vcpu, u32 __user *uindices);
>  #endif /* __ARM_KVM_COPROC_H__ */
> diff --git a/arch/arm/kvm/arm.c b/arch/arm/kvm/arm.c
> index 4cf8d3d..7ac8381 100644
> --- a/arch/arm/kvm/arm.c
> +++ b/arch/arm/kvm/arm.c
> @@ -730,6 +730,35 @@ long kvm_arch_vcpu_ioctl(struct file *filp,
>                 return kvm_vgic_inject_irq(vcpu->kvm, vcpu->vcpu_id, &irq_event);
>         }
>  #endif
> +       case KVM_GET_MSR_INDEX_LIST: {
> +               struct kvm_msr_list __user *user_msr_list = argp;
> +               struct kvm_msr_list msr_list;
> +               unsigned n;
> +
> +               if (copy_from_user(&msr_list, user_msr_list, sizeof msr_list))
> +                       return -EFAULT;
> +               n = msr_list.nmsrs;
> +               msr_list.nmsrs = kvm_arm_num_guest_msrs(vcpu);
> +               if (copy_to_user(user_msr_list, &msr_list, sizeof msr_list))
> +                       return -EFAULT;
> +               if (n < msr_list.nmsrs)
> +                       return -E2BIG;
> +               return kvm_copy_msrindices(vcpu, user_msr_list->indices);
> +       }
> +       case KVM_GET_MSRS: {
> +               struct kvm_msrs msrs;
> +               struct kvm_msrs __user *umsrs = argp;
> +               if (copy_from_user(&msrs, umsrs, sizeof(msrs)) != 0)
> +                       return -EFAULT;
> +               return kvm_arm_get_msrs(vcpu, umsrs->entries, msrs.nmsrs);
> +       }
> +       case KVM_SET_MSRS: {
> +               struct kvm_msrs msrs;
> +               struct kvm_msrs __user *umsrs = argp;
> +               if (copy_from_user(&msrs, umsrs, sizeof(msrs)) != 0)
> +                       return -EFAULT;
> +               return kvm_arm_set_msrs(vcpu, umsrs->entries, msrs.nmsrs);
> +       }
>         default:
>                 return -EINVAL;
>         }
> diff --git a/arch/arm/kvm/coproc.c b/arch/arm/kvm/coproc.c
> index 64b4397..297b3a3 100644
> --- a/arch/arm/kvm/coproc.c
> +++ b/arch/arm/kvm/coproc.c
> @@ -16,6 +16,7 @@
>   * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
>   */
>  #include <linux/mm.h>
> +#include <linux/uaccess.h>
>  #include <asm/kvm_arm.h>
>  #include <asm/kvm_host.h>
>  #include <asm/kvm_emulate.h>
> @@ -555,3 +556,239 @@ int kvm_handle_cp15_32(struct kvm_vcpu *vcpu, struct kvm_run *run)
>
>         return emulate_cp15(vcpu, &params);
>  }
> +
> +/******************************************************************************
> + * Userspace API
> + *****************************************************************************/
> +
> +/* Given a simple mask, get those bits. */
> +static inline u32 get_bits(u32 index, u32 mask)
> +{
> +       return (index & mask) >> (ffs(mask)-1);
> +}
> +
> +static void index_to_params(u32 index, struct coproc_params *params)
> +{
> +       if (get_bits(index, KVM_ARM_MSR_64_BIT_MASK)) {
> +               params->is_64bit = true;
> +               params->CRm = get_bits(index, KVM_ARM_MSR_64_CRM_MASK);
> +               params->Op1 = get_bits(index, KVM_ARM_MSR_64_OPC1_MASK);
> +               params->Op2 = 0;
> +               params->CRn = 0;
> +       } else {
> +               params->is_64bit = false;
> +               params->CRn = get_bits(index, KVM_ARM_MSR_32_CRN_MASK);
> +               params->CRm = get_bits(index, KVM_ARM_MSR_32_CRM_MASK);
> +               params->Op1 = get_bits(index, KVM_ARM_MSR_32_OPC1_MASK);
> +               params->Op2 = get_bits(index, KVM_ARM_MSR_32_OPC2_MASK);
> +       }
> +}
> +
> +/* Decode an index value, and find the cp15 coproc_reg entry. */
> +static const struct coproc_reg *index_to_cp15(struct kvm_vcpu *vcpu, u32 index)
> +{
> +       size_t num;
> +       const struct coproc_reg *table, *r;
> +       struct coproc_params params;
> +
> +       index_to_params(index, &params);
> +
> +       table = get_target_table(vcpu->arch.target, &num);
> +       r = find_reg(&params, table, num);
> +       if (!r)
> +               r = find_reg(&params, cp15_regs, ARRAY_SIZE(cp15_regs));
> +
> +       /* Not saved in the cp15 array? */
> +       if (r && !r->reg)
> +               r = NULL;
> +
> +       return r;
> +}
> +
> +static int get_msr(struct kvm_vcpu *vcpu, u32 index, u64 *val)
> +{
> +       const struct coproc_reg *r;
> +       u32 coproc = get_bits(index, KVM_ARM_MSR_COPROC_MASK);
> +
> +       /* We only do cp15 for now. */
> +       if (coproc != 15)
> +               return -EINVAL;
> +
> +       r = index_to_cp15(vcpu, index);
> +       if (!r)
> +               return -EINVAL;
> +
> +       *val = vcpu->arch.cp15[r->reg];
> +       if (r->is_64)
> +               *val |= ((u64)vcpu->arch.cp15[r->reg+1]) << 32;
> +       return 0;
> +}
> +
> +static int set_msr(struct kvm_vcpu *vcpu, u32 index, u64 val)
> +{
> +       const struct coproc_reg *r;
> +       u32 coproc = get_bits(index, KVM_ARM_MSR_COPROC_MASK);
> +
> +       /* We only do cp15 for now. */
> +       if (coproc != 15)
> +               return -EINVAL;
> +
> +       r = index_to_cp15(vcpu, index);
> +       if (!r)
> +               return -EINVAL;
> +
> +       vcpu->arch.cp15[r->reg] = val;
> +       if (r->is_64)
> +               vcpu->arch.cp15[r->reg+1] = (val >> 32);
> +       return 0;
> +}

worth combining get_msr and set_msr with a 'bool write' parameter?

> +
> +/* Return user adddress to get/set value from. */
> +static u64 __user *get_umsr(struct kvm_msr_entry __user *uentry, u32 *idx)
> +{
> +       struct kvm_msr_entry entry;
> +
> +       if (copy_from_user(&entry, uentry, sizeof(entry)))
> +               return NULL;
> +       *idx = entry.index;
> +       return &uentry->data;
> +}
> +
> +/**
> + * kvm_arm_get_msrs - copy one or more special registers to userspace.
> + * @vcpu: the vcpu
> + * @entries: the array of entries
> + * @num: the number of entries
> + */
> +int kvm_arm_get_msrs(struct kvm_vcpu *vcpu,
> +                    struct kvm_msr_entry __user *entries, u32 num)
> +{
> +       u32 i, index;
> +       u64 val;
> +       u64 __user *uval;
> +       int ret;
> +
> +       for (i = 0; i < num; i++) {
> +               uval = get_umsr(&entries[i], &index);
> +               if (!uval)
> +                       return -EFAULT;
> +               if ((ret = get_msr(vcpu, index, &val)) != 0)
> +                       return ret;
> +               if (put_user(val, uval))
> +                       return -EFAULT;
> +       }
> +       return 0;
> +}
> +
> +/**
> + * kvm_arm_set_msrs - copy one or more special registers from userspace.
> + * @vcpu: the vcpu
> + * @entries: the array of entries
> + * @num: the number of entries
> + */
> +int kvm_arm_set_msrs(struct kvm_vcpu *vcpu,
> +                    struct kvm_msr_entry __user *entries, u32 num)
> +{
> +       u32 i, index;
> +       u64 val;
> +       u64 __user *uval;
> +       int ret;
> +
> +       for (i = 0; i < num; i++) {
> +               uval = get_umsr(&entries[i], &index);
> +               if (!uval)
> +                       return -EFAULT;
> +               if (copy_from_user(&val, uval, sizeof(val)) != 0)
> +                       return -EFAULT;

get_user ?

> +               if ((ret = set_msr(vcpu, index, val)) != 0)
> +                       return ret;
> +       }
> +       return 0;
> +}
> +
> +/**
> + * kvm_arm_num_guest_msrs - how many registers do we present via KVM_GET_MSR
> + *
> + * This is for special registers, particularly cp15.
> + */
> +unsigned long kvm_arm_num_guest_msrs(struct kvm_vcpu *vcpu)
> +{
> +       const struct coproc_reg *table;
> +       unsigned int i, total = 0;
> +       size_t num;
> +
> +       /* Count registers in arch-specific table. */
> +       table = get_target_table(vcpu->arch.target, &num);
> +       for (i = 0; i < num; i++) {
> +               if (table[i].reg)
> +                       total++;
> +       }
> +
> +       /* FIXME: Assumes no duplicates! */

If this is a FIXME, shouldn't we just fix it (allocate a table of the
registers, zero it out, set a bit and only count once)? I prefer not
merging new FIXMEs if possible.

> +       table = cp15_regs;
> +       num = ARRAY_SIZE(cp15_regs);
> +       for (i = 0; i < num; i++) {
> +               if (table[i].reg)
> +                       total++;
> +       }
> +
> +       return total;
> +}
> +
> +/* Puts in the position indicated by mask (assumes val fits in mask) */
> +static inline u32 set_bits(u32 val, u32 mask)
> +{
> +       return val << (ffs(mask)-1);

spaces around the - operator iirc.

> +}
> +
> +static u32 cp15_to_index(const struct coproc_reg *reg)
> +{
> +       u32 val = set_bits(15, KVM_ARM_MSR_COPROC_MASK);
> +       if (reg->is_64) {
> +               val |= set_bits(1, KVM_ARM_MSR_64_BIT_MASK);
> +               val |= set_bits(reg->Op1, KVM_ARM_MSR_64_OPC1_MASK);
> +               val |= set_bits(reg->CRm, KVM_ARM_MSR_64_CRM_MASK);
> +       } else {
> +               val |= set_bits(reg->Op1, KVM_ARM_MSR_32_OPC1_MASK);
> +               val |= set_bits(reg->Op2, KVM_ARM_MSR_32_OPC2_MASK);
> +               val |= set_bits(reg->CRm, KVM_ARM_MSR_32_CRM_MASK);
> +               val |= set_bits(reg->CRn, KVM_ARM_MSR_32_CRN_MASK);
> +       }
> +       return val;
> +}
> +
> +/**
> + * kvm_arm_msridx - what's the index value for the nth register.

this is not the name of the function

> + *
> + * This is for special registers, particularly cp15.
> + */
> +int kvm_copy_msrindices(struct kvm_vcpu *vcpu, u32 __user *uindices)

shouldn't this be kvm_arm_copy_msrindicies for consistency?

> +{
> +       const struct coproc_reg *table;
> +       unsigned int i;
> +       size_t num;
> +
> +       /* First give the target-specific registers' indices. */
> +       table = get_target_table(vcpu->arch.target, &num);
> +       for (i = 0; i < num; i++) {
> +               /* Ingore registers we trap but don't save. */
> +               if (!table[i].reg)
> +                       continue;
> +               if (put_user(cp15_to_index(&table[i]), uindices))
> +                       return -EFAULT;
> +               uindices++;
> +       }
> +
> +       /* Finally give the architected registers' indices. */
> +       table = cp15_regs;
> +       num = ARRAY_SIZE(cp15_regs);
> +       for (i = 0; i < num; i++) {
> +               /* Ingore registers we trap but don't save. */
> +               if (!table[i].reg)
> +                       continue;
> +               if (put_user(cp15_to_index(&table[i]), uindices))
> +                       return -EFAULT;
> +               uindices++;
> +       }
> +       return 0;
> +}


ok, this is pretty damn nice. Good argument for driving the cp15
register reset from here ;)

-Christoffer
_______________________________________________
kvmarm mailing list
kvmarm@xxxxxxxxxxxxxxxxxxxxx
https://lists.cs.columbia.edu/cucslists/listinfo/kvmarm


[Index of Archives]     [Linux KVM]     [Spice Development]     [Libvirt]     [Libvirt Users]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux