Re: [PATCH v3 2/5] KVM: arm64: Implement vGICv3 distributor and redistributor access from userspace

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

 



Hi Pavel,

On 04/09/15 13:40, Pavel Fedin wrote:
> The access is done similar to vGICv2, using KVM_DEV_ARM_VGIC_GRP_DIST_REGS
> and KVM_DEV_ARM_VGIC_GRP_REDIST_REGS with KVM_SET_DEVICE_ATTR and
> KVM_GET_DEVICE_ATTR ioctls. Since GICv3 can handle large number of CPUs,
> KVM_DEV_ARM_VGIC_CPUID_MASK has been extended to 20 bits. This is enough
> for 1048576 CPUs.

I guess the 20 bits come from 8 bits for Aff2 and Aff1 and 4-bits for
Aff0? If so, please mention this. But I am not sure we should limit the
cpu index in this public API to something as low 20 bits. Since this
mask is GIC specific, we could push the size bit into offset and use the
full upper 32 bits for cpuid, or at least 28 bits plus 4 reserved.

> 
> Some registers are 64-bit wide according to the specification.
> KVM_DEV_ARM_VGIC_64BIT flag is introduced, allowing to perform full 64-bit
> accesses.
> 
> Signed-off-by: Pavel Fedin <p.fedin@xxxxxxxxxxx>
> ---
>  Documentation/virtual/kvm/devices/arm-vgic.txt | 35 ++++++++--
>  arch/arm64/include/uapi/asm/kvm.h              |  4 +-
>  virt/kvm/arm/vgic-v3-emul.c                    | 95 ++++++++++++++++++++++----
>  virt/kvm/arm/vgic.c                            |  1 +
>  4 files changed, 116 insertions(+), 19 deletions(-)
> 
> diff --git a/Documentation/virtual/kvm/devices/arm-vgic.txt b/Documentation/virtual/kvm/devices/arm-vgic.txt
> index 3fb9054..03f640f 100644
> --- a/Documentation/virtual/kvm/devices/arm-vgic.txt
> +++ b/Documentation/virtual/kvm/devices/arm-vgic.txt
> @@ -43,10 +43,13 @@ Groups:
>    KVM_DEV_ARM_VGIC_GRP_DIST_REGS
>    Attributes:
>      The attr field of kvm_device_attr encodes two values:
> -    bits:     | 63   ....  40 | 39 ..  32  |  31   ....    0 |
> -    values:   |    reserved   |   cpu id   |      offset     |
> +    bits:     |  63  | 62 .. 52 | 51 ..  32  |  31   ....    0 |
> +    values:   | size | reserved |   cpu id   |      offset     |
>  
>      All distributor regs are (rw, 32-bit)
> +    For GICv3 some regs are also (rw, 64-bit) according to the specification.

That sounds contradictory to me. What about:
All registers can be accessed by using 32-bit accesses, some registers
also by 64-bit reads/writes according to the specification.

> +    In order to perform full 64-bit access 'size' bit should be set to 1.
> +    KVM_DEV_ARM_VGIC_64BIT flag value is provided for this purpose.
>  
>      The offset is relative to the "Distributor base address" as defined in the
>      GICv2 specs.  Getting or setting such a register has the same effect as
> @@ -54,9 +57,33 @@ Groups:
>      specified with cpu id field.  Note that most distributor fields are not
>      banked, but return the same value regardless of the cpu id used to access
>      the register.
> +
> +  Limitations:
> +    - Priorities are not implemented, and registers are RAZ/WI
> +  Errors:
> +    -ENODEV: Getting or setting this register is not yet supported

Isn't that actually -ENXIO in the code? I see that this is just copy &
paste, but it should be fixed in either case.

