|
@@ -22,6 +22,9 @@
|
|
|
#include <linux/io.h>
|
|
|
#include <asm/kvm_emulate.h>
|
|
|
|
|
|
+#define VGIC_ADDR_UNDEF (-1)
|
|
|
+#define IS_VGIC_ADDR_UNDEF(_x) ((_x) == VGIC_ADDR_UNDEF)
|
|
|
+
|
|
|
#define ACCESS_READ_VALUE (1 << 0)
|
|
|
#define ACCESS_READ_RAZ (0 << 0)
|
|
|
#define ACCESS_READ_MASK(x) ((x) & (1 << 0))
|
|
@@ -151,3 +154,62 @@ bool vgic_handle_mmio(struct kvm_vcpu *vcpu, struct kvm_run *run,
|
|
|
{
|
|
|
return KVM_EXIT_MMIO;
|
|
|
}
|
|
|
+
|
|
|
+static bool 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 (!IS_VGIC_ADDR_UNDEF(*ioaddr))
|
|
|
+ return -EEXIST;
|
|
|
+ if (addr + size < addr)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ ret = vgic_ioaddr_overlap(kvm);
|
|
|
+ if (ret)
|
|
|
+ return ret;
|
|
|
+ *ioaddr = addr;
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+int kvm_vgic_set_addr(struct kvm *kvm, unsigned long type, u64 addr)
|
|
|
+{
|
|
|
+ int r = 0;
|
|
|
+ struct vgic_dist *vgic = &kvm->arch.vgic;
|
|
|
+
|
|
|
+ if (addr & ~KVM_PHYS_MASK)
|
|
|
+ return -E2BIG;
|
|
|
+
|
|
|
+ if (addr & ~PAGE_MASK)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ mutex_lock(&kvm->lock);
|
|
|
+ switch (type) {
|
|
|
+ case KVM_VGIC_V2_ADDR_TYPE_DIST:
|
|
|
+ r = vgic_ioaddr_assign(kvm, &vgic->vgic_dist_base,
|
|
|
+ addr, KVM_VGIC_V2_DIST_SIZE);
|
|
|
+ break;
|
|
|
+ case KVM_VGIC_V2_ADDR_TYPE_CPU:
|
|
|
+ r = vgic_ioaddr_assign(kvm, &vgic->vgic_cpu_base,
|
|
|
+ addr, KVM_VGIC_V2_CPU_SIZE);
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ r = -ENODEV;
|
|
|
+ }
|
|
|
+
|
|
|
+ mutex_unlock(&kvm->lock);
|
|
|
+ return r;
|
|
|
+}
|