KVM: arm64: vgic-v3: KVM_DEV_ARM_VGIC_SAVE_PENDING_TABLES
authorEric Auger <eric.auger@redhat.com>
Mon, 9 Jan 2017 15:28:27 +0000 (16:28 +0100)
committerChristoffer Dall <cdall@linaro.org>
Mon, 8 May 2017 12:39:31 +0000 (14:39 +0200)
This patch adds a new attribute to GICV3 KVM device
KVM_DEV_ARM_VGIC_GRP_CTRL group. This allows userspace to
flush all GICR pending tables into guest RAM.

Signed-off-by: Eric Auger <eric.auger@redhat.com>
Reviewed-by: Christoffer Dall <cdall@linaro.org>
Acked-by: Marc Zyngier <marc.zyngier@arm.com>
arch/arm/include/uapi/asm/kvm.h
arch/arm64/include/uapi/asm/kvm.h
virt/kvm/arm/vgic/vgic-kvm-device.c
virt/kvm/arm/vgic/vgic-v3.c
virt/kvm/arm/vgic/vgic.h

index 0f7ee15f9fcf23a6da43742663289e07aee39622..a3fbab56d4a859e01a6a098c534277ccd59d1033 100644 (file)
@@ -204,6 +204,7 @@ struct kvm_arch_memory_slot {
 #define   KVM_DEV_ARM_VGIC_CTRL_INIT           0
 #define   KVM_DEV_ARM_ITS_SAVE_TABLES          1
 #define   KVM_DEV_ARM_ITS_RESTORE_TABLES       2
+#define   KVM_DEV_ARM_VGIC_SAVE_PENDING_TABLES 3
 
 /* KVM_IRQ_LINE irq field index values */
 #define KVM_ARM_IRQ_TYPE_SHIFT         24
index 5dd7be04993448d3c6fdd1ec0eee3729ca5a9007..d46a99d999ec38a59cb759e9c9793ce22d6f9e51 100644 (file)
@@ -224,6 +224,7 @@ struct kvm_arch_memory_slot {
 #define   KVM_DEV_ARM_VGIC_CTRL_INIT           0
 #define   KVM_DEV_ARM_ITS_SAVE_TABLES           1
 #define   KVM_DEV_ARM_ITS_RESTORE_TABLES        2
+#define   KVM_DEV_ARM_VGIC_SAVE_PENDING_TABLES 3
 
 /* Device Control API on vcpu fd */
 #define KVM_ARM_VCPU_PMU_V3_CTRL       0
index 859bfa871b3015e01c3fc08f26e127815007e738..d48743cafedc5c37f0dd5ca85e262f1f90e14e4c 100644 (file)
@@ -580,6 +580,24 @@ static int vgic_v3_set_attr(struct kvm_device *dev,
                reg = tmp32;
                return vgic_v3_attr_regs_access(dev, attr, &reg, true);
        }
+       case KVM_DEV_ARM_VGIC_GRP_CTRL: {
+               int ret;
+
+               switch (attr->attr) {
+               case KVM_DEV_ARM_VGIC_SAVE_PENDING_TABLES:
+                       mutex_lock(&dev->kvm->lock);
+
+                       if (!lock_all_vcpus(dev->kvm)) {
+                               mutex_unlock(&dev->kvm->lock);
+                               return -EBUSY;
+                       }
+                       ret = vgic_v3_save_pending_tables(dev->kvm);
+                       unlock_all_vcpus(dev->kvm);
+                       mutex_unlock(&dev->kvm->lock);
+                       return ret;
+               }
+               break;
+       }
        }
        return -ENXIO;
 }
@@ -658,6 +676,8 @@ static int vgic_v3_has_attr(struct kvm_device *dev,
                switch (attr->attr) {
                case KVM_DEV_ARM_VGIC_CTRL_INIT:
                        return 0;
+               case KVM_DEV_ARM_VGIC_SAVE_PENDING_TABLES:
+                       return 0;
                }
        }
        return -ENXIO;
index 9ae8adddff69ec24ef57557c732002ac0a3a6670..54dee725da18e28983cbb1b959a69938173b7ca3 100644 (file)
@@ -278,6 +278,57 @@ retry:
        return 0;
 }
 
+/**
+ * vgic_its_save_pending_tables - Save the pending tables into guest RAM
+ * kvm lock and all vcpu lock must be held
+ */
+int vgic_v3_save_pending_tables(struct kvm *kvm)
+{
+       struct vgic_dist *dist = &kvm->arch.vgic;
+       int last_byte_offset = -1;
+       struct vgic_irq *irq;
+       int ret;
+
+       list_for_each_entry(irq, &dist->lpi_list_head, lpi_list) {
+               int byte_offset, bit_nr;
+               struct kvm_vcpu *vcpu;
+               gpa_t pendbase, ptr;
+               bool stored;
+               u8 val;
+
+               vcpu = irq->target_vcpu;
+               if (!vcpu)
+                       continue;
+
+               pendbase = GICR_PENDBASER_ADDRESS(vcpu->arch.vgic_cpu.pendbaser);
+
+               byte_offset = irq->intid / BITS_PER_BYTE;
+               bit_nr = irq->intid % BITS_PER_BYTE;
+               ptr = pendbase + byte_offset;
+
+               if (byte_offset != last_byte_offset) {
+                       ret = kvm_read_guest(kvm, ptr, &val, 1);
+                       if (ret)
+                               return ret;
+                       last_byte_offset = byte_offset;
+               }
+
+               stored = val & (1U << bit_nr);
+               if (stored == irq->pending_latch)
+                       continue;
+
+               if (irq->pending_latch)
+                       val |= 1 << bit_nr;
+               else
+                       val &= ~(1 << bit_nr);
+
+               ret = kvm_write_guest(kvm, ptr, &val, 1);
+               if (ret)
+                       return ret;
+       }
+       return 0;
+}
+
 /* check for overlapping regions and for regions crossing the end of memory */
 static bool vgic_v3_check_base(struct kvm *kvm)
 {
index 433449b25c785663b928a723aabe67de4627df88..8259f0a859ab761e459a28501ee1b7a5b5a35ba4 100644 (file)
@@ -173,6 +173,7 @@ void vgic_v3_enable(struct kvm_vcpu *vcpu);
 int vgic_v3_probe(const struct gic_kvm_info *info);
 int vgic_v3_map_resources(struct kvm *kvm);
 int vgic_v3_lpi_sync_pending_status(struct kvm *kvm, struct vgic_irq *irq);
+int vgic_v3_save_pending_tables(struct kvm *kvm);
 int vgic_register_redist_iodevs(struct kvm *kvm, gpa_t dist_base_address);
 
 void vgic_v3_load(struct kvm_vcpu *vcpu);