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

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

 



On Mon, Sep 17, 2012 at 3:28 AM, Rusty Russell <rusty.russell@xxxxxxxxxx> wrote:
> Christoffer Dall <c.dall@xxxxxxxxxxxxxxxxxxxxxx> writes:
>
>> Is this the 3/3 patch or is something missing?
>
> Ah yes, the [PATCH 3/3] was missing.  That will teach me to cut & paste
> the subject line from the patch.
>
>>> +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>
>>
>> ???
>
> Good point, I changed the implementation later.  It's not wrong, but
> it's pretty misleading.  Fixed below.
>
>>> +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;
>>
>> ???
>
> Good catch.  Because we check val < CSSELR_MAX here, we can simplify the
> level calc though:
>
>         level = (val >> 1);
>         ctype = (cache_levels >> (level * 3)) & 7;
>
> (This happens to make no difference on the fastmodel, since it only has
> level 0 and 1 caches anyway):
>
> Before:
>
> Cache 0 (icache): r/o read-alloc 256 sets of 2 assoc 16 linesize
> Cache 0: write-back read-alloc write-alloc 256 sets of 2 assoc 16 linesize
> Cache 1: write-back read-alloc write-alloc 512 sets of 16 assoc 16 linesize
>
> After:
>
> Cache 0 (icache): r/o read-alloc 256 sets of 2 assoc 16 linesize
> Cache 0: write-back read-alloc write-alloc 256 sets of 2 assoc 16 linesize
> Cache 1: write-back read-alloc write-alloc 512 sets of 16 assoc 16 linesize
>
> ---
> Subject: KVM: ARM: Demux CCSIDR in the userspace API.
>
> 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 dbde8ae..2d2f6ff 100644
> --- a/Documentation/virtual/kvm/api.txt
> +++ b/Documentation/virtual/kvm/api.txt
> @@ -1757,6 +1757,8 @@ ARM 32-bit CP15 registers have the following id bit patterns:
>  ARM 64-bit CP15 registers have the following id bit patterns:
>    0x4003 0000 000F <zero:1> <zero:4> <crm:4> <opc1:4> <zero:3>
>
> +ARM CCSIDR registers are demultiplexed by CSSELR value:
> +  0x4003 0000 0011 00 <csselr:8>
>
>  4.69 KVM_GET_ONE_REG
>
> diff --git a/arch/arm/include/asm/kvm.h b/arch/arm/include/asm/kvm.h
> index 9f36b24..2034ea2 100644
> --- a/arch/arm/include/asm/kvm.h
> +++ b/arch/arm/include/asm/kvm.h
> @@ -94,4 +94,12 @@ struct kvm_reg_list {
>  #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 5c7f5fd..0200820 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! */
> @@ -765,11 +771,112 @@ static int set_invariant_cp15(u64 id, void __user *uaddr)
>         return 0;
>  }
>
> +static bool is_valid_cache(u32 val)
> +{
> +       u32 level, ctype;
> +
> +       if (val >= CSSELR_MAX)
> +               return -ENOENT;
> +
> +       /* Bottom bit is Instruction or Data bit.  Next 3 bits are level. */
> +        level = (val >> 1);
> +        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);
> @@ -783,6 +890,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);
> @@ -791,6 +901,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);
> @@ -882,6 +1019,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);
>  }
>
> @@ -898,9 +1036,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)
> @@ -917,6 +1057,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;
>  }
>
>  /**

thanks,
applied.
_______________________________________________
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