KVM: arm-vgic: Support KVM_CREATE_DEVICE for VGIC
[firefly-linux-kernel-4.4.55.git] / virt / kvm / arm / vgic.c
index a2d478aec046ce1cec7cfc8a3ddc5ed968acdb57..b15d6c17a0905609db65d419f4c9877099a0dae3 100644 (file)
@@ -149,7 +149,7 @@ static u32 *vgic_bytemap_get_reg(struct vgic_bytemap *x, int cpuid, u32 offset)
 {
        offset >>= 2;
        BUG_ON(offset > (VGIC_NR_IRQS / 4));
-       if (offset < 4)
+       if (offset < 8)
                return x->percpu[cpuid] + offset;
        else
                return x->shared + offset - 8;
@@ -541,8 +541,12 @@ static bool handle_mmio_cfg_reg(struct kvm_vcpu *vcpu,
                                struct kvm_exit_mmio *mmio, phys_addr_t offset)
 {
        u32 val;
-       u32 *reg = vgic_bitmap_get_reg(&vcpu->kvm->arch.vgic.irq_cfg,
-                                      vcpu->vcpu_id, offset >> 1);
+       u32 *reg;
+
+       offset >>= 1;
+       reg = vgic_bitmap_get_reg(&vcpu->kvm->arch.vgic.irq_cfg,
+                                 vcpu->vcpu_id, offset);
+
        if (offset & 2)
                val = *reg >> 16;
        else
@@ -1239,15 +1243,19 @@ static irqreturn_t vgic_maintenance_handler(int irq, void *data)
        return IRQ_HANDLED;
 }
 
+/**
+ * kvm_vgic_vcpu_init - Initialize per-vcpu VGIC state
+ * @vcpu: pointer to the vcpu struct
+ *
+ * Initialize the vgic_cpu struct and vgic_dist struct fields pertaining to
+ * this vcpu and enable the VGIC for this VCPU
+ */
 int kvm_vgic_vcpu_init(struct kvm_vcpu *vcpu)
 {
        struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
        struct vgic_dist *dist = &vcpu->kvm->arch.vgic;
        int i;
 
-       if (!irqchip_in_kernel(vcpu->kvm))
-               return 0;
-
        if (vcpu->vcpu_id >= VGIC_MAX_CPUS)
                return -EBUSY;
 
@@ -1379,10 +1387,22 @@ out:
        return ret;
 }
 
+/**
+ * kvm_vgic_init - Initialize global VGIC state before running any VCPUs
+ * @kvm: pointer to the kvm struct
+ *
+ * Map the virtual CPU interface into the VM before running any VCPUs.  We
+ * can't do this at creation time, because user space must first set the
+ * virtual CPU interface address in the guest physical address space.  Also
+ * initialize the ITARGETSRn regs to 0 on the emulated distributor.
+ */
 int kvm_vgic_init(struct kvm *kvm)
 {
        int ret = 0, i;
 
+       if (!irqchip_in_kernel(kvm))
+               return 0;
+
        mutex_lock(&kvm->lock);
 
        if (vgic_initialized(kvm))
@@ -1405,7 +1425,6 @@ int kvm_vgic_init(struct kvm *kvm)
        for (i = VGIC_NR_PRIVATE_IRQS; i < VGIC_NR_IRQS; i += 4)
                vgic_set_target_reg(kvm, 0, i);
 
-       kvm_timer_init(kvm);
        kvm->arch.vgic.ready = true;
 out:
        mutex_unlock(&kvm->lock);
@@ -1414,20 +1433,45 @@ out:
 
 int kvm_vgic_create(struct kvm *kvm)
 {
-       int ret = 0;
+       int i, vcpu_lock_idx = -1, ret = 0;
+       struct kvm_vcpu *vcpu;
 
        mutex_lock(&kvm->lock);
 
-       if (atomic_read(&kvm->online_vcpus) || kvm->arch.vgic.vctrl_base) {
+       if (kvm->arch.vgic.vctrl_base) {
                ret = -EEXIST;
                goto out;
        }
 
+       /*
+        * Any time a vcpu is run, vcpu_load is called which tries to grab the
+        * vcpu->mutex.  By grabbing the vcpu->mutex of all VCPUs we ensure
+        * that no other VCPUs are run while we create the vgic.
+        */
+       kvm_for_each_vcpu(i, vcpu, kvm) {
+               if (!mutex_trylock(&vcpu->mutex))
+                       goto out_unlock;
+               vcpu_lock_idx = i;
+       }
+
+       kvm_for_each_vcpu(i, vcpu, kvm) {
+               if (vcpu->arch.has_run_once) {
+                       ret = -EBUSY;
+                       goto out_unlock;
+               }
+       }
+
        spin_lock_init(&kvm->arch.vgic.lock);
        kvm->arch.vgic.vctrl_base = vgic_vctrl_base;
        kvm->arch.vgic.vgic_dist_base = VGIC_ADDR_UNDEF;
        kvm->arch.vgic.vgic_cpu_base = VGIC_ADDR_UNDEF;
 
+out_unlock:
+       for (; vcpu_lock_idx >= 0; vcpu_lock_idx--) {
+               vcpu = kvm_get_vcpu(kvm, vcpu_lock_idx);
+               mutex_unlock(&vcpu->mutex);
+       }
+
 out:
        mutex_unlock(&kvm->lock);
        return ret;
@@ -1491,3 +1535,37 @@ int kvm_vgic_set_addr(struct kvm *kvm, unsigned long type, u64 addr)
        mutex_unlock(&kvm->lock);
        return r;
 }
+
+static int vgic_set_attr(struct kvm_device *dev, struct kvm_device_attr *attr)
+{
+       return -ENXIO;
+}
+
+static int vgic_get_attr(struct kvm_device *dev, struct kvm_device_attr *attr)
+{
+       return -ENXIO;
+}
+
+static int vgic_has_attr(struct kvm_device *dev, struct kvm_device_attr *attr)
+{
+       return -ENXIO;
+}
+
+static void vgic_destroy(struct kvm_device *dev)
+{
+       kfree(dev);
+}
+
+static int vgic_create(struct kvm_device *dev, u32 type)
+{
+       return kvm_vgic_create(dev->kvm);
+}
+
+struct kvm_device_ops kvm_arm_vgic_v2_ops = {
+       .name = "kvm-arm-vgic",
+       .create = vgic_create,
+       .destroy = vgic_destroy,
+       .set_attr = vgic_set_attr,
+       .get_attr = vgic_get_attr,
+       .has_attr = vgic_has_attr,
+};