Re: KVM: ARM: Demux CCSIDR in the userspace API.

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

 



Is this the 3/3 patch or is something missing?

On Tue, Sep 11, 2012 at 2:07 AM, Rusty Russell <rusty.russell@xxxxxxxxxx> wrote:
> The Cache Size Selection Register (CSSELR) selects the current Cache
> Size ID Register (CCSIDR).  You write which cache you are interested
> in to CSSELR, and read the information out of CCSIDR.
>
> Which cache numbers are valid is known by reading the Cache Level ID
> Register (CLIDR).
>
> To export this state to userspace, we add a KVM_REG_ARM_DEMUX
> numberspace (17), which uses 8 bits to represent which register is
> being demultiplexed (0 for CCSIDR), and the lower 8 bits to represent
> this demultiplexing (in our case, the CSSELR value, which is 4 bits).
>
> Signed-off-by: Rusty Russell <rusty.russell@xxxxxxxxxx>
>
> diff --git a/Documentation/virtual/kvm/api.txt b/Documentation/virtual/kvm/api.txt
> index 4d22fdf..1e70024 100644
> --- a/Documentation/virtual/kvm/api.txt
> +++ b/Documentation/virtual/kvm/api.txt
> @@ -1761,6 +1761,9 @@ ARM 64-bit CP15 registers have the following id bit patterns:
>  ARM core registers have the following id format:
>    0x4003 0000 0010 <offset in struct kvm_regs, divided by 4>
>
> +ARM CCSIDR registers are demultiplexed by CSSELR value:
> +  0x4003 0000 0011 <CSSELR>

then this is wrong right?

shouldn't it be:

 +  0x4003 0000 0011 <dmux id:8> <selector:8>

or

 +  0x4003 0000 0011 00 <CSSELR:8>

???

