[PATCH v3 41/55] KVM: arm/arm64: vgic-new: vgic_kvm_device: implement kvm_vgic_addr
Christoffer Dall
christoffer.dall at linaro.org
Fri May 13 03:12:04 PDT 2016
On Fri, May 06, 2016 at 11:45:54AM +0100, Andre Przywara wrote:
> From: Eric Auger <eric.auger at linaro.org>
>
> kvm_vgic_addr is used by the userspace to set the base address of
> the following register regions, as seen by the guest:
> - distributor(v2 and v3),
> - re-distributors (v3),
> - CPU interface (v2).
>
> Signed-off-by: Eric Auger <eric.auger at linaro.org>
> Signed-off-by: Andre Przywara <andre.przywara at arm.com>
> ---
> include/kvm/vgic/vgic.h | 2 +
> virt/kvm/arm/vgic/vgic-kvm-device.c | 112 ++++++++++++++++++++++++++++++++++++
> virt/kvm/arm/vgic/vgic.h | 3 +
> 3 files changed, 117 insertions(+)
>
> diff --git a/include/kvm/vgic/vgic.h b/include/kvm/vgic/vgic.h
> index 2c43eb8..73cab36 100644
> --- a/include/kvm/vgic/vgic.h
> +++ b/include/kvm/vgic/vgic.h
> @@ -194,6 +194,8 @@ struct vgic_cpu {
> u64 live_lrs;
> };
>
> +int kvm_vgic_addr(struct kvm *kvm, unsigned long type, u64 *addr, bool write);
> +
> int kvm_vgic_inject_irq(struct kvm *kvm, int cpuid, unsigned int intid,
> bool level);
>
> diff --git a/virt/kvm/arm/vgic/vgic-kvm-device.c b/virt/kvm/arm/vgic/vgic-kvm-device.c
> index e153f12..493e941 100644
> --- a/virt/kvm/arm/vgic/vgic-kvm-device.c
> +++ b/virt/kvm/arm/vgic/vgic-kvm-device.c
> @@ -16,10 +16,122 @@
> #include <linux/kvm_host.h>
> #include <kvm/arm_vgic.h>
> #include <linux/uaccess.h>
> +#include <asm/kvm_mmu.h>
> #include "vgic.h"
>
> /* common helpers */
>
> +static int vgic_ioaddr_overlap(struct kvm *kvm)
> +{
> + phys_addr_t dist = kvm->arch.vgic.vgic_dist_base;
> + phys_addr_t cpu = kvm->arch.vgic.vgic_cpu_base;
> +
> + if (IS_VGIC_ADDR_UNDEF(dist) || IS_VGIC_ADDR_UNDEF(cpu))
> + return 0;
> + if ((dist <= cpu && dist + KVM_VGIC_V2_DIST_SIZE > cpu) ||
> + (cpu <= dist && cpu + KVM_VGIC_V2_CPU_SIZE > dist))
> + return -EBUSY;
> + return 0;
> +}
> +
> +static int vgic_ioaddr_assign(struct kvm *kvm, phys_addr_t *ioaddr,
> + phys_addr_t addr, phys_addr_t size)
> +{
> + int ret;
> +
> + if (addr & ~KVM_PHYS_MASK)
> + return -E2BIG;
> +
> + if (addr & (SZ_4K - 1))
> + return -EINVAL;
> +
> + if (!IS_VGIC_ADDR_UNDEF(*ioaddr))
> + return -EEXIST;
> + if (addr + size < addr)
> + return -EINVAL;
> +
> + *ioaddr = addr;
> + ret = vgic_ioaddr_overlap(kvm);
> + if (ret)
> + *ioaddr = VGIC_ADDR_UNDEF;
> +
> + return ret;
> +}
> +
> +/**
> + * kvm_vgic_addr - set or get vgic VM base addresses
> + * @kvm: pointer to the vm struct
> + * @type: the VGIC addr type, one of KVM_VGIC_V[23]_ADDR_TYPE_XXX
> + * @addr: pointer to address value
> + * @write: if true set the address in the VM address space, if false read the
> + * address
> + *
> + * Set or get the vgic base addresses for the distributor and the virtual CPU
> + * interface in the VM physical address space. These addresses are properties
> + * of the emulated core/SoC and therefore user space initially knows this
> + * information.
> + */
> +int kvm_vgic_addr(struct kvm *kvm, unsigned long type, u64 *addr, bool write)
> +{
> + int r = 0;
> + struct vgic_dist *vgic = &kvm->arch.vgic;
> + int type_needed;
> + phys_addr_t *addr_ptr, block_size;
> + phys_addr_t alignment;
> +
> + mutex_lock(&kvm->lock);
> + switch (type) {
> + case KVM_VGIC_V2_ADDR_TYPE_DIST:
> + type_needed = KVM_DEV_TYPE_ARM_VGIC_V2;
> + addr_ptr = &vgic->vgic_dist_base;
> + block_size = KVM_VGIC_V2_DIST_SIZE;
> + alignment = SZ_4K;
> + break;
> + case KVM_VGIC_V2_ADDR_TYPE_CPU:
> + type_needed = KVM_DEV_TYPE_ARM_VGIC_V2;
> + addr_ptr = &vgic->vgic_cpu_base;
> + block_size = KVM_VGIC_V2_CPU_SIZE;
> + alignment = SZ_4K;
> + break;
> +#ifdef CONFIG_KVM_ARM_VGIC_V3
> + case KVM_VGIC_V3_ADDR_TYPE_DIST:
> + type_needed = KVM_DEV_TYPE_ARM_VGIC_V3;
> + addr_ptr = &vgic->vgic_dist_base;
> + block_size = KVM_VGIC_V3_DIST_SIZE;
> + alignment = SZ_64K;
> + break;
> + case KVM_VGIC_V3_ADDR_TYPE_REDIST:
> + type_needed = KVM_DEV_TYPE_ARM_VGIC_V3;
> + addr_ptr = &vgic->vgic_redist_base;
> + block_size = KVM_VGIC_V3_REDIST_SIZE;
> + alignment = SZ_64K;
> + break;
> +#endif
> + default:
> + r = -ENODEV;
> + goto out;
> + }
> +
> + if (vgic->vgic_model != type_needed) {
> + r = -ENODEV;
> + goto out;
> + }
> +
> + if (write) {
> + if (!IS_ALIGNED(*addr, alignment))
> + r = -EINVAL;
> + else
> + r = vgic_ioaddr_assign(kvm, addr_ptr,
> + *addr, block_size);
> + } else {
> + *addr = *addr_ptr;
> + }
> +
> +out:
> + mutex_unlock(&kvm->lock);
> + return r;
> +}
> +
> static int vgic_set_common_attr(struct kvm_device *dev,
> struct kvm_device_attr *attr)
> {
> diff --git a/virt/kvm/arm/vgic/vgic.h b/virt/kvm/arm/vgic/vgic.h
> index e7c66a5..c44ee01 100644
> --- a/virt/kvm/arm/vgic/vgic.h
> +++ b/virt/kvm/arm/vgic/vgic.h
> @@ -19,6 +19,9 @@
> #define PRODUCT_ID_KVM 0x4b /* ASCII code K */
> #define IMPLEMENTER_ARM 0x43b
>
> +#define VGIC_ADDR_UNDEF (-1)
> +#define IS_VGIC_ADDR_UNDEF(_x) ((_x) == VGIC_ADDR_UNDEF)
> +
> #define INTERRUPT_ID_BITS_SPIS 10
>
> #define vgic_irq_is_sgi(intid) ((intid) < VGIC_NR_SGIS)
> --
> 2.7.3
>
Reviewed-by: Christoffer Dall <christoffer.dall at linaro.org>
More information about the linux-arm-kernel
mailing list