Blame SOURCES/kvm-hw-intc-arm_gicv3_its-Implement-state-save-restore.patch

76daa3
From 3d589a2d3dbe2a376122713cb67be0e3b64d8f3f Mon Sep 17 00:00:00 2001
76daa3
From: Auger Eric <eric.auger@redhat.com>
76daa3
Date: Fri, 16 Jun 2017 15:17:54 +0200
76daa3
Subject: [PATCH 3/5] hw/intc/arm_gicv3_its: Implement state save/restore
76daa3
76daa3
RH-Author: Auger Eric <eric.auger@redhat.com>
76daa3
Message-id: <1497626276-18221-4-git-send-email-eric.auger@redhat.com>
76daa3
Patchwork-id: 75634
76daa3
O-Subject: [Pegas-1.0 qemu-kvm PATCH v2 3/5] hw/intc/arm_gicv3_its: Implement state save/restore
76daa3
Bugzilla: 1462061
76daa3
RH-Acked-by: Laurent Vivier <lvivier@redhat.com>
76daa3
RH-Acked-by: Peter Xu <peterx@redhat.com>
76daa3
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
76daa3
76daa3
We need to handle both registers and ITS tables. While
76daa3
register handling is standard, ITS table handling is more
76daa3
challenging since the kernel API is devised so that the
76daa3
tables are flushed into guest RAM and not in vmstate buffers.
76daa3
76daa3
Flushing the ITS tables on device pre_save() is too late
76daa3
since the guest RAM is already saved at this point.
76daa3
76daa3
Table flushing needs to happen when we are sure the vcpus
76daa3
are stopped and before the last dirty page saving. The
76daa3
right point is RUN_STATE_FINISH_MIGRATE but sometimes the
76daa3
VM gets stopped before migration launch so let's simply
76daa3
flush the tables each time the VM gets stopped.
76daa3
76daa3
For regular ITS registers we just can use vmstate pre_save()
76daa3
and post_load() callbacks.
76daa3
76daa3
Signed-off-by: Eric Auger <eric.auger@redhat.com>
76daa3
Message-id: 1497023553-18411-3-git-send-email-eric.auger@redhat.com
76daa3
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
76daa3
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
76daa3
(cherry picked from commit cddafd8f353d2d251b1a5c6c948a577a85838582)
76daa3
Signed-off-by: Eric Auger <eric.auger@redhat.com>
76daa3
Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
76daa3
---
76daa3
 hw/intc/arm_gicv3_its_common.c         |  10 ++++
76daa3
 hw/intc/arm_gicv3_its_kvm.c            | 105 +++++++++++++++++++++++++++++++++
76daa3
 include/hw/intc/arm_gicv3_its_common.h |   8 +++
76daa3
 3 files changed, 123 insertions(+)
76daa3
76daa3
diff --git a/hw/intc/arm_gicv3_its_common.c b/hw/intc/arm_gicv3_its_common.c
76daa3
index 9d67c5c..696c11c 100644
76daa3
--- a/hw/intc/arm_gicv3_its_common.c
76daa3
+++ b/hw/intc/arm_gicv3_its_common.c
76daa3
@@ -49,6 +49,15 @@ static const VMStateDescription vmstate_its = {
76daa3
     .pre_save = gicv3_its_pre_save,
76daa3
     .post_load = gicv3_its_post_load,
76daa3
     .unmigratable = true,
76daa3
+    .fields = (VMStateField[]) {
76daa3
+        VMSTATE_UINT32(ctlr, GICv3ITSState),
76daa3
+        VMSTATE_UINT32(iidr, GICv3ITSState),
76daa3
+        VMSTATE_UINT64(cbaser, GICv3ITSState),
76daa3
+        VMSTATE_UINT64(cwriter, GICv3ITSState),
76daa3
+        VMSTATE_UINT64(creadr, GICv3ITSState),
76daa3
+        VMSTATE_UINT64_ARRAY(baser, GICv3ITSState, 8),
76daa3
+        VMSTATE_END_OF_LIST()
76daa3
+    },
76daa3
 };
76daa3
 