> +
>  4.69 KVM_GET_ONE_REG
>
>  Capability: KVM_CAP_ONE_REG
> diff --git a/arch/arm/include/asm/kvm.h b/arch/arm/include/asm/kvm.h
> index 3442ced..86184a4 100644
> --- a/arch/arm/include/asm/kvm.h
> +++ b/arch/arm/include/asm/kvm.h
> @@ -85,4 +85,12 @@ struct kvm_arch_memory_slot {
>  #define KVM_REG_ARM_CORE               (0x0010 << KVM_REG_ARM_COPROC_SHIFT)
>  #define KVM_REG_ARM_CORE_REG(name)     (offsetof(struct kvm_regs, name) / 4)
>
> +/* Some registers need more space to represent values. */
> +#define KVM_REG_ARM_DEMUX              (0x0011 << KVM_REG_ARM_COPROC_SHIFT)
> +#define KVM_REG_ARM_DEMUX_ID_MASK      0x000000000000FF00
> +#define KVM_REG_ARM_DEMUX_ID_SHIFT     8
> +#define KVM_REG_ARM_DEMUX_ID_CCSIDR    (0x00 << KVM_REG_ARM_DEMUX_ID_SHIFT)
> +#define KVM_REG_ARM_DEMUX_VAL_MASK     0x00000000000000FF
> +#define KVM_REG_ARM_DEMUX_VAL_SHIFT    0
> +
>  #endif /* __ARM_KVM_H__ */
> diff --git a/arch/arm/kvm/coproc.c b/arch/arm/kvm/coproc.c
> index b6abe2e..02b7077 100644
> --- a/arch/arm/kvm/coproc.c
> +++ b/arch/arm/kvm/coproc.c
> @@ -67,6 +67,12 @@ struct coproc_reg {
>         u64 val;
>  };
>
> +/* 3 bits per cache level, as per CLIDR, but non-existent caches always 0 */
> +static u32 cache_levels;
> +
> +/* CSSELR values; used to index KVM_REG_ARM_DEMUX_ID_CCSIDR */
> +#define CSSELR_MAX 12
> +
>  static void print_cp_instr(const struct coproc_params *p)
>  {
>         /* Look, we even formatted it for you to paste into the table! */
> @@ -763,11 +767,111 @@ static int set_invariant_cp15(u64 id, void __user *uaddr)
>         return 0;
>  }
>
> +static bool is_valid_cache(u32 val)
> +{
> +       u32 ctype;
> +
> +       if (val >= CSSELR_MAX)
> +               return -ENOENT;
> +
> +       /* Bottom bit is Instruction or Data bit.  Next 3 bits are level. */
> +       ctype = cache_levels & (7 << ((val>>1) & );

this looks wrong, shouldn't this be

+       level = (val >> 1) & 7;
+       ctype = (cache_levels >> (level * 3)) & 7;

???

> +
> +       switch (ctype) {
> +       case 0: /* No cache */
> +               return false;
> +       case 1: /* Instruction cache only */
> +               return (val & 1);
> +       case 2: /* Data cache only */
> +       case 4: /* Unified cache */
> +               return !(val & 1);
> +       case 3: /* Separate instruction and data caches */
> +               return true;
> +       default: /* Reserved: we can't know instruction or data. */
> +               return false;
> +       }
> +}
> +
> +/* Which cache CCSIDR represents depends on CSSELR value. */
> +static u32 get_ccsidr(u32 csselr)
> +{
> +       u32 ccsidr;
> +
> +       /* Make sure noone else changes CSSELR during this! */
> +       local_irq_disable();
> +       /* Put value into CSSELR */
> +       asm volatile("mcr p15, 2, %0, c0, c0, 0" : : "r" (csselr));
> +       /* Read result out of CCSIDR */
> +       asm volatile("mrc p15, 1, %0, c0, c0, 0" : "=r" (ccsidr));
> +       local_irq_enable();
> +
> +       return ccsidr;
> +}
> +
> +static int demux_c15_get(u64 id, void __user *uaddr)
> +{
> +       u32 val;
> +       u32 __user *uval = uaddr;
> +
> +       /* Fail if we have unknown bits set. */
> +       if (id & ~(KVM_REG_ARCH_MASK|KVM_REG_SIZE_MASK|KVM_REG_ARM_COPROC_MASK
> +                  | ((1 << KVM_REG_ARM_COPROC_SHIFT)-1)))
> +               return -ENOENT;
> +
> +       switch (id & KVM_REG_ARM_DEMUX_ID_MASK) {
> +       case KVM_REG_ARM_DEMUX_ID_CCSIDR:
> +               if (KVM_REG_SIZE(id) != 4)
> +                       return -ENOENT;
> +               val = (id & KVM_REG_ARM_DEMUX_VAL_MASK)
> +                       >> KVM_REG_ARM_DEMUX_VAL_SHIFT;
> +               if (!is_valid_cache(val))
> +                       return -ENOENT;
> +
> +               return put_user(get_ccsidr(val), uval);
> +       default:
> +               return -ENOENT;
> +       }
> +}
> +
> +static int demux_c15_set(u64 id, void __user *uaddr)
> +{
> +       u32 val, newval;
> +       u32 __user *uval = uaddr;
> +
> +       /* Fail if we have unknown bits set. */
> +       if (id & ~(KVM_REG_ARCH_MASK|KVM_REG_SIZE_MASK|KVM_REG_ARM_COPROC_MASK
> +                  | ((1 << KVM_REG_ARM_COPROC_SHIFT)-1)))
> +               return -ENOENT;
> +
> +       switch (id & KVM_REG_ARM_DEMUX_ID_MASK) {
> +       case KVM_REG_ARM_DEMUX_ID_CCSIDR:
> +               if (KVM_REG_SIZE(id) != 4)
> +                       return -ENOENT;
> +               val = (id & KVM_REG_ARM_DEMUX_VAL_MASK)
> +                       >> KVM_REG_ARM_DEMUX_VAL_SHIFT;
> +               if (!is_valid_cache(val))
> +                       return -ENOENT;
> +
> +               if (get_user(newval, uval))
> +                       return -EFAULT;
> +
> +               /* This is also invariant: you can't change it. */
> +               if (newval != get_ccsidr(val))
> +                       return -EINVAL;
> +               return 0;
> +       default:
> +               return -ENOENT;
> +       }
> +}
> +
>  int kvm_arm_coproc_get_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
>  {
>         const struct coproc_reg *r;
>         void __user *uaddr = (void __user *)(long)reg->addr;
>
> +       if ((reg->id & KVM_REG_ARM_COPROC_MASK) == KVM_REG_ARM_DEMUX)
> +               return demux_c15_get(reg->id, uaddr);
> +
>         r = index_to_coproc_reg(vcpu, reg->id);
>         if (!r)
>                 return get_invariant_cp15(reg->id, uaddr);
> @@ -781,6 +886,9 @@ int kvm_arm_coproc_set_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
>         const struct coproc_reg *r;
>         void __user *uaddr = (void __user *)(long)reg->addr;
>
> +       if ((reg->id & KVM_REG_ARM_COPROC_MASK) == KVM_REG_ARM_DEMUX)
> +               return demux_c15_set(reg->id, uaddr);
> +
>         r = index_to_coproc_reg(vcpu, reg->id);
>         if (!r)
>                 return set_invariant_cp15(reg->id, uaddr);
> @@ -789,6 +897,33 @@ int kvm_arm_coproc_set_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
>         return reg_from_user(&vcpu->arch.cp15[r->reg], uaddr, reg->id);
>  }
>
> +static unsigned int num_demux_regs(void)
> +{
> +       unsigned int i, count = 0;
> +
> +       for (i = 0; i < CSSELR_MAX; i++)
> +               if (is_valid_cache(i))
> +                       count++;
> +
> +       return count;
> +}
> +
> +static int write_demux_regids(u64 __user *uindices)
> +{
> +       u64 val = KVM_REG_ARM | KVM_REG_SIZE_U32 | KVM_REG_ARM_DEMUX;
> +       unsigned int i;
> +
> +       val |= KVM_REG_ARM_DEMUX_ID_CCSIDR;
> +       for (i = 0; i < CSSELR_MAX; i++) {
> +               if (!is_valid_cache(i))
> +                       continue;
> +               if (put_user(val | i, uindices))
> +                       return -EFAULT;
> +               uindices++;
> +       }
> +       return 0;
> +}
> +
>  static int cmp_reg(const struct coproc_reg *i1, const struct coproc_reg *i2)
>  {
>         BUG_ON(i1 == i2);
> @@ -880,6 +1015,7 @@ static int walk_cp15(struct kvm_vcpu *vcpu, u64 __user *uind)
>  unsigned long kvm_arm_num_coproc_regs(struct kvm_vcpu *vcpu)
>  {
>         return ARRAY_SIZE(invariant_cp15)
> +               + num_demux_regs()
>                 + walk_cp15(vcpu, (u64 __user *)NULL);
>  }
>
> @@ -896,9 +1032,11 @@ int kvm_arm_copy_coproc_indices(struct kvm_vcpu *vcpu, u64 __user *uindices)
>         }
>
>         err = walk_cp15(vcpu, uindices);
> -       if (err > 0)
> -               err = 0;
> -       return err;
> +       if (err < 0)
> +               return err;
> +       uindices += err;
> +
> +       return write_demux_regids(uindices);
>  }
>
>  void kvm_coproc_table_init(void)
> @@ -915,6 +1053,23 @@ void kvm_coproc_table_init(void)
>         /* We abuse the reset function to overwrite the table itself. */
>         for (i = 0; i < ARRAY_SIZE(invariant_cp15); i++)
>                 invariant_cp15[i].reset(NULL, &invariant_cp15[i]);
> +
> +       /*
> +        * CLIDR format is awkward, so clean it up.  See ARM B4.1.20:
> +        *
> +        *   If software reads the Cache Type fields from Ctype1
> +        *   upwards, once it has seen a value of 0b000, no caches
> +        *   exist at further-out levels of the hierarchy. So, for
> +        *   example, if Ctype3 is the first Cache Type field with a
> +        *   value of 0b000, the values of Ctype4 to Ctype7 must be
> +        *   ignored.
> +        */
> +       asm volatile("mrc p15, 1, %0, c0, c0, 1" : "=r" (cache_levels));
> +       for (i = 0; i < 7; i++)
> +               if (((cache_levels >> (i*3)) & 7) == 0)
> +                       break;
> +       /* Clear all higher bits. */
> +       cache_levels &= (1 << (i*3))-1;
>  }
>
>  /**
_______________________________________________
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