qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v2 0/8] GICv3 LPI and ITS feature implementation
@ 2021-04-01  2:41 Shashi Mallela
  2021-04-01  2:41 ` [PATCH v2 1/8] hw/intc: GICv3 ITS initial framework Shashi Mallela
                   ` (7 more replies)
  0 siblings, 8 replies; 25+ messages in thread
From: Shashi Mallela @ 2021-04-01  2:41 UTC (permalink / raw)
  To: peter.maydell, leif, rad; +Cc: qemu-arm, qemu-devel

This patchset implements qemu device model for enabling physical
LPI support and ITS functionality in GIC as per GICv3 specification.
Both flat table and 2 level tables are implemented.The ITS commands
for adding/deleting ITS table entries,trigerring LPI interrupts are 
implemented.Translated LPI interrupt ids are processed by redistributor
to determine priority and set pending state appropriately before
forwarding the same to cpu interface.
The ITS feature support has been added to sbsa-ref platform as well as
virt platform,wherein the emulated functionality co-exists with kvm
kernel functionality.

Changes in v2:
 - used FIELD to avoid manual mask definition/manipulation
 - defined registers in patches that first use them
 - changed LOG_GUEST_ERROR to LOG_UNIMP for unimplemented code
 - all kvm-unit-tests passed except 1 inapplicable case of its-trigger
   which assumes presence of redistributor caching

Shashi Mallela (8):
  hw/intc: GICv3 ITS initial framework
  hw/intc: GICv3 ITS register definitions added
  hw/intc: GICv3 ITS command queue framework
  hw/intc: GICv3 ITS Command processing
  hw/intc: GICv3 ITS Feature enablement
  hw/intc: GICv3 redistributor ITS processing
  hw/arm/sbsa-ref: add ITS support in SBSA GIC
  hw/arm/virt: add ITS support in virt GIC

 hw/arm/sbsa-ref.c                      |   26 +-
 hw/arm/virt.c                          |   10 +-
 hw/intc/arm_gicv3.c                    |    6 +
 hw/intc/arm_gicv3_common.c             |   16 +
 hw/intc/arm_gicv3_cpuif.c              |   15 +-
 hw/intc/arm_gicv3_dist.c               |   22 +-
 hw/intc/arm_gicv3_its.c                | 1359 ++++++++++++++++++++++++
 hw/intc/arm_gicv3_its_common.c         |   54 +-
 hw/intc/arm_gicv3_its_kvm.c            |    2 +-
 hw/intc/arm_gicv3_redist.c             |  152 ++-
 hw/intc/gicv3_internal.h               |  177 ++-
 hw/intc/meson.build                    |    1 +
 include/hw/intc/arm_gicv3_common.h     |   14 +
 include/hw/intc/arm_gicv3_its_common.h |   40 +-
 target/arm/kvm_arm.h                   |    4 +-
 15 files changed, 1867 insertions(+), 31 deletions(-)
 create mode 100644 hw/intc/arm_gicv3_its.c

-- 
2.27.0



^ permalink raw reply	[flat|nested] 25+ messages in thread

* [PATCH v2 1/8] hw/intc: GICv3 ITS initial framework
  2021-04-01  2:41 [PATCH v2 0/8] GICv3 LPI and ITS feature implementation Shashi Mallela
@ 2021-04-01  2:41 ` Shashi Mallela
  2021-04-16 17:21   ` Peter Maydell
  2021-04-01  2:41 ` [PATCH v2 2/8] hw/intc: GICv3 ITS register definitions added Shashi Mallela
                   ` (6 subsequent siblings)
  7 siblings, 1 reply; 25+ messages in thread
From: Shashi Mallela @ 2021-04-01  2:41 UTC (permalink / raw)
  To: peter.maydell, leif, rad; +Cc: qemu-arm, qemu-devel

Added register definitions relevant to ITS,implemented overall
ITS device framework with stubs for ITS control and translater
regions read/write,extended ITS common to handle mmio init between
existing kvm device and newer qemu device.

Signed-off-by: Shashi Mallela <shashi.mallela@linaro.org>
---
 hw/intc/arm_gicv3_its.c                | 334 +++++++++++++++++++++++++
 hw/intc/arm_gicv3_its_common.c         |  12 +-
 hw/intc/arm_gicv3_its_kvm.c            |   2 +-
 hw/intc/gicv3_internal.h               |  86 ++++++-
 hw/intc/meson.build                    |   1 +
 include/hw/intc/arm_gicv3_its_common.h |  12 +-
 6 files changed, 430 insertions(+), 17 deletions(-)
 create mode 100644 hw/intc/arm_gicv3_its.c

diff --git a/hw/intc/arm_gicv3_its.c b/hw/intc/arm_gicv3_its.c
new file mode 100644
index 0000000000..209120d102
--- /dev/null
+++ b/hw/intc/arm_gicv3_its.c
@@ -0,0 +1,334 @@
+/*
+ * ITS emulation for a GICv3-based system
+ *
+ * Copyright Linaro.org 2021
+ *
+ * Authors:
+ *  Shashi Mallela <shashi.mallela@linaro.org>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or (at your
+ * option) any later version.  See the COPYING file in the top-level directory.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "hw/qdev-properties.h"
+#include "hw/intc/arm_gicv3_its_common.h"
+#include "gicv3_internal.h"
+#include "qom/object.h"
+
+typedef struct GICv3ITSClass GICv3ITSClass;
+/* This is reusing the GICv3ITSState typedef from ARM_GICV3_ITS_COMMON */
+DECLARE_OBJ_CHECKERS(GICv3ITSState, GICv3ITSClass,
+                     ARM_GICV3_ITS, TYPE_ARM_GICV3_ITS)
+
+struct GICv3ITSClass {
+    GICv3ITSCommonClass parent_class;
+    void (*parent_reset)(DeviceState *dev);
+};
+
+static MemTxResult its_trans_writew(GICv3ITSState *s, hwaddr offset,
+                               uint64_t value, MemTxAttrs attrs)
+{
+    MemTxResult result = MEMTX_OK;
+
+    return result;
+}
+
+static MemTxResult its_trans_writel(GICv3ITSState *s, hwaddr offset,
+                               uint64_t value, MemTxAttrs attrs)
+{
+    MemTxResult result = MEMTX_OK;
+
+    return result;
+}
+
+static MemTxResult gicv3_its_translation_write(void *opaque, hwaddr offset,
+                               uint64_t data, unsigned size, MemTxAttrs attrs)
+{
+    GICv3ITSState *s = (GICv3ITSState *)opaque;
+    MemTxResult result;
+
+    switch (size) {
+    case 2:
+        result = its_trans_writew(s, offset, data, attrs);
+        break;
+    case 4:
+        result = its_trans_writel(s, offset, data, attrs);
+        break;
+    default:
+        result = MEMTX_ERROR;
+        break;
+    }
+
+    if (result == MEMTX_ERROR) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "%s: invalid guest write at offset " TARGET_FMT_plx
+                      "size %u\n", __func__, offset, size);
+        /*
+         * The spec requires that reserved registers are RAZ/WI;
+         * so use MEMTX_ERROR returns from leaf functions as a way to
+         * trigger the guest-error logging but don't return it to
+         * the caller, or we'll cause a spurious guest data abort.
+         */
+        result = MEMTX_OK;
+    }
+    return result;
+}
+
+static MemTxResult gicv3_its_translation_read(void *opaque, hwaddr offset,
+                              uint64_t *data, unsigned size, MemTxAttrs attrs)
+{
+    qemu_log_mask(LOG_GUEST_ERROR,
+        "%s: Invalid read from translation register area at offset "
+        TARGET_FMT_plx "\n", __func__, offset);
+    return MEMTX_ERROR;
+}
+
+static MemTxResult its_writeb(GICv3ITSState *s, hwaddr offset,
+                               uint64_t value, MemTxAttrs attrs)
+{
+    qemu_log_mask(LOG_UNIMP,
+                "%s: unsupported byte write to register at offset "
+                TARGET_FMT_plx "\n", __func__, offset);
+    return MEMTX_ERROR;
+}
+
+static MemTxResult its_readb(GICv3ITSState *s, hwaddr offset,
+                               uint64_t *data, MemTxAttrs attrs)
+{
+    qemu_log_mask(LOG_UNIMP,
+                "%s: unsupported byte read from register at offset "
+                TARGET_FMT_plx "\n", __func__, offset);
+    return MEMTX_ERROR;
+}
+
+static MemTxResult its_writew(GICv3ITSState *s, hwaddr offset,
+                               uint64_t value, MemTxAttrs attrs)
+{
+    qemu_log_mask(LOG_UNIMP,
+        "%s: unsupported word write to register at offset "
+        TARGET_FMT_plx "\n", __func__, offset);
+    return MEMTX_ERROR;
+}
+
+static MemTxResult its_readw(GICv3ITSState *s, hwaddr offset,
+                               uint64_t *data, MemTxAttrs attrs)
+{
+    qemu_log_mask(LOG_UNIMP,
+        "%s: unsupported word read from register at offset "
+        TARGET_FMT_plx "\n", __func__, offset);
+    return MEMTX_ERROR;
+}
+
+static MemTxResult its_writel(GICv3ITSState *s, hwaddr offset,
+                               uint64_t value, MemTxAttrs attrs)
+{
+    MemTxResult result = MEMTX_OK;
+
+    return result;
+}
+
+static MemTxResult its_readl(GICv3ITSState *s, hwaddr offset,
+                               uint64_t *data, MemTxAttrs attrs)
+{
+    MemTxResult result = MEMTX_OK;
+
+    return result;
+}
+
+static MemTxResult its_writell(GICv3ITSState *s, hwaddr offset,
+                               uint64_t value, MemTxAttrs attrs)
+{
+    MemTxResult result = MEMTX_OK;
+
+    return result;
+}
+
+static MemTxResult its_readll(GICv3ITSState *s, hwaddr offset,
+                               uint64_t *data, MemTxAttrs attrs)
+{
+    MemTxResult result = MEMTX_OK;
+
+    return result;
+}
+
+static MemTxResult gicv3_its_read(void *opaque, hwaddr offset, uint64_t *data,
+                              unsigned size, MemTxAttrs attrs)
+{
+    GICv3ITSState *s = (GICv3ITSState *)opaque;
+    MemTxResult result;
+
+    switch (size) {
+    case 1:
+        result = its_readb(s, offset, data, attrs);
+        break;
+    case 2:
+        result = its_readw(s, offset, data, attrs);
+        break;
+    case 4:
+        result = its_readl(s, offset, data, attrs);
+        break;
+    case 8:
+        result = its_readll(s, offset, data, attrs);
+        break;
+    default:
+        result = MEMTX_ERROR;
+        break;
+    }
+
+    if (result == MEMTX_ERROR) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "%s: invalid guest read at offset " TARGET_FMT_plx
+                      "size %u\n", __func__, offset, size);
+        /*
+         * The spec requires that reserved registers are RAZ/WI;
+         * so use MEMTX_ERROR returns from leaf functions as a way to
+         * trigger the guest-error logging but don't return it to
+         * the caller, or we'll cause a spurious guest data abort.
+         */
+        result = MEMTX_OK;
+        *data = 0;
+    }
+    return result;
+}
+
+static MemTxResult gicv3_its_write(void *opaque, hwaddr offset, uint64_t data,
+                               unsigned size, MemTxAttrs attrs)
+{
+    GICv3ITSState *s = (GICv3ITSState *)opaque;
+    MemTxResult result;
+
+    switch (size) {
+    case 1:
+        result = its_writeb(s, offset, data, attrs);
+        break;
+    case 2:
+        result = its_writew(s, offset, data, attrs);
+        break;
+    case 4:
+        result = its_writel(s, offset, data, attrs);
+        break;
+    case 8:
+        result = its_writell(s, offset, data, attrs);
+        break;
+    default:
+        result = MEMTX_ERROR;
+        break;
+    }
+
+    if (result == MEMTX_ERROR) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "%s: invalid guest write at offset " TARGET_FMT_plx
+                      "size %u\n", __func__, offset, size);
+        /*
+         * The spec requires that reserved registers are RAZ/WI;
+         * so use MEMTX_ERROR returns from leaf functions as a way to
+         * trigger the guest-error logging but don't return it to
+         * the caller, or we'll cause a spurious guest data abort.
+         */
+        result = MEMTX_OK;
+    }
+    return result;
+}
+
+static const MemoryRegionOps gicv3_its_control_ops = {
+    .read_with_attrs = gicv3_its_read,
+    .write_with_attrs = gicv3_its_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static const MemoryRegionOps gicv3_its_translation_ops = {
+    .read_with_attrs = gicv3_its_translation_read,
+    .write_with_attrs = gicv3_its_translation_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void gicv3_arm_its_realize(DeviceState *dev, Error **errp)
+{
+    GICv3ITSState *s = ARM_GICV3_ITS_COMMON(dev);
+
+    gicv3_its_init_mmio(s, &gicv3_its_control_ops, &gicv3_its_translation_ops);
+}
+
+static void gicv3_its_reset(DeviceState *dev)
+{
+    GICv3ITSState *s = ARM_GICV3_ITS_COMMON(dev);
+    GICv3ITSClass *c = ARM_GICV3_ITS_GET_CLASS(s);
+
+    if (s->gicv3->cpu->gicr_typer & GICR_TYPER_PLPIS) {
+        c->parent_reset(dev);
+
+        /* set the ITS default features supported */
+        s->typer = FIELD_DP64(s->typer, GITS_TYPER, PHYSICAL,
+                                       GITS_TYPE_PHYSICAL);
+        s->typer = FIELD_DP64(s->typer, GITS_TYPER, ITT_ENTRY_SIZE,
+                                       ITS_ITT_ENTRY_SIZE);
+        s->typer = FIELD_DP64(s->typer, GITS_TYPER, IDBITS, ITS_IDBITS);
+        s->typer = FIELD_DP64(s->typer, GITS_TYPER, DEVBITS, ITS_DEVBITS);
+        s->typer = FIELD_DP64(s->typer, GITS_TYPER, CIL, 1);
+        s->typer = FIELD_DP64(s->typer, GITS_TYPER, CIDBITS, ITS_CIDBITS);
+
+        /*
+         * We claim to be an ARM r0p0 with a zero ProductID.
+         * This is the same as an r0p0 GIC-500.
+         */
+        s->iidr = gicv3_iidr();
+
+        /* Quiescent bit reset to 1 */
+        s->ctlr = FIELD_DP32(s->ctlr, GITS_CTLR, QUIESCENT, 1);
+
+        /*
+         * setting GITS_BASER0.Type = 0b001 (Device)
+         *         GITS_BASER1.Type = 0b100 (Collection Table)
+         *         GITS_BASER<n>.Type,where n = 3 to 7 are 0b00 (Unimplemented)
+         *         GITS_BASER<0,1>.Page_Size = 64KB
+         * and default translation table entry size to 16 bytes
+         */
+        s->baser[0] = FIELD_DP64(s->baser[0], GITS_BASER, TYPE,
+                                              GITS_ITT_TYPE_DEVICE);
+        s->baser[0] = FIELD_DP64(s->baser[0], GITS_BASER, PAGESIZE,
+                                              GITS_BASER_PAGESIZE_64K);
+        s->baser[0] = FIELD_DP64(s->baser[0], GITS_BASER, ENTRYSIZE,
+                                              GITS_DTE_SIZE);
+
+        s->baser[1] = FIELD_DP64(s->baser[1], GITS_BASER, TYPE,
+                                              GITS_ITT_TYPE_COLLECTION);
+        s->baser[1] = FIELD_DP64(s->baser[1], GITS_BASER, PAGESIZE,
+                                              GITS_BASER_PAGESIZE_64K);
+        s->baser[1] = FIELD_DP64(s->baser[1], GITS_BASER, ENTRYSIZE,
+                                              GITS_CTE_SIZE);
+    }
+}
+
+static Property gicv3_its_props[] = {
+    DEFINE_PROP_LINK("parent-gicv3", GICv3ITSState, gicv3, "arm-gicv3",
+                     GICv3State *),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void gicv3_its_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    GICv3ITSClass *ic = ARM_GICV3_ITS_CLASS(klass);
+
+    dc->realize = gicv3_arm_its_realize;
+    device_class_set_props(dc, gicv3_its_props);
+    device_class_set_parent_reset(dc, gicv3_its_reset, &ic->parent_reset);
+}
+
+static const TypeInfo gicv3_its_info = {
+    .name = TYPE_ARM_GICV3_ITS,
+    .parent = TYPE_ARM_GICV3_ITS_COMMON,
+    .instance_size = sizeof(GICv3ITSState),
+    .class_init = gicv3_its_class_init,
+    .class_size = sizeof(GICv3ITSClass),
+};
+
+static void gicv3_its_register_types(void)
+{
+    type_register_static(&gicv3_its_info);
+}
+
+type_init(gicv3_its_register_types)
diff --git a/hw/intc/arm_gicv3_its_common.c b/hw/intc/arm_gicv3_its_common.c
index 66c4c6a188..80cc9ec6d8 100644
--- a/hw/intc/arm_gicv3_its_common.c
+++ b/hw/intc/arm_gicv3_its_common.c
@@ -50,12 +50,16 @@ static int gicv3_its_post_load(void *opaque, int version_id)
 
 static const VMStateDescription vmstate_its = {
     .name = "arm_gicv3_its",
+    .version_id = 1,
+    .minimum_version_id = 1,
     .pre_save = gicv3_its_pre_save,
     .post_load = gicv3_its_post_load,
     .priority = MIG_PRI_GICV3_ITS,
     .fields = (VMStateField[]) {
         VMSTATE_UINT32(ctlr, GICv3ITSState),
+        VMSTATE_UINT32(translater, GICv3ITSState),
         VMSTATE_UINT32(iidr, GICv3ITSState),
+        VMSTATE_UINT64(typer, GICv3ITSState),
         VMSTATE_UINT64(cbaser, GICv3ITSState),
         VMSTATE_UINT64(cwriter, GICv3ITSState),
         VMSTATE_UINT64(creadr, GICv3ITSState),
@@ -99,15 +103,16 @@ static const MemoryRegionOps gicv3_its_trans_ops = {
     .endianness = DEVICE_NATIVE_ENDIAN,
 };
 
-void gicv3_its_init_mmio(GICv3ITSState *s, const MemoryRegionOps *ops)
+void gicv3_its_init_mmio(GICv3ITSState *s, const MemoryRegionOps *ops,
+                           const MemoryRegionOps *tops)
 {
     SysBusDevice *sbd = SYS_BUS_DEVICE(s);
 
     memory_region_init_io(&s->iomem_its_cntrl, OBJECT(s), ops, s,
                           "control", ITS_CONTROL_SIZE);
     memory_region_init_io(&s->iomem_its_translation, OBJECT(s),
-                          &gicv3_its_trans_ops, s,
-                          "translation", ITS_TRANS_SIZE);
+                         tops ? tops : &gicv3_its_trans_ops, s,
+                         "translation", ITS_TRANS_SIZE);
 
     /* Our two regions are always adjacent, therefore we now combine them
      * into a single one in order to make our users' life easier.
@@ -130,6 +135,7 @@ static void gicv3_its_common_reset(DeviceState *dev)
     s->cwriter = 0;
     s->creadr = 0;
     s->iidr = 0;
+    s->translater = 0;
     memset(&s->baser, 0, sizeof(s->baser));
 }
 
diff --git a/hw/intc/arm_gicv3_its_kvm.c b/hw/intc/arm_gicv3_its_kvm.c
index b554d2ede0..0b4cbed28b 100644
--- a/hw/intc/arm_gicv3_its_kvm.c
+++ b/hw/intc/arm_gicv3_its_kvm.c
@@ -106,7 +106,7 @@ static void kvm_arm_its_realize(DeviceState *dev, Error **errp)
     kvm_arm_register_device(&s->iomem_its_cntrl, -1, KVM_DEV_ARM_VGIC_GRP_ADDR,
                             KVM_VGIC_ITS_ADDR_TYPE, s->dev_fd, 0);
 
-    gicv3_its_init_mmio(s, NULL);
+    gicv3_its_init_mmio(s, NULL, NULL);
 
     if (!kvm_device_check_attr(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
         GITS_CTLR)) {
diff --git a/hw/intc/gicv3_internal.h b/hw/intc/gicv3_internal.h
index 05303a55c8..96cfe2dff9 100644
--- a/hw/intc/gicv3_internal.h
+++ b/hw/intc/gicv3_internal.h
@@ -24,6 +24,7 @@
 #ifndef QEMU_ARM_GICV3_INTERNAL_H
 #define QEMU_ARM_GICV3_INTERNAL_H
 
+#include "hw/registerfields.h"
 #include "hw/intc/arm_gicv3_common.h"
 
 /* Distributor registers, as offsets from the distributor base address */
@@ -67,6 +68,9 @@
 #define GICD_CTLR_E1NWF             (1U << 7)
 #define GICD_CTLR_RWP               (1U << 31)
 
+/* 16 bits EventId */
+#define GICD_TYPER_IDBITS            0xf
+
 /*
  * Redistributor frame offsets from RD_base
  */
@@ -122,18 +126,6 @@
 #define GICR_WAKER_ProcessorSleep    (1U << 1)
 #define GICR_WAKER_ChildrenAsleep    (1U << 2)
 
-#define GICR_PROPBASER_OUTER_CACHEABILITY_MASK (7ULL << 56)
-#define GICR_PROPBASER_ADDR_MASK               (0xfffffffffULL << 12)
-#define GICR_PROPBASER_SHAREABILITY_MASK       (3U << 10)
-#define GICR_PROPBASER_CACHEABILITY_MASK       (7U << 7)
-#define GICR_PROPBASER_IDBITS_MASK             (0x1f)
-
-#define GICR_PENDBASER_PTZ                     (1ULL << 62)
-#define GICR_PENDBASER_OUTER_CACHEABILITY_MASK (7ULL << 56)
-#define GICR_PENDBASER_ADDR_MASK               (0xffffffffULL << 16)
-#define GICR_PENDBASER_SHAREABILITY_MASK       (3U << 10)
-#define GICR_PENDBASER_CACHEABILITY_MASK       (7U << 7)
-
 #define ICC_CTLR_EL1_CBPR           (1U << 0)
 #define ICC_CTLR_EL1_EOIMODE        (1U << 1)
 #define ICC_CTLR_EL1_PMHE           (1U << 6)
@@ -239,6 +231,76 @@
 #define ICH_VTR_EL2_PREBITS_SHIFT 26
 #define ICH_VTR_EL2_PRIBITS_SHIFT 29
 
+/* ITS Registers */
+
+FIELD(GITS_BASER, SIZE, 0, 8)
+FIELD(GITS_BASER, PAGESIZE, 8, 2)
+FIELD(GITS_BASER, SHAREABILITY, 10, 2)
+FIELD(GITS_BASER, PHYADDR, 12, 36)
+FIELD(GITS_BASER, PHYADDRL_64K, 16, 32)
+FIELD(GITS_BASER, PHYADDRH_64K, 48, 4)
+FIELD(GITS_BASER, ENTRYSIZE, 48, 5)
+FIELD(GITS_BASER, OUTERCACHE, 53, 3)
+FIELD(GITS_BASER, TYPE, 56, 3)
+FIELD(GITS_BASER, INNERCACHE, 59, 3)
+FIELD(GITS_BASER, INDIRECT, 62, 1)
+FIELD(GITS_BASER, VALID, 63, 1)
+
+#define GITS_BASER_PAGESIZE_4K                0
+#define GITS_BASER_PAGESIZE_16K               1
+#define GITS_BASER_PAGESIZE_64K               2
+
+#define GITS_ITT_TYPE_DEVICE                  1ULL
+#define GITS_ITT_TYPE_COLLECTION              4ULL
+
+FIELD(GITS_CTLR, QUIESCENT, 31, 1)
+
+FIELD(GITS_TYPER, PHYSICAL, 0, 1)
+FIELD(GITS_TYPER, ITT_ENTRY_SIZE, 4, 4)
+FIELD(GITS_TYPER, IDBITS, 8, 5)
+FIELD(GITS_TYPER, DEVBITS, 13, 5)
+FIELD(GITS_TYPER, SEIS, 18, 1)
+FIELD(GITS_TYPER, PTA, 19, 1)
+FIELD(GITS_TYPER, CIDBITS, 32, 4)
+FIELD(GITS_TYPER, CIL, 36, 1)
+
+/**
+ * Default features advertised by this version of ITS
+ */
+/* Physical LPIs supported */
+#define GITS_TYPE_PHYSICAL           (1U << 0)
+
+/*
+ * 11 bytes Interrupt translation Table Entry size
+ * Valid = 1 bit,InterruptType = 1 bit,
+ * Size of LPI number space[considering max 24 bits],
+ * Size of LPI number space[considering max 24 bits],
+ * ICID = 16 bits,
+ * vPEID = 16 bits
+ */
+#define ITS_ITT_ENTRY_SIZE            0xB
+
+/* 16 bits EventId */
+#define ITS_IDBITS                   GICD_TYPER_IDBITS
+
+/* 16 bits DeviceId */
+#define ITS_DEVBITS                   0xF
+
+/* 16 bits CollectionId */
+#define ITS_CIDBITS                  0xF
+
+/*
+ * 8 bytes Device Table Entry size
+ * Valid = 1 bit,ITTAddr = 44 bits,Size = 5 bits
+ */
+#define GITS_DTE_SIZE                 (0x8ULL)
+
+/*
+ * 8 bytes Collection Table Entry size
+ * Valid = 1 bit,RDBase = 36 bits(considering max RDBASE)
+ */
+#define GITS_CTE_SIZE                 (0x8ULL)
+
 /* Special interrupt IDs */
 #define INTID_SECURE 1020
 #define INTID_NONSECURE 1021
diff --git a/hw/intc/meson.build b/hw/intc/meson.build
index 1c299039f6..53472239f0 100644
--- a/hw/intc/meson.build
+++ b/hw/intc/meson.build
@@ -8,6 +8,7 @@ softmmu_ss.add(when: 'CONFIG_ARM_GIC', if_true: files(
   'arm_gicv3_dist.c',
   'arm_gicv3_its_common.c',
   'arm_gicv3_redist.c',
+  'arm_gicv3_its.c',
 ))
 softmmu_ss.add(when: 'CONFIG_ETRAXFS', if_true: files('etraxfs_pic.c'))
 softmmu_ss.add(when: 'CONFIG_HEATHROW_PIC', if_true: files('heathrow_pic.c'))
diff --git a/include/hw/intc/arm_gicv3_its_common.h b/include/hw/intc/arm_gicv3_its_common.h
index 5a0952b404..08bc5652ed 100644
--- a/include/hw/intc/arm_gicv3_its_common.h
+++ b/include/hw/intc/arm_gicv3_its_common.h
@@ -25,17 +25,24 @@
 #include "hw/intc/arm_gicv3_common.h"
 #include "qom/object.h"
 
+#define TYPE_ARM_GICV3_ITS "arm-gicv3-its"
+
 #define ITS_CONTROL_SIZE 0x10000
 #define ITS_TRANS_SIZE   0x10000
 #define ITS_SIZE         (ITS_CONTROL_SIZE + ITS_TRANS_SIZE)
 
 #define GITS_CTLR        0x0
 #define GITS_IIDR        0x4
+#define GITS_TYPER       0x8
 #define GITS_CBASER      0x80
 #define GITS_CWRITER     0x88
 #define GITS_CREADR      0x90
 #define GITS_BASER       0x100
 
+#define GITS_TRANSLATER  0x0040
+
+#define GITS_PIDR2       0xFFE8
+
 struct GICv3ITSState {
     SysBusDevice parent_obj;
 
@@ -52,6 +59,8 @@ struct GICv3ITSState {
     /* Registers */
     uint32_t ctlr;
     uint32_t iidr;
+    uint32_t translater;
+    uint64_t typer;
     uint64_t cbaser;
     uint64_t cwriter;
     uint64_t creadr;
@@ -62,7 +71,8 @@ struct GICv3ITSState {
 
 typedef struct GICv3ITSState GICv3ITSState;
 
-void gicv3_its_init_mmio(GICv3ITSState *s, const MemoryRegionOps *ops);
+void gicv3_its_init_mmio(GICv3ITSState *s, const MemoryRegionOps *ops,
+                   const MemoryRegionOps *tops);
 
 #define TYPE_ARM_GICV3_ITS_COMMON "arm-gicv3-its-common"
 typedef struct GICv3ITSCommonClass GICv3ITSCommonClass;
-- 
2.27.0



^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [PATCH v2 2/8] hw/intc: GICv3 ITS register definitions added
  2021-04-01  2:41 [PATCH v2 0/8] GICv3 LPI and ITS feature implementation Shashi Mallela
  2021-04-01  2:41 ` [PATCH v2 1/8] hw/intc: GICv3 ITS initial framework Shashi Mallela
@ 2021-04-01  2:41 ` Shashi Mallela
  2021-04-16 18:54   ` Peter Maydell
  2021-04-01  2:41 ` [PATCH v2 3/8] hw/intc: GICv3 ITS command queue framework Shashi Mallela
                   ` (5 subsequent siblings)
  7 siblings, 1 reply; 25+ messages in thread
From: Shashi Mallela @ 2021-04-01  2:41 UTC (permalink / raw)
  To: peter.maydell, leif, rad; +Cc: qemu-arm, qemu-devel

Defined descriptors for ITS device table,collection table and ITS
command queue entities.Implemented register read/write functions,
extract ITS table parameters and command queue parameters,extended
gicv3 common to capture qemu address space(which host the ITS table
platform memories required for subsequent ITS processing) and
initialize the same in its device.

Signed-off-by: Shashi Mallela <shashi.mallela@linaro.org>
---
 hw/intc/arm_gicv3_its.c                | 313 +++++++++++++++++++++++++
 hw/intc/arm_gicv3_its_common.c         |  42 ++++
 hw/intc/gicv3_internal.h               |  33 ++-
 include/hw/intc/arm_gicv3_common.h     |   4 +
 include/hw/intc/arm_gicv3_its_common.h |  28 +++
 5 files changed, 414 insertions(+), 6 deletions(-)

diff --git a/hw/intc/arm_gicv3_its.c b/hw/intc/arm_gicv3_its.c
index 209120d102..81373f049d 100644
--- a/hw/intc/arm_gicv3_its.c
+++ b/hw/intc/arm_gicv3_its.c
@@ -28,6 +28,131 @@ struct GICv3ITSClass {
     void (*parent_reset)(DeviceState *dev);
 };
 
+static bool extract_table_params(GICv3ITSState *s, int index)
+{
+    uint16_t num_pages = 0;
+    uint8_t  page_sz_type;
+    uint8_t type;
+    uint32_t page_sz = 0;
+    uint64_t value = s->baser[index];
+
+    num_pages = FIELD_EX64(value, GITS_BASER, SIZE);
+    page_sz_type = FIELD_EX64(value, GITS_BASER, PAGESIZE);
+
+    if (page_sz_type == 0) {
+        page_sz = GITS_ITT_PAGE_SIZE_0;
+    } else if (page_sz_type == 0) {
+        page_sz = GITS_ITT_PAGE_SIZE_1;
+    } else if (page_sz_type == 2) {
+        page_sz = GITS_ITT_PAGE_SIZE_2;
+    } else {
+        return false;
+    }
+
+    type = FIELD_EX64(value, GITS_BASER, TYPE);
+
+    if (type == GITS_ITT_TYPE_DEVICE) {
+        s->dt.valid = FIELD_EX64(value, GITS_BASER, VALID);
+
+        if (s->dt.valid) {
+            s->dt.indirect = FIELD_EX64(value, GITS_BASER, INDIRECT);
+            s->dt.entry_sz = FIELD_EX64(value, GITS_BASER, ENTRYSIZE);
+
+            if (!s->dt.indirect) {
+                s->dt.max_entries = ((num_pages + 1) * page_sz) /
+                                                       s->dt.entry_sz;
+            } else {
+                s->dt.max_entries = ((((num_pages + 1) * page_sz) /
+                                        L1TABLE_ENTRY_SIZE) *
+                                    (page_sz / s->dt.entry_sz));
+            }
+
+            s->dt.max_devids = (1UL << (FIELD_EX64(s->typer, GITS_TYPER,
+                                                    DEVBITS) + 1));
+
+            if ((page_sz == GITS_ITT_PAGE_SIZE_0) ||
+                    (page_sz == GITS_ITT_PAGE_SIZE_1)) {
+                s->dt.base_addr = FIELD_EX64(value, GITS_BASER, PHYADDR);
+                s->dt.base_addr <<= R_GITS_BASER_PHYADDR_SHIFT;
+            } else if (page_sz == GITS_ITT_PAGE_SIZE_2) {
+                s->dt.base_addr = FIELD_EX64(value, GITS_BASER, PHYADDRL_64K) <<
+                                  R_GITS_BASER_PHYADDRL_64K_SHIFT;
+                  s->dt.base_addr |= ((value >> R_GITS_BASER_PHYADDR_SHIFT) &
+                                       R_GITS_BASER_PHYADDRH_64K_MASK) <<
+                                       R_GITS_BASER_PHYADDRH_64K_SHIFT;
+            }
+        }
+    } else if (type == GITS_ITT_TYPE_COLLECTION) {
+        s->ct.valid = FIELD_EX64(value, GITS_BASER, VALID);
+
+        /*
+         * GITS_TYPER.HCC is 0 for this implementation
+         * hence writes are discarded if ct.valid is 0
+         */
+        if (s->ct.valid) {
+            s->ct.indirect = FIELD_EX64(value, GITS_BASER, INDIRECT);
+            s->ct.entry_sz = FIELD_EX64(value, GITS_BASER, ENTRYSIZE);
+
+            if (!s->ct.indirect) {
+                s->ct.max_entries = ((num_pages + 1) * page_sz) /
+                                      s->ct.entry_sz;
+            } else {
+                s->ct.max_entries = ((((num_pages + 1) * page_sz) /
+                                      L1TABLE_ENTRY_SIZE) *
+                                      (page_sz / s->ct.entry_sz));
+            }
+
+            if (FIELD_EX64(s->typer, GITS_TYPER, CIL)) {
+                s->ct.max_collids = (1UL << (FIELD_EX64(s->typer, GITS_TYPER,
+                                                         CIDBITS) + 1));
+            } else {
+                /* 16-bit CollectionId supported when CIL == 0 */
+                s->ct.max_collids = (1UL << 16);
+            }
+
+            if ((page_sz == GITS_ITT_PAGE_SIZE_0) ||
+                 (page_sz == GITS_ITT_PAGE_SIZE_1)) {
+                s->ct.base_addr = FIELD_EX64(value, GITS_BASER, PHYADDR);
+                s->ct.base_addr <<= R_GITS_BASER_PHYADDR_SHIFT;
+            } else if (page_sz == GITS_ITT_PAGE_SIZE_2) {
+                s->ct.base_addr = FIELD_EX64(value, GITS_BASER, PHYADDRL_64K) <<
+                                    R_GITS_BASER_PHYADDRL_64K_SHIFT;
+                s->ct.base_addr |= ((value >> R_GITS_BASER_PHYADDR_SHIFT) &
+                                     R_GITS_BASER_PHYADDRH_64K_MASK) <<
+                                     R_GITS_BASER_PHYADDRH_64K_SHIFT;
+            }
+        }
+    } else {
+        /* unsupported ITS table type */
+        qemu_log_mask(LOG_GUEST_ERROR, "%s: Unsupported ITS table type %d",
+                         __func__, type);
+        return false;
+    }
+    return true;
+}
+
+static bool extract_cmdq_params(GICv3ITSState *s)
+{
+    uint16_t num_pages = 0;
+    uint64_t value = s->cbaser;
+
+    num_pages = FIELD_EX64(value, GITS_CBASER, SIZE);
+
+    s->cq.valid = FIELD_EX64(value, GITS_CBASER, VALID);
+
+    if (!num_pages || !s->cq.valid) {
+        return false;
+    }
+
+    if (s->cq.valid) {
+        s->cq.max_entries = ((num_pages + 1) * GITS_ITT_PAGE_SIZE_0) /
+                                                GITS_CMDQ_ENTRY_SIZE;
+        s->cq.base_addr = FIELD_EX64(value, GITS_CBASER, PHYADDR);
+        s->cq.base_addr <<= R_GITS_CBASER_PHYADDR_SHIFT;
+    }
+    return true;
+}
+
 static MemTxResult its_trans_writew(GICv3ITSState *s, hwaddr offset,
                                uint64_t value, MemTxAttrs attrs)
 {
@@ -126,7 +251,75 @@ static MemTxResult its_writel(GICv3ITSState *s, hwaddr offset,
                                uint64_t value, MemTxAttrs attrs)
 {
     MemTxResult result = MEMTX_OK;
+    int index;
+    uint64_t temp = 0;
 
+    switch (offset) {
+    case GITS_CTLR:
+        s->ctlr |= (value & ~(s->ctlr));
+        break;
+    case GITS_CBASER:
+        /* GITS_CBASER register becomes RO if ITS is already enabled */
+        if (!(s->ctlr & ITS_CTLR_ENABLED)) {
+            s->cbaser = deposit64(s->cbaser, 0, 32, value);
+            s->creadr = 0;
+        }
+        break;
+    case GITS_CBASER + 4:
+        /* GITS_CBASER register becomes RO if ITS is already enabled */
+        if (!(s->ctlr & ITS_CTLR_ENABLED)) {
+            s->cbaser = deposit64(s->cbaser, 32, 32, value);
+            if (!extract_cmdq_params(s)) {
+                qemu_log_mask(LOG_GUEST_ERROR,
+                       "%s: error extracting GITS_CBASER parameters "
+                       TARGET_FMT_plx "\n", __func__, offset);
+                s->cbaser = 0;
+                result = MEMTX_ERROR;
+            } else {
+                s->creadr = 0;
+            }
+        }
+        break;
+    case GITS_CWRITER:
+        s->cwriter = deposit64(s->cwriter, 0, 32, value);
+        break;
+    case GITS_CWRITER + 4:
+        s->cwriter = deposit64(s->cwriter, 32, 32, value);
+        break;
+    case GITS_BASER ... GITS_BASER + 0x3f:
+        /* GITS_BASERn registers become RO if ITS is already enabled */
+        if (!(s->ctlr & ITS_CTLR_ENABLED)) {
+            index = (offset - GITS_BASER) / 8;
+
+            if (offset & 7) {
+                temp = s->baser[index];
+                temp = deposit64(temp, 32, 32, (value & ~GITS_BASER_VAL_MASK));
+                s->baser[index] |= temp;
+
+                if (!extract_table_params(s, index)) {
+                    qemu_log_mask(LOG_GUEST_ERROR,
+                        "%s: error extracting GITS_BASER parameters "
+                        TARGET_FMT_plx "\n", __func__, offset);
+                    s->baser[index] = 0;
+                    result = MEMTX_ERROR;
+                }
+            } else {
+                s->baser[index] =  deposit64(s->baser[index], 0, 32, value);
+            }
+        }
+        break;
+    case GITS_IIDR:
+    case GITS_TYPER:
+    case GITS_CREADR:
+        /* RO registers, ignore the write */
+        qemu_log_mask(LOG_GUEST_ERROR,
+            "%s: invalid guest write to RO register at offset "
+            TARGET_FMT_plx "\n", __func__, offset);
+        break;
+    default:
+        result = MEMTX_ERROR;
+        break;
+    }
     return result;
 }
 
@@ -134,7 +327,54 @@ static MemTxResult its_readl(GICv3ITSState *s, hwaddr offset,
                                uint64_t *data, MemTxAttrs attrs)
 {
     MemTxResult result = MEMTX_OK;
+    int index;
 
+    switch (offset) {
+    case GITS_CTLR:
+        *data = s->ctlr;
+        break;
+    case GITS_IIDR:
+        *data = s->iidr;
+        break;
+    case GITS_PIDR2:
+        *data = 0x30; /* GICv3 */
+        break;
+    case GITS_TYPER:
+        *data = extract64(s->typer, 0, 32);
+        break;
+    case GITS_TYPER + 4:
+        *data = extract64(s->typer, 32, 32);
+        break;
+    case GITS_CBASER:
+        *data = extract64(s->cbaser, 0, 32);
+        break;
+    case GITS_CBASER + 4:
+        *data = extract64(s->cbaser, 32, 32);
+        break;
+    case GITS_CREADR:
+        *data = extract64(s->creadr, 0, 32);
+        break;
+    case GITS_CREADR + 4:
+        *data = extract64(s->creadr, 32, 32);
+        break;
+    case GITS_CWRITER:
+        *data = extract64(s->cwriter, 0, 32);
+        break;
+    case GITS_CWRITER + 4:
+        *data = extract64(s->cwriter, 32, 32);
+        break;
+    case GITS_BASER ... GITS_BASER + 0x3f:
+        index = (offset - GITS_BASER) / 8;
+        if (offset & 7) {
+            *data = s->baser[index] >> 32;
+        } else {
+            *data = (uint32_t)s->baser[index];
+        }
+        break;
+    default:
+        result = MEMTX_ERROR;
+        break;
+    }
     return result;
 }
 
@@ -142,7 +382,52 @@ static MemTxResult its_writell(GICv3ITSState *s, hwaddr offset,
                                uint64_t value, MemTxAttrs attrs)
 {
     MemTxResult result = MEMTX_OK;
+    int index;
 
+    switch (offset) {
+    case GITS_BASER ... GITS_BASER + 0x3f:
+        /* GITS_BASERn registers become RO if ITS is already enabled */
+        if (!(s->ctlr & ITS_CTLR_ENABLED)) {
+            index = (offset - GITS_BASER) / 8;
+            s->baser[index] |= (value & ~GITS_BASER_VAL_MASK);
+            if (!extract_table_params(s, index)) {
+                qemu_log_mask(LOG_GUEST_ERROR,
+                        "%s: error extracting GITS_BASER parameters "
+                        TARGET_FMT_plx "\n", __func__, offset);
+                s->baser[index] = 0;
+                result = MEMTX_ERROR;
+            }
+        }
+        break;
+    case GITS_CBASER:
+        /* GITS_CBASER register becomes RO if ITS is already enabled */
+        if (!(s->ctlr & ITS_CTLR_ENABLED)) {
+            s->cbaser = value;
+            if (!extract_cmdq_params(s)) {
+                qemu_log_mask(LOG_GUEST_ERROR,
+                       "%s: error extracting GITS_CBASER parameters "
+                       TARGET_FMT_plx "\n", __func__, offset);
+                s->cbaser = 0;
+                result = MEMTX_ERROR;
+            } else {
+                s->creadr = 0;
+            }
+        }
+        break;
+    case GITS_CWRITER:
+        s->cwriter = value;
+        break;
+    case GITS_TYPER:
+    case GITS_CREADR:
+        /* RO register, ignore the write */
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "%s: invalid guest write to RO register at offset "
+                      TARGET_FMT_plx "\n", __func__, offset);
+        break;
+    default:
+        result = MEMTX_ERROR;
+        break;
+    }
     return result;
 }
 
@@ -150,7 +435,29 @@ static MemTxResult its_readll(GICv3ITSState *s, hwaddr offset,
                                uint64_t *data, MemTxAttrs attrs)
 {
     MemTxResult result = MEMTX_OK;
+    int index;
 
+    switch (offset) {
+    case GITS_TYPER:
+        *data = s->typer;
+        break;
+    case GITS_BASER ... GITS_BASER + 0x3f:
+        index = (offset - GITS_BASER) / 8;
+        *data = s->baser[index];
+        break;
+    case GITS_CBASER:
+        *data = s->cbaser;
+        break;
+    case GITS_CREADR:
+        *data = s->creadr;
+        break;
+    case GITS_CWRITER:
+        *data = s->cwriter;
+        break;
+    default:
+        result = MEMTX_ERROR;
+        break;
+    }
     return result;
 }
 
@@ -250,6 +557,9 @@ static void gicv3_arm_its_realize(DeviceState *dev, Error **errp)
     GICv3ITSState *s = ARM_GICV3_ITS_COMMON(dev);
 
     gicv3_its_init_mmio(s, &gicv3_its_control_ops, &gicv3_its_translation_ops);
+
+    address_space_init(&s->gicv3->sysmem_as, s->gicv3->sysmem,
+                        "gicv3-its-sysmem");
 }
 
 static void gicv3_its_reset(DeviceState *dev)
@@ -259,6 +569,9 @@ static void gicv3_its_reset(DeviceState *dev)
 
     if (s->gicv3->cpu->gicr_typer & GICR_TYPER_PLPIS) {
         c->parent_reset(dev);
+        memset(&s->dt, 0, sizeof(s->dt));
+        memset(&s->ct, 0, sizeof(s->ct));
+        memset(&s->cq, 0, sizeof(s->cq));
 
         /* set the ITS default features supported */
         s->typer = FIELD_DP64(s->typer, GITS_TYPER, PHYSICAL,
diff --git a/hw/intc/arm_gicv3_its_common.c b/hw/intc/arm_gicv3_its_common.c
index 80cc9ec6d8..9c5c71f350 100644
--- a/hw/intc/arm_gicv3_its_common.c
+++ b/hw/intc/arm_gicv3_its_common.c
@@ -48,6 +48,42 @@ static int gicv3_its_post_load(void *opaque, int version_id)
     return 0;
 }
 
+static const VMStateDescription vmstate_its_dtdesc = {
+    .name = "arm_gicv3_its_dtdesc",
+    .fields = (VMStateField[]) {
+        VMSTATE_BOOL(valid, DevTableDesc),
+        VMSTATE_BOOL(indirect, DevTableDesc),
+        VMSTATE_UINT16(entry_sz, DevTableDesc),
+        VMSTATE_UINT32(max_entries, DevTableDesc),
+        VMSTATE_UINT32(max_devids, DevTableDesc),
+        VMSTATE_UINT64(base_addr, DevTableDesc),
+        VMSTATE_END_OF_LIST(),
+    },
+};
+
+static const VMStateDescription vmstate_its_ctdesc = {
+    .name = "arm_gicv3_its_ctdesc",
+    .fields = (VMStateField[]) {
+        VMSTATE_BOOL(valid, CollTableDesc),
+        VMSTATE_BOOL(indirect, CollTableDesc),
+        VMSTATE_UINT16(entry_sz, CollTableDesc),
+        VMSTATE_UINT32(max_entries, CollTableDesc),
+        VMSTATE_UINT32(max_collids, CollTableDesc),
+        VMSTATE_UINT64(base_addr, CollTableDesc),
+        VMSTATE_END_OF_LIST(),
+    },
+};
+
+static const VMStateDescription vmstate_its_cqdesc = {
+    .name = "arm_gicv3_its_cqdesc",
+    .fields = (VMStateField[]) {
+        VMSTATE_BOOL(valid, CmdQDesc),
+        VMSTATE_UINT32(max_entries, CmdQDesc),
+        VMSTATE_UINT64(base_addr, CmdQDesc),
+        VMSTATE_END_OF_LIST(),
+    },
+};
+
 static const VMStateDescription vmstate_its = {
     .name = "arm_gicv3_its",
     .version_id = 1,
@@ -56,6 +92,12 @@ static const VMStateDescription vmstate_its = {
     .post_load = gicv3_its_post_load,
     .priority = MIG_PRI_GICV3_ITS,
     .fields = (VMStateField[]) {
+        VMSTATE_STRUCT(dt, GICv3ITSState, 0, vmstate_its_dtdesc,
+                        DevTableDesc),
+        VMSTATE_STRUCT(ct, GICv3ITSState, 0, vmstate_its_ctdesc,
+                        CollTableDesc),
+        VMSTATE_STRUCT(cq, GICv3ITSState, 0, vmstate_its_cqdesc,
+                        CmdQDesc),
         VMSTATE_UINT32(ctlr, GICv3ITSState),
         VMSTATE_UINT32(translater, GICv3ITSState),
         VMSTATE_UINT32(iidr, GICv3ITSState),
diff --git a/hw/intc/gicv3_internal.h b/hw/intc/gicv3_internal.h
index 96cfe2dff9..b7701e8ca5 100644
--- a/hw/intc/gicv3_internal.h
+++ b/hw/intc/gicv3_internal.h
@@ -246,13 +246,14 @@ FIELD(GITS_BASER, INNERCACHE, 59, 3)
 FIELD(GITS_BASER, INDIRECT, 62, 1)
 FIELD(GITS_BASER, VALID, 63, 1)
 
-#define GITS_BASER_PAGESIZE_4K                0
-#define GITS_BASER_PAGESIZE_16K               1
-#define GITS_BASER_PAGESIZE_64K               2
-
-#define GITS_ITT_TYPE_DEVICE                  1ULL
-#define GITS_ITT_TYPE_COLLECTION              4ULL
+FIELD(GITS_CBASER, SIZE, 0, 8)
+FIELD(GITS_CBASER, SHAREABILITY, 10, 2)
+FIELD(GITS_CBASER, PHYADDR, 12, 40)
+FIELD(GITS_CBASER, OUTERCACHE, 53, 3)
+FIELD(GITS_CBASER, INNERCACHE, 59, 3)
+FIELD(GITS_CBASER, VALID, 63, 1)
 
+FIELD(GITS_CTLR, ENABLED, 0, 1)
 FIELD(GITS_CTLR, QUIESCENT, 31, 1)
 
 FIELD(GITS_TYPER, PHYSICAL, 0, 1)
@@ -264,6 +265,26 @@ FIELD(GITS_TYPER, PTA, 19, 1)
 FIELD(GITS_TYPER, CIDBITS, 32, 4)
 FIELD(GITS_TYPER, CIL, 36, 1)
 
+#define ITS_CTLR_ENABLED               (1U)  /* ITS Enabled */
+
+#define GITS_BASER_VAL_MASK                  (R_GITS_BASER_ENTRYSIZE_MASK | \
+                                              R_GITS_BASER_TYPE_MASK)
+
+#define GITS_BASER_PAGESIZE_4K                0
+#define GITS_BASER_PAGESIZE_16K               1
+#define GITS_BASER_PAGESIZE_64K               2
+
+#define GITS_ITT_TYPE_DEVICE                  1ULL
+#define GITS_ITT_TYPE_COLLECTION              4ULL
+
+#define GITS_ITT_PAGE_SIZE_0      0x1000
+#define GITS_ITT_PAGE_SIZE_1      0x4000
+#define GITS_ITT_PAGE_SIZE_2      0x10000
+
+#define L1TABLE_ENTRY_SIZE         8
+
+#define GITS_CMDQ_ENTRY_SIZE               32
+
 /**
  * Default features advertised by this version of ITS
  */
diff --git a/include/hw/intc/arm_gicv3_common.h b/include/hw/intc/arm_gicv3_common.h
index 91491a2f66..b0f2414fa3 100644
--- a/include/hw/intc/arm_gicv3_common.h
+++ b/include/hw/intc/arm_gicv3_common.h
@@ -226,12 +226,16 @@ struct GICv3State {
     int dev_fd; /* kvm device fd if backed by kvm vgic support */
     Error *migration_blocker;
 
+    MemoryRegion *sysmem;
+    AddressSpace sysmem_as;
+
     /* Distributor */
 
     /* for a GIC with the security extensions the NS banked version of this
      * register is just an alias of bit 1 of the S banked version.
      */
     uint32_t gicd_ctlr;
+    uint32_t gicd_typer;
     uint32_t gicd_statusr[2];
     GIC_DECLARE_BITMAP(group);        /* GICD_IGROUPR */
     GIC_DECLARE_BITMAP(grpmod);       /* GICD_IGRPMODR */
diff --git a/include/hw/intc/arm_gicv3_its_common.h b/include/hw/intc/arm_gicv3_its_common.h
index 08bc5652ed..b7eca57221 100644
--- a/include/hw/intc/arm_gicv3_its_common.h
+++ b/include/hw/intc/arm_gicv3_its_common.h
@@ -43,6 +43,30 @@
 
 #define GITS_PIDR2       0xFFE8
 
+typedef struct {
+    bool valid;
+    bool indirect;
+    uint16_t entry_sz;
+    uint32_t max_entries;
+    uint32_t max_devids;
+    uint64_t base_addr;
+} DevTableDesc;
+
+typedef struct {
+    bool valid;
+    bool indirect;
+    uint16_t entry_sz;
+    uint32_t max_entries;
+    uint32_t max_collids;
+    uint64_t base_addr;
+} CollTableDesc;
+
+typedef struct {
+    bool valid;
+    uint32_t max_entries;
+    uint64_t base_addr;
+} CmdQDesc;
+
 struct GICv3ITSState {
     SysBusDevice parent_obj;
 
@@ -66,6 +90,10 @@ struct GICv3ITSState {
     uint64_t creadr;
     uint64_t baser[8];
 
+    DevTableDesc  dt;
+    CollTableDesc ct;
+    CmdQDesc      cq;
+
     Error *migration_blocker;
 };
 
-- 
2.27.0



^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [PATCH v2 3/8] hw/intc: GICv3 ITS command queue framework
  2021-04-01  2:41 [PATCH v2 0/8] GICv3 LPI and ITS feature implementation Shashi Mallela
  2021-04-01  2:41 ` [PATCH v2 1/8] hw/intc: GICv3 ITS initial framework Shashi Mallela
  2021-04-01  2:41 ` [PATCH v2 2/8] hw/intc: GICv3 ITS register definitions added Shashi Mallela
@ 2021-04-01  2:41 ` Shashi Mallela
  2021-04-19 10:30   ` Peter Maydell
  2021-04-01  2:41 ` [PATCH v2 4/8] hw/intc: GICv3 ITS Command processing Shashi Mallela
                   ` (4 subsequent siblings)
  7 siblings, 1 reply; 25+ messages in thread
From: Shashi Mallela @ 2021-04-01  2:41 UTC (permalink / raw)
  To: peter.maydell, leif, rad; +Cc: qemu-arm, qemu-devel

Added functionality to trigger ITS command queue processing on
write to CWRITE register and process each command queue entry to
identify the command type and handle commands like MAPD,MAPC,SYNC.

Signed-off-by: Shashi Mallela <shashi.mallela@linaro.org>
---
 hw/intc/arm_gicv3_its.c  | 347 +++++++++++++++++++++++++++++++++++++++
 hw/intc/gicv3_internal.h |  41 +++++
 2 files changed, 388 insertions(+)

diff --git a/hw/intc/arm_gicv3_its.c b/hw/intc/arm_gicv3_its.c
index 81373f049d..fcac33c836 100644
--- a/hw/intc/arm_gicv3_its.c
+++ b/hw/intc/arm_gicv3_its.c
@@ -28,6 +28,347 @@ struct GICv3ITSClass {
     void (*parent_reset)(DeviceState *dev);
 };
 
+static MemTxResult process_sync(GICv3ITSState *s, uint32_t offset)
+{
+    AddressSpace *as = &s->gicv3->sysmem_as;
+    uint64_t rdbase;
+    uint64_t value;
+    bool pta = false;
+    MemTxResult res = MEMTX_OK;
+
+    offset += NUM_BYTES_IN_DW;
+    offset += NUM_BYTES_IN_DW;
+
+    value = address_space_ldq_le(as, s->cq.base_addr + offset,
+                                     MEMTXATTRS_UNSPECIFIED, &res);
+
+    if (FIELD_EX64(s->typer, GITS_TYPER, PTA)) {
+        /*
+         * only bits[47:16] are considered instead of bits [51:16]
+         * since with a physical address the target address must be
+         * 64KB aligned
+         */
+        rdbase = (value >> RDBASE_OFFSET) & RDBASE_MASK;
+        pta = true;
+    } else {
+        rdbase = (value >> RDBASE_OFFSET) & RDBASE_PROCNUM_MASK;
+    }
+
+    if (!pta && (rdbase < (s->gicv3->num_cpu))) {
+        /*
+         * Current implementation makes a blocking synchronous call
+         * for every command issued earlier,hence the internal state
+         * is already consistent by the time SYNC command is executed.
+         */
+    }
+
+    offset += NUM_BYTES_IN_DW;
+    return res;
+}
+
+static void update_cte(GICv3ITSState *s, uint16_t icid, uint64_t cte)
+{
+    AddressSpace *as = &s->gicv3->sysmem_as;
+    uint64_t value;
+    uint8_t  page_sz_type;
+    uint64_t l2t_addr;
+    bool valid_l2t;
+    uint32_t l2t_id;
+    uint32_t page_sz = 0;
+    uint32_t max_l2_entries;
+
+    if (s->ct.indirect) {
+        /* 2 level table */
+        page_sz_type = FIELD_EX64(s->baser[1], GITS_BASER, PAGESIZE);
+
+        if (page_sz_type == 0) {
+            page_sz = GITS_ITT_PAGE_SIZE_0;
+        } else if (page_sz_type == 1) {
+            page_sz = GITS_ITT_PAGE_SIZE_1;
+        } else if (page_sz_type == 2) {
+            page_sz = GITS_ITT_PAGE_SIZE_2;
+        }
+
+        l2t_id = icid / (page_sz / L1TABLE_ENTRY_SIZE);
+
+        value = address_space_ldq_le(as,
+                                     s->ct.base_addr +
+                                     (l2t_id * L1TABLE_ENTRY_SIZE),
+                                     MEMTXATTRS_UNSPECIFIED, NULL);
+
+        valid_l2t = (value >> VALID_SHIFT) & VALID_MASK;
+
+        if (valid_l2t) {
+            max_l2_entries = page_sz / s->ct.entry_sz;
+
+            l2t_addr = (value >> page_sz_type) &
+                        ((1ULL << (51 - page_sz_type)) - 1);
+
+            address_space_write(as, l2t_addr +
+                                 ((icid % max_l2_entries) * GITS_CTE_SIZE),
+                                 MEMTXATTRS_UNSPECIFIED,
+                                 &cte, sizeof(cte));
+        }
+    } else {
+        /* Flat level table */
+        address_space_write(as, s->ct.base_addr + (icid * GITS_CTE_SIZE),
+                            MEMTXATTRS_UNSPECIFIED, &cte,
+                            sizeof(cte));
+    }
+}
+
+static MemTxResult process_mapc(GICv3ITSState *s, uint32_t offset)
+{
+    AddressSpace *as = &s->gicv3->sysmem_as;
+    uint16_t icid;
+    uint64_t rdbase;
+    bool valid;
+    bool pta = false;
+    MemTxResult res = MEMTX_OK;
+    uint64_t cte_entry;
+    uint64_t value;
+
+    offset += NUM_BYTES_IN_DW;
+    offset += NUM_BYTES_IN_DW;
+
+    value = address_space_ldq_le(as, s->cq.base_addr + offset,
+                                 MEMTXATTRS_UNSPECIFIED, &res);
+
+    icid = value & ICID_MASK;
+
+    if (FIELD_EX64(s->typer, GITS_TYPER, PTA)) {
+        /*
+         * only bits[47:16] are considered instead of bits [51:16]
+         * since with a physical address the target address must be
+         * 64KB aligned
+         */
+        rdbase = (value >> RDBASE_OFFSET) & RDBASE_MASK;
+        pta = true;
+    } else {
+        rdbase = (value >> RDBASE_OFFSET) & RDBASE_PROCNUM_MASK;
+    }
+
+    valid = (value >> VALID_SHIFT) & VALID_MASK;
+
+    if (valid) {
+        if ((icid > s->ct.max_collids) || (!pta &&
+                (rdbase > s->gicv3->num_cpu))) {
+            if (FIELD_EX64(s->typer, GITS_TYPER, SEIS)) {
+                /* Generate System Error here if supported */
+            }
+            qemu_log_mask(LOG_GUEST_ERROR,
+                "%s: invalid collection table attributes "
+                "icid %d rdbase %lu\n", __func__, icid, rdbase);
+            /*
+             * in this implementation,in case of error
+             * we ignore this command and move onto the next
+             * command in the queue
+             */
+        } else {
+            if (s->ct.valid) {
+                /* add mapping entry to collection table */
+                cte_entry = (valid & VALID_MASK) |
+                            (pta ? ((rdbase & RDBASE_MASK) << 1ULL) :
+                            ((rdbase & RDBASE_PROCNUM_MASK) << 1ULL));
+
+                update_cte(s, icid, cte_entry);
+            }
+        }
+    } else {
+        if (s->ct.valid) {
+            /* remove mapping entry from collection table */
+            cte_entry = 0;
+
+            update_cte(s, icid, cte_entry);
+        }
+    }
+
+    offset += NUM_BYTES_IN_DW;
+    offset += NUM_BYTES_IN_DW;
+
+    return res;
+}
+
+static void update_dte(GICv3ITSState *s, uint32_t devid, uint64_t dte)
+{
+    AddressSpace *as = &s->gicv3->sysmem_as;
+    uint64_t value;
+    uint8_t  page_sz_type;
+    uint64_t l2t_addr;
+    bool valid_l2t;
+    uint32_t l2t_id;
+    uint32_t page_sz = 0;
+    uint32_t max_l2_entries;
+
+    if (s->dt.indirect) {
+        /* 2 level table */
+        page_sz_type = FIELD_EX64(s->baser[0], GITS_BASER, PAGESIZE);
+
+        if (page_sz_type == 0) {
+            page_sz = GITS_ITT_PAGE_SIZE_0;
+        } else if (page_sz_type == 1) {
+            page_sz = GITS_ITT_PAGE_SIZE_1;
+        } else if (page_sz_type == 2) {
+            page_sz = GITS_ITT_PAGE_SIZE_2;
+        }
+
+        l2t_id = devid / (page_sz / L1TABLE_ENTRY_SIZE);
+
+        value = address_space_ldq_le(as,
+                                     s->dt.base_addr +
+                                     (l2t_id * L1TABLE_ENTRY_SIZE),
+                                     MEMTXATTRS_UNSPECIFIED, NULL);
+
+        valid_l2t = (value >> VALID_SHIFT) & VALID_MASK;
+
+        if (valid_l2t) {
+            max_l2_entries = page_sz / s->dt.entry_sz;
+
+            l2t_addr = (value >> page_sz_type) &
+                        ((1ULL << (51 - page_sz_type)) - 1);
+
+            address_space_write(as, l2t_addr +
+                                 ((devid % max_l2_entries) * GITS_DTE_SIZE),
+                                 MEMTXATTRS_UNSPECIFIED, &dte, sizeof(dte));
+        }
+    } else {
+        /* Flat level table */
+        address_space_write(as, s->dt.base_addr + (devid * GITS_DTE_SIZE),
+                            MEMTXATTRS_UNSPECIFIED, &dte, sizeof(dte));
+    }
+}
+
+static MemTxResult process_mapd(GICv3ITSState *s, uint64_t value,
+                                 uint32_t offset)
+{
+    AddressSpace *as = &s->gicv3->sysmem_as;
+    uint32_t devid;
+    uint8_t size;
+    uint64_t itt_addr;
+    bool valid;
+    MemTxResult res = MEMTX_OK;
+    uint64_t dte_entry = 0;
+
+    devid = (value >> DEVID_OFFSET) & DEVID_MASK;
+
+    offset += NUM_BYTES_IN_DW;
+    value = address_space_ldq_le(as, s->cq.base_addr + offset,
+                                 MEMTXATTRS_UNSPECIFIED, &res);
+    size = (value & SIZE_MASK);
+
+    offset += NUM_BYTES_IN_DW;
+    value = address_space_ldq_le(as, s->cq.base_addr + offset,
+                                 MEMTXATTRS_UNSPECIFIED, &res);
+    itt_addr = (value >> ITTADDR_OFFSET) & ITTADDR_MASK;
+
+    valid = (value >> VALID_SHIFT) & VALID_MASK;
+
+    if (valid) {
+        if ((devid > s->dt.max_devids) ||
+            (size > FIELD_EX64(s->typer, GITS_TYPER, IDBITS))) {
+            if (FIELD_EX64(s->typer, GITS_TYPER, SEIS)) {
+                /* Generate System Error here if supported */
+            }
+            qemu_log_mask(LOG_GUEST_ERROR,
+                "%s: invalid device table attributes "
+                "devid %d or size %d\n", __func__, devid, size);
+            /*
+             * in this implementation,in case of error
+             * we ignore this command and move onto the next
+             * command in the queue
+             */
+        } else {
+            if (s->dt.valid) {
+                /* add mapping entry to device table */
+                dte_entry = (valid & VALID_MASK) |
+                            ((size & SIZE_MASK) << 1U) |
+                            ((itt_addr & ITTADDR_MASK) << 6ULL);
+
+                update_dte(s, devid, dte_entry);
+            }
+        }
+    } else {
+        if (s->dt.valid) {
+            /* remove mapping entry from device table */
+            dte_entry = 0;
+            update_dte(s, devid, dte_entry);
+        }
+    }
+
+    offset += NUM_BYTES_IN_DW;
+    offset += NUM_BYTES_IN_DW;
+
+    return res;
+}
+
+/*
+ * Current implementation blocks until all
+ * commands are processed
+ */
+static MemTxResult process_cmdq(GICv3ITSState *s)
+{
+    uint32_t wr_offset = 0;
+    uint32_t rd_offset = 0;
+    uint32_t cq_offset = 0;
+    uint64_t data;
+    AddressSpace *as = &s->gicv3->sysmem_as;
+    MemTxResult res = MEMTX_OK;
+    uint8_t cmd;
+
+    wr_offset = FIELD_EX64(s->cwriter, GITS_CWRITER, OFFSET);
+
+    if (wr_offset > s->cq.max_entries) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                        "%s: invalid write offset "
+                        "%d\n", __func__, wr_offset);
+        res = MEMTX_ERROR;
+        return res;
+    }
+
+    rd_offset = FIELD_EX64(s->creadr, GITS_CREADR, OFFSET);
+
+    while (wr_offset != rd_offset) {
+        cq_offset = (rd_offset * GITS_CMDQ_ENTRY_SIZE);
+        data = address_space_ldq_le(as, s->cq.base_addr + cq_offset,
+                                      MEMTXATTRS_UNSPECIFIED, &res);
+        cmd = (data & CMD_MASK);
+
+        switch (cmd) {
+        case GITS_CMD_INT:
+            break;
+        case GITS_CMD_CLEAR:
+            break;
+        case GITS_CMD_SYNC:
+            res = process_sync(s, cq_offset);
+            break;
+        case GITS_CMD_MAPD:
+            res = process_mapd(s, data, cq_offset);
+            break;
+        case GITS_CMD_MAPC:
+            res = process_mapc(s, cq_offset);
+            break;
+        case GITS_CMD_MAPTI:
+            break;
+        case GITS_CMD_MAPI:
+            break;
+        case GITS_CMD_DISCARD:
+            break;
+        default:
+            break;
+        }
+        if (res == MEMTX_OK) {
+            rd_offset++;
+            rd_offset %= s->cq.max_entries;
+            s->creadr = FIELD_DP64(s->creadr, GITS_CREADR, OFFSET, rd_offset);
+        } else {
+            qemu_log_mask(LOG_GUEST_ERROR,
+                "%s: %x cmd processing failed!!\n", __func__, cmd);
+            break;
+        }
+    }
+    return res;
+}
+
 static bool extract_table_params(GICv3ITSState *s, int index)
 {
     uint16_t num_pages = 0;
@@ -282,6 +623,9 @@ static MemTxResult its_writel(GICv3ITSState *s, hwaddr offset,
         break;
     case GITS_CWRITER:
         s->cwriter = deposit64(s->cwriter, 0, 32, value);
+        if ((s->ctlr & ITS_CTLR_ENABLED) && (s->cwriter != s->creadr)) {
+            result = process_cmdq(s);
+        }
         break;
     case GITS_CWRITER + 4:
         s->cwriter = deposit64(s->cwriter, 32, 32, value);
@@ -416,6 +760,9 @@ static MemTxResult its_writell(GICv3ITSState *s, hwaddr offset,
         break;
     case GITS_CWRITER:
         s->cwriter = value;
+        if ((s->ctlr & ITS_CTLR_ENABLED) && (s->cwriter != s->creadr)) {
+            result = process_cmdq(s);
+        }
         break;
     case GITS_TYPER:
     case GITS_CREADR:
diff --git a/hw/intc/gicv3_internal.h b/hw/intc/gicv3_internal.h
index b7701e8ca5..7e1ff426ef 100644
--- a/hw/intc/gicv3_internal.h
+++ b/hw/intc/gicv3_internal.h
@@ -253,6 +253,12 @@ FIELD(GITS_CBASER, OUTERCACHE, 53, 3)
 FIELD(GITS_CBASER, INNERCACHE, 59, 3)
 FIELD(GITS_CBASER, VALID, 63, 1)
 
+FIELD(GITS_CREADR, STALLED, 0, 1)
+FIELD(GITS_CREADR, OFFSET, 5, 15)
+
+FIELD(GITS_CWRITER, RETRY, 0, 1)
+FIELD(GITS_CWRITER, OFFSET, 5, 15)
+
 FIELD(GITS_CTLR, ENABLED, 0, 1)
 FIELD(GITS_CTLR, QUIESCENT, 31, 1)
 
@@ -284,6 +290,41 @@ FIELD(GITS_TYPER, CIL, 36, 1)
 #define L1TABLE_ENTRY_SIZE         8
 
 #define GITS_CMDQ_ENTRY_SIZE               32
+#define NUM_BYTES_IN_DW                     8
+
+#define CMD_MASK                  0xff
+
+/* ITS Commands */
+#define GITS_CMD_CLEAR            0x04
+#define GITS_CMD_DISCARD          0x0F
+#define GITS_CMD_INT              0x03
+#define GITS_CMD_MAPC             0x09
+#define GITS_CMD_MAPD             0x08
+#define GITS_CMD_MAPI             0x0B
+#define GITS_CMD_MAPTI            0x0A
+#define GITS_CMD_SYNC             0x05
+
+/* MAPC command fields */
+#define ICID_LEN                  16
+#define ICID_MASK                 ((1U << ICID_LEN) - 1)
+#define RDBASE_LEN                32
+#define RDBASE_OFFSET             16
+#define RDBASE_MASK               ((1ULL << RDBASE_LEN) - 1)
+#define RDBASE_PROCNUM_LEN        16
+#define RDBASE_PROCNUM_MASK       ((1ULL << RDBASE_PROCNUM_LEN) - 1)
+
+#define DEVID_OFFSET              32
+#define DEVID_LEN                 32
+#define DEVID_MASK                ((1ULL << DEVID_LEN) - 1)
+
+/* MAPD command fields */
+#define ITTADDR_LEN               44
+#define ITTADDR_OFFSET            8
+#define ITTADDR_MASK              ((1ULL << ITTADDR_LEN) - 1)
+#define SIZE_MASK                 0x1f
+
+#define VALID_SHIFT               63
+#define VALID_MASK                0x1
 
 /**
  * Default features advertised by this version of ITS
-- 
2.27.0



^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [PATCH v2 4/8] hw/intc: GICv3 ITS Command processing
  2021-04-01  2:41 [PATCH v2 0/8] GICv3 LPI and ITS feature implementation Shashi Mallela
                   ` (2 preceding siblings ...)
  2021-04-01  2:41 ` [PATCH v2 3/8] hw/intc: GICv3 ITS command queue framework Shashi Mallela
@ 2021-04-01  2:41 ` Shashi Mallela
  2021-04-19 10:39   ` Peter Maydell
  2021-04-01  2:41 ` [PATCH v2 5/8] hw/intc: GICv3 ITS Feature enablement Shashi Mallela
                   ` (3 subsequent siblings)
  7 siblings, 1 reply; 25+ messages in thread
From: Shashi Mallela @ 2021-04-01  2:41 UTC (permalink / raw)
  To: peter.maydell, leif, rad; +Cc: qemu-arm, qemu-devel

Added ITS command queue handling for MAPTI,MAPI commands,handled ITS
translation which triggers an LPI via INT command as well as write
to GITS_TRANSLATER register,defined enum to differentiate between ITS
command interrupt trigger and GITS_TRANSLATER based interrupt trigger.
Each of these commands make use of other functionalities implemented to
get device table entry,collection table entry or interrupt translation
table entry required for their processing.

Signed-off-by: Shashi Mallela <shashi.mallela@linaro.org>
---
 hw/intc/arm_gicv3_its.c            | 360 ++++++++++++++++++++++++++++-
 hw/intc/gicv3_internal.h           |   7 +
 include/hw/intc/arm_gicv3_common.h |   2 +
 3 files changed, 368 insertions(+), 1 deletion(-)

diff --git a/hw/intc/arm_gicv3_its.c b/hw/intc/arm_gicv3_its.c
index fcac33c836..0e3f176809 100644
--- a/hw/intc/arm_gicv3_its.c
+++ b/hw/intc/arm_gicv3_its.c
@@ -28,6 +28,152 @@ struct GICv3ITSClass {
     void (*parent_reset)(DeviceState *dev);
 };
 
+typedef enum ItsCmdType {
+    NONE = 0, /* internal indication for GITS_TRANSLATER write */
+    CLEAR = 1,
+    DISCARD = 2,
+    INT = 3,
+} ItsCmdType;
+
+static bool get_cte(GICv3ITSState *s, uint16_t icid, uint64_t *cte)
+{
+    AddressSpace *as = &s->gicv3->sysmem_as;
+    uint8_t  page_sz_type;
+    uint64_t l2t_addr;
+    uint64_t value;
+    bool valid_l2t;
+    uint32_t l2t_id;
+    uint32_t page_sz = 0;
+    uint32_t max_l2_entries;
+    bool status = false;
+
+    if (s->ct.indirect) {
+        /* 2 level table */
+        page_sz_type = FIELD_EX64(s->baser[1], GITS_BASER, PAGESIZE);
+
+        if (page_sz_type == 0) {
+            page_sz = GITS_ITT_PAGE_SIZE_0;
+        } else if (page_sz_type == 1) {
+            page_sz = GITS_ITT_PAGE_SIZE_1;
+        } else if (page_sz_type == 2) {
+            page_sz = GITS_ITT_PAGE_SIZE_2;
+        }
+
+        l2t_id = icid / (page_sz / L1TABLE_ENTRY_SIZE);
+
+        value = address_space_ldq_le(as,
+                                     s->ct.base_addr +
+                                     (l2t_id * L1TABLE_ENTRY_SIZE),
+                                     MEMTXATTRS_UNSPECIFIED, NULL);
+
+        valid_l2t = (value >> VALID_SHIFT) & VALID_MASK;
+
+        if (valid_l2t) {
+            max_l2_entries = page_sz / s->ct.entry_sz;
+
+            l2t_addr = (value >> page_sz_type) &
+                        ((1ULL << (51 - page_sz_type)) - 1);
+
+            address_space_read(as, l2t_addr +
+                                 ((icid % max_l2_entries) * GITS_CTE_SIZE),
+                                 MEMTXATTRS_UNSPECIFIED,
+                                 cte, sizeof(*cte));
+       }
+    } else {
+        /* Flat level table */
+        address_space_read(as, s->ct.base_addr + (icid * GITS_CTE_SIZE),
+                            MEMTXATTRS_UNSPECIFIED, cte,
+                            sizeof(*cte));
+    }
+
+    if (*cte & VALID_MASK) {
+        status = true;
+    }
+
+    return status;
+}
+
+static bool get_ite(GICv3ITSState *s, uint32_t eventid, uint64_t dte,
+                      uint16_t *icid, uint32_t *pIntid)
+{
+    AddressSpace *as = &s->gicv3->sysmem_as;
+    uint8_t buff[ITS_ITT_ENTRY_SIZE];
+    uint64_t itt_addr;
+    bool status = false;
+
+    itt_addr = (dte >> 6ULL) & ITTADDR_MASK;
+    itt_addr <<= ITTADDR_OFFSET; /* 256 byte aligned */
+
+    address_space_read(as, itt_addr + (eventid * sizeof(buff)),
+                MEMTXATTRS_UNSPECIFIED, &buff,
+                sizeof(buff));
+
+    if (buff[0] & VALID_MASK) {
+        if ((buff[0] >> 1U) & GITS_TYPE_PHYSICAL) {
+            memcpy(pIntid, &buff[1], 3);
+            memcpy(icid, &buff[7], sizeof(*icid));
+            status = true;
+        }
+    }
+
+    return status;
+}
+
+static uint64_t get_dte(GICv3ITSState *s, uint32_t devid)
+{
+    AddressSpace *as = &s->gicv3->sysmem_as;
+    uint8_t  page_sz_type;
+    uint64_t l2t_addr;
+    uint64_t value;
+    bool valid_l2t;
+    uint32_t l2t_id;
+    uint32_t page_sz = 0;
+    uint32_t max_l2_entries;
+
+    if (s->dt.indirect) {
+        /* 2 level table */
+        page_sz_type = FIELD_EX64(s->baser[0], GITS_BASER, PAGESIZE);
+
+        if (page_sz_type == 0) {
+            page_sz = GITS_ITT_PAGE_SIZE_0;
+        } else if (page_sz_type == 1) {
+            page_sz = GITS_ITT_PAGE_SIZE_1;
+        } else if (page_sz_type == 2) {
+            page_sz = GITS_ITT_PAGE_SIZE_2;
+        }
+
+        l2t_id = devid / (page_sz / L1TABLE_ENTRY_SIZE);
+
+        value = address_space_ldq_le(as,
+                                     s->dt.base_addr +
+                                     (l2t_id * L1TABLE_ENTRY_SIZE),
+                                     MEMTXATTRS_UNSPECIFIED, NULL);
+
+        valid_l2t = (value >> VALID_SHIFT) & VALID_MASK;
+
+        if (valid_l2t) {
+            max_l2_entries = page_sz / s->dt.entry_sz;
+
+            l2t_addr = (value >> page_sz_type) &
+                        ((1ULL << (51 - page_sz_type)) - 1);
+
+            value = 0;
+            address_space_read(as, l2t_addr +
+                                 ((devid % max_l2_entries) * GITS_DTE_SIZE),
+                                 MEMTXATTRS_UNSPECIFIED,
+                                 &value, sizeof(value));
+        }
+    } else {
+        /* Flat level table */
+        value = 0;
+        address_space_read(as, s->dt.base_addr + (devid * GITS_DTE_SIZE),
+                            MEMTXATTRS_UNSPECIFIED, &value,
+                            sizeof(value));
+    }
+
+    return value;
+}
+
 static MemTxResult process_sync(GICv3ITSState *s, uint32_t offset)
 {
     AddressSpace *as = &s->gicv3->sysmem_as;
@@ -66,6 +212,187 @@ static MemTxResult process_sync(GICv3ITSState *s, uint32_t offset)
     return res;
 }
 
+static MemTxResult process_int(GICv3ITSState *s, uint64_t value,
+                                uint32_t offset, ItsCmdType cmd)
+{
+    AddressSpace *as = &s->gicv3->sysmem_as;
+    uint32_t devid, eventid;
+    MemTxResult res = MEMTX_OK;
+    bool dte_valid;
+    uint64_t dte = 0;
+    uint32_t max_eventid;
+    uint16_t icid = 0;
+    uint32_t pIntid = 0;
+    bool ite_valid = false;
+    uint64_t cte = 0;
+    bool cte_valid = false;
+    uint8_t buff[ITS_ITT_ENTRY_SIZE];
+    uint64_t itt_addr;
+
+    if (cmd == NONE) {
+        devid = offset;
+    } else {
+        devid = (value >> DEVID_OFFSET) & DEVID_MASK;
+
+        offset += NUM_BYTES_IN_DW;
+        value = address_space_ldq_le(as, s->cq.base_addr + offset,
+                                 MEMTXATTRS_UNSPECIFIED, &res);
+    }
+
+    eventid = (value & EVENTID_MASK);
+
+    dte = get_dte(s, devid);
+    dte_valid = dte & VALID_MASK;
+
+    if (dte_valid) {
+        max_eventid = (1UL << (((dte >> 1U) & SIZE_MASK) + 1));
+
+        ite_valid = get_ite(s, eventid, dte, &icid, &pIntid);
+
+        if (ite_valid) {
+            cte_valid = get_cte(s, icid, &cte);
+        }
+    }
+
+    if ((devid > s->dt.max_devids) || !dte_valid || !ite_valid ||
+            !cte_valid || (eventid > max_eventid)) {
+        if (FIELD_EX64(s->typer, GITS_TYPER, SEIS)) {
+            /*
+             * Generate System Error here if supported
+             * for each of the individual error cases
+             */
+        }
+        qemu_log_mask(LOG_GUEST_ERROR,
+            "%s: invalid interrupt translation table attributes "
+            "devid %d or eventid %d\n",
+            __func__, devid, eventid);
+        /*
+         * in this implementation,in case of error
+         * we ignore this command and move onto the next
+         * command in the queue
+         */
+    } else {
+        if (FIELD_EX64(s->typer, GITS_TYPER, PTA)) {
+            /*
+             * only bits[47:16] are considered instead of bits [51:16]
+             * since with a physical address the target address must be
+             * 64KB aligned
+             */
+
+            /*
+             * Current implementation only supports rdbase == procnum
+             * Hence rdbase physical address is ignored
+             */
+        } else {
+
+            if (cmd == DISCARD) {
+                /* remove mapping from interrupt translation table */
+                memset(buff, 0, sizeof(buff));
+
+                itt_addr = (dte >> 6ULL) & ITTADDR_MASK;
+                itt_addr <<= ITTADDR_OFFSET; /* 256 byte aligned */
+
+                address_space_write(as, itt_addr + (eventid * sizeof(buff)),
+                                    MEMTXATTRS_UNSPECIFIED, &buff,
+                                    sizeof(buff));
+            }
+        }
+    }
+
+    if (cmd != NONE) {
+        offset += NUM_BYTES_IN_DW;
+        offset += NUM_BYTES_IN_DW;
+    }
+
+    return res;
+}
+
+static MemTxResult process_mapti(GICv3ITSState *s, uint64_t value,
+                                    uint32_t offset, bool ignore_pInt)
+{
+    AddressSpace *as = &s->gicv3->sysmem_as;
+    uint32_t devid, eventid;
+    uint32_t pIntid = 0;
+    uint32_t max_eventid, max_Intid;
+    bool dte_valid;
+    MemTxResult res = MEMTX_OK;
+    uint16_t icid = 0;
+    uint64_t dte = 0;
+    uint64_t itt_addr;
+    uint8_t buff[ITS_ITT_ENTRY_SIZE];
+    uint32_t int_spurious = INTID_SPURIOUS;
+
+    devid = (value >> DEVID_OFFSET) & DEVID_MASK;
+    offset += NUM_BYTES_IN_DW;
+    value = address_space_ldq_le(as, s->cq.base_addr + offset,
+                                 MEMTXATTRS_UNSPECIFIED, &res);
+
+    eventid = (value & EVENTID_MASK);
+
+    if (!ignore_pInt) {
+        pIntid = (value >> pINTID_OFFSET) & pINTID_MASK;
+    }
+
+    offset += NUM_BYTES_IN_DW;
+    value = address_space_ldq_le(as, s->cq.base_addr + offset,
+                                 MEMTXATTRS_UNSPECIFIED, &res);
+
+    icid = value & ICID_MASK;
+
+    dte = get_dte(s, devid);
+    dte_valid = dte & VALID_MASK;
+
+    max_eventid = (1UL << (((dte >> 1U) & SIZE_MASK) + 1));
+
+    if (!ignore_pInt) {
+        max_Intid = (1UL << (FIELD_EX64(s->typer, GITS_TYPER, IDBITS) + 1));
+    }
+
+    if ((devid > s->dt.max_devids) || (icid > s->ct.max_collids) ||
+            !dte_valid || (eventid > max_eventid) ||
+            (!ignore_pInt && ((pIntid < GICV3_LPI_INTID_START) ||
+               (pIntid > max_Intid)))) {
+        if (FIELD_EX64(s->typer, GITS_TYPER, SEIS)) {
+            /*
+             * Generate System Error here if supported
+             * for each of the individual error cases
+             */
+        }
+        qemu_log_mask(LOG_GUEST_ERROR,
+            "%s: invalid interrupt translation table attributes "
+            "devid %d or icid %d or eventid %d or pIntid %d\n",
+            __func__, devid, icid, eventid, pIntid);
+        /*
+         * in this implementation,in case of error
+         * we ignore this command and move onto the next
+         * command in the queue
+         */
+    } else {
+        /* add entry to interrupt translation table */
+        memset(buff, 0, sizeof(buff));
+        buff[0] = (dte_valid & VALID_MASK) | (GITS_TYPE_PHYSICAL << 1U);
+        if (ignore_pInt) {
+            memcpy(&buff[1], &eventid, 3);
+        } else {
+            memcpy(&buff[1], &pIntid, 3);
+        }
+        memcpy(&buff[4], &int_spurious, 3);
+        memcpy(&buff[7], &icid, sizeof(icid));
+
+        itt_addr = (dte >> 6ULL) & ITTADDR_MASK;
+        itt_addr <<= ITTADDR_OFFSET; /* 256 byte aligned */
+
+        address_space_write(as, itt_addr + (eventid * sizeof(buff)),
+                    MEMTXATTRS_UNSPECIFIED, &buff,
+                    sizeof(buff));
+    }
+
+    offset += NUM_BYTES_IN_DW;
+    offset += NUM_BYTES_IN_DW;
+
+    return res;
+}
+
 static void update_cte(GICv3ITSState *s, uint16_t icid, uint64_t cte)
 {
     AddressSpace *as = &s->gicv3->sysmem_as;
@@ -239,7 +566,7 @@ static void update_dte(GICv3ITSState *s, uint32_t devid, uint64_t dte)
 }
 
 static MemTxResult process_mapd(GICv3ITSState *s, uint64_t value,
-                                 uint32_t offset)
+                                  uint32_t offset)
 {
     AddressSpace *as = &s->gicv3->sysmem_as;
     uint32_t devid;
@@ -335,8 +662,10 @@ static MemTxResult process_cmdq(GICv3ITSState *s)
 
         switch (cmd) {
         case GITS_CMD_INT:
+            res = process_int(s, data, cq_offset, INT);
             break;
         case GITS_CMD_CLEAR:
+            res = process_int(s, data, cq_offset, CLEAR);
             break;
         case GITS_CMD_SYNC:
             res = process_sync(s, cq_offset);
@@ -348,10 +677,13 @@ static MemTxResult process_cmdq(GICv3ITSState *s)
             res = process_mapc(s, cq_offset);
             break;
         case GITS_CMD_MAPTI:
+            res = process_mapti(s, data, cq_offset, false);
             break;
         case GITS_CMD_MAPI:
+            res = process_mapti(s, data, cq_offset, true);
             break;
         case GITS_CMD_DISCARD:
+            res = process_int(s, data, cq_offset, DISCARD);
             break;
         default:
             break;
@@ -498,7 +830,20 @@ static MemTxResult its_trans_writew(GICv3ITSState *s, hwaddr offset,
                                uint64_t value, MemTxAttrs attrs)
 {
     MemTxResult result = MEMTX_OK;
+    uint32_t devid = 0;
 
+    switch (offset) {
+    case GITS_TRANSLATER:
+        if (s->ctlr & ITS_CTLR_ENABLED) {
+            s->translater = (value & 0x0000FFFFU);
+            devid = attrs.requester_id;
+            result = process_int(s, s->translater, devid, NONE);
+        }
+        break;
+    default:
+        result = MEMTX_ERROR;
+        break;
+    }
     return result;
 }
 
@@ -506,7 +851,20 @@ static MemTxResult its_trans_writel(GICv3ITSState *s, hwaddr offset,
                                uint64_t value, MemTxAttrs attrs)
 {
     MemTxResult result = MEMTX_OK;
+    uint32_t devid = 0;
 
+    switch (offset) {
+    case GITS_TRANSLATER:
+        if (s->ctlr & ITS_CTLR_ENABLED) {
+            s->translater = value;
+            devid = attrs.requester_id;
+            result = process_int(s, s->translater, devid, NONE);
+        }
+        break;
+    default:
+        result = MEMTX_ERROR;
+        break;
+    }
     return result;
 }
 
diff --git a/hw/intc/gicv3_internal.h b/hw/intc/gicv3_internal.h
index 7e1ff426ef..e9f9aa6722 100644
--- a/hw/intc/gicv3_internal.h
+++ b/hw/intc/gicv3_internal.h
@@ -323,6 +323,13 @@ FIELD(GITS_TYPER, CIL, 36, 1)
 #define ITTADDR_MASK              ((1ULL << ITTADDR_LEN) - 1)
 #define SIZE_MASK                 0x1f
 
+/* MAPI command fields */
+#define EVENTID_MASK              ((1ULL << 32) - 1)
+
+/* MAPTI command fields */
+#define pINTID_OFFSET              32
+#define pINTID_MASK               ((1ULL << 32) - 1)
+
 #define VALID_SHIFT               63
 #define VALID_MASK                0x1
 
diff --git a/include/hw/intc/arm_gicv3_common.h b/include/hw/intc/arm_gicv3_common.h
index b0f2414fa3..3a710592a9 100644
--- a/include/hw/intc/arm_gicv3_common.h
+++ b/include/hw/intc/arm_gicv3_common.h
@@ -36,6 +36,8 @@
 #define GICV3_MAXIRQ 1020
 #define GICV3_MAXSPI (GICV3_MAXIRQ - GIC_INTERNAL)
 
+#define GICV3_LPI_INTID_START 8192
+
 #define GICV3_REDIST_SIZE 0x20000
 
 /* Number of SGI target-list bits */
-- 
2.27.0



^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [PATCH v2 5/8] hw/intc: GICv3 ITS Feature enablement
  2021-04-01  2:41 [PATCH v2 0/8] GICv3 LPI and ITS feature implementation Shashi Mallela
                   ` (3 preceding siblings ...)
  2021-04-01  2:41 ` [PATCH v2 4/8] hw/intc: GICv3 ITS Command processing Shashi Mallela
@ 2021-04-01  2:41 ` Shashi Mallela
  2021-04-19 10:51   ` Peter Maydell
  2021-04-01  2:41 ` [PATCH v2 6/8] hw/intc: GICv3 redistributor ITS processing Shashi Mallela
                   ` (2 subsequent siblings)
  7 siblings, 1 reply; 25+ messages in thread
From: Shashi Mallela @ 2021-04-01  2:41 UTC (permalink / raw)
  To: peter.maydell, leif, rad; +Cc: qemu-arm, qemu-devel

Added properties to enable ITS feature and define qemu system
address space memory in gicv3 common,setup distributor and
redistributor registers to indicate LPI support.

Signed-off-by: Shashi Mallela <shashi.mallela@linaro.org>
---
 hw/intc/arm_gicv3_common.c         | 16 ++++++++++++++++
 hw/intc/arm_gicv3_dist.c           | 22 ++++++++++++++++++++--
 hw/intc/arm_gicv3_redist.c         | 28 +++++++++++++++++++++++++---
 hw/intc/gicv3_internal.h           | 17 +++++++++++++++++
 include/hw/intc/arm_gicv3_common.h |  8 ++++++++
 5 files changed, 86 insertions(+), 5 deletions(-)

diff --git a/hw/intc/arm_gicv3_common.c b/hw/intc/arm_gicv3_common.c
index 58ef65f589..3bfc52f7fa 100644
--- a/hw/intc/arm_gicv3_common.c
+++ b/hw/intc/arm_gicv3_common.c
@@ -156,6 +156,7 @@ static const VMStateDescription vmstate_gicv3_cpu = {
         VMSTATE_UINT32(gicr_waker, GICv3CPUState),
         VMSTATE_UINT64(gicr_propbaser, GICv3CPUState),
         VMSTATE_UINT64(gicr_pendbaser, GICv3CPUState),
+        VMSTATE_BOOL(lpi_outofrange, GICv3CPUState),
         VMSTATE_UINT32(gicr_igroupr0, GICv3CPUState),
         VMSTATE_UINT32(gicr_ienabler0, GICv3CPUState),
         VMSTATE_UINT32(gicr_ipendr0, GICv3CPUState),
@@ -227,6 +228,7 @@ static const VMStateDescription vmstate_gicv3 = {
     .priority = MIG_PRI_GICV3,
     .fields = (VMStateField[]) {
         VMSTATE_UINT32(gicd_ctlr, GICv3State),
+        VMSTATE_UINT32(gicd_typer, GICv3State),
         VMSTATE_UINT32_ARRAY(gicd_statusr, GICv3State, 2),
         VMSTATE_UINT32_ARRAY(group, GICv3State, GICV3_BMP_SIZE),
         VMSTATE_UINT32_ARRAY(grpmod, GICv3State, GICV3_BMP_SIZE),
@@ -381,6 +383,16 @@ static void arm_gicv3_common_realize(DeviceState *dev, Error **errp)
             (1 << 24) |
             (i << 8) |
             (last << 4);
+
+        if (s->lpi_enable) {
+            s->cpu[i].gicr_typer |= GICR_TYPER_PLPIS;
+
+            if (!s->sysmem) {
+                error_setg(errp,
+                    "Redist-ITS: Guest 'sysmem' reference link not set");
+                return;
+            }
+        }
     }
 }
 
@@ -406,6 +418,7 @@ static void arm_gicv3_common_reset(DeviceState *dev)
         cs->gicr_waker = GICR_WAKER_ProcessorSleep | GICR_WAKER_ChildrenAsleep;
         cs->gicr_propbaser = 0;
         cs->gicr_pendbaser = 0;
+        cs->lpi_outofrange = false;
         /* If we're resetting a TZ-aware GIC as if secure firmware
          * had set it up ready to start a kernel in non-secure, we
          * need to set interrupts to group 1 so the kernel can use them.
@@ -494,9 +507,12 @@ static Property arm_gicv3_common_properties[] = {
     DEFINE_PROP_UINT32("num-cpu", GICv3State, num_cpu, 1),
     DEFINE_PROP_UINT32("num-irq", GICv3State, num_irq, 32),
     DEFINE_PROP_UINT32("revision", GICv3State, revision, 3),
+    DEFINE_PROP_BOOL("has-lpi", GICv3State, lpi_enable, 0),
     DEFINE_PROP_BOOL("has-security-extensions", GICv3State, security_extn, 0),
     DEFINE_PROP_ARRAY("redist-region-count", GICv3State, nb_redist_regions,
                       redist_region_count, qdev_prop_uint32, uint32_t),
+    DEFINE_PROP_LINK("sysmem", GICv3State, sysmem, TYPE_MEMORY_REGION,
+                     MemoryRegion *),
     DEFINE_PROP_END_OF_LIST(),
 };
 
diff --git a/hw/intc/arm_gicv3_dist.c b/hw/intc/arm_gicv3_dist.c
index b65f56f903..96a317a8ef 100644
--- a/hw/intc/arm_gicv3_dist.c
+++ b/hw/intc/arm_gicv3_dist.c
@@ -366,12 +366,15 @@ static MemTxResult gicd_readl(GICv3State *s, hwaddr offset,
         return MEMTX_OK;
     case GICD_TYPER:
     {
+        bool lpi_supported = false;
         /* For this implementation:
          * No1N == 1 (1-of-N SPI interrupts not supported)
          * A3V == 1 (non-zero values of Affinity level 3 supported)
          * IDbits == 0xf (we support 16-bit interrupt identifiers)
          * DVIS == 0 (Direct virtual LPI injection not supported)
-         * LPIS == 0 (LPIs not supported)
+         * LPIS == 1 (LPIs are supported if affinity routing is enabled)
+         * num_LPIs == 0b00000 (bits [15:11],Number of LPIs as indicated
+         *                      by GICD_TYPER.IDbits)
          * MBIS == 0 (message-based SPIs not supported)
          * SecurityExtn == 1 if security extns supported
          * CPUNumber == 0 since for us ARE is always 1
@@ -385,8 +388,23 @@ static MemTxResult gicd_readl(GICv3State *s, hwaddr offset,
          */
         bool sec_extn = !(s->gicd_ctlr & GICD_CTLR_DS);
 
+        /*
+         * With securityextn on,LPIs are supported when affinity routing
+         * is enabled for non-secure state and if off LPIs are supported
+         * when affinity routing is enabled.
+         */
+        if (s->lpi_enable) {
+            if (sec_extn) {
+                lpi_supported = (s->gicd_ctlr & GICD_CTLR_ARE_NS);
+            } else {
+                lpi_supported = (s->gicd_ctlr & GICD_CTLR_ARE);
+            }
+        }
+
         *data = (1 << 25) | (1 << 24) | (sec_extn << 10) |
-            (0xf << 19) | itlinesnumber;
+            (lpi_supported << GICD_TYPER_LPIS_OFFSET) | (GICD_TYPER_IDBITS <<
+            GICD_TYPER_IDBITS_OFFSET) | itlinesnumber;
+        s->gicd_typer = *data;
         return MEMTX_OK;
     }
     case GICD_IIDR:
diff --git a/hw/intc/arm_gicv3_redist.c b/hw/intc/arm_gicv3_redist.c
index 8645220d61..325b974e70 100644
--- a/hw/intc/arm_gicv3_redist.c
+++ b/hw/intc/arm_gicv3_redist.c
@@ -248,10 +248,16 @@ static MemTxResult gicr_writel(GICv3CPUState *cs, hwaddr offset,
     case GICR_CTLR:
         /* For our implementation, GICR_TYPER.DPGS is 0 and so all
          * the DPG bits are RAZ/WI. We don't do anything asynchronously,
-         * so UWP and RWP are RAZ/WI. And GICR_TYPER.LPIS is 0 (we don't
-         * implement LPIs) so Enable_LPIs is RES0. So there are no writable
-         * bits for us.
+         * so UWP and RWP are RAZ/WI. GICR_TYPER.LPIS is 1 (we
+         * implement LPIs) so Enable_LPIs is programmable.
          */
+        if (cs->gicr_typer & GICR_TYPER_PLPIS) {
+            if (value & GICR_CTLR_ENABLE_LPIS) {
+                cs->gicr_ctlr |= GICR_CTLR_ENABLE_LPIS;
+            } else {
+                cs->gicr_ctlr &= ~GICR_CTLR_ENABLE_LPIS;
+            }
+        }
         return MEMTX_OK;
     case GICR_STATUSR:
         /* RAZ/WI for our implementation */
@@ -275,6 +281,14 @@ static MemTxResult gicr_writel(GICv3CPUState *cs, hwaddr offset,
         cs->gicr_waker = value;
         return MEMTX_OK;
     case GICR_PROPBASER:
+        if (FIELD_EX64(value, GICR_PROPBASER, IDBITS) <
+            GICR_PROPBASER_IDBITS_THRESHOLD) {
+            cs->lpi_outofrange = true;
+        }
+        if (FIELD_EX64(value, GICR_PROPBASER, IDBITS) > GICD_TYPER_IDBITS) {
+            value &= ~R_GICR_PROPBASER_IDBITS_MASK;
+            value |= GICD_TYPER_IDBITS;
+        }
         cs->gicr_propbaser = deposit64(cs->gicr_propbaser, 0, 32, value);
         return MEMTX_OK;
     case GICR_PROPBASER + 4:
@@ -397,6 +411,14 @@ static MemTxResult gicr_writell(GICv3CPUState *cs, hwaddr offset,
 {
     switch (offset) {
     case GICR_PROPBASER:
+        if (FIELD_EX64(value, GICR_PROPBASER, IDBITS) <
+            GICR_PROPBASER_IDBITS_THRESHOLD) {
+            cs->lpi_outofrange = true;
+        }
+        if (FIELD_EX64(value, GICR_PROPBASER, IDBITS) > GICD_TYPER_IDBITS) {
+            value &= ~R_GICR_PROPBASER_IDBITS_MASK;
+            value |= GICD_TYPER_IDBITS;
+        }
         cs->gicr_propbaser = value;
         return MEMTX_OK;
     case GICR_PENDBASER:
diff --git a/hw/intc/gicv3_internal.h b/hw/intc/gicv3_internal.h
index e9f9aa6722..a2718704d4 100644
--- a/hw/intc/gicv3_internal.h
+++ b/hw/intc/gicv3_internal.h
@@ -68,6 +68,9 @@
 #define GICD_CTLR_E1NWF             (1U << 7)
 #define GICD_CTLR_RWP               (1U << 31)
 
+#define GICD_TYPER_LPIS_OFFSET         17
+#define GICD_TYPER_IDBITS_OFFSET       19
+#define GICD_TYPER_IDBITS_MASK       0x1f
 /* 16 bits EventId */
 #define GICD_TYPER_IDBITS            0xf
 
@@ -126,6 +129,20 @@
 #define GICR_WAKER_ProcessorSleep    (1U << 1)
 #define GICR_WAKER_ChildrenAsleep    (1U << 2)
 
+FIELD(GICR_PROPBASER, IDBITS, 0, 5)
+FIELD(GICR_PROPBASER, INNERCACHE, 7, 3)
+FIELD(GICR_PROPBASER, SHAREABILITY, 10, 2)
+FIELD(GICR_PROPBASER, PHYADDR, 12, 40)
+FIELD(GICR_PROPBASER, OUTERCACHE, 56, 3)
+
+#define GICR_PROPBASER_IDBITS_THRESHOLD          0xd
+
+FIELD(GICR_PENDBASER, INNERCACHE, 7, 3)
+FIELD(GICR_PENDBASER, SHAREABILITY, 10, 2)
+FIELD(GICR_PENDBASER, PHYADDR, 16, 36)
+FIELD(GICR_PENDBASER, OUTERCACHE, 56, 3)
+FIELD(GICR_PENDBASER, PTZ, 62, 1)
+
 #define ICC_CTLR_EL1_CBPR           (1U << 0)
 #define ICC_CTLR_EL1_EOIMODE        (1U << 1)
 #define ICC_CTLR_EL1_PMHE           (1U << 6)
diff --git a/include/hw/intc/arm_gicv3_common.h b/include/hw/intc/arm_gicv3_common.h
index 3a710592a9..db3989484d 100644
--- a/include/hw/intc/arm_gicv3_common.h
+++ b/include/hw/intc/arm_gicv3_common.h
@@ -175,6 +175,13 @@ struct GICv3CPUState {
     uint32_t gicr_nsacr;
     uint8_t gicr_ipriorityr[GIC_INTERNAL];
 
+    /*
+     * flag to indicate LPIs are out of range
+     * since IDbits from GICR_PROPBASER is less
+     * than 0b1101
+     */
+    bool lpi_outofrange;
+
     /* CPU interface */
     uint64_t icc_sre_el1;
     uint64_t icc_ctlr_el1[2];
@@ -221,6 +228,7 @@ struct GICv3State {
     uint32_t num_cpu;
     uint32_t num_irq;
     uint32_t revision;
+    bool lpi_enable;
     bool security_extn;
     bool irq_reset_nonsecure;
     bool gicd_no_migration_shift_bug;
-- 
2.27.0



^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [PATCH v2 6/8] hw/intc: GICv3 redistributor ITS processing
  2021-04-01  2:41 [PATCH v2 0/8] GICv3 LPI and ITS feature implementation Shashi Mallela
                   ` (4 preceding siblings ...)
  2021-04-01  2:41 ` [PATCH v2 5/8] hw/intc: GICv3 ITS Feature enablement Shashi Mallela
@ 2021-04-01  2:41 ` Shashi Mallela
  2021-04-19 12:44   ` Peter Maydell
  2021-04-01  2:41 ` [PATCH v2 7/8] hw/arm/sbsa-ref: add ITS support in SBSA GIC Shashi Mallela
  2021-04-01  2:41 ` [PATCH v2 8/8] hw/arm/virt: add ITS support in virt GIC Shashi Mallela
  7 siblings, 1 reply; 25+ messages in thread
From: Shashi Mallela @ 2021-04-01  2:41 UTC (permalink / raw)
  To: peter.maydell, leif, rad; +Cc: qemu-arm, qemu-devel

Implemented lpi processing at redistributor to get lpi config info
from lpi configuration table,determine priority,set pending state in
lpi pending table and forward the lpi to cpuif.Added logic to invoke
redistributor lpi processing with translated LPI which set/clear LPI
from ITS device as part of ITS INT,CLEAR,DISCARD command and
GITS_TRANSLATER processing.

Signed-off-by: Shashi Mallela <shashi.mallela@linaro.org>
---
 hw/intc/arm_gicv3.c        |   6 ++
 hw/intc/arm_gicv3_cpuif.c  |  15 +++--
 hw/intc/arm_gicv3_its.c    |   9 ++-
 hw/intc/arm_gicv3_redist.c | 124 +++++++++++++++++++++++++++++++++++++
 hw/intc/gicv3_internal.h   |   9 +++
 5 files changed, 158 insertions(+), 5 deletions(-)

diff --git a/hw/intc/arm_gicv3.c b/hw/intc/arm_gicv3.c
index 66eaa97198..618fa1af95 100644
--- a/hw/intc/arm_gicv3.c
+++ b/hw/intc/arm_gicv3.c
@@ -166,6 +166,12 @@ static void gicv3_redist_update_noirqset(GICv3CPUState *cs)
         cs->hppi.grp = gicv3_irq_group(cs->gic, cs, cs->hppi.irq);
     }
 
+    if (cs->gic->lpi_enable) {
+        if (gicv3_redist_update_lpi(cs)) {
+            seenbetter = true;
+        }
+    }
+
     /* If the best interrupt we just found would preempt whatever
      * was the previous best interrupt before this update, then
      * we know it's definitely the best one now.
diff --git a/hw/intc/arm_gicv3_cpuif.c b/hw/intc/arm_gicv3_cpuif.c
index 43ef1d7a84..c225b80f66 100644
--- a/hw/intc/arm_gicv3_cpuif.c
+++ b/hw/intc/arm_gicv3_cpuif.c
@@ -899,9 +899,14 @@ static void icc_activate_irq(GICv3CPUState *cs, int irq)
         cs->gicr_ipendr0 = deposit32(cs->gicr_ipendr0, irq, 1, 0);
         gicv3_redist_update(cs);
     } else {
-        gicv3_gicd_active_set(cs->gic, irq);
-        gicv3_gicd_pending_clear(cs->gic, irq);
-        gicv3_update(cs->gic, irq, 1);
+        if (irq >= GICV3_LPI_INTID_START) {
+            gicv3_redist_lpi_pending(cs, irq, 0);
+            gicv3_redist_update(cs);
+        } else {
+            gicv3_gicd_active_set(cs->gic, irq);
+            gicv3_gicd_pending_clear(cs->gic, irq);
+            gicv3_update(cs->gic, irq, 1);
+        }
     }
 }
 
@@ -1337,7 +1342,9 @@ static void icc_eoir_write(CPUARMState *env, const ARMCPRegInfo *ri,
          * valid interrupt value read from the Interrupt Acknowledge
          * register" and so this is UNPREDICTABLE. We choose to ignore it.
          */
-        return;
+        if (!(cs->gic->lpi_enable && (irq >= GICV3_LPI_INTID_START))) {
+            return;
+        }
     }
 
     if (icc_highest_active_group(cs) != grp) {
diff --git a/hw/intc/arm_gicv3_its.c b/hw/intc/arm_gicv3_its.c
index 0e3f176809..41e1e8b2a8 100644
--- a/hw/intc/arm_gicv3_its.c
+++ b/hw/intc/arm_gicv3_its.c
@@ -226,6 +226,7 @@ static MemTxResult process_int(GICv3ITSState *s, uint64_t value,
     bool ite_valid = false;
     uint64_t cte = 0;
     bool cte_valid = false;
+    uint64_t rdbase;
     uint8_t buff[ITS_ITT_ENTRY_SIZE];
     uint64_t itt_addr;
 
@@ -278,12 +279,18 @@ static MemTxResult process_int(GICv3ITSState *s, uint64_t value,
              * since with a physical address the target address must be
              * 64KB aligned
              */
-
+            rdbase = (cte >> 1U) & RDBASE_MASK;
             /*
              * Current implementation only supports rdbase == procnum
              * Hence rdbase physical address is ignored
              */
         } else {
+            rdbase = (cte >> 1U) & RDBASE_PROCNUM_MASK;
+            if ((cmd == CLEAR) || (cmd == DISCARD)) {
+                gicv3_redist_process_lpi(&s->gicv3->cpu[rdbase], pIntid, 0);
+            } else {
+                gicv3_redist_process_lpi(&s->gicv3->cpu[rdbase], pIntid, 1);
+            }
 
             if (cmd == DISCARD) {
                 /* remove mapping from interrupt translation table */
diff --git a/hw/intc/arm_gicv3_redist.c b/hw/intc/arm_gicv3_redist.c
index 325b974e70..71c648a616 100644
--- a/hw/intc/arm_gicv3_redist.c
+++ b/hw/intc/arm_gicv3_redist.c
@@ -254,6 +254,8 @@ static MemTxResult gicr_writel(GICv3CPUState *cs, hwaddr offset,
         if (cs->gicr_typer & GICR_TYPER_PLPIS) {
             if (value & GICR_CTLR_ENABLE_LPIS) {
                 cs->gicr_ctlr |= GICR_CTLR_ENABLE_LPIS;
+                /* Check for any pending interr in pending table */
+                gicv3_redist_update(cs);
             } else {
                 cs->gicr_ctlr &= ~GICR_CTLR_ENABLE_LPIS;
             }
@@ -548,6 +550,128 @@ MemTxResult gicv3_redist_write(void *opaque, hwaddr offset, uint64_t data,
     return r;
 }
 
+bool gicv3_redist_update_lpi(GICv3CPUState *cs)
+{
+    AddressSpace *as = &cs->gic->sysmem_as;
+    uint64_t lpict_baddr, lpipt_baddr;
+    uint32_t pendt_size = 0;
+    uint8_t lpite;
+    uint8_t prio, pend;
+    int i;
+    bool seenbetter = false;
+
+    if ((!cs->gicr_ctlr & GICR_CTLR_ENABLE_LPIS) || !cs->gicr_propbaser ||
+        !cs->gicr_pendbaser || cs->lpi_outofrange) {
+        return seenbetter;
+    }
+
+    lpict_baddr = FIELD_EX64(cs->gicr_propbaser, GICR_PROPBASER, PHYADDR);
+    lpict_baddr <<= R_GICR_PROPBASER_PHYADDR_SHIFT;
+
+    lpipt_baddr =  FIELD_EX64(cs->gicr_pendbaser, GICR_PENDBASER, PHYADDR);
+    lpipt_baddr <<= R_GICR_PENDBASER_PHYADDR_SHIFT;
+
+    /* Determine the highest priority pending interrupt among LPIs */
+    pendt_size = (1UL << (FIELD_EX64(cs->gicr_propbaser, GICR_PROPBASER,
+                          IDBITS) - 1));
+
+    for (i = 0; i < pendt_size; i++) {
+        address_space_read(as, lpipt_baddr +
+                (((GICV3_LPI_INTID_START + i) / 8) * sizeof(pend)),
+                MEMTXATTRS_UNSPECIFIED, &pend, sizeof(pend));
+
+        if ((1 << ((GICV3_LPI_INTID_START + i) % 8)) & pend) {
+            address_space_read(as, lpict_baddr + (i * sizeof(lpite)),
+                      MEMTXATTRS_UNSPECIFIED, &lpite, sizeof(lpite));
+
+            prio = ((lpite >> LPI_CTE_PRIORITY_OFFSET) &
+                     LPI_CTE_PRIORITY_MASK);
+            prio &= LPI_PRIORITY_MASK;
+
+            if (prio < cs->hppi.prio) {
+                cs->hppi.irq = GICV3_LPI_INTID_START + i;
+                cs->hppi.prio = prio;
+                /* LPIs are always non-secure Grp1 interrupts */
+                cs->hppi.grp = GICV3_G1NS;
+                seenbetter = true;
+            }
+        }
+    }
+    return seenbetter;
+}
+
+void gicv3_redist_lpi_pending(GICv3CPUState *cs, int irq, int level)
+{
+    AddressSpace *as = &cs->gic->sysmem_as;
+    uint64_t lpipt_baddr;
+    bool ispend = false;
+    uint8_t pend;
+
+    /*
+     * get the bit value corresponding to this irq in the
+     * lpi pending table
+     */
+    lpipt_baddr = FIELD_EX64(cs->gicr_pendbaser, GICR_PENDBASER, PHYADDR);
+    lpipt_baddr <<= R_GICR_PENDBASER_PHYADDR_SHIFT;
+
+    address_space_read(as, lpipt_baddr + ((irq / 8) * sizeof(pend)),
+                         MEMTXATTRS_UNSPECIFIED, &pend, sizeof(pend));
+    ispend = ((pend >> (irq % 8)) & 0x1);
+
+    if (ispend) {
+        if (!level) {
+            /*
+             * clear the pending bit and update the lpi pending table
+             */
+            pend &= ~(1 << (irq % 8));
+
+            address_space_write(as, lpipt_baddr + ((irq / 8) * sizeof(pend)),
+                                 MEMTXATTRS_UNSPECIFIED, &pend, sizeof(pend));
+        }
+    } else {
+        if (level) {
+            /*
+             * if pending bit is not already set for this irq,turn-on the
+             * pending bit and update the lpi pending table
+             */
+            pend |= (1 << (irq % 8));
+
+            address_space_write(as, lpipt_baddr + ((irq / 8) * sizeof(pend)),
+                                 MEMTXATTRS_UNSPECIFIED, &pend, sizeof(pend));
+        }
+    }
+}
+
+void gicv3_redist_process_lpi(GICv3CPUState *cs, int irq, int level)
+{
+    AddressSpace *as = &cs->gic->sysmem_as;
+    uint64_t lpict_baddr;
+    uint8_t lpite;
+
+    if ((!cs->gicr_ctlr & GICR_CTLR_ENABLE_LPIS) || !cs->gicr_propbaser ||
+         !cs->gicr_pendbaser || cs->lpi_outofrange) {
+        return;
+    }
+
+    lpict_baddr = FIELD_EX64(cs->gicr_propbaser, GICR_PROPBASER, PHYADDR);
+    lpict_baddr <<= R_GICR_PROPBASER_PHYADDR_SHIFT;
+
+    /* get the lpi config table entry corresponding to this irq */
+    address_space_read(as, lpict_baddr + ((irq - GICV3_LPI_INTID_START) *
+                        sizeof(lpite)), MEMTXATTRS_UNSPECIFIED,
+                        &lpite, sizeof(lpite));
+
+    /* check if this irq is enabled before proceeding further */
+    if (!(lpite & LPI_CTE_ENABLED)) {
+        return;
+    }
+
+    /* set/clear the pending bit for this irq */
+    gicv3_redist_lpi_pending(cs, irq, level);
+
+    gicv3_redist_update(cs);
+}
+
 void gicv3_redist_set_irq(GICv3CPUState *cs, int irq, int level)
 {
     /* Update redistributor state for a change in an external PPI input line */
diff --git a/hw/intc/gicv3_internal.h b/hw/intc/gicv3_internal.h
index a2718704d4..4c97c22850 100644
--- a/hw/intc/gicv3_internal.h
+++ b/hw/intc/gicv3_internal.h
@@ -306,6 +306,12 @@ FIELD(GITS_TYPER, CIL, 36, 1)
 
 #define L1TABLE_ENTRY_SIZE         8
 
+#define LPI_CTE_ENABLE_OFFSET      0
+#define LPI_CTE_ENABLED          VALID_MASK
+#define LPI_CTE_PRIORITY_OFFSET    2
+#define LPI_CTE_PRIORITY_MASK     ((1U << 6) - 1)
+#define LPI_PRIORITY_MASK         0xfc
+
 #define GITS_CMDQ_ENTRY_SIZE               32
 #define NUM_BYTES_IN_DW                     8
 
@@ -444,6 +450,9 @@ MemTxResult gicv3_redist_write(void *opaque, hwaddr offset, uint64_t data,
                                unsigned size, MemTxAttrs attrs);
 void gicv3_dist_set_irq(GICv3State *s, int irq, int level);
 void gicv3_redist_set_irq(GICv3CPUState *cs, int irq, int level);
+void gicv3_redist_process_lpi(GICv3CPUState *cs, int irq, int level);
+void gicv3_redist_lpi_pending(GICv3CPUState *cs, int irq, int level);
+bool gicv3_redist_update_lpi(GICv3CPUState *cs);
 void gicv3_redist_send_sgi(GICv3CPUState *cs, int grp, int irq, bool ns);
 void gicv3_init_cpuif(GICv3State *s);
 
-- 
2.27.0



^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [PATCH v2 7/8] hw/arm/sbsa-ref: add ITS support in SBSA GIC
  2021-04-01  2:41 [PATCH v2 0/8] GICv3 LPI and ITS feature implementation Shashi Mallela
                   ` (5 preceding siblings ...)
  2021-04-01  2:41 ` [PATCH v2 6/8] hw/intc: GICv3 redistributor ITS processing Shashi Mallela
@ 2021-04-01  2:41 ` Shashi Mallela
  2021-04-19 12:44   ` Peter Maydell
  2021-04-01  2:41 ` [PATCH v2 8/8] hw/arm/virt: add ITS support in virt GIC Shashi Mallela
  7 siblings, 1 reply; 25+ messages in thread
From: Shashi Mallela @ 2021-04-01  2:41 UTC (permalink / raw)
  To: peter.maydell, leif, rad; +Cc: qemu-arm, qemu-devel

Included creation of ITS as part of SBSA platform GIC
initialization.

Signed-off-by: Shashi Mallela <shashi.mallela@linaro.org>
---
 hw/arm/sbsa-ref.c | 26 +++++++++++++++++++++++---
 1 file changed, 23 insertions(+), 3 deletions(-)

diff --git a/hw/arm/sbsa-ref.c b/hw/arm/sbsa-ref.c
index 88dfb2284c..d05cbcae48 100644
--- a/hw/arm/sbsa-ref.c
+++ b/hw/arm/sbsa-ref.c
@@ -35,7 +35,7 @@
 #include "hw/boards.h"
 #include "hw/ide/internal.h"
 #include "hw/ide/ahci_internal.h"
-#include "hw/intc/arm_gicv3_common.h"
+#include "hw/intc/arm_gicv3_its_common.h"
 #include "hw/loader.h"
 #include "hw/pci-host/gpex.h"
 #include "hw/qdev-properties.h"
@@ -65,6 +65,7 @@ enum {
     SBSA_CPUPERIPHS,
     SBSA_GIC_DIST,
     SBSA_GIC_REDIST,
+    SBSA_GIC_ITS,
     SBSA_SECURE_EC,
     SBSA_GWDT,
     SBSA_GWDT_REFRESH,
@@ -108,6 +109,7 @@ static const MemMapEntry sbsa_ref_memmap[] = {
     [SBSA_CPUPERIPHS] =         { 0x40000000, 0x00040000 },
     [SBSA_GIC_DIST] =           { 0x40060000, 0x00010000 },
     [SBSA_GIC_REDIST] =         { 0x40080000, 0x04000000 },
+    [SBSA_GIC_ITS] =            { 0x44090000, 0x00020000 },
     [SBSA_SECURE_EC] =          { 0x50000000, 0x00001000 },
     [SBSA_GWDT_REFRESH] =       { 0x50010000, 0x00001000 },
     [SBSA_GWDT_CONTROL] =       { 0x50011000, 0x00001000 },
@@ -378,7 +380,20 @@ static void create_secure_ram(SBSAMachineState *sms,
     memory_region_add_subregion(secure_sysmem, base, secram);
 }
 
-static void create_gic(SBSAMachineState *sms)
+static void create_its(SBSAMachineState *sms)
+{
+    DeviceState *dev;
+
+    dev = qdev_new(TYPE_ARM_GICV3_ITS);
+    SysBusDevice *s = SYS_BUS_DEVICE(dev);
+
+    object_property_set_link(OBJECT(dev), "parent-gicv3", OBJECT(sms->gic),
+                             &error_abort);
+    sysbus_realize_and_unref(s, &error_fatal);
+    sysbus_mmio_map(s, 0, sbsa_ref_memmap[SBSA_GIC_ITS].base);
+}
+
+static void create_gic(SBSAMachineState *sms, MemoryRegion *mem)
 {
     unsigned int smp_cpus = MACHINE(sms)->smp.cpus;
     SysBusDevice *gicbusdev;
@@ -405,6 +420,10 @@ static void create_gic(SBSAMachineState *sms)
     qdev_prop_set_uint32(sms->gic, "len-redist-region-count", 1);
     qdev_prop_set_uint32(sms->gic, "redist-region-count[0]", redist0_count);
 
+    object_property_set_link(OBJECT(sms->gic), "sysmem", OBJECT(mem),
+                                 &error_fatal);
+    qdev_prop_set_bit(sms->gic, "has-lpi", true);
+
     gicbusdev = SYS_BUS_DEVICE(sms->gic);
     sysbus_realize_and_unref(gicbusdev, &error_fatal);
     sysbus_mmio_map(gicbusdev, 0, sbsa_ref_memmap[SBSA_GIC_DIST].base);
@@ -451,6 +470,7 @@ static void create_gic(SBSAMachineState *sms)
         sysbus_connect_irq(gicbusdev, i + 3 * smp_cpus,
                            qdev_get_gpio_in(cpudev, ARM_CPU_VFIQ));
     }
+    create_its(sms);
 }
 
 static void create_uart(const SBSAMachineState *sms, int uart,
@@ -763,7 +783,7 @@ static void sbsa_ref_init(MachineState *machine)
 
     create_secure_ram(sms, secure_sysmem);
 
-    create_gic(sms);
+    create_gic(sms, sysmem);
 
     create_uart(sms, SBSA_UART, sysmem, serial_hd(0));
     create_uart(sms, SBSA_SECURE_UART, secure_sysmem, serial_hd(1));
-- 
2.27.0



^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [PATCH v2 8/8] hw/arm/virt: add ITS support in virt GIC
  2021-04-01  2:41 [PATCH v2 0/8] GICv3 LPI and ITS feature implementation Shashi Mallela
                   ` (6 preceding siblings ...)
  2021-04-01  2:41 ` [PATCH v2 7/8] hw/arm/sbsa-ref: add ITS support in SBSA GIC Shashi Mallela
@ 2021-04-01  2:41 ` Shashi Mallela
  2021-04-19 12:46   ` Peter Maydell
  7 siblings, 1 reply; 25+ messages in thread
From: Shashi Mallela @ 2021-04-01  2:41 UTC (permalink / raw)
  To: peter.maydell, leif, rad; +Cc: qemu-arm, qemu-devel

Included creation of ITS as part of virt platform GIC
initialization.This Emulated ITS model now co-exists with kvm
ITS and is enabled in absence of kvm irq kernel support in a
platform.

Signed-off-by: Shashi Mallela <shashi.mallela@linaro.org>
---
 hw/arm/virt.c        | 10 ++++++++--
 target/arm/kvm_arm.h |  4 ++--
 2 files changed, 10 insertions(+), 4 deletions(-)

diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index aa2bbd14e0..77cf2db90f 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -622,7 +622,7 @@ static void create_v2m(VirtMachineState *vms)
     vms->msi_controller = VIRT_MSI_CTRL_GICV2M;
 }
 
-static void create_gic(VirtMachineState *vms)
+static void create_gic(VirtMachineState *vms, MemoryRegion *mem)
 {
     MachineState *ms = MACHINE(vms);
     /* We create a standalone GIC */
@@ -656,6 +656,12 @@ static void create_gic(VirtMachineState *vms)
                              nb_redist_regions);
         qdev_prop_set_uint32(vms->gic, "redist-region-count[0]", redist0_count);
 
+        if (!kvm_irqchip_in_kernel()) {
+            object_property_set_link(OBJECT(vms->gic), "sysmem", OBJECT(mem),
+                                     &error_fatal);
+            qdev_prop_set_bit(vms->gic, "has-lpi", true);
+        }
+
         if (nb_redist_regions == 2) {
             uint32_t redist1_capacity =
                     vms->memmap[VIRT_HIGH_GIC_REDIST2].size / GICV3_REDIST_SIZE;
@@ -2039,7 +2045,7 @@ static void machvirt_init(MachineState *machine)
 
     virt_flash_fdt(vms, sysmem, secure_sysmem ?: sysmem);
 
-    create_gic(vms);
+    create_gic(vms, sysmem);
 
     virt_cpu_post_init(vms, sysmem);
 
diff --git a/target/arm/kvm_arm.h b/target/arm/kvm_arm.h
index 34f8daa377..0613454975 100644
--- a/target/arm/kvm_arm.h
+++ b/target/arm/kvm_arm.h
@@ -525,8 +525,8 @@ static inline const char *its_class_name(void)
         /* KVM implementation requires this capability */
         return kvm_direct_msi_enabled() ? "arm-its-kvm" : NULL;
     } else {
-        /* Software emulation is not implemented yet */
-        return NULL;
+        /* Software emulation based model */
+        return "arm-gicv3-its";
     }
 }
 
-- 
2.27.0



^ permalink raw reply related	[flat|nested] 25+ messages in thread

* Re: [PATCH v2 1/8] hw/intc: GICv3 ITS initial framework
  2021-04-01  2:41 ` [PATCH v2 1/8] hw/intc: GICv3 ITS initial framework Shashi Mallela
@ 2021-04-16 17:21   ` Peter Maydell
  2021-04-29 23:36     ` shashi.mallela
  0 siblings, 1 reply; 25+ messages in thread
From: Peter Maydell @ 2021-04-16 17:21 UTC (permalink / raw)
  To: Shashi Mallela
  Cc: Leif Lindholm, QEMU Developers, qemu-arm, Radoslaw Biernacki

On Thu, 1 Apr 2021 at 03:41, Shashi Mallela <shashi.mallela@linaro.org> wrote:
>
> Added register definitions relevant to ITS,implemented overall
> ITS device framework with stubs for ITS control and translater
> regions read/write,extended ITS common to handle mmio init between
> existing kvm device and newer qemu device.
>
> Signed-off-by: Shashi Mallela <shashi.mallela@linaro.org>
> ---
>  hw/intc/arm_gicv3_its.c                | 334 +++++++++++++++++++++++++
>  hw/intc/arm_gicv3_its_common.c         |  12 +-
>  hw/intc/arm_gicv3_its_kvm.c            |   2 +-
>  hw/intc/gicv3_internal.h               |  86 ++++++-
>  hw/intc/meson.build                    |   1 +
>  include/hw/intc/arm_gicv3_its_common.h |  12 +-
>  6 files changed, 430 insertions(+), 17 deletions(-)
>  create mode 100644 hw/intc/arm_gicv3_its.c

Overall I think the structure of the patchset and of the device
is good, so I'm just going to dive into more detailed review comments.


> +static MemTxResult its_trans_writew(GICv3ITSState *s, hwaddr offset,
> +                               uint64_t value, MemTxAttrs attrs)
> +{
> +    MemTxResult result = MEMTX_OK;
> +
> +    return result;
> +}
> +
> +static MemTxResult its_trans_writel(GICv3ITSState *s, hwaddr offset,
> +                               uint64_t value, MemTxAttrs attrs)
> +{
> +    MemTxResult result = MEMTX_OK;
> +
> +    return result;
> +}
> +
> +static MemTxResult gicv3_its_translation_write(void *opaque, hwaddr offset,
> +                               uint64_t data, unsigned size, MemTxAttrs attrs)
> +{
> +    GICv3ITSState *s = (GICv3ITSState *)opaque;
> +    MemTxResult result;
> +
> +    switch (size) {
> +    case 2:
> +        result = its_trans_writew(s, offset, data, attrs);
> +        break;
> +    case 4:
> +        result = its_trans_writel(s, offset, data, attrs);
> +        break;
> +    default:
> +        result = MEMTX_ERROR;
> +        break;
> +    }
> +
> +    if (result == MEMTX_ERROR) {
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +                      "%s: invalid guest write at offset " TARGET_FMT_plx
> +                      "size %u\n", __func__, offset, size);
> +        /*
> +         * The spec requires that reserved registers are RAZ/WI;
> +         * so use MEMTX_ERROR returns from leaf functions as a way to
> +         * trigger the guest-error logging but don't return it to
> +         * the caller, or we'll cause a spurious guest data abort.
> +         */
> +        result = MEMTX_OK;
> +    }
> +    return result;
> +}

There is exactly one register in the translation register frame, so
having this generic top level write function that calls out to
separate writew and writel functions is overkill and ends up
with duplication of code. I would suggest you just fold the
implementation into gicv3_its_translation_write(), which would
end up looking something like

    switch (offset) {
    case GITS_TRANSLATER:
        if (s->ctlr & ITS_CTLR_ENABLED) {
            /* 16 bit accesses behave as writes with bits [31:16] zero */
            s->translater = value;
            devid = attrs.requester_id;
            process_int(s, s->translater, devid, NONE);
        }
        break;
    default:
        break;
    }
    return MEMTX_OK;

which handles both the 16 and 32 bit case. You can tell the
core memory system code not to hand you 8 or 64 bit accesses
using fields in the MemoryRegionOps; see below.

> +static MemTxResult gicv3_its_translation_read(void *opaque, hwaddr offset,
> +                              uint64_t *data, unsigned size, MemTxAttrs attrs)
> +{
> +    qemu_log_mask(LOG_GUEST_ERROR,
> +        "%s: Invalid read from translation register area at offset "
> +        TARGET_FMT_plx "\n", __func__, offset);
> +    return MEMTX_ERROR;
> +}
> +
> +static MemTxResult its_writeb(GICv3ITSState *s, hwaddr offset,
> +                               uint64_t value, MemTxAttrs attrs)
> +{
> +    qemu_log_mask(LOG_UNIMP,
> +                "%s: unsupported byte write to register at offset "
> +                TARGET_FMT_plx "\n", __func__, offset);
> +    return MEMTX_ERROR;
> +}
> +
> +static MemTxResult its_readb(GICv3ITSState *s, hwaddr offset,
> +                               uint64_t *data, MemTxAttrs attrs)
> +{
> +    qemu_log_mask(LOG_UNIMP,
> +                "%s: unsupported byte read from register at offset "
> +                TARGET_FMT_plx "\n", __func__, offset);
> +    return MEMTX_ERROR;
> +}
> +
> +static MemTxResult its_writew(GICv3ITSState *s, hwaddr offset,
> +                               uint64_t value, MemTxAttrs attrs)
> +{
> +    qemu_log_mask(LOG_UNIMP,
> +        "%s: unsupported word write to register at offset "
> +        TARGET_FMT_plx "\n", __func__, offset);
> +    return MEMTX_ERROR;
> +}
> +
> +static MemTxResult its_readw(GICv3ITSState *s, hwaddr offset,
> +                               uint64_t *data, MemTxAttrs attrs)
> +{
> +    qemu_log_mask(LOG_UNIMP,
> +        "%s: unsupported word read from register at offset "
> +        TARGET_FMT_plx "\n", __func__, offset);
> +    return MEMTX_ERROR;
> +}

Similarly, there are no byte or halfword accessible registers
on the ITS, so these are unnecessary; just use MemoryRegionOps
to cause the core code to reject them.

> +static MemTxResult its_writel(GICv3ITSState *s, hwaddr offset,
> +                               uint64_t value, MemTxAttrs attrs)
> +{
> +    MemTxResult result = MEMTX_OK;
> +
> +    return result;
> +}
> +
> +static MemTxResult its_readl(GICv3ITSState *s, hwaddr offset,
> +                               uint64_t *data, MemTxAttrs attrs)
> +{
> +    MemTxResult result = MEMTX_OK;
> +
> +    return result;
> +}
> +
> +static MemTxResult its_writell(GICv3ITSState *s, hwaddr offset,
> +                               uint64_t value, MemTxAttrs attrs)
> +{
> +    MemTxResult result = MEMTX_OK;
> +
> +    return result;
> +}
> +
> +static MemTxResult its_readll(GICv3ITSState *s, hwaddr offset,
> +                               uint64_t *data, MemTxAttrs attrs)
> +{
> +    MemTxResult result = MEMTX_OK;
> +
> +    return result;
> +}
> +
> +static MemTxResult gicv3_its_read(void *opaque, hwaddr offset, uint64_t *data,
> +                              unsigned size, MemTxAttrs attrs)
> +{
> +    GICv3ITSState *s = (GICv3ITSState *)opaque;
> +    MemTxResult result;
> +
> +    switch (size) {
> +    case 1:
> +        result = its_readb(s, offset, data, attrs);
> +        break;
> +    case 2:
> +        result = its_readw(s, offset, data, attrs);
> +        break;
> +    case 4:
> +        result = its_readl(s, offset, data, attrs);
> +        break;
> +    case 8:
> +        result = its_readll(s, offset, data, attrs);
> +        break;
> +    default:
> +        result = MEMTX_ERROR;
> +        break;
> +    }
> +
> +    if (result == MEMTX_ERROR) {
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +                      "%s: invalid guest read at offset " TARGET_FMT_plx
> +                      "size %u\n", __func__, offset, size);
> +        /*
> +         * The spec requires that reserved registers are RAZ/WI;
> +         * so use MEMTX_ERROR returns from leaf functions as a way to
> +         * trigger the guest-error logging but don't return it to
> +         * the caller, or we'll cause a spurious guest data abort.
> +         */
> +        result = MEMTX_OK;
> +        *data = 0;
> +    }
> +    return result;
> +}
> +
> +static MemTxResult gicv3_its_write(void *opaque, hwaddr offset, uint64_t data,
> +                               unsigned size, MemTxAttrs attrs)
> +{
> +    GICv3ITSState *s = (GICv3ITSState *)opaque;
> +    MemTxResult result;
> +
> +    switch (size) {
> +    case 1:
> +        result = its_writeb(s, offset, data, attrs);
> +        break;
> +    case 2:
> +        result = its_writew(s, offset, data, attrs);
> +        break;
> +    case 4:
> +        result = its_writel(s, offset, data, attrs);
> +        break;
> +    case 8:
> +        result = its_writell(s, offset, data, attrs);
> +        break;
> +    default:
> +        result = MEMTX_ERROR;
> +        break;
> +    }
> +
> +    if (result == MEMTX_ERROR) {
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +                      "%s: invalid guest write at offset " TARGET_FMT_plx
> +                      "size %u\n", __func__, offset, size);
> +        /*
> +         * The spec requires that reserved registers are RAZ/WI;
> +         * so use MEMTX_ERROR returns from leaf functions as a way to
> +         * trigger the guest-error logging but don't return it to
> +         * the caller, or we'll cause a spurious guest data abort.
> +         */
> +        result = MEMTX_OK;
> +    }
> +    return result;
> +}
> +
> +static const MemoryRegionOps gicv3_its_control_ops = {
> +    .read_with_attrs = gicv3_its_read,
> +    .write_with_attrs = gicv3_its_write,
> +    .endianness = DEVICE_NATIVE_ENDIAN,

       .valid.min_access_size = 4,
       .valid.max_access_size = 8,
       .impl.min_access_size = 4,
       .impl.max_access_size = 8,

And then you don't need to handle 1 and 2 in your read/write fns.


> +};
> +
> +static const MemoryRegionOps gicv3_its_translation_ops = {
> +    .read_with_attrs = gicv3_its_translation_read,
> +    .write_with_attrs = gicv3_its_translation_write,
> +    .endianness = DEVICE_NATIVE_ENDIAN,

Similarly here set the valid and impl min and max to 2 and 4.

> +};
> +
> +static void gicv3_arm_its_realize(DeviceState *dev, Error **errp)
> +{
> +    GICv3ITSState *s = ARM_GICV3_ITS_COMMON(dev);
> +
> +    gicv3_its_init_mmio(s, &gicv3_its_control_ops, &gicv3_its_translation_ops);
> +}
> +
> +static void gicv3_its_reset(DeviceState *dev)
> +{
> +    GICv3ITSState *s = ARM_GICV3_ITS_COMMON(dev);
> +    GICv3ITSClass *c = ARM_GICV3_ITS_GET_CLASS(s);
> +
> +    if (s->gicv3->cpu->gicr_typer & GICR_TYPER_PLPIS) {
> +        c->parent_reset(dev);
> +
> +        /* set the ITS default features supported */
> +        s->typer = FIELD_DP64(s->typer, GITS_TYPER, PHYSICAL,
> +                                       GITS_TYPE_PHYSICAL);
> +        s->typer = FIELD_DP64(s->typer, GITS_TYPER, ITT_ENTRY_SIZE,
> +                                       ITS_ITT_ENTRY_SIZE);
> +        s->typer = FIELD_DP64(s->typer, GITS_TYPER, IDBITS, ITS_IDBITS);
> +        s->typer = FIELD_DP64(s->typer, GITS_TYPER, DEVBITS, ITS_DEVBITS);
> +        s->typer = FIELD_DP64(s->typer, GITS_TYPER, CIL, 1);
> +        s->typer = FIELD_DP64(s->typer, GITS_TYPER, CIDBITS, ITS_CIDBITS);

This is a read-only constant value. Two options:
 (1) set s->typer in realize, not in reset (this is how we handle
     GICR_TYPER)
 (2) don't have an s->typer at all, and just return the right
     constant value when in the read function (this is how we
     handle GICD_TYPER)

> +
> +        /*
> +         * We claim to be an ARM r0p0 with a zero ProductID.
> +         * This is the same as an r0p0 GIC-500.
> +         */
> +        s->iidr = gicv3_iidr();

IIDR is constant, so just call gicv3_iidr() to get its value directly
in the register read function.

> +
> +        /* Quiescent bit reset to 1 */
> +        s->ctlr = FIELD_DP32(s->ctlr, GITS_CTLR, QUIESCENT, 1);
> +
> +        /*
> +         * setting GITS_BASER0.Type = 0b001 (Device)
> +         *         GITS_BASER1.Type = 0b100 (Collection Table)
> +         *         GITS_BASER<n>.Type,where n = 3 to 7 are 0b00 (Unimplemented)
> +         *         GITS_BASER<0,1>.Page_Size = 64KB
> +         * and default translation table entry size to 16 bytes
> +         */
> +        s->baser[0] = FIELD_DP64(s->baser[0], GITS_BASER, TYPE,
> +                                              GITS_ITT_TYPE_DEVICE);
> +        s->baser[0] = FIELD_DP64(s->baser[0], GITS_BASER, PAGESIZE,
> +                                              GITS_BASER_PAGESIZE_64K);
> +        s->baser[0] = FIELD_DP64(s->baser[0], GITS_BASER, ENTRYSIZE,
> +                                              GITS_DTE_SIZE);
> +
> +        s->baser[1] = FIELD_DP64(s->baser[1], GITS_BASER, TYPE,
> +                                              GITS_ITT_TYPE_COLLECTION);
> +        s->baser[1] = FIELD_DP64(s->baser[1], GITS_BASER, PAGESIZE,
> +                                              GITS_BASER_PAGESIZE_64K);
> +        s->baser[1] = FIELD_DP64(s->baser[1], GITS_BASER, ENTRYSIZE,
> +                                              GITS_CTE_SIZE);
> +    }
> +}
> +
> +static Property gicv3_its_props[] = {
> +    DEFINE_PROP_LINK("parent-gicv3", GICv3ITSState, gicv3, "arm-gicv3",
> +                     GICv3State *),
> +    DEFINE_PROP_END_OF_LIST(),
> +};
> +
> +static void gicv3_its_class_init(ObjectClass *klass, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +    GICv3ITSClass *ic = ARM_GICV3_ITS_CLASS(klass);
> +
> +    dc->realize = gicv3_arm_its_realize;
> +    device_class_set_props(dc, gicv3_its_props);
> +    device_class_set_parent_reset(dc, gicv3_its_reset, &ic->parent_reset);
> +}
> +
> +static const TypeInfo gicv3_its_info = {
> +    .name = TYPE_ARM_GICV3_ITS,
> +    .parent = TYPE_ARM_GICV3_ITS_COMMON,
> +    .instance_size = sizeof(GICv3ITSState),
> +    .class_init = gicv3_its_class_init,
> +    .class_size = sizeof(GICv3ITSClass),
> +};
> +
> +static void gicv3_its_register_types(void)
> +{
> +    type_register_static(&gicv3_its_info);
> +}
> +
> +type_init(gicv3_its_register_types)
> diff --git a/hw/intc/arm_gicv3_its_common.c b/hw/intc/arm_gicv3_its_common.c
> index 66c4c6a188..80cc9ec6d8 100644
> --- a/hw/intc/arm_gicv3_its_common.c
> +++ b/hw/intc/arm_gicv3_its_common.c
> @@ -50,12 +50,16 @@ static int gicv3_its_post_load(void *opaque, int version_id)
>
>  static const VMStateDescription vmstate_its = {
>      .name = "arm_gicv3_its",
> +    .version_id = 1,
> +    .minimum_version_id = 1,
>      .pre_save = gicv3_its_pre_save,
>      .post_load = gicv3_its_post_load,
>      .priority = MIG_PRI_GICV3_ITS,
>      .fields = (VMStateField[]) {
>          VMSTATE_UINT32(ctlr, GICv3ITSState),
> +        VMSTATE_UINT32(translater, GICv3ITSState),
>          VMSTATE_UINT32(iidr, GICv3ITSState),
> +        VMSTATE_UINT64(typer, GICv3ITSState),

You cannot change a VMStateDescription like this -- you will break
migration compatibility. Luckily, in this case there's no need:
GITS_TYPER is read-only, so it doesn't need to be migrated, and
GITS_TRANSLATER is not a real contains-state register (it's write-only),
so there's no new state there either. Neither needs to be a new field
in the GICv3ITSState structure either (though typer could be if you prefer).

>          VMSTATE_UINT64(cbaser, GICv3ITSState),
>          VMSTATE_UINT64(cwriter, GICv3ITSState),
>          VMSTATE_UINT64(creadr, GICv3ITSState),
> @@ -99,15 +103,16 @@ static const MemoryRegionOps gicv3_its_trans_ops = {
>      .endianness = DEVICE_NATIVE_ENDIAN,
>  };

> +/**
> + * Default features advertised by this version of ITS
> + */
> +/* Physical LPIs supported */
> +#define GITS_TYPE_PHYSICAL           (1U << 0)
> +
> +/*
> + * 11 bytes Interrupt translation Table Entry size
> + * Valid = 1 bit,InterruptType = 1 bit,
> + * Size of LPI number space[considering max 24 bits],
> + * Size of LPI number space[considering max 24 bits],
> + * ICID = 16 bits,
> + * vPEID = 16 bits
> + */
> +#define ITS_ITT_ENTRY_SIZE            0xB
> +
> +/* 16 bits EventId */
> +#define ITS_IDBITS                   GICD_TYPER_IDBITS
> +
> +/* 16 bits DeviceId */
> +#define ITS_DEVBITS                   0xF
> +
> +/* 16 bits CollectionId */
> +#define ITS_CIDBITS                  0xF
> +
> +/*
> + * 8 bytes Device Table Entry size
> + * Valid = 1 bit,ITTAddr = 44 bits,Size = 5 bits
> + */
> +#define GITS_DTE_SIZE                 (0x8ULL)
> +
> +/*
> + * 8 bytes Collection Table Entry size
> + * Valid = 1 bit,RDBase = 36 bits(considering max RDBASE)
> + */
> +#define GITS_CTE_SIZE                 (0x8ULL)
> +

Is this the same data structure the in-kernel KVM ITS uses, or is
it just one specific to the QEMU emulated implementation ?

>  /* Special interrupt IDs */
>  #define INTID_SECURE 1020
>  #define INTID_NONSECURE 1021
> diff --git a/hw/intc/meson.build b/hw/intc/meson.build
> index 1c299039f6..53472239f0 100644
> --- a/hw/intc/meson.build
> +++ b/hw/intc/meson.build
> @@ -8,6 +8,7 @@ softmmu_ss.add(when: 'CONFIG_ARM_GIC', if_true: files(
>    'arm_gicv3_dist.c',
>    'arm_gicv3_its_common.c',
>    'arm_gicv3_redist.c',
> +  'arm_gicv3_its.c',
>  ))
>  softmmu_ss.add(when: 'CONFIG_ETRAXFS', if_true: files('etraxfs_pic.c'))
>  softmmu_ss.add(when: 'CONFIG_HEATHROW_PIC', if_true: files('heathrow_pic.c'))
> diff --git a/include/hw/intc/arm_gicv3_its_common.h b/include/hw/intc/arm_gicv3_its_common.h
> index 5a0952b404..08bc5652ed 100644
> --- a/include/hw/intc/arm_gicv3_its_common.h
> +++ b/include/hw/intc/arm_gicv3_its_common.h
> @@ -25,17 +25,24 @@
>  #include "hw/intc/arm_gicv3_common.h"
>  #include "qom/object.h"
>
> +#define TYPE_ARM_GICV3_ITS "arm-gicv3-its"
> +
>  #define ITS_CONTROL_SIZE 0x10000
>  #define ITS_TRANS_SIZE   0x10000
>  #define ITS_SIZE         (ITS_CONTROL_SIZE + ITS_TRANS_SIZE)
>
>  #define GITS_CTLR        0x0
>  #define GITS_IIDR        0x4
> +#define GITS_TYPER       0x8
>  #define GITS_CBASER      0x80
>  #define GITS_CWRITER     0x88
>  #define GITS_CREADR      0x90
>  #define GITS_BASER       0x100
>
> +#define GITS_TRANSLATER  0x0040
> +
> +#define GITS_PIDR2       0xFFE8

You probably don't want an offset for GITS_PIDR2 specifically.
Compare handling of GICD_IDREGS in the distributor emulation.

thanks
-- PMM


^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH v2 2/8] hw/intc: GICv3 ITS register definitions added
  2021-04-01  2:41 ` [PATCH v2 2/8] hw/intc: GICv3 ITS register definitions added Shashi Mallela
@ 2021-04-16 18:54   ` Peter Maydell
       [not found]     ` <937a2923445e3ff629c9799a8579c470d2636375.camel@linaro.org>
  0 siblings, 1 reply; 25+ messages in thread
From: Peter Maydell @ 2021-04-16 18:54 UTC (permalink / raw)
  To: Shashi Mallela
  Cc: Leif Lindholm, QEMU Developers, qemu-arm, Radoslaw Biernacki

On Thu, 1 Apr 2021 at 03:41, Shashi Mallela <shashi.mallela@linaro.org> wrote:
>
> Defined descriptors for ITS device table,collection table and ITS
> command queue entities.Implemented register read/write functions,
> extract ITS table parameters and command queue parameters,extended
> gicv3 common to capture qemu address space(which host the ITS table
> platform memories required for subsequent ITS processing) and
> initialize the same in its device.
>
> Signed-off-by: Shashi Mallela <shashi.mallela@linaro.org>
> ---
>  hw/intc/arm_gicv3_its.c                | 313 +++++++++++++++++++++++++
>  hw/intc/arm_gicv3_its_common.c         |  42 ++++
>  hw/intc/gicv3_internal.h               |  33 ++-
>  include/hw/intc/arm_gicv3_common.h     |   4 +
>  include/hw/intc/arm_gicv3_its_common.h |  28 +++
>  5 files changed, 414 insertions(+), 6 deletions(-)
>
> diff --git a/hw/intc/arm_gicv3_its.c b/hw/intc/arm_gicv3_its.c
> index 209120d102..81373f049d 100644
> --- a/hw/intc/arm_gicv3_its.c
> +++ b/hw/intc/arm_gicv3_its.c
> @@ -28,6 +28,131 @@ struct GICv3ITSClass {
>      void (*parent_reset)(DeviceState *dev);
>  };
>
> +static bool extract_table_params(GICv3ITSState *s, int index)
> +{
> +    uint16_t num_pages = 0;
> +    uint8_t  page_sz_type;
> +    uint8_t type;
> +    uint32_t page_sz = 0;
> +    uint64_t value = s->baser[index];
> +
> +    num_pages = FIELD_EX64(value, GITS_BASER, SIZE);
> +    page_sz_type = FIELD_EX64(value, GITS_BASER, PAGESIZE);
> +
> +    if (page_sz_type == 0) {
> +        page_sz = GITS_ITT_PAGE_SIZE_0;
> +    } else if (page_sz_type == 0) {
> +        page_sz = GITS_ITT_PAGE_SIZE_1;
> +    } else if (page_sz_type == 2) {
> +        page_sz = GITS_ITT_PAGE_SIZE_2;
> +    } else {
> +        return false;

The spec says that page_sz+type = 0b11 should be treated as 0b10.
So we could log this as a guest error if you like but otherwise
should handle it the same way we do 0b10.

> +    }

if (x == CONST1) {
} else if (x == CONST2) {
} else {
}

is usually clearer written with switch(). (There are other instances
of this pattern below.)

> +
> +    type = FIELD_EX64(value, GITS_BASER, TYPE);
> +
> +    if (type == GITS_ITT_TYPE_DEVICE) {


> +        s->dt.valid = FIELD_EX64(value, GITS_BASER, VALID);
> +
> +        if (s->dt.valid) {
> +            s->dt.indirect = FIELD_EX64(value, GITS_BASER, INDIRECT);
> +            s->dt.entry_sz = FIELD_EX64(value, GITS_BASER, ENTRYSIZE);
> +
> +            if (!s->dt.indirect) {
> +                s->dt.max_entries = ((num_pages + 1) * page_sz) /
> +                                                       s->dt.entry_sz;
> +            } else {
> +                s->dt.max_entries = ((((num_pages + 1) * page_sz) /
> +                                        L1TABLE_ENTRY_SIZE) *
> +                                    (page_sz / s->dt.entry_sz));
> +            }
> +
> +            s->dt.max_devids = (1UL << (FIELD_EX64(s->typer, GITS_TYPER,
> +                                                    DEVBITS) + 1));
> +
> +            if ((page_sz == GITS_ITT_PAGE_SIZE_0) ||
> +                    (page_sz == GITS_ITT_PAGE_SIZE_1)) {
> +                s->dt.base_addr = FIELD_EX64(value, GITS_BASER, PHYADDR);
> +                s->dt.base_addr <<= R_GITS_BASER_PHYADDR_SHIFT;
> +            } else if (page_sz == GITS_ITT_PAGE_SIZE_2) {
> +                s->dt.base_addr = FIELD_EX64(value, GITS_BASER, PHYADDRL_64K) <<
> +                                  R_GITS_BASER_PHYADDRL_64K_SHIFT;
> +                  s->dt.base_addr |= ((value >> R_GITS_BASER_PHYADDR_SHIFT) &
> +                                       R_GITS_BASER_PHYADDRH_64K_MASK) <<
> +                                       R_GITS_BASER_PHYADDRH_64K_SHIFT;
> +            }
> +        }
> +    } else if (type == GITS_ITT_TYPE_COLLECTION) {
> +        s->ct.valid = FIELD_EX64(value, GITS_BASER, VALID);
> +
> +        /*
> +         * GITS_TYPER.HCC is 0 for this implementation
> +         * hence writes are discarded if ct.valid is 0
> +         */
> +        if (s->ct.valid) {
> +            s->ct.indirect = FIELD_EX64(value, GITS_BASER, INDIRECT);
> +            s->ct.entry_sz = FIELD_EX64(value, GITS_BASER, ENTRYSIZE);
> +
> +            if (!s->ct.indirect) {
> +                s->ct.max_entries = ((num_pages + 1) * page_sz) /
> +                                      s->ct.entry_sz;
> +            } else {
> +                s->ct.max_entries = ((((num_pages + 1) * page_sz) /
> +                                      L1TABLE_ENTRY_SIZE) *
> +                                      (page_sz / s->ct.entry_sz));
> +            }
> +
> +            if (FIELD_EX64(s->typer, GITS_TYPER, CIL)) {
> +                s->ct.max_collids = (1UL << (FIELD_EX64(s->typer, GITS_TYPER,
> +                                                         CIDBITS) + 1));
> +            } else {
> +                /* 16-bit CollectionId supported when CIL == 0 */
> +                s->ct.max_collids = (1UL << 16);
> +            }
> +
> +            if ((page_sz == GITS_ITT_PAGE_SIZE_0) ||
> +                 (page_sz == GITS_ITT_PAGE_SIZE_1)) {
> +                s->ct.base_addr = FIELD_EX64(value, GITS_BASER, PHYADDR);
> +                s->ct.base_addr <<= R_GITS_BASER_PHYADDR_SHIFT;
> +            } else if (page_sz == GITS_ITT_PAGE_SIZE_2) {
> +                s->ct.base_addr = FIELD_EX64(value, GITS_BASER, PHYADDRL_64K) <<
> +                                    R_GITS_BASER_PHYADDRL_64K_SHIFT;
> +                s->ct.base_addr |= ((value >> R_GITS_BASER_PHYADDR_SHIFT) &
> +                                     R_GITS_BASER_PHYADDRH_64K_MASK) <<
> +                                     R_GITS_BASER_PHYADDRH_64K_SHIFT;
> +            }
> +        }
> +    } else {
> +        /* unsupported ITS table type */
> +        qemu_log_mask(LOG_GUEST_ERROR, "%s: Unsupported ITS table type %d",
> +                         __func__, type);

This should never happen, because the TYPE field in a GITS_BASER register
is read-only. The model should insure that the guest can never write to
these bits. Then you can just assert() that the field is valid, if you like.

> +        return false;
> +    }
> +    return true;
> +}
> +
> +static bool extract_cmdq_params(GICv3ITSState *s)
> +{
> +    uint16_t num_pages = 0;
> +    uint64_t value = s->cbaser;
> +
> +    num_pages = FIELD_EX64(value, GITS_CBASER, SIZE);
> +
> +    s->cq.valid = FIELD_EX64(value, GITS_CBASER, VALID);
> +
> +    if (!num_pages || !s->cq.valid) {

Why do you think num_pages == 0 is not valid ?

> +        return false;
> +    }
> +
> +    if (s->cq.valid) {
> +        s->cq.max_entries = ((num_pages + 1) * GITS_ITT_PAGE_SIZE_0) /
> +                                                GITS_CMDQ_ENTRY_SIZE;
> +        s->cq.base_addr = FIELD_EX64(value, GITS_CBASER, PHYADDR);
> +        s->cq.base_addr <<= R_GITS_CBASER_PHYADDR_SHIFT;
> +    }
> +    return true;
> +}
> +
>  static MemTxResult its_trans_writew(GICv3ITSState *s, hwaddr offset,
>                                 uint64_t value, MemTxAttrs attrs)
>  {
> @@ -126,7 +251,75 @@ static MemTxResult its_writel(GICv3ITSState *s, hwaddr offset,
>                                 uint64_t value, MemTxAttrs attrs)>  {
>      MemTxResult result = MEMTX_OK;
> +    int index;
> +    uint64_t temp = 0;
>
> +    switch (offset) {
> +    case GITS_CTLR:
> +        s->ctlr |= (value & ~(s->ctlr));
> +        break;
> +    case GITS_CBASER:
> +        /* GITS_CBASER register becomes RO if ITS is already enabled */

The spec says writes are UNPREDICTABLE, not RO. We can make an
impdef choice to interpret that as RO for our convenience, but
the comment should note that it is our IMPDEF choice.

> +        if (!(s->ctlr & ITS_CTLR_ENABLED)) {
> +            s->cbaser = deposit64(s->cbaser, 0, 32, value);
> +            s->creadr = 0;
> +        }
> +        break;
> +    case GITS_CBASER + 4:
> +        /* GITS_CBASER register becomes RO if ITS is already enabled */
> +        if (!(s->ctlr & ITS_CTLR_ENABLED)) {
> +            s->cbaser = deposit64(s->cbaser, 32, 32, value);
> +            if (!extract_cmdq_params(s)) {

You should postpone trying to parse the CBASER fields until
the guest enables the ITS by writing to GITS_CTLR.Enabled.
Because CBASER is a 64-bit register that can be written as two
32-bit halves, the guest might choose to write the two halves
in either order, and you won't have a full valid CBASER.
If you wait until the ITS is enabled then you know the guest
should have written the register values completely then.
Similarly with BASER.

> +                qemu_log_mask(LOG_GUEST_ERROR,
> +                       "%s: error extracting GITS_CBASER parameters "
> +                       TARGET_FMT_plx "\n", __func__, offset);
> +                s->cbaser = 0;

Clearing CBASER if the guest writes an invalid value to it
doesn't seem right. Since you have these expanded-out structs,
you can just treat any bogus values as if the guest had written
VALID=0, and leave the actual guest-written value in s->cbaser.
(In fact I think the only case when extract_cmdq_params()
can reasonably return false is exactly when VALID=0, so in
that case you might as well drop the bool return type from it
completely.)

> +                result = MEMTX_ERROR;

I don't recommend using MEMTX_ERROR here for this kind of
"the guest wrote a value to a valid register but there happened
to be something wrong with the value it wrote". The intention
with the arm_gicv3_dist.c code structure that you've copied
here for the ITS was simply to use MEMTX_ERROR to capture "this
register doesn't exist for this size" to be able to have a
single LOG_GUEST_ERROR message for that at the top level
function.

For more complicated stuff like this you should just report a
suitable specific LOG_GUEST_ERROR directly in extract_cmdq_params()
and return MEMTX_OK. (The code you have for BASER will actually report
three different log messages for a single guest mistake -- once inside
extract_table_params(), once in the code in this function that calls
extract_table_params(), and then once in gicv3_its_read(). One is
sufficient.)

> +            } else {
> +                s->creadr = 0;
> +            }
> +        }
> +        break;
> +    case GITS_CWRITER:
> +        s->cwriter = deposit64(s->cwriter, 0, 32, value);
> +        break;
> +    case GITS_CWRITER + 4:
> +        s->cwriter = deposit64(s->cwriter, 32, 32, value);
> +        break;
> +    case GITS_BASER ... GITS_BASER + 0x3f:
> +        /* GITS_BASERn registers become RO if ITS is already enabled */
> +        if (!(s->ctlr & ITS_CTLR_ENABLED)) {
> +            index = (offset - GITS_BASER) / 8;
> +
> +            if (offset & 7) {
> +                temp = s->baser[index];
> +                temp = deposit64(temp, 32, 32, (value & ~GITS_BASER_VAL_MASK));
> +                s->baser[index] |= temp;
> +
> +                if (!extract_table_params(s, index)) {
> +                    qemu_log_mask(LOG_GUEST_ERROR,
> +                        "%s: error extracting GITS_BASER parameters "
> +                        TARGET_FMT_plx "\n", __func__, offset);
> +                    s->baser[index] = 0;
> +                    result = MEMTX_ERROR;
> +                }
> +            } else {
> +                s->baser[index] =  deposit64(s->baser[index], 0, 32, value);
> +            }
> +        }

Pretty much all the remarks I made earlier about CBASER apply here too.

Also, 'unimplemented' BASER registers are supposed to be RES0, and
I don't think we actually implement more than either 2 or 3, right?

> +        break;
> +    case GITS_IIDR:
> +    case GITS_TYPER:
> +    case GITS_CREADR:
> +        /* RO registers, ignore the write */
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +            "%s: invalid guest write to RO register at offset "
> +            TARGET_FMT_plx "\n", __func__, offset);
> +        break;

I wouldn't personally bother explicitly reporting write-to-RO-register
here: if you just let the default case handle it then the log in
the caller will do a LOG_GUEST_ERROR for you.

> +    default:
> +        result = MEMTX_ERROR;
> +        break;
> +    }
>      return result;
>  }
>
> @@ -142,7 +382,52 @@ static MemTxResult its_writell(GICv3ITSState *s, hwaddr offset,
>                                 uint64_t value, MemTxAttrs attrs)
>  {
>      MemTxResult result = MEMTX_OK;
> +    int index;
>
> +    switch (offset) {
> +    case GITS_BASER ... GITS_BASER + 0x3f:
> +        /* GITS_BASERn registers become RO if ITS is already enabled */
> +        if (!(s->ctlr & ITS_CTLR_ENABLED)) {
> +            index = (offset - GITS_BASER) / 8;
> +            s->baser[index] |= (value & ~GITS_BASER_VAL_MASK);
> +            if (!extract_table_params(s, index)) {
> +                qemu_log_mask(LOG_GUEST_ERROR,
> +                        "%s: error extracting GITS_BASER parameters "
> +                        TARGET_FMT_plx "\n", __func__, offset);
> +                s->baser[index] = 0;
> +                result = MEMTX_ERROR;
> +            }
> +        }
> +        break;
> +    case GITS_CBASER:
> +        /* GITS_CBASER register becomes RO if ITS is already enabled */
> +        if (!(s->ctlr & ITS_CTLR_ENABLED)) {
> +            s->cbaser = value;
> +            if (!extract_cmdq_params(s)) {
> +                qemu_log_mask(LOG_GUEST_ERROR,
> +                       "%s: error extracting GITS_CBASER parameters "
> +                       TARGET_FMT_plx "\n", __func__, offset);
> +                s->cbaser = 0;
> +                result = MEMTX_ERROR;
> +            } else {
> +                s->creadr = 0;
> +            }
> +        }
> +        break;
> +    case GITS_CWRITER:
> +        s->cwriter = value;
> +        break;
> +    case GITS_TYPER:
> +    case GITS_CREADR:
> +        /* RO register, ignore the write */
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +                      "%s: invalid guest write to RO register at offset "
> +                      TARGET_FMT_plx "\n", __func__, offset);
> +        break;
> +    default:
> +        result = MEMTX_ERROR;
> +        break;
> +    }

Comments on the 32-bit write function apply here too.

>      return result;
>  }
> @@ -250,6 +557,9 @@ static void gicv3_arm_its_realize(DeviceState *dev, Error **errp)
>      GICv3ITSState *s = ARM_GICV3_ITS_COMMON(dev);
>
>      gicv3_its_init_mmio(s, &gicv3_its_control_ops, &gicv3_its_translation_ops);
> +
> +    address_space_init(&s->gicv3->sysmem_as, s->gicv3->sysmem,
> +                        "gicv3-its-sysmem");

Indent here seems wrong -- the second line should be lined up with
the first, so the '"' is below the '&'. I think I noticed some of this
in patch 1 as well, so worth going through and checking that.

>  }
>
>  static void gicv3_its_reset(DeviceState *dev)
> @@ -259,6 +569,9 @@ static void gicv3_its_reset(DeviceState *dev)
>
>      if (s->gicv3->cpu->gicr_typer & GICR_TYPER_PLPIS) {
>          c->parent_reset(dev);
> +        memset(&s->dt, 0, sizeof(s->dt));
> +        memset(&s->ct, 0, sizeof(s->ct));
> +        memset(&s->cq, 0, sizeof(s->cq));

Would be cleaner to first set the baser/cbaser registers to their
reset values and then just call the function that calculates these
struct fields based on the register values.

>
>          /* set the ITS default features supported */
>          s->typer = FIELD_DP64(s->typer, GITS_TYPER, PHYSICAL,
> diff --git a/hw/intc/arm_gicv3_its_common.c b/hw/intc/arm_gicv3_its_common.c
> index 80cc9ec6d8..9c5c71f350 100644
> --- a/hw/intc/arm_gicv3_its_common.c
> +++ b/hw/intc/arm_gicv3_its_common.c
> @@ -48,6 +48,42 @@ static int gicv3_its_post_load(void *opaque, int version_id)
>      return 0;
>  }
>
> +static const VMStateDescription vmstate_its_dtdesc = {
> +    .name = "arm_gicv3_its_dtdesc",
> +    .fields = (VMStateField[]) {
> +        VMSTATE_BOOL(valid, DevTableDesc),
> +        VMSTATE_BOOL(indirect, DevTableDesc),
> +        VMSTATE_UINT16(entry_sz, DevTableDesc),
> +        VMSTATE_UINT32(max_entries, DevTableDesc),
> +        VMSTATE_UINT32(max_devids, DevTableDesc),
> +        VMSTATE_UINT64(base_addr, DevTableDesc),
> +        VMSTATE_END_OF_LIST(),
> +    },
> +};
> +
> +static const VMStateDescription vmstate_its_ctdesc = {
> +    .name = "arm_gicv3_its_ctdesc",
> +    .fields = (VMStateField[]) {
> +        VMSTATE_BOOL(valid, CollTableDesc),
> +        VMSTATE_BOOL(indirect, CollTableDesc),
> +        VMSTATE_UINT16(entry_sz, CollTableDesc),
> +        VMSTATE_UINT32(max_entries, CollTableDesc),
> +        VMSTATE_UINT32(max_collids, CollTableDesc),
> +        VMSTATE_UINT64(base_addr, CollTableDesc),
> +        VMSTATE_END_OF_LIST(),
> +    },
> +};
> +
> +static const VMStateDescription vmstate_its_cqdesc = {
> +    .name = "arm_gicv3_its_cqdesc",
> +    .fields = (VMStateField[]) {
> +        VMSTATE_BOOL(valid, CmdQDesc),
> +        VMSTATE_UINT32(max_entries, CmdQDesc),
> +        VMSTATE_UINT64(base_addr, CmdQDesc),
> +        VMSTATE_END_OF_LIST(),
> +    },
> +};

Do these really need migrating, or are they just expanded out values
that can be recalculated from the BASER and CBASER register values?
If so then it would be simpler to recalculate in a migration post-load
function.

> +
>  static const VMStateDescription vmstate_its = {
>      .name = "arm_gicv3_its",
>      .version_id = 1,
> @@ -56,6 +92,12 @@ static const VMStateDescription vmstate_its = {
>      .post_load = gicv3_its_post_load,
>      .priority = MIG_PRI_GICV3_ITS,
>      .fields = (VMStateField[]) {
> +        VMSTATE_STRUCT(dt, GICv3ITSState, 0, vmstate_its_dtdesc,
> +                        DevTableDesc),
> +        VMSTATE_STRUCT(ct, GICv3ITSState, 0, vmstate_its_ctdesc,
> +                        CollTableDesc),
> +        VMSTATE_STRUCT(cq, GICv3ITSState, 0, vmstate_its_cqdesc,
> +                        CmdQDesc),

As noted previously, you can't just add fields to a VMStateDescription;
retaining migration compatibility requires more than that.

>          VMSTATE_UINT32(ctlr, GICv3ITSState),
>          VMSTATE_UINT32(translater, GICv3ITSState),
>          VMSTATE_UINT32(iidr, GICv3ITSState),
> diff --git a/hw/intc/gicv3_internal.h b/hw/intc/gicv3_internal.h
> index 96cfe2dff9..b7701e8ca5 100644
> --- a/hw/intc/gicv3_internal.h
> +++ b/hw/intc/gicv3_internal.h
> @@ -246,13 +246,14 @@ FIELD(GITS_BASER, INNERCACHE, 59, 3)
>  FIELD(GITS_BASER, INDIRECT, 62, 1)
>  FIELD(GITS_BASER, VALID, 63, 1)
>
> -#define GITS_BASER_PAGESIZE_4K                0
> -#define GITS_BASER_PAGESIZE_16K               1
> -#define GITS_BASER_PAGESIZE_64K               2
> -
> -#define GITS_ITT_TYPE_DEVICE                  1ULL
> -#define GITS_ITT_TYPE_COLLECTION              4ULL

You just added these in the previous patch, don't move them
in this one. Have patch 1 put them in the right place in the file
to start with.

> +FIELD(GITS_CBASER, SIZE, 0, 8)
> +FIELD(GITS_CBASER, SHAREABILITY, 10, 2)
> +FIELD(GITS_CBASER, PHYADDR, 12, 40)
> +FIELD(GITS_CBASER, OUTERCACHE, 53, 3)
> +FIELD(GITS_CBASER, INNERCACHE, 59, 3)
> +FIELD(GITS_CBASER, VALID, 63, 1)
>
> +FIELD(GITS_CTLR, ENABLED, 0, 1)
>  FIELD(GITS_CTLR, QUIESCENT, 31, 1)
>
>  FIELD(GITS_TYPER, PHYSICAL, 0, 1)
> @@ -264,6 +265,26 @@ FIELD(GITS_TYPER, PTA, 19, 1)
>  FIELD(GITS_TYPER, CIDBITS, 32, 4)
>  FIELD(GITS_TYPER, CIL, 36, 1)
>
> +#define ITS_CTLR_ENABLED               (1U)  /* ITS Enabled */
> +
> +#define GITS_BASER_VAL_MASK                  (R_GITS_BASER_ENTRYSIZE_MASK | \
> +                                              R_GITS_BASER_TYPE_MASK)
> +
> +#define GITS_BASER_PAGESIZE_4K                0
> +#define GITS_BASER_PAGESIZE_16K               1
> +#define GITS_BASER_PAGESIZE_64K               2
> +
> +#define GITS_ITT_TYPE_DEVICE                  1ULL
> +#define GITS_ITT_TYPE_COLLECTION              4ULL
> +
> +#define GITS_ITT_PAGE_SIZE_0      0x1000
> +#define GITS_ITT_PAGE_SIZE_1      0x4000
> +#define GITS_ITT_PAGE_SIZE_2      0x10000
> +
> +#define L1TABLE_ENTRY_SIZE         8
> +
> +#define GITS_CMDQ_ENTRY_SIZE               32
> +
>  /**
>   * Default features advertised by this version of ITS
>   */
> diff --git a/include/hw/intc/arm_gicv3_common.h b/include/hw/intc/arm_gicv3_common.h
> index 91491a2f66..b0f2414fa3 100644
> --- a/include/hw/intc/arm_gicv3_common.h
> +++ b/include/hw/intc/arm_gicv3_common.h
> @@ -226,12 +226,16 @@ struct GICv3State {
>      int dev_fd; /* kvm device fd if backed by kvm vgic support */
>      Error *migration_blocker;
>
> +    MemoryRegion *sysmem;
> +    AddressSpace sysmem_as;

I think these would be better named "dma" and "dma_as", because
from the ITS' point of view that's what they are. There's no inherent
requirement that they be connected to the system address space in particular.

> +
>      /* Distributor */
>
>      /* for a GIC with the security extensions the NS banked version of this
>       * register is just an alias of bit 1 of the S banked version.
>       */
>      uint32_t gicd_ctlr;
> +    uint32_t gicd_typer;

What's this for ? You shouldn't need to be adding new distributor
state fields.

>      uint32_t gicd_statusr[2];
>      GIC_DECLARE_BITMAP(group);        /* GICD_IGROUPR */
>      GIC_DECLARE_BITMAP(grpmod);       /* GICD_IGRPMODR */
> diff --git a/include/hw/intc/arm_gicv3_its_common.h b/include/hw/intc/arm_gicv3_its_common.h
> index 08bc5652ed..b7eca57221 100644
> --- a/include/hw/intc/arm_gicv3_its_common.h
> +++ b/include/hw/intc/arm_gicv3_its_common.h
> @@ -43,6 +43,30 @@
>
>  #define GITS_PIDR2       0xFFE8
>
> +typedef struct {
> +    bool valid;
> +    bool indirect;
> +    uint16_t entry_sz;
> +    uint32_t max_entries;
> +    uint32_t max_devids;
> +    uint64_t base_addr;
> +} DevTableDesc;
> +
> +typedef struct {
> +    bool valid;
> +    bool indirect;
> +    uint16_t entry_sz;
> +    uint32_t max_entries;
> +    uint32_t max_collids;
> +    uint64_t base_addr;
> +} CollTableDesc;
> +
> +typedef struct {
> +    bool valid;
> +    uint32_t max_entries;
> +    uint64_t base_addr;
> +} CmdQDesc;
> +
>  struct GICv3ITSState {
>      SysBusDevice parent_obj;
>
> @@ -66,6 +90,10 @@ struct GICv3ITSState {
>      uint64_t creadr;
>      uint64_t baser[8];
>
> +    DevTableDesc  dt;
> +    CollTableDesc ct;
> +    CmdQDesc      cq;


> +
>      Error *migration_blocker;
>  };
>
> --
> 2.27.0

thanks
-- PMM


^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH v2 3/8] hw/intc: GICv3 ITS command queue framework
  2021-04-01  2:41 ` [PATCH v2 3/8] hw/intc: GICv3 ITS command queue framework Shashi Mallela
@ 2021-04-19 10:30   ` Peter Maydell
  2021-04-29 23:38     ` shashi.mallela
  0 siblings, 1 reply; 25+ messages in thread
From: Peter Maydell @ 2021-04-19 10:30 UTC (permalink / raw)
  To: Shashi Mallela
  Cc: Leif Lindholm, QEMU Developers, qemu-arm, Radoslaw Biernacki

On Thu, 1 Apr 2021 at 03:41, Shashi Mallela <shashi.mallela@linaro.org> wrote:
>
> Added functionality to trigger ITS command queue processing on
> write to CWRITE register and process each command queue entry to
> identify the command type and handle commands like MAPD,MAPC,SYNC.
>
> Signed-off-by: Shashi Mallela <shashi.mallela@linaro.org>
> ---
>  hw/intc/arm_gicv3_its.c  | 347 +++++++++++++++++++++++++++++++++++++++
>  hw/intc/gicv3_internal.h |  41 +++++
>  2 files changed, 388 insertions(+)
>
> diff --git a/hw/intc/arm_gicv3_its.c b/hw/intc/arm_gicv3_its.c
> index 81373f049d..fcac33c836 100644
> --- a/hw/intc/arm_gicv3_its.c
> +++ b/hw/intc/arm_gicv3_its.c
> @@ -28,6 +28,347 @@ struct GICv3ITSClass {
>      void (*parent_reset)(DeviceState *dev);
>  };
>
> +static MemTxResult process_sync(GICv3ITSState *s, uint32_t offset)
> +{
> +    AddressSpace *as = &s->gicv3->sysmem_as;
> +    uint64_t rdbase;
> +    uint64_t value;
> +    bool pta = false;
> +    MemTxResult res = MEMTX_OK;
> +
> +    offset += NUM_BYTES_IN_DW;
> +    offset += NUM_BYTES_IN_DW;
> +
> +    value = address_space_ldq_le(as, s->cq.base_addr + offset,
> +                                     MEMTXATTRS_UNSPECIFIED, &res);

If the ITS fails to read or write DMA, it does not report that
by provoking a bus error for the CPU's write to GITS_CWRITER,
which is what will happen if you just propagate up the MemTxResult
from this DMA operation back through the register write function
to its caller. The spec doesn't seem to define a particular behaviour
for "I couldn't read the command out of the command queue", but
it would seem sensible to pick one of the options from the "Command errors"
section: ignore the command, or stall the command queue ("treat as
valid" is the other option there but doesn't seem relevant here).

"Stall" is probably best as otherwise we might loop forever through
a region of unreadable addresses.

> +    if (FIELD_EX64(s->typer, GITS_TYPER, PTA)) {
> +        /*
> +         * only bits[47:16] are considered instead of bits [51:16]
> +         * since with a physical address the target address must be
> +         * 64KB aligned
> +         */
> +        rdbase = (value >> RDBASE_OFFSET) & RDBASE_MASK;
> +        pta = true;
> +    } else {
> +        rdbase = (value >> RDBASE_OFFSET) & RDBASE_PROCNUM_MASK;
> +    }
> +
> +    if (!pta && (rdbase < (s->gicv3->num_cpu))) {
> +        /*
> +         * Current implementation makes a blocking synchronous call
> +         * for every command issued earlier,hence the internal state
> +         * is already consistent by the time SYNC command is executed.
> +         */
> +    }
> +
> +    offset += NUM_BYTES_IN_DW;
> +    return res;
> +}

TODO: review process_mapd and process_mapc

> +
> +static void update_cte(GICv3ITSState *s, uint16_t icid, uint64_t cte)
> +{
> +    AddressSpace *as = &s->gicv3->sysmem_as;
> +    uint64_t value;
> +    uint8_t  page_sz_type;
> +    uint64_t l2t_addr;
> +    bool valid_l2t;
> +    uint32_t l2t_id;
> +    uint32_t page_sz = 0;
> +    uint32_t max_l2_entries;

I think it's worth having a comment here:

/*
 * The specification defines the format of level 1 entries of a
 * 2-level table, but the format of level 2 entries and the format
 * of flat-mapped tables is IMPDEF.
 */

Q: why have you chosen to make the level-2 and flatmap entries
64 bits here ? They only need to contain the valid bit plus a
16 bit processor number. (We know the processor number fits in 16
bits because the GICR_TYPER.Processor_Number field in the redistributor
is that large.) Is there something I'm missing in the spec that makes
64-bit entries a good choice anyway ?

> +
> +    if (s->ct.indirect) {
> +        /* 2 level table */
> +        page_sz_type = FIELD_EX64(s->baser[1], GITS_BASER, PAGESIZE);
> +
> +        if (page_sz_type == 0) {
> +            page_sz = GITS_ITT_PAGE_SIZE_0;
> +        } else if (page_sz_type == 1) {
> +            page_sz = GITS_ITT_PAGE_SIZE_1;
> +        } else if (page_sz_type == 2) {
> +            page_sz = GITS_ITT_PAGE_SIZE_2;
> +        }

page_sz_type == 3 has a defined meaning: must be treated like 2.

If we're caching stuff in s->ct, maybe we should cache page_sz too ?

> +
> +        l2t_id = icid / (page_sz / L1TABLE_ENTRY_SIZE);
> +
> +        value = address_space_ldq_le(as,
> +                                     s->ct.base_addr +
> +                                     (l2t_id * L1TABLE_ENTRY_SIZE),
> +                                     MEMTXATTRS_UNSPECIFIED, NULL);
> +
> +        valid_l2t = (value >> VALID_SHIFT) & VALID_MASK;
> +
> +        if (valid_l2t) {
> +            max_l2_entries = page_sz / s->ct.entry_sz;
> +
> +            l2t_addr = (value >> page_sz_type) &
> +                        ((1ULL << (51 - page_sz_type)) - 1);

The spec text could be more clearly worded, but I don't think this is how
the physaddr is encoded in the L1 descriptor. I think that the intent is
that bits [51:0] are the physical address, with bits [N-1:0] of that being 0
because the L2 table must be page aligned. Your intepretation (that the
physaddr bit 0 is in bit N of the descriptor) wouldn't allow L2 tables
to be anywhere in the address space.

> +
> +            address_space_write(as, l2t_addr +
> +                                 ((icid % max_l2_entries) * GITS_CTE_SIZE),
> +                                 MEMTXATTRS_UNSPECIFIED,
> +                                 &cte, sizeof(cte));

Writing the CTE word using address_space_write() like this will
do the wrong thing on big-endian hosts. address_space_write() is the
"write a string of bytes to guest memory" function, so it will write
the uint64_t in whatever the host's byte order is. You want a fixed
byte order, so use address_space_ldq_le() or similar.

> +        }

> +    } else {
> +        /* Flat level table */
> +        address_space_write(as, s->ct.base_addr + (icid * GITS_CTE_SIZE),
> +                            MEMTXATTRS_UNSPECIFIED, &cte,
> +                            sizeof(cte));
> +    }
> +}

I feel like there's a fair amount of code duplication between
get_cte(), get_dte(), update_cte() and update_dte() which could
perhaps be refactored out.

> +static MemTxResult process_mapc(GICv3ITSState *s, uint32_t offset)
> +{
> +    AddressSpace *as = &s->gicv3->sysmem_as;
> +    uint16_t icid;
> +    uint64_t rdbase;
> +    bool valid;
> +    bool pta = false;
> +    MemTxResult res = MEMTX_OK;
> +    uint64_t cte_entry;
> +    uint64_t value;
> +
> +    offset += NUM_BYTES_IN_DW;
> +    offset += NUM_BYTES_IN_DW;
> +
> +    value = address_space_ldq_le(as, s->cq.base_addr + offset,
> +                                 MEMTXATTRS_UNSPECIFIED, &res);
> +
> +    icid = value & ICID_MASK;
> +
> +    if (FIELD_EX64(s->typer, GITS_TYPER, PTA)) {
> +        /*
> +         * only bits[47:16] are considered instead of bits [51:16]
> +         * since with a physical address the target address must be
> +         * 64KB aligned
> +         */
> +        rdbase = (value >> RDBASE_OFFSET) & RDBASE_MASK;
> +        pta = true;
> +    } else {
> +        rdbase = (value >> RDBASE_OFFSET) & RDBASE_PROCNUM_MASK;
> +    }

GITS_TYPER.PTA is a read-only bit where we report to the guest how
our implementation behaves (ie whether these are processor numbers
or redistributor base addresses). We should implement one thing,
whichever is the easy one to implement for us, not both.

The specification is giving implementations a choice here because
there are different ways an ITS-to-redistributor interface might work:
 * the ITS might talk to the redistributor by literally writing to
   its registers via DMA (in which case setting GITS_TYPER.PTA makes
   its life easier)
 * the ITS might have its own backdoor or internal communications
   channel to the redistributors (in which case it wants to know a
   CPU number)

Looking forward, in patch 6 you add a comment:
  * Current implementation only supports rdbase == procnum
  * Hence rdbase physical address is ignored

So we're the second type of implementation. We should set
GITS_TYPER.PTA=0, and then this code can simply assume that
the data is always the "it's a CPU number" format.

> +
> +    valid = (value >> VALID_SHIFT) & VALID_MASK;
> +
> +    if (valid) {
> +        if ((icid > s->ct.max_collids) || (!pta &&
> +                (rdbase > s->gicv3->num_cpu))) {
> +            if (FIELD_EX64(s->typer, GITS_TYPER, SEIS)) {
> +                /* Generate System Error here if supported */
> +            }

Again, GITS_TYPER.SEIS is a bit which we use to tell the guest whether
we're going to generate system errors or not; it's not a bit which
we read in order to decide whether to generate them. For QEMU, the
CPU itself doesn't have system-error support properly yet, so for
us SEIS should be zero. In the ITS code we thus have two choices:
 (1) just don't put anything in the code
 (2) add a function call to an empty function called something
     like its_raise_serror() which just has a comment noting that
     it's a placeholder to mark where we would need to raise an SError
     if we wanted to support it

I think I would favour option 1.

> +            qemu_log_mask(LOG_GUEST_ERROR,
> +                "%s: invalid collection table attributes "
> +                "icid %d rdbase %lu\n", __func__, icid, rdbase);

This kind of LOG_GUEST_ERROR logging doesn't need to include the
__func__ string:
saying "ITS MAPC command" is more comprehensible to a reader of the
log who doesn't necessarily want to look into the QEMU source code
than saying "process_mapc".

> +            /*
> +             * in this implementation,in case of error
> +             * we ignore this command and move onto the next
> +             * command in the queue
> +             */
> +        } else {
> +            if (s->ct.valid) {
> +                /* add mapping entry to collection table */
> +                cte_entry = (valid & VALID_MASK) |
> +                            (pta ? ((rdbase & RDBASE_MASK) << 1ULL) :
> +                            ((rdbase & RDBASE_PROCNUM_MASK) << 1ULL));
> +
> +                update_cte(s, icid, cte_entry);
> +            }

Put the s->ct.valid check inside update_cte(). Pass the
various parameters -- valid, pta, rdbase -- to update_cte()
and have it do the assembly into a word. That way all the code
dealing with the specific format of the table is inside that function,
rather than being partly here and partly there.

> +        }
> +    } else {
> +        if (s->ct.valid) {
> +            /* remove mapping entry from collection table */
> +            cte_entry = 0;
> +
> +            update_cte(s, icid, cte_entry);


> +        }
> +    }
> +
> +    offset += NUM_BYTES_IN_DW;
> +    offset += NUM_BYTES_IN_DW;
> +
> +    return res;
> +}
> +
> +static void update_dte(GICv3ITSState *s, uint32_t devid, uint64_t dte)
> +{
> +    AddressSpace *as = &s->gicv3->sysmem_as;
> +    uint64_t value;
> +    uint8_t  page_sz_type;
> +    uint64_t l2t_addr;
> +    bool valid_l2t;
> +    uint32_t l2t_id;
> +    uint32_t page_sz = 0;
> +    uint32_t max_l2_entries;
> +
> +    if (s->dt.indirect) {
> +        /* 2 level table */
> +        page_sz_type = FIELD_EX64(s->baser[0], GITS_BASER, PAGESIZE);
> +
> +        if (page_sz_type == 0) {
> +            page_sz = GITS_ITT_PAGE_SIZE_0;
> +        } else if (page_sz_type == 1) {
> +            page_sz = GITS_ITT_PAGE_SIZE_1;
> +        } else if (page_sz_type == 2) {
> +            page_sz = GITS_ITT_PAGE_SIZE_2;
> +        }
> +
> +        l2t_id = devid / (page_sz / L1TABLE_ENTRY_SIZE);
> +
> +        value = address_space_ldq_le(as,
> +                                     s->dt.base_addr +
> +                                     (l2t_id * L1TABLE_ENTRY_SIZE),
> +                                     MEMTXATTRS_UNSPECIFIED, NULL);
> +
> +        valid_l2t = (value >> VALID_SHIFT) & VALID_MASK;
> +
> +        if (valid_l2t) {
> +            max_l2_entries = page_sz / s->dt.entry_sz;
> +
> +            l2t_addr = (value >> page_sz_type) &
> +                        ((1ULL << (51 - page_sz_type)) - 1);
> +
> +            address_space_write(as, l2t_addr +
> +                                 ((devid % max_l2_entries) * GITS_DTE_SIZE),
> +                                 MEMTXATTRS_UNSPECIFIED, &dte, sizeof(dte));
> +        }
> +    } else {
> +        /* Flat level table */
> +        address_space_write(as, s->dt.base_addr + (devid * GITS_DTE_SIZE),
> +                            MEMTXATTRS_UNSPECIFIED, &dte, sizeof(dte));
> +    }
> +}
> +
> +static MemTxResult process_mapd(GICv3ITSState *s, uint64_t value,
> +                                 uint32_t offset)
> +{
> +    AddressSpace *as = &s->gicv3->sysmem_as;
> +    uint32_t devid;
> +    uint8_t size;
> +    uint64_t itt_addr;
> +    bool valid;
> +    MemTxResult res = MEMTX_OK;
> +    uint64_t dte_entry = 0;
> +
> +    devid = (value >> DEVID_OFFSET) & DEVID_MASK;
> +
> +    offset += NUM_BYTES_IN_DW;
> +    value = address_space_ldq_le(as, s->cq.base_addr + offset,
> +                                 MEMTXATTRS_UNSPECIFIED, &res);
> +    size = (value & SIZE_MASK);
> +
> +    offset += NUM_BYTES_IN_DW;
> +    value = address_space_ldq_le(as, s->cq.base_addr + offset,
> +                                 MEMTXATTRS_UNSPECIFIED, &res);
> +    itt_addr = (value >> ITTADDR_OFFSET) & ITTADDR_MASK;
> +
> +    valid = (value >> VALID_SHIFT) & VALID_MASK;
> +
> +    if (valid) {
> +        if ((devid > s->dt.max_devids) ||
> +            (size > FIELD_EX64(s->typer, GITS_TYPER, IDBITS))) {
> +            if (FIELD_EX64(s->typer, GITS_TYPER, SEIS)) {
> +                /* Generate System Error here if supported */
> +            }
> +            qemu_log_mask(LOG_GUEST_ERROR,
> +                "%s: invalid device table attributes "
> +                "devid %d or size %d\n", __func__, devid, size);
> +            /*
> +             * in this implementation,in case of error
> +             * we ignore this command and move onto the next
> +             * command in the queue
> +             */
> +        } else {
> +            if (s->dt.valid) {
> +                /* add mapping entry to device table */
> +                dte_entry = (valid & VALID_MASK) |
> +                            ((size & SIZE_MASK) << 1U) |
> +                            ((itt_addr & ITTADDR_MASK) << 6ULL);
> +
> +                update_dte(s, devid, dte_entry);
> +            }
> +        }
> +    } else {
> +        if (s->dt.valid) {
> +            /* remove mapping entry from device table */
> +            dte_entry = 0;
> +            update_dte(s, devid, dte_entry);
> +        }
> +    }
> +
> +    offset += NUM_BYTES_IN_DW;
> +    offset += NUM_BYTES_IN_DW;
> +
> +    return res;
> +}

Review comments for process_mapc() and update_cte() apply also to
process_mapd() and update_dte().

> +
> +/*
> + * Current implementation blocks until all
> + * commands are processed
> + */
> +static MemTxResult process_cmdq(GICv3ITSState *s)

This should return 'void' (see comments earlier).

> +{
> +    uint32_t wr_offset = 0;
> +    uint32_t rd_offset = 0;
> +    uint32_t cq_offset = 0;
> +    uint64_t data;
> +    AddressSpace *as = &s->gicv3->sysmem_as;
> +    MemTxResult res = MEMTX_OK;
> +    uint8_t cmd;
> +
> +    wr_offset = FIELD_EX64(s->cwriter, GITS_CWRITER, OFFSET);
> +
> +    if (wr_offset > s->cq.max_entries) {
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +                        "%s: invalid write offset "
> +                        "%d\n", __func__, wr_offset);
> +        res = MEMTX_ERROR;
> +        return res;
> +    }
> +
> +    rd_offset = FIELD_EX64(s->creadr, GITS_CREADR, OFFSET);
> +
> +    while (wr_offset != rd_offset) {
> +        cq_offset = (rd_offset * GITS_CMDQ_ENTRY_SIZE);
> +        data = address_space_ldq_le(as, s->cq.base_addr + cq_offset,
> +                                      MEMTXATTRS_UNSPECIFIED, &res);

I think I would just read all four quadwords of the command here
into a local uint64_t data[4], and pass that to the process_$cmd()
functions. That saves those functions doing the loads and handling
possible errors doing the loads themselves.

> +        cmd = (data & CMD_MASK);
> +
> +        switch (cmd) {
> +        case GITS_CMD_INT:
> +            break;
> +        case GITS_CMD_CLEAR:
> +            break;
> +        case GITS_CMD_SYNC:
> +            res = process_sync(s, cq_offset);
> +            break;
> +        case GITS_CMD_MAPD:
> +            res = process_mapd(s, data, cq_offset);
> +            break;
> +        case GITS_CMD_MAPC:
> +            res = process_mapc(s, cq_offset);
> +            break;
> +        case GITS_CMD_MAPTI:
> +            break;
> +        case GITS_CMD_MAPI:
> +            break;
> +        case GITS_CMD_DISCARD:
> +            break;
> +        default:
> +            break;
> +        }
> +        if (res == MEMTX_OK) {
> +            rd_offset++;
> +            rd_offset %= s->cq.max_entries;
> +            s->creadr = FIELD_DP64(s->creadr, GITS_CREADR, OFFSET, rd_offset);
> +        } else {
> +            qemu_log_mask(LOG_GUEST_ERROR,
> +                "%s: %x cmd processing failed!!\n", __func__, cmd);
> +            break;
> +        }
> +    }
> +    return res;
> +}
> +
>  static bool extract_table_params(GICv3ITSState *s, int index)
>  {
>      uint16_t num_pages = 0;
> @@ -282,6 +623,9 @@ static MemTxResult its_writel(GICv3ITSState *s, hwaddr offset,
>          break;
>      case GITS_CWRITER:
>          s->cwriter = deposit64(s->cwriter, 0, 32, value);
> +        if ((s->ctlr & ITS_CTLR_ENABLED) && (s->cwriter != s->creadr)) {
> +            result = process_cmdq(s);
> +        }
>          break;
>      case GITS_CWRITER + 4:
>          s->cwriter = deposit64(s->cwriter, 32, 32, value);
> @@ -416,6 +760,9 @@ static MemTxResult its_writell(GICv3ITSState *s, hwaddr offset,
>          break;
>      case GITS_CWRITER:
>          s->cwriter = value;
> +        if ((s->ctlr & ITS_CTLR_ENABLED) && (s->cwriter != s->creadr)) {
> +            result = process_cmdq(s);
> +        }


I would move the check on CTLR.ENABLED into process_cmdq() itself
(which is already doing a writer != reader check).

>          break;
>      case GITS_TYPER:
>      case GITS_CREADR:
> diff --git a/hw/intc/gicv3_internal.h b/hw/intc/gicv3_internal.h
> index b7701e8ca5..7e1ff426ef 100644
> --- a/hw/intc/gicv3_internal.h
> +++ b/hw/intc/gicv3_internal.h
> @@ -253,6 +253,12 @@ FIELD(GITS_CBASER, OUTERCACHE, 53, 3)
>  FIELD(GITS_CBASER, INNERCACHE, 59, 3)
>  FIELD(GITS_CBASER, VALID, 63, 1)
>
> +FIELD(GITS_CREADR, STALLED, 0, 1)
> +FIELD(GITS_CREADR, OFFSET, 5, 15)
> +
> +FIELD(GITS_CWRITER, RETRY, 0, 1)
> +FIELD(GITS_CWRITER, OFFSET, 5, 15)
> +
>  FIELD(GITS_CTLR, ENABLED, 0, 1)
>  FIELD(GITS_CTLR, QUIESCENT, 31, 1)
>
> @@ -284,6 +290,41 @@ FIELD(GITS_TYPER, CIL, 36, 1)
>  #define L1TABLE_ENTRY_SIZE         8
>
>  #define GITS_CMDQ_ENTRY_SIZE               32
> +#define NUM_BYTES_IN_DW                     8
> +
> +#define CMD_MASK                  0xff
> +
> +/* ITS Commands */
> +#define GITS_CMD_CLEAR            0x04
> +#define GITS_CMD_DISCARD          0x0F
> +#define GITS_CMD_INT              0x03
> +#define GITS_CMD_MAPC             0x09
> +#define GITS_CMD_MAPD             0x08
> +#define GITS_CMD_MAPI             0x0B
> +#define GITS_CMD_MAPTI            0x0A
> +#define GITS_CMD_SYNC             0x05
> +
> +/* MAPC command fields */
> +#define ICID_LEN                  16
> +#define ICID_MASK                 ((1U << ICID_LEN) - 1)
> +#define RDBASE_LEN                32
> +#define RDBASE_OFFSET             16
> +#define RDBASE_MASK               ((1ULL << RDBASE_LEN) - 1)

If you want to define these by hand, you could at least use the same
suffixes that the FIELD() macro does: _SHIFT, _LENGTH and _MASK.

> +#define RDBASE_PROCNUM_LEN        16
> +#define RDBASE_PROCNUM_MASK       ((1ULL << RDBASE_PROCNUM_LEN) - 1)
> +
> +#define DEVID_OFFSET              32
> +#define DEVID_LEN                 32
> +#define DEVID_MASK                ((1ULL << DEVID_LEN) - 1)
> +
> +/* MAPD command fields */
> +#define ITTADDR_LEN               44
> +#define ITTADDR_OFFSET            8
> +#define ITTADDR_MASK              ((1ULL << ITTADDR_LEN) - 1)
> +#define SIZE_MASK                 0x1f
> +
> +#define VALID_SHIFT               63
> +#define VALID_MASK                0x1
>
>  /**
>   * Default features advertised by this version of ITS
> --
> 2.27

thanks
-- PMM


^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH v2 4/8] hw/intc: GICv3 ITS Command processing
  2021-04-01  2:41 ` [PATCH v2 4/8] hw/intc: GICv3 ITS Command processing Shashi Mallela
@ 2021-04-19 10:39   ` Peter Maydell
  0 siblings, 0 replies; 25+ messages in thread
From: Peter Maydell @ 2021-04-19 10:39 UTC (permalink / raw)
  To: Shashi Mallela
  Cc: Leif Lindholm, QEMU Developers, qemu-arm, Radoslaw Biernacki

On Thu, 1 Apr 2021 at 03:41, Shashi Mallela <shashi.mallela@linaro.org> wrote:
>
> Added ITS command queue handling for MAPTI,MAPI commands,handled ITS
> translation which triggers an LPI via INT command as well as write
> to GITS_TRANSLATER register,defined enum to differentiate between ITS
> command interrupt trigger and GITS_TRANSLATER based interrupt trigger.
> Each of these commands make use of other functionalities implemented to
> get device table entry,collection table entry or interrupt translation
> table entry required for their processing.
>
> Signed-off-by: Shashi Mallela <shashi.mallela@linaro.org>
> ---
>  hw/intc/arm_gicv3_its.c            | 360 ++++++++++++++++++++++++++++-
>  hw/intc/gicv3_internal.h           |   7 +
>  include/hw/intc/arm_gicv3_common.h |   2 +
>  3 files changed, 368 insertions(+), 1 deletion(-)
>
> diff --git a/hw/intc/arm_gicv3_its.c b/hw/intc/arm_gicv3_its.c
> index fcac33c836..0e3f176809 100644
> --- a/hw/intc/arm_gicv3_its.c
> +++ b/hw/intc/arm_gicv3_its.c
> @@ -28,6 +28,152 @@ struct GICv3ITSClass {
>      void (*parent_reset)(DeviceState *dev);
>  };
>
> +typedef enum ItsCmdType {
> +    NONE = 0, /* internal indication for GITS_TRANSLATER write */
> +    CLEAR = 1,
> +    DISCARD = 2,
> +    INT = 3,
> +} ItsCmdType;
> +
> +static bool get_cte(GICv3ITSState *s, uint16_t icid, uint64_t *cte)
> +{
> +    AddressSpace *as = &s->gicv3->sysmem_as;
> +    uint8_t  page_sz_type;
> +    uint64_t l2t_addr;
> +    uint64_t value;
> +    bool valid_l2t;
> +    uint32_t l2t_id;
> +    uint32_t page_sz = 0;
> +    uint32_t max_l2_entries;
> +    bool status = false;
> +
> +    if (s->ct.indirect) {
> +        /* 2 level table */
> +        page_sz_type = FIELD_EX64(s->baser[1], GITS_BASER, PAGESIZE);
> +
> +        if (page_sz_type == 0) {
> +            page_sz = GITS_ITT_PAGE_SIZE_0;
> +        } else if (page_sz_type == 1) {
> +            page_sz = GITS_ITT_PAGE_SIZE_1;
> +        } else if (page_sz_type == 2) {
> +            page_sz = GITS_ITT_PAGE_SIZE_2;
> +        }
> +
> +        l2t_id = icid / (page_sz / L1TABLE_ENTRY_SIZE);
> +
> +        value = address_space_ldq_le(as,
> +                                     s->ct.base_addr +
> +                                     (l2t_id * L1TABLE_ENTRY_SIZE),
> +                                     MEMTXATTRS_UNSPECIFIED, NULL);
> +
> +        valid_l2t = (value >> VALID_SHIFT) & VALID_MASK;
> +
> +        if (valid_l2t) {
> +            max_l2_entries = page_sz / s->ct.entry_sz;
> +
> +            l2t_addr = (value >> page_sz_type) &
> +                        ((1ULL << (51 - page_sz_type)) - 1);
> +
> +            address_space_read(as, l2t_addr +
> +                                 ((icid % max_l2_entries) * GITS_CTE_SIZE),
> +                                 MEMTXATTRS_UNSPECIFIED,
> +                                 cte, sizeof(*cte));
> +       }
> +    } else {
> +        /* Flat level table */
> +        address_space_read(as, s->ct.base_addr + (icid * GITS_CTE_SIZE),
> +                            MEMTXATTRS_UNSPECIFIED, cte,
> +                            sizeof(*cte));
> +    }
> +
> +    if (*cte & VALID_MASK) {
> +        status = true;
> +    }
>

Most of the remarks about update_cte() apply here too (including
the endianness issue).

+
> +    return status;
> +}
> +
> +static bool get_ite(GICv3ITSState *s, uint32_t eventid, uint64_t dte,
> +                      uint16_t *icid, uint32_t *pIntid)
> +{
> +    AddressSpace *as = &s->gicv3->sysmem_as;
> +    uint8_t buff[ITS_ITT_ENTRY_SIZE];
> +    uint64_t itt_addr;
> +    bool status = false;
> +
> +    itt_addr = (dte >> 6ULL) & ITTADDR_MASK;
> +    itt_addr <<= ITTADDR_OFFSET; /* 256 byte aligned */
> +
> +    address_space_read(as, itt_addr + (eventid * sizeof(buff)),
> +                MEMTXATTRS_UNSPECIFIED, &buff,
> +                sizeof(buff));
> +
> +    if (buff[0] & VALID_MASK) {
> +        if ((buff[0] >> 1U) & GITS_TYPE_PHYSICAL) {
> +            memcpy(pIntid, &buff[1], 3);
> +            memcpy(icid, &buff[7], sizeof(*icid));

You can't memcpy() values from guest memory into host variables
like this: it will give the wrong results on big-endian hosts.

> +            status = true;
> +        }
> +    }
> +
> +    return status;
> +}
> +
> +static uint64_t get_dte(GICv3ITSState *s, uint32_t devid)
> +{
> +    AddressSpace *as = &s->gicv3->sysmem_as;
> +    uint8_t  page_sz_type;
> +    uint64_t l2t_addr;
> +    uint64_t value;
> +    bool valid_l2t;
> +    uint32_t l2t_id;
> +    uint32_t page_sz = 0;
> +    uint32_t max_l2_entries;
> +
> +    if (s->dt.indirect) {
> +        /* 2 level table */
> +        page_sz_type = FIELD_EX64(s->baser[0], GITS_BASER, PAGESIZE);
> +
> +        if (page_sz_type == 0) {
> +            page_sz = GITS_ITT_PAGE_SIZE_0;
> +        } else if (page_sz_type == 1) {
> +            page_sz = GITS_ITT_PAGE_SIZE_1;
> +        } else if (page_sz_type == 2) {
> +            page_sz = GITS_ITT_PAGE_SIZE_2;
> +        }
> +
> +        l2t_id = devid / (page_sz / L1TABLE_ENTRY_SIZE);
> +
> +        value = address_space_ldq_le(as,
> +                                     s->dt.base_addr +
> +                                     (l2t_id * L1TABLE_ENTRY_SIZE),
> +                                     MEMTXATTRS_UNSPECIFIED, NULL);
> +
> +        valid_l2t = (value >> VALID_SHIFT) & VALID_MASK;
> +
> +        if (valid_l2t) {
> +            max_l2_entries = page_sz / s->dt.entry_sz;
> +
> +            l2t_addr = (value >> page_sz_type) &
> +                        ((1ULL << (51 - page_sz_type)) - 1);
> +
> +            value = 0;
> +            address_space_read(as, l2t_addr +
> +                                 ((devid % max_l2_entries) * GITS_DTE_SIZE),
> +                                 MEMTXATTRS_UNSPECIFIED,
> +                                 &value, sizeof(value));
> +        }
> +    } else {
> +        /* Flat level table */
> +        value = 0;
> +        address_space_read(as, s->dt.base_addr + (devid * GITS_DTE_SIZE),
> +                            MEMTXATTRS_UNSPECIFIED, &value,
> +                            sizeof(value));
> +    }
> +
> +    return value;
> +}
> +
>  static MemTxResult process_sync(GICv3ITSState *s, uint32_t offset)
>  {
>      AddressSpace *as = &s->gicv3->sysmem_as;
> @@ -66,6 +212,187 @@ static MemTxResult process_sync(GICv3ITSState *s, uint32_t offset)
>      return res;
>  }
>
> +static MemTxResult process_int(GICv3ITSState *s, uint64_t value,
> +                                uint32_t offset, ItsCmdType cmd)
> +{
> +    AddressSpace *as = &s->gicv3->sysmem_as;
> +    uint32_t devid, eventid;
> +    MemTxResult res = MEMTX_OK;
> +    bool dte_valid;
> +    uint64_t dte = 0;
> +    uint32_t max_eventid;
> +    uint16_t icid = 0;
> +    uint32_t pIntid = 0;
> +    bool ite_valid = false;
> +    uint64_t cte = 0;
> +    bool cte_valid = false;
> +    uint8_t buff[ITS_ITT_ENTRY_SIZE];
> +    uint64_t itt_addr;
> +
> +    if (cmd == NONE) {
> +        devid = offset;
> +    } else {
> +        devid = (value >> DEVID_OFFSET) & DEVID_MASK;
> +
> +        offset += NUM_BYTES_IN_DW;
> +        value = address_space_ldq_le(as, s->cq.base_addr + offset,
> +                                 MEMTXATTRS_UNSPECIFIED, &res);
> +    }
> +
> +    eventid = (value & EVENTID_MASK);
> +
> +    dte = get_dte(s, devid);
> +    dte_valid = dte & VALID_MASK;
> +
> +    if (dte_valid) {
> +        max_eventid = (1UL << (((dte >> 1U) & SIZE_MASK) + 1));
> +
> +        ite_valid = get_ite(s, eventid, dte, &icid, &pIntid);
> +
> +        if (ite_valid) {
> +            cte_valid = get_cte(s, icid, &cte);
> +        }
> +    }
> +
> +    if ((devid > s->dt.max_devids) || !dte_valid || !ite_valid ||
> +            !cte_valid || (eventid > max_eventid)) {
> +        if (FIELD_EX64(s->typer, GITS_TYPER, SEIS)) {
> +            /*
> +             * Generate System Error here if supported
> +             * for each of the individual error cases
> +             */
> +        }
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +            "%s: invalid interrupt translation table attributes "
> +            "devid %d or eventid %d\n",
> +            __func__, devid, eventid);
> +        /*
> +         * in this implementation,in case of error
> +         * we ignore this command and move onto the next
> +         * command in the queue
> +         */
> +    } else {
> +        if (FIELD_EX64(s->typer, GITS_TYPER, PTA)) {
> +            /*
> +             * only bits[47:16] are considered instead of bits [51:16]
> +             * since with a physical address the target address must be
> +             * 64KB aligned
> +             */
> +
> +            /*
> +             * Current implementation only supports rdbase == procnum
> +             * Hence rdbase physical address is ignored
> +             */

As noted earlier, just delete this and write code which assumes PTA=0.

> +        } else {
> +
> +            if (cmd == DISCARD) {
> +                /* remove mapping from interrupt translation table */
> +                memset(buff, 0, sizeof(buff));
> +
> +                itt_addr = (dte >> 6ULL) & ITTADDR_MASK;
> +                itt_addr <<= ITTADDR_OFFSET; /* 256 byte aligned */
> +
> +                address_space_write(as, itt_addr + (eventid * sizeof(buff)),
> +                                    MEMTXATTRS_UNSPECIFIED, &buff,
> +                                    sizeof(buff));

Please abstract this out into a function so that the code dealing
with the format of the ITT is not mixed in with this function.

> +            }
> +        }
> +    }
> +
> +    if (cmd != NONE) {
> +        offset += NUM_BYTES_IN_DW;
> +        offset += NUM_BYTES_IN_DW;
> +    }
> +
> +    return res;
> +}
> +
> +static MemTxResult process_mapti(GICv3ITSState *s, uint64_t value,
> +                                    uint32_t offset, bool ignore_pInt)
> +{
> +    AddressSpace *as = &s->gicv3->sysmem_as;
> +    uint32_t devid, eventid;
> +    uint32_t pIntid = 0;
> +    uint32_t max_eventid, max_Intid;
> +    bool dte_valid;
> +    MemTxResult res = MEMTX_OK;
> +    uint16_t icid = 0;
> +    uint64_t dte = 0;
> +    uint64_t itt_addr;
> +    uint8_t buff[ITS_ITT_ENTRY_SIZE];
> +    uint32_t int_spurious = INTID_SPURIOUS;
> +
> +    devid = (value >> DEVID_OFFSET) & DEVID_MASK;
> +    offset += NUM_BYTES_IN_DW;
> +    value = address_space_ldq_le(as, s->cq.base_addr + offset,
> +                                 MEMTXATTRS_UNSPECIFIED, &res);
> +
> +    eventid = (value & EVENTID_MASK);
> +
> +    if (!ignore_pInt) {
> +        pIntid = (value >> pINTID_OFFSET) & pINTID_MASK;
> +    }
> +
> +    offset += NUM_BYTES_IN_DW;
> +    value = address_space_ldq_le(as, s->cq.base_addr + offset,
> +                                 MEMTXATTRS_UNSPECIFIED, &res);
> +
> +    icid = value & ICID_MASK;
> +
> +    dte = get_dte(s, devid);
> +    dte_valid = dte & VALID_MASK;
> +
> +    max_eventid = (1UL << (((dte >> 1U) & SIZE_MASK) + 1));
> +
> +    if (!ignore_pInt) {
> +        max_Intid = (1UL << (FIELD_EX64(s->typer, GITS_TYPER, IDBITS) + 1));
> +    }
> +
> +    if ((devid > s->dt.max_devids) || (icid > s->ct.max_collids) ||
> +            !dte_valid || (eventid > max_eventid) ||
> +            (!ignore_pInt && ((pIntid < GICV3_LPI_INTID_START) ||
> +               (pIntid > max_Intid)))) {
> +        if (FIELD_EX64(s->typer, GITS_TYPER, SEIS)) {
> +            /*
> +             * Generate System Error here if supported
> +             * for each of the individual error cases
> +             */
> +        }
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +            "%s: invalid interrupt translation table attributes "
> +            "devid %d or icid %d or eventid %d or pIntid %d\n",
> +            __func__, devid, icid, eventid, pIntid);
> +        /*
> +         * in this implementation,in case of error
> +         * we ignore this command and move onto the next
> +         * command in the queue
> +         */
> +    } else {
> +        /* add entry to interrupt translation table */
> +        memset(buff, 0, sizeof(buff));
> +        buff[0] = (dte_valid & VALID_MASK) | (GITS_TYPE_PHYSICAL << 1U);
> +        if (ignore_pInt) {
> +            memcpy(&buff[1], &eventid, 3);
> +        } else {
> +            memcpy(&buff[1], &pIntid, 3);
> +        }
> +        memcpy(&buff[4], &int_spurious, 3);
> +        memcpy(&buff[7], &icid, sizeof(icid));

Memcpy of host variables into guest memory isn't endian-agnostic.

> +
> +        itt_addr = (dte >> 6ULL) & ITTADDR_MASK;
> +        itt_addr <<= ITTADDR_OFFSET; /* 256 byte aligned */
> +
> +        address_space_write(as, itt_addr + (eventid * sizeof(buff)),
> +                    MEMTXATTRS_UNSPECIFIED, &buff,
> +                    sizeof(buff));

Again, abstract this out, please.

> +    }
> +
> +    offset += NUM_BYTES_IN_DW;
> +    offset += NUM_BYTES_IN_DW;
> +
> +    return res;
> +}
> +
>  static void update_cte(GICv3ITSState *s, uint16_t icid, uint64_t cte)
>  {
>      AddressSpace *as = &s->gicv3->sysmem_as;
> @@ -239,7 +566,7 @@ static void update_dte(GICv3ITSState *s, uint32_t devid, uint64_t dte)
>  }
>
>  static MemTxResult process_mapd(GICv3ITSState *s, uint64_t value,
> -                                 uint32_t offset)
> +                                  uint32_t offset)
>  {
>      AddressSpace *as = &s->gicv3->sysmem_as;
>      uint32_t devid;
> @@ -335,8 +662,10 @@ static MemTxResult process_cmdq(GICv3ITSState *s)
>
>          switch (cmd) {
>          case GITS_CMD_INT:
> +            res = process_int(s, data, cq_offset, INT);
>              break;
>          case GITS_CMD_CLEAR:
> +            res = process_int(s, data, cq_offset, CLEAR);
>              break;
>          case GITS_CMD_SYNC:
>              res = process_sync(s, cq_offset);
> @@ -348,10 +677,13 @@ static MemTxResult process_cmdq(GICv3ITSState *s)
>              res = process_mapc(s, cq_offset);
>              break;
>          case GITS_CMD_MAPTI:
> +            res = process_mapti(s, data, cq_offset, false);
>              break;
>          case GITS_CMD_MAPI:
> +            res = process_mapti(s, data, cq_offset, true);
>              break;
>          case GITS_CMD_DISCARD:
> +            res = process_int(s, data, cq_offset, DISCARD);
>              break;
>          default:
>              break;
> @@ -498,7 +830,20 @@ static MemTxResult its_trans_writew(GICv3ITSState *s, hwaddr offset,
>                                 uint64_t value, MemTxAttrs attrs)
>  {
>      MemTxResult result = MEMTX_OK;
> +    uint32_t devid = 0;
>
> +    switch (offset) {
> +    case GITS_TRANSLATER:
> +        if (s->ctlr & ITS_CTLR_ENABLED) {
> +            s->translater = (value & 0x0000FFFFU);
> +            devid = attrs.requester_id;
> +            result = process_int(s, s->translater, devid, NONE);
> +        }
> +        break;
> +    default:
> +        result = MEMTX_ERROR;
> +        break;
> +    }
>      return result;
>  }
>
> @@ -506,7 +851,20 @@ static MemTxResult its_trans_writel(GICv3ITSState *s, hwaddr offset,
>                                 uint64_t value, MemTxAttrs attrs)
>  {
>      MemTxResult result = MEMTX_OK;
> +    uint32_t devid = 0;
>
> +    switch (offset) {
> +    case GITS_TRANSLATER:
> +        if (s->ctlr & ITS_CTLR_ENABLED) {
> +            s->translater = value;
> +            devid = attrs.requester_id;
> +            result = process_int(s, s->translater, devid, NONE);
> +        }
> +        break;
> +    default:
> +        result = MEMTX_ERROR;
> +        break;
> +    }
>      return result;
>  }
>
> diff --git a/hw/intc/gicv3_internal.h b/hw/intc/gicv3_internal.h
> index 7e1ff426ef..e9f9aa6722 100644
> --- a/hw/intc/gicv3_internal.h
> +++ b/hw/intc/gicv3_internal.h
> @@ -323,6 +323,13 @@ FIELD(GITS_TYPER, CIL, 36, 1)
>  #define ITTADDR_MASK              ((1ULL << ITTADDR_LEN) - 1)
>  #define SIZE_MASK                 0x1f
>
> +/* MAPI command fields */
> +#define EVENTID_MASK              ((1ULL << 32) - 1)
> +
> +/* MAPTI command fields */
> +#define pINTID_OFFSET              32
> +#define pINTID_MASK               ((1ULL << 32) - 1)
> +
>  #define VALID_SHIFT               63
>  #define VALID_MASK                0x1
>
> diff --git a/include/hw/intc/arm_gicv3_common.h b/include/hw/intc/arm_gicv3_common.h
> index b0f2414fa3..3a710592a9 100644
> --- a/include/hw/intc/arm_gicv3_common.h
> +++ b/include/hw/intc/arm_gicv3_common.h
> @@ -36,6 +36,8 @@
>  #define GICV3_MAXIRQ 1020
>  #define GICV3_MAXSPI (GICV3_MAXIRQ - GIC_INTERNAL)
>
> +#define GICV3_LPI_INTID_START 8192
> +
>  #define GICV3_REDIST_SIZE 0x20000
>
>  /* Number of SGI target-list bits */
> --
> 2.27.0

thanks
-- PMM


^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH v2 5/8] hw/intc: GICv3 ITS Feature enablement
  2021-04-01  2:41 ` [PATCH v2 5/8] hw/intc: GICv3 ITS Feature enablement Shashi Mallela
@ 2021-04-19 10:51   ` Peter Maydell
  2021-04-29 23:39     ` shashi.mallela
  0 siblings, 1 reply; 25+ messages in thread
From: Peter Maydell @ 2021-04-19 10:51 UTC (permalink / raw)
  To: Shashi Mallela
  Cc: Leif Lindholm, QEMU Developers, qemu-arm, Radoslaw Biernacki

On Thu, 1 Apr 2021 at 03:41, Shashi Mallela <shashi.mallela@linaro.org> wrote:
>
> Added properties to enable ITS feature and define qemu system
> address space memory in gicv3 common,setup distributor and
> redistributor registers to indicate LPI support.
>
> Signed-off-by: Shashi Mallela <shashi.mallela@linaro.org>
> ---
>  hw/intc/arm_gicv3_common.c         | 16 ++++++++++++++++
>  hw/intc/arm_gicv3_dist.c           | 22 ++++++++++++++++++++--
>  hw/intc/arm_gicv3_redist.c         | 28 +++++++++++++++++++++++++---
>  hw/intc/gicv3_internal.h           | 17 +++++++++++++++++
>  include/hw/intc/arm_gicv3_common.h |  8 ++++++++
>  5 files changed, 86 insertions(+), 5 deletions(-)
>
> diff --git a/hw/intc/arm_gicv3_common.c b/hw/intc/arm_gicv3_common.c
> index 58ef65f589..3bfc52f7fa 100644
> --- a/hw/intc/arm_gicv3_common.c
> +++ b/hw/intc/arm_gicv3_common.c
> @@ -156,6 +156,7 @@ static const VMStateDescription vmstate_gicv3_cpu = {
>          VMSTATE_UINT32(gicr_waker, GICv3CPUState),
>          VMSTATE_UINT64(gicr_propbaser, GICv3CPUState),
>          VMSTATE_UINT64(gicr_pendbaser, GICv3CPUState),
> +        VMSTATE_BOOL(lpi_outofrange, GICv3CPUState),
>          VMSTATE_UINT32(gicr_igroupr0, GICv3CPUState),
>          VMSTATE_UINT32(gicr_ienabler0, GICv3CPUState),
>          VMSTATE_UINT32(gicr_ipendr0, GICv3CPUState),
> @@ -227,6 +228,7 @@ static const VMStateDescription vmstate_gicv3 = {
>      .priority = MIG_PRI_GICV3,
>      .fields = (VMStateField[]) {
>          VMSTATE_UINT32(gicd_ctlr, GICv3State),
> +        VMSTATE_UINT32(gicd_typer, GICv3State),
>          VMSTATE_UINT32_ARRAY(gicd_statusr, GICv3State, 2),
>          VMSTATE_UINT32_ARRAY(group, GICv3State, GICV3_BMP_SIZE),
>          VMSTATE_UINT32_ARRAY(grpmod, GICv3State, GICV3_BMP_SIZE),


You can't add fields to an existing VMStateDescription like this
without extra effort to handle migration compatibility.
What are we trying to achieve with the extra fields?
GICD_TYPER is read-only, I think.
I don't think we need an lpi_outofrange flag: we should just naturally
handle this as part of working with the GITR_PROPBASER.IDbits field.

> @@ -381,6 +383,16 @@ static void arm_gicv3_common_realize(DeviceState *dev, Error **errp)
>              (1 << 24) |
>              (i << 8) |
>              (last << 4);
> +
> +        if (s->lpi_enable) {
> +            s->cpu[i].gicr_typer |= GICR_TYPER_PLPIS;
> +
> +            if (!s->sysmem) {
> +                error_setg(errp,
> +                    "Redist-ITS: Guest 'sysmem' reference link not set");
> +                return;
> +            }
> +        }
>      }
>  }
>
> @@ -406,6 +418,7 @@ static void arm_gicv3_common_reset(DeviceState *dev)
>          cs->gicr_waker = GICR_WAKER_ProcessorSleep | GICR_WAKER_ChildrenAsleep;
>          cs->gicr_propbaser = 0;
>          cs->gicr_pendbaser = 0;
> +        cs->lpi_outofrange = false;
>          /* If we're resetting a TZ-aware GIC as if secure firmware
>           * had set it up ready to start a kernel in non-secure, we
>           * need to set interrupts to group 1 so the kernel can use them.
> @@ -494,9 +507,12 @@ static Property arm_gicv3_common_properties[] = {
>      DEFINE_PROP_UINT32("num-cpu", GICv3State, num_cpu, 1),
>      DEFINE_PROP_UINT32("num-irq", GICv3State, num_irq, 32),
>      DEFINE_PROP_UINT32("revision", GICv3State, revision, 3),
> +    DEFINE_PROP_BOOL("has-lpi", GICv3State, lpi_enable, 0),
>      DEFINE_PROP_BOOL("has-security-extensions", GICv3State, security_extn, 0),
>      DEFINE_PROP_ARRAY("redist-region-count", GICv3State, nb_redist_regions,
>                        redist_region_count, qdev_prop_uint32, uint32_t),
> +    DEFINE_PROP_LINK("sysmem", GICv3State, sysmem, TYPE_MEMORY_REGION,
> +                     MemoryRegion *),
>      DEFINE_PROP_END_OF_LIST(),
>  };
>
> diff --git a/hw/intc/arm_gicv3_dist.c b/hw/intc/arm_gicv3_dist.c
> index b65f56f903..96a317a8ef 100644
> --- a/hw/intc/arm_gicv3_dist.c
> +++ b/hw/intc/arm_gicv3_dist.c
> @@ -366,12 +366,15 @@ static MemTxResult gicd_readl(GICv3State *s, hwaddr offset,
>          return MEMTX_OK;
>      case GICD_TYPER:
>      {
> +        bool lpi_supported = false;
>          /* For this implementation:
>           * No1N == 1 (1-of-N SPI interrupts not supported)
>           * A3V == 1 (non-zero values of Affinity level 3 supported)
>           * IDbits == 0xf (we support 16-bit interrupt identifiers)
>           * DVIS == 0 (Direct virtual LPI injection not supported)
> -         * LPIS == 0 (LPIs not supported)
> +         * LPIS == 1 (LPIs are supported if affinity routing is enabled)
> +         * num_LPIs == 0b00000 (bits [15:11],Number of LPIs as indicated
> +         *                      by GICD_TYPER.IDbits)

We support LPIs, but we have none ? That doesn't sound right, and it's
not what the code below reports.

>           * MBIS == 0 (message-based SPIs not supported)
>           * SecurityExtn == 1 if security extns supported
>           * CPUNumber == 0 since for us ARE is always 1
> @@ -385,8 +388,23 @@ static MemTxResult gicd_readl(GICv3State *s, hwaddr offset,
>           */
>          bool sec_extn = !(s->gicd_ctlr & GICD_CTLR_DS);
>
> +        /*
> +         * With securityextn on,LPIs are supported when affinity routing

(missing space after comma)

> +         * is enabled for non-secure state and if off LPIs are supported
> +         * when affinity routing is enabled.
> +         */
> +        if (s->lpi_enable) {
> +            if (sec_extn) {
> +                lpi_supported = (s->gicd_ctlr & GICD_CTLR_ARE_NS);
> +            } else {
> +                lpi_supported = (s->gicd_ctlr & GICD_CTLR_ARE);
> +            }
> +        }
> +
>          *data = (1 << 25) | (1 << 24) | (sec_extn << 10) |
> -            (0xf << 19) | itlinesnumber;
> +            (lpi_supported << GICD_TYPER_LPIS_OFFSET) | (GICD_TYPER_IDBITS <<
> +            GICD_TYPER_IDBITS_OFFSET) | itlinesnumber;
> +        s->gicd_typer = *data;
>          return MEMTX_OK;
>      }
>      case GICD_IIDR:
> diff --git a/hw/intc/arm_gicv3_redist.c b/hw/intc/arm_gicv3_redist.c
> index 8645220d61..325b974e70 100644
> --- a/hw/intc/arm_gicv3_redist.c
> +++ b/hw/intc/arm_gicv3_redist.c
> @@ -248,10 +248,16 @@ static MemTxResult gicr_writel(GICv3CPUState *cs, hwaddr offset,
>      case GICR_CTLR:
>          /* For our implementation, GICR_TYPER.DPGS is 0 and so all
>           * the DPG bits are RAZ/WI. We don't do anything asynchronously,
> -         * so UWP and RWP are RAZ/WI. And GICR_TYPER.LPIS is 0 (we don't
> -         * implement LPIs) so Enable_LPIs is RES0. So there are no writable
> -         * bits for us.
> +         * so UWP and RWP are RAZ/WI. GICR_TYPER.LPIS is 1 (we
> +         * implement LPIs) so Enable_LPIs is programmable.
>           */
> +        if (cs->gicr_typer & GICR_TYPER_PLPIS) {
> +            if (value & GICR_CTLR_ENABLE_LPIS) {
> +                cs->gicr_ctlr |= GICR_CTLR_ENABLE_LPIS;
> +            } else {
> +                cs->gicr_ctlr &= ~GICR_CTLR_ENABLE_LPIS;
> +            }
> +        }
>          return MEMTX_OK;
>      case GICR_STATUSR:
>          /* RAZ/WI for our implementation */
> @@ -275,6 +281,14 @@ static MemTxResult gicr_writel(GICv3CPUState *cs, hwaddr offset,
>          cs->gicr_waker = value;
>          return MEMTX_OK;
>      case GICR_PROPBASER:
> +        if (FIELD_EX64(value, GICR_PROPBASER, IDBITS) <
> +            GICR_PROPBASER_IDBITS_THRESHOLD) {
> +            cs->lpi_outofrange = true;

I don't think you should need to special case this. It just means "turns out
that the LPI table is effectively zero length". When code doing LPI table
lookups calculates the LPI table size (which it needs to do anyway) it can
just catch the "turns out to be negative" case then.

> +        }
> +        if (FIELD_EX64(value, GICR_PROPBASER, IDBITS) > GICD_TYPER_IDBITS) {
> +            value &= ~R_GICR_PROPBASER_IDBITS_MASK;
> +            value |= GICD_TYPER_IDBITS;
> +        }

This isn't what the spec says happens. It says that if the value the
guest writes
in this field is larger than GICD_TYPER.IDbits, then the GICD_TYPER.IDBits value
applies. That doesn't mean the value reads back as GICD_TYPER.IDBits.

How you want to handle this depends on what's going on, but it probably mostly
looks like "code that cares about GICR_PROPBASER.IDBits will do
MIN(field_value, GICD_TYPER_IDBITS) to find the effective value of the field".

>          cs->gicr_propbaser = deposit64(cs->gicr_propbaser, 0, 32, value);
>          return MEMTX_OK;
>      case GICR_PROPBASER + 4:
> @@ -397,6 +411,14 @@ static MemTxResult gicr_writell(GICv3CPUState *cs, hwaddr offset,
>  {
>      switch (offset) {
>      case GICR_PROPBASER:
> +        if (FIELD_EX64(value, GICR_PROPBASER, IDBITS) <
> +            GICR_PROPBASER_IDBITS_THRESHOLD) {
> +            cs->lpi_outofrange = true;
> +        }
> +        if (FIELD_EX64(value, GICR_PROPBASER, IDBITS) > GICD_TYPER_IDBITS) {
> +            value &= ~R_GICR_PROPBASER_IDBITS_MASK;
> +            value |= GICD_TYPER_IDBITS;
> +        }

Same comments as above.

>          cs->gicr_propbaser = value;
>          return MEMTX_OK;
>      case GICR_PENDBASER:
> diff --git a/hw/intc/gicv3_internal.h b/hw/intc/gicv3_internal.h
> index e9f9aa6722..a2718704d4 100644
> --- a/hw/intc/gicv3_internal.h
> +++ b/hw/intc/gicv3_internal.h
> @@ -68,6 +68,9 @@
>  #define GICD_CTLR_E1NWF             (1U << 7)
>  #define GICD_CTLR_RWP               (1U << 31)
>
> +#define GICD_TYPER_LPIS_OFFSET         17
> +#define GICD_TYPER_IDBITS_OFFSET       19
> +#define GICD_TYPER_IDBITS_MASK       0x1f
>  /* 16 bits EventId */
>  #define GICD_TYPER_IDBITS            0xf
>
> @@ -126,6 +129,20 @@
>  #define GICR_WAKER_ProcessorSleep    (1U << 1)
>  #define GICR_WAKER_ChildrenAsleep    (1U << 2)
>
> +FIELD(GICR_PROPBASER, IDBITS, 0, 5)
> +FIELD(GICR_PROPBASER, INNERCACHE, 7, 3)
> +FIELD(GICR_PROPBASER, SHAREABILITY, 10, 2)
> +FIELD(GICR_PROPBASER, PHYADDR, 12, 40)
> +FIELD(GICR_PROPBASER, OUTERCACHE, 56, 3)
> +
> +#define GICR_PROPBASER_IDBITS_THRESHOLD          0xd
> +
> +FIELD(GICR_PENDBASER, INNERCACHE, 7, 3)
> +FIELD(GICR_PENDBASER, SHAREABILITY, 10, 2)
> +FIELD(GICR_PENDBASER, PHYADDR, 16, 36)
> +FIELD(GICR_PENDBASER, OUTERCACHE, 56, 3)
> +FIELD(GICR_PENDBASER, PTZ, 62, 1)
> +
>  #define ICC_CTLR_EL1_CBPR           (1U << 0)
>  #define ICC_CTLR_EL1_EOIMODE        (1U << 1)
>  #define ICC_CTLR_EL1_PMHE           (1U << 6)
> diff --git a/include/hw/intc/arm_gicv3_common.h b/include/hw/intc/arm_gicv3_common.h
> index 3a710592a9..db3989484d 100644
> --- a/include/hw/intc/arm_gicv3_common.h
> +++ b/include/hw/intc/arm_gicv3_common.h
> @@ -175,6 +175,13 @@ struct GICv3CPUState {
>      uint32_t gicr_nsacr;
>      uint8_t gicr_ipriorityr[GIC_INTERNAL];
>
> +    /*
> +     * flag to indicate LPIs are out of range
> +     * since IDbits from GICR_PROPBASER is less
> +     * than 0b1101
> +     */
> +    bool lpi_outofrange;
> +
>      /* CPU interface */
>      uint64_t icc_sre_el1;
>      uint64_t icc_ctlr_el1[2];
> @@ -221,6 +228,7 @@ struct GICv3State {
>      uint32_t num_cpu;
>      uint32_t num_irq;
>      uint32_t revision;
> +    bool lpi_enable;
>      bool security_extn;
>      bool irq_reset_nonsecure;
>      bool gicd_no_migration_shift_bug;

thanks
-- PMM


^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH v2 6/8] hw/intc: GICv3 redistributor ITS processing
  2021-04-01  2:41 ` [PATCH v2 6/8] hw/intc: GICv3 redistributor ITS processing Shashi Mallela
@ 2021-04-19 12:44   ` Peter Maydell
       [not found]     ` <89852279ad379f2e50563dd47eb67376262355c0.camel@linaro.org>
  0 siblings, 1 reply; 25+ messages in thread
From: Peter Maydell @ 2021-04-19 12:44 UTC (permalink / raw)
  To: Shashi Mallela
  Cc: Leif Lindholm, QEMU Developers, qemu-arm, Radoslaw Biernacki

On Thu, 1 Apr 2021 at 03:41, Shashi Mallela <shashi.mallela@linaro.org> wrote:
>
> Implemented lpi processing at redistributor to get lpi config info
> from lpi configuration table,determine priority,set pending state in
> lpi pending table and forward the lpi to cpuif.Added logic to invoke
> redistributor lpi processing with translated LPI which set/clear LPI
> from ITS device as part of ITS INT,CLEAR,DISCARD command and
> GITS_TRANSLATER processing.

Nit: commas should all have a space following.

>
> Signed-off-by: Shashi Mallela <shashi.mallela@linaro.org>
> ---
>  hw/intc/arm_gicv3.c        |   6 ++
>  hw/intc/arm_gicv3_cpuif.c  |  15 +++--
>  hw/intc/arm_gicv3_its.c    |   9 ++-
>  hw/intc/arm_gicv3_redist.c | 124 +++++++++++++++++++++++++++++++++++++
>  hw/intc/gicv3_internal.h   |   9 +++
>  5 files changed, 158 insertions(+), 5 deletions(-)
>
> diff --git a/hw/intc/arm_gicv3.c b/hw/intc/arm_gicv3.c
> index 66eaa97198..618fa1af95 100644
> --- a/hw/intc/arm_gicv3.c
> +++ b/hw/intc/arm_gicv3.c
> @@ -166,6 +166,12 @@ static void gicv3_redist_update_noirqset(GICv3CPUState *cs)
>          cs->hppi.grp = gicv3_irq_group(cs->gic, cs, cs->hppi.irq);
>      }
>
> +    if (cs->gic->lpi_enable) {
> +        if (gicv3_redist_update_lpi(cs)) {
> +            seenbetter = true;
> +        }
> +    }

> +
>      /* If the best interrupt we just found would preempt whatever
>       * was the previous best interrupt before this update, then
>       * we know it's definitely the best one now.
> diff --git a/hw/intc/arm_gicv3_cpuif.c b/hw/intc/arm_gicv3_cpuif.c
> index 43ef1d7a84..c225b80f66 100644
> --- a/hw/intc/arm_gicv3_cpuif.c
> +++ b/hw/intc/arm_gicv3_cpuif.c
> @@ -899,9 +899,14 @@ static void icc_activate_irq(GICv3CPUState *cs, int irq)
>          cs->gicr_ipendr0 = deposit32(cs->gicr_ipendr0, irq, 1, 0);
>          gicv3_redist_update(cs);
>      } else {
> -        gicv3_gicd_active_set(cs->gic, irq);
> -        gicv3_gicd_pending_clear(cs->gic, irq);
> -        gicv3_update(cs->gic, irq, 1);
> +        if (irq >= GICV3_LPI_INTID_START) {
> +            gicv3_redist_lpi_pending(cs, irq, 0);
> +            gicv3_redist_update(cs);
> +        } else {
> +            gicv3_gicd_active_set(cs->gic, irq);
> +            gicv3_gicd_pending_clear(cs->gic, irq);
> +            gicv3_update(cs->gic, irq, 1);
> +        }
>      }
>  }
>
> @@ -1337,7 +1342,9 @@ static void icc_eoir_write(CPUARMState *env, const ARMCPRegInfo *ri,
>           * valid interrupt value read from the Interrupt Acknowledge
>           * register" and so this is UNPREDICTABLE. We choose to ignore it.
>           */
> -        return;
> +        if (!(cs->gic->lpi_enable && (irq >= GICV3_LPI_INTID_START))) {
> +            return;
> +        }

This condition is in the wrong place. I think what you are trying to
do is modify "what is the set of numbers we consider to be valid IRQ
numbers", in which case you want to be changing the outer
"if (irq >= cs->gic->num_irq)" condition, not adding an extra one
inside it.

More importantly, the thing this condition is guarding is that
the code below it assumes that IRQ numbers are in range for the
GIC's own internal non-LPI interrupts. If you allow numbers
>= cs->gic->num_irq through, then you will get array overruns
when icc_deactivate_irq() tries to clear its Active bit.

>      }
>
>      if (icc_highest_active_group(cs) != grp) {
> diff --git a/hw/intc/arm_gicv3_its.c b/hw/intc/arm_gicv3_its.c
> index 0e3f176809..41e1e8b2a8 100644
> --- a/hw/intc/arm_gicv3_its.c
> +++ b/hw/intc/arm_gicv3_its.c
> @@ -226,6 +226,7 @@ static MemTxResult process_int(GICv3ITSState *s, uint64_t value,
>      bool ite_valid = false;
>      uint64_t cte = 0;
>      bool cte_valid = false;
> +    uint64_t rdbase;
>      uint8_t buff[ITS_ITT_ENTRY_SIZE];
>      uint64_t itt_addr;
>
> @@ -278,12 +279,18 @@ static MemTxResult process_int(GICv3ITSState *s, uint64_t value,
>               * since with a physical address the target address must be
>               * 64KB aligned
>               */
> -
> +            rdbase = (cte >> 1U) & RDBASE_MASK;

What's going on here? Shouldn't this be in a previous patch ?

>              /*
>               * Current implementation only supports rdbase == procnum
>               * Hence rdbase physical address is ignored
>               */
>          } else {
> +            rdbase = (cte >> 1U) & RDBASE_PROCNUM_MASK;
> +            if ((cmd == CLEAR) || (cmd == DISCARD)) {
> +                gicv3_redist_process_lpi(&s->gicv3->cpu[rdbase], pIntid, 0);
> +            } else {
> +                gicv3_redist_process_lpi(&s->gicv3->cpu[rdbase], pIntid, 1);
> +            }
>
>              if (cmd == DISCARD) {
>                  /* remove mapping from interrupt translation table */
> diff --git a/hw/intc/arm_gicv3_redist.c b/hw/intc/arm_gicv3_redist.c
> index 325b974e70..71c648a616 100644
> --- a/hw/intc/arm_gicv3_redist.c
> +++ b/hw/intc/arm_gicv3_redist.c
> @@ -254,6 +254,8 @@ static MemTxResult gicr_writel(GICv3CPUState *cs, hwaddr offset,
>          if (cs->gicr_typer & GICR_TYPER_PLPIS) {
>              if (value & GICR_CTLR_ENABLE_LPIS) {
>                  cs->gicr_ctlr |= GICR_CTLR_ENABLE_LPIS;
> +                /* Check for any pending interr in pending table */
> +                gicv3_redist_update(cs);
>              } else {
>                  cs->gicr_ctlr &= ~GICR_CTLR_ENABLE_LPIS;
>              }
> @@ -548,6 +550,128 @@ MemTxResult gicv3_redist_write(void *opaque, hwaddr offset, uint64_t data,
>      return r;
>  }
>
> +bool gicv3_redist_update_lpi(GICv3CPUState *cs)

Could we have a comment explaining what this function does, please ?

> +{
> +    AddressSpace *as = &cs->gic->sysmem_as;
> +    uint64_t lpict_baddr, lpipt_baddr;
> +    uint32_t pendt_size = 0;
> +    uint8_t lpite;
> +    uint8_t prio, pend;
> +    int i;
> +    bool seenbetter = false;
> +
> +    if ((!cs->gicr_ctlr & GICR_CTLR_ENABLE_LPIS) || !cs->gicr_propbaser ||
> +        !cs->gicr_pendbaser || cs->lpi_outofrange) {
> +        return seenbetter;
> +    }
> +
> +    lpict_baddr = FIELD_EX64(cs->gicr_propbaser, GICR_PROPBASER, PHYADDR);
> +    lpict_baddr <<= R_GICR_PROPBASER_PHYADDR_SHIFT;
> +
> +    lpipt_baddr =  FIELD_EX64(cs->gicr_pendbaser, GICR_PENDBASER, PHYADDR);
> +    lpipt_baddr <<= R_GICR_PENDBASER_PHYADDR_SHIFT;
> +
> +    /* Determine the highest priority pending interrupt among LPIs */
> +    pendt_size = (1UL << (FIELD_EX64(cs->gicr_propbaser, GICR_PROPBASER,
> +                          IDBITS) - 1));

This is where you should be handling "actually the table is empty"
or "table size is capped at the implementation's maximum size".

> +
> +    for (i = 0; i < pendt_size; i++) {
> +        address_space_read(as, lpipt_baddr +
> +                (((GICV3_LPI_INTID_START + i) / 8) * sizeof(pend)),
> +                MEMTXATTRS_UNSPECIFIED, &pend, sizeof(pend));
> +
> +        if ((1 << ((GICV3_LPI_INTID_START + i) % 8)) & pend) {
> +            address_space_read(as, lpict_baddr + (i * sizeof(lpite)),
> +                      MEMTXATTRS_UNSPECIFIED, &lpite, sizeof(lpite));
> +
> +            prio = ((lpite >> LPI_CTE_PRIORITY_OFFSET) &
> +                     LPI_CTE_PRIORITY_MASK);
> +            prio &= LPI_PRIORITY_MASK;
> +
> +            if (prio < cs->hppi.prio) {
> +                cs->hppi.irq = GICV3_LPI_INTID_START + i;
> +                cs->hppi.prio = prio;
> +                /* LPIs are always non-secure Grp1 interrupts */
> +                cs->hppi.grp = GICV3_G1NS;
> +                seenbetter = true;
> +            }
> +        }
> +    }

Oof. How big is an LPI configuration table, typically? I'm not sure we want
to be scanning the entire LPI configuration table in guest memory for every
time gicv3_redist_update() is called if we can avoid it.

A bit of benchmarking of how much this slows down emulation might
be interesting.

> +    return seenbetter;
> +}
> +
> +void gicv3_redist_lpi_pending(GICv3CPUState *cs, int irq, int level)
> +{
> +    AddressSpace *as = &cs->gic->sysmem_as;
> +    uint64_t lpipt_baddr;
> +    bool ispend = false;
> +    uint8_t pend;
> +
> +    /*
> +     * get the bit value corresponding to this irq in the
> +     * lpi pending table
> +     */
> +    lpipt_baddr = FIELD_EX64(cs->gicr_pendbaser, GICR_PENDBASER, PHYADDR);
> +    lpipt_baddr <<= R_GICR_PENDBASER_PHYADDR_SHIFT;
> +
> +    address_space_read(as, lpipt_baddr + ((irq / 8) * sizeof(pend)),
> +                         MEMTXATTRS_UNSPECIFIED, &pend, sizeof(pend));
> +    ispend = ((pend >> (irq % 8)) & 0x1);
> +
> +    if (ispend) {
> +        if (!level) {
> +            /*
> +             * clear the pending bit and update the lpi pending table
> +             */
> +            pend &= ~(1 << (irq % 8));
> +
> +            address_space_write(as, lpipt_baddr + ((irq / 8) * sizeof(pend)),
> +                                 MEMTXATTRS_UNSPECIFIED, &pend, sizeof(pend));
> +        }
> +    } else {
> +        if (level) {
> +            /*
> +             * if pending bit is not already set for this irq,turn-on the
> +             * pending bit and update the lpi pending table
> +             */
> +            pend |= (1 << (irq % 8));
> +
> +            address_space_write(as, lpipt_baddr + ((irq / 8) * sizeof(pend)),
> +                                 MEMTXATTRS_UNSPECIFIED, &pend, sizeof(pend));
> +        }
> +    }
> +}
>
> +void gicv3_redist_process_lpi(GICv3CPUState *cs, int irq, int level)
> +{
> +    AddressSpace *as = &cs->gic->sysmem_as;
> +    uint64_t lpict_baddr;
> +    uint8_t lpite;
> +
> +    if ((!cs->gicr_ctlr & GICR_CTLR_ENABLE_LPIS) || !cs->gicr_propbaser ||
> +         !cs->gicr_pendbaser || cs->lpi_outofrange) {
> +        return;
> +    }
> +
> +    lpict_baddr = FIELD_EX64(cs->gicr_propbaser, GICR_PROPBASER, PHYADDR);
> +    lpict_baddr <<= R_GICR_PROPBASER_PHYADDR_SHIFT;
> +
> +    /* get the lpi config table entry corresponding to this irq */
> +    address_space_read(as, lpict_baddr + ((irq - GICV3_LPI_INTID_START) *
> +                        sizeof(lpite)), MEMTXATTRS_UNSPECIFIED,
> +                        &lpite, sizeof(lpite));
> +
> +    /* check if this irq is enabled before proceeding further */
> +    if (!(lpite & LPI_CTE_ENABLED)) {
> +        return;
> +    }
> +
> +    /* set/clear the pending bit for this irq */
> +    gicv3_redist_lpi_pending(cs, irq, level);
> +
> +    gicv3_redist_update(cs);
> +}
> +
>  void gicv3_redist_set_irq(GICv3CPUState *cs, int irq, int level)
>  {
>      /* Update redistributor state for a change in an external PPI input line */
> diff --git a/hw/intc/gicv3_internal.h b/hw/intc/gicv3_internal.h
> index a2718704d4..4c97c22850 100644
> --- a/hw/intc/gicv3_internal.h
> +++ b/hw/intc/gicv3_internal.h
> @@ -306,6 +306,12 @@ FIELD(GITS_TYPER, CIL, 36, 1)
>
>  #define L1TABLE_ENTRY_SIZE         8
>
> +#define LPI_CTE_ENABLE_OFFSET      0
> +#define LPI_CTE_ENABLED          VALID_MASK
> +#define LPI_CTE_PRIORITY_OFFSET    2
> +#define LPI_CTE_PRIORITY_MASK     ((1U << 6) - 1)
> +#define LPI_PRIORITY_MASK         0xfc
> +
>  #define GITS_CMDQ_ENTRY_SIZE               32
>  #define NUM_BYTES_IN_DW                     8
>
> @@ -444,6 +450,9 @@ MemTxResult gicv3_redist_write(void *opaque, hwaddr offset, uint64_t data,
>                                 unsigned size, MemTxAttrs attrs);
>  void gicv3_dist_set_irq(GICv3State *s, int irq, int level);
>  void gicv3_redist_set_irq(GICv3CPUState *cs, int irq, int level);
> +void gicv3_redist_process_lpi(GICv3CPUState *cs, int irq, int level);
> +void gicv3_redist_lpi_pending(GICv3CPUState *cs, int irq, int level);
> +bool gicv3_redist_update_lpi(GICv3CPUState *cs);
>  void gicv3_redist_send_sgi(GICv3CPUState *cs, int grp, int irq, bool ns);
>  void gicv3_init_cpuif(GICv3State *s);

thanks
-- PMM


^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH v2 7/8] hw/arm/sbsa-ref: add ITS support in SBSA GIC
  2021-04-01  2:41 ` [PATCH v2 7/8] hw/arm/sbsa-ref: add ITS support in SBSA GIC Shashi Mallela
@ 2021-04-19 12:44   ` Peter Maydell
  0 siblings, 0 replies; 25+ messages in thread
From: Peter Maydell @ 2021-04-19 12:44 UTC (permalink / raw)
  To: Shashi Mallela
  Cc: Leif Lindholm, QEMU Developers, qemu-arm, Radoslaw Biernacki

On Thu, 1 Apr 2021 at 03:41, Shashi Mallela <shashi.mallela@linaro.org> wrote:
>
> Included creation of ITS as part of SBSA platform GIC
> initialization.
>
> Signed-off-by: Shashi Mallela <shashi.mallela@linaro.org>
> ---
>  hw/arm/sbsa-ref.c | 26 +++++++++++++++++++++++---
>  1 file changed, 23 insertions(+), 3 deletions(-)

Reviewed-by: Peter Maydell <peter.maydell@linaro.org>

thanks
-- PMM


^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH v2 8/8] hw/arm/virt: add ITS support in virt GIC
  2021-04-01  2:41 ` [PATCH v2 8/8] hw/arm/virt: add ITS support in virt GIC Shashi Mallela
@ 2021-04-19 12:46   ` Peter Maydell
  2021-04-29 23:40     ` shashi.mallela
  0 siblings, 1 reply; 25+ messages in thread
From: Peter Maydell @ 2021-04-19 12:46 UTC (permalink / raw)
  To: Shashi Mallela
  Cc: Leif Lindholm, QEMU Developers, qemu-arm, Radoslaw Biernacki

On Thu, 1 Apr 2021 at 03:42, Shashi Mallela <shashi.mallela@linaro.org> wrote:
>
> Included creation of ITS as part of virt platform GIC
> initialization.This Emulated ITS model now co-exists with kvm
> ITS and is enabled in absence of kvm irq kernel support in a
> platform.
>
> Signed-off-by: Shashi Mallela <shashi.mallela@linaro.org>
> ---
>  hw/arm/virt.c        | 10 ++++++++--
>  target/arm/kvm_arm.h |  4 ++--
>  2 files changed, 10 insertions(+), 4 deletions(-)
>
> diff --git a/hw/arm/virt.c b/hw/arm/virt.c
> index aa2bbd14e0..77cf2db90f 100644
> --- a/hw/arm/virt.c
> +++ b/hw/arm/virt.c
> @@ -622,7 +622,7 @@ static void create_v2m(VirtMachineState *vms)
>      vms->msi_controller = VIRT_MSI_CTRL_GICV2M;
>  }
>
> -static void create_gic(VirtMachineState *vms)
> +static void create_gic(VirtMachineState *vms, MemoryRegion *mem)
>  {
>      MachineState *ms = MACHINE(vms);
>      /* We create a standalone GIC */
> @@ -656,6 +656,12 @@ static void create_gic(VirtMachineState *vms)
>                               nb_redist_regions);
>          qdev_prop_set_uint32(vms->gic, "redist-region-count[0]", redist0_count);
>
> +        if (!kvm_irqchip_in_kernel()) {
> +            object_property_set_link(OBJECT(vms->gic), "sysmem", OBJECT(mem),
> +                                     &error_fatal);
> +            qdev_prop_set_bit(vms->gic, "has-lpi", true);
> +        }
> +
>          if (nb_redist_regions == 2) {
>              uint32_t redist1_capacity =
>                      vms->memmap[VIRT_HIGH_GIC_REDIST2].size / GICV3_REDIST_SIZE;
> @@ -2039,7 +2045,7 @@ static void machvirt_init(MachineState *machine)
>
>      virt_flash_fdt(vms, sysmem, secure_sysmem ?: sysmem);
>
> -    create_gic(vms);
> +    create_gic(vms, sysmem);
>
>      virt_cpu_post_init(vms, sysmem);
>
> diff --git a/target/arm/kvm_arm.h b/target/arm/kvm_arm.h
> index 34f8daa377..0613454975 100644
> --- a/target/arm/kvm_arm.h
> +++ b/target/arm/kvm_arm.h
> @@ -525,8 +525,8 @@ static inline const char *its_class_name(void)
>          /* KVM implementation requires this capability */
>          return kvm_direct_msi_enabled() ? "arm-its-kvm" : NULL;
>      } else {
> -        /* Software emulation is not implemented yet */
> -        return NULL;
> +        /* Software emulation based model */
> +        return "arm-gicv3-its";
>      }
>  }

This is OK as far as it goes, but I think we need to add the
versioned-machine support so that only "virt-6.1" and later get the
new ITS, and "virt-6.0" behave the same way (ie no ITS) as they did
in older QEMU versions.

thanks
-- PMM


^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH v2 1/8] hw/intc: GICv3 ITS initial framework
  2021-04-16 17:21   ` Peter Maydell
@ 2021-04-29 23:36     ` shashi.mallela
  2021-04-30 10:09       ` Peter Maydell
  0 siblings, 1 reply; 25+ messages in thread
From: shashi.mallela @ 2021-04-29 23:36 UTC (permalink / raw)
  To: Peter Maydell
  Cc: Leif Lindholm, QEMU Developers, qemu-arm, Radoslaw Biernacki

On Fri, 2021-04-16 at 18:21 +0100, Peter Maydell wrote:
> On Thu, 1 Apr 2021 at 03:41, Shashi Mallela <
> shashi.mallela@linaro.org> wrote:
> > Added register definitions relevant to ITS,implemented overall
> > ITS device framework with stubs for ITS control and translater
> > regions read/write,extended ITS common to handle mmio init between
> > existing kvm device and newer qemu device.
> > 
> > Signed-off-by: Shashi Mallela <shashi.mallela@linaro.org>
> > ---
> >  hw/intc/arm_gicv3_its.c                | 334
> > +++++++++++++++++++++++++
> >  hw/intc/arm_gicv3_its_common.c         |  12 +-
> >  hw/intc/arm_gicv3_its_kvm.c            |   2 +-
> >  hw/intc/gicv3_internal.h               |  86 ++++++-
> >  hw/intc/meson.build                    |   1 +
> >  include/hw/intc/arm_gicv3_its_common.h |  12 +-
> >  6 files changed, 430 insertions(+), 17 deletions(-)
> >  create mode 100644 hw/intc/arm_gicv3_its.c
> 
> Overall I think the structure of the patchset and of the device
> is good, so I'm just going to dive into more detailed review
> comments.
> 
> Have accepted and addressed all the comments and will share the
> updated patch sets soon,responding to comments which need 
>  clarification in all the relevant patch sets 
> 
> > +static MemTxResult its_trans_writew(GICv3ITSState *s, hwaddr
> > offset,
> > +                               uint64_t value, MemTxAttrs attrs)
> > +{
> > +    MemTxResult result = MEMTX_OK;
> > +
> > +    return result;
> > +}
> > +
> > +static MemTxResult its_trans_writel(GICv3ITSState *s, hwaddr
> > offset,
> > +                               uint64_t value, MemTxAttrs attrs)
> > +{
> > +    MemTxResult result = MEMTX_OK;
> > +
> > +    return result;
> > +}
> > +
> > +static MemTxResult gicv3_its_translation_write(void *opaque,
> > hwaddr offset,
> > +                               uint64_t data, unsigned size,
> > MemTxAttrs attrs)
> > +{
> > +    GICv3ITSState *s = (GICv3ITSState *)opaque;
> > +    MemTxResult result;
> > +
> > +    switch (size) {
> > +    case 2:
> > +        result = its_trans_writew(s, offset, data, attrs);
> > +        break;
> > +    case 4:
> > +        result = its_trans_writel(s, offset, data, attrs);
> > +        break;
> > +    default:
> > +        result = MEMTX_ERROR;
> > +        break;
> > +    }
> > +
> > +    if (result == MEMTX_ERROR) {
> > +        qemu_log_mask(LOG_GUEST_ERROR,
> > +                      "%s: invalid guest write at offset "
> > TARGET_FMT_plx
> > +                      "size %u\n", __func__, offset, size);
> > +        /*
> > +         * The spec requires that reserved registers are RAZ/WI;
> > +         * so use MEMTX_ERROR returns from leaf functions as a way
> > to
> > +         * trigger the guest-error logging but don't return it to
> > +         * the caller, or we'll cause a spurious guest data abort.
> > +         */
> > +        result = MEMTX_OK;
> > +    }
> > +    return result;
> > +}
> 
> There is exactly one register in the translation register frame, so
> having this generic top level write function that calls out to
> separate writew and writel functions is overkill and ends up
> with duplication of code. I would suggest you just fold the
> implementation into gicv3_its_translation_write(), which would
> end up looking something like
> 
>     switch (offset) {
>     case GITS_TRANSLATER:
>         if (s->ctlr & ITS_CTLR_ENABLED) {
>             /* 16 bit accesses behave as writes with bits [31:16]
> zero */
>             s->translater = value;
>             devid = attrs.requester_id;
>             process_int(s, s->translater, devid, NONE);
>         }
>         break;
>     default:
>         break;
>     }
>     return MEMTX_OK;
> 
> which handles both the 16 and 32 bit case. You can tell the
> core memory system code not to hand you 8 or 64 bit accesses
> using fields in the MemoryRegionOps; see below.
> 
> > +static MemTxResult gicv3_its_translation_read(void *opaque, hwaddr
> > offset,
> > +                              uint64_t *data, unsigned size,
> > MemTxAttrs attrs)
> > +{
> > +    qemu_log_mask(LOG_GUEST_ERROR,
> > +        "%s: Invalid read from translation register area at offset
> > "
> > +        TARGET_FMT_plx "\n", __func__, offset);
> > +    return MEMTX_ERROR;
> > +}
> > +
> > +static MemTxResult its_writeb(GICv3ITSState *s, hwaddr offset,
> > +                               uint64_t value, MemTxAttrs attrs)
> > +{
> > +    qemu_log_mask(LOG_UNIMP,
> > +                "%s: unsupported byte write to register at offset
> > "
> > +                TARGET_FMT_plx "\n", __func__, offset);
> > +    return MEMTX_ERROR;
> > +}
> > +
> > +static MemTxResult its_readb(GICv3ITSState *s, hwaddr offset,
> > +                               uint64_t *data, MemTxAttrs attrs)
> > +{
> > +    qemu_log_mask(LOG_UNIMP,
> > +                "%s: unsupported byte read from register at offset
> > "
> > +                TARGET_FMT_plx "\n", __func__, offset);
> > +    return MEMTX_ERROR;
> > +}
> > +
> > +static MemTxResult its_writew(GICv3ITSState *s, hwaddr offset,
> > +                               uint64_t value, MemTxAttrs attrs)
> > +{
> > +    qemu_log_mask(LOG_UNIMP,
> > +        "%s: unsupported word write to register at offset "
> > +        TARGET_FMT_plx "\n", __func__, offset);
> > +    return MEMTX_ERROR;
> > +}
> > +
> > +static MemTxResult its_readw(GICv3ITSState *s, hwaddr offset,
> > +                               uint64_t *data, MemTxAttrs attrs)
> > +{
> > +    qemu_log_mask(LOG_UNIMP,
> > +        "%s: unsupported word read from register at offset "
> > +        TARGET_FMT_plx "\n", __func__, offset);
> > +    return MEMTX_ERROR;
> > +}
> 
> Similarly, there are no byte or halfword accessible registers
> on the ITS, so these are unnecessary; just use MemoryRegionOps
> to cause the core code to reject them.
> 
> > +static MemTxResult its_writel(GICv3ITSState *s, hwaddr offset,
> > +                               uint64_t value, MemTxAttrs attrs)
> > +{
> > +    MemTxResult result = MEMTX_OK;
> > +
> > +    return result;
> > +}
> > +
> > +static MemTxResult its_readl(GICv3ITSState *s, hwaddr offset,
> > +                               uint64_t *data, MemTxAttrs attrs)
> > +{
> > +    MemTxResult result = MEMTX_OK;
> > +
> > +    return result;
> > +}
> > +
> > +static MemTxResult its_writell(GICv3ITSState *s, hwaddr offset,
> > +                               uint64_t value, MemTxAttrs attrs)
> > +{
> > +    MemTxResult result = MEMTX_OK;
> > +
> > +    return result;
> > +}
> > +
> > +static MemTxResult its_readll(GICv3ITSState *s, hwaddr offset,
> > +                               uint64_t *data, MemTxAttrs attrs)
> > +{
> > +    MemTxResult result = MEMTX_OK;
> > +
> > +    return result;
> > +}
> > +
> > +static MemTxResult gicv3_its_read(void *opaque, hwaddr offset,
> > uint64_t *data,
> > +                              unsigned size, MemTxAttrs attrs)
> > +{
> > +    GICv3ITSState *s = (GICv3ITSState *)opaque;
> > +    MemTxResult result;
> > +
> > +    switch (size) {
> > +    case 1:
> > +        result = its_readb(s, offset, data, attrs);
> > +        break;
> > +    case 2:
> > +        result = its_readw(s, offset, data, attrs);
> > +        break;
> > +    case 4:
> > +        result = its_readl(s, offset, data, attrs);
> > +        break;
> > +    case 8:
> > +        result = its_readll(s, offset, data, attrs);
> > +        break;
> > +    default:
> > +        result = MEMTX_ERROR;
> > +        break;
> > +    }
> > +
> > +    if (result == MEMTX_ERROR) {
> > +        qemu_log_mask(LOG_GUEST_ERROR,
> > +                      "%s: invalid guest read at offset "
> > TARGET_FMT_plx
> > +                      "size %u\n", __func__, offset, size);
> > +        /*
> > +         * The spec requires that reserved registers are RAZ/WI;
> > +         * so use MEMTX_ERROR returns from leaf functions as a way
> > to
> > +         * trigger the guest-error logging but don't return it to
> > +         * the caller, or we'll cause a spurious guest data abort.
> > +         */
> > +        result = MEMTX_OK;
> > +        *data = 0;
> > +    }
> > +    return result;
> > +}
> > +
> > +static MemTxResult gicv3_its_write(void *opaque, hwaddr offset,
> > uint64_t data,
> > +                               unsigned size, MemTxAttrs attrs)
> > +{
> > +    GICv3ITSState *s = (GICv3ITSState *)opaque;
> > +    MemTxResult result;
> > +
> > +    switch (size) {
> > +    case 1:
> > +        result = its_writeb(s, offset, data, attrs);
> > +        break;
> > +    case 2:
> > +        result = its_writew(s, offset, data, attrs);
> > +        break;
> > +    case 4:
> > +        result = its_writel(s, offset, data, attrs);
> > +        break;
> > +    case 8:
> > +        result = its_writell(s, offset, data, attrs);
> > +        break;
> > +    default:
> > +        result = MEMTX_ERROR;
> > +        break;
> > +    }
> > +
> > +    if (result == MEMTX_ERROR) {
> > +        qemu_log_mask(LOG_GUEST_ERROR,
> > +                      "%s: invalid guest write at offset "
> > TARGET_FMT_plx
> > +                      "size %u\n", __func__, offset, size);
> > +        /*
> > +         * The spec requires that reserved registers are RAZ/WI;
> > +         * so use MEMTX_ERROR returns from leaf functions as a way
> > to
> > +         * trigger the guest-error logging but don't return it to
> > +         * the caller, or we'll cause a spurious guest data abort.
> > +         */
> > +        result = MEMTX_OK;
> > +    }
> > +    return result;
> > +}
> > +
> > +static const MemoryRegionOps gicv3_its_control_ops = {
> > +    .read_with_attrs = gicv3_its_read,
> > +    .write_with_attrs = gicv3_its_write,
> > +    .endianness = DEVICE_NATIVE_ENDIAN,
> 
>        .valid.min_access_size = 4,
>        .valid.max_access_size = 8,
>        .impl.min_access_size = 4,
>        .impl.max_access_size = 8,
> 
> And then you don't need to handle 1 and 2 in your read/write fns.
> 
> 
> > +};
> > +
> > +static const MemoryRegionOps gicv3_its_translation_ops = {
> > +    .read_with_attrs = gicv3_its_translation_read,
> > +    .write_with_attrs = gicv3_its_translation_write,
> > +    .endianness = DEVICE_NATIVE_ENDIAN,
> 
> Similarly here set the valid and impl min and max to 2 and 4.
> 
> > +};
> > +
> > +static void gicv3_arm_its_realize(DeviceState *dev, Error **errp)
> > +{
> > +    GICv3ITSState *s = ARM_GICV3_ITS_COMMON(dev);
> > +
> > +    gicv3_its_init_mmio(s, &gicv3_its_control_ops,
> > &gicv3_its_translation_ops);
> > +}
> > +
> > +static void gicv3_its_reset(DeviceState *dev)
> > +{
> > +    GICv3ITSState *s = ARM_GICV3_ITS_COMMON(dev);
> > +    GICv3ITSClass *c = ARM_GICV3_ITS_GET_CLASS(s);
> > +
> > +    if (s->gicv3->cpu->gicr_typer & GICR_TYPER_PLPIS) {
> > +        c->parent_reset(dev);
> > +
> > +        /* set the ITS default features supported */
> > +        s->typer = FIELD_DP64(s->typer, GITS_TYPER, PHYSICAL,
> > +                                       GITS_TYPE_PHYSICAL);
> > +        s->typer = FIELD_DP64(s->typer, GITS_TYPER,
> > ITT_ENTRY_SIZE,
> > +                                       ITS_ITT_ENTRY_SIZE);
> > +        s->typer = FIELD_DP64(s->typer, GITS_TYPER, IDBITS,
> > ITS_IDBITS);
> > +        s->typer = FIELD_DP64(s->typer, GITS_TYPER, DEVBITS,
> > ITS_DEVBITS);
> > +        s->typer = FIELD_DP64(s->typer, GITS_TYPER, CIL, 1);
> > +        s->typer = FIELD_DP64(s->typer, GITS_TYPER, CIDBITS,
> > ITS_CIDBITS);
> 
> This is a read-only constant value. Two options:
>  (1) set s->typer in realize, not in reset (this is how we handle
>      GICR_TYPER)
>  (2) don't have an s->typer at all, and just return the right
>      constant value when in the read function (this is how we
>      handle GICD_TYPER)
> Implemented option 1
> 
> > +
> > +        /*
> > +         * We claim to be an ARM r0p0 with a zero ProductID.
> > +         * This is the same as an r0p0 GIC-500.
> > +         */
> > +        s->iidr = gicv3_iidr();
> 
> IIDR is constant, so just call gicv3_iidr() to get its value directly
> in the register read function.
> 
> > +
> > +        /* Quiescent bit reset to 1 */
> > +        s->ctlr = FIELD_DP32(s->ctlr, GITS_CTLR, QUIESCENT, 1);
> > +
> > +        /*
> > +         * setting GITS_BASER0.Type = 0b001 (Device)
> > +         *         GITS_BASER1.Type = 0b100 (Collection Table)
> > +         *         GITS_BASER<n>.Type,where n = 3 to 7 are 0b00
> > (Unimplemented)
> > +         *         GITS_BASER<0,1>.Page_Size = 64KB
> > +         * and default translation table entry size to 16 bytes
> > +         */
> > +        s->baser[0] = FIELD_DP64(s->baser[0], GITS_BASER, TYPE,
> > +                                              GITS_ITT_TYPE_DEVICE
> > );
> > +        s->baser[0] = FIELD_DP64(s->baser[0], GITS_BASER,
> > PAGESIZE,
> > +                                              GITS_BASER_PAGESIZE_
> > 64K);
> > +        s->baser[0] = FIELD_DP64(s->baser[0], GITS_BASER,
> > ENTRYSIZE,
> > +                                              GITS_DTE_SIZE);
> > +
> > +        s->baser[1] = FIELD_DP64(s->baser[1], GITS_BASER, TYPE,
> > +                                              GITS_ITT_TYPE_COLLEC
> > TION);
> > +        s->baser[1] = FIELD_DP64(s->baser[1], GITS_BASER,
> > PAGESIZE,
> > +                                              GITS_BASER_PAGESIZE_
> > 64K);
> > +        s->baser[1] = FIELD_DP64(s->baser[1], GITS_BASER,
> > ENTRYSIZE,
> > +                                              GITS_CTE_SIZE);
> > +    }
> > +}
> > +
> > +static Property gicv3_its_props[] = {
> > +    DEFINE_PROP_LINK("parent-gicv3", GICv3ITSState, gicv3, "arm-
> > gicv3",
> > +                     GICv3State *),
> > +    DEFINE_PROP_END_OF_LIST(),
> > +};
> > +
> > +static void gicv3_its_class_init(ObjectClass *klass, void *data)
> > +{
> > +    DeviceClass *dc = DEVICE_CLASS(klass);
> > +    GICv3ITSClass *ic = ARM_GICV3_ITS_CLASS(klass);
> > +
> > +    dc->realize = gicv3_arm_its_realize;
> > +    device_class_set_props(dc, gicv3_its_props);
> > +    device_class_set_parent_reset(dc, gicv3_its_reset, &ic-
> > >parent_reset);
> > +}
> > +
> > +static const TypeInfo gicv3_its_info = {
> > +    .name = TYPE_ARM_GICV3_ITS,
> > +    .parent = TYPE_ARM_GICV3_ITS_COMMON,
> > +    .instance_size = sizeof(GICv3ITSState),
> > +    .class_init = gicv3_its_class_init,
> > +    .class_size = sizeof(GICv3ITSClass),
> > +};
> > +
> > +static void gicv3_its_register_types(void)
> > +{
> > +    type_register_static(&gicv3_its_info);
> > +}
> > +
> > +type_init(gicv3_its_register_types)
> > diff --git a/hw/intc/arm_gicv3_its_common.c
> > b/hw/intc/arm_gicv3_its_common.c
> > index 66c4c6a188..80cc9ec6d8 100644
> > --- a/hw/intc/arm_gicv3_its_common.c
> > +++ b/hw/intc/arm_gicv3_its_common.c
> > @@ -50,12 +50,16 @@ static int gicv3_its_post_load(void *opaque,
> > int version_id)
> > 
> >  static const VMStateDescription vmstate_its = {
> >      .name = "arm_gicv3_its",
> > +    .version_id = 1,
> > +    .minimum_version_id = 1,
> >      .pre_save = gicv3_its_pre_save,
> >      .post_load = gicv3_its_post_load,
> >      .priority = MIG_PRI_GICV3_ITS,
> >      .fields = (VMStateField[]) {
> >          VMSTATE_UINT32(ctlr, GICv3ITSState),
> > +        VMSTATE_UINT32(translater, GICv3ITSState),
> >          VMSTATE_UINT32(iidr, GICv3ITSState),
> > +        VMSTATE_UINT64(typer, GICv3ITSState),
> 
> You cannot change a VMStateDescription like this -- you will break
> migration compatibility. Luckily, in this case there's no need:
> GITS_TYPER is read-only, so it doesn't need to be migrated, and
> GITS_TRANSLATER is not a real contains-state register (it's write-
> only),
> so there's no new state there either. Neither needs to be a new field
> in the GICv3ITSState structure either (though typer could be if you
> prefer).
> 
> >          VMSTATE_UINT64(cbaser, GICv3ITSState),
> >          VMSTATE_UINT64(cwriter, GICv3ITSState),
> >          VMSTATE_UINT64(creadr, GICv3ITSState),
> > @@ -99,15 +103,16 @@ static const MemoryRegionOps
> > gicv3_its_trans_ops = {
> >      .endianness = DEVICE_NATIVE_ENDIAN,
> >  };
> > +/**
> > + * Default features advertised by this version of ITS
> > + */
> > +/* Physical LPIs supported */
> > +#define GITS_TYPE_PHYSICAL           (1U << 0)
> > +
> > +/*
> > + * 11 bytes Interrupt translation Table Entry size
> > + * Valid = 1 bit,InterruptType = 1 bit,
> > + * Size of LPI number space[considering max 24 bits],
> > + * Size of LPI number space[considering max 24 bits],
> > + * ICID = 16 bits,
> > + * vPEID = 16 bits
> > + */
> > +#define ITS_ITT_ENTRY_SIZE            0xB
> > +
> > +/* 16 bits EventId */
> > +#define ITS_IDBITS                   GICD_TYPER_IDBITS
> > +
> > +/* 16 bits DeviceId */
> > +#define ITS_DEVBITS                   0xF
> > +
> > +/* 16 bits CollectionId */
> > +#define ITS_CIDBITS                  0xF
> > +
> > +/*
> > + * 8 bytes Device Table Entry size
> > + * Valid = 1 bit,ITTAddr = 44 bits,Size = 5 bits
> > + */
> > +#define GITS_DTE_SIZE                 (0x8ULL)
> > +
> > +/*
> > + * 8 bytes Collection Table Entry size
> > + * Valid = 1 bit,RDBase = 36 bits(considering max RDBASE)
> > + */
> > +#define GITS_CTE_SIZE                 (0x8ULL)
> > +
> 
> Is this the same data structure the in-kernel KVM ITS uses, or is
> it just one specific to the QEMU emulated implementation ?
> This is specific to QEMU emulated implementation
> 
> >  /* Special interrupt IDs */
> >  #define INTID_SECURE 1020
> >  #define INTID_NONSECURE 1021
> > diff --git a/hw/intc/meson.build b/hw/intc/meson.build
> > index 1c299039f6..53472239f0 100644
> > --- a/hw/intc/meson.build
> > +++ b/hw/intc/meson.build
> > @@ -8,6 +8,7 @@ softmmu_ss.add(when: 'CONFIG_ARM_GIC', if_true:
> > files(
> >    'arm_gicv3_dist.c',
> >    'arm_gicv3_its_common.c',
> >    'arm_gicv3_redist.c',
> > +  'arm_gicv3_its.c',
> >  ))
> >  softmmu_ss.add(when: 'CONFIG_ETRAXFS', if_true:
> > files('etraxfs_pic.c'))
> >  softmmu_ss.add(when: 'CONFIG_HEATHROW_PIC', if_true:
> > files('heathrow_pic.c'))
> > diff --git a/include/hw/intc/arm_gicv3_its_common.h
> > b/include/hw/intc/arm_gicv3_its_common.h
> > index 5a0952b404..08bc5652ed 100644
> > --- a/include/hw/intc/arm_gicv3_its_common.h
> > +++ b/include/hw/intc/arm_gicv3_its_common.h
> > @@ -25,17 +25,24 @@
> >  #include "hw/intc/arm_gicv3_common.h"
> >  #include "qom/object.h"
> > 
> > +#define TYPE_ARM_GICV3_ITS "arm-gicv3-its"
> > +
> >  #define ITS_CONTROL_SIZE 0x10000
> >  #define ITS_TRANS_SIZE   0x10000
> >  #define ITS_SIZE         (ITS_CONTROL_SIZE + ITS_TRANS_SIZE)
> > 
> >  #define GITS_CTLR        0x0
> >  #define GITS_IIDR        0x4
> > +#define GITS_TYPER       0x8
> >  #define GITS_CBASER      0x80
> >  #define GITS_CWRITER     0x88
> >  #define GITS_CREADR      0x90
> >  #define GITS_BASER       0x100
> > 
> > +#define GITS_TRANSLATER  0x0040
> > +
> > +#define GITS_PIDR2       0xFFE8
> 
> You probably don't want an offset for GITS_PIDR2 specifically.
> Compare handling of GICD_IDREGS in the distributor emulation.
> Tried re-using the GICD_IDREGS offset for GITS_PIDR2,but from testing
> observed that the linux kernel driver and kvm-unit-tests both relied
> on using the 0xFFE8 offset instead of 0xFFD0 (used by GICD & GICR) to
> complete ITS initialization and setup
> 
> thanks
> -- PMM



^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH v2 2/8] hw/intc: GICv3 ITS register definitions added
       [not found]     ` <937a2923445e3ff629c9799a8579c470d2636375.camel@linaro.org>
@ 2021-04-29 23:37       ` shashi.mallela
  0 siblings, 0 replies; 25+ messages in thread
From: shashi.mallela @ 2021-04-29 23:37 UTC (permalink / raw)
  To: Peter Maydell
  Cc: Leif Lindholm, QEMU Developers, qemu-arm, Radoslaw Biernacki

On Thu, 2021-04-29 at 17:46 -0400, shashi.mallela@linaro.org wrote:
On Fri, 2021-04-16 at 19:54 +0100, Peter Maydell wrote:
> On Thu, 1 Apr 2021 at 03:41, Shashi Mallela <
> shashi.mallela@linaro.org> wrote:
> > Defined descriptors for ITS device table,collection table and ITS
> > command queue entities.Implemented register read/write functions,
> > extract ITS table parameters and command queue parameters,extended
> > gicv3 common to capture qemu address space(which host the ITS table
> > platform memories required for subsequent ITS processing) and
> > initialize the same in its device.
> > 
> > Signed-off-by: Shashi Mallela <shashi.mallela@linaro.org>
> > ---
> >  hw/intc/arm_gicv3_its.c                | 313
> > +++++++++++++++++++++++++
> >  hw/intc/arm_gicv3_its_common.c         |  42 ++++
> >  hw/intc/gicv3_internal.h               |  33 ++-
> >  include/hw/intc/arm_gicv3_common.h     |   4 +
> >  include/hw/intc/arm_gicv3_its_common.h |  28 +++
> >  5 files changed, 414 insertions(+), 6 deletions(-)
> > 
> > diff --git a/hw/intc/arm_gicv3_its.c b/hw/intc/arm_gicv3_its.c
> > index 209120d102..81373f049d 100644
> > --- a/hw/intc/arm_gicv3_its.c
> > +++ b/hw/intc/arm_gicv3_its.c
> > @@ -28,6 +28,131 @@ struct GICv3ITSClass {
> >      void (*parent_reset)(DeviceState *dev);
> >  };
> > 
> > +static bool extract_table_params(GICv3ITSState *s, int index)
> > +{
> > +    uint16_t num_pages = 0;
> > +    uint8_t  page_sz_type;
> > +    uint8_t type;
> > +    uint32_t page_sz = 0;
> > +    uint64_t value = s->baser[index];
> > +
> > +    num_pages = FIELD_EX64(value, GITS_BASER, SIZE);
> > +    page_sz_type = FIELD_EX64(value, GITS_BASER, PAGESIZE);
> > +
> > +    if (page_sz_type == 0) {
> > +        page_sz = GITS_ITT_PAGE_SIZE_0;
> > +    } else if (page_sz_type == 0) {
> > +        page_sz = GITS_ITT_PAGE_SIZE_1;
> > +    } else if (page_sz_type == 2) {
> > +        page_sz = GITS_ITT_PAGE_SIZE_2;
> > +    } else {
> > +        return false;
> 
> The spec says that page_sz+type = 0b11 should be treated as 0b10.
> So we could log this as a guest error if you like but otherwise
> should handle it the same way we do 0b10.
> 
> > +    }
> 
> if (x == CONST1) {
> } else if (x == CONST2) {
> } else {
> }
> 
> is usually clearer written with switch(). (There are other instances
> of this pattern below.)
> 
> > +
> > +    type = FIELD_EX64(value, GITS_BASER, TYPE);
> > +
> > +    if (type == GITS_ITT_TYPE_DEVICE) {
> > +        s->dt.valid = FIELD_EX64(value, GITS_BASER, VALID);
> > +
> > +        if (s->dt.valid) {
> > +            s->dt.indirect = FIELD_EX64(value, GITS_BASER,
> > INDIRECT);
> > +            s->dt.entry_sz = FIELD_EX64(value, GITS_BASER,
> > ENTRYSIZE);
> > +
> > +            if (!s->dt.indirect) {
> > +                s->dt.max_entries = ((num_pages + 1) * page_sz) /
> > +                                                       s-
> > >dt.entry_sz;
> > +            } else {
> > +                s->dt.max_entries = ((((num_pages + 1) * page_sz)
> > /
> > +                                        L1TABLE_ENTRY_SIZE) *
> > +                                    (page_sz / s->dt.entry_sz));
> > +            }
> > +
> > +            s->dt.max_devids = (1UL << (FIELD_EX64(s->typer,
> > GITS_TYPER,
> > +                                                    DEVBITS) +
> > 1));
> > +
> > +            if ((page_sz == GITS_ITT_PAGE_SIZE_0) ||
> > +                    (page_sz == GITS_ITT_PAGE_SIZE_1)) {
> > +                s->dt.base_addr = FIELD_EX64(value, GITS_BASER,
> > PHYADDR);
> > +                s->dt.base_addr <<= R_GITS_BASER_PHYADDR_SHIFT;
> > +            } else if (page_sz == GITS_ITT_PAGE_SIZE_2) {
> > +                s->dt.base_addr = FIELD_EX64(value, GITS_BASER,
> > PHYADDRL_64K) <<
> > +                                  R_GITS_BASER_PHYADDRL_64K_SHIFT;
> > +                  s->dt.base_addr |= ((value >>
> > R_GITS_BASER_PHYADDR_SHIFT) &
> > +                                       R_GITS_BASER_PHYADDRH_64K_M
> > ASK) <<
> > +                                       R_GITS_BASER_PHYADDRH_64K_S
> > HIFT;
> > +            }
> > +        }
> > +    } else if (type == GITS_ITT_TYPE_COLLECTION) {
> > +        s->ct.valid = FIELD_EX64(value, GITS_BASER, VALID);
> > +
> > +        /*
> > +         * GITS_TYPER.HCC is 0 for this implementation
> > +         * hence writes are discarded if ct.valid is 0
> > +         */
> > +        if (s->ct.valid) {
> > +            s->ct.indirect = FIELD_EX64(value, GITS_BASER,
> > INDIRECT);
> > +            s->ct.entry_sz = FIELD_EX64(value, GITS_BASER,
> > ENTRYSIZE);
> > +
> > +            if (!s->ct.indirect) {
> > +                s->ct.max_entries = ((num_pages + 1) * page_sz) /
> > +                                      s->ct.entry_sz;
> > +            } else {
> > +                s->ct.max_entries = ((((num_pages + 1) * page_sz)
> > /
> > +                                      L1TABLE_ENTRY_SIZE) *
> > +                                      (page_sz / s->ct.entry_sz));
> > +            }
> > +
> > +            if (FIELD_EX64(s->typer, GITS_TYPER, CIL)) {
> > +                s->ct.max_collids = (1UL << (FIELD_EX64(s->typer,
> > GITS_TYPER,
> > +                                                         CIDBITS)
> > + 1));
> > +            } else {
> > +                /* 16-bit CollectionId supported when CIL == 0 */
> > +                s->ct.max_collids = (1UL << 16);
> > +            }
> > +
> > +            if ((page_sz == GITS_ITT_PAGE_SIZE_0) ||
> > +                 (page_sz == GITS_ITT_PAGE_SIZE_1)) {
> > +                s->ct.base_addr = FIELD_EX64(value, GITS_BASER,
> > PHYADDR);
> > +                s->ct.base_addr <<= R_GITS_BASER_PHYADDR_SHIFT;
> > +            } else if (page_sz == GITS_ITT_PAGE_SIZE_2) {
> > +                s->ct.base_addr = FIELD_EX64(value, GITS_BASER,
> > PHYADDRL_64K) <<
> > +                                    R_GITS_BASER_PHYADDRL_64K_SHIF
> > T;
> > +                s->ct.base_addr |= ((value >>
> > R_GITS_BASER_PHYADDR_SHIFT) &
> > +                                     R_GITS_BASER_PHYADDRH_64K_MAS
> > K) <<
> > +                                     R_GITS_BASER_PHYADDRH_64K_SHI
> > FT;
> > +            }
> > +        }
> > +    } else {
> > +        /* unsupported ITS table type */
> > +        qemu_log_mask(LOG_GUEST_ERROR, "%s: Unsupported ITS table
> > type %d",
> > +                         __func__, type);
> 
> This should never happen, because the TYPE field in a GITS_BASER
> register
> is read-only. The model should insure that the guest can never write
> to
> these bits. Then you can just assert() that the field is valid, if
> you like.
> 
> > +        return false;
> > +    }
> > +    return true;
> > +}
> > +
> > +static bool extract_cmdq_params(GICv3ITSState *s)
> > +{
> > +    uint16_t num_pages = 0;
> > +    uint64_t value = s->cbaser;
> > +
> > +    num_pages = FIELD_EX64(value, GITS_CBASER, SIZE);
> > +
> > +    s->cq.valid = FIELD_EX64(value, GITS_CBASER, VALID);
> > +
> > +    if (!num_pages || !s->cq.valid) {
> 
> Why do you think num_pages == 0 is not valid ?
> Have rectified this
> > +        return false;
> > +    }
> > +
> > +    if (s->cq.valid) {
> > +        s->cq.max_entries = ((num_pages + 1) *
> > GITS_ITT_PAGE_SIZE_0) /
> > +                                                GITS_CMDQ_ENTRY_SI
> > ZE;
> > +        s->cq.base_addr = FIELD_EX64(value, GITS_CBASER, PHYADDR);
> > +        s->cq.base_addr <<= R_GITS_CBASER_PHYADDR_SHIFT;
> > +    }
> > +    return true;
> > +}
> > +
> >  static MemTxResult its_trans_writew(GICv3ITSState *s, hwaddr
> > offset,
> >                                 uint64_t value, MemTxAttrs attrs)
> >  {
> > @@ -126,7 +251,75 @@ static MemTxResult its_writel(GICv3ITSState
> > *s, hwaddr offset,
> >                                 uint64_t value, MemTxAttrs
> > attrs)>  {
> >      MemTxResult result = MEMTX_OK;
> > +    int index;
> > +    uint64_t temp = 0;
> > 
> > +    switch (offset) {
> > +    case GITS_CTLR:
> > +        s->ctlr |= (value & ~(s->ctlr));
> > +        break;
> > +    case GITS_CBASER:
> > +        /* GITS_CBASER register becomes RO if ITS is already
> > enabled */
> 
> The spec says writes are UNPREDICTABLE, not RO. We can make an
> impdef choice to interpret that as RO for our convenience, but
> the comment should note that it is our IMPDEF choice.
> 
> > +        if (!(s->ctlr & ITS_CTLR_ENABLED)) {
> > +            s->cbaser = deposit64(s->cbaser, 0, 32, value);
> > +            s->creadr = 0;
> > +        }
> > +        break;
> > +    case GITS_CBASER + 4:
> > +        /* GITS_CBASER register becomes RO if ITS is already
> > enabled */
> > +        if (!(s->ctlr & ITS_CTLR_ENABLED)) {
> > +            s->cbaser = deposit64(s->cbaser, 32, 32, value);
> > +            if (!extract_cmdq_params(s)) {
> 
> You should postpone trying to parse the CBASER fields until
> the guest enables the ITS by writing to GITS_CTLR.Enabled.
> Because CBASER is a 64-bit register that can be written as two
> 32-bit halves, the guest might choose to write the two halves
> in either order, and you won't have a full valid CBASER.
> If you wait until the ITS is enabled then you know the guest
> should have written the register values completely then.
> Similarly with BASER.
> 
> > +                qemu_log_mask(LOG_GUEST_ERROR,
> > +                       "%s: error extracting GITS_CBASER
> > parameters "
> > +                       TARGET_FMT_plx "\n", __func__, offset);
> > +                s->cbaser = 0;
> 
> Clearing CBASER if the guest writes an invalid value to it
> doesn't seem right. Since you have these expanded-out structs,
> you can just treat any bogus values as if the guest had written
> VALID=0, and leave the actual guest-written value in s->cbaser.
> (In fact I think the only case when extract_cmdq_params()
> can reasonably return false is exactly when VALID=0, so in
> that case you might as well drop the bool return type from it
> completely.)
> 
> > +                result = MEMTX_ERROR;
> 
> I don't recommend using MEMTX_ERROR here for this kind of
> "the guest wrote a value to a valid register but there happened
> to be something wrong with the value it wrote". The intention
> with the arm_gicv3_dist.c code structure that you've copied
> here for the ITS was simply to use MEMTX_ERROR to capture "this
> register doesn't exist for this size" to be able to have a
> single LOG_GUEST_ERROR message for that at the top level
> function.
> 
> For more complicated stuff like this you should just report a
> suitable specific LOG_GUEST_ERROR directly in extract_cmdq_params()
> and return MEMTX_OK. (The code you have for BASER will actually
> report
> three different log messages for a single guest mistake -- once
> inside
> extract_table_params(), once in the code in this function that calls
> extract_table_params(), and then once in gicv3_its_read(). One is
> sufficient.)
> 
> > +            } else {
> > +                s->creadr = 0;
> > +            }
> > +        }
> > +        break;
> > +    case GITS_CWRITER:
> > +        s->cwriter = deposit64(s->cwriter, 0, 32, value);
> > +        break;
> > +    case GITS_CWRITER + 4:
> > +        s->cwriter = deposit64(s->cwriter, 32, 32, value);
> > +        break;
> > +    case GITS_BASER ... GITS_BASER + 0x3f:
> > +        /* GITS_BASERn registers become RO if ITS is already
> > enabled */
> > +        if (!(s->ctlr & ITS_CTLR_ENABLED)) {
> > +            index = (offset - GITS_BASER) / 8;
> > +
> > +            if (offset & 7) {
> > +                temp = s->baser[index];
> > +                temp = deposit64(temp, 32, 32, (value &
> > ~GITS_BASER_VAL_MASK));
> > +                s->baser[index] |= temp;
> > +
> > +                if (!extract_table_params(s, index)) {
> > +                    qemu_log_mask(LOG_GUEST_ERROR,
> > +                        "%s: error extracting GITS_BASER
> > parameters "
> > +                        TARGET_FMT_plx "\n", __func__, offset);
> > +                    s->baser[index] = 0;
> > +                    result = MEMTX_ERROR;
> > +                }
> > +            } else {
> > +                s->baser[index] =  deposit64(s->baser[index], 0,
> > 32, value);
> > +            }
> > +        }
> 
> Pretty much all the remarks I made earlier about CBASER apply here
> too.
> 
> Also, 'unimplemented' BASER registers are supposed to be RES0, and
> I don't think we actually implement more than either 2 or 3, right?
> we implement only 2 BASER registers one for device table and one
for collection table
> 
> > +        break;
> > +    case GITS_IIDR:
> > +    case GITS_TYPER:
> > +    case GITS_CREADR:
> > +        /* RO registers, ignore the write */
> > +        qemu_log_mask(LOG_GUEST_ERROR,
> > +            "%s: invalid guest write to RO register at offset "
> > +            TARGET_FMT_plx "\n", __func__, offset);
> > +        break;
> 
> I wouldn't personally bother explicitly reporting write-to-RO-
> register
> here: if you just let the default case handle it then the log in
> the caller will do a LOG_GUEST_ERROR for you.
> 
> > +    default:
> > +        result = MEMTX_ERROR;
> > +        break;
> > +    }
> >      return result;
> >  }
> > 
> > @@ -142,7 +382,52 @@ static MemTxResult its_writell(GICv3ITSState
> > *s, hwaddr offset,
> >                                 uint64_t value, MemTxAttrs attrs)
> >  {
> >      MemTxResult result = MEMTX_OK;
> > +    int index;
> > 
> > +    switch (offset) {
> > +    case GITS_BASER ... GITS_BASER + 0x3f:
> > +        /* GITS_BASERn registers become RO if ITS is already
> > enabled */
> > +        if (!(s->ctlr & ITS_CTLR_ENABLED)) {
> > +            index = (offset - GITS_BASER) / 8;
> > +            s->baser[index] |= (value & ~GITS_BASER_VAL_MASK);
> > +            if (!extract_table_params(s, index)) {
> > +                qemu_log_mask(LOG_GUEST_ERROR,
> > +                        "%s: error extracting GITS_BASER
> > parameters "
> > +                        TARGET_FMT_plx "\n", __func__, offset);
> > +                s->baser[index] = 0;
> > +                result = MEMTX_ERROR;
> > +            }
> > +        }
> > +        break;
> > +    case GITS_CBASER:
> > +        /* GITS_CBASER register becomes RO if ITS is already
> > enabled */
> > +        if (!(s->ctlr & ITS_CTLR_ENABLED)) {
> > +            s->cbaser = value;
> > +            if (!extract_cmdq_params(s)) {
> > +                qemu_log_mask(LOG_GUEST_ERROR,
> > +                       "%s: error extracting GITS_CBASER
> > parameters "
> > +                       TARGET_FMT_plx "\n", __func__, offset);
> > +                s->cbaser = 0;
> > +                result = MEMTX_ERROR;
> > +            } else {
> > +                s->creadr = 0;
> > +            }
> > +        }
> > +        break;
> > +    case GITS_CWRITER:
> > +        s->cwriter = value;
> > +        break;
> > +    case GITS_TYPER:
> > +    case GITS_CREADR:
> > +        /* RO register, ignore the write */
> > +        qemu_log_mask(LOG_GUEST_ERROR,
> > +                      "%s: invalid guest write to RO register at
> > offset "
> > +                      TARGET_FMT_plx "\n", __func__, offset);
> > +        break;
> > +    default:
> > +        result = MEMTX_ERROR;
> > +        break;
> > +    }
> 
> Comments on the 32-bit write function apply here too.
> 
> >      return result;
> >  }
> > @@ -250,6 +557,9 @@ static void gicv3_arm_its_realize(DeviceState
> > *dev, Error **errp)
> >      GICv3ITSState *s = ARM_GICV3_ITS_COMMON(dev);
> > 
> >      gicv3_its_init_mmio(s, &gicv3_its_control_ops,
> > &gicv3_its_translation_ops);
> > +
> > +    address_space_init(&s->gicv3->sysmem_as, s->gicv3->sysmem,
> > +                        "gicv3-its-sysmem");
> 
> Indent here seems wrong -- the second line should be lined up with
> the first, so the '"' is below the '&'. I think I noticed some of
> this
> in patch 1 as well, so worth going through and checking that.
> 
> >  }
> > 
> >  static void gicv3_its_reset(DeviceState *dev)
> > @@ -259,6 +569,9 @@ static void gicv3_its_reset(DeviceState *dev)
> > 
> >      if (s->gicv3->cpu->gicr_typer & GICR_TYPER_PLPIS) {
> >          c->parent_reset(dev);
> > +        memset(&s->dt, 0, sizeof(s->dt));
> > +        memset(&s->ct, 0, sizeof(s->ct));
> > +        memset(&s->cq, 0, sizeof(s->cq));
> 
> Would be cleaner to first set the baser/cbaser registers to their
> reset values and then just call the function that calculates these
> struct fields based on the register values.
> 
> >          /* set the ITS default features supported */
> >          s->typer = FIELD_DP64(s->typer, GITS_TYPER, PHYSICAL,
> > diff --git a/hw/intc/arm_gicv3_its_common.c
> > b/hw/intc/arm_gicv3_its_common.c
> > index 80cc9ec6d8..9c5c71f350 100644
> > --- a/hw/intc/arm_gicv3_its_common.c
> > +++ b/hw/intc/arm_gicv3_its_common.c
> > @@ -48,6 +48,42 @@ static int gicv3_its_post_load(void *opaque, int
> > version_id)
> >      return 0;
> >  }
> > 
> > +static const VMStateDescription vmstate_its_dtdesc = {
> > +    .name = "arm_gicv3_its_dtdesc",
> > +    .fields = (VMStateField[]) {
> > +        VMSTATE_BOOL(valid, DevTableDesc),
> > +        VMSTATE_BOOL(indirect, DevTableDesc),
> > +        VMSTATE_UINT16(entry_sz, DevTableDesc),
> > +        VMSTATE_UINT32(max_entries, DevTableDesc),
> > +        VMSTATE_UINT32(max_devids, DevTableDesc),
> > +        VMSTATE_UINT64(base_addr, DevTableDesc),
> > +        VMSTATE_END_OF_LIST(),
> > +    },
> > +};
> > +
> > +static const VMStateDescription vmstate_its_ctdesc = {
> > +    .name = "arm_gicv3_its_ctdesc",
> > +    .fields = (VMStateField[]) {
> > +        VMSTATE_BOOL(valid, CollTableDesc),
> > +        VMSTATE_BOOL(indirect, CollTableDesc),
> > +        VMSTATE_UINT16(entry_sz, CollTableDesc),
> > +        VMSTATE_UINT32(max_entries, CollTableDesc),
> > +        VMSTATE_UINT32(max_collids, CollTableDesc),
> > +        VMSTATE_UINT64(base_addr, CollTableDesc),
> > +        VMSTATE_END_OF_LIST(),
> > +    },
> > +};
> > +
> > +static const VMStateDescription vmstate_its_cqdesc = {
> > +    .name = "arm_gicv3_its_cqdesc",
> > +    .fields = (VMStateField[]) {
> > +        VMSTATE_BOOL(valid, CmdQDesc),
> > +        VMSTATE_UINT32(max_entries, CmdQDesc),
> > +        VMSTATE_UINT64(base_addr, CmdQDesc),
> > +        VMSTATE_END_OF_LIST(),
> > +    },
> > +};
> 
> Do these really need migrating, or are they just expanded out values
> that can be recalculated from the BASER and CBASER register values?
> If so then it would be simpler to recalculate in a migration post-
> load
> function.
> have removed all the migration specific variables and implemented
> migration post-load to recalculate the required values
> 
> > +
> >  static const VMStateDescription vmstate_its = {
> >      .name = "arm_gicv3_its",
> >      .version_id = 1,
> > @@ -56,6 +92,12 @@ static const VMStateDescription vmstate_its = {
> >      .post_load = gicv3_its_post_load,
> >      .priority = MIG_PRI_GICV3_ITS,
> >      .fields = (VMStateField[]) {
> > +        VMSTATE_STRUCT(dt, GICv3ITSState, 0, vmstate_its_dtdesc,
> > +                        DevTableDesc),
> > +        VMSTATE_STRUCT(ct, GICv3ITSState, 0, vmstate_its_ctdesc,
> > +                        CollTableDesc),
> > +        VMSTATE_STRUCT(cq, GICv3ITSState, 0, vmstate_its_cqdesc,
> > +                        CmdQDesc),
> 
> As noted previously, you can't just add fields to a
> VMStateDescription;
> retaining migration compatibility requires more than that.
> 
> >          VMSTATE_UINT32(ctlr, GICv3ITSState),
> >          VMSTATE_UINT32(translater, GICv3ITSState),
> >          VMSTATE_UINT32(iidr, GICv3ITSState),
> > diff --git a/hw/intc/gicv3_internal.h b/hw/intc/gicv3_internal.h
> > index 96cfe2dff9..b7701e8ca5 100644
> > --- a/hw/intc/gicv3_internal.h
> > +++ b/hw/intc/gicv3_internal.h
> > @@ -246,13 +246,14 @@ FIELD(GITS_BASER, INNERCACHE, 59, 3)
> >  FIELD(GITS_BASER, INDIRECT, 62, 1)
> >  FIELD(GITS_BASER, VALID, 63, 1)
> > 
> > -#define GITS_BASER_PAGESIZE_4K                0
> > -#define GITS_BASER_PAGESIZE_16K               1
> > -#define GITS_BASER_PAGESIZE_64K               2
> > -
> > -#define GITS_ITT_TYPE_DEVICE                  1ULL
> > -#define GITS_ITT_TYPE_COLLECTION              4ULL
> 
> You just added these in the previous patch, don't move them
> in this one. Have patch 1 put them in the right place in the file
> to start with.
> 
> > +FIELD(GITS_CBASER, SIZE, 0, 8)
> > +FIELD(GITS_CBASER, SHAREABILITY, 10, 2)
> > +FIELD(GITS_CBASER, PHYADDR, 12, 40)
> > +FIELD(GITS_CBASER, OUTERCACHE, 53, 3)
> > +FIELD(GITS_CBASER, INNERCACHE, 59, 3)
> > +FIELD(GITS_CBASER, VALID, 63, 1)
> > 
> > +FIELD(GITS_CTLR, ENABLED, 0, 1)
> >  FIELD(GITS_CTLR, QUIESCENT, 31, 1)
> > 
> >  FIELD(GITS_TYPER, PHYSICAL, 0, 1)
> > @@ -264,6 +265,26 @@ FIELD(GITS_TYPER, PTA, 19, 1)
> >  FIELD(GITS_TYPER, CIDBITS, 32, 4)
> >  FIELD(GITS_TYPER, CIL, 36, 1)
> > 
> > +#define ITS_CTLR_ENABLED               (1U)  /* ITS Enabled */
> > +
> > +#define
> > GITS_BASER_VAL_MASK                  (R_GITS_BASER_ENTRYSIZE_MASK |
> > \
> > +                                              R_GITS_BASER_TYPE_MA
> > SK)
> > +
> > +#define GITS_BASER_PAGESIZE_4K                0
> > +#define GITS_BASER_PAGESIZE_16K               1
> > +#define GITS_BASER_PAGESIZE_64K               2
> > +
> > +#define GITS_ITT_TYPE_DEVICE                  1ULL
> > +#define GITS_ITT_TYPE_COLLECTION              4ULL
> > +
> > +#define GITS_ITT_PAGE_SIZE_0      0x1000
> > +#define GITS_ITT_PAGE_SIZE_1      0x4000
> > +#define GITS_ITT_PAGE_SIZE_2      0x10000
> > +
> > +#define L1TABLE_ENTRY_SIZE         8
> > +
> > +#define GITS_CMDQ_ENTRY_SIZE               32
> > +
> >  /**
> >   * Default features advertised by this version of ITS
> >   */
> > diff --git a/include/hw/intc/arm_gicv3_common.h
> > b/include/hw/intc/arm_gicv3_common.h
> > index 91491a2f66..b0f2414fa3 100644
> > --- a/include/hw/intc/arm_gicv3_common.h
> > +++ b/include/hw/intc/arm_gicv3_common.h
> > @@ -226,12 +226,16 @@ struct GICv3State {
> >      int dev_fd; /* kvm device fd if backed by kvm vgic support */
> >      Error *migration_blocker;
> > 
> > +    MemoryRegion *sysmem;
> > +    AddressSpace sysmem_as;
> 
> I think these would be better named "dma" and "dma_as", because
> from the ITS' point of view that's what they are. There's no inherent
> requirement that they be connected to the system address space in
> particular.
> 
> > +
> >      /* Distributor */
> > 
> >      /* for a GIC with the security extensions the NS banked
> > version of this
> >       * register is just an alias of bit 1 of the S banked version.
> >       */
> >      uint32_t gicd_ctlr;
> > +    uint32_t gicd_typer;
> 
> What's this for ? You shouldn't need to be adding new distributor
> state fields.
> removed
> 
> >      uint32_t gicd_statusr[2];
> >      GIC_DECLARE_BITMAP(group);        /* GICD_IGROUPR */
> >      GIC_DECLARE_BITMAP(grpmod);       /* GICD_IGRPMODR */
> > diff --git a/include/hw/intc/arm_gicv3_its_common.h
> > b/include/hw/intc/arm_gicv3_its_common.h
> > index 08bc5652ed..b7eca57221 100644
> > --- a/include/hw/intc/arm_gicv3_its_common.h
> > +++ b/include/hw/intc/arm_gicv3_its_common.h
> > @@ -43,6 +43,30 @@
> > 
> >  #define GITS_PIDR2       0xFFE8
> > 
> > +typedef struct {
> > +    bool valid;
> > +    bool indirect;
> > +    uint16_t entry_sz;
> > +    uint32_t max_entries;
> > +    uint32_t max_devids;
> > +    uint64_t base_addr;
> > +} DevTableDesc;
> > +
> > +typedef struct {
> > +    bool valid;
> > +    bool indirect;
> > +    uint16_t entry_sz;
> > +    uint32_t max_entries;
> > +    uint32_t max_collids;
> > +    uint64_t base_addr;
> > +} CollTableDesc;
> > +
> > +typedef struct {
> > +    bool valid;
> > +    uint32_t max_entries;
> > +    uint64_t base_addr;
> > +} CmdQDesc;
> > +
> >  struct GICv3ITSState {
> >      SysBusDevice parent_obj;
> > 
> > @@ -66,6 +90,10 @@ struct GICv3ITSState {
> >      uint64_t creadr;
> >      uint64_t baser[8];
> > 
> > +    DevTableDesc  dt;
> > +    CollTableDesc ct;
> > +    CmdQDesc      cq;
> > +
> >      Error *migration_blocker;
> >  };
> > 
> > --
> > 2.27.0
> 
> thanks
> -- PMM



^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH v2 3/8] hw/intc: GICv3 ITS command queue framework
  2021-04-19 10:30   ` Peter Maydell
@ 2021-04-29 23:38     ` shashi.mallela
  0 siblings, 0 replies; 25+ messages in thread
From: shashi.mallela @ 2021-04-29 23:38 UTC (permalink / raw)
  To: Peter Maydell
  Cc: Leif Lindholm, QEMU Developers, qemu-arm, Radoslaw Biernacki

On Mon, 2021-04-19 at 11:30 +0100, Peter Maydell wrote:
> On Thu, 1 Apr 2021 at 03:41, Shashi Mallela <
> shashi.mallela@linaro.org> wrote:
> > Added functionality to trigger ITS command queue processing on
> > write to CWRITE register and process each command queue entry to
> > identify the command type and handle commands like MAPD,MAPC,SYNC.
> > 
> > Signed-off-by: Shashi Mallela <shashi.mallela@linaro.org>
> > ---
> >  hw/intc/arm_gicv3_its.c  | 347
> > +++++++++++++++++++++++++++++++++++++++
> >  hw/intc/gicv3_internal.h |  41 +++++
> >  2 files changed, 388 insertions(+)
> > 
> > diff --git a/hw/intc/arm_gicv3_its.c b/hw/intc/arm_gicv3_its.c
> > index 81373f049d..fcac33c836 100644
> > --- a/hw/intc/arm_gicv3_its.c
> > +++ b/hw/intc/arm_gicv3_its.c
> > @@ -28,6 +28,347 @@ struct GICv3ITSClass {
> >      void (*parent_reset)(DeviceState *dev);
> >  };
> > 
> > +static MemTxResult process_sync(GICv3ITSState *s, uint32_t offset)
> > +{
> > +    AddressSpace *as = &s->gicv3->sysmem_as;
> > +    uint64_t rdbase;
> > +    uint64_t value;
> > +    bool pta = false;
> > +    MemTxResult res = MEMTX_OK;
> > +
> > +    offset += NUM_BYTES_IN_DW;
> > +    offset += NUM_BYTES_IN_DW;
> > +
> > +    value = address_space_ldq_le(as, s->cq.base_addr + offset,
> > +                                     MEMTXATTRS_UNSPECIFIED,
> > &res);
> 
> If the ITS fails to read or write DMA, it does not report that
> by provoking a bus error for the CPU's write to GITS_CWRITER,
> which is what will happen if you just propagate up the MemTxResult
> from this DMA operation back through the register write function
> to its caller. The spec doesn't seem to define a particular behaviour
> for "I couldn't read the command out of the command queue", but
> it would seem sensible to pick one of the options from the "Command
> errors"
> section: ignore the command, or stall the command queue ("treat as
> valid" is the other option there but doesn't seem relevant here).
> 
> "Stall" is probably best as otherwise we might loop forever through
> a region of unreadable addresses.
>
> have implemented "stall" indication for all failing dma read/write
calls(based on the return value of each call) 
> 
> > +    if (FIELD_EX64(s->typer, GITS_TYPER, PTA)) {
> > +        /*
> > +         * only bits[47:16] are considered instead of bits [51:16]
> > +         * since with a physical address the target address must
> > be
> > +         * 64KB aligned
> > +         */
> > +        rdbase = (value >> RDBASE_OFFSET) & RDBASE_MASK;
> > +        pta = true;
> > +    } else {
> > +        rdbase = (value >> RDBASE_OFFSET) & RDBASE_PROCNUM_MASK;
> > +    }
> > +
> > +    if (!pta && (rdbase < (s->gicv3->num_cpu))) {
> > +        /*
> > +         * Current implementation makes a blocking synchronous
> > call
> > +         * for every command issued earlier,hence the internal
> > state
> > +         * is already consistent by the time SYNC command is
> > executed.
> > +         */
> > +    }
> > +
> > +    offset += NUM_BYTES_IN_DW;
> > +    return res;
> > +}
> 
> TODO: review process_mapd and process_mapc
> 
> > +
> > +static void update_cte(GICv3ITSState *s, uint16_t icid, uint64_t
> > cte)
> > +{
> > +    AddressSpace *as = &s->gicv3->sysmem_as;
> > +    uint64_t value;
> > +    uint8_t  page_sz_type;
> > +    uint64_t l2t_addr;
> > +    bool valid_l2t;
> > +    uint32_t l2t_id;
> > +    uint32_t page_sz = 0;
> > +    uint32_t max_l2_entries;
> 
> I think it's worth having a comment here:
> 
> /*
>  * The specification defines the format of level 1 entries of a
>  * 2-level table, but the format of level 2 entries and the format
>  * of flat-mapped tables is IMPDEF.
>  */
> 
> Q: why have you chosen to make the level-2 and flatmap entries
> 64 bits here ? They only need to contain the valid bit plus a
> 16 bit processor number. (We know the processor number fits in 16
> bits because the GICR_TYPER.Processor_Number field in the
> redistributor
> is that large.) Is there something I'm missing in the spec that makes
> 64-bit entries a good choice anyway ?
> 
> I chose 64 bits to be able to have the data structure ready for both
the processor number and RD_base address possibilities, in case we add
RD_Base address support in the future
> > +
> > +    if (s->ct.indirect) {
> > +        /* 2 level table */
> > +        page_sz_type = FIELD_EX64(s->baser[1], GITS_BASER,
> > PAGESIZE);
> > +
> > +        if (page_sz_type == 0) {
> > +            page_sz = GITS_ITT_PAGE_SIZE_0;
> > +        } else if (page_sz_type == 1) {
> > +            page_sz = GITS_ITT_PAGE_SIZE_1;
> > +        } else if (page_sz_type == 2) {
> > +            page_sz = GITS_ITT_PAGE_SIZE_2;
> > +        }
> 
> page_sz_type == 3 has a defined meaning: must be treated like 2.
> 
> If we're caching stuff in s->ct, maybe we should cache page_sz too ?
> 
> > +
> > +        l2t_id = icid / (page_sz / L1TABLE_ENTRY_SIZE);
> > +
> > +        value = address_space_ldq_le(as,
> > +                                     s->ct.base_addr +
> > +                                     (l2t_id *
> > L1TABLE_ENTRY_SIZE),
> > +                                     MEMTXATTRS_UNSPECIFIED,
> > NULL);
> > +
> > +        valid_l2t = (value >> VALID_SHIFT) & VALID_MASK;
> > +
> > +        if (valid_l2t) {
> > +            max_l2_entries = page_sz / s->ct.entry_sz;
> > +
> > +            l2t_addr = (value >> page_sz_type) &
> > +                        ((1ULL << (51 - page_sz_type)) - 1);
> 
> The spec text could be more clearly worded, but I don't think this is
> how
> the physaddr is encoded in the L1 descriptor. I think that the intent
> is
> that bits [51:0] are the physical address, with bits [N-1:0] of that
> being 0
> because the L2 table must be page aligned. Your intepretation (that
> the
> physaddr bit 0 is in bit N of the descriptor) wouldn't allow L2
> tables
> to be anywhere in the address space.
> 
> > +
> > +            address_space_write(as, l2t_addr +
> > +                                 ((icid % max_l2_entries) *
> > GITS_CTE_SIZE),
> > +                                 MEMTXATTRS_UNSPECIFIED,
> > +                                 &cte, sizeof(cte));
> 
> Writing the CTE word using address_space_write() like this will
> do the wrong thing on big-endian hosts. address_space_write() is the
> "write a string of bytes to guest memory" function, so it will write
> the uint64_t in whatever the host's byte order is. You want a fixed
> byte order, so use address_space_ldq_le() or similar.
> 
> > +        }
> > +    } else {
> > +        /* Flat level table */
> > +        address_space_write(as, s->ct.base_addr + (icid *
> > GITS_CTE_SIZE),
> > +                            MEMTXATTRS_UNSPECIFIED, &cte,
> > +                            sizeof(cte));
> > +    }
> > +}
> 
> I feel like there's a fair amount of code duplication between
> get_cte(), get_dte(), update_cte() and update_dte() which could
> perhaps be refactored out.
> 
> > +static MemTxResult process_mapc(GICv3ITSState *s, uint32_t offset)
> > +{
> > +    AddressSpace *as = &s->gicv3->sysmem_as;
> > +    uint16_t icid;
> > +    uint64_t rdbase;
> > +    bool valid;
> > +    bool pta = false;
> > +    MemTxResult res = MEMTX_OK;
> > +    uint64_t cte_entry;
> > +    uint64_t value;
> > +
> > +    offset += NUM_BYTES_IN_DW;
> > +    offset += NUM_BYTES_IN_DW;
> > +
> > +    value = address_space_ldq_le(as, s->cq.base_addr + offset,
> > +                                 MEMTXATTRS_UNSPECIFIED, &res);
> > +
> > +    icid = value & ICID_MASK;
> > +
> > +    if (FIELD_EX64(s->typer, GITS_TYPER, PTA)) {
> > +        /*
> > +         * only bits[47:16] are considered instead of bits [51:16]
> > +         * since with a physical address the target address must
> > be
> > +         * 64KB aligned
> > +         */
> > +        rdbase = (value >> RDBASE_OFFSET) & RDBASE_MASK;
> > +        pta = true;
> > +    } else {
> > +        rdbase = (value >> RDBASE_OFFSET) & RDBASE_PROCNUM_MASK;
> > +    }
> 
> GITS_TYPER.PTA is a read-only bit where we report to the guest how
> our implementation behaves (ie whether these are processor numbers
> or redistributor base addresses). We should implement one thing,
> whichever is the easy one to implement for us, not both.
> 
> The specification is giving implementations a choice here because
> there are different ways an ITS-to-redistributor interface might
> work:
>  * the ITS might talk to the redistributor by literally writing to
>    its registers via DMA (in which case setting GITS_TYPER.PTA makes
>    its life easier)
>  * the ITS might have its own backdoor or internal communications
>    channel to the redistributors (in which case it wants to know a
>    CPU number)
> 
> Looking forward, in patch 6 you add a comment:
>   * Current implementation only supports rdbase == procnum
>   * Hence rdbase physical address is ignored
> 
> So we're the second type of implementation. We should set
> GITS_TYPER.PTA=0, and then this code can simply assume that
> the data is always the "it's a CPU number" format.
> 
> > +
> > +    valid = (value >> VALID_SHIFT) & VALID_MASK;
> > +
> > +    if (valid) {
> > +        if ((icid > s->ct.max_collids) || (!pta &&
> > +                (rdbase > s->gicv3->num_cpu))) {
> > +            if (FIELD_EX64(s->typer, GITS_TYPER, SEIS)) {
> > +                /* Generate System Error here if supported */
> > +            }
> 
> Again, GITS_TYPER.SEIS is a bit which we use to tell the guest
> whether
> we're going to generate system errors or not; it's not a bit which
> we read in order to decide whether to generate them. For QEMU, the
> CPU itself doesn't have system-error support properly yet, so for
> us SEIS should be zero. In the ITS code we thus have two choices:
>  (1) just don't put anything in the code
>  (2) add a function call to an empty function called something
>      like its_raise_serror() which just has a comment noting that
>      it's a placeholder to mark where we would need to raise an
> SError
>      if we wanted to support it
> 
> I think I would favour option 1.
> 
> > +            qemu_log_mask(LOG_GUEST_ERROR,
> > +                "%s: invalid collection table attributes "
> > +                "icid %d rdbase %lu\n", __func__, icid, rdbase);
> 
> This kind of LOG_GUEST_ERROR logging doesn't need to include the
> __func__ string:
> saying "ITS MAPC command" is more comprehensible to a reader of the
> log who doesn't necessarily want to look into the QEMU source code
> than saying "process_mapc".
> 
> > +            /*
> > +             * in this implementation,in case of error
> > +             * we ignore this command and move onto the next
> > +             * command in the queue
> > +             */
> > +        } else {
> > +            if (s->ct.valid) {
> > +                /* add mapping entry to collection table */
> > +                cte_entry = (valid & VALID_MASK) |
> > +                            (pta ? ((rdbase & RDBASE_MASK) <<
> > 1ULL) :
> > +                            ((rdbase & RDBASE_PROCNUM_MASK) <<
> > 1ULL));
> > +
> > +                update_cte(s, icid, cte_entry);
> > +            }
> 
> Put the s->ct.valid check inside update_cte(). Pass the
> various parameters -- valid, pta, rdbase -- to update_cte()
> and have it do the assembly into a word. That way all the code
> dealing with the specific format of the table is inside that
> function,
> rather than being partly here and partly there.
> 
> > +        }
> > +    } else {
> > +        if (s->ct.valid) {
> > +            /* remove mapping entry from collection table */
> > +            cte_entry = 0;
> > +
> > +            update_cte(s, icid, cte_entry);
> > +        }
> > +    }
> > +
> > +    offset += NUM_BYTES_IN_DW;
> > +    offset += NUM_BYTES_IN_DW;
> > +
> > +    return res;
> > +}
> > +
> > +static void update_dte(GICv3ITSState *s, uint32_t devid, uint64_t
> > dte)
> > +{
> > +    AddressSpace *as = &s->gicv3->sysmem_as;
> > +    uint64_t value;
> > +    uint8_t  page_sz_type;
> > +    uint64_t l2t_addr;
> > +    bool valid_l2t;
> > +    uint32_t l2t_id;
> > +    uint32_t page_sz = 0;
> > +    uint32_t max_l2_entries;
> > +
> > +    if (s->dt.indirect) {
> > +        /* 2 level table */
> > +        page_sz_type = FIELD_EX64(s->baser[0], GITS_BASER,
> > PAGESIZE);
> > +
> > +        if (page_sz_type == 0) {
> > +            page_sz = GITS_ITT_PAGE_SIZE_0;
> > +        } else if (page_sz_type == 1) {
> > +            page_sz = GITS_ITT_PAGE_SIZE_1;
> > +        } else if (page_sz_type == 2) {
> > +            page_sz = GITS_ITT_PAGE_SIZE_2;
> > +        }
> > +
> > +        l2t_id = devid / (page_sz / L1TABLE_ENTRY_SIZE);
> > +
> > +        value = address_space_ldq_le(as,
> > +                                     s->dt.base_addr +
> > +                                     (l2t_id *
> > L1TABLE_ENTRY_SIZE),
> > +                                     MEMTXATTRS_UNSPECIFIED,
> > NULL);
> > +
> > +        valid_l2t = (value >> VALID_SHIFT) & VALID_MASK;
> > +
> > +        if (valid_l2t) {
> > +            max_l2_entries = page_sz / s->dt.entry_sz;
> > +
> > +            l2t_addr = (value >> page_sz_type) &
> > +                        ((1ULL << (51 - page_sz_type)) - 1);
> > +
> > +            address_space_write(as, l2t_addr +
> > +                                 ((devid % max_l2_entries) *
> > GITS_DTE_SIZE),
> > +                                 MEMTXATTRS_UNSPECIFIED, &dte,
> > sizeof(dte));
> > +        }
> > +    } else {
> > +        /* Flat level table */
> > +        address_space_write(as, s->dt.base_addr + (devid *
> > GITS_DTE_SIZE),
> > +                            MEMTXATTRS_UNSPECIFIED, &dte,
> > sizeof(dte));
> > +    }
> > +}
> > +
> > +static MemTxResult process_mapd(GICv3ITSState *s, uint64_t value,
> > +                                 uint32_t offset)
> > +{
> > +    AddressSpace *as = &s->gicv3->sysmem_as;
> > +    uint32_t devid;
> > +    uint8_t size;
> > +    uint64_t itt_addr;
> > +    bool valid;
> > +    MemTxResult res = MEMTX_OK;
> > +    uint64_t dte_entry = 0;
> > +
> > +    devid = (value >> DEVID_OFFSET) & DEVID_MASK;
> > +
> > +    offset += NUM_BYTES_IN_DW;
> > +    value = address_space_ldq_le(as, s->cq.base_addr + offset,
> > +                                 MEMTXATTRS_UNSPECIFIED, &res);
> > +    size = (value & SIZE_MASK);
> > +
> > +    offset += NUM_BYTES_IN_DW;
> > +    value = address_space_ldq_le(as, s->cq.base_addr + offset,
> > +                                 MEMTXATTRS_UNSPECIFIED, &res);
> > +    itt_addr = (value >> ITTADDR_OFFSET) & ITTADDR_MASK;
> > +
> > +    valid = (value >> VALID_SHIFT) & VALID_MASK;
> > +
> > +    if (valid) {
> > +        if ((devid > s->dt.max_devids) ||
> > +            (size > FIELD_EX64(s->typer, GITS_TYPER, IDBITS))) {
> > +            if (FIELD_EX64(s->typer, GITS_TYPER, SEIS)) {
> > +                /* Generate System Error here if supported */
> > +            }
> > +            qemu_log_mask(LOG_GUEST_ERROR,
> > +                "%s: invalid device table attributes "
> > +                "devid %d or size %d\n", __func__, devid, size);
> > +            /*
> > +             * in this implementation,in case of error
> > +             * we ignore this command and move onto the next
> > +             * command in the queue
> > +             */
> > +        } else {
> > +            if (s->dt.valid) {
> > +                /* add mapping entry to device table */
> > +                dte_entry = (valid & VALID_MASK) |
> > +                            ((size & SIZE_MASK) << 1U) |
> > +                            ((itt_addr & ITTADDR_MASK) << 6ULL);
> > +
> > +                update_dte(s, devid, dte_entry);
> > +            }
> > +        }
> > +    } else {
> > +        if (s->dt.valid) {
> > +            /* remove mapping entry from device table */
> > +            dte_entry = 0;
> > +            update_dte(s, devid, dte_entry);
> > +        }
> > +    }
> > +
> > +    offset += NUM_BYTES_IN_DW;
> > +    offset += NUM_BYTES_IN_DW;
> > +
> > +    return res;
> > +}
> 
> Review comments for process_mapc() and update_cte() apply also to
> process_mapd() and update_dte().
> 
> > +
> > +/*
> > + * Current implementation blocks until all
> > + * commands are processed
> > + */
> > +static MemTxResult process_cmdq(GICv3ITSState *s)
> 
> This should return 'void' (see comments earlier).
> 
> > +{
> > +    uint32_t wr_offset = 0;
> > +    uint32_t rd_offset = 0;
> > +    uint32_t cq_offset = 0;
> > +    uint64_t data;
> > +    AddressSpace *as = &s->gicv3->sysmem_as;
> > +    MemTxResult res = MEMTX_OK;
> > +    uint8_t cmd;
> > +
> > +    wr_offset = FIELD_EX64(s->cwriter, GITS_CWRITER, OFFSET);
> > +
> > +    if (wr_offset > s->cq.max_entries) {
> > +        qemu_log_mask(LOG_GUEST_ERROR,
> > +                        "%s: invalid write offset "
> > +                        "%d\n", __func__, wr_offset);
> > +        res = MEMTX_ERROR;
> > +        return res;
> > +    }
> > +
> > +    rd_offset = FIELD_EX64(s->creadr, GITS_CREADR, OFFSET);
> > +
> > +    while (wr_offset != rd_offset) {
> > +        cq_offset = (rd_offset * GITS_CMDQ_ENTRY_SIZE);
> > +        data = address_space_ldq_le(as, s->cq.base_addr +
> > cq_offset,
> > +                                      MEMTXATTRS_UNSPECIFIED,
> > &res);
> 
> I think I would just read all four quadwords of the command here
> into a local uint64_t data[4], and pass that to the process_$cmd()
> functions. That saves those functions doing the loads and handling
> possible errors doing the loads themselves.
> 
> There are 2 reasons i didn't pick reading all 4 quadwords at once:-
> 1) not all commands have valid quadword data making the dma call
> for such RES0 quadwords insignificant
> 2) Since we are now stalling on any dma read/write,in case we fail
>    during RES0 quadword read ,stalling doesnt seem significant
>
> Have optimised most of the similar-looking boilerplate code though
> > +        cmd = (data & CMD_MASK);
> > +
> > +        switch (cmd) {
> > +        case GITS_CMD_INT:
> > +            break;
> > +        case GITS_CMD_CLEAR:
> > +            break;
> > +        case GITS_CMD_SYNC:
> > +            res = process_sync(s, cq_offset);
> > +            break;
> > +        case GITS_CMD_MAPD:
> > +            res = process_mapd(s, data, cq_offset);
> > +            break;
> > +        case GITS_CMD_MAPC:
> > +            res = process_mapc(s, cq_offset);
> > +            break;
> > +        case GITS_CMD_MAPTI:
> > +            break;
> > +        case GITS_CMD_MAPI:
> > +            break;
> > +        case GITS_CMD_DISCARD:
> > +            break;
> > +        default:
> > +            break;
> > +        }
> > +        if (res == MEMTX_OK) {
> > +            rd_offset++;
> > +            rd_offset %= s->cq.max_entries;
> > +            s->creadr = FIELD_DP64(s->creadr, GITS_CREADR, OFFSET,
> > rd_offset);
> > +        } else {
> > +            qemu_log_mask(LOG_GUEST_ERROR,
> > +                "%s: %x cmd processing failed!!\n", __func__,
> > cmd);
> > +            break;
> > +        }
> > +    }
> > +    return res;
> > +}
> > +
> >  static bool extract_table_params(GICv3ITSState *s, int index)
> >  {
> >      uint16_t num_pages = 0;
> > @@ -282,6 +623,9 @@ static MemTxResult its_writel(GICv3ITSState *s,
> > hwaddr offset,
> >          break;
> >      case GITS_CWRITER:
> >          s->cwriter = deposit64(s->cwriter, 0, 32, value);
> > +        if ((s->ctlr & ITS_CTLR_ENABLED) && (s->cwriter != s-
> > >creadr)) {
> > +            result = process_cmdq(s);
> > +        }
> >          break;
> >      case GITS_CWRITER + 4:
> >          s->cwriter = deposit64(s->cwriter, 32, 32, value);
> > @@ -416,6 +760,9 @@ static MemTxResult its_writell(GICv3ITSState
> > *s, hwaddr offset,
> >          break;
> >      case GITS_CWRITER:
> >          s->cwriter = value;
> > +        if ((s->ctlr & ITS_CTLR_ENABLED) && (s->cwriter != s-
> > >creadr)) {
> > +            result = process_cmdq(s);
> > +        }
> 
> I would move the check on CTLR.ENABLED into process_cmdq() itself
> (which is already doing a writer != reader check).
> 
> >          break;
> >      case GITS_TYPER:
> >      case GITS_CREADR:
> > diff --git a/hw/intc/gicv3_internal.h b/hw/intc/gicv3_internal.h
> > index b7701e8ca5..7e1ff426ef 100644
> > --- a/hw/intc/gicv3_internal.h
> > +++ b/hw/intc/gicv3_internal.h
> > @@ -253,6 +253,12 @@ FIELD(GITS_CBASER, OUTERCACHE, 53, 3)
> >  FIELD(GITS_CBASER, INNERCACHE, 59, 3)
> >  FIELD(GITS_CBASER, VALID, 63, 1)
> > 
> > +FIELD(GITS_CREADR, STALLED, 0, 1)
> > +FIELD(GITS_CREADR, OFFSET, 5, 15)
> > +
> > +FIELD(GITS_CWRITER, RETRY, 0, 1)
> > +FIELD(GITS_CWRITER, OFFSET, 5, 15)
> > +
> >  FIELD(GITS_CTLR, ENABLED, 0, 1)
> >  FIELD(GITS_CTLR, QUIESCENT, 31, 1)
> > 
> > @@ -284,6 +290,41 @@ FIELD(GITS_TYPER, CIL, 36, 1)
> >  #define L1TABLE_ENTRY_SIZE         8
> > 
> >  #define GITS_CMDQ_ENTRY_SIZE               32
> > +#define NUM_BYTES_IN_DW                     8
> > +
> > +#define CMD_MASK                  0xff
> > +
> > +/* ITS Commands */
> > +#define GITS_CMD_CLEAR            0x04
> > +#define GITS_CMD_DISCARD          0x0F
> > +#define GITS_CMD_INT              0x03
> > +#define GITS_CMD_MAPC             0x09
> > +#define GITS_CMD_MAPD             0x08
> > +#define GITS_CMD_MAPI             0x0B
> > +#define GITS_CMD_MAPTI            0x0A
> > +#define GITS_CMD_SYNC             0x05
> > +
> > +/* MAPC command fields */
> > +#define ICID_LEN                  16
> > +#define ICID_MASK                 ((1U << ICID_LEN) - 1)
> > +#define RDBASE_LEN                32
> > +#define RDBASE_OFFSET             16
> > +#define RDBASE_MASK               ((1ULL << RDBASE_LEN) - 1)
> 
> If you want to define these by hand, you could at least use the same
> suffixes that the FIELD() macro does: _SHIFT, _LENGTH and _MASK.
> 
> > +#define RDBASE_PROCNUM_LEN        16
> > +#define RDBASE_PROCNUM_MASK       ((1ULL << RDBASE_PROCNUM_LEN) -
> > 1)
> > +
> > +#define DEVID_OFFSET              32
> > +#define DEVID_LEN                 32
> > +#define DEVID_MASK                ((1ULL << DEVID_LEN) - 1)
> > +
> > +/* MAPD command fields */
> > +#define ITTADDR_LEN               44
> > +#define ITTADDR_OFFSET            8
> > +#define ITTADDR_MASK              ((1ULL << ITTADDR_LEN) - 1)
> > +#define SIZE_MASK                 0x1f
> > +
> > +#define VALID_SHIFT               63
> > +#define VALID_MASK                0x1
> > 
> >  /**
> >   * Default features advertised by this version of ITS
> > --
> > 2.27
> 
> thanks
> -- PMM



^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH v2 5/8] hw/intc: GICv3 ITS Feature enablement
  2021-04-19 10:51   ` Peter Maydell
@ 2021-04-29 23:39     ` shashi.mallela
  0 siblings, 0 replies; 25+ messages in thread
From: shashi.mallela @ 2021-04-29 23:39 UTC (permalink / raw)
  To: Peter Maydell
  Cc: Leif Lindholm, QEMU Developers, qemu-arm, Radoslaw Biernacki

On Mon, 2021-04-19 at 11:51 +0100, Peter Maydell wrote:
> On Thu, 1 Apr 2021 at 03:41, Shashi Mallela <
> shashi.mallela@linaro.org> wrote:
> > Added properties to enable ITS feature and define qemu system
> > address space memory in gicv3 common,setup distributor and
> > redistributor registers to indicate LPI support.
> > 
> > Signed-off-by: Shashi Mallela <shashi.mallela@linaro.org>
> > ---
> >  hw/intc/arm_gicv3_common.c         | 16 ++++++++++++++++
> >  hw/intc/arm_gicv3_dist.c           | 22 ++++++++++++++++++++--
> >  hw/intc/arm_gicv3_redist.c         | 28 +++++++++++++++++++++++++-
> > --
> >  hw/intc/gicv3_internal.h           | 17 +++++++++++++++++
> >  include/hw/intc/arm_gicv3_common.h |  8 ++++++++
> >  5 files changed, 86 insertions(+), 5 deletions(-)
> > 
> > diff --git a/hw/intc/arm_gicv3_common.c
> > b/hw/intc/arm_gicv3_common.c
> > index 58ef65f589..3bfc52f7fa 100644
> > --- a/hw/intc/arm_gicv3_common.c
> > +++ b/hw/intc/arm_gicv3_common.c
> > @@ -156,6 +156,7 @@ static const VMStateDescription
> > vmstate_gicv3_cpu = {
> >          VMSTATE_UINT32(gicr_waker, GICv3CPUState),
> >          VMSTATE_UINT64(gicr_propbaser, GICv3CPUState),
> >          VMSTATE_UINT64(gicr_pendbaser, GICv3CPUState),
> > +        VMSTATE_BOOL(lpi_outofrange, GICv3CPUState),
> >          VMSTATE_UINT32(gicr_igroupr0, GICv3CPUState),
> >          VMSTATE_UINT32(gicr_ienabler0, GICv3CPUState),
> >          VMSTATE_UINT32(gicr_ipendr0, GICv3CPUState),
> > @@ -227,6 +228,7 @@ static const VMStateDescription vmstate_gicv3 =
> > {
> >      .priority = MIG_PRI_GICV3,
> >      .fields = (VMStateField[]) {
> >          VMSTATE_UINT32(gicd_ctlr, GICv3State),
> > +        VMSTATE_UINT32(gicd_typer, GICv3State),
> >          VMSTATE_UINT32_ARRAY(gicd_statusr, GICv3State, 2),
> >          VMSTATE_UINT32_ARRAY(group, GICv3State, GICV3_BMP_SIZE),
> >          VMSTATE_UINT32_ARRAY(grpmod, GICv3State, GICV3_BMP_SIZE),
> 
> You can't add fields to an existing VMStateDescription like this
> without extra effort to handle migration compatibility.
> What are we trying to achieve with the extra fields?
> GICD_TYPER is read-only, I think.
> I don't think we need an lpi_outofrange flag: we should just
> naturally
> handle this as part of working with the GITR_PROPBASER.IDbits field.
> 
> Removed all fields to VMStateDescription,handled as part of working 
> with GITR_PROPBASER.IDbits field
>
> > @@ -381,6 +383,16 @@ static void
> > arm_gicv3_common_realize(DeviceState *dev, Error **errp)
> >              (1 << 24) |
> >              (i << 8) |
> >              (last << 4);
> > +
> > +        if (s->lpi_enable) {
> > +            s->cpu[i].gicr_typer |= GICR_TYPER_PLPIS;
> > +
> > +            if (!s->sysmem) {
> > +                error_setg(errp,
> > +                    "Redist-ITS: Guest 'sysmem' reference link not
> > set");
> > +                return;
> > +            }
> > +        }
> >      }
> >  }
> > 
> > @@ -406,6 +418,7 @@ static void arm_gicv3_common_reset(DeviceState
> > *dev)
> >          cs->gicr_waker = GICR_WAKER_ProcessorSleep |
> > GICR_WAKER_ChildrenAsleep;
> >          cs->gicr_propbaser = 0;
> >          cs->gicr_pendbaser = 0;
> > +        cs->lpi_outofrange = false;
> >          /* If we're resetting a TZ-aware GIC as if secure firmware
> >           * had set it up ready to start a kernel in non-secure, we
> >           * need to set interrupts to group 1 so the kernel can use
> > them.
> > @@ -494,9 +507,12 @@ static Property arm_gicv3_common_properties[]
> > = {
> >      DEFINE_PROP_UINT32("num-cpu", GICv3State, num_cpu, 1),
> >      DEFINE_PROP_UINT32("num-irq", GICv3State, num_irq, 32),
> >      DEFINE_PROP_UINT32("revision", GICv3State, revision, 3),
> > +    DEFINE_PROP_BOOL("has-lpi", GICv3State, lpi_enable, 0),
> >      DEFINE_PROP_BOOL("has-security-extensions", GICv3State,
> > security_extn, 0),
> >      DEFINE_PROP_ARRAY("redist-region-count", GICv3State,
> > nb_redist_regions,
> >                        redist_region_count, qdev_prop_uint32,
> > uint32_t),
> > +    DEFINE_PROP_LINK("sysmem", GICv3State, sysmem,
> > TYPE_MEMORY_REGION,
> > +                     MemoryRegion *),
> >      DEFINE_PROP_END_OF_LIST(),
> >  };
> > 
> > diff --git a/hw/intc/arm_gicv3_dist.c b/hw/intc/arm_gicv3_dist.c
> > index b65f56f903..96a317a8ef 100644
> > --- a/hw/intc/arm_gicv3_dist.c
> > +++ b/hw/intc/arm_gicv3_dist.c
> > @@ -366,12 +366,15 @@ static MemTxResult gicd_readl(GICv3State *s,
> > hwaddr offset,
> >          return MEMTX_OK;
> >      case GICD_TYPER:
> >      {
> > +        bool lpi_supported = false;
> >          /* For this implementation:
> >           * No1N == 1 (1-of-N SPI interrupts not supported)
> >           * A3V == 1 (non-zero values of Affinity level 3
> > supported)
> >           * IDbits == 0xf (we support 16-bit interrupt identifiers)
> >           * DVIS == 0 (Direct virtual LPI injection not supported)
> > -         * LPIS == 0 (LPIs not supported)
> > +         * LPIS == 1 (LPIs are supported if affinity routing is
> > enabled)
> > +         * num_LPIs == 0b00000 (bits [15:11],Number of LPIs as
> > indicated
> > +         *                      by GICD_TYPER.IDbits)
> 
> We support LPIs, but we have none ? That doesn't sound right, and
> it's
> not what the code below reports.
> The interpretation as per spec here is that number of LPIs indication
> comes from GICD_TYPER.IDbits with bits being set to 0b00000
> 
> >           * MBIS == 0 (message-based SPIs not supported)
> >           * SecurityExtn == 1 if security extns supported
> >           * CPUNumber == 0 since for us ARE is always 1
> > @@ -385,8 +388,23 @@ static MemTxResult gicd_readl(GICv3State *s,
> > hwaddr offset,
> >           */
> >          bool sec_extn = !(s->gicd_ctlr & GICD_CTLR_DS);
> > 
> > +        /*
> > +         * With securityextn on,LPIs are supported when affinity
> > routing
> 
> (missing space after comma)
> 
> > +         * is enabled for non-secure state and if off LPIs are
> > supported
> > +         * when affinity routing is enabled.
> > +         */
> > +        if (s->lpi_enable) {
> > +            if (sec_extn) {
> > +                lpi_supported = (s->gicd_ctlr & GICD_CTLR_ARE_NS);
> > +            } else {
> > +                lpi_supported = (s->gicd_ctlr & GICD_CTLR_ARE);
> > +            }
> > +        }
> > +
> >          *data = (1 << 25) | (1 << 24) | (sec_extn << 10) |
> > -            (0xf << 19) | itlinesnumber;
> > +            (lpi_supported << GICD_TYPER_LPIS_OFFSET) |
> > (GICD_TYPER_IDBITS <<
> > +            GICD_TYPER_IDBITS_OFFSET) | itlinesnumber;
> > +        s->gicd_typer = *data;
> >          return MEMTX_OK;
> >      }
> >      case GICD_IIDR:
> > diff --git a/hw/intc/arm_gicv3_redist.c
> > b/hw/intc/arm_gicv3_redist.c
> > index 8645220d61..325b974e70 100644
> > --- a/hw/intc/arm_gicv3_redist.c
> > +++ b/hw/intc/arm_gicv3_redist.c
> > @@ -248,10 +248,16 @@ static MemTxResult gicr_writel(GICv3CPUState
> > *cs, hwaddr offset,
> >      case GICR_CTLR:
> >          /* For our implementation, GICR_TYPER.DPGS is 0 and so all
> >           * the DPG bits are RAZ/WI. We don't do anything
> > asynchronously,
> > -         * so UWP and RWP are RAZ/WI. And GICR_TYPER.LPIS is 0 (we
> > don't
> > -         * implement LPIs) so Enable_LPIs is RES0. So there are no
> > writable
> > -         * bits for us.
> > +         * so UWP and RWP are RAZ/WI. GICR_TYPER.LPIS is 1 (we
> > +         * implement LPIs) so Enable_LPIs is programmable.
> >           */
> > +        if (cs->gicr_typer & GICR_TYPER_PLPIS) {
> > +            if (value & GICR_CTLR_ENABLE_LPIS) {
> > +                cs->gicr_ctlr |= GICR_CTLR_ENABLE_LPIS;
> > +            } else {
> > +                cs->gicr_ctlr &= ~GICR_CTLR_ENABLE_LPIS;
> > +            }
> > +        }
> >          return MEMTX_OK;
> >      case GICR_STATUSR:
> >          /* RAZ/WI for our implementation */
> > @@ -275,6 +281,14 @@ static MemTxResult gicr_writel(GICv3CPUState
> > *cs, hwaddr offset,
> >          cs->gicr_waker = value;
> >          return MEMTX_OK;
> >      case GICR_PROPBASER:
> > +        if (FIELD_EX64(value, GICR_PROPBASER, IDBITS) <
> > +            GICR_PROPBASER_IDBITS_THRESHOLD) {
> > +            cs->lpi_outofrange = true;
> 
> I don't think you should need to special case this. It just means
> "turns out
> that the LPI table is effectively zero length". When code doing LPI
> table
> lookups calculates the LPI table size (which it needs to do anyway)
> it can
> just catch the "turns out to be negative" case then.
> 
> > +        }
> > +        if (FIELD_EX64(value, GICR_PROPBASER, IDBITS) >
> > GICD_TYPER_IDBITS) {
> > +            value &= ~R_GICR_PROPBASER_IDBITS_MASK;
> > +            value |= GICD_TYPER_IDBITS;
> > +        }
> 
> This isn't what the spec says happens. It says that if the value the
> guest writes
> in this field is larger than GICD_TYPER.IDbits, then the
> GICD_TYPER.IDBits value
> applies. That doesn't mean the value reads back as GICD_TYPER.IDBits.
> 
> How you want to handle this depends on what's going on, but it
> probably mostly
> looks like "code that cares about GICR_PROPBASER.IDBits will do
> MIN(field_value, GICD_TYPER_IDBITS) to find the effective value of
> the field".
> 
> >          cs->gicr_propbaser = deposit64(cs->gicr_propbaser, 0, 32,
> > value);
> >          return MEMTX_OK;
> >      case GICR_PROPBASER + 4:
> > @@ -397,6 +411,14 @@ static MemTxResult gicr_writell(GICv3CPUState
> > *cs, hwaddr offset,
> >  {
> >      switch (offset) {
> >      case GICR_PROPBASER:
> > +        if (FIELD_EX64(value, GICR_PROPBASER, IDBITS) <
> > +            GICR_PROPBASER_IDBITS_THRESHOLD) {
> > +            cs->lpi_outofrange = true;
> > +        }
> > +        if (FIELD_EX64(value, GICR_PROPBASER, IDBITS) >
> > GICD_TYPER_IDBITS) {
> > +            value &= ~R_GICR_PROPBASER_IDBITS_MASK;
> > +            value |= GICD_TYPER_IDBITS;
> > +        }
> 
> Same comments as above.
> 
> >          cs->gicr_propbaser = value;
> >          return MEMTX_OK;
> >      case GICR_PENDBASER:
> > diff --git a/hw/intc/gicv3_internal.h b/hw/intc/gicv3_internal.h
> > index e9f9aa6722..a2718704d4 100644
> > --- a/hw/intc/gicv3_internal.h
> > +++ b/hw/intc/gicv3_internal.h
> > @@ -68,6 +68,9 @@
> >  #define GICD_CTLR_E1NWF             (1U << 7)
> >  #define GICD_CTLR_RWP               (1U << 31)
> > 
> > +#define GICD_TYPER_LPIS_OFFSET         17
> > +#define GICD_TYPER_IDBITS_OFFSET       19
> > +#define GICD_TYPER_IDBITS_MASK       0x1f
> >  /* 16 bits EventId */
> >  #define GICD_TYPER_IDBITS            0xf
> > 
> > @@ -126,6 +129,20 @@
> >  #define GICR_WAKER_ProcessorSleep    (1U << 1)
> >  #define GICR_WAKER_ChildrenAsleep    (1U << 2)
> > 
> > +FIELD(GICR_PROPBASER, IDBITS, 0, 5)
> > +FIELD(GICR_PROPBASER, INNERCACHE, 7, 3)
> > +FIELD(GICR_PROPBASER, SHAREABILITY, 10, 2)
> > +FIELD(GICR_PROPBASER, PHYADDR, 12, 40)
> > +FIELD(GICR_PROPBASER, OUTERCACHE, 56, 3)
> > +
> > +#define GICR_PROPBASER_IDBITS_THRESHOLD          0xd
> > +
> > +FIELD(GICR_PENDBASER, INNERCACHE, 7, 3)
> > +FIELD(GICR_PENDBASER, SHAREABILITY, 10, 2)
> > +FIELD(GICR_PENDBASER, PHYADDR, 16, 36)
> > +FIELD(GICR_PENDBASER, OUTERCACHE, 56, 3)
> > +FIELD(GICR_PENDBASER, PTZ, 62, 1)
> > +
> >  #define ICC_CTLR_EL1_CBPR           (1U << 0)
> >  #define ICC_CTLR_EL1_EOIMODE        (1U << 1)
> >  #define ICC_CTLR_EL1_PMHE           (1U << 6)
> > diff --git a/include/hw/intc/arm_gicv3_common.h
> > b/include/hw/intc/arm_gicv3_common.h
> > index 3a710592a9..db3989484d 100644
> > --- a/include/hw/intc/arm_gicv3_common.h
> > +++ b/include/hw/intc/arm_gicv3_common.h
> > @@ -175,6 +175,13 @@ struct GICv3CPUState {
> >      uint32_t gicr_nsacr;
> >      uint8_t gicr_ipriorityr[GIC_INTERNAL];
> > 
> > +    /*
> > +     * flag to indicate LPIs are out of range
> > +     * since IDbits from GICR_PROPBASER is less
> > +     * than 0b1101
> > +     */
> > +    bool lpi_outofrange;
> > +
> >      /* CPU interface */
> >      uint64_t icc_sre_el1;
> >      uint64_t icc_ctlr_el1[2];
> > @@ -221,6 +228,7 @@ struct GICv3State {
> >      uint32_t num_cpu;
> >      uint32_t num_irq;
> >      uint32_t revision;
> > +    bool lpi_enable;
> >      bool security_extn;
> >      bool irq_reset_nonsecure;
> >      bool gicd_no_migration_shift_bug;
> 
> thanks
> -- PMM



^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH v2 6/8] hw/intc: GICv3 redistributor ITS processing
       [not found]     ` <89852279ad379f2e50563dd47eb67376262355c0.camel@linaro.org>
@ 2021-04-29 23:40       ` shashi.mallela
  0 siblings, 0 replies; 25+ messages in thread
From: shashi.mallela @ 2021-04-29 23:40 UTC (permalink / raw)
  To: Peter Maydell
  Cc: Leif Lindholm, QEMU Developers, qemu-arm, Radoslaw Biernacki

On Thu, 2021-04-29 at 18:16 -0400, shashi.mallela@linaro.org wrote:
On Mon, 2021-04-19 at 13:44 +0100, Peter Maydell wrote:
> On Thu, 1 Apr 2021 at 03:41, Shashi Mallela <
> shashi.mallela@linaro.org> wrote:
> > Implemented lpi processing at redistributor to get lpi config info
> > from lpi configuration table,determine priority,set pending state
> > in
> > lpi pending table and forward the lpi to cpuif.Added logic to
> > invoke
> > redistributor lpi processing with translated LPI which set/clear
> > LPI
> > from ITS device as part of ITS INT,CLEAR,DISCARD command and
> > GITS_TRANSLATER processing.
> 
> Nit: commas should all have a space following.
> 
> > Signed-off-by: Shashi Mallela <shashi.mallela@linaro.org>
> > ---
> >  hw/intc/arm_gicv3.c        |   6 ++
> >  hw/intc/arm_gicv3_cpuif.c  |  15 +++--
> >  hw/intc/arm_gicv3_its.c    |   9 ++-
> >  hw/intc/arm_gicv3_redist.c | 124
> > +++++++++++++++++++++++++++++++++++++
> >  hw/intc/gicv3_internal.h   |   9 +++
> >  5 files changed, 158 insertions(+), 5 deletions(-)
> > 
> > diff --git a/hw/intc/arm_gicv3.c b/hw/intc/arm_gicv3.c
> > index 66eaa97198..618fa1af95 100644
> > --- a/hw/intc/arm_gicv3.c
> > +++ b/hw/intc/arm_gicv3.c
> > @@ -166,6 +166,12 @@ static void
> > gicv3_redist_update_noirqset(GICv3CPUState *cs)
> >          cs->hppi.grp = gicv3_irq_group(cs->gic, cs, cs->hppi.irq);
> >      }
> > 
> > +    if (cs->gic->lpi_enable) {
> > +        if (gicv3_redist_update_lpi(cs)) {
> > +            seenbetter = true;
> > +        }
> > +    }
> > +
> >      /* If the best interrupt we just found would preempt whatever
> >       * was the previous best interrupt before this update, then
> >       * we know it's definitely the best one now.
> > diff --git a/hw/intc/arm_gicv3_cpuif.c b/hw/intc/arm_gicv3_cpuif.c
> > index 43ef1d7a84..c225b80f66 100644
> > --- a/hw/intc/arm_gicv3_cpuif.c
> > +++ b/hw/intc/arm_gicv3_cpuif.c
> > @@ -899,9 +899,14 @@ static void icc_activate_irq(GICv3CPUState
> > *cs, int irq)
> >          cs->gicr_ipendr0 = deposit32(cs->gicr_ipendr0, irq, 1, 0);
> >          gicv3_redist_update(cs);
> >      } else {
> > -        gicv3_gicd_active_set(cs->gic, irq);
> > -        gicv3_gicd_pending_clear(cs->gic, irq);
> > -        gicv3_update(cs->gic, irq, 1);
> > +        if (irq >= GICV3_LPI_INTID_START) {
> > +            gicv3_redist_lpi_pending(cs, irq, 0);
> > +            gicv3_redist_update(cs);
> > +        } else {
> > +            gicv3_gicd_active_set(cs->gic, irq);
> > +            gicv3_gicd_pending_clear(cs->gic, irq);
> > +            gicv3_update(cs->gic, irq, 1);
> > +        }
> >      }
> >  }
> > 
> > @@ -1337,7 +1342,9 @@ static void icc_eoir_write(CPUARMState *env,
> > const ARMCPRegInfo *ri,
> >           * valid interrupt value read from the Interrupt
> > Acknowledge
> >           * register" and so this is UNPREDICTABLE. We choose to
> > ignore it.
> >           */
> > -        return;
> > +        if (!(cs->gic->lpi_enable && (irq >=
> > GICV3_LPI_INTID_START))) {
> > +            return;
> > +        }
> 
> This condition is in the wrong place. I think what you are trying to
> do is modify "what is the set of numbers we consider to be valid IRQ
> numbers", in which case you want to be changing the outer
> "if (irq >= cs->gic->num_irq)" condition, not adding an extra one
> inside it.
> 
> More importantly, the thing this condition is guarding is that
> the code below it assumes that IRQ numbers are in range for the
> GIC's own internal non-LPI interrupts. If you allow numbers
> > = cs->gic->num_irq through, then you will get array overruns
> when icc_deactivate_irq() tries to clear its Active bit.
> 
> >      }
> > 
> >      if (icc_highest_active_group(cs) != grp) {
> > diff --git a/hw/intc/arm_gicv3_its.c b/hw/intc/arm_gicv3_its.c
> > index 0e3f176809..41e1e8b2a8 100644
> > --- a/hw/intc/arm_gicv3_its.c
> > +++ b/hw/intc/arm_gicv3_its.c
> > @@ -226,6 +226,7 @@ static MemTxResult process_int(GICv3ITSState
> > *s, uint64_t value,
> >      bool ite_valid = false;
> >      uint64_t cte = 0;
> >      bool cte_valid = false;
> > +    uint64_t rdbase;
> >      uint8_t buff[ITS_ITT_ENTRY_SIZE];
> >      uint64_t itt_addr;
> > 
> > @@ -278,12 +279,18 @@ static MemTxResult process_int(GICv3ITSState
> > *s, uint64_t value,
> >               * since with a physical address the target address
> > must be
> >               * 64KB aligned
> >               */
> > -
> > +            rdbase = (cte >> 1U) & RDBASE_MASK;
> 
> What's going on here? Shouldn't this be in a previous patch ?
> > 
> No,this part relates to the ITS processing wherein the rdbase is
extracted and used for subsequent redistributor trigger
> > >              /*
> >               * Current implementation only supports rdbase ==
> > procnum
> >               * Hence rdbase physical address is ignored
> >               */
> >          } else {
> > +            rdbase = (cte >> 1U) & RDBASE_PROCNUM_MASK;
> > +            if ((cmd == CLEAR) || (cmd == DISCARD)) {
> > +                gicv3_redist_process_lpi(&s->gicv3->cpu[rdbase],
> > pIntid, 0);
> > +            } else {
> > +                gicv3_redist_process_lpi(&s->gicv3->cpu[rdbase],
> > pIntid, 1);
> > +            }
> > 
> >              if (cmd == DISCARD) {
> >                  /* remove mapping from interrupt translation table
> > */
> > diff --git a/hw/intc/arm_gicv3_redist.c
> > b/hw/intc/arm_gicv3_redist.c
> > index 325b974e70..71c648a616 100644
> > --- a/hw/intc/arm_gicv3_redist.c
> > +++ b/hw/intc/arm_gicv3_redist.c
> > @@ -254,6 +254,8 @@ static MemTxResult gicr_writel(GICv3CPUState
> > *cs, hwaddr offset,
> >          if (cs->gicr_typer & GICR_TYPER_PLPIS) {
> >              if (value & GICR_CTLR_ENABLE_LPIS) {
> >                  cs->gicr_ctlr |= GICR_CTLR_ENABLE_LPIS;
> > +                /* Check for any pending interr in pending table
> > */
> > +                gicv3_redist_update(cs);
> >              } else {
> >                  cs->gicr_ctlr &= ~GICR_CTLR_ENABLE_LPIS;
> >              }
> > @@ -548,6 +550,128 @@ MemTxResult gicv3_redist_write(void *opaque,
> > hwaddr offset, uint64_t data,
> >      return r;
> >  }
> > 
> > +bool gicv3_redist_update_lpi(GICv3CPUState *cs)
> 
> Could we have a comment explaining what this function does, please ?
> 
> > +{
> > +    AddressSpace *as = &cs->gic->sysmem_as;
> > +    uint64_t lpict_baddr, lpipt_baddr;
> > +    uint32_t pendt_size = 0;
> > +    uint8_t lpite;
> > +    uint8_t prio, pend;
> > +    int i;
> > +    bool seenbetter = false;
> > +
> > +    if ((!cs->gicr_ctlr & GICR_CTLR_ENABLE_LPIS) || !cs-
> > >gicr_propbaser ||
> > +        !cs->gicr_pendbaser || cs->lpi_outofrange) {
> > +        return seenbetter;
> > +    }
> > +
> > +    lpict_baddr = FIELD_EX64(cs->gicr_propbaser, GICR_PROPBASER,
> > PHYADDR);
> > +    lpict_baddr <<= R_GICR_PROPBASER_PHYADDR_SHIFT;
> > +
> > +    lpipt_baddr =  FIELD_EX64(cs->gicr_pendbaser, GICR_PENDBASER,
> > PHYADDR);
> > +    lpipt_baddr <<= R_GICR_PENDBASER_PHYADDR_SHIFT;
> > +
> > +    /* Determine the highest priority pending interrupt among LPIs
> > */
> > +    pendt_size = (1UL << (FIELD_EX64(cs->gicr_propbaser,
> > GICR_PROPBASER,
> > +                          IDBITS) - 1));
> 
> This is where you should be handling "actually the table is empty"
> or "table size is capped at the implementation's maximum size".
> 
> > +
> > +    for (i = 0; i < pendt_size; i++) {
> > +        address_space_read(as, lpipt_baddr +
> > +                (((GICV3_LPI_INTID_START + i) / 8) *
> > sizeof(pend)),
> > +                MEMTXATTRS_UNSPECIFIED, &pend, sizeof(pend));
> > +
> > +        if ((1 << ((GICV3_LPI_INTID_START + i) % 8)) & pend) {
> > +            address_space_read(as, lpict_baddr + (i *
> > sizeof(lpite)),
> > +                      MEMTXATTRS_UNSPECIFIED, &lpite,
> > sizeof(lpite));
> > +
> > +            prio = ((lpite >> LPI_CTE_PRIORITY_OFFSET) &
> > +                     LPI_CTE_PRIORITY_MASK);
> > +            prio &= LPI_PRIORITY_MASK;
> > +
> > +            if (prio < cs->hppi.prio) {
> > +                cs->hppi.irq = GICV3_LPI_INTID_START + i;
> > +                cs->hppi.prio = prio;
> > +                /* LPIs are always non-secure Grp1 interrupts */
> > +                cs->hppi.grp = GICV3_G1NS;
> > +                seenbetter = true;
> > +            }
> > +        }
> > +    }
> 
> Oof. How big is an LPI configuration table, typically? I'm not sure
> we want
> to be scanning the entire LPI configuration table in guest memory for
> every
> time gicv3_redist_update() is called if we can avoid it.
> 
> A bit of benchmarking of how much this slows down emulation might
> be interesting.
> 
> The implementation scans the full LPI pending table,but only scans
> the LPI config table entry for which the pending bit is set on
every 
> gicv3_redist_update.
> With preliminary testing on linux guest os and kvm-unit-tests didnt
observe any noticeable delay during the ITS processing
 
> > +    return seenbetter;
> > +}
> > +
> > +void gicv3_redist_lpi_pending(GICv3CPUState *cs, int irq, int
> > level)
> > +{
> > +    AddressSpace *as = &cs->gic->sysmem_as;
> > +    uint64_t lpipt_baddr;
> > +    bool ispend = false;
> > +    uint8_t pend;
> > +
> > +    /*
> > +     * get the bit value corresponding to this irq in the
> > +     * lpi pending table
> > +     */
> > +    lpipt_baddr = FIELD_EX64(cs->gicr_pendbaser, GICR_PENDBASER,
> > PHYADDR);
> > +    lpipt_baddr <<= R_GICR_PENDBASER_PHYADDR_SHIFT;
> > +
> > +    address_space_read(as, lpipt_baddr + ((irq / 8) *
> > sizeof(pend)),
> > +                         MEMTXATTRS_UNSPECIFIED, &pend,
> > sizeof(pend));
> > +    ispend = ((pend >> (irq % 8)) & 0x1);
> > +
> > +    if (ispend) {
> > +        if (!level) {
> > +            /*
> > +             * clear the pending bit and update the lpi pending
> > table
> > +             */
> > +            pend &= ~(1 << (irq % 8));
> > +
> > +            address_space_write(as, lpipt_baddr + ((irq / 8) *
> > sizeof(pend)),
> > +                                 MEMTXATTRS_UNSPECIFIED, &pend,
> > sizeof(pend));
> > +        }
> > +    } else {
> > +        if (level) {
> > +            /*
> > +             * if pending bit is not already set for this
> > irq,turn-on the
> > +             * pending bit and update the lpi pending table
> > +             */
> > +            pend |= (1 << (irq % 8));
> > +
> > +            address_space_write(as, lpipt_baddr + ((irq / 8) *
> > sizeof(pend)),
> > +                                 MEMTXATTRS_UNSPECIFIED, &pend,
> > sizeof(pend));
> > +        }
> > +    }
> > +}
> > 
> > +void gicv3_redist_process_lpi(GICv3CPUState *cs, int irq, int
> > level)
> > +{
> > +    AddressSpace *as = &cs->gic->sysmem_as;
> > +    uint64_t lpict_baddr;
> > +    uint8_t lpite;
> > +
> > +    if ((!cs->gicr_ctlr & GICR_CTLR_ENABLE_LPIS) || !cs-
> > >gicr_propbaser ||
> > +         !cs->gicr_pendbaser || cs->lpi_outofrange) {
> > +        return;
> > +    }
> > +
> > +    lpict_baddr = FIELD_EX64(cs->gicr_propbaser, GICR_PROPBASER,
> > PHYADDR);
> > +    lpict_baddr <<= R_GICR_PROPBASER_PHYADDR_SHIFT;
> > +
> > +    /* get the lpi config table entry corresponding to this irq */
> > +    address_space_read(as, lpict_baddr + ((irq -
> > GICV3_LPI_INTID_START) *
> > +                        sizeof(lpite)), MEMTXATTRS_UNSPECIFIED,
> > +                        &lpite, sizeof(lpite));
> > +
> > +    /* check if this irq is enabled before proceeding further */
> > +    if (!(lpite & LPI_CTE_ENABLED)) {
> > +        return;
> > +    }
> > +
> > +    /* set/clear the pending bit for this irq */
> > +    gicv3_redist_lpi_pending(cs, irq, level);
> > +
> > +    gicv3_redist_update(cs);
> > +}
> > +
> >  void gicv3_redist_set_irq(GICv3CPUState *cs, int irq, int level)
> >  {
> >      /* Update redistributor state for a change in an external PPI
> > input line */
> > diff --git a/hw/intc/gicv3_internal.h b/hw/intc/gicv3_internal.h
> > index a2718704d4..4c97c22850 100644
> > --- a/hw/intc/gicv3_internal.h
> > +++ b/hw/intc/gicv3_internal.h
> > @@ -306,6 +306,12 @@ FIELD(GITS_TYPER, CIL, 36, 1)
> > 
> >  #define L1TABLE_ENTRY_SIZE         8
> > 
> > +#define LPI_CTE_ENABLE_OFFSET      0
> > +#define LPI_CTE_ENABLED          VALID_MASK
> > +#define LPI_CTE_PRIORITY_OFFSET    2
> > +#define LPI_CTE_PRIORITY_MASK     ((1U << 6) - 1)
> > +#define LPI_PRIORITY_MASK         0xfc
> > +
> >  #define GITS_CMDQ_ENTRY_SIZE               32
> >  #define NUM_BYTES_IN_DW                     8
> > 
> > @@ -444,6 +450,9 @@ MemTxResult gicv3_redist_write(void *opaque,
> > hwaddr offset, uint64_t data,
> >                                 unsigned size, MemTxAttrs attrs);
> >  void gicv3_dist_set_irq(GICv3State *s, int irq, int level);
> >  void gicv3_redist_set_irq(GICv3CPUState *cs, int irq, int level);
> > +void gicv3_redist_process_lpi(GICv3CPUState *cs, int irq, int
> > level);
> > +void gicv3_redist_lpi_pending(GICv3CPUState *cs, int irq, int
> > level);
> > +bool gicv3_redist_update_lpi(GICv3CPUState *cs);
> >  void gicv3_redist_send_sgi(GICv3CPUState *cs, int grp, int irq,
> > bool ns);
> >  void gicv3_init_cpuif(GICv3State *s);
> 
> thanks
> -- PMM



^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH v2 8/8] hw/arm/virt: add ITS support in virt GIC
  2021-04-19 12:46   ` Peter Maydell
@ 2021-04-29 23:40     ` shashi.mallela
  0 siblings, 0 replies; 25+ messages in thread
From: shashi.mallela @ 2021-04-29 23:40 UTC (permalink / raw)
  To: Peter Maydell
  Cc: Leif Lindholm, QEMU Developers, qemu-arm, Radoslaw Biernacki

On Mon, 2021-04-19 at 13:46 +0100, Peter Maydell wrote:
> On Thu, 1 Apr 2021 at 03:42, Shashi Mallela <
> shashi.mallela@linaro.org> wrote:
> > Included creation of ITS as part of virt platform GIC
> > initialization.This Emulated ITS model now co-exists with kvm
> > ITS and is enabled in absence of kvm irq kernel support in a
> > platform.
> > 
> > Signed-off-by: Shashi Mallela <shashi.mallela@linaro.org>
> > ---
> >  hw/arm/virt.c        | 10 ++++++++--
> >  target/arm/kvm_arm.h |  4 ++--
> >  2 files changed, 10 insertions(+), 4 deletions(-)
> > 
> > diff --git a/hw/arm/virt.c b/hw/arm/virt.c
> > index aa2bbd14e0..77cf2db90f 100644
> > --- a/hw/arm/virt.c
> > +++ b/hw/arm/virt.c
> > @@ -622,7 +622,7 @@ static void create_v2m(VirtMachineState *vms)
> >      vms->msi_controller = VIRT_MSI_CTRL_GICV2M;
> >  }
> > 
> > -static void create_gic(VirtMachineState *vms)
> > +static void create_gic(VirtMachineState *vms, MemoryRegion *mem)
> >  {
> >      MachineState *ms = MACHINE(vms);
> >      /* We create a standalone GIC */
> > @@ -656,6 +656,12 @@ static void create_gic(VirtMachineState *vms)
> >                               nb_redist_regions);
> >          qdev_prop_set_uint32(vms->gic, "redist-region-count[0]",
> > redist0_count);
> > 
> > +        if (!kvm_irqchip_in_kernel()) {
> > +            object_property_set_link(OBJECT(vms->gic), "sysmem",
> > OBJECT(mem),
> > +                                     &error_fatal);
> > +            qdev_prop_set_bit(vms->gic, "has-lpi", true);
> > +        }
> > +
> >          if (nb_redist_regions == 2) {
> >              uint32_t redist1_capacity =
> >                      vms->memmap[VIRT_HIGH_GIC_REDIST2].size /
> > GICV3_REDIST_SIZE;
> > @@ -2039,7 +2045,7 @@ static void machvirt_init(MachineState
> > *machine)
> > 
> >      virt_flash_fdt(vms, sysmem, secure_sysmem ?: sysmem);
> > 
> > -    create_gic(vms);
> > +    create_gic(vms, sysmem);
> > 
> >      virt_cpu_post_init(vms, sysmem);
> > 
> > diff --git a/target/arm/kvm_arm.h b/target/arm/kvm_arm.h
> > index 34f8daa377..0613454975 100644
> > --- a/target/arm/kvm_arm.h
> > +++ b/target/arm/kvm_arm.h
> > @@ -525,8 +525,8 @@ static inline const char *its_class_name(void)
> >          /* KVM implementation requires this capability */
> >          return kvm_direct_msi_enabled() ? "arm-its-kvm" : NULL;
> >      } else {
> > -        /* Software emulation is not implemented yet */
> > -        return NULL;
> > +        /* Software emulation based model */
> > +        return "arm-gicv3-its";
> >      }
> >  }
> 
> This is OK as far as it goes, but I think we need to add the
> versioned-machine support so that only "virt-6.1" and later get the
> new ITS, and "virt-6.0" behave the same way (ie no ITS) as they did
> in older QEMU versions.
> 
> Have added versioned machine support to enable new ITS only for
> versions 6.1 and above
> thanks
> -- PMM



^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH v2 1/8] hw/intc: GICv3 ITS initial framework
  2021-04-29 23:36     ` shashi.mallela
@ 2021-04-30 10:09       ` Peter Maydell
  2021-04-30 15:56         ` Shashi Mallela
  0 siblings, 1 reply; 25+ messages in thread
From: Peter Maydell @ 2021-04-30 10:09 UTC (permalink / raw)
  To: Shashi Mallela
  Cc: Leif Lindholm, QEMU Developers, qemu-arm, Radoslaw Biernacki

On Fri, 30 Apr 2021 at 00:36, <shashi.mallela@linaro.org> wrote:
>
> On Fri, 2021-04-16 at 18:21 +0100, Peter Maydell wrote:
> > On Thu, 1 Apr 2021 at 03:41, Shashi Mallela <
> > shashi.mallela@linaro.org> wrote:
> > > +#define GITS_PIDR2       0xFFE8
> >
> > You probably don't want an offset for GITS_PIDR2 specifically.
> > Compare handling of GICD_IDREGS in the distributor emulation.
> > Tried re-using the GICD_IDREGS offset for GITS_PIDR2,but from testing
> > observed that the linux kernel driver and kvm-unit-tests both relied
> > on using the 0xFFE8 offset instead of 0xFFD0 (used by GICD & GICR) to
> > complete ITS initialization and setup

I didn't mean "just put PIDR2 at the GICD_IDREGS offset", which
obviously won't work, I meant "implement the whole set of ID registers
(of which PIDR2 is one in the middle) in the same way we implement that
set on the other GIC components".

-- PMM


^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH v2 1/8] hw/intc: GICv3 ITS initial framework
  2021-04-30 10:09       ` Peter Maydell
@ 2021-04-30 15:56         ` Shashi Mallela
  0 siblings, 0 replies; 25+ messages in thread
From: Shashi Mallela @ 2021-04-30 15:56 UTC (permalink / raw)
  To: Peter Maydell
  Cc: Leif Lindholm, QEMU Developers, qemu-arm, Radoslaw Biernacki

[-- Attachment #1: Type: text/plain, Size: 1160 bytes --]

Have taken care of it.please ignore PIDR2 changes in the current patchset ,will update the latest changes in the next patchset version

Thanks
Shashi

On Apr 30 2021, at 6:09 am, Peter Maydell <peter.maydell@linaro.org> wrote:
> On Fri, 30 Apr 2021 at 00:36, <shashi.mallela@linaro.org> wrote:
> >
> > On Fri, 2021-04-16 at 18:21 +0100, Peter Maydell wrote:
> > > On Thu, 1 Apr 2021 at 03:41, Shashi Mallela <
> > > shashi.mallela@linaro.org> wrote:
> > > > +#define GITS_PIDR2 0xFFE8
> > >
> > > You probably don't want an offset for GITS_PIDR2 specifically.
> > > Compare handling of GICD_IDREGS in the distributor emulation.
> > > Tried re-using the GICD_IDREGS offset for GITS_PIDR2,but from testing
> > > observed that the linux kernel driver and kvm-unit-tests both relied
> > > on using the 0xFFE8 offset instead of 0xFFD0 (used by GICD & GICR) to
> > > complete ITS initialization and setup
>
> I didn't mean "just put PIDR2 at the GICD_IDREGS offset", which
> obviously won't work, I meant "implement the whole set of ID registers
> (of which PIDR2 is one in the middle) in the same way we implement that
> set on the other GIC components".
>
> -- PMM

[-- Attachment #2: Type: text/html, Size: 1786 bytes --]

^ permalink raw reply	[flat|nested] 25+ messages in thread

end of thread, other threads:[~2021-04-30 15:57 UTC | newest]

Thread overview: 25+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-04-01  2:41 [PATCH v2 0/8] GICv3 LPI and ITS feature implementation Shashi Mallela
2021-04-01  2:41 ` [PATCH v2 1/8] hw/intc: GICv3 ITS initial framework Shashi Mallela
2021-04-16 17:21   ` Peter Maydell
2021-04-29 23:36     ` shashi.mallela
2021-04-30 10:09       ` Peter Maydell
2021-04-30 15:56         ` Shashi Mallela
2021-04-01  2:41 ` [PATCH v2 2/8] hw/intc: GICv3 ITS register definitions added Shashi Mallela
2021-04-16 18:54   ` Peter Maydell
     [not found]     ` <937a2923445e3ff629c9799a8579c470d2636375.camel@linaro.org>
2021-04-29 23:37       ` shashi.mallela
2021-04-01  2:41 ` [PATCH v2 3/8] hw/intc: GICv3 ITS command queue framework Shashi Mallela
2021-04-19 10:30   ` Peter Maydell
2021-04-29 23:38     ` shashi.mallela
2021-04-01  2:41 ` [PATCH v2 4/8] hw/intc: GICv3 ITS Command processing Shashi Mallela
2021-04-19 10:39   ` Peter Maydell
2021-04-01  2:41 ` [PATCH v2 5/8] hw/intc: GICv3 ITS Feature enablement Shashi Mallela
2021-04-19 10:51   ` Peter Maydell
2021-04-29 23:39     ` shashi.mallela
2021-04-01  2:41 ` [PATCH v2 6/8] hw/intc: GICv3 redistributor ITS processing Shashi Mallela
2021-04-19 12:44   ` Peter Maydell
     [not found]     ` <89852279ad379f2e50563dd47eb67376262355c0.camel@linaro.org>
2021-04-29 23:40       ` shashi.mallela
2021-04-01  2:41 ` [PATCH v2 7/8] hw/arm/sbsa-ref: add ITS support in SBSA GIC Shashi Mallela
2021-04-19 12:44   ` Peter Maydell
2021-04-01  2:41 ` [PATCH v2 8/8] hw/arm/virt: add ITS support in virt GIC Shashi Mallela
2021-04-19 12:46   ` Peter Maydell
2021-04-29 23:40     ` shashi.mallela

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).