76daa3
 static MemTxResult gicv3_its_trans_read(void *opaque, hwaddr offset,
76daa3
@@ -118,6 +127,7 @@ static void gicv3_its_common_reset(DeviceState *dev)
76daa3
     s->cbaser = 0;
76daa3
     s->cwriter = 0;
76daa3
     s->creadr = 0;
76daa3
+    s->iidr = 0;
76daa3
     memset(&s->baser, 0, sizeof(s->baser));
76daa3
 
76daa3
     gicv3_its_post_load(s, 0);
76daa3
diff --git a/hw/intc/arm_gicv3_its_kvm.c b/hw/intc/arm_gicv3_its_kvm.c
76daa3
index ad2a1db..3c7c28c 100644
76daa3
--- a/hw/intc/arm_gicv3_its_kvm.c
76daa3
+++ b/hw/intc/arm_gicv3_its_kvm.c
76daa3
@@ -53,6 +53,33 @@ static int kvm_its_send_msi(GICv3ITSState *s, uint32_t value, uint16_t devid)
76daa3
     return kvm_vm_ioctl(kvm_state, KVM_SIGNAL_MSI, &msi;;
76daa3
 }
76daa3
 
76daa3
+/**
76daa3
+ * vm_change_state_handler - VM change state callback aiming at flushing
76daa3
+ * ITS tables into guest RAM
76daa3
+ *
76daa3
+ * The tables get flushed to guest RAM whenever the VM gets stopped.
76daa3
+ */
76daa3
+static void vm_change_state_handler(void *opaque, int running,
76daa3
+                                    RunState state)
76daa3
+{
76daa3
+    GICv3ITSState *s = (GICv3ITSState *)opaque;
76daa3
+    Error *err = NULL;
76daa3
+    int ret;
76daa3
+
76daa3
+    if (running) {
76daa3
+        return;
76daa3
+    }
76daa3
+
76daa3
+    ret = kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_CTRL,
76daa3
+                            KVM_DEV_ARM_ITS_SAVE_TABLES, NULL, true, &err;;
76daa3
+    if (err) {
76daa3
+        error_report_err(err);
76daa3
+    }
76daa3
+    if (ret < 0 && ret != -EFAULT) {
76daa3
+        abort();
76daa3
+    }
76daa3
+}
76daa3
+
76daa3
 static void kvm_arm_its_realize(DeviceState *dev, Error **errp)
76daa3
 {
76daa3
     GICv3ITSState *s = ARM_GICV3_ITS_COMMON(dev);
76daa3
@@ -89,6 +116,8 @@ static void kvm_arm_its_realize(DeviceState *dev, Error **errp)
76daa3
     kvm_msi_use_devid = true;
76daa3
     kvm_gsi_direct_mapping = false;
76daa3
     kvm_msi_via_irqfd_allowed = kvm_irqfds_enabled();
76daa3
+
76daa3
+    qemu_add_vm_change_state_handler(vm_change_state_handler, s);
76daa3
 }
76daa3
 
76daa3
 static void kvm_arm_its_init(Object *obj)
76daa3
@@ -102,6 +131,80 @@ static void kvm_arm_its_init(Object *obj)
76daa3
                              &error_abort);
76daa3
 }
76daa3
 