> +    -EBUSY: One or more VCPUs are running
> +
> +  KVM_DEV_ARM_VGIC_GRP_REDIST_REGS
> +  Attributes:
> +    The attr field of kvm_device_attr encodes two values:
> +    bits:     |  63  | 62 .. 52 | 51 ..  32  |  31   ....    0 |
> +    values:   | size | reserved |   cpu id   |      offset     |
> +
> +    All redistributor regs are (rw, 32-bit)
> +    For GICv3 some regs are also (rw, 64-bit) according to the specification.
> +    In order to perform full 64-bit access 'size' bit should be set to 1.
> +    KVM_DEV_ARM_VGIC_64BIT flag value is provided for this purpose.
> +
> +    The offset is relative to the "Redistributor base address" as defined in
> +    the GICv3 specs.  Getting or setting such a register has the same effect as
> +    reading or writing the register on the actual hardware from the cpu
> +    specified with cpu id field.  Note that most distributor fields are not
> +    banked, but return the same value regardless of the cpu id used to access
> +    the register.
> +
>    Limitations:
>      - Priorities are not implemented, and registers are RAZ/WI
> -    - Currently only implemented for KVM_DEV_TYPE_ARM_VGIC_V2.
>    Errors:
>      -ENODEV: Getting or setting this register is not yet supported
>      -EBUSY: One or more VCPUs are running
> @@ -64,7 +91,7 @@ Groups:
>    KVM_DEV_ARM_VGIC_GRP_CPU_REGS
>    Attributes:
>      The attr field of kvm_device_attr encodes two values:
> -    bits:     | 63   ....  40 | 39 ..  32  |  31   ....    0 |
> +    bits:     | 63   ....  52 | 51 ..  32  |  31   ....    0 |
>      values:   |    reserved   |   cpu id   |      offset     |
>  
>      All CPU interface regs are (rw, 32-bit)
> diff --git a/arch/arm64/include/uapi/asm/kvm.h b/arch/arm64/include/uapi/asm/kvm.h
> index 0cd7b59..249954f 100644
> --- a/arch/arm64/include/uapi/asm/kvm.h
> +++ b/arch/arm64/include/uapi/asm/kvm.h
> @@ -196,13 +196,15 @@ struct kvm_arch_memory_slot {
>  #define KVM_DEV_ARM_VGIC_GRP_ADDR	0
>  #define KVM_DEV_ARM_VGIC_GRP_DIST_REGS	1
>  #define KVM_DEV_ARM_VGIC_GRP_CPU_REGS	2
> +#define   KVM_DEV_ARM_VGIC_64BIT	(1ULL << 63)
>  #define   KVM_DEV_ARM_VGIC_CPUID_SHIFT	32
> -#define   KVM_DEV_ARM_VGIC_CPUID_MASK	(0xffULL << KVM_DEV_ARM_VGIC_CPUID_SHIFT)
> +#define   KVM_DEV_ARM_VGIC_CPUID_MASK	(0xfffffULL << KVM_DEV_ARM_VGIC_CPUID_SHIFT)
>  #define   KVM_DEV_ARM_VGIC_OFFSET_SHIFT	0
>  #define   KVM_DEV_ARM_VGIC_OFFSET_MASK	(0xffffffffULL << KVM_DEV_ARM_VGIC_OFFSET_SHIFT)
>  #define KVM_DEV_ARM_VGIC_GRP_NR_IRQS	3
>  #define KVM_DEV_ARM_VGIC_GRP_CTRL	4
>  #define   KVM_DEV_ARM_VGIC_CTRL_INIT	0
> +#define KVM_DEV_ARM_VGIC_GRP_REDIST_REGS 5
>  
>  /* KVM_IRQ_LINE irq field index values */
>  #define KVM_ARM_IRQ_TYPE_SHIFT		24
> diff --git a/virt/kvm/arm/vgic-v3-emul.c b/virt/kvm/arm/vgic-v3-emul.c
> index e661e7f..8bda714 100644
> --- a/virt/kvm/arm/vgic-v3-emul.c
> +++ b/virt/kvm/arm/vgic-v3-emul.c
> @@ -39,6 +39,7 @@
>  #include <linux/kvm.h>
>  #include <linux/kvm_host.h>
>  #include <linux/interrupt.h>
> +#include <linux/uaccess.h>
>  
>  #include <linux/irqchip/arm-gic-v3.h>
>  #include <kvm/arm_vgic.h>
> @@ -990,6 +991,77 @@ void vgic_v3_dispatch_sgi(struct kvm_vcpu *vcpu, u64 reg)
>  		vgic_kick_vcpus(vcpu->kvm);
>  }
>  
> +static int vgic_v3_attr_regs_access(struct kvm_device *dev,
> +				    struct kvm_device_attr *attr,
> +				    bool is_write)
> +{
> +	const struct vgic_io_range *ranges;
> +	phys_addr_t offset;
> +	int cpuid, ret;
> +	struct vgic_dist *vgic = &dev->kvm->arch.vgic;
> +	struct kvm_exit_mmio mmio;
> +
> +	cpuid = (attr->attr & KVM_DEV_ARM_VGIC_CPUID_MASK) >>
> +		KVM_DEV_ARM_VGIC_CPUID_SHIFT;
> +
> +	switch (attr->group) {
> +	case KVM_DEV_ARM_VGIC_GRP_DIST_REGS:
> +		offset = attr->attr & KVM_DEV_ARM_VGIC_OFFSET_MASK;

Can't this be just after the cpuid= assignment outside of the switch
statement?

> +		mmio.phys_addr = vgic->vgic_dist_base;
> +		ranges = vgic_v3_dist_ranges;
> +		break;
> +	case KVM_DEV_ARM_VGIC_GRP_REDIST_REGS:
> +		offset = attr->attr & KVM_DEV_ARM_VGIC_OFFSET_MASK;
> +		mmio.phys_addr = vgic->vgic_redist_base;
> +		ranges = vgic_redist_ranges;
> +		break;
> +	default:
> +		return -ENXIO;
> +	}
> +
> +	mmio.is_write = is_write;
> +
> +	if (attr->attr & KVM_DEV_ARM_VGIC_64BIT) {
> +		u64 __user *uaddr = (u64 __user *)(long)attr->addr;
> +		__le64 data;
> +
> +		if (is_write) {
> +			u64 reg;
> +
> +			if (get_user(reg, uaddr))

Wouldn't the use of copy_from_user/copy_to_user here make this whole
switch redundant? Even if not, you could think about moving this logic
into into vgic_attr_regs_access() and just hardcode "len=4" into GICv2
accesses.

> +				return -EFAULT;
> +			data = cpu_to_le64(reg);
> +		}
> +
> +		mmio.data = &data;
> +		ret = vgic_attr_regs_access(dev, ranges, &mmio, offset,
> +					    sizeof(data), cpuid);
> +
> +		if (!ret && !is_write)
> +			ret = put_user(le64_to_cpu(data), uaddr);
> +	} else {
> +		u32 __user *uaddr = (u32 __user *)(long)attr->addr;
> +		__le32 data;
> +
> +		if (is_write) {
> +			u32 reg;
> +
> +			if (get_user(reg, uaddr))
> +				return -EFAULT;
> +			data = cpu_to_le32(reg);
> +		}
> +
> +		mmio.data = &data;
> +		ret = vgic_attr_regs_access(dev, ranges, &mmio, offset,
> +					    sizeof(data), cpuid);
> +
> +		if (!ret && !is_write)
> +			ret = put_user(le32_to_cpu(data), uaddr);
> +	}
> +
> +	return ret;
> +}
> +
>  static int vgic_v3_create(struct kvm_device *dev, u32 type)
>  {
>  	return kvm_vgic_create(dev->kvm, type);
> @@ -1009,13 +1081,7 @@ static int vgic_v3_set_attr(struct kvm_device *dev,
>  	if (ret != -ENXIO)
>  		return ret;
>  
> -	switch (attr->group) {
> -	case KVM_DEV_ARM_VGIC_GRP_DIST_REGS:
> -	case KVM_DEV_ARM_VGIC_GRP_CPU_REGS:
> -		return -ENXIO;
> -	}
> -
> -	return -ENXIO;
> +	return vgic_v3_attr_regs_access(dev, attr, true);
>  }
>  
>  static int vgic_v3_get_attr(struct kvm_device *dev,
> @@ -1027,18 +1093,14 @@ static int vgic_v3_get_attr(struct kvm_device *dev,
>  	if (ret != -ENXIO)
>  		return ret;
>  
> -	switch (attr->group) {
> -	case KVM_DEV_ARM_VGIC_GRP_DIST_REGS:
> -	case KVM_DEV_ARM_VGIC_GRP_CPU_REGS:
> -		return -ENXIO;
> -	}
> -
> -	return -ENXIO;
> +	return vgic_v3_attr_regs_access(dev, attr, false);
>  }
>  
>  static int vgic_v3_has_attr(struct kvm_device *dev,
>  			    struct kvm_device_attr *attr)
>  {
> +	phys_addr_t offset;
> +
>  	switch (attr->group) {
>  	case KVM_DEV_ARM_VGIC_GRP_ADDR:
>  		switch (attr->attr) {
> @@ -1051,6 +1113,11 @@ static int vgic_v3_has_attr(struct kvm_device *dev,
>  		}
>  		break;
>  	case KVM_DEV_ARM_VGIC_GRP_DIST_REGS:
> +		offset = attr->attr & KVM_DEV_ARM_VGIC_OFFSET_MASK;
> +		return vgic_has_attr_regs(vgic_v3_dist_ranges, offset);
> +	case KVM_DEV_ARM_VGIC_GRP_REDIST_REGS:
> +		offset = attr->attr & KVM_DEV_ARM_VGIC_OFFSET_MASK;
> +		return vgic_has_attr_regs(vgic_redist_ranges, offset);
>  	case KVM_DEV_ARM_VGIC_GRP_CPU_REGS:
>  		return -ENXIO;
>  	case KVM_DEV_ARM_VGIC_GRP_NR_IRQS:
> diff --git a/virt/kvm/arm/vgic.c b/virt/kvm/arm/vgic.c
> index 33b00e5..9aa802b 100644
> --- a/virt/kvm/arm/vgic.c
> +++ b/virt/kvm/arm/vgic.c
> @@ -2468,6 +2468,7 @@ int vgic_attr_regs_access(struct kvm_device *dev,
>  	 * access. For 64-bit registers we have to split up the operation.
>  	 */
>  	mmio->len = sizeof(u32);
> +	mmio->private = vcpu; /* For redistributor handlers */

I guess this can be moved into the caller and then you can drop the vcpu
parameter and use private here instead, no?

Cheers,
Andre.

>  	mmio->phys_addr += offset;
>  	offset -= r->base;
>  	r->handle_mmio(vcpu, mmio, offset);
> 
--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [KVM ARM]     [KVM ia64]     [KVM ppc]     [Virtualization Tools]     [Spice Development]     [Libvirt]     [Libvirt Users]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite Questions]     [Linux Kernel]     [Linux SCSI]     [XFree86]
  Powered by Linux