]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - virt/kvm/arm/vgic/vgic-v3.c
Merge tag 'kvm-arm-for-4-7-take2' of git://git.kernel.org/pub/scm/linux/kernel/git...
[karo-tx-linux.git] / virt / kvm / arm / vgic / vgic-v3.c
index 8548297c6f76d69a53c842264147bd833ab32f1c..336a4611593732ba6e44b29e1d8f7aac97e89830 100644 (file)
@@ -15,6 +15,9 @@
 #include <linux/irqchip/arm-gic-v3.h>
 #include <linux/kvm.h>
 #include <linux/kvm_host.h>
+#include <kvm/arm_vgic.h>
+#include <asm/kvm_mmu.h>
+#include <asm/kvm_asm.h>
 
 #include "vgic.h"
 
@@ -182,3 +185,146 @@ void vgic_v3_get_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcrp)
        vmcrp->bpr  = (vmcr & ICH_VMCR_BPR0_MASK) >> ICH_VMCR_BPR0_SHIFT;
        vmcrp->pmr  = (vmcr & ICH_VMCR_PMR_MASK) >> ICH_VMCR_PMR_SHIFT;
 }
+
+void vgic_v3_enable(struct kvm_vcpu *vcpu)
+{
+       struct vgic_v3_cpu_if *vgic_v3 = &vcpu->arch.vgic_cpu.vgic_v3;
+
+       /*
+        * By forcing VMCR to zero, the GIC will restore the binary
+        * points to their reset values. Anything else resets to zero
+        * anyway.
+        */
+       vgic_v3->vgic_vmcr = 0;
+       vgic_v3->vgic_elrsr = ~0;
+
+       /*
+        * If we are emulating a GICv3, we do it in an non-GICv2-compatible
+        * way, so we force SRE to 1 to demonstrate this to the guest.
+        * This goes with the spec allowing the value to be RAO/WI.
+        */
+       if (vcpu->kvm->arch.vgic.vgic_model == KVM_DEV_TYPE_ARM_VGIC_V3)
+               vgic_v3->vgic_sre = ICC_SRE_EL1_SRE;
+       else
+               vgic_v3->vgic_sre = 0;
+
+       /* Get the show on the road... */
+       vgic_v3->vgic_hcr = ICH_HCR_EN;
+}
+
+/* check for overlapping regions and for regions crossing the end of memory */
+static bool vgic_v3_check_base(struct kvm *kvm)
+{
+       struct vgic_dist *d = &kvm->arch.vgic;
+       gpa_t redist_size = KVM_VGIC_V3_REDIST_SIZE;
+
+       redist_size *= atomic_read(&kvm->online_vcpus);
+
+       if (d->vgic_dist_base + KVM_VGIC_V3_DIST_SIZE < d->vgic_dist_base)
+               return false;
+       if (d->vgic_redist_base + redist_size < d->vgic_redist_base)
+               return false;
+
+       if (d->vgic_dist_base + KVM_VGIC_V3_DIST_SIZE <= d->vgic_redist_base)
+               return true;
+       if (d->vgic_redist_base + redist_size <= d->vgic_dist_base)
+               return true;
+
+       return false;
+}
+
+int vgic_v3_map_resources(struct kvm *kvm)
+{
+       int ret = 0;
+       struct vgic_dist *dist = &kvm->arch.vgic;
+
+       if (vgic_ready(kvm))
+               goto out;
+
+       if (IS_VGIC_ADDR_UNDEF(dist->vgic_dist_base) ||
+           IS_VGIC_ADDR_UNDEF(dist->vgic_redist_base)) {
+               kvm_err("Need to set vgic distributor addresses first\n");
+               ret = -ENXIO;
+               goto out;
+       }
+
+       if (!vgic_v3_check_base(kvm)) {
+               kvm_err("VGIC redist and dist frames overlap\n");
+               ret = -EINVAL;
+               goto out;
+       }
+
+       /*
+        * For a VGICv3 we require the userland to explicitly initialize
+        * the VGIC before we need to use it.
+        */
+       if (!vgic_initialized(kvm)) {
+               ret = -EBUSY;
+               goto out;
+       }
+
+       ret = vgic_register_dist_iodev(kvm, dist->vgic_dist_base, VGIC_V3);
+       if (ret) {
+               kvm_err("Unable to register VGICv3 dist MMIO regions\n");
+               goto out;
+       }
+
+       ret = vgic_register_redist_iodevs(kvm, dist->vgic_redist_base);
+       if (ret) {
+               kvm_err("Unable to register VGICv3 redist MMIO regions\n");
+               goto out;
+       }
+
+       dist->ready = true;
+
+out:
+       if (ret)
+               kvm_vgic_destroy(kvm);
+       return ret;
+}
+
+/**
+ * vgic_v3_probe - probe for a GICv3 compatible interrupt controller in DT
+ * @node:      pointer to the DT node
+ *
+ * Returns 0 if a GICv3 has been found, returns an error code otherwise
+ */
+int vgic_v3_probe(const struct gic_kvm_info *info)
+{
+       u32 ich_vtr_el2 = kvm_call_hyp(__vgic_v3_get_ich_vtr_el2);
+
+       /*
+        * The ListRegs field is 5 bits, but there is a architectural
+        * maximum of 16 list registers. Just ignore bit 4...
+        */
+       kvm_vgic_global_state.nr_lr = (ich_vtr_el2 & 0xf) + 1;
+       kvm_vgic_global_state.can_emulate_gicv2 = false;
+
+       if (!info->vcpu.start) {
+               kvm_info("GICv3: no GICV resource entry\n");
+               kvm_vgic_global_state.vcpu_base = 0;
+       } else if (!PAGE_ALIGNED(info->vcpu.start)) {
+               pr_warn("GICV physical address 0x%llx not page aligned\n",
+                       (unsigned long long)info->vcpu.start);
+               kvm_vgic_global_state.vcpu_base = 0;
+       } else if (!PAGE_ALIGNED(resource_size(&info->vcpu))) {
+               pr_warn("GICV size 0x%llx not a multiple of page size 0x%lx\n",
+                       (unsigned long long)resource_size(&info->vcpu),
+                       PAGE_SIZE);
+               kvm_vgic_global_state.vcpu_base = 0;
+       } else {
+               kvm_vgic_global_state.vcpu_base = info->vcpu.start;
+               kvm_vgic_global_state.can_emulate_gicv2 = true;
+               kvm_register_vgic_device(KVM_DEV_TYPE_ARM_VGIC_V2);
+               kvm_info("vgic-v2@%llx\n", info->vcpu.start);
+       }
+       if (kvm_vgic_global_state.vcpu_base == 0)
+               kvm_info("disabling GICv2 emulation\n");
+       kvm_register_vgic_device(KVM_DEV_TYPE_ARM_VGIC_V3);
+
+       kvm_vgic_global_state.vctrl_base = NULL;
+       kvm_vgic_global_state.type = VGIC_V3;
+       kvm_vgic_global_state.max_gic_vcpus = VGIC_V3_MAX_CPUS;
+
+       return 0;
+}