KVM: arm/arm64: vgic-new: vgic_kvm_device: implement kvm_vgic_addr
authorEric Auger <eric.auger@linaro.org>
Mon, 21 Dec 2015 15:36:04 +0000 (16:36 +0100)
committerChristoffer Dall <christoffer.dall@linaro.org>
Fri, 20 May 2016 13:40:01 +0000 (15:40 +0200)
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@linaro.org>
Signed-off-by: Andre Przywara <andre.przywara@arm.com>
Reviewed-by: Christoffer Dall <christoffer.dall@linaro.org>
include/kvm/vgic/vgic.h
virt/kvm/arm/vgic/vgic-kvm-device.c
virt/kvm/arm/vgic/vgic.h

index 00e3dca10a6385e9cf0262423a0f81e57fdfc43a..3689b9bf3af7b4703de2cbcf7c47551d3f3d6b0a 100644 (file)
@@ -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);
 
index e153f125458667208eaf450a7f68e185cb754cf3..082829a764dd305dcdd16a7de9e78aaa74f143a8 100644 (file)
 #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_check_ioaddr(struct kvm *kvm, phys_addr_t *ioaddr,
+                            phys_addr_t addr, phys_addr_t alignment)
+{
+       if (addr & ~KVM_PHYS_MASK)
+               return -E2BIG;
+
+       if (!IS_ALIGNED(addr, alignment))
+               return -EINVAL;
+
+       if (!IS_VGIC_ADDR_UNDEF(*ioaddr))
+               return -EEXIST;
+
+       return 0;
+}
+
+/**
+ * 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.
+ * Check them for sanity (alignment, double assignment). We can't check for
+ * overlapping regions in case of a virtual GICv3 here, since we don't know
+ * the number of VCPUs yet, so we defer this check to map_resources().
+ */
+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, 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;
+               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;
+               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;
+               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;
+               alignment = SZ_64K;
+               break;
+#endif
+       default:
+               r = -ENODEV;
+               goto out;
+       }
+
+       if (vgic->vgic_model != type_needed) {
+               r = -ENODEV;
+               goto out;
+       }
+
+       if (write) {
+               r = vgic_check_ioaddr(kvm, addr_ptr, *addr, alignment);
+               if (!r)
+                       *addr_ptr = *addr;
+       } 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)
 {
index 77b0ab38a271981a199736bcd7966a6ffa377f2e..6abc9a35c228526eafa99695531c0515a80b1335 100644 (file)
@@ -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_PRI_BITS          5