76daa3
+/**
76daa3
+ * kvm_arm_its_pre_save - handles the saving of ITS registers.
76daa3
+ * ITS tables are flushed into guest RAM separately and earlier,
76daa3
+ * through the VM change state handler, since at the moment pre_save()
76daa3
+ * is called, the guest RAM has already been saved.
76daa3
+ */
76daa3
+static void kvm_arm_its_pre_save(GICv3ITSState *s)
76daa3
+{
76daa3
+    int i;
76daa3
+
76daa3
+    for (i = 0; i < 8; i++) {
76daa3
+        kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
76daa3
+                          GITS_BASER + i * 8, &s->baser[i], false,
76daa3
+                          &error_abort);
76daa3
+    }
76daa3
+
76daa3
+    kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
76daa3
+                      GITS_CTLR, &s->ctlr, false, &error_abort);
76daa3
+
76daa3
+    kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
76daa3
+                      GITS_CBASER, &s->cbaser, false, &error_abort);
76daa3
+
76daa3
+    kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
76daa3
+                      GITS_CREADR, &s->creadr, false, &error_abort);
76daa3
+
76daa3
+    kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
76daa3
+                      GITS_CWRITER, &s->cwriter, false, &error_abort);
76daa3
+
76daa3
+    kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
76daa3
+                      GITS_IIDR, &s->iidr, false, &error_abort);
76daa3
+}
76daa3
+
76daa3
+/**
76daa3
+ * kvm_arm_its_post_load - Restore both the ITS registers and tables
76daa3
+ */
76daa3
+static void kvm_arm_its_post_load(GICv3ITSState *s)
76daa3
+{
76daa3
+    int i;
76daa3
+
76daa3
+    if (!s->iidr) {
76daa3
+        return;
76daa3
+    }
76daa3
+
76daa3
+    kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
76daa3
+                      GITS_IIDR, &s->iidr, true, &error_abort);
76daa3
+
76daa3
+    /*
76daa3
+     * must be written before GITS_CREADR since GITS_CBASER write
76daa3
+     * access resets GITS_CREADR.
76daa3
+     */
76daa3
+    kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
76daa3
+                      GITS_CBASER, &s->cbaser, true, &error_abort);
76daa3
+
76daa3
+    kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
76daa3
+                      GITS_CREADR, &s->creadr, true, &error_abort);
76daa3
+
76daa3
+    kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
76daa3
+                      GITS_CWRITER, &s->cwriter, true, &error_abort);
76daa3
+
76daa3
+
76daa3
+    for (i = 0; i < 8; i++) {
76daa3
+        kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
76daa3
+                          GITS_BASER + i * 8, &s->baser[i], true,
76daa3
+                          &error_abort);
76daa3
+    }
76daa3
+
76daa3
+    kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_CTRL,
76daa3
+                      KVM_DEV_ARM_ITS_RESTORE_TABLES, NULL, true,
76daa3
+                      &error_abort);
76daa3
+
76daa3
+    kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
76daa3
+                      GITS_CTLR, &s->ctlr, true, &error_abort);
76daa3
+}
76daa3
+
76daa3
 static void kvm_arm_its_class_init(ObjectClass *klass, void *data)
76daa3
 {
76daa3
     DeviceClass *dc = DEVICE_CLASS(klass);
76daa3
@@ -109,6 +212,8 @@ static void kvm_arm_its_class_init(ObjectClass *klass, void *data)
76daa3
 
76daa3
     dc->realize = kvm_arm_its_realize;
76daa3
     icc->send_msi = kvm_its_send_msi;
76daa3
+    icc->pre_save = kvm_arm_its_pre_save;
76daa3
+    icc->post_load = kvm_arm_its_post_load;
76daa3
 }
76daa3
 
76daa3
 static const TypeInfo kvm_arm_its_info = {
76daa3
diff --git a/include/hw/intc/arm_gicv3_its_common.h b/include/hw/intc/arm_gicv3_its_common.h
76daa3
index 1ba1894..fd1fe64 100644
76daa3
--- a/include/hw/intc/arm_gicv3_its_common.h
76daa3
+++ b/include/hw/intc/arm_gicv3_its_common.h
76daa3
@@ -28,6 +28,13 @@
76daa3
 #define ITS_TRANS_SIZE   0x10000
76daa3
 #define ITS_SIZE         (ITS_CONTROL_SIZE + ITS_TRANS_SIZE)
76daa3
 
76daa3
+#define GITS_CTLR        0x0
76daa3
+#define GITS_IIDR        0x4
76daa3
+#define GITS_CBASER      0x80
76daa3
+#define GITS_CWRITER     0x88
76daa3
+#define GITS_CREADR      0x90
76daa3
+#define GITS_BASER       0x100
76daa3
+
76daa3
 struct GICv3ITSState {
76daa3
     SysBusDevice parent_obj;
76daa3
 
76daa3
@@ -43,6 +50,7 @@ struct GICv3ITSState {
76daa3
 
76daa3
     /* Registers */
76daa3
     uint32_t ctlr;
76daa3
+    uint32_t iidr;
76daa3
     uint64_t cbaser;
76daa3
     uint64_t cwriter;
76daa3
     uint64_t creadr;
76daa3
-- 
76daa3
1.8.3.1
76daa3