All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v4 0/8] GICv3 LPI and ITS feature implementation
@ 2021-06-02 18:00 Shashi Mallela
  2021-06-02 18:00 ` [PATCH v4 1/8] hw/intc: GICv3 ITS initial framework Shashi Mallela
                   ` (8 more replies)
  0 siblings, 9 replies; 52+ messages in thread
From: Shashi Mallela @ 2021-06-02 18:00 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 v4:
 - review comments addressed
 - redesigned the lpi pending priority determination logic to scan
   LPI pending table and config table right after lpi pending
   statechanges(SET/RESET) through gicv3_redist_update_lpi() call to
   determinethe highest priority lpi among the active lpis and save
   the details.The high priority interrupt determination logic in
   redistributor now usesthe saved high priority lpi details
   (alongside other interrupt types) instead of calling
   gicv3_redist_update_lpi() everytime(as in v3).This
   significantly reduces the call overhead associated with
   address_space_read of lpi config and pending tables.
   Testing with this new design showed no boot delays.
 - profiled execution of gicv3_redist_update_lpi() using perf and
   framegraph to confirm execution is within normal limits.
   Also,specifically measured execution time to be an average of 175us
   with linux distro testing.
 - All kvm_unit_tests PASS

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                          |   27 +-
 hw/intc/arm_gicv3.c                    |    9 +
 hw/intc/arm_gicv3_common.c             |   13 +
 hw/intc/arm_gicv3_cpuif.c              |    7 +-
 hw/intc/arm_gicv3_dist.c               |    7 +-
 hw/intc/arm_gicv3_its.c                | 1217 ++++++++++++++++++++++++
 hw/intc/arm_gicv3_its_common.c         |    8 +-
 hw/intc/arm_gicv3_its_kvm.c            |    2 +-
 hw/intc/arm_gicv3_redist.c             |  159 +++-
 hw/intc/gicv3_internal.h               |  186 +++-
 hw/intc/meson.build                    |    1 +
 include/hw/arm/virt.h                  |    2 +
 include/hw/intc/arm_gicv3_common.h     |   16 +
 include/hw/intc/arm_gicv3_its_common.h |   39 +-
 target/arm/kvm_arm.h                   |    4 +-
 16 files changed, 1694 insertions(+), 29 deletions(-)
 create mode 100644 hw/intc/arm_gicv3_its.c

-- 
2.27.0



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

* [PATCH v4 1/8] hw/intc: GICv3 ITS initial framework
  2021-06-02 18:00 [PATCH v4 0/8] GICv3 LPI and ITS feature implementation Shashi Mallela
@ 2021-06-02 18:00 ` Shashi Mallela
  2021-06-08 10:02   ` Peter Maydell
                     ` (2 more replies)
  2021-06-02 18:00 ` [PATCH v4 2/8] hw/intc: GICv3 ITS register definitions added Shashi Mallela
                   ` (7 subsequent siblings)
  8 siblings, 3 replies; 52+ messages in thread
From: Shashi Mallela @ 2021-06-02 18:00 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                | 240 +++++++++++++++++++++++++
 hw/intc/arm_gicv3_its_common.c         |   8 +-
 hw/intc/arm_gicv3_its_kvm.c            |   2 +-
 hw/intc/gicv3_internal.h               |  88 +++++++--
 hw/intc/meson.build                    |   1 +
 include/hw/intc/arm_gicv3_its_common.h |   9 +-
 6 files changed, 331 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..545cda3665
--- /dev/null
+++ b/hw/intc/arm_gicv3_its.c
@@ -0,0 +1,240 @@
+/*
+ * 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 gicv3_its_translation_write(void *opaque, hwaddr offset,
+                                               uint64_t data, unsigned size,
+                                               MemTxAttrs attrs)
+{
+    MemTxResult result = MEMTX_OK;
+
+    return result;
+}
+
+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 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 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,
+    .valid.min_access_size = 4,
+    .valid.max_access_size = 8,
+    .impl.min_access_size = 4,
+    .impl.max_access_size = 8,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static const MemoryRegionOps gicv3_its_translation_ops = {
+    .write_with_attrs = gicv3_its_translation_write,
+    .valid.min_access_size = 2,
+    .valid.max_access_size = 4,
+    .impl.min_access_size = 2,
+    .impl.max_access_size = 4,
+    .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);
+
+    if (s->gicv3->cpu->gicr_typer & GICR_TYPER_PLPIS) {
+        /* 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 - 1);
+        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);
+    }
+}
+
+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);
+
+        /* 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 - 1);
+
+        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 - 1);
+    }
+}
+
+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..f1657c84e0 100644
--- a/hw/intc/arm_gicv3_its_common.c
+++ b/hw/intc/arm_gicv3_its_common.c
@@ -50,6 +50,8 @@ 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,
@@ -99,14 +101,15 @@ 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,
+                          tops ? tops : &gicv3_its_trans_ops, s,
                           "translation", ITS_TRANS_SIZE);
 
     /* Our two regions are always adjacent, therefore we now combine them
@@ -129,7 +132,6 @@ static void gicv3_its_common_reset(DeviceState *dev)
     s->cbaser = 0;
     s->cwriter = 0;
     s->creadr = 0;
-    s->iidr = 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..e0b06930a7 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,78 @@
 #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)
+
+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)
+
+#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
+
+/**
+ * Default features advertised by this version of ITS
+ */
+/* Physical LPIs supported */
+#define GITS_TYPE_PHYSICAL           (1U << 0)
+
+/*
+ * 12 bytes Interrupt translation Table Entry size
+ * ITE Lower 8 Bytes
+ * Valid = 1 bit,InterruptType = 1 bit,
+ * Size of LPI number space[considering max 24 bits],
+ * Size of LPI number space[considering max 24 bits],
+ * ITE Higher 4 Bytes
+ * ICID = 16 bits,
+ * vPEID = 16 bits
+ */
+#define ITS_ITT_ENTRY_SIZE            0xC
+
+/* 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 6e52a166e3..4dcfea6aa8 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..65d1191db1 100644
--- a/include/hw/intc/arm_gicv3_its_common.h
+++ b/include/hw/intc/arm_gicv3_its_common.h
@@ -25,17 +25,22 @@
 #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
+
 struct GICv3ITSState {
     SysBusDevice parent_obj;
 
@@ -52,6 +57,7 @@ struct GICv3ITSState {
     /* Registers */
     uint32_t ctlr;
     uint32_t iidr;
+    uint64_t typer;
     uint64_t cbaser;
     uint64_t cwriter;
     uint64_t creadr;
@@ -62,7 +68,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] 52+ messages in thread

* [PATCH v4 2/8] hw/intc: GICv3 ITS register definitions added
  2021-06-02 18:00 [PATCH v4 0/8] GICv3 LPI and ITS feature implementation Shashi Mallela
  2021-06-02 18:00 ` [PATCH v4 1/8] hw/intc: GICv3 ITS initial framework Shashi Mallela
@ 2021-06-02 18:00 ` Shashi Mallela
  2021-06-08 10:31   ` Peter Maydell
  2021-06-12  6:08   ` Eric Auger
  2021-06-02 18:00 ` [PATCH v4 3/8] hw/intc: GICv3 ITS command queue framework Shashi Mallela
                   ` (6 subsequent siblings)
  8 siblings, 2 replies; 52+ messages in thread
From: Shashi Mallela @ 2021-06-02 18:00 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                | 335 +++++++++++++++++++++++++
 hw/intc/gicv3_internal.h               |  28 ++-
 include/hw/intc/arm_gicv3_common.h     |   3 +
 include/hw/intc/arm_gicv3_its_common.h |  30 +++
 4 files changed, 395 insertions(+), 1 deletion(-)

diff --git a/hw/intc/arm_gicv3_its.c b/hw/intc/arm_gicv3_its.c
index 545cda3665..af60f19c98 100644
--- a/hw/intc/arm_gicv3_its.c
+++ b/hw/intc/arm_gicv3_its.c
@@ -28,6 +28,157 @@ struct GICv3ITSClass {
     void (*parent_reset)(DeviceState *dev);
 };
 
+static uint64_t baser_base_addr(uint64_t value, uint32_t page_sz)
+{
+    uint64_t result = 0;
+
+    switch (page_sz) {
+    case GITS_ITT_PAGE_SIZE_0:
+    case GITS_ITT_PAGE_SIZE_1:
+        result = value & R_GITS_BASER_PHYADDR_MASK;
+        break;
+
+    case GITS_ITT_PAGE_SIZE_2:
+        result = value & R_GITS_BASER_PHYADDRL_64K_MASK;
+        result |= FIELD_EX64(value, GITS_BASER, PHYADDRH_64K) << 48;
+        break;
+
+    default:
+        break;
+    }
+    return result;
+}
+
+static void extract_table_params(GICv3ITSState *s)
+{
+    uint16_t num_pages = 0;
+    uint8_t  page_sz_type;
+    uint8_t type;
+    uint32_t page_sz = 0;
+    uint64_t value;
+
+    for (int i = 0; i < 8; i++) {
+        value = s->baser[i];
+
+        if (!value) {
+            continue;
+        }
+
+        page_sz_type = FIELD_EX64(value, GITS_BASER, PAGESIZE);
+
+        switch (page_sz_type) {
+        case 0:
+            page_sz = GITS_ITT_PAGE_SIZE_0;
+            break;
+
+        case 1:
+            page_sz = GITS_ITT_PAGE_SIZE_1;
+            break;
+
+        case 2:
+        case 3:
+            page_sz = GITS_ITT_PAGE_SIZE_2;
+            break;
+
+        default:
+            g_assert_not_reached();
+        }
+
+        num_pages = FIELD_EX64(value, GITS_BASER, SIZE);
+
+        type = FIELD_EX64(value, GITS_BASER, TYPE);
+
+        switch (type) {
+
+        case GITS_ITT_TYPE_DEVICE:
+            memset(&s->dt, 0 , sizeof(s->dt));
+            s->dt.valid = FIELD_EX64(value, GITS_BASER, VALID);
+
+            if (!s->dt.valid) {
+                return;
+            }
+
+            s->dt.page_sz = page_sz;
+            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));
+
+            s->dt.base_addr = baser_base_addr(value, page_sz);
+
+            break;
+
+        case GITS_ITT_TYPE_COLLECTION:
+            memset(&s->ct, 0 , sizeof(s->ct));
+            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) {
+                return;
+            }
+
+            s->ct.page_sz = page_sz;
+            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);
+            }
+
+            s->ct.base_addr = baser_base_addr(value, page_sz);
+
+            break;
+
+        default:
+            break;
+        }
+    }
+}
+
+static void extract_cmdq_params(GICv3ITSState *s)
+{
+    uint16_t num_pages = 0;
+    uint64_t value = s->cbaser;
+
+    num_pages = FIELD_EX64(value, GITS_CBASER, SIZE);
+
+    memset(&s->cq, 0 , sizeof(s->cq));
+    s->cq.valid = FIELD_EX64(value, GITS_CBASER, VALID);
+
+    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;
+    }
+}
+
 static MemTxResult gicv3_its_translation_write(void *opaque, hwaddr offset,
                                                uint64_t data, unsigned size,
                                                MemTxAttrs attrs)
@@ -41,7 +192,73 @@ static MemTxResult its_writel(GICv3ITSState *s, hwaddr offset,
                               uint64_t value, MemTxAttrs attrs)
 {
     MemTxResult result = MEMTX_OK;
+    int index;
 
+    switch (offset) {
+    case GITS_CTLR:
+        s->ctlr |= (value & ~(s->ctlr));
+
+        if (s->ctlr & ITS_CTLR_ENABLED) {
+            extract_table_params(s);
+            extract_cmdq_params(s);
+            s->creadr = 0;
+        }
+        break;
+    case GITS_CBASER:
+        /*
+         * IMPDEF choice:- 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:
+        /*
+         * IMPDEF choice:- GITS_CBASER register becomes RO if ITS is
+         *                 already enabled
+         */
+        if (!(s->ctlr & ITS_CTLR_ENABLED)) {
+            s->cbaser = deposit64(s->cbaser, 32, 32, value);
+        }
+        break;
+    case GITS_CWRITER:
+        s->cwriter = deposit64(s->cwriter, 0, 32,
+                               (value & ~R_GITS_CWRITER_RETRY_MASK));
+        break;
+    case GITS_CWRITER + 4:
+        s->cwriter = deposit64(s->cwriter, 32, 32,
+                               (value & ~R_GITS_CWRITER_RETRY_MASK));
+        break;
+    case GITS_BASER ... GITS_BASER + 0x3f:
+        /*
+         * IMPDEF choice:- GITS_BASERn register becomes RO if ITS is
+         *                 already enabled
+         */
+        if (!(s->ctlr & ITS_CTLR_ENABLED)) {
+            index = (offset - GITS_BASER) / 8;
+
+            if (offset & 7) {
+                s->baser[index] = deposit64(s->baser[index], 32, 32,
+                                            (value & ~GITS_BASER_VAL_MASK));
+            } else {
+                s->baser[index] = deposit64(s->baser[index], 0, 32,
+                                            (value & ~GITS_BASER_VAL_MASK));
+            }
+        }
+        break;
+    case GITS_IIDR:
+    case GITS_IDREGS ... GITS_IDREGS + 0x2f:
+        /* 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;
 }
 
@@ -49,7 +266,55 @@ 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 = gicv3_iidr();
+        break;
+    case GITS_IDREGS ... GITS_IDREGS + 0x2f:
+        /* ID registers */
+        *data = gicv3_idreg(offset - GITS_IDREGS);
+        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 = extract64(s->baser[index], 32, 32);
+        } else {
+            *data = extract64(s->baser[index], 0, 32);
+        }
+        break;
+    default:
+        result = MEMTX_ERROR;
+        break;
+    }
     return result;
 }
 
@@ -57,7 +322,42 @@ 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:
+        /*
+         * IMPDEF choice:- GITS_BASERn register becomes 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);
+        }
+        break;
+    case GITS_CBASER:
+        /*
+         * IMPDEF choice:- GITS_CBASER register becomes RO if ITS is
+         *                 already enabled
+         */
+        if (!(s->ctlr & ITS_CTLR_ENABLED)) {
+            s->cbaser = value;
+        }
+        break;
+    case GITS_CWRITER:
+        s->cwriter = value & ~R_GITS_CWRITER_RETRY_MASK;
+        break;
+    case GITS_CREADR:
+    case GITS_TYPER:
+        /* 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;
 }
 
@@ -65,7 +365,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;
 }
 
@@ -162,6 +484,9 @@ static void gicv3_arm_its_realize(DeviceState *dev, Error **errp)
     gicv3_its_init_mmio(s, &gicv3_its_control_ops, &gicv3_its_translation_ops);
 
     if (s->gicv3->cpu->gicr_typer & GICR_TYPER_PLPIS) {
+        address_space_init(&s->gicv3->dma_as, s->gicv3->dma,
+                           "gicv3-its-sysmem");
+
         /* set the ITS default features supported */
         s->typer = FIELD_DP64(s->typer, GITS_TYPER, PHYSICAL,
                               GITS_TYPE_PHYSICAL);
@@ -208,6 +533,14 @@ static void gicv3_its_reset(DeviceState *dev)
     }
 }
 
+static void gicv3_its_post_load(GICv3ITSState *s)
+{
+    if (s->ctlr & ITS_CTLR_ENABLED) {
+        extract_table_params(s);
+        extract_cmdq_params(s);
+    }
+}
+
 static Property gicv3_its_props[] = {
     DEFINE_PROP_LINK("parent-gicv3", GICv3ITSState, gicv3, "arm-gicv3",
                      GICv3State *),
@@ -218,10 +551,12 @@ static void gicv3_its_class_init(ObjectClass *klass, void *data)
 {
     DeviceClass *dc = DEVICE_CLASS(klass);
     GICv3ITSClass *ic = ARM_GICV3_ITS_CLASS(klass);
+    GICv3ITSCommonClass *icc = ARM_GICV3_ITS_COMMON_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);
+    icc->post_load = gicv3_its_post_load;
 }
 
 static const TypeInfo gicv3_its_info = {
diff --git a/hw/intc/gicv3_internal.h b/hw/intc/gicv3_internal.h
index e0b06930a7..d6aaa94e4c 100644
--- a/hw/intc/gicv3_internal.h
+++ b/hw/intc/gicv3_internal.h
@@ -238,7 +238,7 @@ 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, PHYADDRH_64K, 12, 4)
 FIELD(GITS_BASER, ENTRYSIZE, 48, 5)
 FIELD(GITS_BASER, OUTERCACHE, 53, 3)
 FIELD(GITS_BASER, TYPE, 56, 3)
@@ -246,6 +246,17 @@ FIELD(GITS_BASER, INNERCACHE, 59, 3)
 FIELD(GITS_BASER, INDIRECT, 62, 1)
 FIELD(GITS_BASER, VALID, 63, 1)
 
+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_CWRITER, RETRY, 0, 1)
+FIELD(GITS_CWRITER, OFFSET, 5, 15)
+
+FIELD(GITS_CTLR, ENABLED, 0, 1)
 FIELD(GITS_CTLR, QUIESCENT, 31, 1)
 
 FIELD(GITS_TYPER, PHYSICAL, 0, 1)
@@ -257,6 +268,13 @@ FIELD(GITS_TYPER, PTA, 19, 1)
 FIELD(GITS_TYPER, CIDBITS, 32, 4)
 FIELD(GITS_TYPER, CIL, 36, 1)
 
+#define GITS_IDREGS           0xFFD0
+
+#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
@@ -264,6 +282,14 @@ FIELD(GITS_TYPER, CIL, 36, 1)
 #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..1fd5cedbbd 100644
--- a/include/hw/intc/arm_gicv3_common.h
+++ b/include/hw/intc/arm_gicv3_common.h
@@ -226,6 +226,9 @@ struct GICv3State {
     int dev_fd; /* kvm device fd if backed by kvm vgic support */
     Error *migration_blocker;
 
+    MemoryRegion *dma;
+    AddressSpace dma_as;
+
     /* Distributor */
 
     /* for a GIC with the security extensions the NS banked version of this
diff --git a/include/hw/intc/arm_gicv3_its_common.h b/include/hw/intc/arm_gicv3_its_common.h
index 65d1191db1..78b1ba7e6b 100644
--- a/include/hw/intc/arm_gicv3_its_common.h
+++ b/include/hw/intc/arm_gicv3_its_common.h
@@ -41,6 +41,32 @@
 
 #define GITS_TRANSLATER  0x0040
 
+typedef struct {
+    bool valid;
+    bool indirect;
+    uint16_t entry_sz;
+    uint32_t page_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 page_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;
 
@@ -63,6 +89,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] 52+ messages in thread

* [PATCH v4 3/8] hw/intc: GICv3 ITS command queue framework
  2021-06-02 18:00 [PATCH v4 0/8] GICv3 LPI and ITS feature implementation Shashi Mallela
  2021-06-02 18:00 ` [PATCH v4 1/8] hw/intc: GICv3 ITS initial framework Shashi Mallela
  2021-06-02 18:00 ` [PATCH v4 2/8] hw/intc: GICv3 ITS register definitions added Shashi Mallela
@ 2021-06-02 18:00 ` Shashi Mallela
  2021-06-08 10:38   ` Peter Maydell
                     ` (2 more replies)
  2021-06-02 18:00 ` [PATCH v4 4/8] hw/intc: GICv3 ITS Command processing Shashi Mallela
                   ` (5 subsequent siblings)
  8 siblings, 3 replies; 52+ messages in thread
From: Shashi Mallela @ 2021-06-02 18:00 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  | 295 +++++++++++++++++++++++++++++++++++++++
 hw/intc/gicv3_internal.h |  37 +++++
 2 files changed, 332 insertions(+)

diff --git a/hw/intc/arm_gicv3_its.c b/hw/intc/arm_gicv3_its.c
index af60f19c98..6551c577b3 100644
--- a/hw/intc/arm_gicv3_its.c
+++ b/hw/intc/arm_gicv3_its.c
@@ -49,6 +49,295 @@ static uint64_t baser_base_addr(uint64_t value, uint32_t page_sz)
     return result;
 }
 
+static MemTxResult update_cte(GICv3ITSState *s, uint16_t icid, bool valid,
+                              uint64_t rdbase)
+{
+    AddressSpace *as = &s->gicv3->dma_as;
+    uint64_t value;
+    uint64_t l2t_addr;
+    bool valid_l2t;
+    uint32_t l2t_id;
+    uint32_t max_l2_entries;
+    uint64_t cte = 0;
+    MemTxResult res = MEMTX_OK;
+
+    if (!s->ct.valid) {
+        return res;
+    }
+
+    if (valid) {
+        /* add mapping entry to collection table */
+        cte = (valid & VALID_MASK) |
+              ((rdbase & RDBASE_PROCNUM_MASK) << 1ULL);
+    }
+
+    /*
+     * 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.
+     */
+    if (s->ct.indirect) {
+        l2t_id = icid / (s->ct.page_sz / L1TABLE_ENTRY_SIZE);
+
+        value = address_space_ldq_le(as,
+                                     s->ct.base_addr +
+                                     (l2t_id * L1TABLE_ENTRY_SIZE),
+                                     MEMTXATTRS_UNSPECIFIED, &res);
+
+        if (res != MEMTX_OK) {
+            return res;
+        }
+
+        valid_l2t = (value >> VALID_SHIFT) & VALID_MASK;
+
+        if (valid_l2t) {
+            max_l2_entries = s->ct.page_sz / s->ct.entry_sz;
+
+            l2t_addr = value & ((1ULL << 51) - 1);
+
+            address_space_stq_le(as, l2t_addr +
+                                 ((icid % max_l2_entries) * GITS_CTE_SIZE),
+                                 cte, MEMTXATTRS_UNSPECIFIED, &res);
+        }
+    } else {
+        /* Flat level table */
+        address_space_stq_le(as, s->ct.base_addr + (icid * GITS_CTE_SIZE),
+                             cte, MEMTXATTRS_UNSPECIFIED, &res);
+    }
+    return res;
+}
+
+static MemTxResult process_mapc(GICv3ITSState *s, uint32_t offset)
+{
+    AddressSpace *as = &s->gicv3->dma_as;
+    uint16_t icid;
+    uint64_t rdbase;
+    bool valid;
+    MemTxResult res = MEMTX_OK;
+    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);
+
+    if (res != MEMTX_OK) {
+        return res;
+    }
+
+    icid = value & ICID_MASK;
+
+    rdbase = (value >> R_MAPC_RDBASE_SHIFT) & RDBASE_PROCNUM_MASK;
+
+    valid = (value >> VALID_SHIFT) & VALID_MASK;
+
+    if ((icid > s->ct.max_collids) || (rdbase > s->gicv3->num_cpu)) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "ITS MAPC: invalid collection table attributes "
+                      "icid %d rdbase %lu\n",  icid, rdbase);
+        /*
+         * in this implementation,in case of error
+         * we ignore this command and move onto the next
+         * command in the queue
+         */
+    } else {
+        res = update_cte(s, icid, valid, rdbase);
+    }
+
+    return res;
+}
+
+static MemTxResult update_dte(GICv3ITSState *s, uint32_t devid, bool valid,
+                              uint8_t size, uint64_t itt_addr)
+{
+    AddressSpace *as = &s->gicv3->dma_as;
+    uint64_t value;
+    uint64_t l2t_addr;
+    bool valid_l2t;
+    uint32_t l2t_id;
+    uint32_t max_l2_entries;
+    uint64_t dte = 0;
+    MemTxResult res = MEMTX_OK;
+
+    if (s->dt.valid) {
+        if (valid) {
+            /* add mapping entry to device table */
+            dte = (valid & VALID_MASK) |
+                  ((size & SIZE_MASK) << 1U) |
+                  ((itt_addr & ITTADDR_MASK) << 6ULL);
+        }
+    } else {
+        return res;
+    }
+
+    /*
+     * 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.
+     */
+    if (s->dt.indirect) {
+        l2t_id = devid / (s->dt.page_sz / L1TABLE_ENTRY_SIZE);
+
+        value = address_space_ldq_le(as,
+                                     s->dt.base_addr +
+                                     (l2t_id * L1TABLE_ENTRY_SIZE),
+                                     MEMTXATTRS_UNSPECIFIED, &res);
+
+        if (res != MEMTX_OK) {
+            return res;
+        }
+
+        valid_l2t = (value >> VALID_SHIFT) & VALID_MASK;
+
+        if (valid_l2t) {
+            max_l2_entries = s->dt.page_sz / s->dt.entry_sz;
+
+            l2t_addr = value & ((1ULL << 51) - 1);
+
+            address_space_stq_le(as, l2t_addr +
+                                 ((devid % max_l2_entries) * GITS_DTE_SIZE),
+                                 dte, MEMTXATTRS_UNSPECIFIED, &res);
+        }
+    } else {
+        /* Flat level table */
+        address_space_stq_le(as, s->dt.base_addr + (devid * GITS_DTE_SIZE),
+                             dte, MEMTXATTRS_UNSPECIFIED, &res);
+    }
+    return res;
+}
+
+static MemTxResult process_mapd(GICv3ITSState *s, uint64_t value,
+                                uint32_t offset)
+{
+    AddressSpace *as = &s->gicv3->dma_as;
+    uint32_t devid;
+    uint8_t size;
+    uint64_t itt_addr;
+    bool valid;
+    MemTxResult res = MEMTX_OK;
+
+    devid = (value >> DEVID_SHIFT) & DEVID_MASK;
+
+    offset += NUM_BYTES_IN_DW;
+    value = address_space_ldq_le(as, s->cq.base_addr + offset,
+                                 MEMTXATTRS_UNSPECIFIED, &res);
+
+    if (res != MEMTX_OK) {
+        return res;
+    }
+
+    size = (value & SIZE_MASK);
+
+    offset += NUM_BYTES_IN_DW;
+    value = address_space_ldq_le(as, s->cq.base_addr + offset,
+                                 MEMTXATTRS_UNSPECIFIED, &res);
+
+    if (res != MEMTX_OK) {
+        return res;
+    }
+
+    itt_addr = (value >> ITTADDR_SHIFT) & ITTADDR_MASK;
+
+    valid = (value >> VALID_SHIFT) & VALID_MASK;
+
+    if ((devid > s->dt.max_devids) ||
+        (size > FIELD_EX64(s->typer, GITS_TYPER, IDBITS))) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "ITS MAPD: invalid device table attributes "
+                      "devid %d or size %d\n", devid, size);
+        /*
+         * in this implementation, in case of error
+         * we ignore this command and move onto the next
+         * command in the queue
+         */
+    } else {
+        res = update_dte(s, devid, valid, size, itt_addr);
+    }
+
+    return res;
+}
+
+/*
+ * Current implementation blocks until all
+ * commands are processed
+ */
+static void 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->dma_as;
+    MemTxResult res = MEMTX_OK;
+    uint8_t cmd;
+
+    if (!(s->ctlr & ITS_CTLR_ENABLED)) {
+        return;
+    }
+
+    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);
+        return;
+    }
+
+    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:
+            /*
+             * 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.
+             * Hence no further processing is required for SYNC command.
+             */
+            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 {
+            /*
+             * in this implementation,in case of dma read/write error
+             * we stall the command processing
+             */
+            s->creadr = FIELD_DP64(s->creadr, GITS_CREADR, STALLED, 1);
+            qemu_log_mask(LOG_GUEST_ERROR,
+                          "%s: %x cmd processing failed!!\n", __func__, cmd);
+            break;
+        }
+    }
+}
+
 static void extract_table_params(GICv3ITSState *s)
 {
     uint16_t num_pages = 0;
@@ -226,6 +515,9 @@ static MemTxResult its_writel(GICv3ITSState *s, hwaddr offset,
     case GITS_CWRITER:
         s->cwriter = deposit64(s->cwriter, 0, 32,
                                (value & ~R_GITS_CWRITER_RETRY_MASK));
+        if (s->cwriter != s->creadr) {
+            process_cmdq(s);
+        }
         break;
     case GITS_CWRITER + 4:
         s->cwriter = deposit64(s->cwriter, 32, 32,
@@ -346,6 +638,9 @@ static MemTxResult its_writell(GICv3ITSState *s, hwaddr offset,
         break;
     case GITS_CWRITER:
         s->cwriter = value & ~R_GITS_CWRITER_RETRY_MASK;
+        if (s->cwriter != s->creadr) {
+            process_cmdq(s);
+        }
         break;
     case GITS_CREADR:
     case GITS_TYPER:
diff --git a/hw/intc/gicv3_internal.h b/hw/intc/gicv3_internal.h
index d6aaa94e4c..0932a30560 100644
--- a/hw/intc/gicv3_internal.h
+++ b/hw/intc/gicv3_internal.h
@@ -253,6 +253,9 @@ 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)
 
@@ -289,6 +292,40 @@ 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_LENGTH                  16
+#define ICID_MASK                 ((1U << ICID_LENGTH) - 1)
+FIELD(MAPC, RDBASE, 16, 32)
+
+#define RDBASE_PROCNUM_LENGTH        16
+#define RDBASE_PROCNUM_MASK       ((1ULL << RDBASE_PROCNUM_LENGTH) - 1)
+
+#define DEVID_SHIFT                  32
+#define DEVID_LENGTH                 32
+#define DEVID_MASK                ((1ULL << DEVID_LENGTH) - 1)
+
+/* MAPD command fields */
+#define ITTADDR_LENGTH               44
+#define ITTADDR_SHIFT                 8
+#define ITTADDR_MASK              ((1ULL << ITTADDR_LENGTH) - 1)
+#define SIZE_MASK                 0x1f
+
+#define VALID_SHIFT               63
+#define VALID_MASK                1ULL
 
 /**
  * Default features advertised by this version of ITS
-- 
2.27.0



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

* [PATCH v4 4/8] hw/intc: GICv3 ITS Command processing
  2021-06-02 18:00 [PATCH v4 0/8] GICv3 LPI and ITS feature implementation Shashi Mallela
                   ` (2 preceding siblings ...)
  2021-06-02 18:00 ` [PATCH v4 3/8] hw/intc: GICv3 ITS command queue framework Shashi Mallela
@ 2021-06-02 18:00 ` Shashi Mallela
  2021-06-08 10:45   ` Peter Maydell
  2021-06-13 15:55   ` Eric Auger
  2021-06-02 18:00 ` [PATCH v4 5/8] hw/intc: GICv3 ITS Feature enablement Shashi Mallela
                   ` (4 subsequent siblings)
  8 siblings, 2 replies; 52+ messages in thread
From: Shashi Mallela @ 2021-06-02 18:00 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            | 334 +++++++++++++++++++++++++++++
 hw/intc/gicv3_internal.h           |  12 ++
 include/hw/intc/arm_gicv3_common.h |   2 +
 3 files changed, 348 insertions(+)

diff --git a/hw/intc/arm_gicv3_its.c b/hw/intc/arm_gicv3_its.c
index 6551c577b3..82bb5b84ef 100644
--- a/hw/intc/arm_gicv3_its.c
+++ b/hw/intc/arm_gicv3_its.c
@@ -28,6 +28,13 @@ 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 uint64_t baser_base_addr(uint64_t value, uint32_t page_sz)
 {
     uint64_t result = 0;
@@ -49,6 +56,315 @@ static uint64_t baser_base_addr(uint64_t value, uint32_t page_sz)
     return result;
 }
 
+static bool get_cte(GICv3ITSState *s, uint16_t icid, uint64_t *cte,
+                    MemTxResult *res)
+{
+    AddressSpace *as = &s->gicv3->dma_as;
+    uint64_t l2t_addr;
+    uint64_t value;
+    bool valid_l2t;
+    uint32_t l2t_id;
+    uint32_t max_l2_entries;
+    bool status = false;
+
+    if (s->ct.indirect) {
+        l2t_id = icid / (s->ct.page_sz / L1TABLE_ENTRY_SIZE);
+
+        value = address_space_ldq_le(as,
+                                     s->ct.base_addr +
+                                     (l2t_id * L1TABLE_ENTRY_SIZE),
+                                     MEMTXATTRS_UNSPECIFIED, res);
+
+        if (*res == MEMTX_OK) {
+            valid_l2t = (value >> VALID_SHIFT) & VALID_MASK;
+
+            if (valid_l2t) {
+                max_l2_entries = s->ct.page_sz / s->ct.entry_sz;
+
+                l2t_addr = value & ((1ULL << 51) - 1);
+
+                *cte =  address_space_ldq_le(as, l2t_addr +
+                                    ((icid % max_l2_entries) * GITS_CTE_SIZE),
+                                    MEMTXATTRS_UNSPECIFIED, res);
+           }
+       }
+    } else {
+        /* Flat level table */
+        *cte =  address_space_ldq_le(as, s->ct.base_addr +
+                                     (icid * GITS_CTE_SIZE),
+                                      MEMTXATTRS_UNSPECIFIED, res);
+    }
+
+    if (*cte & VALID_MASK) {
+        status = true;
+    }
+
+    return status;
+}
+
+static MemTxResult update_ite(GICv3ITSState *s, uint32_t eventid, uint64_t dte,
+                              uint64_t itel, uint32_t iteh)
+{
+    AddressSpace *as = &s->gicv3->dma_as;
+    uint64_t itt_addr;
+    MemTxResult res = MEMTX_OK;
+
+    itt_addr = (dte >> 6ULL) & ITTADDR_MASK;
+    itt_addr <<= ITTADDR_SHIFT; /* 256 byte aligned */
+
+    address_space_stq_le(as, itt_addr + (eventid * sizeof(uint64_t)),
+                         itel, MEMTXATTRS_UNSPECIFIED, &res);
+
+    if (res == MEMTX_OK) {
+        address_space_stl_le(as, itt_addr + ((eventid + sizeof(uint64_t)) *
+                             sizeof(uint32_t)), iteh, MEMTXATTRS_UNSPECIFIED,
+                             &res);
+    }
+   return res;
+}
+
+static bool get_ite(GICv3ITSState *s, uint32_t eventid, uint64_t dte,
+                    uint16_t *icid, uint32_t *pIntid, MemTxResult *res)
+{
+    AddressSpace *as = &s->gicv3->dma_as;
+    uint64_t itt_addr;
+    bool status = false;
+    uint64_t itel = 0;
+    uint32_t iteh = 0;
+
+    itt_addr = (dte >> 6ULL) & ITTADDR_MASK;
+    itt_addr <<= ITTADDR_SHIFT; /* 256 byte aligned */
+
+    itel = address_space_ldq_le(as, itt_addr + (eventid * sizeof(uint64_t)),
+                                MEMTXATTRS_UNSPECIFIED, res);
+
+    if (*res == MEMTX_OK) {
+        iteh = address_space_ldl_le(as, itt_addr + ((eventid +
+                                    sizeof(uint64_t)) * sizeof(uint32_t)),
+                                    MEMTXATTRS_UNSPECIFIED, res);
+
+        if (*res == MEMTX_OK) {
+            if (itel & VALID_MASK) {
+                if ((itel >> ITE_ENTRY_INTTYPE_SHIFT) & GITS_TYPE_PHYSICAL) {
+                    *pIntid = (itel >> ITE_ENTRY_INTID_SHIFT) &
+                               ITE_ENTRY_INTID_MASK;
+                    *icid = iteh & ITE_ENTRY_ICID_MASK;
+                    status = true;
+                }
+            }
+        }
+    }
+    return status;
+}
+
+static uint64_t get_dte(GICv3ITSState *s, uint32_t devid, MemTxResult *res)
+{
+    AddressSpace *as = &s->gicv3->dma_as;
+    uint64_t l2t_addr;
+    uint64_t value;
+    bool valid_l2t;
+    uint32_t l2t_id;
+    uint32_t max_l2_entries;
+
+    if (s->dt.indirect) {
+        l2t_id = devid / (s->dt.page_sz / L1TABLE_ENTRY_SIZE);
+
+        value = address_space_ldq_le(as,
+                                     s->dt.base_addr +
+                                     (l2t_id * L1TABLE_ENTRY_SIZE),
+                                     MEMTXATTRS_UNSPECIFIED, res);
+
+        if (*res == MEMTX_OK) {
+            valid_l2t = (value >> VALID_SHIFT) & VALID_MASK;
+
+            if (valid_l2t) {
+                max_l2_entries = s->dt.page_sz / s->dt.entry_sz;
+
+                l2t_addr = value & ((1ULL << 51) - 1);
+
+                value =  address_space_ldq_le(as, l2t_addr +
+                                   ((devid % max_l2_entries) * GITS_DTE_SIZE),
+                                   MEMTXATTRS_UNSPECIFIED, res);
+            }
+        }
+    } else {
+        /* Flat level table */
+        value = address_space_ldq_le(as, s->dt.base_addr +
+                                     (devid * GITS_DTE_SIZE),
+                                     MEMTXATTRS_UNSPECIFIED, res);
+    }
+
+    return value;
+}
+
+static MemTxResult process_int(GICv3ITSState *s, uint64_t value,
+                               uint32_t offset, ItsCmdType cmd)
+{
+    AddressSpace *as = &s->gicv3->dma_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;
+    uint64_t itel = 0;
+    uint32_t iteh = 0;
+
+    if (cmd == NONE) {
+        devid = offset;
+    } else {
+        devid = (value >> DEVID_SHIFT) & DEVID_MASK;
+
+        offset += NUM_BYTES_IN_DW;
+        value = address_space_ldq_le(as, s->cq.base_addr + offset,
+                                     MEMTXATTRS_UNSPECIFIED, &res);
+    }
+
+    if (res != MEMTX_OK) {
+        return res;
+    }
+
+    eventid = (value & EVENTID_MASK);
+
+    dte = get_dte(s, devid, &res);
+
+    if (res != MEMTX_OK) {
+        return res;
+    }
+    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, &res);
+
+        if (res != MEMTX_OK) {
+            return res;
+        }
+
+        if (ite_valid) {
+            cte_valid = get_cte(s, icid, &cte, &res);
+        }
+
+        if (res != MEMTX_OK) {
+            return res;
+        }
+    }
+
+    if ((devid > s->dt.max_devids) || !dte_valid || !ite_valid ||
+            !cte_valid || (eventid > max_eventid)) {
+        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 {
+        /*
+         * Current implementation only supports rdbase == procnum
+         * Hence rdbase physical address is ignored
+         */
+        if (cmd == DISCARD) {
+            /* remove mapping from interrupt translation table */
+            res = update_ite(s, eventid, dte, itel, iteh);
+        }
+    }
+
+    return res;
+}
+
+static MemTxResult process_mapti(GICv3ITSState *s, uint64_t value,
+                                 uint32_t offset, bool ignore_pInt)
+{
+    AddressSpace *as = &s->gicv3->dma_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 itel = 0;
+    uint32_t iteh = 0;
+    uint32_t int_spurious = INTID_SPURIOUS;
+
+    devid = (value >> DEVID_SHIFT) & DEVID_MASK;
+    offset += NUM_BYTES_IN_DW;
+    value = address_space_ldq_le(as, s->cq.base_addr + offset,
+                                 MEMTXATTRS_UNSPECIFIED, &res);
+
+    if (res != MEMTX_OK) {
+        return 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);
+
+    if (res != MEMTX_OK) {
+        return res;
+    }
+
+    icid = value & ICID_MASK;
+
+    dte = get_dte(s, devid, &res);
+
+    if (res != MEMTX_OK) {
+        return res;
+    }
+    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)))) {
+        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 ite entry to interrupt translation table */
+        itel = (dte_valid & VALID_MASK) | (GITS_TYPE_PHYSICAL <<
+                                           ITE_ENTRY_INTTYPE_SHIFT);
+
+        if (ignore_pInt) {
+            itel |= (eventid << ITE_ENTRY_INTID_SHIFT);
+        } else {
+            itel |= (pIntid << ITE_ENTRY_INTID_SHIFT);
+        }
+        itel |= (int_spurious << ITE_ENTRY_INTSP_SHIFT);
+        iteh |= icid;
+
+        res = update_ite(s, eventid, dte, itel, iteh);
+    }
+
+    return res;
+}
+
 static MemTxResult update_cte(GICv3ITSState *s, uint16_t icid, bool valid,
                               uint64_t rdbase)
 {
@@ -295,8 +611,10 @@ static void 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:
             /*
@@ -313,10 +631,13 @@ static void 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;
@@ -472,7 +793,20 @@ static MemTxResult gicv3_its_translation_write(void *opaque, hwaddr offset,
                                                uint64_t data, unsigned size,
                                                MemTxAttrs attrs)
 {
+    GICv3ITSState *s = (GICv3ITSState *)opaque;
     MemTxResult result = MEMTX_OK;
+    uint32_t devid = 0;
+
+    switch (offset) {
+    case GITS_TRANSLATER:
+        if (s->ctlr & ITS_CTLR_ENABLED) {
+            devid = attrs.requester_id;
+            result = process_int(s, data, devid, NONE);
+        }
+        break;
+    default:
+        break;
+    }
 
     return result;
 }
diff --git a/hw/intc/gicv3_internal.h b/hw/intc/gicv3_internal.h
index 0932a30560..ce45cd0ef6 100644
--- a/hw/intc/gicv3_internal.h
+++ b/hw/intc/gicv3_internal.h
@@ -324,6 +324,13 @@ FIELD(MAPC, RDBASE, 16, 32)
 #define ITTADDR_MASK              ((1ULL << ITTADDR_LENGTH) - 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                1ULL
 
@@ -344,6 +351,11 @@ FIELD(MAPC, RDBASE, 16, 32)
  * vPEID = 16 bits
  */
 #define ITS_ITT_ENTRY_SIZE            0xC
+#define ITE_ENTRY_INTTYPE_SHIFT        1
+#define ITE_ENTRY_INTID_SHIFT          2
+#define ITE_ENTRY_INTID_MASK         ((1ULL << 24) - 1)
+#define ITE_ENTRY_INTSP_SHIFT          26
+#define ITE_ENTRY_ICID_MASK          ((1ULL << 16) - 1)
 
 /* 16 bits EventId */
 #define ITS_IDBITS                   GICD_TYPER_IDBITS
diff --git a/include/hw/intc/arm_gicv3_common.h b/include/hw/intc/arm_gicv3_common.h
index 1fd5cedbbd..0715b0bc2a 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] 52+ messages in thread

* [PATCH v4 5/8] hw/intc: GICv3 ITS Feature enablement
  2021-06-02 18:00 [PATCH v4 0/8] GICv3 LPI and ITS feature implementation Shashi Mallela
                   ` (3 preceding siblings ...)
  2021-06-02 18:00 ` [PATCH v4 4/8] hw/intc: GICv3 ITS Command processing Shashi Mallela
@ 2021-06-02 18:00 ` Shashi Mallela
  2021-06-08 10:57   ` Peter Maydell
  2021-06-02 18:00 ` [PATCH v4 6/8] hw/intc: GICv3 redistributor ITS processing Shashi Mallela
                   ` (3 subsequent siblings)
  8 siblings, 1 reply; 52+ messages in thread
From: Shashi Mallela @ 2021-06-02 18:00 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         | 12 ++++++++++++
 hw/intc/arm_gicv3_dist.c           |  7 +++++--
 hw/intc/arm_gicv3_its.c            |  9 ++++++++-
 hw/intc/arm_gicv3_redist.c         | 14 +++++++++++---
 hw/intc/gicv3_internal.h           | 17 +++++++++++++++++
 include/hw/intc/arm_gicv3_common.h |  1 +
 6 files changed, 54 insertions(+), 6 deletions(-)

diff --git a/hw/intc/arm_gicv3_common.c b/hw/intc/arm_gicv3_common.c
index 58ef65f589..53dea2a775 100644
--- a/hw/intc/arm_gicv3_common.c
+++ b/hw/intc/arm_gicv3_common.c
@@ -345,6 +345,11 @@ static void arm_gicv3_common_realize(DeviceState *dev, Error **errp)
         return;
     }
 
+    if (s->lpi_enable && !s->dma) {
+        error_setg(errp, "Redist-ITS: Guest 'sysmem' reference link not set");
+        return;
+    }
+
     s->cpu = g_new0(GICv3CPUState, s->num_cpu);
 
     for (i = 0; i < s->num_cpu; i++) {
@@ -381,6 +386,10 @@ 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;
+        }
     }
 }
 
@@ -494,9 +503,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, dma, 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..7e57654061 100644
--- a/hw/intc/arm_gicv3_dist.c
+++ b/hw/intc/arm_gicv3_dist.c
@@ -371,7 +371,9 @@ static MemTxResult gicd_readl(GICv3State *s, hwaddr offset,
          * 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
@@ -386,7 +388,8 @@ static MemTxResult gicd_readl(GICv3State *s, hwaddr offset,
         bool sec_extn = !(s->gicd_ctlr & GICD_CTLR_DS);
 
         *data = (1 << 25) | (1 << 24) | (sec_extn << 10) |
-            (0xf << 19) | itlinesnumber;
+            (s->lpi_enable << GICD_TYPER_LPIS_OFFSET) |
+            (GICD_TYPER_IDBITS << GICD_TYPER_IDBITS_OFFSET) | itlinesnumber;
         return MEMTX_OK;
     }
     case GICD_IIDR:
diff --git a/hw/intc/arm_gicv3_its.c b/hw/intc/arm_gicv3_its.c
index 82bb5b84ef..0a978cf55b 100644
--- a/hw/intc/arm_gicv3_its.c
+++ b/hw/intc/arm_gicv3_its.c
@@ -294,6 +294,7 @@ static MemTxResult process_mapti(GICv3ITSState *s, uint64_t value,
     uint64_t itel = 0;
     uint32_t iteh = 0;
     uint32_t int_spurious = INTID_SPURIOUS;
+    uint64_t idbits;
 
     devid = (value >> DEVID_SHIFT) & DEVID_MASK;
     offset += NUM_BYTES_IN_DW;
@@ -330,7 +331,13 @@ static MemTxResult process_mapti(GICv3ITSState *s, uint64_t value,
     max_eventid = (1UL << (((dte >> 1U) & SIZE_MASK) + 1));
 
     if (!ignore_pInt) {
-        max_Intid = (1UL << (FIELD_EX64(s->typer, GITS_TYPER, IDBITS) + 1));
+        idbits = MIN(FIELD_EX64(s->gicv3->cpu->gicr_propbaser, GICR_PROPBASER,
+                                IDBITS), GICD_TYPER_IDBITS);
+
+        if (idbits < GICR_PROPBASER_IDBITS_THRESHOLD) {
+            return res;
+        }
+        max_Intid = (1ULL << (idbits + 1));
     }
 
     if ((devid > s->dt.max_devids) || (icid > s->ct.max_collids) ||
diff --git a/hw/intc/arm_gicv3_redist.c b/hw/intc/arm_gicv3_redist.c
index 8645220d61..fb9a4ee3cc 100644
--- a/hw/intc/arm_gicv3_redist.c
+++ b/hw/intc/arm_gicv3_redist.c
@@ -244,14 +244,21 @@ static MemTxResult gicr_readl(GICv3CPUState *cs, hwaddr offset,
 static MemTxResult gicr_writel(GICv3CPUState *cs, hwaddr offset,
                                uint64_t value, MemTxAttrs attrs)
 {
+
     switch (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 */
@@ -395,6 +402,7 @@ static MemTxResult gicr_readll(GICv3CPUState *cs, hwaddr offset,
 static MemTxResult gicr_writell(GICv3CPUState *cs, hwaddr offset,
                                 uint64_t value, MemTxAttrs attrs)
 {
+
     switch (offset) {
     case GICR_PROPBASER:
         cs->gicr_propbaser = value;
diff --git a/hw/intc/gicv3_internal.h b/hw/intc/gicv3_internal.h
index ce45cd0ef6..91dbe01176 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 0715b0bc2a..c1348cc60a 100644
--- a/include/hw/intc/arm_gicv3_common.h
+++ b/include/hw/intc/arm_gicv3_common.h
@@ -221,6 +221,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] 52+ messages in thread

* [PATCH v4 6/8] hw/intc: GICv3 redistributor ITS processing
  2021-06-02 18:00 [PATCH v4 0/8] GICv3 LPI and ITS feature implementation Shashi Mallela
                   ` (4 preceding siblings ...)
  2021-06-02 18:00 ` [PATCH v4 5/8] hw/intc: GICv3 ITS Feature enablement Shashi Mallela
@ 2021-06-02 18:00 ` Shashi Mallela
  2021-06-08 13:57   ` Peter Maydell
  2021-06-13 16:26   ` Eric Auger
  2021-06-02 18:00 ` [PATCH v4 7/8] hw/arm/sbsa-ref: add ITS support in SBSA GIC Shashi Mallela
                   ` (2 subsequent siblings)
  8 siblings, 2 replies; 52+ messages in thread
From: Shashi Mallela @ 2021-06-02 18:00 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                |   9 ++
 hw/intc/arm_gicv3_common.c         |   1 +
 hw/intc/arm_gicv3_cpuif.c          |   7 +-
 hw/intc/arm_gicv3_its.c            |  14 ++-
 hw/intc/arm_gicv3_redist.c         | 145 +++++++++++++++++++++++++++++
 hw/intc/gicv3_internal.h           |  10 ++
 include/hw/intc/arm_gicv3_common.h |  10 ++
 7 files changed, 190 insertions(+), 6 deletions(-)

diff --git a/hw/intc/arm_gicv3.c b/hw/intc/arm_gicv3.c
index d63f8af604..4d19190b9c 100644
--- a/hw/intc/arm_gicv3.c
+++ b/hw/intc/arm_gicv3.c
@@ -165,6 +165,15 @@ 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 && cs->lpivalid) {
+        if (irqbetter(cs, cs->hpplpi.irq, cs->hpplpi.prio)) {
+            cs->hppi.irq = cs->hpplpi.irq;
+            cs->hppi.prio = cs->hpplpi.prio;
+            cs->hppi.grp = cs->hpplpi.grp;
+            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_common.c b/hw/intc/arm_gicv3_common.c
index 53dea2a775..223db16fec 100644
--- a/hw/intc/arm_gicv3_common.c
+++ b/hw/intc/arm_gicv3_common.c
@@ -435,6 +435,7 @@ static void arm_gicv3_common_reset(DeviceState *dev)
         memset(cs->gicr_ipriorityr, 0, sizeof(cs->gicr_ipriorityr));
 
         cs->hppi.prio = 0xff;
+        cs->hpplpi.prio = 0xff;
 
         /* State in the CPU interface must *not* be reset here, because it
          * is part of the CPU's reset domain, not the GIC device's.
diff --git a/hw/intc/arm_gicv3_cpuif.c b/hw/intc/arm_gicv3_cpuif.c
index 81f94c7f4a..5be3efaa3f 100644
--- a/hw/intc/arm_gicv3_cpuif.c
+++ b/hw/intc/arm_gicv3_cpuif.c
@@ -898,10 +898,12 @@ static void icc_activate_irq(GICv3CPUState *cs, int irq)
         cs->gicr_iactiver0 = deposit32(cs->gicr_iactiver0, irq, 1, 1);
         cs->gicr_ipendr0 = deposit32(cs->gicr_ipendr0, irq, 1, 0);
         gicv3_redist_update(cs);
-    } else {
+    } else if (irq < GICV3_LPI_INTID_START) {
         gicv3_gicd_active_set(cs->gic, irq);
         gicv3_gicd_pending_clear(cs->gic, irq);
         gicv3_update(cs->gic, irq, 1);
+    } else {
+        gicv3_redist_lpi_pending(cs, irq, 0);
     }
 }
 
@@ -1317,7 +1319,8 @@ static void icc_eoir_write(CPUARMState *env, const ARMCPRegInfo *ri,
     trace_gicv3_icc_eoir_write(is_eoir0 ? 0 : 1,
                                gicv3_redist_affid(cs), value);
 
-    if (irq >= cs->gic->num_irq) {
+    if ((irq >= cs->gic->num_irq) &&  (!(cs->gic->lpi_enable &&
+        (irq >= GICV3_LPI_INTID_START)))) {
         /* This handles two cases:
          * 1. If software writes the ID of a spurious interrupt [ie 1020-1023]
          * to the GICC_EOIR, the GIC ignores that write.
diff --git a/hw/intc/arm_gicv3_its.c b/hw/intc/arm_gicv3_its.c
index 0a978cf55b..e0fbd4041f 100644
--- a/hw/intc/arm_gicv3_its.c
+++ b/hw/intc/arm_gicv3_its.c
@@ -211,6 +211,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;
     uint64_t itel = 0;
     uint32_t iteh = 0;
 
@@ -267,10 +268,15 @@ static MemTxResult process_int(GICv3ITSState *s, uint64_t value,
          * command in the queue
          */
     } else {
-        /*
-         * Current implementation only supports rdbase == procnum
-         * Hence rdbase physical address is ignored
-         */
+        rdbase = (cte >> 1U) & RDBASE_PROCNUM_MASK;
+        assert(rdbase <= s->gicv3->num_cpu);
+
+        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 */
             res = update_ite(s, eventid, dte, itel, iteh);
diff --git a/hw/intc/arm_gicv3_redist.c b/hw/intc/arm_gicv3_redist.c
index fb9a4ee3cc..bfc6e4e9b9 100644
--- a/hw/intc/arm_gicv3_redist.c
+++ b/hw/intc/arm_gicv3_redist.c
@@ -255,6 +255,11 @@ 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 */
+                cs->lpivalid = false;
+                cs->hpplpi.prio = 0xff;
+                gicv3_redist_update_lpi(cs);
+                gicv3_redist_update(cs);
             } else {
                 cs->gicr_ctlr &= ~GICR_CTLR_ENABLE_LPIS;
             }
@@ -534,6 +539,146 @@ MemTxResult gicv3_redist_write(void *opaque, hwaddr offset, uint64_t data,
     return r;
 }
 
+void gicv3_redist_update_lpi(GICv3CPUState *cs)
+{
+    /*
+     * This function scans the LPI pending table and for each pending
+     * LPI, reads the corresponding entry from LPI configuration table
+     * to extract the priority info and determine if the LPI priority
+     * is lower than the current high priority interrupt.If yes, update
+     * high priority pending interrupt to that of LPI.
+     */
+    AddressSpace *as = &cs->gic->dma_as;
+    uint64_t lpict_baddr, lpipt_baddr;
+    uint32_t pendt_size = 0;
+    uint8_t lpite;
+    uint8_t prio, pend;
+    int i;
+    uint64_t idbits;
+
+    idbits = MIN(FIELD_EX64(cs->gicr_propbaser, GICR_PROPBASER, IDBITS),
+                 GICD_TYPER_IDBITS);
+
+    if ((!cs->gicr_ctlr & GICR_CTLR_ENABLE_LPIS) || !cs->gicr_propbaser ||
+        !cs->gicr_pendbaser || (idbits < GICR_PROPBASER_IDBITS_THRESHOLD)) {
+        return;
+    }
+
+    lpict_baddr = cs->gicr_propbaser & R_GICR_PROPBASER_PHYADDR_MASK;
+
+    lpipt_baddr = cs->gicr_pendbaser & R_GICR_PENDBASER_PHYADDR_MASK;
+
+    /* Determine the highest priority pending interrupt among LPIs */
+    pendt_size = (1ULL << (idbits + 1));
+
+    for (i = 0; i < pendt_size / 8; 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));
+
+            if (!(lpite & LPI_CTE_ENABLED)) {
+                continue;
+            }
+
+            if (cs->gic->gicd_ctlr & GICD_CTLR_DS) {
+                prio = lpite & LPI_PRIORITY_MASK;
+            } else {
+                prio = lpite & LPI_SPRIORITY_MASK;
+            }
+
+            if (prio <= cs->hpplpi.prio) {
+                cs->hpplpi.irq = GICV3_LPI_INTID_START + i;
+                cs->hpplpi.prio = prio;
+                /* LPIs are always non-secure Grp1 interrupts */
+                cs->hpplpi.grp = GICV3_G1NS;
+                cs->lpivalid = true;
+            }
+        }
+    }
+}
+
+void gicv3_redist_lpi_pending(GICv3CPUState *cs, int irq, int level)
+{
+    AddressSpace *as = &cs->gic->dma_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 = cs->gicr_pendbaser & R_GICR_PENDBASER_PHYADDR_MASK;
+
+    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));
+        }
+    }
+    cs->lpivalid = false;
+    cs->hpplpi.prio = 0xff;
+    gicv3_redist_update_lpi(cs);
+}
+
+void gicv3_redist_process_lpi(GICv3CPUState *cs, int irq, int level)
+{
+    AddressSpace *as = &cs->gic->dma_as;
+    uint64_t lpict_baddr;
+    uint8_t lpite;
+    uint64_t idbits;
+
+    idbits = MIN(FIELD_EX64(cs->gicr_propbaser, GICR_PROPBASER, IDBITS),
+                 GICD_TYPER_IDBITS);
+
+    if ((!cs->gicr_ctlr & GICR_CTLR_ENABLE_LPIS) || !cs->gicr_propbaser ||
+         !cs->gicr_pendbaser || (idbits < GICR_PROPBASER_IDBITS_THRESHOLD) ||
+         (irq > (1ULL << (idbits + 1)))) {
+        return;
+    }
+
+    lpict_baddr = cs->gicr_propbaser & R_GICR_PROPBASER_PHYADDR_MASK;
+
+    /* 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 91dbe01176..bcbccba573 100644
--- a/hw/intc/gicv3_internal.h
+++ b/hw/intc/gicv3_internal.h
@@ -308,6 +308,13 @@ 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 LPI_SPRIORITY_MASK        0x7e
+
 #define GITS_CMDQ_ENTRY_SIZE               32
 #define NUM_BYTES_IN_DW                     8
 
@@ -452,6 +459,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);
+void 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);
 
diff --git a/include/hw/intc/arm_gicv3_common.h b/include/hw/intc/arm_gicv3_common.h
index c1348cc60a..5d839da9c9 100644
--- a/include/hw/intc/arm_gicv3_common.h
+++ b/include/hw/intc/arm_gicv3_common.h
@@ -204,6 +204,16 @@ struct GICv3CPUState {
      * real state above; it doesn't need to be migrated.
      */
     PendingIrq hppi;
+
+    /*
+     * Current highest priority pending lpi for this CPU.
+     * This is cached information that can be recalculated from the
+     * real state above; it doesn't need to be migrated.
+     */
+    PendingIrq hpplpi;
+
+    bool lpivalid; /* current highest priority lpi validity status */
+
     /* This is temporary working state, to avoid a malloc in gicv3_update() */
     bool seenbetter;
 };
-- 
2.27.0



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

* [PATCH v4 7/8] hw/arm/sbsa-ref: add ITS support in SBSA GIC
  2021-06-02 18:00 [PATCH v4 0/8] GICv3 LPI and ITS feature implementation Shashi Mallela
                   ` (5 preceding siblings ...)
  2021-06-02 18:00 ` [PATCH v4 6/8] hw/intc: GICv3 redistributor ITS processing Shashi Mallela
@ 2021-06-02 18:00 ` Shashi Mallela
  2021-06-03 11:42   ` Leif Lindholm
  2021-06-02 18:00 ` [PATCH v4 8/8] hw/arm/virt: add ITS support in virt GIC Shashi Mallela
  2021-06-08 10:00 ` [PATCH v4 0/8] GICv3 LPI and ITS feature implementation Peter Maydell
  8 siblings, 1 reply; 52+ messages in thread
From: Shashi Mallela @ 2021-06-02 18:00 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 43c19b4923..3d9c073636 100644
--- a/hw/arm/sbsa-ref.c
+++ b/hw/arm/sbsa-ref.c
@@ -34,7 +34,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"
@@ -64,6 +64,7 @@ enum {
     SBSA_CPUPERIPHS,
     SBSA_GIC_DIST,
     SBSA_GIC_REDIST,
+    SBSA_GIC_ITS,
     SBSA_SECURE_EC,
     SBSA_GWDT,
     SBSA_GWDT_REFRESH,
@@ -107,6 +108,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 },
@@ -377,7 +379,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;
@@ -404,6 +419,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);
@@ -450,6 +469,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,
@@ -762,7 +782,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] 52+ messages in thread

* [PATCH v4 8/8] hw/arm/virt: add ITS support in virt GIC
  2021-06-02 18:00 [PATCH v4 0/8] GICv3 LPI and ITS feature implementation Shashi Mallela
                   ` (6 preceding siblings ...)
  2021-06-02 18:00 ` [PATCH v4 7/8] hw/arm/sbsa-ref: add ITS support in SBSA GIC Shashi Mallela
@ 2021-06-02 18:00 ` Shashi Mallela
  2021-06-08 11:00   ` Peter Maydell
  2021-06-08 10:00 ` [PATCH v4 0/8] GICv3 LPI and ITS feature implementation Peter Maydell
  8 siblings, 1 reply; 52+ messages in thread
From: Shashi Mallela @ 2021-06-02 18:00 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         | 27 +++++++++++++++++++++++++--
 include/hw/arm/virt.h |  2 ++
 target/arm/kvm_arm.h  |  4 ++--
 3 files changed, 29 insertions(+), 4 deletions(-)

diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index 840758666d..f598f048da 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -583,6 +583,12 @@ static void create_its(VirtMachineState *vms)
     const char *itsclass = its_class_name();
     DeviceState *dev;
 
+    if (!strcmp(itsclass, "arm-gicv3-its")) {
+        if (!vms->tcg_its) {
+            itsclass = NULL;
+        }
+    }
+
     if (!itsclass) {
         /* Do nothing if not supported */
         return;
@@ -620,7 +626,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 */
@@ -654,6 +660,14 @@ 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()) {
+            if (vms->tcg_its) {
+                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 +2053,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);
 
@@ -2718,6 +2732,12 @@ static void virt_instance_init(Object *obj)
     } else {
         /* Default allows ITS instantiation */
         vms->its = true;
+
+        if (vmc->no_tcg_its) {
+            vms->tcg_its = false;
+        } else {
+            vms->tcg_its = true;
+        }
     }
 
     /* Default disallows iommu instantiation */
@@ -2764,6 +2784,9 @@ DEFINE_VIRT_MACHINE_AS_LATEST(6, 1)
 
 static void virt_machine_6_0_options(MachineClass *mc)
 {
+    VirtMachineClass *vmc = VIRT_MACHINE_CLASS(OBJECT_CLASS(mc));
+    /* qemu ITS was introduced with 6.1 */
+    vmc->no_tcg_its = true;
 }
 DEFINE_VIRT_MACHINE(6, 0)
 
diff --git a/include/hw/arm/virt.h b/include/hw/arm/virt.h
index 921416f918..f873ab9068 100644
--- a/include/hw/arm/virt.h
+++ b/include/hw/arm/virt.h
@@ -120,6 +120,7 @@ struct VirtMachineClass {
     MachineClass parent;
     bool disallow_affinity_adjustment;
     bool no_its;
+    bool no_tcg_its;
     bool no_pmu;
     bool claim_edge_triggered_timers;
     bool smbios_old_sys_ver;
@@ -141,6 +142,7 @@ struct VirtMachineState {
     bool highmem;
     bool highmem_ecam;
     bool its;
+    bool tcg_its;
     bool virt;
     bool ras;
     bool mte;
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] 52+ messages in thread

* Re: [PATCH v4 7/8] hw/arm/sbsa-ref: add ITS support in SBSA GIC
  2021-06-02 18:00 ` [PATCH v4 7/8] hw/arm/sbsa-ref: add ITS support in SBSA GIC Shashi Mallela
@ 2021-06-03 11:42   ` Leif Lindholm
  2021-06-03 15:31     ` shashi.mallela
  0 siblings, 1 reply; 52+ messages in thread
From: Leif Lindholm @ 2021-06-03 11:42 UTC (permalink / raw)
  To: Shashi Mallela; +Cc: peter.maydell, rad, qemu-devel, qemu-arm

On Wed, Jun 02, 2021 at 14:00:41 -0400, Shashi Mallela 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(-)
> 
> diff --git a/hw/arm/sbsa-ref.c b/hw/arm/sbsa-ref.c
> index 43c19b4923..3d9c073636 100644
> --- a/hw/arm/sbsa-ref.c
> +++ b/hw/arm/sbsa-ref.c
> @@ -34,7 +34,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"
> @@ -64,6 +64,7 @@ enum {
>      SBSA_CPUPERIPHS,
>      SBSA_GIC_DIST,
>      SBSA_GIC_REDIST,
> +    SBSA_GIC_ITS,
>      SBSA_SECURE_EC,
>      SBSA_GWDT,
>      SBSA_GWDT_REFRESH,
> @@ -107,6 +108,7 @@ static const MemMapEntry sbsa_ref_memmap[] = {
>      [SBSA_CPUPERIPHS] =         { 0x40000000, 0x00040000 },
>      [SBSA_GIC_DIST] =           { 0x40060000, 0x00010000 },
>      [SBSA_GIC_REDIST] =         { 0x40080000, 0x04000000 },

It seems customary in QEMU to flag gaps in memory space (although
admittedly, we'd already failed to do so here). This patch leaves a
gap of 0x00010000. Is there a particular reason?

> +    [SBSA_GIC_ITS] =            { 0x44090000, 0x00020000 },

And then again a gap (the one we already had).

/
    Leif

>      [SBSA_SECURE_EC] =          { 0x50000000, 0x00001000 },
>      [SBSA_GWDT_REFRESH] =       { 0x50010000, 0x00001000 },
>      [SBSA_GWDT_CONTROL] =       { 0x50011000, 0x00001000 },
> @@ -377,7 +379,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;
> @@ -404,6 +419,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);
> @@ -450,6 +469,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,
> @@ -762,7 +782,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	[flat|nested] 52+ messages in thread

* Re: [PATCH v4 7/8] hw/arm/sbsa-ref: add ITS support in SBSA GIC
  2021-06-03 11:42   ` Leif Lindholm
@ 2021-06-03 15:31     ` shashi.mallela
  2021-06-04 10:42       ` Leif Lindholm
  0 siblings, 1 reply; 52+ messages in thread
From: shashi.mallela @ 2021-06-03 15:31 UTC (permalink / raw)
  To: Leif Lindholm; +Cc: peter.maydell, rad, qemu-devel, qemu-arm

On Thu, 2021-06-03 at 12:42 +0100, Leif Lindholm wrote:
> On Wed, Jun 02, 2021 at 14:00:41 -0400, Shashi Mallela 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(-)
> > 
> > diff --git a/hw/arm/sbsa-ref.c b/hw/arm/sbsa-ref.c
> > index 43c19b4923..3d9c073636 100644
> > --- a/hw/arm/sbsa-ref.c
> > +++ b/hw/arm/sbsa-ref.c
> > @@ -34,7 +34,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"
> > @@ -64,6 +64,7 @@ enum {
> >      SBSA_CPUPERIPHS,
> >      SBSA_GIC_DIST,
> >      SBSA_GIC_REDIST,
> > +    SBSA_GIC_ITS,
> >      SBSA_SECURE_EC,
> >      SBSA_GWDT,
> >      SBSA_GWDT_REFRESH,
> > @@ -107,6 +108,7 @@ static const MemMapEntry sbsa_ref_memmap[] = {
> >      [SBSA_CPUPERIPHS] =         { 0x40000000, 0x00040000 },
> >      [SBSA_GIC_DIST] =           { 0x40060000, 0x00010000 },
> >      [SBSA_GIC_REDIST] =         { 0x40080000, 0x04000000 },
> 
> It seems customary in QEMU to flag gaps in memory space (although
> admittedly, we'd already failed to do so here). This patch leaves a
> gap of 0x00010000. Is there a particular reason?
> 
> > +    [SBSA_GIC_ITS] =            { 0x44090000, 0x00020000 },
> 
> And then again a gap (the one we already had).
> 
> /
>     Leif
>
> No specific reason,but from ITS point of view tried to stay within 
> the GIC's 0x40060000 to 0x50000000 zone.The gap of 0x00010000 would 
> also account for future GIC additions like virtual LPI support.
>
> 
> >      [SBSA_SECURE_EC] =          { 0x50000000, 0x00001000 },
> >      [SBSA_GWDT_REFRESH] =       { 0x50010000, 0x00001000 },
> >      [SBSA_GWDT_CONTROL] =       { 0x50011000, 0x00001000 },
> > @@ -377,7 +379,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;
> > @@ -404,6 +419,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);
> > @@ -450,6 +469,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,
> > @@ -762,7 +782,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	[flat|nested] 52+ messages in thread

* Re: [PATCH v4 7/8] hw/arm/sbsa-ref: add ITS support in SBSA GIC
  2021-06-03 15:31     ` shashi.mallela
@ 2021-06-04 10:42       ` Leif Lindholm
  2021-06-04 15:36         ` shashi.mallela
  0 siblings, 1 reply; 52+ messages in thread
From: Leif Lindholm @ 2021-06-04 10:42 UTC (permalink / raw)
  To: shashi.mallela; +Cc: peter.maydell, rad, qemu-devel, qemu-arm

On Thu, Jun 03, 2021 at 11:31:21 -0400, shashi.mallela@linaro.org wrote:
> On Thu, 2021-06-03 at 12:42 +0100, Leif Lindholm wrote:
> > On Wed, Jun 02, 2021 at 14:00:41 -0400, Shashi Mallela 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(-)
> > > 
> > > diff --git a/hw/arm/sbsa-ref.c b/hw/arm/sbsa-ref.c
> > > index 43c19b4923..3d9c073636 100644
> > > --- a/hw/arm/sbsa-ref.c
> > > +++ b/hw/arm/sbsa-ref.c
> > > @@ -34,7 +34,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"
> > > @@ -64,6 +64,7 @@ enum {
> > >      SBSA_CPUPERIPHS,
> > >      SBSA_GIC_DIST,
> > >      SBSA_GIC_REDIST,
> > > +    SBSA_GIC_ITS,
> > >      SBSA_SECURE_EC,
> > >      SBSA_GWDT,
> > >      SBSA_GWDT_REFRESH,
> > > @@ -107,6 +108,7 @@ static const MemMapEntry sbsa_ref_memmap[] = {
> > >      [SBSA_CPUPERIPHS] =         { 0x40000000, 0x00040000 },
> > >      [SBSA_GIC_DIST] =           { 0x40060000, 0x00010000 },
> > >      [SBSA_GIC_REDIST] =         { 0x40080000, 0x04000000 },
> > 
> > It seems customary in QEMU to flag gaps in memory space (although
> > admittedly, we'd already failed to do so here). This patch leaves a
> > gap of 0x00010000. Is there a particular reason?
> > 
> > > +    [SBSA_GIC_ITS] =            { 0x44090000, 0x00020000 },
> > 
> > And then again a gap (the one we already had).
> > 
> > No specific reason,but from ITS point of view tried to stay within 
> > the GIC's 0x40060000 to 0x50000000 zone.The gap of 0x00010000 would 
> > also account for future GIC additions like virtual LPI support.

Right. I was more thinking 64kB isn't much space to extend into.
Would it be worth pushing the ITS either all the way up to just below
0x50000000, 0x48000000, or 0x45000000?

Either way, the gap(s) would be good to point out with comments, and
potential future use. I only noticed this one on like the third pass
of reading.

/
    Leif

> > >      [SBSA_SECURE_EC] =          { 0x50000000, 0x00001000 },
> > >      [SBSA_GWDT_REFRESH] =       { 0x50010000, 0x00001000 },
> > >      [SBSA_GWDT_CONTROL] =       { 0x50011000, 0x00001000 },
> > > @@ -377,7 +379,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;
> > > @@ -404,6 +419,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);
> > > @@ -450,6 +469,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,
> > > @@ -762,7 +782,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	[flat|nested] 52+ messages in thread

* Re: [PATCH v4 7/8] hw/arm/sbsa-ref: add ITS support in SBSA GIC
  2021-06-04 10:42       ` Leif Lindholm
@ 2021-06-04 15:36         ` shashi.mallela
  2021-07-08 19:40           ` Leif Lindholm
  0 siblings, 1 reply; 52+ messages in thread
From: shashi.mallela @ 2021-06-04 15:36 UTC (permalink / raw)
  To: Leif Lindholm; +Cc: peter.maydell, rad, qemu-devel, qemu-arm

On Fri, 2021-06-04 at 11:42 +0100, Leif Lindholm wrote:
> On Thu, Jun 03, 2021 at 11:31:21 -0400, shashi.mallela@linaro.org
> wrote:
> > On Thu, 2021-06-03 at 12:42 +0100, Leif Lindholm wrote:
> > > On Wed, Jun 02, 2021 at 14:00:41 -0400, Shashi Mallela 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(-)
> > > > 
> > > > diff --git a/hw/arm/sbsa-ref.c b/hw/arm/sbsa-ref.c
> > > > index 43c19b4923..3d9c073636 100644
> > > > --- a/hw/arm/sbsa-ref.c
> > > > +++ b/hw/arm/sbsa-ref.c
> > > > @@ -34,7 +34,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"
> > > > @@ -64,6 +64,7 @@ enum {
> > > >      SBSA_CPUPERIPHS,
> > > >      SBSA_GIC_DIST,
> > > >      SBSA_GIC_REDIST,
> > > > +    SBSA_GIC_ITS,
> > > >      SBSA_SECURE_EC,
> > > >      SBSA_GWDT,
> > > >      SBSA_GWDT_REFRESH,
> > > > @@ -107,6 +108,7 @@ static const MemMapEntry sbsa_ref_memmap[]
> > > > = {
> > > >      [SBSA_CPUPERIPHS] =         { 0x40000000, 0x00040000 },
> > > >      [SBSA_GIC_DIST] =           { 0x40060000, 0x00010000 },
> > > >      [SBSA_GIC_REDIST] =         { 0x40080000, 0x04000000 },
> > > 
> > > It seems customary in QEMU to flag gaps in memory space (although
> > > admittedly, we'd already failed to do so here). This patch leaves
> > > a
> > > gap of 0x00010000. Is there a particular reason?
> > > 
> > > > +    [SBSA_GIC_ITS] =            { 0x44090000, 0x00020000 },
> > > 
> > > And then again a gap (the one we already had).
> > > 
> > > No specific reason,but from ITS point of view tried to stay
> > > within 
> > > the GIC's 0x40060000 to 0x50000000 zone.The gap of 0x00010000
> > > would 
> > > also account for future GIC additions like virtual LPI support.
> 
> Right. I was more thinking 64kB isn't much space to extend into.
> Would it be worth pushing the ITS either all the way up to just below
> 0x50000000, 0x48000000, or 0x45000000?
>
> The current memory allocation size (of 67MB) for
redistributor(SBSA_GIC_REDIST) is already very large relative to its
overall required register address space.Hence ITS started at 0x44090000
(considering that redistributor space is sufficiently spaced) until
0x440B0000.Future virtual LPI addition can still stay within the
0x45000000 mark,leaving the whole area between 0x45000000 to 0x50000000
free for other devices.
are comments still recommended here? 
> 
> Either way, the gap(s) would be good to point out with comments, and
> potential future use. I only noticed this one on like the third pass
> of reading.
> 
> /
>     Leif
> 
> > > >      [SBSA_SECURE_EC] =          { 0x50000000, 0x00001000 },
> > > >      [SBSA_GWDT_REFRESH] =       { 0x50010000, 0x00001000 },
> > > >      [SBSA_GWDT_CONTROL] =       { 0x50011000, 0x00001000 },
> > > > @@ -377,7 +379,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;
> > > > @@ -404,6 +419,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);
> > > > @@ -450,6 +469,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,
> > > > @@ -762,7 +782,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	[flat|nested] 52+ messages in thread

* Re: [PATCH v4 0/8] GICv3 LPI and ITS feature implementation
  2021-06-02 18:00 [PATCH v4 0/8] GICv3 LPI and ITS feature implementation Shashi Mallela
                   ` (7 preceding siblings ...)
  2021-06-02 18:00 ` [PATCH v4 8/8] hw/arm/virt: add ITS support in virt GIC Shashi Mallela
@ 2021-06-08 10:00 ` Peter Maydell
  8 siblings, 0 replies; 52+ messages in thread
From: Peter Maydell @ 2021-06-08 10:00 UTC (permalink / raw)
  To: Shashi Mallela
  Cc: Leif Lindholm, QEMU Developers, qemu-arm, Radoslaw Biernacki

On Wed, 2 Jun 2021 at 19:00, Shashi Mallela <shashi.mallela@linaro.org> wrote:
>
> 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 v4:
>  - review comments addressed
>  - redesigned the lpi pending priority determination logic to scan
>    LPI pending table and config table right after lpi pending
>    statechanges(SET/RESET) through gicv3_redist_update_lpi() call to
>    determinethe highest priority lpi among the active lpis and save
>    the details.The high priority interrupt determination logic in
>    redistributor now usesthe saved high priority lpi details
>    (alongside other interrupt types) instead of calling
>    gicv3_redist_update_lpi() everytime(as in v3).This
>    significantly reduces the call overhead associated with
>    address_space_read of lpi config and pending tables.
>    Testing with this new design showed no boot delays.
>  - profiled execution of gicv3_redist_update_lpi() using perf and
>    framegraph to confirm execution is within normal limits.
>    Also,specifically measured execution time to be an average of 175us
>    with linux distro testing.
>  - All kvm_unit_tests PASS

This still fails to build with clang, in the same way as v3 failed.
Also (as noted in my other email, you need to integrate the updates
to the ACPI table test data into this series; 'make' and 'make check'
should work at every step in the patch series.

thanks
-- PMM


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

* Re: [PATCH v4 1/8] hw/intc: GICv3 ITS initial framework
  2021-06-02 18:00 ` [PATCH v4 1/8] hw/intc: GICv3 ITS initial framework Shashi Mallela
@ 2021-06-08 10:02   ` Peter Maydell
  2021-06-11 16:21   ` Eric Auger
  2021-06-12  6:52   ` Eric Auger
  2 siblings, 0 replies; 52+ messages in thread
From: Peter Maydell @ 2021-06-08 10:02 UTC (permalink / raw)
  To: Shashi Mallela
  Cc: Leif Lindholm, QEMU Developers, qemu-arm, Radoslaw Biernacki

On Wed, 2 Jun 2021 at 19:00, 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                | 240 +++++++++++++++++++++++++
>  hw/intc/arm_gicv3_its_common.c         |   8 +-
>  hw/intc/arm_gicv3_its_kvm.c            |   2 +-
>  hw/intc/gicv3_internal.h               |  88 +++++++--
>  hw/intc/meson.build                    |   1 +
>  include/hw/intc/arm_gicv3_its_common.h |   9 +-
>  6 files changed, 331 insertions(+), 17 deletions(-)
>  create mode 100644 hw/intc/arm_gicv3_its.c> @@ -129,7 +132,6 @@ static void gicv3_its_common_reset(DeviceState *dev)

>      s->cbaser = 0;
>      s->cwriter = 0;
>      s->creadr = 0;
> -    s->iidr = 0;

You don't need to delete this -- leave it for the benefit of the KVM code.

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

thanks
-- PMM


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

* Re: [PATCH v4 2/8] hw/intc: GICv3 ITS register definitions added
  2021-06-02 18:00 ` [PATCH v4 2/8] hw/intc: GICv3 ITS register definitions added Shashi Mallela
@ 2021-06-08 10:31   ` Peter Maydell
  2021-06-12  6:08   ` Eric Auger
  1 sibling, 0 replies; 52+ messages in thread
From: Peter Maydell @ 2021-06-08 10:31 UTC (permalink / raw)
  To: Shashi Mallela
  Cc: Leif Lindholm, QEMU Developers, qemu-arm, Radoslaw Biernacki

On Wed, 2 Jun 2021 at 19:00, 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>

> @@ -41,7 +192,73 @@ static MemTxResult its_writel(GICv3ITSState *s, hwaddr offset,
>                                uint64_t value, MemTxAttrs attrs)
>  {
>      MemTxResult result = MEMTX_OK;
> +    int index;
>
> +    switch (offset) {
> +    case GITS_CTLR:
> +        s->ctlr |= (value & ~(s->ctlr));
> +
> +        if (s->ctlr & ITS_CTLR_ENABLED) {
> +            extract_table_params(s);
> +            extract_cmdq_params(s);
> +            s->creadr = 0;
> +        }
> +        break;
> +    case GITS_CBASER:
> +        /*
> +         * IMPDEF choice:- 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:
> +        /*
> +         * IMPDEF choice:- GITS_CBASER register becomes RO if ITS is
> +         *                 already enabled
> +         */
> +        if (!(s->ctlr & ITS_CTLR_ENABLED)) {
> +            s->cbaser = deposit64(s->cbaser, 32, 32, value);
> +        }
> +        break;
> +    case GITS_CWRITER:
> +        s->cwriter = deposit64(s->cwriter, 0, 32,
> +                               (value & ~R_GITS_CWRITER_RETRY_MASK));
> +        break;
> +    case GITS_CWRITER + 4:
> +        s->cwriter = deposit64(s->cwriter, 32, 32,
> +                               (value & ~R_GITS_CWRITER_RETRY_MASK));

The RETRY bit is at the bottom of the 64-bit register, so you
don't want to mask with it when we're writing the top 32 bits
(otherwise you incorrectly clear bit 33 of the full 64-bit register).

> +        break;
> +    case GITS_BASER ... GITS_BASER + 0x3f:
> +        /*
> +         * IMPDEF choice:- GITS_BASERn register becomes RO if ITS is
> +         *                 already enabled
> +         */
> +        if (!(s->ctlr & ITS_CTLR_ENABLED)) {
> +            index = (offset - GITS_BASER) / 8;
> +
> +            if (offset & 7) {
> +                s->baser[index] = deposit64(s->baser[index], 32, 32,
> +                                            (value & ~GITS_BASER_VAL_MASK));
> +            } else {
> +                s->baser[index] = deposit64(s->baser[index], 0, 32,
> +                                            (value & ~GITS_BASER_VAL_MASK));
> +            }

This has two problems:
(1) same as above, you're masking a 32-bit half-value with a MASK
constant that's for the full 64-bit value
(2) here (unlike with CWRITER) we don't want to clear the non-writeable
bits but leave them alone.

Something like this should work:

               if (offset & 7) {
                   value <<= 32;
                   value &= ~GITS_BASER_VAL_MASK;
                   s->baser[index] &= GITS_BASER_VAL_MASK |
MAKE_64BIT_MASK(0, 32);
                   s->baser[index] |= value;
                } else {
                   value &= ~GITS_BASER_VAL_MASK;
                   s->baser[index] &= GITS_BASER_VAL_MASK |
MAKE_64BIT_MASK(32, 32);
                   s->baser[index] |= value;
                }

> +        }
> +        break;
> +    case GITS_IIDR:
> +    case GITS_IDREGS ... GITS_IDREGS + 0x2f:
> +        /* 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;
>  }

> @@ -57,7 +322,42 @@ 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:
> +        /*
> +         * IMPDEF choice:- GITS_BASERn register becomes 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);

This will allow the guest to write a 1 to a writeable bit,
but will not allow it to write a 0 again...
     s->baser[index] &= GITS_BASER_VAL_MASK;
     s->baser[index] |= (value & ~GITS_BASER_VAL_MASK);

Why VAL_MASK, by the way? The mask is defining the set of read-only bits,
so RO_MASK seems like a clearer name.

> +        }
> +        break;
> +    case GITS_CBASER:
> +        /*
> +         * IMPDEF choice:- GITS_CBASER register becomes RO if ITS is
> +         *                 already enabled
> +         */
> +        if (!(s->ctlr & ITS_CTLR_ENABLED)) {
> +            s->cbaser = value;
> +        }
> +        break;
> +    case GITS_CWRITER:
> +        s->cwriter = value & ~R_GITS_CWRITER_RETRY_MASK;
> +        break;
> +    case GITS_CREADR:
> +    case GITS_TYPER:
> +        /* 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;
>  }

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


thanks
-- PMM


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

* Re: [PATCH v4 3/8] hw/intc: GICv3 ITS command queue framework
  2021-06-02 18:00 ` [PATCH v4 3/8] hw/intc: GICv3 ITS command queue framework Shashi Mallela
@ 2021-06-08 10:38   ` Peter Maydell
  2021-06-13 14:13   ` Eric Auger
  2021-06-13 14:39   ` Eric Auger
  2 siblings, 0 replies; 52+ messages in thread
From: Peter Maydell @ 2021-06-08 10:38 UTC (permalink / raw)
  To: Shashi Mallela
  Cc: Leif Lindholm, QEMU Developers, qemu-arm, Radoslaw Biernacki

On Wed, 2 Jun 2021 at 19:00, 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  | 295 +++++++++++++++++++++++++++++++++++++++
>  hw/intc/gicv3_internal.h |  37 +++++
>  2 files changed, 332 insertions(+)

> +    if ((icid > s->ct.max_collids) || (rdbase > s->gicv3->num_cpu)) {
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +                      "ITS MAPC: invalid collection table attributes "
> +                      "icid %d rdbase %lu\n",  icid, rdbase);
> +        /*
> +         * in this implementation,in case of error

Still missing space after comma.

> +         * we ignore this command and move onto the next
> +         * command in the queue
> +         */
> +    } else {
> +        res = update_cte(s, icid, valid, rdbase);
> +    }
> +
> +    return res;
> +}


> +        } else {
> +            /*
> +             * in this implementation,in case of dma read/write error
> +             * we stall the command processing
> +             */

Ditto.

> +            s->creadr = FIELD_DP64(s->creadr, GITS_CREADR, STALLED, 1);
> +            qemu_log_mask(LOG_GUEST_ERROR,
> +                          "%s: %x cmd processing failed!!\n", __func__, cmd);

The double-exclamation marks are unnecessary :-)

> +            break;
> +        }
> +    }
> +}

Otherwise

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

thanks
-- PMM


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

* Re: [PATCH v4 4/8] hw/intc: GICv3 ITS Command processing
  2021-06-02 18:00 ` [PATCH v4 4/8] hw/intc: GICv3 ITS Command processing Shashi Mallela
@ 2021-06-08 10:45   ` Peter Maydell
  2021-06-13 15:55   ` Eric Auger
  1 sibling, 0 replies; 52+ messages in thread
From: Peter Maydell @ 2021-06-08 10:45 UTC (permalink / raw)
  To: Shashi Mallela
  Cc: Leif Lindholm, QEMU Developers, qemu-arm, Radoslaw Biernacki

On Wed, 2 Jun 2021 at 19:00, 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            | 334 +++++++++++++++++++++++++++++
>  hw/intc/gicv3_internal.h           |  12 ++
>  include/hw/intc/arm_gicv3_common.h |   2 +
>  3 files changed, 348 insertions(+)
>
> diff --git a/hw/intc/arm_gicv3_its.c b/hw/intc/arm_gicv3_its.c
> index 6551c577b3..82bb5b84ef 100644
> --- a/hw/intc/arm_gicv3_its.c
> +++ b/hw/intc/arm_gicv3_its.c
> @@ -28,6 +28,13 @@ 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 uint64_t baser_base_addr(uint64_t value, uint32_t page_sz)
>  {
>      uint64_t result = 0;
> @@ -49,6 +56,315 @@ static uint64_t baser_base_addr(uint64_t value, uint32_t page_sz)
>      return result;
>  }
>
> +static bool get_cte(GICv3ITSState *s, uint16_t icid, uint64_t *cte,
> +                    MemTxResult *res)
> +{
> +    AddressSpace *as = &s->gicv3->dma_as;
> +    uint64_t l2t_addr;
> +    uint64_t value;
> +    bool valid_l2t;
> +    uint32_t l2t_id;
> +    uint32_t max_l2_entries;
> +    bool status = false;
> +
> +    if (s->ct.indirect) {
> +        l2t_id = icid / (s->ct.page_sz / L1TABLE_ENTRY_SIZE);
> +
> +        value = address_space_ldq_le(as,
> +                                     s->ct.base_addr +
> +                                     (l2t_id * L1TABLE_ENTRY_SIZE),
> +                                     MEMTXATTRS_UNSPECIFIED, res);
> +
> +        if (*res == MEMTX_OK) {
> +            valid_l2t = (value >> VALID_SHIFT) & VALID_MASK;

VALID_MASK should be the mask in its shifted location
(for consistency with how the FIELD macros do it). Then
this is just
   valid_l2t = (value & VALID_MASK) != 0;

> +
> +            if (valid_l2t) {
> +                max_l2_entries = s->ct.page_sz / s->ct.entry_sz;
> +
> +                l2t_addr = value & ((1ULL << 51) - 1);
> +
> +                *cte =  address_space_ldq_le(as, l2t_addr +
> +                                    ((icid % max_l2_entries) * GITS_CTE_SIZE),
> +                                    MEMTXATTRS_UNSPECIFIED, res);
> +           }
> +       }
> +    } else {
> +        /* Flat level table */
> +        *cte =  address_space_ldq_le(as, s->ct.base_addr +
> +                                     (icid * GITS_CTE_SIZE),
> +                                      MEMTXATTRS_UNSPECIFIED, res);
> +    }
> +
> +    if (*cte & VALID_MASK) {
> +        status = true;
> +    }
> +
> +    return status;

You don't need the 'status' variable, you can just
     return (*cte & VALID_MASK) != 0;

(Looks like this code is already assuming VALID_MASK is the mask
in its shifted location, and so inconsistent with your current definition ?)

> +static bool get_ite(GICv3ITSState *s, uint32_t eventid, uint64_t dte,
> +                    uint16_t *icid, uint32_t *pIntid, MemTxResult *res)
> +{
> +    AddressSpace *as = &s->gicv3->dma_as;
> +    uint64_t itt_addr;
> +    bool status = false;
> +    uint64_t itel = 0;
> +    uint32_t iteh = 0;
> +
> +    itt_addr = (dte >> 6ULL) & ITTADDR_MASK;
> +    itt_addr <<= ITTADDR_SHIFT; /* 256 byte aligned */
> +
> +    itel = address_space_ldq_le(as, itt_addr + (eventid * sizeof(uint64_t)),
> +                                MEMTXATTRS_UNSPECIFIED, res);
> +
> +    if (*res == MEMTX_OK) {
> +        iteh = address_space_ldl_le(as, itt_addr + ((eventid +
> +                                    sizeof(uint64_t)) * sizeof(uint32_t)),
> +                                    MEMTXATTRS_UNSPECIFIED, res);
> +
> +        if (*res == MEMTX_OK) {
> +            if (itel & VALID_MASK) {
> +                if ((itel >> ITE_ENTRY_INTTYPE_SHIFT) & GITS_TYPE_PHYSICAL) {
> +                    *pIntid = (itel >> ITE_ENTRY_INTID_SHIFT) &
> +                               ITE_ENTRY_INTID_MASK;

More _MASK constants that don't have the same semantics as the
registerfields versions. Please can you change all of these ?

> +                    *icid = iteh & ITE_ENTRY_ICID_MASK;
> +                    status = true;
> +                }
> +            }
> +        }
> +    }
> +    return status;
> +}
> +
> +    if ((devid > s->dt.max_devids) || !dte_valid || !ite_valid ||
> +            !cte_valid || (eventid > max_eventid)) {
> +        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

Another missing space after comma.

> +        /*
> +         * in this implementation,in case of error

And again.

> +         * we ignore this command and move onto the next
> +         * command in the queue
> +         */

thanks
-- PMM


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

* Re: [PATCH v4 5/8] hw/intc: GICv3 ITS Feature enablement
  2021-06-02 18:00 ` [PATCH v4 5/8] hw/intc: GICv3 ITS Feature enablement Shashi Mallela
@ 2021-06-08 10:57   ` Peter Maydell
  0 siblings, 0 replies; 52+ messages in thread
From: Peter Maydell @ 2021-06-08 10:57 UTC (permalink / raw)
  To: Shashi Mallela
  Cc: Leif Lindholm, QEMU Developers, qemu-arm, Radoslaw Biernacki

On Wed, 2 Jun 2021 at 19:00, 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         | 12 ++++++++++++
>  hw/intc/arm_gicv3_dist.c           |  7 +++++--
>  hw/intc/arm_gicv3_its.c            |  9 ++++++++-
>  hw/intc/arm_gicv3_redist.c         | 14 +++++++++++---
>  hw/intc/gicv3_internal.h           | 17 +++++++++++++++++
>  include/hw/intc/arm_gicv3_common.h |  1 +
>  6 files changed, 54 insertions(+), 6 deletions(-)


> @@ -386,7 +388,8 @@ static MemTxResult gicd_readl(GICv3State *s, hwaddr offset,
>          bool sec_extn = !(s->gicd_ctlr & GICD_CTLR_DS);
>
>          *data = (1 << 25) | (1 << 24) | (sec_extn << 10) |
> -            (0xf << 19) | itlinesnumber;
> +            (s->lpi_enable << GICD_TYPER_LPIS_OFFSET) |
> +            (GICD_TYPER_IDBITS << GICD_TYPER_IDBITS_OFFSET) | itlinesnumber;
>          return MEMTX_OK;
>      }
>      case GICD_IIDR:

This change is doing two things at once:
(1) setting the LPI enable bit
(2) changing from (0xf << 19) to something using symbolic constants.

If you want to do (2) as a cleanup I don't object, but please put
it in its own patch as it is unrelated to this one.

> diff --git a/hw/intc/arm_gicv3_its.c b/hw/intc/arm_gicv3_its.c
> index 82bb5b84ef..0a978cf55b 100644
> --- a/hw/intc/arm_gicv3_its.c
> +++ b/hw/intc/arm_gicv3_its.c
> @@ -294,6 +294,7 @@ static MemTxResult process_mapti(GICv3ITSState *s, uint64_t value,
>      uint64_t itel = 0;
>      uint32_t iteh = 0;
>      uint32_t int_spurious = INTID_SPURIOUS;
> +    uint64_t idbits;
>
>      devid = (value >> DEVID_SHIFT) & DEVID_MASK;
>      offset += NUM_BYTES_IN_DW;
> @@ -330,7 +331,13 @@ static MemTxResult process_mapti(GICv3ITSState *s, uint64_t value,
>      max_eventid = (1UL << (((dte >> 1U) & SIZE_MASK) + 1));
>
>      if (!ignore_pInt) {
> -        max_Intid = (1UL << (FIELD_EX64(s->typer, GITS_TYPER, IDBITS) + 1));
> +        idbits = MIN(FIELD_EX64(s->gicv3->cpu->gicr_propbaser, GICR_PROPBASER,
> +                                IDBITS), GICD_TYPER_IDBITS);
> +
> +        if (idbits < GICR_PROPBASER_IDBITS_THRESHOLD) {
> +            return res;
> +        }
> +        max_Intid = (1ULL << (idbits + 1));
>      }
>

This change should be folded into the patch where you add
this process_mapti() code, so it is correct from the start.

>      if ((devid > s->dt.max_devids) || (icid > s->ct.max_collids) ||
> diff --git a/hw/intc/arm_gicv3_redist.c b/hw/intc/arm_gicv3_redist.c
> index 8645220d61..fb9a4ee3cc 100644
> --- a/hw/intc/arm_gicv3_redist.c
> +++ b/hw/intc/arm_gicv3_redist.c
> @@ -244,14 +244,21 @@ static MemTxResult gicr_readl(GICv3CPUState *cs, hwaddr offset,
>  static MemTxResult gicr_writel(GICv3CPUState *cs, hwaddr offset,
>                                 uint64_t value, MemTxAttrs attrs)
>  {
> +

Stray new blank line.

>      switch (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 */
> @@ -395,6 +402,7 @@ static MemTxResult gicr_readll(GICv3CPUState *cs, hwaddr offset,
>  static MemTxResult gicr_writell(GICv3CPUState *cs, hwaddr offset,
>                                  uint64_t value, MemTxAttrs attrs)
>  {
> +
>      switch (offset) {
>      case GICR_PROPBASER:
>          cs->gicr_propbaser = value;

Another stray new blank line.

thanks
-- PMM


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

* Re: [PATCH v4 8/8] hw/arm/virt: add ITS support in virt GIC
  2021-06-02 18:00 ` [PATCH v4 8/8] hw/arm/virt: add ITS support in virt GIC Shashi Mallela
@ 2021-06-08 11:00   ` Peter Maydell
  0 siblings, 0 replies; 52+ messages in thread
From: Peter Maydell @ 2021-06-08 11:00 UTC (permalink / raw)
  To: Shashi Mallela
  Cc: Leif Lindholm, QEMU Developers, qemu-arm, Radoslaw Biernacki

On Wed, 2 Jun 2021 at 19:00, 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


Still missing space after '.'.

> ITS and is enabled in absence of kvm irq kernel support in a
> platform.
>
> Signed-off-by: Shashi Mallela <shashi.mallela@linaro.org>
> ---

I gave you a reviewed-by tag on this patch in v3; please don't
drop reviewed-by tags unless you make changes to a patch,
they help reviewers know which parts of the series they don't
need to look at again.

For the record,

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


thanks
-- PMM


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

* Re: [PATCH v4 6/8] hw/intc: GICv3 redistributor ITS processing
  2021-06-02 18:00 ` [PATCH v4 6/8] hw/intc: GICv3 redistributor ITS processing Shashi Mallela
@ 2021-06-08 13:57   ` Peter Maydell
  2021-06-10 23:39     ` Shashi Mallela
  2021-06-13 16:26   ` Eric Auger
  1 sibling, 1 reply; 52+ messages in thread
From: Peter Maydell @ 2021-06-08 13:57 UTC (permalink / raw)
  To: Shashi Mallela
  Cc: Leif Lindholm, QEMU Developers, qemu-arm, Radoslaw Biernacki

On Wed, 2 Jun 2021 at 19:00, 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.
>
> Signed-off-by: Shashi Mallela <shashi.mallela@linaro.org>
> ---
>  hw/intc/arm_gicv3.c                |   9 ++
>  hw/intc/arm_gicv3_common.c         |   1 +
>  hw/intc/arm_gicv3_cpuif.c          |   7 +-
>  hw/intc/arm_gicv3_its.c            |  14 ++-
>  hw/intc/arm_gicv3_redist.c         | 145 +++++++++++++++++++++++++++++
>  hw/intc/gicv3_internal.h           |  10 ++
>  include/hw/intc/arm_gicv3_common.h |  10 ++
>  7 files changed, 190 insertions(+), 6 deletions(-)

The code for finding/updating the best pending LPI looks a lot
better in this version -- thanks for working through that.

An important thing which I hadn't realized previously:
the hpplpi information counts as information cached from the
LPI configuration tables (because it is based on the priority
and enable-bit information from those tables). That means that when
the guest sends the ITS INV or INVALL command we need to throw it
away and recalculate by calling gicv3_redist_update_lpi().
(The idea here is that the guest can validly raise the priority
of an interrupt by the sequence "write to table; INVALL; SYNC",
and we need to correctly figure out that that might mean that
that LPI is now the interrupt we should be taking.)

> diff --git a/hw/intc/arm_gicv3.c b/hw/intc/arm_gicv3.c
> index d63f8af604..4d19190b9c 100644
> --- a/hw/intc/arm_gicv3.c
> +++ b/hw/intc/arm_gicv3.c
> @@ -165,6 +165,15 @@ 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 && cs->lpivalid) {

You don't need a separate lpivalid flag -- you can use
hpplpi.prio == 0xff as your "no pending LPI" indication.
This is how the existing cs->hppi works.
(irqbetter() will always return false if passed an 0xff priority,
so you don't need to special case check anything here.)

> +        if (irqbetter(cs, cs->hpplpi.irq, cs->hpplpi.prio)) {
> +            cs->hppi.irq = cs->hpplpi.irq;
> +            cs->hppi.prio = cs->hpplpi.prio;
> +            cs->hppi.grp = cs->hpplpi.grp;
> +            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_common.c b/hw/intc/arm_gicv3_common.c
> index 53dea2a775..223db16fec 100644
> --- a/hw/intc/arm_gicv3_common.c
> +++ b/hw/intc/arm_gicv3_common.c
> @@ -435,6 +435,7 @@ static void arm_gicv3_common_reset(DeviceState *dev)
>          memset(cs->gicr_ipriorityr, 0, sizeof(cs->gicr_ipriorityr));
>
>          cs->hppi.prio = 0xff;
> +        cs->hpplpi.prio = 0xff;
>
>          /* State in the CPU interface must *not* be reset here, because it
>           * is part of the CPU's reset domain, not the GIC device's.
> diff --git a/hw/intc/arm_gicv3_cpuif.c b/hw/intc/arm_gicv3_cpuif.c
> index 81f94c7f4a..5be3efaa3f 100644
> --- a/hw/intc/arm_gicv3_cpuif.c
> +++ b/hw/intc/arm_gicv3_cpuif.c
> @@ -898,10 +898,12 @@ static void icc_activate_irq(GICv3CPUState *cs, int irq)
>          cs->gicr_iactiver0 = deposit32(cs->gicr_iactiver0, irq, 1, 1);
>          cs->gicr_ipendr0 = deposit32(cs->gicr_ipendr0, irq, 1, 0);
>          gicv3_redist_update(cs);
> -    } else {
> +    } else if (irq < GICV3_LPI_INTID_START) {
>          gicv3_gicd_active_set(cs->gic, irq);
>          gicv3_gicd_pending_clear(cs->gic, irq);
>          gicv3_update(cs->gic, irq, 1);
> +    } else {
> +        gicv3_redist_lpi_pending(cs, irq, 0);
>      }
>  }
>
> @@ -1317,7 +1319,8 @@ static void icc_eoir_write(CPUARMState *env, const ARMCPRegInfo *ri,
>      trace_gicv3_icc_eoir_write(is_eoir0 ? 0 : 1,
>                                 gicv3_redist_affid(cs), value);
>
> -    if (irq >= cs->gic->num_irq) {
> +    if ((irq >= cs->gic->num_irq) &&  (!(cs->gic->lpi_enable &&
> +        (irq >= GICV3_LPI_INTID_START)))) {

Please put the line break after the first &&, not the second. That means
that you avoid linebreaking in the middle of a () expression.
Also you don't need the () on the outside of the !.

>          /* This handles two cases:
>           * 1. If software writes the ID of a spurious interrupt [ie 1020-1023]
>           * to the GICC_EOIR, the GIC ignores that write.
> diff --git a/hw/intc/arm_gicv3_its.c b/hw/intc/arm_gicv3_its.c
> index 0a978cf55b..e0fbd4041f 100644
> --- a/hw/intc/arm_gicv3_its.c
> +++ b/hw/intc/arm_gicv3_its.c
> @@ -211,6 +211,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;
>      uint64_t itel = 0;
>      uint32_t iteh = 0;
>
> @@ -267,10 +268,15 @@ static MemTxResult process_int(GICv3ITSState *s, uint64_t value,
>           * command in the queue
>           */
>      } else {
> -        /*
> -         * Current implementation only supports rdbase == procnum
> -         * Hence rdbase physical address is ignored
> -         */
> +        rdbase = (cte >> 1U) & RDBASE_PROCNUM_MASK;
> +        assert(rdbase <= s->gicv3->num_cpu);

We just read cte from guest memory. We mustn't allow guests to
trigger assert()s in QEMU, so if the value is out of range then
we need to handle it by treating the command as invalid, not by crashing.

Also, your bounds-check is off by one; it should be "<", not "<=".

> +
> +        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 */
>              res = update_ite(s, eventid, dte, itel, iteh);
> diff --git a/hw/intc/arm_gicv3_redist.c b/hw/intc/arm_gicv3_redist.c
> index fb9a4ee3cc..bfc6e4e9b9 100644
> --- a/hw/intc/arm_gicv3_redist.c
> +++ b/hw/intc/arm_gicv3_redist.c
> @@ -255,6 +255,11 @@ 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 */
> +                cs->lpivalid = false;
> +                cs->hpplpi.prio = 0xff;
> +                gicv3_redist_update_lpi(cs);
> +                gicv3_redist_update(cs);
>              } else {
>                  cs->gicr_ctlr &= ~GICR_CTLR_ENABLE_LPIS;
>              }
> @@ -534,6 +539,146 @@ MemTxResult gicv3_redist_write(void *opaque, hwaddr offset, uint64_t data,
>      return r;
>  }
>
> +void gicv3_redist_update_lpi(GICv3CPUState *cs)
> +{
> +    /*
> +     * This function scans the LPI pending table and for each pending
> +     * LPI, reads the corresponding entry from LPI configuration table
> +     * to extract the priority info and determine if the LPI priority
> +     * is lower than the current high priority interrupt.If yes, update

Missing space after ".".

> +     * high priority pending interrupt to that of LPI.
> +     */
> +    AddressSpace *as = &cs->gic->dma_as;
> +    uint64_t lpict_baddr, lpipt_baddr;
> +    uint32_t pendt_size = 0;
> +    uint8_t lpite;
> +    uint8_t prio, pend;
> +    int i;
> +    uint64_t idbits;

You should set hpplpi.prio = 0xff; here, so you don't need to do
it at every callsite.

That is, what you're really doing in this function is "recalculate the
hpplpi information from scratch".

> +
> +    idbits = MIN(FIELD_EX64(cs->gicr_propbaser, GICR_PROPBASER, IDBITS),
> +                 GICD_TYPER_IDBITS);
> +
> +    if ((!cs->gicr_ctlr & GICR_CTLR_ENABLE_LPIS) || !cs->gicr_propbaser ||

This is the set of missing brackets that clang complains about: it should
be "!(cs->gicr_ctlr & GICR_CTLR_ENABLE_LPIS)" because ! has higher priority
than &.

> +        !cs->gicr_pendbaser || (idbits < GICR_PROPBASER_IDBITS_THRESHOLD)) {
> +        return;
> +    }
> +
> +    lpict_baddr = cs->gicr_propbaser & R_GICR_PROPBASER_PHYADDR_MASK;
> +
> +    lpipt_baddr = cs->gicr_pendbaser & R_GICR_PENDBASER_PHYADDR_MASK;
> +
> +    /* Determine the highest priority pending interrupt among LPIs */
> +    pendt_size = (1ULL << (idbits + 1));
> +
> +    for (i = 0; i < pendt_size / 8; 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) {

Better written as "if (the pend bit is not set) continue;"

> +            address_space_read(as, lpict_baddr + (i * sizeof(lpite)),
> +                      MEMTXATTRS_UNSPECIFIED, &lpite, sizeof(lpite));
> +
> +            if (!(lpite & LPI_CTE_ENABLED)) {
> +                continue;
> +            }
> +
> +            if (cs->gic->gicd_ctlr & GICD_CTLR_DS) {
> +                prio = lpite & LPI_PRIORITY_MASK;
> +            } else {
> +                prio = lpite & LPI_SPRIORITY_MASK;

This isn't the right calculation. When reading a priority value
when GICD_CTLR.DS is zero, you need to shift it right by one
and set bit 7:
    prio = ((lpite & LPI_PRIORITY_MASK) >> 1) & 0x80;

> +            }
> +
> +            if (prio <= cs->hpplpi.prio) {
> +                cs->hpplpi.irq = GICV3_LPI_INTID_START + i;
> +                cs->hpplpi.prio = prio;
> +                /* LPIs are always non-secure Grp1 interrupts */
> +                cs->hpplpi.grp = GICV3_G1NS;
> +                cs->lpivalid = true;
> +            }
> +        }
> +    }
> +}
> +
> +void gicv3_redist_lpi_pending(GICv3CPUState *cs, int irq, int level)
> +{
> +    AddressSpace *as = &cs->gic->dma_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 = cs->gicr_pendbaser & R_GICR_PENDBASER_PHYADDR_MASK;
> +
> +    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));
> +        }
> +    }

You can simplify this code a bit:

    address_space_read(as, lpipt_baddr + ((irq / 8) * sizeof(pend)),
                       MEMTXATTRS_UNSPECIFIED, &pend, sizeof(pend));
    ispend = extract32(pend, irq % 8, 1);
    if (ispend == level) {
        return;
    }
    pend = deposit32(pend, irq % 8, 1, level ? 1 : 0);
    address_space_write(as, lpipt_baddr + ((irq / 8) * sizeof(pend)),
                        MEMTXATTRS_UNSPECIFIED, &pend, sizeof(pend));


> +    cs->lpivalid = false;
> +    cs->hpplpi.prio = 0xff;
> +    gicv3_redist_update_lpi(cs);

You can avoid doing a full update a lot of the time:
 * if this LPI is worse than the current value in hpplpi
   (where by "worse" I mean lower-priority by the same kind of
   comparison irqbetter() does) then we haven't changed the best-available
   pending LPI, so we don't need to do an update
 * if we set the pending bit to 1 and the LPI is enabled and the priority
   of this LPI is better than the current hpplpi, then we know this LPI
   is now the best, so we can just set hpplpi.prio and .irq without
   doing a full rescan
 * if we didn't actually change the value of the pending bit, we
   don't need to do an update (you get this for free if you take the
   simplification suggestion I make above, which does an early-return
   in the "no change" case)

> +}
> +
> +void gicv3_redist_process_lpi(GICv3CPUState *cs, int irq, int level)
> +{
> +    AddressSpace *as = &cs->gic->dma_as;
> +    uint64_t lpict_baddr;
> +    uint8_t lpite;
> +    uint64_t idbits;
> +
> +    idbits = MIN(FIELD_EX64(cs->gicr_propbaser, GICR_PROPBASER, IDBITS),
> +                 GICD_TYPER_IDBITS);
> +
> +    if ((!cs->gicr_ctlr & GICR_CTLR_ENABLE_LPIS) || !cs->gicr_propbaser ||

This is the other set of missing brackets that clang complains about.

> +         !cs->gicr_pendbaser || (idbits < GICR_PROPBASER_IDBITS_THRESHOLD) ||
> +         (irq > (1ULL << (idbits + 1)))) {
> +        return;
> +    }
> +
> +    lpict_baddr = cs->gicr_propbaser & R_GICR_PROPBASER_PHYADDR_MASK;
> +
> +    /* 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;
> +    }

I don't think you need to make this check -- you can just set/clear
the pending status of the LPI. If the LPI is not enabled then it will
be ignored by gicv3_redist_update_lpi(). This is how non-LPI interrupts
work and I think that LPIs behave the same way. (But it's a big spec,
so I might have missed something -- if I'm wrong, please say so.)

> +
> +    /* 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 91dbe01176..bcbccba573 100644
> --- a/hw/intc/gicv3_internal.h
> +++ b/hw/intc/gicv3_internal.h
> @@ -308,6 +308,13 @@ 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 LPI_SPRIORITY_MASK        0x7e
> +
>  #define GITS_CMDQ_ENTRY_SIZE               32
>  #define NUM_BYTES_IN_DW                     8
>
> @@ -452,6 +459,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);
> +void 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);
>
> diff --git a/include/hw/intc/arm_gicv3_common.h b/include/hw/intc/arm_gicv3_common.h
> index c1348cc60a..5d839da9c9 100644
> --- a/include/hw/intc/arm_gicv3_common.h
> +++ b/include/hw/intc/arm_gicv3_common.h
> @@ -204,6 +204,16 @@ struct GICv3CPUState {
>       * real state above; it doesn't need to be migrated.
>       */
>      PendingIrq hppi;
> +
> +    /*
> +     * Current highest priority pending lpi for this CPU.
> +     * This is cached information that can be recalculated from the
> +     * real state above; it doesn't need to be migrated.

This comment is true for hppi, but not for hpplpi. For hpplpi
it is "cached information that can be recalculated from the LPI
tables in guest memory".

This means that we need either to:
 (1) call gicv3_redist_update_lpi() in an appropriate post-load function
so that the field gets re-calculated on the destination end of a migration
 (2) migrate the hpplpi fields

Option 1 is what we do for hppi: arm_gicv3_post_load() calls
gicv3_full_update_noirqset(), which does a full recalculation of the
GIC state. Calling gicv3_redist_update_lpi() in arm_gicv3_post_load()
before it calls gicv3_full_update_noirqset() is probably the best thing.

> +     */
> +    PendingIrq hpplpi;
> +
> +    bool lpivalid; /* current highest priority lpi validity status */
> +
>      /* This is temporary working state, to avoid a malloc in gicv3_update() */
>      bool seenbetter;
>  };
> --
> 2.27.0
>

thanks
-- PMM


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

* Re: [PATCH v4 6/8] hw/intc: GICv3 redistributor ITS processing
  2021-06-08 13:57   ` Peter Maydell
@ 2021-06-10 23:39     ` Shashi Mallela
  2021-06-11  8:30       ` Peter Maydell
  0 siblings, 1 reply; 52+ messages in thread
From: Shashi Mallela @ 2021-06-10 23:39 UTC (permalink / raw)
  To: Peter Maydell
  Cc: Leif Lindholm, QEMU Developers, qemu-arm, Radoslaw Biernacki

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

Have addressed all comments except the ones with responses(inline) below:-

On Jun 8 2021, at 9:57 am, Peter Maydell <peter.maydell@linaro.org> wrote:
> On Wed, 2 Jun 2021 at 19:00, 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.
> >
> > Signed-off-by: Shashi Mallela <shashi.mallela@linaro.org>
> > ---
> > hw/intc/arm_gicv3.c | 9 ++
> > hw/intc/arm_gicv3_common.c | 1 +
> > hw/intc/arm_gicv3_cpuif.c | 7 +-
> > hw/intc/arm_gicv3_its.c | 14 ++-
> > hw/intc/arm_gicv3_redist.c | 145 +++++++++++++++++++++++++++++
> > hw/intc/gicv3_internal.h | 10 ++
> > include/hw/intc/arm_gicv3_common.h | 10 ++
> > 7 files changed, 190 insertions(+), 6 deletions(-)
>
> The code for finding/updating the best pending LPI looks a lot
> better in this version -- thanks for working through that.
>
> An important thing which I hadn't realized previously:
> the hpplpi information counts as information cached from the
> LPI configuration tables (because it is based on the priority
> and enable-bit information from those tables). That means that when
> the guest sends the ITS INV or INVALL command we need to throw it
> away and recalculate by calling gicv3_redist_update_lpi().
> (The idea here is that the guest can validly raise the priority
> of an interrupt by the sequence "write to table; INVALL; SYNC",
> and we need to correctly figure out that that might mean that
> that LPI is now the interrupt we should be taking.)
>

> Agreed,will be implementing the INV/INVALL command processing in addition to existing ITS commands

> diff --git a/hw/intc/arm_gicv3.c b/hw/intc/arm_gicv3.c
> index d63f8af604..4d19190b9c 100644
> --- a/hw/intc/arm_gicv3.c
> +++ b/hw/intc/arm_gicv3.c
> @@ -165,6 +165,15 @@ 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 && cs->lpivalid) {

You don't need a separate lpivalid flag -- you can use
hpplpi.prio == 0xff as your "no pending LPI" indication.
This is how the existing cs->hppi works.
(irqbetter() will always return false if passed an 0xff priority,
so you don't need to special case check anything here.)

> + if (irqbetter(cs, cs->hpplpi.irq, cs->hpplpi.prio)) {
> + cs->hppi.irq = cs->hpplpi.irq;
> + cs->hppi.prio = cs->hpplpi.prio;
> + cs->hppi.grp = cs->hpplpi.grp;
> + 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_common.c b/hw/intc/arm_gicv3_common.c
> index 53dea2a775..223db16fec 100644
> --- a/hw/intc/arm_gicv3_common.c
> +++ b/hw/intc/arm_gicv3_common.c
> @@ -435,6 +435,7 @@ static void arm_gicv3_common_reset(DeviceState *dev)
> memset(cs->gicr_ipriorityr, 0, sizeof(cs->gicr_ipriorityr));
>
> cs->hppi.prio = 0xff;
> + cs->hpplpi.prio = 0xff;
>
> /* State in the CPU interface must *not* be reset here, because it
> * is part of the CPU's reset domain, not the GIC device's.
> diff --git a/hw/intc/arm_gicv3_cpuif.c b/hw/intc/arm_gicv3_cpuif.c
> index 81f94c7f4a..5be3efaa3f 100644
> --- a/hw/intc/arm_gicv3_cpuif.c
> +++ b/hw/intc/arm_gicv3_cpuif.c
> @@ -898,10 +898,12 @@ static void icc_activate_irq(GICv3CPUState *cs, int irq)
> cs->gicr_iactiver0 = deposit32(cs->gicr_iactiver0, irq, 1, 1);
> cs->gicr_ipendr0 = deposit32(cs->gicr_ipendr0, irq, 1, 0);
> gicv3_redist_update(cs);
> - } else {
> + } else if (irq < GICV3_LPI_INTID_START) {
> gicv3_gicd_active_set(cs->gic, irq);
> gicv3_gicd_pending_clear(cs->gic, irq);
> gicv3_update(cs->gic, irq, 1);
> + } else {
> + gicv3_redist_lpi_pending(cs, irq, 0);
> }
> }
>
> @@ -1317,7 +1319,8 @@ static void icc_eoir_write(CPUARMState *env, const ARMCPRegInfo *ri,
> trace_gicv3_icc_eoir_write(is_eoir0 ? 0 : 1,
> gicv3_redist_affid(cs), value);
>
> - if (irq >= cs->gic->num_irq) {
> + if ((irq >= cs->gic->num_irq) && (!(cs->gic->lpi_enable &&
> + (irq >= GICV3_LPI_INTID_START)))) {

Please put the line break after the first &&, not the second. That means
that you avoid linebreaking in the middle of a () expression.
Also you don't need the () on the outside of the !.

> /* This handles two cases:
> * 1. If software writes the ID of a spurious interrupt [ie 1020-1023]
> * to the GICC_EOIR, the GIC ignores that write.
> diff --git a/hw/intc/arm_gicv3_its.c b/hw/intc/arm_gicv3_its.c
> index 0a978cf55b..e0fbd4041f 100644
> --- a/hw/intc/arm_gicv3_its.c
> +++ b/hw/intc/arm_gicv3_its.c
> @@ -211,6 +211,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;
> uint64_t itel = 0;
> uint32_t iteh = 0;
>
> @@ -267,10 +268,15 @@ static MemTxResult process_int(GICv3ITSState *s, uint64_t value,
> * command in the queue
> */
> } else {
> - /*
> - * Current implementation only supports rdbase == procnum
> - * Hence rdbase physical address is ignored
> - */
> + rdbase = (cte >> 1U) & RDBASE_PROCNUM_MASK;
> + assert(rdbase <= s->gicv3->num_cpu);

We just read cte from guest memory. We mustn't allow guests to
trigger assert()s in QEMU, so if the value is out of range then
we need to handle it by treating the command as invalid, not by crashing.

Also, your bounds-check is off by one; it should be "<", not "<=".
> +
> + 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 */
> res = update_ite(s, eventid, dte, itel, iteh);
> diff --git a/hw/intc/arm_gicv3_redist.c b/hw/intc/arm_gicv3_redist.c
> index fb9a4ee3cc..bfc6e4e9b9 100644
> --- a/hw/intc/arm_gicv3_redist.c
> +++ b/hw/intc/arm_gicv3_redist.c
> @@ -255,6 +255,11 @@ 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 */
> + cs->lpivalid = false;
> + cs->hpplpi.prio = 0xff;
> + gicv3_redist_update_lpi(cs);
> + gicv3_redist_update(cs);
> } else {
> cs->gicr_ctlr &= ~GICR_CTLR_ENABLE_LPIS;
> }
> @@ -534,6 +539,146 @@ MemTxResult gicv3_redist_write(void *opaque, hwaddr offset, uint64_t data,
> return r;
> }
>
> +void gicv3_redist_update_lpi(GICv3CPUState *cs)
> +{
> + /*
> + * This function scans the LPI pending table and for each pending
> + * LPI, reads the corresponding entry from LPI configuration table
> + * to extract the priority info and determine if the LPI priority
> + * is lower than the current high priority interrupt.If yes, update

Missing space after ".".
> + * high priority pending interrupt to that of LPI.
> + */
> + AddressSpace *as = &cs->gic->dma_as;
> + uint64_t lpict_baddr, lpipt_baddr;
> + uint32_t pendt_size = 0;
> + uint8_t lpite;
> + uint8_t prio, pend;
> + int i;
> + uint64_t idbits;

You should set hpplpi.prio = 0xff; here, so you don't need to do
it at every callsite.

That is, what you're really doing in this function is "recalculate the
hpplpi information from scratch".

> +
> + idbits = MIN(FIELD_EX64(cs->gicr_propbaser, GICR_PROPBASER, IDBITS),
> + GICD_TYPER_IDBITS);
> +
> + if ((!cs->gicr_ctlr & GICR_CTLR_ENABLE_LPIS) || !cs->gicr_propbaser ||

This is the set of missing brackets that clang complains about: it should
be "!(cs->gicr_ctlr & GICR_CTLR_ENABLE_LPIS)" because ! has higher priority
than &.

> + !cs->gicr_pendbaser || (idbits < GICR_PROPBASER_IDBITS_THRESHOLD)) {
> + return;
> + }
> +
> + lpict_baddr = cs->gicr_propbaser & R_GICR_PROPBASER_PHYADDR_MASK;
> +
> + lpipt_baddr = cs->gicr_pendbaser & R_GICR_PENDBASER_PHYADDR_MASK;
> +
> + /* Determine the highest priority pending interrupt among LPIs */
> + pendt_size = (1ULL << (idbits + 1));
> +
> + for (i = 0; i < pendt_size / 8; 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) {

Better written as "if (the pend bit is not set) continue;"
> + address_space_read(as, lpict_baddr + (i * sizeof(lpite)),
> + MEMTXATTRS_UNSPECIFIED, &lpite, sizeof(lpite));
> +
> + if (!(lpite & LPI_CTE_ENABLED)) {
> + continue;
> + }
> +
> + if (cs->gic->gicd_ctlr & GICD_CTLR_DS) {
> + prio = lpite & LPI_PRIORITY_MASK;
> + } else {
> + prio = lpite & LPI_SPRIORITY_MASK;

This isn't the right calculation. When reading a priority value
when GICD_CTLR.DS is zero, you need to shift it right by one
and set bit 7:
prio = ((lpite & LPI_PRIORITY_MASK) >> 1) & 0x80;

> + }
> +
> + if (prio <= cs->hpplpi.prio) {
> + cs->hpplpi.irq = GICV3_LPI_INTID_START + i;
> + cs->hpplpi.prio = prio;
> + /* LPIs are always non-secure Grp1 interrupts */
> + cs->hpplpi.grp = GICV3_G1NS;
> + cs->lpivalid = true;
> + }
> + }
> + }
> +}
> +
> +void gicv3_redist_lpi_pending(GICv3CPUState *cs, int irq, int level)
> +{
> + AddressSpace *as = &cs->gic->dma_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 = cs->gicr_pendbaser & R_GICR_PENDBASER_PHYADDR_MASK;
> +
> + 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));
> + }
> + }

You can simplify this code a bit:
address_space_read(as, lpipt_baddr + ((irq / 8) * sizeof(pend)),
MEMTXATTRS_UNSPECIFIED, &pend, sizeof(pend));
ispend = extract32(pend, irq % 8, 1);
if (ispend == level) {
return;
}
pend = deposit32(pend, irq % 8, 1, level ? 1 : 0);
address_space_write(as, lpipt_baddr + ((irq / 8) * sizeof(pend)),
MEMTXATTRS_UNSPECIFIED, &pend, sizeof(pend));

> + cs->lpivalid = false;
> + cs->hpplpi.prio = 0xff;
> + gicv3_redist_update_lpi(cs);

You can avoid doing a full update a lot of the time:
* if this LPI is worse than the current value in hpplpi
(where by "worse" I mean lower-priority by the same kind of
comparison irqbetter() does) then we haven't changed the best-available
pending LPI, so we don't need to do an update
* if we set the pending bit to 1 and the LPI is enabled and the priority
of this LPI is better than the current hpplpi, then we know this LPI
is now the best, so we can just set hpplpi.prio and .irq without
doing a full rescan
* if we didn't actually change the value of the pending bit, we
don't need to do an update (you get this for free if you take the
simplification suggestion I make above, which does an early-return
in the "no change" case)

> Accepted the code simplification,but by not calling gicv3_redist_update_lpi(cs) here ,the scenario of an activated LPI fails because
this LPI's priority (which could be lower than current hpplpi) is never checked/updated and gicv3_redist_update_noirqset() fails to present a valid active high priority LPI(if applicable) to the cpu,since it is always checking against a stale hpplpi info.
Have confirmed this with the kvm-unit-tests as well,wherein the LPIs are never processed and test cases fail.

> +}
> +
> +void gicv3_redist_process_lpi(GICv3CPUState *cs, int irq, int level)
> +{
> + AddressSpace *as = &cs->gic->dma_as;
> + uint64_t lpict_baddr;
> + uint8_t lpite;
> + uint64_t idbits;
> +
> + idbits = MIN(FIELD_EX64(cs->gicr_propbaser, GICR_PROPBASER, IDBITS),
> + GICD_TYPER_IDBITS);
> +
> + if ((!cs->gicr_ctlr & GICR_CTLR_ENABLE_LPIS) || !cs->gicr_propbaser ||

This is the other set of missing brackets that clang complains about.
> + !cs->gicr_pendbaser || (idbits < GICR_PROPBASER_IDBITS_THRESHOLD) ||
> + (irq > (1ULL << (idbits + 1)))) {
> + return;
> + }
> +
> + lpict_baddr = cs->gicr_propbaser & R_GICR_PROPBASER_PHYADDR_MASK;
> +
> + /* 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;
> + }

I don't think you need to make this check -- you can just set/clear
the pending status of the LPI. If the LPI is not enabled then it will
be ignored by gicv3_redist_update_lpi(). This is how non-LPI interrupts
work and I think that LPIs behave the same way. (But it's a big spec,
so I might have missed something -- if I'm wrong, please say so.)

> +
> + /* 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 91dbe01176..bcbccba573 100644
> --- a/hw/intc/gicv3_internal.h
> +++ b/hw/intc/gicv3_internal.h
> @@ -308,6 +308,13 @@ 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 LPI_SPRIORITY_MASK 0x7e
> +
> #define GITS_CMDQ_ENTRY_SIZE 32
> #define NUM_BYTES_IN_DW 8
>
> @@ -452,6 +459,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);
> +void 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);
>
> diff --git a/include/hw/intc/arm_gicv3_common.h b/include/hw/intc/arm_gicv3_common.h
> index c1348cc60a..5d839da9c9 100644
> --- a/include/hw/intc/arm_gicv3_common.h
> +++ b/include/hw/intc/arm_gicv3_common.h
> @@ -204,6 +204,16 @@ struct GICv3CPUState {
> * real state above; it doesn't need to be migrated.
> */
> PendingIrq hppi;
> +
> + /*
> + * Current highest priority pending lpi for this CPU.
> + * This is cached information that can be recalculated from the
> + * real state above; it doesn't need to be migrated.

This comment is true for hppi, but not for hpplpi. For hpplpi
it is "cached information that can be recalculated from the LPI
tables in guest memory".

This means that we need either to:
(1) call gicv3_redist_update_lpi() in an appropriate post-load function
so that the field gets re-calculated on the destination end of a migration
(2) migrate the hpplpi fields

Option 1 is what we do for hppi: arm_gicv3_post_load() calls
gicv3_full_update_noirqset(), which does a full recalculation of the
GIC state. Calling gicv3_redist_update_lpi() in arm_gicv3_post_load()
before it calls gicv3_full_update_noirqset() is probably the best thing.

> + */
> + PendingIrq hpplpi;
> +
> + bool lpivalid; /* current highest priority lpi validity status */
> +
> /* This is temporary working state, to avoid a malloc in gicv3_update() */
> bool seenbetter;
> };
> --
> 2.27.0
>

thanks
-- PMM


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

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

* Re: [PATCH v4 6/8] hw/intc: GICv3 redistributor ITS processing
  2021-06-10 23:39     ` Shashi Mallela
@ 2021-06-11  8:30       ` Peter Maydell
  2021-06-15  2:23         ` Shashi Mallela
  0 siblings, 1 reply; 52+ messages in thread
From: Peter Maydell @ 2021-06-11  8:30 UTC (permalink / raw)
  To: Shashi Mallela
  Cc: Leif Lindholm, QEMU Developers, qemu-arm, Radoslaw Biernacki

On Fri, 11 Jun 2021 at 00:39, Shashi Mallela <shashi.mallela@linaro.org> wrote:
>
> Have addressed all comments except the ones with responses(inline) below:-
>
> On Jun 8 2021, at 9:57 am, Peter Maydell <peter.maydell@linaro.org> wrote:
>
> > + cs->lpivalid = false;
> > + cs->hpplpi.prio = 0xff;
> > + gicv3_redist_update_lpi(cs);
>
> You can avoid doing a full update a lot of the time:
> * if this LPI is worse than the current value in hpplpi
> (where by "worse" I mean lower-priority by the same kind of
> comparison irqbetter() does) then we haven't changed the best-available
> pending LPI, so we don't need to do an update
> * if we set the pending bit to 1 and the LPI is enabled and the priority
> of this LPI is better than the current hpplpi, then we know this LPI
> is now the best, so we can just set hpplpi.prio and .irq without
> doing a full rescan
> * if we didn't actually change the value of the pending bit, we
> don't need to do an update (you get this for free if you take the
> simplification suggestion I make above, which does an early-return
> in the "no change" case)
>
> > Accepted the code simplification,but by not calling gicv3_redist_update_lpi(cs) here ,the scenario of an activated LPI fails because
> this LPI's priority (which could be lower than current hpplpi) is never checked/updated and gicv3_redist_update_noirqset() fails to present a valid active high priority LPI(if applicable) to the cpu,since it is always checking against a stale hpplpi info.

If the LPI is lower priority (higher number) than the current
hpplpi then it would not change the existing hpplpi info in
a full-scan. If the LPI being activated is higher priority
(lower number) than the current hpplpi then that is my point 2 above,
and we set it as the hpplpi without needing the full-scan. And for
the other cases (eg highest-priority LPI being deactivated) we
should fall through to the default "call update_lpi" case.

So I don't really understand why this wouldn't work.

-- PMM


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

* Re: [PATCH v4 1/8] hw/intc: GICv3 ITS initial framework
  2021-06-02 18:00 ` [PATCH v4 1/8] hw/intc: GICv3 ITS initial framework Shashi Mallela
  2021-06-08 10:02   ` Peter Maydell
@ 2021-06-11 16:21   ` Eric Auger
  2021-06-11 17:23     ` Shashi Mallela
  2021-07-06  7:38     ` Eric Auger
  2021-06-12  6:52   ` Eric Auger
  2 siblings, 2 replies; 52+ messages in thread
From: Eric Auger @ 2021-06-11 16:21 UTC (permalink / raw)
  To: Shashi Mallela, peter.maydell, leif, rad; +Cc: qemu-arm, qemu-devel

Hi,

On 6/2/21 8:00 PM, Shashi Mallela 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                | 240 +++++++++++++++++++++++++
>  hw/intc/arm_gicv3_its_common.c         |   8 +-
>  hw/intc/arm_gicv3_its_kvm.c            |   2 +-
>  hw/intc/gicv3_internal.h               |  88 +++++++--
>  hw/intc/meson.build                    |   1 +
>  include/hw/intc/arm_gicv3_its_common.h |   9 +-
>  6 files changed, 331 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..545cda3665
> --- /dev/null
> +++ b/hw/intc/arm_gicv3_its.c
> @@ -0,0 +1,240 @@
> +/*
> + * 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 gicv3_its_translation_write(void *opaque, hwaddr offset,
> +                                               uint64_t data, unsigned size,
> +                                               MemTxAttrs attrs)
> +{
> +    MemTxResult result = MEMTX_OK;
> +
> +    return result;
> +}
> +
> +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 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 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,
> +    .valid.min_access_size = 4,
> +    .valid.max_access_size = 8,
> +    .impl.min_access_size = 4,
> +    .impl.max_access_size = 8,
> +    .endianness = DEVICE_NATIVE_ENDIAN,
> +};
> +
> +static const MemoryRegionOps gicv3_its_translation_ops = {
> +    .write_with_attrs = gicv3_its_translation_write,
> +    .valid.min_access_size = 2,
> +    .valid.max_access_size = 4,
> +    .impl.min_access_size = 2,
> +    .impl.max_access_size = 4,
> +    .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);
> +
> +    if (s->gicv3->cpu->gicr_typer & GICR_TYPER_PLPIS) {
> +        /* 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 - 1);
> +        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);
> +    }
> +}
> +
> +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);
> +
> +        /* 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 - 1);
> +
> +        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 - 1);
> +    }
> +}
> +
> +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..f1657c84e0 100644
> --- a/hw/intc/arm_gicv3_its_common.c
> +++ b/hw/intc/arm_gicv3_its_common.c
> @@ -50,6 +50,8 @@ 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,
> @@ -99,14 +101,15 @@ 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,
> +                          tops ? tops : &gicv3_its_trans_ops, s,
>                            "translation", ITS_TRANS_SIZE);
>  
>      /* Our two regions are always adjacent, therefore we now combine them
> @@ -129,7 +132,6 @@ static void gicv3_its_common_reset(DeviceState *dev)
>      s->cbaser = 0;
>      s->cwriter = 0;
>      s->creadr = 0;
> -    s->iidr = 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..e0b06930a7 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,78 @@
>  #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)
Isn't it FIELD(GITS_BASER, PHYADDRH_64K, 12, 4)
hum actually it is fixed in next patch ;-) The right value can be put
here directly
> +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)
> +
> +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)
> +
> +#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 may rename into GITS_BASER_TYPE_DEVICE and COLLECTION?
> +
> +/**
> + * Default features advertised by this version of ITS
> + */
> +/* Physical LPIs supported */
> +#define GITS_TYPE_PHYSICAL           (1U << 0)
> +
> +/*
> + * 12 bytes Interrupt translation Table Entry size
> + * ITE Lower 8 Bytes
> + * Valid = 1 bit,InterruptType = 1 bit,
> + * Size of LPI number space[considering max 24 bits],
> + * Size of LPI number space[considering max 24 bits],
repeated
> + * ITE Higher 4 Bytes
> + * ICID = 16 bits,
> + * vPEID = 16 bits

for info the ABI used by the kernel can be found in linux
Documentation/virt/kvm/devices/arm-vgic-its.rst

The ITE there is 8 bytes.

Have you considered the same?

> + */
> +#define ITS_ITT_ENTRY_SIZE            0xC
> +
> +/* 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 6e52a166e3..4dcfea6aa8 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..65d1191db1 100644
> --- a/include/hw/intc/arm_gicv3_its_common.h
> +++ b/include/hw/intc/arm_gicv3_its_common.h
> @@ -25,17 +25,22 @@
>  #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
> +
>  struct GICv3ITSState {
>      SysBusDevice parent_obj;
>  
> @@ -52,6 +57,7 @@ struct GICv3ITSState {
>      /* Registers */
>      uint32_t ctlr;
>      uint32_t iidr;
> +    uint64_t typer;
>      uint64_t cbaser;
>      uint64_t cwriter;
>      uint64_t creadr;
> @@ -62,7 +68,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;
> 
Thanks

Eric



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

* Re: [PATCH v4 1/8] hw/intc: GICv3 ITS initial framework
  2021-06-11 16:21   ` Eric Auger
@ 2021-06-11 17:23     ` Shashi Mallela
  2021-07-06  7:38     ` Eric Auger
  1 sibling, 0 replies; 52+ messages in thread
From: Shashi Mallela @ 2021-06-11 17:23 UTC (permalink / raw)
  To: Eric Auger; +Cc: peter.maydell, leif, qemu-devel, qemu-arm, rad

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



On Jun 11 2021, at 12:21 pm, Eric Auger <eauger@redhat.com> wrote:
> Hi,
>
> On 6/2/21 8:00 PM, Shashi Mallela 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 | 240 +++++++++++++++++++++++++
> > hw/intc/arm_gicv3_its_common.c | 8 +-
> > hw/intc/arm_gicv3_its_kvm.c | 2 +-
> > hw/intc/gicv3_internal.h | 88 +++++++--
> > hw/intc/meson.build | 1 +
> > include/hw/intc/arm_gicv3_its_common.h | 9 +-
> > 6 files changed, 331 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..545cda3665
> > --- /dev/null
> > +++ b/hw/intc/arm_gicv3_its.c
> > @@ -0,0 +1,240 @@
> > +/*
> > + * 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 gicv3_its_translation_write(void *opaque, hwaddr offset,
> > + uint64_t data, unsigned size,
> > + MemTxAttrs attrs)
> > +{
> > + MemTxResult result = MEMTX_OK;
> > +
> > + return result;
> > +}
> > +
> > +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 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 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,
> > + .valid.min_access_size = 4,
> > + .valid.max_access_size = 8,
> > + .impl.min_access_size = 4,
> > + .impl.max_access_size = 8,
> > + .endianness = DEVICE_NATIVE_ENDIAN,
> > +};
> > +
> > +static const MemoryRegionOps gicv3_its_translation_ops = {
> > + .write_with_attrs = gicv3_its_translation_write,
> > + .valid.min_access_size = 2,
> > + .valid.max_access_size = 4,
> > + .impl.min_access_size = 2,
> > + .impl.max_access_size = 4,
> > + .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);
> > +
> > + if (s->gicv3->cpu->gicr_typer & GICR_TYPER_PLPIS) {
> > + /* 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 - 1);
> > + 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);
> > + }
> > +}
> > +
> > +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);
> > +
> > + /* 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 - 1);
> > +
> > + 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 - 1);
> > + }
> > +}
> > +
> > +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..f1657c84e0 100644
> > --- a/hw/intc/arm_gicv3_its_common.c
> > +++ b/hw/intc/arm_gicv3_its_common.c
> > @@ -50,6 +50,8 @@ 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,
> > @@ -99,14 +101,15 @@ 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,
> > + tops ? tops : &gicv3_its_trans_ops, s,
> > "translation", ITS_TRANS_SIZE);
> >
> > /* Our two regions are always adjacent, therefore we now combine them
> > @@ -129,7 +132,6 @@ static void gicv3_its_common_reset(DeviceState *dev)
> > s->cbaser = 0;
> > s->cwriter = 0;
> > s->creadr = 0;
> > - s->iidr = 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..e0b06930a7 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,78 @@
> > #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)
> Isn't it FIELD(GITS_BASER, PHYADDRH_64K, 12, 4)
> hum actually it is fixed in next patch ;-) The right value can be put
> here directly
> > +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)
> > +
> > +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)
> > +
> > +#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 may rename into GITS_BASER_TYPE_DEVICE and COLLECTION?
> > +
> > +/**
> > + * Default features advertised by this version of ITS
> > + */
> > +/* Physical LPIs supported */
> > +#define GITS_TYPE_PHYSICAL (1U << 0)
> > +
> > +/*
> > + * 12 bytes Interrupt translation Table Entry size
> > + * ITE Lower 8 Bytes
> > + * Valid = 1 bit,InterruptType = 1 bit,
> > + * Size of LPI number space[considering max 24 bits],
> > + * Size of LPI number space[considering max 24 bits],
> repeated
> > + * ITE Higher 4 Bytes
> > + * ICID = 16 bits,
> > + * vPEID = 16 bits
>
> for info the ABI used by the kernel can be found in linux
> Documentation/virt/kvm/devices/arm-vgic-its.rst
>
> The ITE there is 8 bytes.
> Have you considered the same?

> The implementation defines ITE size of max 12 bytes (based on Table 5-3 ITE entries in GICv3 spec) by considering max LPI number space of 24 bits (for each of Interrupt_Number-pIntid field & Interrupt_Number-HypervisorID field) .
> + */
> +#define ITS_ITT_ENTRY_SIZE 0xC
> +
> +/* 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 6e52a166e3..4dcfea6aa8 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..65d1191db1 100644
> --- a/include/hw/intc/arm_gicv3_its_common.h
> +++ b/include/hw/intc/arm_gicv3_its_common.h
> @@ -25,17 +25,22 @@
> #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
> +
> struct GICv3ITSState {
> SysBusDevice parent_obj;
>
> @@ -52,6 +57,7 @@ struct GICv3ITSState {
> /* Registers */
> uint32_t ctlr;
> uint32_t iidr;
> + uint64_t typer;
> uint64_t cbaser;
> uint64_t cwriter;
> uint64_t creadr;
> @@ -62,7 +68,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;
>
Thanks

Eric

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

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

* Re: [PATCH v4 2/8] hw/intc: GICv3 ITS register definitions added
  2021-06-02 18:00 ` [PATCH v4 2/8] hw/intc: GICv3 ITS register definitions added Shashi Mallela
  2021-06-08 10:31   ` Peter Maydell
@ 2021-06-12  6:08   ` Eric Auger
  2021-06-16 21:02     ` shashi.mallela
  1 sibling, 1 reply; 52+ messages in thread
From: Eric Auger @ 2021-06-12  6:08 UTC (permalink / raw)
  To: Shashi Mallela, peter.maydell, leif, rad; +Cc: qemu-arm, qemu-devel



On 6/2/21 8:00 PM, Shashi Mallela 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                | 335 +++++++++++++++++++++++++
>  hw/intc/gicv3_internal.h               |  28 ++-
>  include/hw/intc/arm_gicv3_common.h     |   3 +
>  include/hw/intc/arm_gicv3_its_common.h |  30 +++
>  4 files changed, 395 insertions(+), 1 deletion(-)
> 
> diff --git a/hw/intc/arm_gicv3_its.c b/hw/intc/arm_gicv3_its.c
> index 545cda3665..af60f19c98 100644
> --- a/hw/intc/arm_gicv3_its.c
> +++ b/hw/intc/arm_gicv3_its.c
> @@ -28,6 +28,157 @@ struct GICv3ITSClass {
>      void (*parent_reset)(DeviceState *dev);
>  };
>  
> +static uint64_t baser_base_addr(uint64_t value, uint32_t page_sz)
> +{
> +    uint64_t result = 0;
> +
> +    switch (page_sz) {
> +    case GITS_ITT_PAGE_SIZE_0:
> +    case GITS_ITT_PAGE_SIZE_1:
> +        result = value & R_GITS_BASER_PHYADDR_MASK;
Use FIELD_EX64 as well for homogeneity?
> +        break;
> +
> +    case GITS_ITT_PAGE_SIZE_2:
> +        result = value & R_GITS_BASER_PHYADDRL_64K_MASK;
here as well?
> +        result |= FIELD_EX64(value, GITS_BASER, PHYADDRH_64K) << 48;
> +        break;
> +
> +    default:
> +        break;
> +    }
> +    return result;
> +}
> +
> +static void extract_table_params(GICv3ITSState *s)
> +{
> +    uint16_t num_pages = 0;
> +    uint8_t  page_sz_type;
> +    uint8_t type;
> +    uint32_t page_sz = 0;
> +    uint64_t value;
> +
> +    for (int i = 0; i < 8; i++) {
> +        value = s->baser[i];
> +
> +        if (!value) {
> +            continue;
> +        }
> +
> +        page_sz_type = FIELD_EX64(value, GITS_BASER, PAGESIZE);
> +
> +        switch (page_sz_type) {
> +        case 0:
> +            page_sz = GITS_ITT_PAGE_SIZE_0;
> +            break;
> +
> +        case 1:
> +            page_sz = GITS_ITT_PAGE_SIZE_1;
> +            break;
> +
> +        case 2:
> +        case 3:
> +            page_sz = GITS_ITT_PAGE_SIZE_2;
> +            break;
> +
> +        default:
> +            g_assert_not_reached();
> +        }
> +
> +        num_pages = FIELD_EX64(value, GITS_BASER, SIZE);
 + 1 directly? and remove num_pages + 1 below.
> +
> +        type = FIELD_EX64(value, GITS_BASER, TYPE);
> +
> +        switch (type) {
> +
> +        case GITS_ITT_TYPE_DEVICE:
> +            memset(&s->dt, 0 , sizeof(s->dt));
> +            s->dt.valid = FIELD_EX64(value, GITS_BASER, VALID);
> +
> +            if (!s->dt.valid) {
> +                return;
> +            }
> +
> +            s->dt.page_sz = page_sz;
> +            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));
> +
> +            s->dt.base_addr = baser_base_addr(value, page_sz);
> +
> +            break;
> +
> +        case GITS_ITT_TYPE_COLLECTION:
> +            memset(&s->ct, 0 , sizeof(s->ct));
> +            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) {
> +                return;
as this is an helper routine, I think it would be better to have this
check in the caller. Also you reset ct above.
> +            }
> +
> +            s->ct.page_sz = page_sz;
> +            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);
> +            }
> +
> +            s->ct.base_addr = baser_base_addr(value, page_sz);
> +
> +            break;
> +
> +        default:
> +            break;
> +        }
> +    }
> +}
> +
> +static void extract_cmdq_params(GICv3ITSState *s)
> +{
> +    uint16_t num_pages = 0;
> +    uint64_t value = s->cbaser;
> +
> +    num_pages = FIELD_EX64(value, GITS_CBASER, SIZE);
+ 1
> +
> +    memset(&s->cq, 0 , sizeof(s->cq));
> +    s->cq.valid = FIELD_EX64(value, GITS_CBASER, VALID);
> +
> +    if (s->cq.valid) {
> +        s->cq.max_entries = ((num_pages + 1) * GITS_ITT_PAGE_SIZE_0) /
nit: use of GITS_ITT_PAGE_SIZE_0 is misleading as ITT stands for
interrupt translation table and does not relate to CMDQ. Use 4K define
instead.
> +                             GITS_CMDQ_ENTRY_SIZE;
> +        s->cq.base_addr = FIELD_EX64(value, GITS_CBASER, PHYADDR);
> +        s->cq.base_addr <<= R_GITS_CBASER_PHYADDR_SHIFT;
> +    }
> +}
> +
>  static MemTxResult gicv3_its_translation_write(void *opaque, hwaddr offset,
>                                                 uint64_t data, unsigned size,
>                                                 MemTxAttrs attrs)
> @@ -41,7 +192,73 @@ static MemTxResult its_writel(GICv3ITSState *s, hwaddr offset,
>                                uint64_t value, MemTxAttrs attrs)
>  {
>      MemTxResult result = MEMTX_OK;
> +    int index;
>  
> +    switch (offset) {
> +    case GITS_CTLR:
> +        s->ctlr |= (value & ~(s->ctlr));
> +
> +        if (s->ctlr & ITS_CTLR_ENABLED) {
> +            extract_table_params(s);
> +            extract_cmdq_params(s);
> +            s->creadr = 0;
The KVM code also checks the he CBASER and
device/collection BASER are valid

To be further checked in subsequent patches:
- cache invalidation when turning off
- process commands if turned on?
- any cmd lock

> +        }
> +        break;
> +    case GITS_CBASER:
> +        /*
> +         * IMPDEF choice:- 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:
> +        /*
> +         * IMPDEF choice:- GITS_CBASER register becomes RO if ITS is
> +         *                 already enabled
> +         */
> +        if (!(s->ctlr & ITS_CTLR_ENABLED)) {
> +            s->cbaser = deposit64(s->cbaser, 32, 32, value);
you need to reset creadr here also

also CWRITER should be reset to CREADR.
KVM code comment:
/*
 * CWRITER is architecturally UNKNOWN on reset, but we need to reset
 * it to CREADR to make sure we start with an empty command buffer.
 */

> +        }> +        break;
> +    case GITS_CWRITER:
> +        s->cwriter = deposit64(s->cwriter, 0, 32,
> +                               (value & ~R_GITS_CWRITER_RETRY_MASK));
how do you implement the overflow case?
"If GITS_CWRITER is written with a value outside of the valid range
specified by
GITS_CBASER.Physical_Address and GITS_CBASER.Size, behavior is a
CONSTRAINED UNPREDICTABLE choice"
for info the KVM code does not write the actual reg

further check: process command?

> +        break;
> +    case GITS_CWRITER + 4:
> +        s->cwriter = deposit64(s->cwriter, 32, 32,
> +                               (value & ~R_GITS_CWRITER_RETRY_MASK));
> +        break;
> +    case GITS_BASER ... GITS_BASER + 0x3f:
> +        /*
> +         * IMPDEF choice:- GITS_BASERn register becomes RO if ITS is
> +         *                 already enabled
> +         */
> +        if (!(s->ctlr & ITS_CTLR_ENABLED)) {
> +            index = (offset - GITS_BASER) / 8;
> +
> +            if (offset & 7) {
> +                s->baser[index] = deposit64(s->baser[index], 32, 32,
> +                                            (value & ~GITS_BASER_VAL_MASK));
> +            } else {
> +                s->baser[index] = deposit64(s->baser[index], 0, 32,
> +                                            (value & ~GITS_BASER_VAL_MASK));
> +            }
> +        }
> +        break;
> +    case GITS_IIDR:
> +    case GITS_IDREGS ... GITS_IDREGS + 0x2f:
> +        /* 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;
>  }
>  
> @@ -49,7 +266,55 @@ 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 = gicv3_iidr();
> +        break;
> +    case GITS_IDREGS ... GITS_IDREGS + 0x2f:
> +        /* ID registers */
> +        *data = gicv3_idreg(offset - GITS_IDREGS);
I am not sure those are the same as the gicv3
on KVM we have
        case GITS_PIDR0:
                return 0x92;    /* part number, bits[7:0] */
        case GITS_PIDR1:
                return 0xb4;    /* part number, bits[11:8] */
        case GITS_PIDR2:
                return GIC_PIDR2_ARCH_GICv3 | 0x0b;
        case GITS_PIDR4:
                return 0x40;    /* This is a 64K software visible page */
        /* The following are the ID registers for (any) GIC. */
        case GITS_CIDR0:
                return 0x0d;
        case GITS_CIDR1:
                return 0xf0;
        case GITS_CIDR2:
                return 0x05;
        case GITS_CIDR3:
                return 0xb1;


> +        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 = extract64(s->baser[index], 32, 32);
> +        } else {
> +            *data = extract64(s->baser[index], 0, 32);
> +        }
> +        break;
> +    default:
> +        result = MEMTX_ERROR;
> +        break;
> +    }
>      return result;
>  }
>  
> @@ -57,7 +322,42 @@ 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:
> +        /*
> +         * IMPDEF choice:- GITS_BASERn register becomes 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);
> +        }
> +        break;
> +    case GITS_CBASER:
> +        /*
> +         * IMPDEF choice:- GITS_CBASER register becomes RO if ITS is
> +         *                 already enabled
> +         */
> +        if (!(s->ctlr & ITS_CTLR_ENABLED)) {
> +            s->cbaser = value;
s->creadr = 0;
cwriter = creader?
> +        }
> +        break;
> +    case GITS_CWRITER:
> +        s->cwriter = value & ~R_GITS_CWRITER_RETRY_MASK;
> +        break;
> +    case GITS_CREADR:
RO if GICD_CTLR.DS = 0
On KVM side the write access is implemented
> +    case GITS_TYPER:
> +        /* 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;
>  }
>  
> @@ -65,7 +365,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;
>  }
>  
> @@ -162,6 +484,9 @@ static void gicv3_arm_its_realize(DeviceState *dev, Error **errp)
>      gicv3_its_init_mmio(s, &gicv3_its_control_ops, &gicv3_its_translation_ops);
>  
>      if (s->gicv3->cpu->gicr_typer & GICR_TYPER_PLPIS) {
> +        address_space_init(&s->gicv3->dma_as, s->gicv3->dma,
> +                           "gicv3-its-sysmem");
> +
>          /* set the ITS default features supported */
>          s->typer = FIELD_DP64(s->typer, GITS_TYPER, PHYSICAL,
>                                GITS_TYPE_PHYSICAL);
> @@ -208,6 +533,14 @@ static void gicv3_its_reset(DeviceState *dev)
>      }
>  }
>  
> +static void gicv3_its_post_load(GICv3ITSState *s)
> +{
> +    if (s->ctlr & ITS_CTLR_ENABLED) {
> +        extract_table_params(s);
> +        extract_cmdq_params(s);
> +    }
> +}
> +
>  static Property gicv3_its_props[] = {
>      DEFINE_PROP_LINK("parent-gicv3", GICv3ITSState, gicv3, "arm-gicv3",
>                       GICv3State *),
> @@ -218,10 +551,12 @@ static void gicv3_its_class_init(ObjectClass *klass, void *data)
>  {
>      DeviceClass *dc = DEVICE_CLASS(klass);
>      GICv3ITSClass *ic = ARM_GICV3_ITS_CLASS(klass);
> +    GICv3ITSCommonClass *icc = ARM_GICV3_ITS_COMMON_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);
> +    icc->post_load = gicv3_its_post_load;
>  }
>  
>  static const TypeInfo gicv3_its_info = {
> diff --git a/hw/intc/gicv3_internal.h b/hw/intc/gicv3_internal.h
> index e0b06930a7..d6aaa94e4c 100644
> --- a/hw/intc/gicv3_internal.h
> +++ b/hw/intc/gicv3_internal.h
> @@ -238,7 +238,7 @@ 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, PHYADDRH_64K, 12, 4)
>  FIELD(GITS_BASER, ENTRYSIZE, 48, 5)
>  FIELD(GITS_BASER, OUTERCACHE, 53, 3)
>  FIELD(GITS_BASER, TYPE, 56, 3)
> @@ -246,6 +246,17 @@ FIELD(GITS_BASER, INNERCACHE, 59, 3)
>  FIELD(GITS_BASER, INDIRECT, 62, 1)
>  FIELD(GITS_BASER, VALID, 63, 1)
>  
> +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_CWRITER, RETRY, 0, 1)
> +FIELD(GITS_CWRITER, OFFSET, 5, 15)
> +
> +FIELD(GITS_CTLR, ENABLED, 0, 1)
>  FIELD(GITS_CTLR, QUIESCENT, 31, 1)
>  
>  FIELD(GITS_TYPER, PHYSICAL, 0, 1)
> @@ -257,6 +268,13 @@ FIELD(GITS_TYPER, PTA, 19, 1)
>  FIELD(GITS_TYPER, CIDBITS, 32, 4)
>  FIELD(GITS_TYPER, CIL, 36, 1)
>  
> +#define GITS_IDREGS           0xFFD0
> +
> +#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
> @@ -264,6 +282,14 @@ FIELD(GITS_TYPER, CIL, 36, 1)
>  #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
Why not naming _4K 16K 64K instead of _0, 1, 2?
> +
> +#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..1fd5cedbbd 100644
> --- a/include/hw/intc/arm_gicv3_common.h
> +++ b/include/hw/intc/arm_gicv3_common.h
> @@ -226,6 +226,9 @@ struct GICv3State {
>      int dev_fd; /* kvm device fd if backed by kvm vgic support */
>      Error *migration_blocker;
>  
> +    MemoryRegion *dma;
> +    AddressSpace dma_as;
> +
>      /* Distributor */
>  
>      /* for a GIC with the security extensions the NS banked version of this
> diff --git a/include/hw/intc/arm_gicv3_its_common.h b/include/hw/intc/arm_gicv3_its_common.h
> index 65d1191db1..78b1ba7e6b 100644
> --- a/include/hw/intc/arm_gicv3_its_common.h
> +++ b/include/hw/intc/arm_gicv3_its_common.h
> @@ -41,6 +41,32 @@
>  
>  #define GITS_TRANSLATER  0x0040
>  
> +typedef struct {
> +    bool valid;
> +    bool indirect;
> +    uint16_t entry_sz;
> +    uint32_t page_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 page_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;
>  
> @@ -63,6 +89,10 @@ struct GICv3ITSState {
>      uint64_t creadr;
>      uint64_t baser[8];
>  
> +    DevTableDesc  dt;
> +    CollTableDesc ct;
> +    CmdQDesc      cq;
> +
>      Error *migration_blocker;
>  };
Thanks

Eric
>  
> 



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

* Re: [PATCH v4 1/8] hw/intc: GICv3 ITS initial framework
  2021-06-02 18:00 ` [PATCH v4 1/8] hw/intc: GICv3 ITS initial framework Shashi Mallela
  2021-06-08 10:02   ` Peter Maydell
  2021-06-11 16:21   ` Eric Auger
@ 2021-06-12  6:52   ` Eric Auger
  2021-07-06  7:29     ` Eric Auger
  2 siblings, 1 reply; 52+ messages in thread
From: Eric Auger @ 2021-06-12  6:52 UTC (permalink / raw)
  To: Shashi Mallela, peter.maydell, leif, rad; +Cc: qemu-arm, qemu-devel



On 6/2/21 8:00 PM, Shashi Mallela 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                | 240 +++++++++++++++++++++++++
>  hw/intc/arm_gicv3_its_common.c         |   8 +-
>  hw/intc/arm_gicv3_its_kvm.c            |   2 +-
>  hw/intc/gicv3_internal.h               |  88 +++++++--
>  hw/intc/meson.build                    |   1 +
>  include/hw/intc/arm_gicv3_its_common.h |   9 +-
>  6 files changed, 331 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..545cda3665
> --- /dev/null
> +++ b/hw/intc/arm_gicv3_its.c
> @@ -0,0 +1,240 @@
> +/*
> + * 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 gicv3_its_translation_write(void *opaque, hwaddr offset,
> +                                               uint64_t data, unsigned size,
> +                                               MemTxAttrs attrs)
> +{
> +    MemTxResult result = MEMTX_OK;
> +
> +    return result;
> +}
> +
> +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 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 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,
> +    .valid.min_access_size = 4,
> +    .valid.max_access_size = 8,
> +    .impl.min_access_size = 4,
> +    .impl.max_access_size = 8,
> +    .endianness = DEVICE_NATIVE_ENDIAN,
> +};
> +
> +static const MemoryRegionOps gicv3_its_translation_ops = {
> +    .write_with_attrs = gicv3_its_translation_write,
> +    .valid.min_access_size = 2,
> +    .valid.max_access_size = 4,
> +    .impl.min_access_size = 2,
> +    .impl.max_access_size = 4,
> +    .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);
> +
> +    if (s->gicv3->cpu->gicr_typer & GICR_TYPER_PLPIS) {
> +        /* 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 - 1);
> +        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);
> +    }
> +}
> +
> +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);
> +
> +        /* 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 - 1);
> +
> +        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 - 1);
> +    }
> +}
> +
> +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..f1657c84e0 100644
> --- a/hw/intc/arm_gicv3_its_common.c
> +++ b/hw/intc/arm_gicv3_its_common.c
> @@ -50,6 +50,8 @@ 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,
> @@ -99,14 +101,15 @@ 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,
> +                          tops ? tops : &gicv3_its_trans_ops, s,
>                            "translation", ITS_TRANS_SIZE);
>  
>      /* Our two regions are always adjacent, therefore we now combine them
> @@ -129,7 +132,6 @@ static void gicv3_its_common_reset(DeviceState *dev)
>      s->cbaser = 0;
>      s->cwriter = 0;
>      s->creadr = 0;
> -    s->iidr = 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..e0b06930a7 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)
> -
Here you remove those defs without much explanation in the commit msg.
I see you restore them in 5/8

+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)

Maybe squash this in this patch?

Thanks

Eric

>  #define ICC_CTLR_EL1_CBPR           (1U << 0)
>  #define ICC_CTLR_EL1_EOIMODE        (1U << 1)
>  #define ICC_CTLR_EL1_PMHE           (1U << 6)
> @@ -239,6 +231,78 @@
>  #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)
> +
> +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)
> +
> +#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
> +
> +/**
> + * Default features advertised by this version of ITS
> + */
> +/* Physical LPIs supported */
> +#define GITS_TYPE_PHYSICAL           (1U << 0)
> +
> +/*
> + * 12 bytes Interrupt translation Table Entry size
> + * ITE Lower 8 Bytes
> + * Valid = 1 bit,InterruptType = 1 bit,
> + * Size of LPI number space[considering max 24 bits],
> + * Size of LPI number space[considering max 24 bits],
> + * ITE Higher 4 Bytes
> + * ICID = 16 bits,
> + * vPEID = 16 bits
> + */
> +#define ITS_ITT_ENTRY_SIZE            0xC
> +
> +/* 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 6e52a166e3..4dcfea6aa8 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..65d1191db1 100644
> --- a/include/hw/intc/arm_gicv3_its_common.h
> +++ b/include/hw/intc/arm_gicv3_its_common.h
> @@ -25,17 +25,22 @@
>  #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
> +
>  struct GICv3ITSState {
>      SysBusDevice parent_obj;
>  
> @@ -52,6 +57,7 @@ struct GICv3ITSState {
>      /* Registers */
>      uint32_t ctlr;
>      uint32_t iidr;
> +    uint64_t typer;
>      uint64_t cbaser;
>      uint64_t cwriter;
>      uint64_t creadr;
> @@ -62,7 +68,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;
> 



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

* Re: [PATCH v4 3/8] hw/intc: GICv3 ITS command queue framework
  2021-06-02 18:00 ` [PATCH v4 3/8] hw/intc: GICv3 ITS command queue framework Shashi Mallela
  2021-06-08 10:38   ` Peter Maydell
@ 2021-06-13 14:13   ` Eric Auger
  2021-06-16 21:02     ` shashi.mallela
  2021-06-13 14:39   ` Eric Auger
  2 siblings, 1 reply; 52+ messages in thread
From: Eric Auger @ 2021-06-13 14:13 UTC (permalink / raw)
  To: Shashi Mallela, peter.maydell, leif, rad; +Cc: qemu-arm, qemu-devel

Hi Sashi,

On 6/2/21 8:00 PM, Shashi Mallela 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  | 295 +++++++++++++++++++++++++++++++++++++++
>  hw/intc/gicv3_internal.h |  37 +++++
>  2 files changed, 332 insertions(+)
> 
> diff --git a/hw/intc/arm_gicv3_its.c b/hw/intc/arm_gicv3_its.c
> index af60f19c98..6551c577b3 100644
> --- a/hw/intc/arm_gicv3_its.c
> +++ b/hw/intc/arm_gicv3_its.c
> @@ -49,6 +49,295 @@ static uint64_t baser_base_addr(uint64_t value, uint32_t page_sz)
>      return result;
>  }
>  
> +static MemTxResult update_cte(GICv3ITSState *s, uint16_t icid, bool valid,
> +                              uint64_t rdbase)
> +{
> +    AddressSpace *as = &s->gicv3->dma_as;
> +    uint64_t value;
> +    uint64_t l2t_addr;
> +    bool valid_l2t;
> +    uint32_t l2t_id;
> +    uint32_t max_l2_entries;
> +    uint64_t cte = 0;
> +    MemTxResult res = MEMTX_OK;
> +
> +    if (!s->ct.valid) {
Isn't it a guest log error case. Also you return MEMTX_OK in that case.
Is that what you want?
> +        return res;
> +    }
> +
> +    if (valid) {
> +        /* add mapping entry to collection table */
> +        cte = (valid & VALID_MASK) |
> +              ((rdbase & RDBASE_PROCNUM_MASK) << 1ULL);
Do you really need to sanitize rdbase again?
> +    }
> +
> +    /*
> +     * 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.
> +     */
> +    if (s->ct.indirect) {
> +        l2t_id = icid / (s->ct.page_sz / L1TABLE_ENTRY_SIZE);
> +
> +        value = address_space_ldq_le(as,
> +                                     s->ct.base_addr +
> +                                     (l2t_id * L1TABLE_ENTRY_SIZE),
> +                                     MEMTXATTRS_UNSPECIFIED, &res);
> +
> +        if (res != MEMTX_OK) {
> +            return res;
> +        }
> +
> +        valid_l2t = (value >> VALID_SHIFT) & VALID_MASK;
> +
> +        if (valid_l2t) {
> +            max_l2_entries = s->ct.page_sz / s->ct.entry_sz;
> +
> +            l2t_addr = value & ((1ULL << 51) - 1);
> +
> +            address_space_stq_le(as, l2t_addr +
> +                                 ((icid % max_l2_entries) * GITS_CTE_SIZE),
> +                                 cte, MEMTXATTRS_UNSPECIFIED, &res);
> +        }
> +    } else {
> +        /* Flat level table */
> +        address_space_stq_le(as, s->ct.base_addr + (icid * GITS_CTE_SIZE),
> +                             cte, MEMTXATTRS_UNSPECIFIED, &res);
> +    }
> +    return res;
> +}
> +
> +static MemTxResult process_mapc(GICv3ITSState *s, uint32_t offset)
> +{
> +    AddressSpace *as = &s->gicv3->dma_as;
> +    uint16_t icid;
> +    uint64_t rdbase;
> +    bool valid;
> +    MemTxResult res = MEMTX_OK;
> +    uint64_t value;
> +
> +    offset += NUM_BYTES_IN_DW;
> +    offset += NUM_BYTES_IN_DW;
May be relevant to add some trace points for debuggability.
> +
> +    value = address_space_ldq_le(as, s->cq.base_addr + offset,
> +                                 MEMTXATTRS_UNSPECIFIED, &res);
> +
> +    if (res != MEMTX_OK) {
> +        return res;
> +    }
> +
> +    icid = value & ICID_MASK;
> +
> +    rdbase = (value >> R_MAPC_RDBASE_SHIFT) & RDBASE_PROCNUM_MASK;
usually the mask is applied before the shift.
> +
> +    valid = (value >> VALID_SHIFT) & VALID_MASK;
use FIELD, see below
> +
> +    if ((icid > s->ct.max_collids) || (rdbase > s->gicv3->num_cpu)) {
you also need to check against ITS_CIDBITS limit?
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +                      "ITS MAPC: invalid collection table attributes "
> +                      "icid %d rdbase %lu\n",  icid, rdbase);
> +        /*
> +         * in this implementation,in case of error
> +         * we ignore this command and move onto the next
> +         * command in the queue
spec says a command error occurs in that case.
> +         */
> +    } else {
> +        res = update_cte(s, icid, valid, rdbase);
> +    }
> +
> +    return res;
> +}
> +
> +static MemTxResult update_dte(GICv3ITSState *s, uint32_t devid, bool valid,
> +                              uint8_t size, uint64_t itt_addr)
> +{
> +    AddressSpace *as = &s->gicv3->dma_as;
> +    uint64_t value;
> +    uint64_t l2t_addr;
> +    bool valid_l2t;
> +    uint32_t l2t_id;
> +    uint32_t max_l2_entries;
> +    uint64_t dte = 0;
> +    MemTxResult res = MEMTX_OK;
> +
> +    if (s->dt.valid) {
> +        if (valid) {
> +            /* add mapping entry to device table */
> +            dte = (valid & VALID_MASK) |
> +                  ((size & SIZE_MASK) << 1U) |
> +                  ((itt_addr & ITTADDR_MASK) << 6ULL);
> +        }
> +    } else {
> +        return res;
> +    }
> +
> +    /*
> +     * 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.
> +     */
> +    if (s->dt.indirect) {
> +        l2t_id = devid / (s->dt.page_sz / L1TABLE_ENTRY_SIZE);
> +
> +        value = address_space_ldq_le(as,
> +                                     s->dt.base_addr +
> +                                     (l2t_id * L1TABLE_ENTRY_SIZE),
> +                                     MEMTXATTRS_UNSPECIFIED, &res);
> +
> +        if (res != MEMTX_OK) {
> +            return res;
> +        }
> +
> +        valid_l2t = (value >> VALID_SHIFT) & VALID_MASK;
> +
> +        if (valid_l2t) {
> +            max_l2_entries = s->dt.page_sz / s->dt.entry_sz;
> +
> +            l2t_addr = value & ((1ULL << 51) - 1);
> +
> +            address_space_stq_le(as, l2t_addr +
> +                                 ((devid % max_l2_entries) * GITS_DTE_SIZE),
> +                                 dte, MEMTXATTRS_UNSPECIFIED, &res);
> +        }
> +    } else {
> +        /* Flat level table */
> +        address_space_stq_le(as, s->dt.base_addr + (devid * GITS_DTE_SIZE),
> +                             dte, MEMTXATTRS_UNSPECIFIED, &res);
> +    }
> +    return res;
> +}
> +
> +static MemTxResult process_mapd(GICv3ITSState *s, uint64_t value,
you do not seem to use the input value, remove it?
> +                                uint32_t offset)
> +{
> +    AddressSpace *as = &s->gicv3->dma_as;
> +    uint32_t devid;
> +    uint8_t size;
> +    uint64_t itt_addr;
> +    bool valid;
> +    MemTxResult res = MEMTX_OK;
> +
> +    devid = (value >> DEVID_SHIFT) & DEVID_MASK;
> +
> +    offset += NUM_BYTES_IN_DW;
> +    value = address_space_ldq_le(as, s->cq.base_addr + offset,
> +                                 MEMTXATTRS_UNSPECIFIED, &res);
> +
> +    if (res != MEMTX_OK) {
> +        return res;
> +    }
> +
> +    size = (value & SIZE_MASK);
> +
> +    offset += NUM_BYTES_IN_DW;
> +    value = address_space_ldq_le(as, s->cq.base_addr + offset,
> +                                 MEMTXATTRS_UNSPECIFIED, &res);
> +
> +    if (res != MEMTX_OK) {
> +        return res;
> +    }
> +
> +    itt_addr = (value >> ITTADDR_SHIFT) & ITTADDR_MASK;
this looks weird to me, usually we apply the mask first and then shift.
> +
> +    valid = (value >> VALID_SHIFT) & VALID_MASK;
use FIELD_EX64()?
> +
> +    if ((devid > s->dt.max_devids) ||
> +        (size > FIELD_EX64(s->typer, GITS_TYPER, IDBITS))) {
ITS_IDBITS?
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +                      "ITS MAPD: invalid device table attributes "
> +                      "devid %d or size %d\n", devid, size);
> +        /*
> +         * in this implementation, in case of error
> +         * we ignore this command and move onto the next
> +         * command in the queue
> +         */
> +    } else {
> +        res = update_dte(s, devid, valid, size, itt_addr);
> +    }
> +
> +    return res;
> +}
> +
> +/*
> + * Current implementation blocks until all
> + * commands are processed
> + */
> +static void 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->dma_as;
> +    MemTxResult res = MEMTX_OK;
> +    uint8_t cmd;
> +
> +    if (!(s->ctlr & ITS_CTLR_ENABLED)) {
> +        return;
> +    }
> +
> +    wr_offset = FIELD_EX64(s->cwriter, GITS_CWRITER, OFFSET);
> +
> +    if (wr_offset > s->cq.max_entries) {
Shouldn't this be checked on cwrite write instead?
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +                      "%s: invalid write offset "
> +                      "%d\n", __func__, wr_offset);
> +        return;
> +    }
> +
> +    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:
> +            /*
> +             * 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.
> +             * Hence no further processing is required for SYNC command.
> +             */
> +            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 {
> +            /*
> +             * in this implementation,in case of dma read/write error
> +             * we stall the command processing
> +             */
> +            s->creadr = FIELD_DP64(s->creadr, GITS_CREADR, STALLED, 1);
> +            qemu_log_mask(LOG_GUEST_ERROR,
> +                          "%s: %x cmd processing failed!!\n", __func__, cmd);
> +            break;
> +        }
> +    }
> +}
> +
>  static void extract_table_params(GICv3ITSState *s)
>  {
>      uint16_t num_pages = 0;
> @@ -226,6 +515,9 @@ static MemTxResult its_writel(GICv3ITSState *s, hwaddr offset,
>      case GITS_CWRITER:
>          s->cwriter = deposit64(s->cwriter, 0, 32,
>                                 (value & ~R_GITS_CWRITER_RETRY_MASK));
> +        if (s->cwriter != s->creadr) {
> +            process_cmdq(s);
I would expect process_cmdq() to be called as well on ITS enable
> +        }
>          break;
>      case GITS_CWRITER + 4:
>          s->cwriter = deposit64(s->cwriter, 32, 32,
> @@ -346,6 +638,9 @@ static MemTxResult its_writell(GICv3ITSState *s, hwaddr offset,
>          break;
>      case GITS_CWRITER:
>          s->cwriter = value & ~R_GITS_CWRITER_RETRY_MASK;
> +        if (s->cwriter != s->creadr) {
> +            process_cmdq(s);
> +        }
>          break;
>      case GITS_CREADR:
>      case GITS_TYPER:
> diff --git a/hw/intc/gicv3_internal.h b/hw/intc/gicv3_internal.h
> index d6aaa94e4c..0932a30560 100644
> --- a/hw/intc/gicv3_internal.h
> +++ b/hw/intc/gicv3_internal.h
> @@ -253,6 +253,9 @@ 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)
>  
> @@ -289,6 +292,40 @@ 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_LENGTH                  16
> +#define ICID_MASK                 ((1U << ICID_LENGTH) - 1)
can't you use FIELD') as well for the ICID?
> +FIELD(MAPC, RDBASE, 16, 32)
> +
> +#define RDBASE_PROCNUM_LENGTH        16
> +#define RDBASE_PROCNUM_MASK       ((1ULL << RDBASE_PROCNUM_LENGTH) - 1)
why do we have both the RDBASE FIELD def and above defs?
> +
> +#define DEVID_SHIFT                  32
> +#define DEVID_LENGTH                 32
> +#define DEVID_MASK                ((1ULL << DEVID_LENGTH) - 1)
we don't have any DEVID field in MAPC, I guess it belongs to MAPD?
> +
> +/* MAPD command fields */
> +#define ITTADDR_LENGTH               44
> +#define ITTADDR_SHIFT                 8
> +#define ITTADDR_MASK              ((1ULL << ITTADDR_LENGTH) - 1)
> +#define SIZE_MASK                 0x1f
Can't you homogenize the definition, use field() and/or prefix with the
cmd name when not common to severals cmds?

> +
> +#define VALID_SHIFT               63
> +#define VALID_MASK                1ULL
>  
>  /**
>   * Default features advertised by this version of ITS
> 
Thanks

Eric



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

* Re: [PATCH v4 3/8] hw/intc: GICv3 ITS command queue framework
  2021-06-02 18:00 ` [PATCH v4 3/8] hw/intc: GICv3 ITS command queue framework Shashi Mallela
  2021-06-08 10:38   ` Peter Maydell
  2021-06-13 14:13   ` Eric Auger
@ 2021-06-13 14:39   ` Eric Auger
  2021-06-28 15:55     ` shashi.mallela
  2 siblings, 1 reply; 52+ messages in thread
From: Eric Auger @ 2021-06-13 14:39 UTC (permalink / raw)
  To: Shashi Mallela, peter.maydell, leif, rad; +Cc: qemu-arm, qemu-devel

Hi,

On 6/2/21 8:00 PM, Shashi Mallela 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  | 295 +++++++++++++++++++++++++++++++++++++++
>  hw/intc/gicv3_internal.h |  37 +++++
>  2 files changed, 332 insertions(+)
> 
> diff --git a/hw/intc/arm_gicv3_its.c b/hw/intc/arm_gicv3_its.c
> index af60f19c98..6551c577b3 100644
> --- a/hw/intc/arm_gicv3_its.c
> +++ b/hw/intc/arm_gicv3_its.c
> @@ -49,6 +49,295 @@ static uint64_t baser_base_addr(uint64_t value, uint32_t page_sz)
>      return result;
>  }
>  
> +static MemTxResult update_cte(GICv3ITSState *s, uint16_t icid, bool valid,
> +                              uint64_t rdbase)
> +{
> +    AddressSpace *as = &s->gicv3->dma_as;
> +    uint64_t value;
> +    uint64_t l2t_addr;
> +    bool valid_l2t;
> +    uint32_t l2t_id;
> +    uint32_t max_l2_entries;
> +    uint64_t cte = 0;
> +    MemTxResult res = MEMTX_OK;
> +
> +    if (!s->ct.valid) {
> +        return res;
> +    }
> +
> +    if (valid) {
> +        /* add mapping entry to collection table */
> +        cte = (valid & VALID_MASK) |
> +              ((rdbase & RDBASE_PROCNUM_MASK) << 1ULL);
> +    }
> +
> +    /*
> +     * 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.
> +     */
> +    if (s->ct.indirect) {
> +        l2t_id = icid / (s->ct.page_sz / L1TABLE_ENTRY_SIZE);
> +
> +        value = address_space_ldq_le(as,
> +                                     s->ct.base_addr +
> +                                     (l2t_id * L1TABLE_ENTRY_SIZE),
> +                                     MEMTXATTRS_UNSPECIFIED, &res);
> +
> +        if (res != MEMTX_OK) {
> +            return res;
> +        }
> +
> +        valid_l2t = (value >> VALID_SHIFT) & VALID_MASK;
> +
> +        if (valid_l2t) {
> +            max_l2_entries = s->ct.page_sz / s->ct.entry_sz;
> +
> +            l2t_addr = value & ((1ULL << 51) - 1);
> +
> +            address_space_stq_le(as, l2t_addr +
> +                                 ((icid % max_l2_entries) * GITS_CTE_SIZE),
> +                                 cte, MEMTXATTRS_UNSPECIFIED, &res);
> +        }
> +    } else {
> +        /* Flat level table */
> +        address_space_stq_le(as, s->ct.base_addr + (icid * GITS_CTE_SIZE),
> +                             cte, MEMTXATTRS_UNSPECIFIED, &res);
> +    }
> +    return res;
> +}

Looking at your DevTableDesc and CollTableDesc types again, they are
basically the same except max_devids/max_collids. You may use a single
one and it may be possible to define helpers to access an entry in the
DT or CT.

update_cte/update_dte looks quite similar if you compute the cte and dte
externally and pass a pointer to the associated TableDesc?

Thanks

Eric


> +
> +static MemTxResult process_mapc(GICv3ITSState *s, uint32_t offset)
> +{
> +    AddressSpace *as = &s->gicv3->dma_as;
> +    uint16_t icid;
> +    uint64_t rdbase;
> +    bool valid;
> +    MemTxResult res = MEMTX_OK;
> +    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);
> +
> +    if (res != MEMTX_OK) {
> +        return res;
> +    }
> +
> +    icid = value & ICID_MASK;
> +
> +    rdbase = (value >> R_MAPC_RDBASE_SHIFT) & RDBASE_PROCNUM_MASK;
> +
> +    valid = (value >> VALID_SHIFT) & VALID_MASK;
> +
> +    if ((icid > s->ct.max_collids) || (rdbase > s->gicv3->num_cpu)) {
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +                      "ITS MAPC: invalid collection table attributes "
> +                      "icid %d rdbase %lu\n",  icid, rdbase);
> +        /*
> +         * in this implementation,in case of error
> +         * we ignore this command and move onto the next
> +         * command in the queue
> +         */
> +    } else {
> +        res = update_cte(s, icid, valid, rdbase);
> +    }
> +
> +    return res;
> +}
> +
> +static MemTxResult update_dte(GICv3ITSState *s, uint32_t devid, bool valid,
> +                              uint8_t size, uint64_t itt_addr)
> +{
> +    AddressSpace *as = &s->gicv3->dma_as;
> +    uint64_t value;
> +    uint64_t l2t_addr;
> +    bool valid_l2t;
> +    uint32_t l2t_id;
> +    uint32_t max_l2_entries;
> +    uint64_t dte = 0;
> +    MemTxResult res = MEMTX_OK;
> +
> +    if (s->dt.valid) {
> +        if (valid) {
> +            /* add mapping entry to device table */
> +            dte = (valid & VALID_MASK) |
> +                  ((size & SIZE_MASK) << 1U) |
> +                  ((itt_addr & ITTADDR_MASK) << 6ULL);
> +        }
> +    } else {
> +        return res;
> +    }
> +
> +    /*
> +     * 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.
> +     */
> +    if (s->dt.indirect) {
> +        l2t_id = devid / (s->dt.page_sz / L1TABLE_ENTRY_SIZE);
> +
> +        value = address_space_ldq_le(as,
> +                                     s->dt.base_addr +
> +                                     (l2t_id * L1TABLE_ENTRY_SIZE),
> +                                     MEMTXATTRS_UNSPECIFIED, &res);
> +
> +        if (res != MEMTX_OK) {
> +            return res;
> +        }
> +
> +        valid_l2t = (value >> VALID_SHIFT) & VALID_MASK;
> +
> +        if (valid_l2t) {
> +            max_l2_entries = s->dt.page_sz / s->dt.entry_sz;
> +
> +            l2t_addr = value & ((1ULL << 51) - 1);
> +
> +            address_space_stq_le(as, l2t_addr +
> +                                 ((devid % max_l2_entries) * GITS_DTE_SIZE),
> +                                 dte, MEMTXATTRS_UNSPECIFIED, &res);
> +        }
> +    } else {
> +        /* Flat level table */
> +        address_space_stq_le(as, s->dt.base_addr + (devid * GITS_DTE_SIZE),
> +                             dte, MEMTXATTRS_UNSPECIFIED, &res);
> +    }
> +    return res;
> +}
> +
> +static MemTxResult process_mapd(GICv3ITSState *s, uint64_t value,
> +                                uint32_t offset)
> +{
> +    AddressSpace *as = &s->gicv3->dma_as;
> +    uint32_t devid;
> +    uint8_t size;
> +    uint64_t itt_addr;
> +    bool valid;
> +    MemTxResult res = MEMTX_OK;
> +
> +    devid = (value >> DEVID_SHIFT) & DEVID_MASK;
> +
> +    offset += NUM_BYTES_IN_DW;
> +    value = address_space_ldq_le(as, s->cq.base_addr + offset,
> +                                 MEMTXATTRS_UNSPECIFIED, &res);
> +
> +    if (res != MEMTX_OK) {
> +        return res;
> +    }
> +
> +    size = (value & SIZE_MASK);
> +
> +    offset += NUM_BYTES_IN_DW;
> +    value = address_space_ldq_le(as, s->cq.base_addr + offset,
> +                                 MEMTXATTRS_UNSPECIFIED, &res);
> +
> +    if (res != MEMTX_OK) {
> +        return res;
> +    }
> +
> +    itt_addr = (value >> ITTADDR_SHIFT) & ITTADDR_MASK;
> +
> +    valid = (value >> VALID_SHIFT) & VALID_MASK;
> +
> +    if ((devid > s->dt.max_devids) ||
> +        (size > FIELD_EX64(s->typer, GITS_TYPER, IDBITS))) {
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +                      "ITS MAPD: invalid device table attributes "
> +                      "devid %d or size %d\n", devid, size);
> +        /*
> +         * in this implementation, in case of error
> +         * we ignore this command and move onto the next
> +         * command in the queue
> +         */
> +    } else {
> +        res = update_dte(s, devid, valid, size, itt_addr);
> +    }
> +
> +    return res;
> +}
> +
> +/*
> + * Current implementation blocks until all
> + * commands are processed
> + */
> +static void 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->dma_as;
> +    MemTxResult res = MEMTX_OK;
> +    uint8_t cmd;
> +
> +    if (!(s->ctlr & ITS_CTLR_ENABLED)) {
> +        return;
> +    }
> +
> +    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);
> +        return;
> +    }
> +
> +    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:
> +            /*
> +             * 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.
> +             * Hence no further processing is required for SYNC command.
> +             */
> +            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 {
> +            /*
> +             * in this implementation,in case of dma read/write error
> +             * we stall the command processing
> +             */
> +            s->creadr = FIELD_DP64(s->creadr, GITS_CREADR, STALLED, 1);
> +            qemu_log_mask(LOG_GUEST_ERROR,
> +                          "%s: %x cmd processing failed!!\n", __func__, cmd);
> +            break;
> +        }
> +    }
> +}
> +
>  static void extract_table_params(GICv3ITSState *s)
>  {
>      uint16_t num_pages = 0;
> @@ -226,6 +515,9 @@ static MemTxResult its_writel(GICv3ITSState *s, hwaddr offset,
>      case GITS_CWRITER:
>          s->cwriter = deposit64(s->cwriter, 0, 32,
>                                 (value & ~R_GITS_CWRITER_RETRY_MASK));
> +        if (s->cwriter != s->creadr) {
> +            process_cmdq(s);
> +        }
>          break;
>      case GITS_CWRITER + 4:
>          s->cwriter = deposit64(s->cwriter, 32, 32,
> @@ -346,6 +638,9 @@ static MemTxResult its_writell(GICv3ITSState *s, hwaddr offset,
>          break;
>      case GITS_CWRITER:
>          s->cwriter = value & ~R_GITS_CWRITER_RETRY_MASK;
> +        if (s->cwriter != s->creadr) {
> +            process_cmdq(s);
> +        }
>          break;
>      case GITS_CREADR:
>      case GITS_TYPER:
> diff --git a/hw/intc/gicv3_internal.h b/hw/intc/gicv3_internal.h
> index d6aaa94e4c..0932a30560 100644
> --- a/hw/intc/gicv3_internal.h
> +++ b/hw/intc/gicv3_internal.h
> @@ -253,6 +253,9 @@ 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)
>  
> @@ -289,6 +292,40 @@ 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_LENGTH                  16
> +#define ICID_MASK                 ((1U << ICID_LENGTH) - 1)
> +FIELD(MAPC, RDBASE, 16, 32)
> +
> +#define RDBASE_PROCNUM_LENGTH        16
> +#define RDBASE_PROCNUM_MASK       ((1ULL << RDBASE_PROCNUM_LENGTH) - 1)
> +
> +#define DEVID_SHIFT                  32
> +#define DEVID_LENGTH                 32
> +#define DEVID_MASK                ((1ULL << DEVID_LENGTH) - 1)
> +
> +/* MAPD command fields */
> +#define ITTADDR_LENGTH               44
> +#define ITTADDR_SHIFT                 8
> +#define ITTADDR_MASK              ((1ULL << ITTADDR_LENGTH) - 1)
> +#define SIZE_MASK                 0x1f
> +
> +#define VALID_SHIFT               63
> +#define VALID_MASK                1ULL
>  
>  /**
>   * Default features advertised by this version of ITS
> 



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

* Re: [PATCH v4 4/8] hw/intc: GICv3 ITS Command processing
  2021-06-02 18:00 ` [PATCH v4 4/8] hw/intc: GICv3 ITS Command processing Shashi Mallela
  2021-06-08 10:45   ` Peter Maydell
@ 2021-06-13 15:55   ` Eric Auger
  2021-06-16 21:02     ` shashi.mallela
  1 sibling, 1 reply; 52+ messages in thread
From: Eric Auger @ 2021-06-13 15:55 UTC (permalink / raw)
  To: Shashi Mallela, peter.maydell, leif, rad; +Cc: qemu-arm, qemu-devel

Hi Shashi,

On 6/2/21 8:00 PM, Shashi Mallela 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            | 334 +++++++++++++++++++++++++++++
>  hw/intc/gicv3_internal.h           |  12 ++
>  include/hw/intc/arm_gicv3_common.h |   2 +
>  3 files changed, 348 insertions(+)
> 
> diff --git a/hw/intc/arm_gicv3_its.c b/hw/intc/arm_gicv3_its.c
> index 6551c577b3..82bb5b84ef 100644
> --- a/hw/intc/arm_gicv3_its.c
> +++ b/hw/intc/arm_gicv3_its.c
> @@ -28,6 +28,13 @@ 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;
Add a comment to explain what this enum stand for. This sounds
misleading to me versus the command IDs. Why don't you use the cmd id
then and add NONE?
> +
>  static uint64_t baser_base_addr(uint64_t value, uint32_t page_sz)
>  {
>      uint64_t result = 0;
> @@ -49,6 +56,315 @@ static uint64_t baser_base_addr(uint64_t value, uint32_t page_sz)
>      return result;
>  }
>  
> +static bool get_cte(GICv3ITSState *s, uint16_t icid, uint64_t *cte,
> +                    MemTxResult *res)
> +{
> +    AddressSpace *as = &s->gicv3->dma_as;
> +    uint64_t l2t_addr;
> +    uint64_t value;
> +    bool valid_l2t;
> +    uint32_t l2t_id;
> +    uint32_t max_l2_entries;
> +    bool status = false;
> +
> +    if (s->ct.indirect) {
> +        l2t_id = icid / (s->ct.page_sz / L1TABLE_ENTRY_SIZE);
> +
> +        value = address_space_ldq_le(as,
> +                                     s->ct.base_addr +
> +                                     (l2t_id * L1TABLE_ENTRY_SIZE),
> +                                     MEMTXATTRS_UNSPECIFIED, res);
> +
> +        if (*res == MEMTX_OK) {
> +            valid_l2t = (value >> VALID_SHIFT) & VALID_MASK;
> +
> +            if (valid_l2t) {
> +                max_l2_entries = s->ct.page_sz / s->ct.entry_sz;
> +
> +                l2t_addr = value & ((1ULL << 51) - 1);
> +
> +                *cte =  address_space_ldq_le(as, l2t_addr +
> +                                    ((icid % max_l2_entries) * GITS_CTE_SIZE),
> +                                    MEMTXATTRS_UNSPECIFIED, res);
> +           }
> +       }
> +    } else {
> +        /* Flat level table */
> +        *cte =  address_space_ldq_le(as, s->ct.base_addr +
> +                                     (icid * GITS_CTE_SIZE),
> +                                      MEMTXATTRS_UNSPECIFIED, res);
> +    }
> +
> +    if (*cte & VALID_MASK) {
> +        status = true;
> +    }
> +
> +    return status;
> +}
> +
> +static MemTxResult update_ite(GICv3ITSState *s, uint32_t eventid, uint64_t dte,
> +                              uint64_t itel, uint32_t iteh)
why not introducing an ite struct instead of the h/l args?
> +{
> +    AddressSpace *as = &s->gicv3->dma_as;
> +    uint64_t itt_addr;
> +    MemTxResult res = MEMTX_OK;
> +
> +    itt_addr = (dte >> 6ULL) & ITTADDR_MASK;
> +    itt_addr <<= ITTADDR_SHIFT; /* 256 byte aligned */
> +
> +    address_space_stq_le(as, itt_addr + (eventid * sizeof(uint64_t)),
> +                         itel, MEMTXATTRS_UNSPECIFIED, &res);
> +
> +    if (res == MEMTX_OK) {
> +        address_space_stl_le(as, itt_addr + ((eventid + sizeof(uint64_t)) *
> +                             sizeof(uint32_t)), iteh, MEMTXATTRS_UNSPECIFIED,
> +                             &res);
> +    }
> +   return res;
> +}
> +
> +static bool get_ite(GICv3ITSState *s, uint32_t eventid, uint64_t dte,
> +                    uint16_t *icid, uint32_t *pIntid, MemTxResult *res)
> +{
> +    AddressSpace *as = &s->gicv3->dma_as;
> +    uint64_t itt_addr;
> +    bool status = false;
> +    uint64_t itel = 0;
> +    uint32_t iteh = 0;
> +
> +    itt_addr = (dte >> 6ULL) & ITTADDR_MASK;
> +    itt_addr <<= ITTADDR_SHIFT; /* 256 byte aligned */
> +
> +    itel = address_space_ldq_le(as, itt_addr + (eventid * sizeof(uint64_t)),
> +                                MEMTXATTRS_UNSPECIFIED, res);
> +
> +    if (*res == MEMTX_OK) {
> +        iteh = address_space_ldl_le(as, itt_addr + ((eventid +
> +                                    sizeof(uint64_t)) * sizeof(uint32_t)),
> +                                    MEMTXATTRS_UNSPECIFIED, res);
> +
> +        if (*res == MEMTX_OK) {
> +            if (itel & VALID_MASK) {
> +                if ((itel >> ITE_ENTRY_INTTYPE_SHIFT) & GITS_TYPE_PHYSICAL) {
> +                    *pIntid = (itel >> ITE_ENTRY_INTID_SHIFT) &
> +                               ITE_ENTRY_INTID_MASK;
> +                    *icid = iteh & ITE_ENTRY_ICID_MASK;
> +                    status = true;
> +                }
> +            }
> +        }
> +    }
> +    return status;
> +}
> +
> +static uint64_t get_dte(GICv3ITSState *s, uint32_t devid, MemTxResult *res)
maybe the code would be more readable if you were returning a strcut for
dte/cte instead of uint64_t. The decoding of the fields would be done
here instead?
> +{
> +    AddressSpace *as = &s->gicv3->dma_as;
> +    uint64_t l2t_addr;
> +    uint64_t value;
> +    bool valid_l2t;
> +    uint32_t l2t_id;
> +    uint32_t max_l2_entries;
> +
> +    if (s->dt.indirect) {
> +        l2t_id = devid / (s->dt.page_sz / L1TABLE_ENTRY_SIZE);
> +
> +        value = address_space_ldq_le(as,
> +                                     s->dt.base_addr +
> +                                     (l2t_id * L1TABLE_ENTRY_SIZE),
> +                                     MEMTXATTRS_UNSPECIFIED, res);
> +
> +        if (*res == MEMTX_OK) {
> +            valid_l2t = (value >> VALID_SHIFT) & VALID_MASK;
> +
> +            if (valid_l2t) {
> +                max_l2_entries = s->dt.page_sz / s->dt.entry_sz;
> +
> +                l2t_addr = value & ((1ULL << 51) - 1);
> +
> +                value =  address_space_ldq_le(as, l2t_addr +
> +                                   ((devid % max_l2_entries) * GITS_DTE_SIZE),
> +                                   MEMTXATTRS_UNSPECIFIED, res);
> +            }
> +        }
> +    } else {
> +        /* Flat level table */
> +        value = address_space_ldq_le(as, s->dt.base_addr +
> +                                     (devid * GITS_DTE_SIZE),
> +                                     MEMTXATTRS_UNSPECIFIED, res);
> +    }
> +
> +    return value;
> +}
I think a common helper could be defined for get_cte and get_dte.
> +
> +static MemTxResult process_int(GICv3ITSState *s, uint64_t value,
> +                               uint32_t offset, ItsCmdType cmd)
this is a bit misleanding as INT is a command. You should rename it I
think. Also it is not really homogeneous with other cmds, ie. you have
process_mapti, process_mapd, process_mac and all the remaining cmds are
handled though this one? At least add a doc comment to explain what it does.
> +{
> +    AddressSpace *as = &s->gicv3->dma_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;
> +    uint64_t itel = 0;
> +    uint32_t iteh = 0;
> +
> +    if (cmd == NONE) {
> +        devid = offset;
> +    } else {
> +        devid = (value >> DEVID_SHIFT) & DEVID_MASK;
> +
> +        offset += NUM_BYTES_IN_DW;
> +        value = address_space_ldq_le(as, s->cq.base_addr + offset,
> +                                     MEMTXATTRS_UNSPECIFIED, &res);
> +    }
> +
> +    if (res != MEMTX_OK) {
> +        return res;
> +    }
> +
> +    eventid = (value & EVENTID_MASK);
> +
> +    dte = get_dte(s, devid, &res);
> +
> +    if (res != MEMTX_OK) {
> +        return res;
> +    }
> +    dte_valid = dte & VALID_MASK;
> +
> +    if (dte_valid) {
> +        max_eventid = (1UL << (((dte >> 1U) & SIZE_MASK) + 1));
THE DTE format is implementation defined, and the decoding is hard to
follow because it is documented somewhere else.
> +
> +        ite_valid = get_ite(s, eventid, dte, &icid, &pIntid, &res);
> +
> +        if (res != MEMTX_OK) {
> +            return res;
> +        }
> +
> +        if (ite_valid) {
> +            cte_valid = get_cte(s, icid, &cte, &res);
> +        }
> +
> +        if (res != MEMTX_OK) {
> +            return res;
> +        }
instead of having this process_int() helper, why not having a helper
doing just the decoding phase, ie.
get_dte -> get_ite -> get_cte and collect the relevant info and collect
error and then keep the actual cmd processing in the switch?
> +    }
> +
> +    if ((devid > s->dt.max_devids) || !dte_valid || !ite_valid ||
> +            !cte_valid || (eventid > max_eventid)) {
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +                      "%s: invalid interrupt translation table attributes "
> +                      "devid %d or eventid %d\n",
> +                      __func__, devid, eventid);
the msg does not necessarily match the error case. You mention ITT issue
whereas the problem may come from invalid DTE, CTE, or even devid.
> +        /*
> +         * in this implementation,in case of error
> +         * we ignore this command and move onto the next
> +         * command in the queue
> +         */
so you don't return an error?
> +    } else {
> +        /*
> +         * Current implementation only supports rdbase == procnum
> +         * Hence rdbase physical address is ignored
> +         */
> +        if (cmd == DISCARD) {
> +            /* remove mapping from interrupt translation table */
> +            res = update_ite(s, eventid, dte, itel, iteh);
iteh and itel always are 0, why not use a struct ite with valid field unset.
> +        }
> +    }
> +
> +    return res;
> +}
> +
> +static MemTxResult process_mapti(GICv3ITSState *s, uint64_t value,
> +                                 uint32_t offset, bool ignore_pInt)
> +{
> +    AddressSpace *as = &s->gicv3->dma_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 itel = 0;
> +    uint32_t iteh = 0;
> +    uint32_t int_spurious = INTID_SPURIOUS;
> +
> +    devid = (value >> DEVID_SHIFT) & DEVID_MASK;
> +    offset += NUM_BYTES_IN_DW;
> +    value = address_space_ldq_le(as, s->cq.base_addr + offset,
> +                                 MEMTXATTRS_UNSPECIFIED, &res);
> +
> +    if (res != MEMTX_OK) {
> +        return 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);
> +
> +    if (res != MEMTX_OK) {
> +        return res;
> +    }
> +
> +    icid = value & ICID_MASK;
> +
> +    dte = get_dte(s, devid, &res);
> +
> +    if (res != MEMTX_OK) {
> +        return res;
> +    }
> +    dte_valid = dte & VALID_MASK;
> +
> +    max_eventid = (1UL << (((dte >> 1U) & SIZE_MASK) + 1));
Again I think you would gain in readibility if get_cte were to return a
struct and you would avoid code duplication.
> +
> +    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)))) {
> +        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 ite entry to interrupt translation table */
> +        itel = (dte_valid & VALID_MASK) | (GITS_TYPE_PHYSICAL <<
> +                                           ITE_ENTRY_INTTYPE_SHIFT);
> +
> +        if (ignore_pInt) {
> +            itel |= (eventid << ITE_ENTRY_INTID_SHIFT);
> +        } else {
> +            itel |= (pIntid << ITE_ENTRY_INTID_SHIFT);
> +        }
> +        itel |= (int_spurious << ITE_ENTRY_INTSP_SHIFT);
> +        iteh |= icid;
> +
> +        res = update_ite(s, eventid, dte, itel, iteh);
> +    }
> +
> +    return res;
> +}
> +
>  static MemTxResult update_cte(GICv3ITSState *s, uint16_t icid, bool valid,
>                                uint64_t rdbase)
>  {
> @@ -295,8 +611,10 @@ static void 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:
>              /*
> @@ -313,10 +631,13 @@ static void 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;
> @@ -472,7 +793,20 @@ static MemTxResult gicv3_its_translation_write(void *opaque, hwaddr offset,
>                                                 uint64_t data, unsigned size,
>                                                 MemTxAttrs attrs)
>  {
> +    GICv3ITSState *s = (GICv3ITSState *)opaque;
>      MemTxResult result = MEMTX_OK;
> +    uint32_t devid = 0;
> +
> +    switch (offset) {
> +    case GITS_TRANSLATER:
> +        if (s->ctlr & ITS_CTLR_ENABLED) {
> +            devid = attrs.requester_id;
> +            result = process_int(s, data, devid, NONE);
> +        }
> +        break;
> +    default:
> +        break;
> +    }
>  
>      return result;
>  }
> diff --git a/hw/intc/gicv3_internal.h b/hw/intc/gicv3_internal.h
> index 0932a30560..ce45cd0ef6 100644
> --- a/hw/intc/gicv3_internal.h
> +++ b/hw/intc/gicv3_internal.h
> @@ -324,6 +324,13 @@ FIELD(MAPC, RDBASE, 16, 32)
>  #define ITTADDR_MASK              ((1ULL << ITTADDR_LENGTH) - 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                1ULL
>  
> @@ -344,6 +351,11 @@ FIELD(MAPC, RDBASE, 16, 32)
>   * vPEID = 16 bits
>   */
>  #define ITS_ITT_ENTRY_SIZE            0xC
> +#define ITE_ENTRY_INTTYPE_SHIFT        1
> +#define ITE_ENTRY_INTID_SHIFT          2
> +#define ITE_ENTRY_INTID_MASK         ((1ULL << 24) - 1)
> +#define ITE_ENTRY_INTSP_SHIFT          26
> +#define ITE_ENTRY_ICID_MASK          ((1ULL << 16) - 1)
>  
>  /* 16 bits EventId */
>  #define ITS_IDBITS                   GICD_TYPER_IDBITS
> diff --git a/include/hw/intc/arm_gicv3_common.h b/include/hw/intc/arm_gicv3_common.h
> index 1fd5cedbbd..0715b0bc2a 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 */
> 
Thanks

Eric



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

* Re: [PATCH v4 6/8] hw/intc: GICv3 redistributor ITS processing
  2021-06-02 18:00 ` [PATCH v4 6/8] hw/intc: GICv3 redistributor ITS processing Shashi Mallela
  2021-06-08 13:57   ` Peter Maydell
@ 2021-06-13 16:26   ` Eric Auger
  2021-06-16 21:02     ` shashi.mallela
  1 sibling, 1 reply; 52+ messages in thread
From: Eric Auger @ 2021-06-13 16:26 UTC (permalink / raw)
  To: Shashi Mallela, peter.maydell, leif, rad; +Cc: qemu-arm, qemu-devel

Hi Shashi,

On 6/2/21 8:00 PM, Shashi Mallela wrote:
> Implemented lpi processing at redistributor to get lpi config info
s/Implemented/Implement here are elsewhere.
> 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                |   9 ++
>  hw/intc/arm_gicv3_common.c         |   1 +
>  hw/intc/arm_gicv3_cpuif.c          |   7 +-
>  hw/intc/arm_gicv3_its.c            |  14 ++-
>  hw/intc/arm_gicv3_redist.c         | 145 +++++++++++++++++++++++++++++
>  hw/intc/gicv3_internal.h           |  10 ++
>  include/hw/intc/arm_gicv3_common.h |  10 ++
>  7 files changed, 190 insertions(+), 6 deletions(-)
> 
> diff --git a/hw/intc/arm_gicv3.c b/hw/intc/arm_gicv3.c
> index d63f8af604..4d19190b9c 100644
> --- a/hw/intc/arm_gicv3.c
> +++ b/hw/intc/arm_gicv3.c
> @@ -165,6 +165,15 @@ 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 && cs->lpivalid) {
> +        if (irqbetter(cs, cs->hpplpi.irq, cs->hpplpi.prio)) {
> +            cs->hppi.irq = cs->hpplpi.irq;
> +            cs->hppi.prio = cs->hpplpi.prio;
> +            cs->hppi.grp = cs->hpplpi.grp;
> +            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_common.c b/hw/intc/arm_gicv3_common.c
> index 53dea2a775..223db16fec 100644
> --- a/hw/intc/arm_gicv3_common.c
> +++ b/hw/intc/arm_gicv3_common.c
> @@ -435,6 +435,7 @@ static void arm_gicv3_common_reset(DeviceState *dev)
>          memset(cs->gicr_ipriorityr, 0, sizeof(cs->gicr_ipriorityr));
>  
>          cs->hppi.prio = 0xff;
> +        cs->hpplpi.prio = 0xff;
>  
>          /* State in the CPU interface must *not* be reset here, because it
>           * is part of the CPU's reset domain, not the GIC device's.
> diff --git a/hw/intc/arm_gicv3_cpuif.c b/hw/intc/arm_gicv3_cpuif.c
> index 81f94c7f4a..5be3efaa3f 100644
> --- a/hw/intc/arm_gicv3_cpuif.c
> +++ b/hw/intc/arm_gicv3_cpuif.c
> @@ -898,10 +898,12 @@ static void icc_activate_irq(GICv3CPUState *cs, int irq)
>          cs->gicr_iactiver0 = deposit32(cs->gicr_iactiver0, irq, 1, 1);
>          cs->gicr_ipendr0 = deposit32(cs->gicr_ipendr0, irq, 1, 0);
>          gicv3_redist_update(cs);
> -    } else {
> +    } else if (irq < GICV3_LPI_INTID_START) {
>          gicv3_gicd_active_set(cs->gic, irq);
>          gicv3_gicd_pending_clear(cs->gic, irq);
>          gicv3_update(cs->gic, irq, 1);
> +    } else {
> +        gicv3_redist_lpi_pending(cs, irq, 0);
>      }
>  }
>  
> @@ -1317,7 +1319,8 @@ static void icc_eoir_write(CPUARMState *env, const ARMCPRegInfo *ri,
>      trace_gicv3_icc_eoir_write(is_eoir0 ? 0 : 1,
>                                 gicv3_redist_affid(cs), value);
>  
> -    if (irq >= cs->gic->num_irq) {
> +    if ((irq >= cs->gic->num_irq) &&  (!(cs->gic->lpi_enable &&
> +        (irq >= GICV3_LPI_INTID_START)))) {
>          /* This handles two cases:
>           * 1. If software writes the ID of a spurious interrupt [ie 1020-1023]
>           * to the GICC_EOIR, the GIC ignores that write.
> diff --git a/hw/intc/arm_gicv3_its.c b/hw/intc/arm_gicv3_its.c
> index 0a978cf55b..e0fbd4041f 100644
> --- a/hw/intc/arm_gicv3_its.c
> +++ b/hw/intc/arm_gicv3_its.c
> @@ -211,6 +211,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;
>      uint64_t itel = 0;
>      uint32_t iteh = 0;
>  
> @@ -267,10 +268,15 @@ static MemTxResult process_int(GICv3ITSState *s, uint64_t value,
>           * command in the queue
>           */
>      } else {
> -        /*
> -         * Current implementation only supports rdbase == procnum
> -         * Hence rdbase physical address is ignored
> -         */
> +        rdbase = (cte >> 1U) & RDBASE_PROCNUM_MASK;
> +        assert(rdbase <= s->gicv3->num_cpu);
> +
> +        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 */
>              res = update_ite(s, eventid, dte, itel, iteh);
> diff --git a/hw/intc/arm_gicv3_redist.c b/hw/intc/arm_gicv3_redist.c
> index fb9a4ee3cc..bfc6e4e9b9 100644
> --- a/hw/intc/arm_gicv3_redist.c
> +++ b/hw/intc/arm_gicv3_redist.c
> @@ -255,6 +255,11 @@ 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 */
> +                cs->lpivalid = false;
> +                cs->hpplpi.prio = 0xff;
> +                gicv3_redist_update_lpi(cs);
> +                gicv3_redist_update(cs);
>              } else {
>                  cs->gicr_ctlr &= ~GICR_CTLR_ENABLE_LPIS;
>              }
> @@ -534,6 +539,146 @@ MemTxResult gicv3_redist_write(void *opaque, hwaddr offset, uint64_t data,
>      return r;
>  }
>  
> +void gicv3_redist_update_lpi(GICv3CPUState *cs)
> +{
> +    /*
> +     * This function scans the LPI pending table and for each pending
> +     * LPI, reads the corresponding entry from LPI configuration table
> +     * to extract the priority info and determine if the LPI priority
> +     * is lower than the current high priority interrupt.If yes, update> +     * high priority pending interrupt to that of LPI.

"update high priority pending interrupt to that of LPI" may need some
rewording
> +     */
> +    AddressSpace *as = &cs->gic->dma_as;
> +    uint64_t lpict_baddr, lpipt_baddr;
> +    uint32_t pendt_size = 0;
> +    uint8_t lpite;
> +    uint8_t prio, pend;
> +    int i;
> +    uint64_t idbits;
> +
> +    idbits = MIN(FIELD_EX64(cs->gicr_propbaser, GICR_PROPBASER, IDBITS),
> +                 GICD_TYPER_IDBITS);
> +
> +    if ((!cs->gicr_ctlr & GICR_CTLR_ENABLE_LPIS) || !cs->gicr_propbaser ||
> +        !cs->gicr_pendbaser || (idbits < GICR_PROPBASER_IDBITS_THRESHOLD)) {
> +        return;
> +    }
> +
> +    lpict_baddr = cs->gicr_propbaser & R_GICR_PROPBASER_PHYADDR_MASK;
> +
> +    lpipt_baddr = cs->gicr_pendbaser & R_GICR_PENDBASER_PHYADDR_MASK;
> +
> +    /* Determine the highest priority pending interrupt among LPIs */
> +    pendt_size = (1ULL << (idbits + 1));
> +
> +    for (i = 0; i < pendt_size / 8; 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));
> +
> +            if (!(lpite & LPI_CTE_ENABLED)) {
> +                continue;
> +            }
> +
> +            if (cs->gic->gicd_ctlr & GICD_CTLR_DS) {
> +                prio = lpite & LPI_PRIORITY_MASK;
> +            } else {
> +                prio = lpite & LPI_SPRIORITY_MASK;
> +            }
> +
> +            if (prio <= cs->hpplpi.prio) {
> +                cs->hpplpi.irq = GICV3_LPI_INTID_START + i;
> +                cs->hpplpi.prio = prio;
> +                /* LPIs are always non-secure Grp1 interrupts */
> +                cs->hpplpi.grp = GICV3_G1NS;
> +                cs->lpivalid = true;
> +            }
> +        }
> +    }
> +}
> +
nit: add a comment to explain what the function does, that's not
straightforward gievn its name.
> +void gicv3_redist_lpi_pending(GICv3CPUState *cs, int irq, int level)
> +{
> +    AddressSpace *as = &cs->gic->dma_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 = cs->gicr_pendbaser & R_GICR_PENDBASER_PHYADDR_MASK;
> +
> +    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));
> +        }
> +    }
> +    cs->lpivalid = false;
> +    cs->hpplpi.prio = 0xff;
> +    gicv3_redist_update_lpi(cs);
> +}
> +
> +void gicv3_redist_process_lpi(GICv3CPUState *cs, int irq, int level)
> +{
> +    AddressSpace *as = &cs->gic->dma_as;
> +    uint64_t lpict_baddr;
> +    uint8_t lpite;
> +    uint64_t idbits;
> +
> +    idbits = MIN(FIELD_EX64(cs->gicr_propbaser, GICR_PROPBASER, IDBITS),
> +                 GICD_TYPER_IDBITS);
> +
> +    if ((!cs->gicr_ctlr & GICR_CTLR_ENABLE_LPIS) || !cs->gicr_propbaser ||
> +         !cs->gicr_pendbaser || (idbits < GICR_PROPBASER_IDBITS_THRESHOLD) ||
> +         (irq > (1ULL << (idbits + 1)))) {
> +        return;
> +    }
> +
> +    lpict_baddr = cs->gicr_propbaser & R_GICR_PROPBASER_PHYADDR_MASK;
> +
> +    /* 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 91dbe01176..bcbccba573 100644
> --- a/hw/intc/gicv3_internal.h
> +++ b/hw/intc/gicv3_internal.h
> @@ -308,6 +308,13 @@ 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 LPI_SPRIORITY_MASK        0x7e
> +
>  #define GITS_CMDQ_ENTRY_SIZE               32
>  #define NUM_BYTES_IN_DW                     8
>  
> @@ -452,6 +459,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);
> +void 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);
>  
> diff --git a/include/hw/intc/arm_gicv3_common.h b/include/hw/intc/arm_gicv3_common.h
> index c1348cc60a..5d839da9c9 100644
> --- a/include/hw/intc/arm_gicv3_common.h
> +++ b/include/hw/intc/arm_gicv3_common.h
> @@ -204,6 +204,16 @@ struct GICv3CPUState {
>       * real state above; it doesn't need to be migrated.
>       */
>      PendingIrq hppi;
> +
> +    /*
> +     * Current highest priority pending lpi for this CPU.
> +     * This is cached information that can be recalculated from the
> +     * real state above; it doesn't need to be migrated.
> +     */
> +    PendingIrq hpplpi;
> +
> +    bool lpivalid; /* current highest priority lpi validity status */
> +
>      /* This is temporary working state, to avoid a malloc in gicv3_update() */
>      bool seenbetter;
>  };>

Thanks

Eric



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

* Re: [PATCH v4 6/8] hw/intc: GICv3 redistributor ITS processing
  2021-06-11  8:30       ` Peter Maydell
@ 2021-06-15  2:23         ` Shashi Mallela
  0 siblings, 0 replies; 52+ messages in thread
From: Shashi Mallela @ 2021-06-15  2:23 UTC (permalink / raw)
  To: Peter Maydell
  Cc: Leif Lindholm, QEMU Developers, qemu-arm, Radoslaw Biernacki

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



On Jun 11 2021, at 4:30 am, Peter Maydell <peter.maydell@linaro.org> wrote:
> On Fri, 11 Jun 2021 at 00:39, Shashi Mallela <shashi.mallela@linaro.org> wrote:
> >
> > Have addressed all comments except the ones with responses(inline) below:-
> >
> > On Jun 8 2021, at 9:57 am, Peter Maydell <peter.maydell@linaro.org> wrote:
> >
> > > + cs->lpivalid = false;
> > > + cs->hpplpi.prio = 0xff;
> > > + gicv3_redist_update_lpi(cs);
> >
> > You can avoid doing a full update a lot of the time:
> > * if this LPI is worse than the current value in hpplpi
> > (where by "worse" I mean lower-priority by the same kind of
> > comparison irqbetter() does) then we haven't changed the best-available
> > pending LPI, so we don't need to do an update
> > * if we set the pending bit to 1 and the LPI is enabled and the priority
> > of this LPI is better than the current hpplpi, then we know this LPI
> > is now the best, so we can just set hpplpi.prio and .irq without
> > doing a full rescan
> > * if we didn't actually change the value of the pending bit, we
> > don't need to do an update (you get this for free if you take the
> > simplification suggestion I make above, which does an early-return
> > in the "no change" case)
> >
> > > Accepted the code simplification,but by not calling gicv3_redist_update_lpi(cs) here ,the scenario of an activated LPI fails because
> > this LPI's priority (which could be lower than current hpplpi) is never checked/updated and gicv3_redist_update_noirqset() fails to present a valid active high priority LPI(if applicable) to the cpu,since it is always checking against a stale hpplpi info.
>
> If the LPI is lower priority (higher number) than the current
> hpplpi then it would not change the existing hpplpi info in
> a full-scan. If the LPI being activated is higher priority
> (lower number) than the current hpplpi then that is my point 2 above,
> and we set it as the hpplpi without needing the full-scan. And for
> the other cases (eg highest-priority LPI being deactivated) we
> should fall through to the default "call update_lpi" case.
>
> So I don't really understand why this wouldn't work.
> -- PMM

Have got this working as per comments.Please ignore my last comment.

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

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

* Re: [PATCH v4 2/8] hw/intc: GICv3 ITS register definitions added
  2021-06-12  6:08   ` Eric Auger
@ 2021-06-16 21:02     ` shashi.mallela
  2021-06-21  9:51       ` Eric Auger
  0 siblings, 1 reply; 52+ messages in thread
From: shashi.mallela @ 2021-06-16 21:02 UTC (permalink / raw)
  To: Eric Auger, peter.maydell, leif, rad; +Cc: qemu-arm, qemu-devel

Hi Eric,

Please find my responses inline (below):-

On Sat, 2021-06-12 at 08:08 +0200, Eric Auger wrote:
> 
> On 6/2/21 8:00 PM, Shashi Mallela 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                | 335
> > +++++++++++++++++++++++++
> >  hw/intc/gicv3_internal.h               |  28 ++-
> >  include/hw/intc/arm_gicv3_common.h     |   3 +
> >  include/hw/intc/arm_gicv3_its_common.h |  30 +++
> >  4 files changed, 395 insertions(+), 1 deletion(-)
> > 
> > diff --git a/hw/intc/arm_gicv3_its.c b/hw/intc/arm_gicv3_its.c
> > index 545cda3665..af60f19c98 100644
> > --- a/hw/intc/arm_gicv3_its.c
> > +++ b/hw/intc/arm_gicv3_its.c
> > @@ -28,6 +28,157 @@ struct GICv3ITSClass {
> >      void (*parent_reset)(DeviceState *dev);
> >  };
> >  
> > +static uint64_t baser_base_addr(uint64_t value, uint32_t page_sz)
> > +{
> > +    uint64_t result = 0;
> > +
> > +    switch (page_sz) {
> > +    case GITS_ITT_PAGE_SIZE_0:
> > +    case GITS_ITT_PAGE_SIZE_1:
> > +        result = value & R_GITS_BASER_PHYADDR_MASK;
> Use FIELD_EX64 as well for homogeneity?
Done
> > +        break;
> > +
> > +    case GITS_ITT_PAGE_SIZE_2:
> > +        result = value & R_GITS_BASER_PHYADDRL_64K_MASK;
> here as well?
Done
> > +        result |= FIELD_EX64(value, GITS_BASER, PHYADDRH_64K) <<
> > 48;
> > +        break;
> > +
> > +    default:
> > +        break;
> > +    }
> > +    return result;
> > +}
> > +
> > +static void extract_table_params(GICv3ITSState *s)
> > +{
> > +    uint16_t num_pages = 0;
> > +    uint8_t  page_sz_type;
> > +    uint8_t type;
> > +    uint32_t page_sz = 0;
> > +    uint64_t value;
> > +
> > +    for (int i = 0; i < 8; i++) {
> > +        value = s->baser[i];
> > +
> > +        if (!value) {
> > +            continue;
> > +        }
> > +
> > +        page_sz_type = FIELD_EX64(value, GITS_BASER, PAGESIZE);
> > +
> > +        switch (page_sz_type) {
> > +        case 0:
> > +            page_sz = GITS_ITT_PAGE_SIZE_0;
> > +            break;
> > +
> > +        case 1:
> > +            page_sz = GITS_ITT_PAGE_SIZE_1;
> > +            break;
> > +
> > +        case 2:
> > +        case 3:
> > +            page_sz = GITS_ITT_PAGE_SIZE_2;
> > +            break;
> > +
> > +        default:
> > +            g_assert_not_reached();
> > +        }
> > +
> > +        num_pages = FIELD_EX64(value, GITS_BASER, SIZE);
>  + 1 directly? and remove num_pages + 1 below.
Done
> > +
> > +        type = FIELD_EX64(value, GITS_BASER, TYPE);
> > +
> > +        switch (type) {
> > +
> > +        case GITS_ITT_TYPE_DEVICE:
> > +            memset(&s->dt, 0 , sizeof(s->dt));
> > +            s->dt.valid = FIELD_EX64(value, GITS_BASER, VALID);
> > +
> > +            if (!s->dt.valid) {
> > +                return;
> > +            }
> > +
> > +            s->dt.page_sz = page_sz;
> > +            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));
> > +
> > +            s->dt.base_addr = baser_base_addr(value, page_sz);
> > +
> > +            break;
> > +
> > +        case GITS_ITT_TYPE_COLLECTION:
> > +            memset(&s->ct, 0 , sizeof(s->ct));
> > +            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) {
> > +                return;
> as this is an helper routine, I think it would be better to have this
> check in the caller. Also you reset ct above.
The idea here was to keep all the GITS_BASER fields parsing and
extraction in one place in this function without the caller (like
its_writel) having to know the GITS_BASER fields format and thereby
split the logic between the caller and this function 
> > +            }
> > +
> > +            s->ct.page_sz = page_sz;
> > +            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);
> > +            }
> > +
> > +            s->ct.base_addr = baser_base_addr(value, page_sz);
> > +
> > +            break;
> > +
> > +        default:
> > +            break;
> > +        }
> > +    }
> > +}
> > +
> > +static void extract_cmdq_params(GICv3ITSState *s)
> > +{
> > +    uint16_t num_pages = 0;
> > +    uint64_t value = s->cbaser;
> > +
> > +    num_pages = FIELD_EX64(value, GITS_CBASER, SIZE);
> + 1
> > +
> > +    memset(&s->cq, 0 , sizeof(s->cq));
> > +    s->cq.valid = FIELD_EX64(value, GITS_CBASER, VALID);
> > +
> > +    if (s->cq.valid) {
> > +        s->cq.max_entries = ((num_pages + 1) *
> > GITS_ITT_PAGE_SIZE_0) /
> nit: use of GITS_ITT_PAGE_SIZE_0 is misleading as ITT stands for
> interrupt translation table and does not relate to CMDQ. Use 4K
> define
> instead.
changed the names to GITS_PAGE_SIZE_4K/16K/64K
> > +                             GITS_CMDQ_ENTRY_SIZE;
> > +        s->cq.base_addr = FIELD_EX64(value, GITS_CBASER, PHYADDR);
> > +        s->cq.base_addr <<= R_GITS_CBASER_PHYADDR_SHIFT;
> > +    }
> > +}
> > +
> >  static MemTxResult gicv3_its_translation_write(void *opaque,
> > hwaddr offset,
> >                                                 uint64_t data,
> > unsigned size,
> >                                                 MemTxAttrs attrs)
> > @@ -41,7 +192,73 @@ static MemTxResult its_writel(GICv3ITSState *s,
> > hwaddr offset,
> >                                uint64_t value, MemTxAttrs attrs)
> >  {
> >      MemTxResult result = MEMTX_OK;
> > +    int index;
> >  
> > +    switch (offset) {
> > +    case GITS_CTLR:
> > +        s->ctlr |= (value & ~(s->ctlr));
> > +
> > +        if (s->ctlr & ITS_CTLR_ENABLED) {
> > +            extract_table_params(s);
> > +            extract_cmdq_params(s);
> > +            s->creadr = 0;
> The KVM code also checks the he CBASER and
> device/collection BASER are valid
> we do check CBASER and device/collection BASER are valid in this
> implementation too (via extract_cmdq_params & extract_table_params)
> To be further checked in subsequent patches:
> - cache invalidation when turning off
> - process commands if turned on?
> - any cmd lock
> 
> > +        }
> > +        break;
> > +    case GITS_CBASER:
> > +        /*
> > +         * IMPDEF choice:- 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:
> > +        /*
> > +         * IMPDEF choice:- GITS_CBASER register becomes RO if ITS
> > is
> > +         *                 already enabled
> > +         */
> > +        if (!(s->ctlr & ITS_CTLR_ENABLED)) {
> > +            s->cbaser = deposit64(s->cbaser, 32, 32, value);
> you need to reset creadr here also
Done
> 
> also CWRITER should be reset to CREADR.
Done
> KVM code comment:
> /*
>  * CWRITER is architecturally UNKNOWN on reset, but we need to reset
>  * it to CREADR to make sure we start with an empty command buffer.
>  */
> 
> > +        }> +        break;
> > +    case GITS_CWRITER:
> > +        s->cwriter = deposit64(s->cwriter, 0, 32,
> > +                               (value &
> > ~R_GITS_CWRITER_RETRY_MASK));
> how do you implement the overflow case?
> "If GITS_CWRITER is written with a value outside of the valid range
> specified by
> GITS_CBASER.Physical_Address and GITS_CBASER.Size, behavior is a
> CONSTRAINED UNPREDICTABLE choice"
> for info the KVM code does not write the actual reg
we write the reg and log a guest error
> further check: process command?
> 
> > +        break;
> > +    case GITS_CWRITER + 4:
> > +        s->cwriter = deposit64(s->cwriter, 32, 32,
> > +                               (value &
> > ~R_GITS_CWRITER_RETRY_MASK));
> > +        break;
> > +    case GITS_BASER ... GITS_BASER + 0x3f:
> > +        /*
> > +         * IMPDEF choice:- GITS_BASERn register becomes RO if ITS
> > is
> > +         *                 already enabled
> > +         */
> > +        if (!(s->ctlr & ITS_CTLR_ENABLED)) {
> > +            index = (offset - GITS_BASER) / 8;
> > +
> > +            if (offset & 7) {
> > +                s->baser[index] = deposit64(s->baser[index], 32,
> > 32,
> > +                                            (value &
> > ~GITS_BASER_VAL_MASK));
> > +            } else {
> > +                s->baser[index] = deposit64(s->baser[index], 0,
> > 32,
> > +                                            (value &
> > ~GITS_BASER_VAL_MASK));
> > +            }
> > +        }
> > +        break;
> > +    case GITS_IIDR:
> > +    case GITS_IDREGS ... GITS_IDREGS + 0x2f:
> > +        /* 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;
> >  }
> >  
> > @@ -49,7 +266,55 @@ 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 = gicv3_iidr();
> > +        break;
> > +    case GITS_IDREGS ... GITS_IDREGS + 0x2f:
> > +        /* ID registers */
> > +        *data = gicv3_idreg(offset - GITS_IDREGS);
> I am not sure those are the same as the gicv3
Yes they are, and consistent with the distributor,redistributor gicv3
idregs implementation too in qemu (as indicated in previous patch
review comments)
> on KVM we have
>         case GITS_PIDR0:
>                 return 0x92;    /* part number, bits[7:0] */
>         case GITS_PIDR1:
>                 return 0xb4;    /* part number, bits[11:8] */
>         case GITS_PIDR2:
>                 return GIC_PIDR2_ARCH_GICv3 | 0x0b;
>         case GITS_PIDR4:
>                 return 0x40;    /* This is a 64K software visible
> page */
>         /* The following are the ID registers for (any) GIC. */
>         case GITS_CIDR0:
>                 return 0x0d;
>         case GITS_CIDR1:
>                 return 0xf0;
>         case GITS_CIDR2:
>                 return 0x05;
>         case GITS_CIDR3:
>                 return 0xb1;
> 
> 
> > +        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 = extract64(s->baser[index], 32, 32);
> > +        } else {
> > +            *data = extract64(s->baser[index], 0, 32);
> > +        }
> > +        break;
> > +    default:
> > +        result = MEMTX_ERROR;
> > +        break;
> > +    }
> >      return result;
> >  }
> >  
> > @@ -57,7 +322,42 @@ 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:
> > +        /*
> > +         * IMPDEF choice:- GITS_BASERn register becomes 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);
> > +        }
> > +        break;
> > +    case GITS_CBASER:
> > +        /*
> > +         * IMPDEF choice:- GITS_CBASER register becomes RO if ITS
> > is
> > +         *                 already enabled
> > +         */
> > +        if (!(s->ctlr & ITS_CTLR_ENABLED)) {
> > +            s->cbaser = value;
> s->creadr = 0;
> cwriter = creader?
Done
> > +        }
> > +        break;
> > +    case GITS_CWRITER:
> > +        s->cwriter = value & ~R_GITS_CWRITER_RETRY_MASK;
> > +        break;
> > +    case GITS_CREADR:
> RO if GICD_CTLR.DS = 0
> On KVM side the write access is implemented
Done
> > +    case GITS_TYPER:
> > +        /* 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;
> >  }
> >  
> > @@ -65,7 +365,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;
> >  }
> >  
> > @@ -162,6 +484,9 @@ static void gicv3_arm_its_realize(DeviceState
> > *dev, Error **errp)
> >      gicv3_its_init_mmio(s, &gicv3_its_control_ops,
> > &gicv3_its_translation_ops);
> >  
> >      if (s->gicv3->cpu->gicr_typer & GICR_TYPER_PLPIS) {
> > +        address_space_init(&s->gicv3->dma_as, s->gicv3->dma,
> > +                           "gicv3-its-sysmem");
> > +
> >          /* set the ITS default features supported */
> >          s->typer = FIELD_DP64(s->typer, GITS_TYPER, PHYSICAL,
> >                                GITS_TYPE_PHYSICAL);
> > @@ -208,6 +533,14 @@ static void gicv3_its_reset(DeviceState *dev)
> >      }
> >  }
> >  
> > +static void gicv3_its_post_load(GICv3ITSState *s)
> > +{
> > +    if (s->ctlr & ITS_CTLR_ENABLED) {
> > +        extract_table_params(s);
> > +        extract_cmdq_params(s);
> > +    }
> > +}
> > +
> >  static Property gicv3_its_props[] = {
> >      DEFINE_PROP_LINK("parent-gicv3", GICv3ITSState, gicv3, "arm-
> > gicv3",
> >                       GICv3State *),
> > @@ -218,10 +551,12 @@ static void gicv3_its_class_init(ObjectClass
> > *klass, void *data)
> >  {
> >      DeviceClass *dc = DEVICE_CLASS(klass);
> >      GICv3ITSClass *ic = ARM_GICV3_ITS_CLASS(klass);
> > +    GICv3ITSCommonClass *icc = ARM_GICV3_ITS_COMMON_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);
> > +    icc->post_load = gicv3_its_post_load;
> >  }
> >  
> >  static const TypeInfo gicv3_its_info = {
> > diff --git a/hw/intc/gicv3_internal.h b/hw/intc/gicv3_internal.h
> > index e0b06930a7..d6aaa94e4c 100644
> > --- a/hw/intc/gicv3_internal.h
> > +++ b/hw/intc/gicv3_internal.h
> > @@ -238,7 +238,7 @@ 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, PHYADDRH_64K, 12, 4)
> >  FIELD(GITS_BASER, ENTRYSIZE, 48, 5)
> >  FIELD(GITS_BASER, OUTERCACHE, 53, 3)
> >  FIELD(GITS_BASER, TYPE, 56, 3)
> > @@ -246,6 +246,17 @@ FIELD(GITS_BASER, INNERCACHE, 59, 3)
> >  FIELD(GITS_BASER, INDIRECT, 62, 1)
> >  FIELD(GITS_BASER, VALID, 63, 1)
> >  
> > +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_CWRITER, RETRY, 0, 1)
> > +FIELD(GITS_CWRITER, OFFSET, 5, 15)
> > +
> > +FIELD(GITS_CTLR, ENABLED, 0, 1)
> >  FIELD(GITS_CTLR, QUIESCENT, 31, 1)
> >  
> >  FIELD(GITS_TYPER, PHYSICAL, 0, 1)
> > @@ -257,6 +268,13 @@ FIELD(GITS_TYPER, PTA, 19, 1)
> >  FIELD(GITS_TYPER, CIDBITS, 32, 4)
> >  FIELD(GITS_TYPER, CIL, 36, 1)
> >  
> > +#define GITS_IDREGS           0xFFD0
> > +
> > +#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
> > @@ -264,6 +282,14 @@ FIELD(GITS_TYPER, CIL, 36, 1)
> >  #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
> Why not naming _4K 16K 64K instead of _0, 1, 2?
Done,as indicated above
> > +
> > +#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..1fd5cedbbd 100644
> > --- a/include/hw/intc/arm_gicv3_common.h
> > +++ b/include/hw/intc/arm_gicv3_common.h
> > @@ -226,6 +226,9 @@ struct GICv3State {
> >      int dev_fd; /* kvm device fd if backed by kvm vgic support */
> >      Error *migration_blocker;
> >  
> > +    MemoryRegion *dma;
> > +    AddressSpace dma_as;
> > +
> >      /* Distributor */
> >  
> >      /* for a GIC with the security extensions the NS banked
> > version of this
> > diff --git a/include/hw/intc/arm_gicv3_its_common.h
> > b/include/hw/intc/arm_gicv3_its_common.h
> > index 65d1191db1..78b1ba7e6b 100644
> > --- a/include/hw/intc/arm_gicv3_its_common.h
> > +++ b/include/hw/intc/arm_gicv3_its_common.h
> > @@ -41,6 +41,32 @@
> >  
> >  #define GITS_TRANSLATER  0x0040
> >  
> > +typedef struct {
> > +    bool valid;
> > +    bool indirect;
> > +    uint16_t entry_sz;
> > +    uint32_t page_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 page_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;
> >  
> > @@ -63,6 +89,10 @@ struct GICv3ITSState {
> >      uint64_t creadr;
> >      uint64_t baser[8];
> >  
> > +    DevTableDesc  dt;
> > +    CollTableDesc ct;
> > +    CmdQDesc      cq;
> > +
> >      Error *migration_blocker;
> >  };
> Thanks
> 
> Eric
> >  
> > 






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

* Re: [PATCH v4 3/8] hw/intc: GICv3 ITS command queue framework
  2021-06-13 14:13   ` Eric Auger
@ 2021-06-16 21:02     ` shashi.mallela
  2021-06-21 10:03       ` Eric Auger
  0 siblings, 1 reply; 52+ messages in thread
From: shashi.mallela @ 2021-06-16 21:02 UTC (permalink / raw)
  To: Eric Auger, peter.maydell, leif, rad; +Cc: qemu-arm, qemu-devel

Hi Eric,

Please find my responses inline (below):-

On Sun, 2021-06-13 at 16:13 +0200, Eric Auger wrote:
> Hi Sashi,
> 
> On 6/2/21 8:00 PM, Shashi Mallela 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  | 295
> > +++++++++++++++++++++++++++++++++++++++
> >  hw/intc/gicv3_internal.h |  37 +++++
> >  2 files changed, 332 insertions(+)
> > 
> > diff --git a/hw/intc/arm_gicv3_its.c b/hw/intc/arm_gicv3_its.c
> > index af60f19c98..6551c577b3 100644
> > --- a/hw/intc/arm_gicv3_its.c
> > +++ b/hw/intc/arm_gicv3_its.c
> > @@ -49,6 +49,295 @@ static uint64_t baser_base_addr(uint64_t value,
> > uint32_t page_sz)
> >      return result;
> >  }
> >  
> > +static MemTxResult update_cte(GICv3ITSState *s, uint16_t icid,
> > bool valid,
> > +                              uint64_t rdbase)
> > +{
> > +    AddressSpace *as = &s->gicv3->dma_as;
> > +    uint64_t value;
> > +    uint64_t l2t_addr;
> > +    bool valid_l2t;
> > +    uint32_t l2t_id;
> > +    uint32_t max_l2_entries;
> > +    uint64_t cte = 0;
> > +    MemTxResult res = MEMTX_OK;
> > +
> > +    if (!s->ct.valid) {
> Isn't it a guest log error case. Also you return MEMTX_OK in that
> case.
> Is that what you want?
Yes,because the current implementation treats all command specific
errors as "ignored" and moves onto next command in the queue.MEMTX
return values are significant for dma read/write status and in case of
error we stall the command processing 
> > +        return res;
> > +    }
> > +
> > +    if (valid) {
> > +        /* add mapping entry to collection table */
> > +        cte = (valid & VALID_MASK) |
> > +              ((rdbase & RDBASE_PROCNUM_MASK) << 1ULL);
> Do you really need to sanitize rdbase again?
Not required,have rectified it.
> > +    }
> > +
> > +    /*
> > +     * 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.
> > +     */
> > +    if (s->ct.indirect) {
> > +        l2t_id = icid / (s->ct.page_sz / L1TABLE_ENTRY_SIZE);
> > +
> > +        value = address_space_ldq_le(as,
> > +                                     s->ct.base_addr +
> > +                                     (l2t_id *
> > L1TABLE_ENTRY_SIZE),
> > +                                     MEMTXATTRS_UNSPECIFIED,
> > &res);
> > +
> > +        if (res != MEMTX_OK) {
> > +            return res;
> > +        }
> > +
> > +        valid_l2t = (value >> VALID_SHIFT) & VALID_MASK;
> > +
> > +        if (valid_l2t) {
> > +            max_l2_entries = s->ct.page_sz / s->ct.entry_sz;
> > +
> > +            l2t_addr = value & ((1ULL << 51) - 1);
> > +
> > +            address_space_stq_le(as, l2t_addr +
> > +                                 ((icid % max_l2_entries) *
> > GITS_CTE_SIZE),
> > +                                 cte, MEMTXATTRS_UNSPECIFIED,
> > &res);
> > +        }
> > +    } else {
> > +        /* Flat level table */
> > +        address_space_stq_le(as, s->ct.base_addr + (icid *
> > GITS_CTE_SIZE),
> > +                             cte, MEMTXATTRS_UNSPECIFIED, &res);
> > +    }
> > +    return res;
> > +}
> > +
> > +static MemTxResult process_mapc(GICv3ITSState *s, uint32_t offset)
> > +{
> > +    AddressSpace *as = &s->gicv3->dma_as;
> > +    uint16_t icid;
> > +    uint64_t rdbase;
> > +    bool valid;
> > +    MemTxResult res = MEMTX_OK;
> > +    uint64_t value;
> > +
> > +    offset += NUM_BYTES_IN_DW;
> > +    offset += NUM_BYTES_IN_DW;
> May be relevant to add some trace points for debuggability.
Probably the trace functionality for ITS can be taken up as a seperate
task/feature TODO.
> > +
> > +    value = address_space_ldq_le(as, s->cq.base_addr + offset,
> > +                                 MEMTXATTRS_UNSPECIFIED, &res);
> > +
> > +    if (res != MEMTX_OK) {
> > +        return res;
> > +    }
> > +
> > +    icid = value & ICID_MASK;
> > +
> > +    rdbase = (value >> R_MAPC_RDBASE_SHIFT) & RDBASE_PROCNUM_MASK;
> usually the mask is applied before the shift.
Here we are extracting only 16 bit rdbase(processor number) value by
masking with RDBASE_PROCNUM_MASK only after we have right shifted the
rdbase offset from the 64 bit DW value.
As an alternative,I could have used rdbase = (value &
R_MAPC_RDBASE_MASK) to first extract the 32 bits rdbase value from DW
and then later mask again with RDBASE_PROCNUM_MASK to narrow it down to
16 bit rdbase(processor number).
> > +
> > +    valid = (value >> VALID_SHIFT) & VALID_MASK;
> use FIELD, see below
> > +
> > +    if ((icid > s->ct.max_collids) || (rdbase > s->gicv3-
> > >num_cpu)) {
> you also need to check against ITS_CIDBITS limit?
CIDBITS limits is being checked through the s->ct.max_collids member
above
> > +        qemu_log_mask(LOG_GUEST_ERROR,
> > +                      "ITS MAPC: invalid collection table
> > attributes "
> > +                      "icid %d rdbase %lu\n",  icid, rdbase);
> > +        /*
> > +         * in this implementation,in case of error
> > +         * we ignore this command and move onto the next
> > +         * command in the queue
> spec says a command error occurs in that case.
Yes,we chose to ignore the  error'ed command and move onto the next one
in the queue as per command error options in the spec
> > +         */
> > +    } else {
> > +        res = update_cte(s, icid, valid, rdbase);
> > +    }
> > +
> > +    return res;
> > +}
> > +
> > +static MemTxResult update_dte(GICv3ITSState *s, uint32_t devid,
> > bool valid,
> > +                              uint8_t size, uint64_t itt_addr)
> > +{
> > +    AddressSpace *as = &s->gicv3->dma_as;
> > +    uint64_t value;
> > +    uint64_t l2t_addr;
> > +    bool valid_l2t;
> > +    uint32_t l2t_id;
> > +    uint32_t max_l2_entries;
> > +    uint64_t dte = 0;
> > +    MemTxResult res = MEMTX_OK;
> > +
> > +    if (s->dt.valid) {
> > +        if (valid) {
> > +            /* add mapping entry to device table */
> > +            dte = (valid & VALID_MASK) |
> > +                  ((size & SIZE_MASK) << 1U) |
> > +                  ((itt_addr & ITTADDR_MASK) << 6ULL);
> > +        }
> > +    } else {
> > +        return res;
> > +    }
> > +
> > +    /*
> > +     * 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.
> > +     */
> > +    if (s->dt.indirect) {
> > +        l2t_id = devid / (s->dt.page_sz / L1TABLE_ENTRY_SIZE);
> > +
> > +        value = address_space_ldq_le(as,
> > +                                     s->dt.base_addr +
> > +                                     (l2t_id *
> > L1TABLE_ENTRY_SIZE),
> > +                                     MEMTXATTRS_UNSPECIFIED,
> > &res);
> > +
> > +        if (res != MEMTX_OK) {
> > +            return res;
> > +        }
> > +
> > +        valid_l2t = (value >> VALID_SHIFT) & VALID_MASK;
> > +
> > +        if (valid_l2t) {
> > +            max_l2_entries = s->dt.page_sz / s->dt.entry_sz;
> > +
> > +            l2t_addr = value & ((1ULL << 51) - 1);
> > +
> > +            address_space_stq_le(as, l2t_addr +
> > +                                 ((devid % max_l2_entries) *
> > GITS_DTE_SIZE),
> > +                                 dte, MEMTXATTRS_UNSPECIFIED,
> > &res);
> > +        }
> > +    } else {
> > +        /* Flat level table */
> > +        address_space_stq_le(as, s->dt.base_addr + (devid *
> > GITS_DTE_SIZE),
> > +                             dte, MEMTXATTRS_UNSPECIFIED, &res);
> > +    }
> > +    return res;
> > +}
> > +
> > +static MemTxResult process_mapd(GICv3ITSState *s, uint64_t value,
> you do not seem to use the input value, remove it?
yes we are using the input value,which is the 1st DW from the command
to extract the deviceid (devid) field below
> > +                                uint32_t offset)
> > +{
> > +    AddressSpace *as = &s->gicv3->dma_as;
> > +    uint32_t devid;
> > +    uint8_t size;
> > +    uint64_t itt_addr;
> > +    bool valid;
> > +    MemTxResult res = MEMTX_OK;
> > +
> > +    devid = (value >> DEVID_SHIFT) & DEVID_MASK;
> > +
> > +    offset += NUM_BYTES_IN_DW;
> > +    value = address_space_ldq_le(as, s->cq.base_addr + offset,
> > +                                 MEMTXATTRS_UNSPECIFIED, &res);
> > +
> > +    if (res != MEMTX_OK) {
> > +        return res;
> > +    }
> > +
> > +    size = (value & SIZE_MASK);
> > +
> > +    offset += NUM_BYTES_IN_DW;
> > +    value = address_space_ldq_le(as, s->cq.base_addr + offset,
> > +                                 MEMTXATTRS_UNSPECIFIED, &res);
> > +
> > +    if (res != MEMTX_OK) {
> > +        return res;
> > +    }
> > +
> > +    itt_addr = (value >> ITTADDR_SHIFT) & ITTADDR_MASK;
> this looks weird to me, usually we apply the mask first and then
> shift.
from the 64 bit DW,we right shift (by 8)to align the itt_addr at 0th
position and extract 44 bits(0 to 43) using the mask 
> > +
> > +    valid = (value >> VALID_SHIFT) & VALID_MASK;
> use FIELD_EX64()?
> > +
> > +    if ((devid > s->dt.max_devids) ||
> > +        (size > FIELD_EX64(s->typer, GITS_TYPER, IDBITS))) {
> ITS_IDBITS?
IDBITS is one of the fields in GITS_TYPER and the field naming is
consistent with the spec definition
> > +        qemu_log_mask(LOG_GUEST_ERROR,
> > +                      "ITS MAPD: invalid device table attributes "
> > +                      "devid %d or size %d\n", devid, size);
> > +        /*
> > +         * in this implementation, in case of error
> > +         * we ignore this command and move onto the next
> > +         * command in the queue
> > +         */
> > +    } else {
> > +        res = update_dte(s, devid, valid, size, itt_addr);
> > +    }
> > +
> > +    return res;
> > +}
> > +
> > +/*
> > + * Current implementation blocks until all
> > + * commands are processed
> > + */
> > +static void 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->dma_as;
> > +    MemTxResult res = MEMTX_OK;
> > +    uint8_t cmd;
> > +
> > +    if (!(s->ctlr & ITS_CTLR_ENABLED)) {
> > +        return;
> > +    }
> > +
> > +    wr_offset = FIELD_EX64(s->cwriter, GITS_CWRITER, OFFSET);
> > +
> > +    if (wr_offset > s->cq.max_entries) {
> Shouldn't this be checked on cwrite write instead?
Yes we are checking within the cwriter write scope,just that the check
is happening through this function (called during cwrite write)
> > +        qemu_log_mask(LOG_GUEST_ERROR,
> > +                      "%s: invalid write offset "
> > +                      "%d\n", __func__, wr_offset);
> > +        return;
> > +    }
> > +
> > +    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:
> > +            /*
> > +             * 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.
> > +             * Hence no further processing is required for SYNC
> > command.
> > +             */
> > +            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 {
> > +            /*
> > +             * in this implementation,in case of dma read/write
> > error
> > +             * we stall the command processing
> > +             */
> > +            s->creadr = FIELD_DP64(s->creadr, GITS_CREADR,
> > STALLED, 1);
> > +            qemu_log_mask(LOG_GUEST_ERROR,
> > +                          "%s: %x cmd processing failed!!\n",
> > __func__, cmd);
> > +            break;
> > +        }
> > +    }
> > +}
> > +
> >  static void extract_table_params(GICv3ITSState *s)
> >  {
> >      uint16_t num_pages = 0;
> > @@ -226,6 +515,9 @@ static MemTxResult its_writel(GICv3ITSState *s,
> > hwaddr offset,
> >      case GITS_CWRITER:
> >          s->cwriter = deposit64(s->cwriter, 0, 32,
> >                                 (value &
> > ~R_GITS_CWRITER_RETRY_MASK));
> > +        if (s->cwriter != s->creadr) {
> > +            process_cmdq(s);
> I would expect process_cmdq() to be called as well on ITS enable
Done
> > +        }
> >          break;
> >      case GITS_CWRITER + 4:
> >          s->cwriter = deposit64(s->cwriter, 32, 32,
> > @@ -346,6 +638,9 @@ static MemTxResult its_writell(GICv3ITSState
> > *s, hwaddr offset,
> >          break;
> >      case GITS_CWRITER:
> >          s->cwriter = value & ~R_GITS_CWRITER_RETRY_MASK;
> > +        if (s->cwriter != s->creadr) {
> > +            process_cmdq(s);
> > +        }
> >          break;
> >      case GITS_CREADR:
> >      case GITS_TYPER:
> > diff --git a/hw/intc/gicv3_internal.h b/hw/intc/gicv3_internal.h
> > index d6aaa94e4c..0932a30560 100644
> > --- a/hw/intc/gicv3_internal.h
> > +++ b/hw/intc/gicv3_internal.h
> > @@ -253,6 +253,9 @@ 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)
> >  
> > @@ -289,6 +292,40 @@ 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_LENGTH                  16
> > +#define ICID_MASK                 ((1U << ICID_LENGTH) - 1)
> can't you use FIELD') as well for the ICID?
in addition to MAPC command ICID is a common field for MAPTI,MAPI
commands as well,hence wanted to keep it common and seperate
> > +FIELD(MAPC, RDBASE, 16, 32)
> > +
> > +#define RDBASE_PROCNUM_LENGTH        16
> > +#define RDBASE_PROCNUM_MASK       ((1ULL << RDBASE_PROCNUM_LENGTH)
> > - 1)
> why do we have both the RDBASE FIELD def and above defs?
RDBASE FIELD def points to the rdbase field within the MAPC
command,while the RDBASE_PROCNUM_ defines are used to consider 16 bit
PE number as the target destination instead of redistributor base
address option.
> > +
> > +#define DEVID_SHIFT                  32
> > +#define DEVID_LENGTH                 32
> > +#define DEVID_MASK                ((1ULL << DEVID_LENGTH) - 1)
> we don't have any DEVID field in MAPC, I guess it belongs to MAPD?
MAPC doesnt have a DEVID field ,but it is a common field in
MAPD,INT,MAPI,MAPTI commands(at the same offset)
> > +
> > +/* MAPD command fields */
> > +#define ITTADDR_LENGTH               44
> > +#define ITTADDR_SHIFT                 8
> > +#define ITTADDR_MASK              ((1ULL << ITTADDR_LENGTH) - 1)
> > +#define SIZE_MASK                 0x1f
> Can't you homogenize the definition, use field() and/or prefix with
> the
> cmd name when not common to severals cmds?
Since ITTADDR_MASK is common to both MAPD command as well as device
table entry field,didnt want to go with field() as the MAPD tag-name in
device table entry would be insignificant
> 
> > +
> > +#define VALID_SHIFT               63
> > +#define VALID_MASK                1ULL
> >  
> >  /**
> >   * Default features advertised by this version of ITS
> > 
> Thanks
> 
> Eric
> 






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

* Re: [PATCH v4 4/8] hw/intc: GICv3 ITS Command processing
  2021-06-13 15:55   ` Eric Auger
@ 2021-06-16 21:02     ` shashi.mallela
  2021-06-21 10:13       ` Eric Auger
  0 siblings, 1 reply; 52+ messages in thread
From: shashi.mallela @ 2021-06-16 21:02 UTC (permalink / raw)
  To: Eric Auger, peter.maydell, leif, rad; +Cc: qemu-arm, qemu-devel

Hi Eric,

Please find my responses inline (below):-

On Sun, 2021-06-13 at 17:55 +0200, Eric Auger wrote:
> Hi Shashi,
> 
> On 6/2/21 8:00 PM, Shashi Mallela 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            | 334
> > +++++++++++++++++++++++++++++
> >  hw/intc/gicv3_internal.h           |  12 ++
> >  include/hw/intc/arm_gicv3_common.h |   2 +
> >  3 files changed, 348 insertions(+)
> > 
> > diff --git a/hw/intc/arm_gicv3_its.c b/hw/intc/arm_gicv3_its.c
> > index 6551c577b3..82bb5b84ef 100644
> > --- a/hw/intc/arm_gicv3_its.c
> > +++ b/hw/intc/arm_gicv3_its.c
> > @@ -28,6 +28,13 @@ 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;
> Add a comment to explain what this enum stand for. This sounds
> misleading to me versus the command IDs. Why don't you use the cmd id
> then and add NONE?
This is an internal enum used to distinguish between interrupt
triggered via command queue and interrupt triggered via gits_translater
write.Will add the same comment in code.
Since NONE is only 1 command applicable for GITS_TRANSLATER,started
with it so that in the future if any further command queue commands
have to be added we can just extend the numbering.
> > +
> >  static uint64_t baser_base_addr(uint64_t value, uint32_t page_sz)
> >  {
> >      uint64_t result = 0;
> > @@ -49,6 +56,315 @@ static uint64_t baser_base_addr(uint64_t value,
> > uint32_t page_sz)
> >      return result;
> >  }
> >  
> > +static bool get_cte(GICv3ITSState *s, uint16_t icid, uint64_t
> > *cte,
> > +                    MemTxResult *res)
> > +{
> > +    AddressSpace *as = &s->gicv3->dma_as;
> > +    uint64_t l2t_addr;
> > +    uint64_t value;
> > +    bool valid_l2t;
> > +    uint32_t l2t_id;
> > +    uint32_t max_l2_entries;
> > +    bool status = false;
> > +
> > +    if (s->ct.indirect) {
> > +        l2t_id = icid / (s->ct.page_sz / L1TABLE_ENTRY_SIZE);
> > +
> > +        value = address_space_ldq_le(as,
> > +                                     s->ct.base_addr +
> > +                                     (l2t_id *
> > L1TABLE_ENTRY_SIZE),
> > +                                     MEMTXATTRS_UNSPECIFIED, res);
> > +
> > +        if (*res == MEMTX_OK) {
> > +            valid_l2t = (value >> VALID_SHIFT) & VALID_MASK;
> > +
> > +            if (valid_l2t) {
> > +                max_l2_entries = s->ct.page_sz / s->ct.entry_sz;
> > +
> > +                l2t_addr = value & ((1ULL << 51) - 1);
> > +
> > +                *cte =  address_space_ldq_le(as, l2t_addr +
> > +                                    ((icid % max_l2_entries) *
> > GITS_CTE_SIZE),
> > +                                    MEMTXATTRS_UNSPECIFIED, res);
> > +           }
> > +       }
> > +    } else {
> > +        /* Flat level table */
> > +        *cte =  address_space_ldq_le(as, s->ct.base_addr +
> > +                                     (icid * GITS_CTE_SIZE),
> > +                                      MEMTXATTRS_UNSPECIFIED,
> > res);
> > +    }
> > +
> > +    if (*cte & VALID_MASK) {
> > +        status = true;
> > +    }
> > +
> > +    return status;
> > +}
> > +
> > +static MemTxResult update_ite(GICv3ITSState *s, uint32_t eventid,
> > uint64_t dte,
> > +                              uint64_t itel, uint32_t iteh)
> why not introducing an ite struct instead of the h/l args?based on
> the same reason for packing individual fields within bit positions
> instead of using more memory to store the same fields as struct
> members.
Will create an ite struct with existing itel & iteh as members and
retain their bit fields processing to avoid extra params being passed.
> > +{
> > +    AddressSpace *as = &s->gicv3->dma_as;
> > +    uint64_t itt_addr;
> > +    MemTxResult res = MEMTX_OK;
> > +
> > +    itt_addr = (dte >> 6ULL) & ITTADDR_MASK;
> > +    itt_addr <<= ITTADDR_SHIFT; /* 256 byte aligned */
> > +
> > +    address_space_stq_le(as, itt_addr + (eventid *
> > sizeof(uint64_t)),
> > +                         itel, MEMTXATTRS_UNSPECIFIED, &res);
> > +
> > +    if (res == MEMTX_OK) {
> > +        address_space_stl_le(as, itt_addr + ((eventid +
> > sizeof(uint64_t)) *
> > +                             sizeof(uint32_t)), iteh,
> > MEMTXATTRS_UNSPECIFIED,
> > +                             &res);
> > +    }
> > +   return res;
> > +}
> > +
> > +static bool get_ite(GICv3ITSState *s, uint32_t eventid, uint64_t
> > dte,
> > +                    uint16_t *icid, uint32_t *pIntid, MemTxResult
> > *res)
> > +{
> > +    AddressSpace *as = &s->gicv3->dma_as;
> > +    uint64_t itt_addr;
> > +    bool status = false;
> > +    uint64_t itel = 0;
> > +    uint32_t iteh = 0;
> > +
> > +    itt_addr = (dte >> 6ULL) & ITTADDR_MASK;
> > +    itt_addr <<= ITTADDR_SHIFT; /* 256 byte aligned */
> > +
> > +    itel = address_space_ldq_le(as, itt_addr + (eventid *
> > sizeof(uint64_t)),
> > +                                MEMTXATTRS_UNSPECIFIED, res);
> > +
> > +    if (*res == MEMTX_OK) {
> > +        iteh = address_space_ldl_le(as, itt_addr + ((eventid +
> > +                                    sizeof(uint64_t)) *
> > sizeof(uint32_t)),
> > +                                    MEMTXATTRS_UNSPECIFIED, res);
> > +
> > +        if (*res == MEMTX_OK) {
> > +            if (itel & VALID_MASK) {
> > +                if ((itel >> ITE_ENTRY_INTTYPE_SHIFT) &
> > GITS_TYPE_PHYSICAL) {
> > +                    *pIntid = (itel >> ITE_ENTRY_INTID_SHIFT) &
> > +                               ITE_ENTRY_INTID_MASK;
> > +                    *icid = iteh & ITE_ENTRY_ICID_MASK;
> > +                    status = true;
> > +                }
> > +            }
> > +        }
> > +    }
> > +    return status;
> > +}
> > +
> > +static uint64_t get_dte(GICv3ITSState *s, uint32_t devid,
> > MemTxResult *res)
> maybe the code would be more readable if you were returning a strcut
> for
> dte/cte instead of uint64_t. The decoding of the fields would be done
> here instead?
Both dte and cte are 8 bytes and hence chose a single uint64_t to
efficiently utilize the applicable bit fields in the variable instead
of a struct(either with bitfields to match the current field layouts
within dte/cte/ite entries or use more memory variables within the
struct to hold each individual dte/cte/ite fields)
> > +{
> > +    AddressSpace *as = &s->gicv3->dma_as;
> > +    uint64_t l2t_addr;
> > +    uint64_t value;
> > +    bool valid_l2t;
> > +    uint32_t l2t_id;
> > +    uint32_t max_l2_entries;
> > +
> > +    if (s->dt.indirect) {
> > +        l2t_id = devid / (s->dt.page_sz / L1TABLE_ENTRY_SIZE);
> > +
> > +        value = address_space_ldq_le(as,
> > +                                     s->dt.base_addr +
> > +                                     (l2t_id *
> > L1TABLE_ENTRY_SIZE),
> > +                                     MEMTXATTRS_UNSPECIFIED, res);
> > +
> > +        if (*res == MEMTX_OK) {
> > +            valid_l2t = (value >> VALID_SHIFT) & VALID_MASK;
> > +
> > +            if (valid_l2t) {
> > +                max_l2_entries = s->dt.page_sz / s->dt.entry_sz;
> > +
> > +                l2t_addr = value & ((1ULL << 51) - 1);
> > +
> > +                value =  address_space_ldq_le(as, l2t_addr +
> > +                                   ((devid % max_l2_entries) *
> > GITS_DTE_SIZE),
> > +                                   MEMTXATTRS_UNSPECIFIED, res);
> > +            }
> > +        }
> > +    } else {
> > +        /* Flat level table */
> > +        value = address_space_ldq_le(as, s->dt.base_addr +
> > +                                     (devid * GITS_DTE_SIZE),
> > +                                     MEMTXATTRS_UNSPECIFIED, res);
> > +    }
> > +
> > +    return value;
> > +}
> I think a common helper could be defined for get_cte and get_dte.
was just trying to keep the code modular for getting dte and cte
instead of various if else's within a common helper 
> > +
> > +static MemTxResult process_int(GICv3ITSState *s, uint64_t value,
> > +                               uint32_t offset, ItsCmdType cmd)
> this is a bit misleanding as INT is a command. You should rename it I
> think. Also it is not really homogeneous with other cmds, ie. you
> have
> process_mapti, process_mapd, process_mac and all the remaining cmds
> are
> handled though this one? At least add a doc comment to explain what
> it does.
the naming of this function is along the lines of other ITS commands
i.e process_xxx format where xxx is the actual ITS command.
There is no connection between handling of process_mapd,process_mapc
with process_int.Each of them handle their respective command
processing independently.
> > +{
> > +    AddressSpace *as = &s->gicv3->dma_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;
> > +    uint64_t itel = 0;
> > +    uint32_t iteh = 0;
> > +
> > +    if (cmd == NONE) {
> > +        devid = offset;
> > +    } else {
> > +        devid = (value >> DEVID_SHIFT) & DEVID_MASK;
> > +
> > +        offset += NUM_BYTES_IN_DW;
> > +        value = address_space_ldq_le(as, s->cq.base_addr + offset,
> > +                                     MEMTXATTRS_UNSPECIFIED,
> > &res);
> > +    }
> > +
> > +    if (res != MEMTX_OK) {
> > +        return res;
> > +    }
> > +
> > +    eventid = (value & EVENTID_MASK);
> > +
> > +    dte = get_dte(s, devid, &res);
> > +
> > +    if (res != MEMTX_OK) {
> > +        return res;
> > +    }
> > +    dte_valid = dte & VALID_MASK;
> > +
> > +    if (dte_valid) {
> > +        max_eventid = (1UL << (((dte >> 1U) & SIZE_MASK) + 1));
> THE DTE format is implementation defined, and the decoding is hard to
> follow because it is documented somewhere else.
The decoding of fields within each of dte,cte and ite has been
explained as comments in gicv3_internal.h via
GITS_DTE_SIZE,GITS_CTE_SIZE, ITS_ITT_ENTRY_SIZE defines
> > +
> > +        ite_valid = get_ite(s, eventid, dte, &icid, &pIntid,
> > &res);
> > +
> > +        if (res != MEMTX_OK) {
> > +            return res;
> > +        }
> > +
> > +        if (ite_valid) {
> > +            cte_valid = get_cte(s, icid, &cte, &res);
> > +        }
> > +
> > +        if (res != MEMTX_OK) {
> > +            return res;
> > +        }
> instead of having this process_int() helper, why not having a helper
> doing just the decoding phase, ie.
> get_dte -> get_ite -> get_cte and collect the relevant info and
> collect
> error and then keep the actual cmd processing in the switch?
As explained previously chose to keep the code modular instead of a
large monolothic structure with all the functionality in 1 place.
process_cmdq handles the command queue (and their read/write offsets)
while the individual ITS command handling is done by respective
functions
> > +    }
> > +
> > +    if ((devid > s->dt.max_devids) || !dte_valid || !ite_valid ||
> > +            !cte_valid || (eventid > max_eventid)) {
> > +        qemu_log_mask(LOG_GUEST_ERROR,
> > +                      "%s: invalid interrupt translation table
> > attributes "
> > +                      "devid %d or eventid %d\n",
> > +                      __func__, devid, eventid);
> the msg does not necessarily match the error case. You mention ITT
> issue
> whereas the problem may come from invalid DTE, CTE, or even devid.
will change it to consider the missing cases
> > +        /*
> > +         * in this implementation,in case of error
> > +         * we ignore this command and move onto the next
> > +         * command in the queue
> > +         */
> so you don't return an error?
yes we dont,we just ignore this command and move onto next one in the
queue
> > +    } else {
> > +        /*
> > +         * Current implementation only supports rdbase == procnum
> > +         * Hence rdbase physical address is ignored
> > +         */
> > +        if (cmd == DISCARD) {
> > +            /* remove mapping from interrupt translation table */
> > +            res = update_ite(s, eventid, dte, itel, iteh);
> iteh and itel always are 0, why not use a struct ite with valid field
> unset.
based on the same reason for packing individual fields within bit
positions instead of using more memory to store the same fields as
struct members.
I could however create an ite struct with existing itel & iteh as
members and retain their bit fields processing. 
> > +        }
> > +    }
> > +
> > +    return res;
> > +}
> > +
> > +static MemTxResult process_mapti(GICv3ITSState *s, uint64_t value,
> > +                                 uint32_t offset, bool
> > ignore_pInt)
> > +{
> > +    AddressSpace *as = &s->gicv3->dma_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 itel = 0;
> > +    uint32_t iteh = 0;
> > +    uint32_t int_spurious = INTID_SPURIOUS;
> > +
> > +    devid = (value >> DEVID_SHIFT) & DEVID_MASK;
> > +    offset += NUM_BYTES_IN_DW;
> > +    value = address_space_ldq_le(as, s->cq.base_addr + offset,
> > +                                 MEMTXATTRS_UNSPECIFIED, &res);
> > +
> > +    if (res != MEMTX_OK) {
> > +        return 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);
> > +
> > +    if (res != MEMTX_OK) {
> > +        return res;
> > +    }
> > +
> > +    icid = value & ICID_MASK;
> > +
> > +    dte = get_dte(s, devid, &res);
> > +
> > +    if (res != MEMTX_OK) {
> > +        return res;
> > +    }
> > +    dte_valid = dte & VALID_MASK;
> > +
> > +    max_eventid = (1UL << (((dte >> 1U) & SIZE_MASK) + 1));
> Again I think you would gain in readibility if get_cte were to return
> a
> struct and you would avoid code duplication.
Like mentioned earlier, preferred to use a single uint64_t for cte (and
dte) instead of a struct (either with bitfields to match the current
field layouts within dte/cte/ite entries or use more memory variables
within the struct to hold each individual dte/cte/ite fields).The
layout of all these functions follows the pseudocde format defined
under each ITS command in the spec.
> > +
> > +    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)))) {
> > +        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 ite entry to interrupt translation table */
> > +        itel = (dte_valid & VALID_MASK) | (GITS_TYPE_PHYSICAL <<
> > +                                           ITE_ENTRY_INTTYPE_SHIFT
> > );
> > +
> > +        if (ignore_pInt) {
> > +            itel |= (eventid << ITE_ENTRY_INTID_SHIFT);
> > +        } else {
> > +            itel |= (pIntid << ITE_ENTRY_INTID_SHIFT);
> > +        }
> > +        itel |= (int_spurious << ITE_ENTRY_INTSP_SHIFT);
> > +        iteh |= icid;
> > +
> > +        res = update_ite(s, eventid, dte, itel, iteh);
> > +    }
> > +
> > +    return res;
> > +}
> > +
> >  static MemTxResult update_cte(GICv3ITSState *s, uint16_t icid,
> > bool valid,
> >                                uint64_t rdbase)
> >  {
> > @@ -295,8 +611,10 @@ static void 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:
> >              /*
> > @@ -313,10 +631,13 @@ static void 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;
> > @@ -472,7 +793,20 @@ static MemTxResult
> > gicv3_its_translation_write(void *opaque, hwaddr offset,
> >                                                 uint64_t data,
> > unsigned size,
> >                                                 MemTxAttrs attrs)
> >  {
> > +    GICv3ITSState *s = (GICv3ITSState *)opaque;
> >      MemTxResult result = MEMTX_OK;
> > +    uint32_t devid = 0;
> > +
> > +    switch (offset) {
> > +    case GITS_TRANSLATER:
> > +        if (s->ctlr & ITS_CTLR_ENABLED) {
> > +            devid = attrs.requester_id;
> > +            result = process_int(s, data, devid, NONE);
> > +        }
> > +        break;
> > +    default:
> > +        break;
> > +    }
> >  
> >      return result;
> >  }
> > diff --git a/hw/intc/gicv3_internal.h b/hw/intc/gicv3_internal.h
> > index 0932a30560..ce45cd0ef6 100644
> > --- a/hw/intc/gicv3_internal.h
> > +++ b/hw/intc/gicv3_internal.h
> > @@ -324,6 +324,13 @@ FIELD(MAPC, RDBASE, 16, 32)
> >  #define ITTADDR_MASK              ((1ULL << ITTADDR_LENGTH) - 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                1ULL
> >  
> > @@ -344,6 +351,11 @@ FIELD(MAPC, RDBASE, 16, 32)
> >   * vPEID = 16 bits
> >   */
> >  #define ITS_ITT_ENTRY_SIZE            0xC
> > +#define ITE_ENTRY_INTTYPE_SHIFT        1
> > +#define ITE_ENTRY_INTID_SHIFT          2
> > +#define ITE_ENTRY_INTID_MASK         ((1ULL << 24) - 1)
> > +#define ITE_ENTRY_INTSP_SHIFT          26
> > +#define ITE_ENTRY_ICID_MASK          ((1ULL << 16) - 1)
> >  
> >  /* 16 bits EventId */
> >  #define ITS_IDBITS                   GICD_TYPER_IDBITS
> > diff --git a/include/hw/intc/arm_gicv3_common.h
> > b/include/hw/intc/arm_gicv3_common.h
> > index 1fd5cedbbd..0715b0bc2a 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 */
> > 
> Thanks
> 
> Eric
> 



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

* Re: [PATCH v4 6/8] hw/intc: GICv3 redistributor ITS processing
  2021-06-13 16:26   ` Eric Auger
@ 2021-06-16 21:02     ` shashi.mallela
  0 siblings, 0 replies; 52+ messages in thread
From: shashi.mallela @ 2021-06-16 21:02 UTC (permalink / raw)
  To: Eric Auger, peter.maydell, leif, rad; +Cc: qemu-arm, qemu-devel

Hi Eric,

Have accepted all comments with responses inline (below):-

On Sun, 2021-06-13 at 18:26 +0200, Eric Auger wrote:
> Hi Shashi,
> 
> On 6/2/21 8:00 PM, Shashi Mallela wrote:
> > Implemented lpi processing at redistributor to get lpi config info
> s/Implemented/Implement here are elsewhere.
> > 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                |   9 ++
> >  hw/intc/arm_gicv3_common.c         |   1 +
> >  hw/intc/arm_gicv3_cpuif.c          |   7 +-
> >  hw/intc/arm_gicv3_its.c            |  14 ++-
> >  hw/intc/arm_gicv3_redist.c         | 145
> > +++++++++++++++++++++++++++++
> >  hw/intc/gicv3_internal.h           |  10 ++
> >  include/hw/intc/arm_gicv3_common.h |  10 ++
> >  7 files changed, 190 insertions(+), 6 deletions(-)
> > 
> > diff --git a/hw/intc/arm_gicv3.c b/hw/intc/arm_gicv3.c
> > index d63f8af604..4d19190b9c 100644
> > --- a/hw/intc/arm_gicv3.c
> > +++ b/hw/intc/arm_gicv3.c
> > @@ -165,6 +165,15 @@ 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 && cs->lpivalid) {
> > +        if (irqbetter(cs, cs->hpplpi.irq, cs->hpplpi.prio)) {
> > +            cs->hppi.irq = cs->hpplpi.irq;
> > +            cs->hppi.prio = cs->hpplpi.prio;
> > +            cs->hppi.grp = cs->hpplpi.grp;
> > +            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_common.c
> > b/hw/intc/arm_gicv3_common.c
> > index 53dea2a775..223db16fec 100644
> > --- a/hw/intc/arm_gicv3_common.c
> > +++ b/hw/intc/arm_gicv3_common.c
> > @@ -435,6 +435,7 @@ static void arm_gicv3_common_reset(DeviceState
> > *dev)
> >          memset(cs->gicr_ipriorityr, 0, sizeof(cs-
> > >gicr_ipriorityr));
> >  
> >          cs->hppi.prio = 0xff;
> > +        cs->hpplpi.prio = 0xff;
> >  
> >          /* State in the CPU interface must *not* be reset here,
> > because it
> >           * is part of the CPU's reset domain, not the GIC
> > device's.
> > diff --git a/hw/intc/arm_gicv3_cpuif.c b/hw/intc/arm_gicv3_cpuif.c
> > index 81f94c7f4a..5be3efaa3f 100644
> > --- a/hw/intc/arm_gicv3_cpuif.c
> > +++ b/hw/intc/arm_gicv3_cpuif.c
> > @@ -898,10 +898,12 @@ static void icc_activate_irq(GICv3CPUState
> > *cs, int irq)
> >          cs->gicr_iactiver0 = deposit32(cs->gicr_iactiver0, irq, 1,
> > 1);
> >          cs->gicr_ipendr0 = deposit32(cs->gicr_ipendr0, irq, 1, 0);
> >          gicv3_redist_update(cs);
> > -    } else {
> > +    } else if (irq < GICV3_LPI_INTID_START) {
> >          gicv3_gicd_active_set(cs->gic, irq);
> >          gicv3_gicd_pending_clear(cs->gic, irq);
> >          gicv3_update(cs->gic, irq, 1);
> > +    } else {
> > +        gicv3_redist_lpi_pending(cs, irq, 0);
> >      }
> >  }
> >  
> > @@ -1317,7 +1319,8 @@ static void icc_eoir_write(CPUARMState *env,
> > const ARMCPRegInfo *ri,
> >      trace_gicv3_icc_eoir_write(is_eoir0 ? 0 : 1,
> >                                 gicv3_redist_affid(cs), value);
> >  
> > -    if (irq >= cs->gic->num_irq) {
> > +    if ((irq >= cs->gic->num_irq) &&  (!(cs->gic->lpi_enable &&
> > +        (irq >= GICV3_LPI_INTID_START)))) {
> >          /* This handles two cases:
> >           * 1. If software writes the ID of a spurious interrupt
> > [ie 1020-1023]
> >           * to the GICC_EOIR, the GIC ignores that write.
> > diff --git a/hw/intc/arm_gicv3_its.c b/hw/intc/arm_gicv3_its.c
> > index 0a978cf55b..e0fbd4041f 100644
> > --- a/hw/intc/arm_gicv3_its.c
> > +++ b/hw/intc/arm_gicv3_its.c
> > @@ -211,6 +211,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;
> >      uint64_t itel = 0;
> >      uint32_t iteh = 0;
> >  
> > @@ -267,10 +268,15 @@ static MemTxResult process_int(GICv3ITSState
> > *s, uint64_t value,
> >           * command in the queue
> >           */
> >      } else {
> > -        /*
> > -         * Current implementation only supports rdbase == procnum
> > -         * Hence rdbase physical address is ignored
> > -         */
> > +        rdbase = (cte >> 1U) & RDBASE_PROCNUM_MASK;
> > +        assert(rdbase <= s->gicv3->num_cpu);
> > +
> > +        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 */
> >              res = update_ite(s, eventid, dte, itel, iteh);
> > diff --git a/hw/intc/arm_gicv3_redist.c
> > b/hw/intc/arm_gicv3_redist.c
> > index fb9a4ee3cc..bfc6e4e9b9 100644
> > --- a/hw/intc/arm_gicv3_redist.c
> > +++ b/hw/intc/arm_gicv3_redist.c
> > @@ -255,6 +255,11 @@ 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
> > */
> > +                cs->lpivalid = false;
> > +                cs->hpplpi.prio = 0xff;
> > +                gicv3_redist_update_lpi(cs);
> > +                gicv3_redist_update(cs);
> >              } else {
> >                  cs->gicr_ctlr &= ~GICR_CTLR_ENABLE_LPIS;
> >              }
> > @@ -534,6 +539,146 @@ MemTxResult gicv3_redist_write(void *opaque,
> > hwaddr offset, uint64_t data,
> >      return r;
> >  }
> >  
> > +void gicv3_redist_update_lpi(GICv3CPUState *cs)
> > +{
> > +    /*
> > +     * This function scans the LPI pending table and for each
> > pending
> > +     * LPI, reads the corresponding entry from LPI configuration
> > table
> > +     * to extract the priority info and determine if the LPI
> > priority
> > +     * is lower than the current high priority interrupt.If yes,
> > update> +     * high priority pending interrupt to that of LPI.
> 
> "update high priority pending interrupt to that of LPI" may need some
> rewording
Will do
> > +     */
> > +    AddressSpace *as = &cs->gic->dma_as;
> > +    uint64_t lpict_baddr, lpipt_baddr;
> > +    uint32_t pendt_size = 0;
> > +    uint8_t lpite;
> > +    uint8_t prio, pend;
> > +    int i;
> > +    uint64_t idbits;
> > +
> > +    idbits = MIN(FIELD_EX64(cs->gicr_propbaser, GICR_PROPBASER,
> > IDBITS),
> > +                 GICD_TYPER_IDBITS);
> > +
> > +    if ((!cs->gicr_ctlr & GICR_CTLR_ENABLE_LPIS) || !cs-
> > >gicr_propbaser ||
> > +        !cs->gicr_pendbaser || (idbits <
> > GICR_PROPBASER_IDBITS_THRESHOLD)) {
> > +        return;
> > +    }
> > +
> > +    lpict_baddr = cs->gicr_propbaser &
> > R_GICR_PROPBASER_PHYADDR_MASK;
> > +
> > +    lpipt_baddr = cs->gicr_pendbaser &
> > R_GICR_PENDBASER_PHYADDR_MASK;
> > +
> > +    /* Determine the highest priority pending interrupt among LPIs
> > */
> > +    pendt_size = (1ULL << (idbits + 1));
> > +
> > +    for (i = 0; i < pendt_size / 8; 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));
> > +
> > +            if (!(lpite & LPI_CTE_ENABLED)) {
> > +                continue;
> > +            }
> > +
> > +            if (cs->gic->gicd_ctlr & GICD_CTLR_DS) {
> > +                prio = lpite & LPI_PRIORITY_MASK;
> > +            } else {
> > +                prio = lpite & LPI_SPRIORITY_MASK;
> > +            }
> > +
> > +            if (prio <= cs->hpplpi.prio) {
> > +                cs->hpplpi.irq = GICV3_LPI_INTID_START + i;
> > +                cs->hpplpi.prio = prio;
> > +                /* LPIs are always non-secure Grp1 interrupts */
> > +                cs->hpplpi.grp = GICV3_G1NS;
> > +                cs->lpivalid = true;
> > +            }
> > +        }
> > +    }
> > +}
> > +
> nit: add a comment to explain what the function does, that's not
> straightforward gievn its name.
Will do
> > +void gicv3_redist_lpi_pending(GICv3CPUState *cs, int irq, int
> > level)
> > +{
> > +    AddressSpace *as = &cs->gic->dma_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 = cs->gicr_pendbaser &
> > R_GICR_PENDBASER_PHYADDR_MASK;
> > +
> > +    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));
> > +        }
> > +    }
> > +    cs->lpivalid = false;
> > +    cs->hpplpi.prio = 0xff;
> > +    gicv3_redist_update_lpi(cs);
> > +}
> > +
> > +void gicv3_redist_process_lpi(GICv3CPUState *cs, int irq, int
> > level)
> > +{
> > +    AddressSpace *as = &cs->gic->dma_as;
> > +    uint64_t lpict_baddr;
> > +    uint8_t lpite;
> > +    uint64_t idbits;
> > +
> > +    idbits = MIN(FIELD_EX64(cs->gicr_propbaser, GICR_PROPBASER,
> > IDBITS),
> > +                 GICD_TYPER_IDBITS);
> > +
> > +    if ((!cs->gicr_ctlr & GICR_CTLR_ENABLE_LPIS) || !cs-
> > >gicr_propbaser ||
> > +         !cs->gicr_pendbaser || (idbits <
> > GICR_PROPBASER_IDBITS_THRESHOLD) ||
> > +         (irq > (1ULL << (idbits + 1)))) {
> > +        return;
> > +    }
> > +
> > +    lpict_baddr = cs->gicr_propbaser &
> > R_GICR_PROPBASER_PHYADDR_MASK;
> > +
> > +    /* 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 91dbe01176..bcbccba573 100644
> > --- a/hw/intc/gicv3_internal.h
> > +++ b/hw/intc/gicv3_internal.h
> > @@ -308,6 +308,13 @@ 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 LPI_SPRIORITY_MASK        0x7e
> > +
> >  #define GITS_CMDQ_ENTRY_SIZE               32
> >  #define NUM_BYTES_IN_DW                     8
> >  
> > @@ -452,6 +459,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);
> > +void 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);
> >  
> > diff --git a/include/hw/intc/arm_gicv3_common.h
> > b/include/hw/intc/arm_gicv3_common.h
> > index c1348cc60a..5d839da9c9 100644
> > --- a/include/hw/intc/arm_gicv3_common.h
> > +++ b/include/hw/intc/arm_gicv3_common.h
> > @@ -204,6 +204,16 @@ struct GICv3CPUState {
> >       * real state above; it doesn't need to be migrated.
> >       */
> >      PendingIrq hppi;
> > +
> > +    /*
> > +     * Current highest priority pending lpi for this CPU.
> > +     * This is cached information that can be recalculated from
> > the
> > +     * real state above; it doesn't need to be migrated.
> > +     */
> > +    PendingIrq hpplpi;
> > +
> > +    bool lpivalid; /* current highest priority lpi validity status
> > */
> > +
> >      /* This is temporary working state, to avoid a malloc in
> > gicv3_update() */
> >      bool seenbetter;
> >  };>
> 
> Thanks
> 
> Eric
> 



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

* Re: [PATCH v4 2/8] hw/intc: GICv3 ITS register definitions added
  2021-06-16 21:02     ` shashi.mallela
@ 2021-06-21  9:51       ` Eric Auger
  2021-06-28 21:51         ` shashi.mallela
  0 siblings, 1 reply; 52+ messages in thread
From: Eric Auger @ 2021-06-21  9:51 UTC (permalink / raw)
  To: shashi.mallela, peter.maydell, leif, rad; +Cc: qemu-arm, qemu-devel

Hi Shashi,

On 6/16/21 11:02 PM, shashi.mallela@linaro.org wrote:
> Hi Eric,
> 
> Please find my responses inline (below):-
> 
> On Sat, 2021-06-12 at 08:08 +0200, Eric Auger wrote:
>>
>> On 6/2/21 8:00 PM, Shashi Mallela 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                | 335
>>> +++++++++++++++++++++++++
>>>  hw/intc/gicv3_internal.h               |  28 ++-
>>>  include/hw/intc/arm_gicv3_common.h     |   3 +
>>>  include/hw/intc/arm_gicv3_its_common.h |  30 +++
>>>  4 files changed, 395 insertions(+), 1 deletion(-)
>>>
>>> diff --git a/hw/intc/arm_gicv3_its.c b/hw/intc/arm_gicv3_its.c
>>> index 545cda3665..af60f19c98 100644
>>> --- a/hw/intc/arm_gicv3_its.c
>>> +++ b/hw/intc/arm_gicv3_its.c
>>> @@ -28,6 +28,157 @@ struct GICv3ITSClass {
>>>      void (*parent_reset)(DeviceState *dev);
>>>  };
>>>  
>>> +static uint64_t baser_base_addr(uint64_t value, uint32_t page_sz)
>>> +{
>>> +    uint64_t result = 0;
>>> +
>>> +    switch (page_sz) {
>>> +    case GITS_ITT_PAGE_SIZE_0:
>>> +    case GITS_ITT_PAGE_SIZE_1:
>>> +        result = value & R_GITS_BASER_PHYADDR_MASK;
>> Use FIELD_EX64 as well for homogeneity?
> Done
>>> +        break;
>>> +
>>> +    case GITS_ITT_PAGE_SIZE_2:
>>> +        result = value & R_GITS_BASER_PHYADDRL_64K_MASK;
>> here as well?
> Done
>>> +        result |= FIELD_EX64(value, GITS_BASER, PHYADDRH_64K) <<
>>> 48;
>>> +        break;
>>> +
>>> +    default:
>>> +        break;
>>> +    }
>>> +    return result;
>>> +}
>>> +
>>> +static void extract_table_params(GICv3ITSState *s)
>>> +{
>>> +    uint16_t num_pages = 0;
>>> +    uint8_t  page_sz_type;
>>> +    uint8_t type;
>>> +    uint32_t page_sz = 0;
>>> +    uint64_t value;
>>> +
>>> +    for (int i = 0; i < 8; i++) {
>>> +        value = s->baser[i];
>>> +
>>> +        if (!value) {
>>> +            continue;
>>> +        }
>>> +
>>> +        page_sz_type = FIELD_EX64(value, GITS_BASER, PAGESIZE);
>>> +
>>> +        switch (page_sz_type) {
>>> +        case 0:
>>> +            page_sz = GITS_ITT_PAGE_SIZE_0;
>>> +            break;
>>> +
>>> +        case 1:
>>> +            page_sz = GITS_ITT_PAGE_SIZE_1;
>>> +            break;
>>> +
>>> +        case 2:
>>> +        case 3:
>>> +            page_sz = GITS_ITT_PAGE_SIZE_2;
>>> +            break;
>>> +
>>> +        default:
>>> +            g_assert_not_reached();
>>> +        }
>>> +
>>> +        num_pages = FIELD_EX64(value, GITS_BASER, SIZE);
>>  + 1 directly? and remove num_pages + 1 below.
> Done
>>> +
>>> +        type = FIELD_EX64(value, GITS_BASER, TYPE);
>>> +
>>> +        switch (type) {
>>> +
>>> +        case GITS_ITT_TYPE_DEVICE:
>>> +            memset(&s->dt, 0 , sizeof(s->dt));
>>> +            s->dt.valid = FIELD_EX64(value, GITS_BASER, VALID);
>>> +
>>> +            if (!s->dt.valid) {
>>> +                return;
>>> +            }
>>> +
>>> +            s->dt.page_sz = page_sz;
>>> +            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));
>>> +
>>> +            s->dt.base_addr = baser_base_addr(value, page_sz);
>>> +
>>> +            break;
>>> +
>>> +        case GITS_ITT_TYPE_COLLECTION:
>>> +            memset(&s->ct, 0 , sizeof(s->ct));
>>> +            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) {
>>> +                return;
>> as this is an helper routine, I think it would be better to have this
>> check in the caller. Also you reset ct above.
> The idea here was to keep all the GITS_BASER fields parsing and
> extraction in one place in this function without the caller (like
> its_writel) having to know the GITS_BASER fields format and thereby
> split the logic between the caller and this function 
Maybe add a doc comment explaining what the function does and in which
context it is supposed to be called then.
>>> +            }
>>> +
>>> +            s->ct.page_sz = page_sz;
>>> +            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);
>>> +            }
>>> +
>>> +            s->ct.base_addr = baser_base_addr(value, page_sz);
>>> +
>>> +            break;
>>> +
>>> +        default:
>>> +            break;
>>> +        }
>>> +    }
>>> +}
>>> +
>>> +static void extract_cmdq_params(GICv3ITSState *s)
>>> +{
>>> +    uint16_t num_pages = 0;
>>> +    uint64_t value = s->cbaser;
>>> +
>>> +    num_pages = FIELD_EX64(value, GITS_CBASER, SIZE);
>> + 1
>>> +
>>> +    memset(&s->cq, 0 , sizeof(s->cq));
>>> +    s->cq.valid = FIELD_EX64(value, GITS_CBASER, VALID);
>>> +
>>> +    if (s->cq.valid) {
>>> +        s->cq.max_entries = ((num_pages + 1) *
>>> GITS_ITT_PAGE_SIZE_0) /
>> nit: use of GITS_ITT_PAGE_SIZE_0 is misleading as ITT stands for
>> interrupt translation table and does not relate to CMDQ. Use 4K
>> define
>> instead.
> changed the names to GITS_PAGE_SIZE_4K/16K/64K
>>> +                             GITS_CMDQ_ENTRY_SIZE;
>>> +        s->cq.base_addr = FIELD_EX64(value, GITS_CBASER, PHYADDR);
>>> +        s->cq.base_addr <<= R_GITS_CBASER_PHYADDR_SHIFT;
>>> +    }
>>> +}
>>> +
>>>  static MemTxResult gicv3_its_translation_write(void *opaque,
>>> hwaddr offset,
>>>                                                 uint64_t data,
>>> unsigned size,
>>>                                                 MemTxAttrs attrs)
>>> @@ -41,7 +192,73 @@ static MemTxResult its_writel(GICv3ITSState *s,
>>> hwaddr offset,
>>>                                uint64_t value, MemTxAttrs attrs)
>>>  {
>>>      MemTxResult result = MEMTX_OK;
>>> +    int index;
>>>  
>>> +    switch (offset) {
>>> +    case GITS_CTLR:
>>> +        s->ctlr |= (value & ~(s->ctlr));
>>> +
>>> +        if (s->ctlr & ITS_CTLR_ENABLED) {
>>> +            extract_table_params(s);
>>> +            extract_cmdq_params(s);
>>> +            s->creadr = 0;
>> The KVM code also checks the he CBASER and
>> device/collection BASER are valid
>> we do check CBASER and device/collection BASER are valid in this
>> implementation too (via extract_cmdq_params & extract_table_params)
>> To be further checked in subsequent patches:
>> - cache invalidation when turning off
>> - process commands if turned on?
>> - any cmd lock
>>
>>> +        }
>>> +        break;
>>> +    case GITS_CBASER:
>>> +        /*
>>> +         * IMPDEF choice:- 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:
>>> +        /*
>>> +         * IMPDEF choice:- GITS_CBASER register becomes RO if ITS
>>> is
>>> +         *                 already enabled
>>> +         */
>>> +        if (!(s->ctlr & ITS_CTLR_ENABLED)) {
>>> +            s->cbaser = deposit64(s->cbaser, 32, 32, value);
>> you need to reset creadr here also
> Done
>>
>> also CWRITER should be reset to CREADR.
> Done
>> KVM code comment:
>> /*
>>  * CWRITER is architecturally UNKNOWN on reset, but we need to reset
>>  * it to CREADR to make sure we start with an empty command buffer.
>>  */
>>
>>> +        }> +        break;
>>> +    case GITS_CWRITER:
>>> +        s->cwriter = deposit64(s->cwriter, 0, 32,
>>> +                               (value &
>>> ~R_GITS_CWRITER_RETRY_MASK));
>> how do you implement the overflow case?
>> "If GITS_CWRITER is written with a value outside of the valid range
>> specified by
>> GITS_CBASER.Physical_Address and GITS_CBASER.Size, behavior is a
>> CONSTRAINED UNPREDICTABLE choice"
>> for info the KVM code does not write the actual reg
> we write the reg and log a guest error
>> further check: process command?
>>
>>> +        break;
>>> +    case GITS_CWRITER + 4:
>>> +        s->cwriter = deposit64(s->cwriter, 32, 32,
>>> +                               (value &
>>> ~R_GITS_CWRITER_RETRY_MASK));
>>> +        break;
>>> +    case GITS_BASER ... GITS_BASER + 0x3f:
>>> +        /*
>>> +         * IMPDEF choice:- GITS_BASERn register becomes RO if ITS
>>> is
>>> +         *                 already enabled
>>> +         */
>>> +        if (!(s->ctlr & ITS_CTLR_ENABLED)) {
>>> +            index = (offset - GITS_BASER) / 8;
>>> +
>>> +            if (offset & 7) {
>>> +                s->baser[index] = deposit64(s->baser[index], 32,
>>> 32,
>>> +                                            (value &
>>> ~GITS_BASER_VAL_MASK));
>>> +            } else {
>>> +                s->baser[index] = deposit64(s->baser[index], 0,
>>> 32,
>>> +                                            (value &
>>> ~GITS_BASER_VAL_MASK));
>>> +            }
>>> +        }
>>> +        break;
>>> +    case GITS_IIDR:
>>> +    case GITS_IDREGS ... GITS_IDREGS + 0x2f:
>>> +        /* 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;
>>>  }
>>>  
>>> @@ -49,7 +266,55 @@ 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 = gicv3_iidr();
>>> +        break;
>>> +    case GITS_IDREGS ... GITS_IDREGS + 0x2f:
>>> +        /* ID registers */
>>> +        *data = gicv3_idreg(offset - GITS_IDREGS);
>> I am not sure those are the same as the gicv3
> Yes they are, and consistent with the distributor,redistributor gicv3
> idregs implementation too in qemu (as indicated in previous patch
> review comments)
Ah OK

Thanks

Eric
>> on KVM we have
>>         case GITS_PIDR0:
>>                 return 0x92;    /* part number, bits[7:0] */
>>         case GITS_PIDR1:
>>                 return 0xb4;    /* part number, bits[11:8] */
>>         case GITS_PIDR2:
>>                 return GIC_PIDR2_ARCH_GICv3 | 0x0b;
>>         case GITS_PIDR4:
>>                 return 0x40;    /* This is a 64K software visible
>> page */
>>         /* The following are the ID registers for (any) GIC. */
>>         case GITS_CIDR0:
>>                 return 0x0d;
>>         case GITS_CIDR1:
>>                 return 0xf0;
>>         case GITS_CIDR2:
>>                 return 0x05;
>>         case GITS_CIDR3:
>>                 return 0xb1;
>>
>>
>>> +        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 = extract64(s->baser[index], 32, 32);
>>> +        } else {
>>> +            *data = extract64(s->baser[index], 0, 32);
>>> +        }
>>> +        break;
>>> +    default:
>>> +        result = MEMTX_ERROR;
>>> +        break;
>>> +    }
>>>      return result;
>>>  }
>>>  
>>> @@ -57,7 +322,42 @@ 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:
>>> +        /*
>>> +         * IMPDEF choice:- GITS_BASERn register becomes 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);
>>> +        }
>>> +        break;
>>> +    case GITS_CBASER:
>>> +        /*
>>> +         * IMPDEF choice:- GITS_CBASER register becomes RO if ITS
>>> is
>>> +         *                 already enabled
>>> +         */
>>> +        if (!(s->ctlr & ITS_CTLR_ENABLED)) {
>>> +            s->cbaser = value;
>> s->creadr = 0;
>> cwriter = creader?
> Done
>>> +        }
>>> +        break;
>>> +    case GITS_CWRITER:
>>> +        s->cwriter = value & ~R_GITS_CWRITER_RETRY_MASK;
>>> +        break;
>>> +    case GITS_CREADR:
>> RO if GICD_CTLR.DS = 0
>> On KVM side the write access is implemented
> Done
>>> +    case GITS_TYPER:
>>> +        /* 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;
>>>  }
>>>  
>>> @@ -65,7 +365,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;
>>>  }
>>>  
>>> @@ -162,6 +484,9 @@ static void gicv3_arm_its_realize(DeviceState
>>> *dev, Error **errp)
>>>      gicv3_its_init_mmio(s, &gicv3_its_control_ops,
>>> &gicv3_its_translation_ops);
>>>  
>>>      if (s->gicv3->cpu->gicr_typer & GICR_TYPER_PLPIS) {
>>> +        address_space_init(&s->gicv3->dma_as, s->gicv3->dma,
>>> +                           "gicv3-its-sysmem");
>>> +
>>>          /* set the ITS default features supported */
>>>          s->typer = FIELD_DP64(s->typer, GITS_TYPER, PHYSICAL,
>>>                                GITS_TYPE_PHYSICAL);
>>> @@ -208,6 +533,14 @@ static void gicv3_its_reset(DeviceState *dev)
>>>      }
>>>  }
>>>  
>>> +static void gicv3_its_post_load(GICv3ITSState *s)
>>> +{
>>> +    if (s->ctlr & ITS_CTLR_ENABLED) {
>>> +        extract_table_params(s);
>>> +        extract_cmdq_params(s);
>>> +    }
>>> +}
>>> +
>>>  static Property gicv3_its_props[] = {
>>>      DEFINE_PROP_LINK("parent-gicv3", GICv3ITSState, gicv3, "arm-
>>> gicv3",
>>>                       GICv3State *),
>>> @@ -218,10 +551,12 @@ static void gicv3_its_class_init(ObjectClass
>>> *klass, void *data)
>>>  {
>>>      DeviceClass *dc = DEVICE_CLASS(klass);
>>>      GICv3ITSClass *ic = ARM_GICV3_ITS_CLASS(klass);
>>> +    GICv3ITSCommonClass *icc = ARM_GICV3_ITS_COMMON_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);
>>> +    icc->post_load = gicv3_its_post_load;
>>>  }
>>>  
>>>  static const TypeInfo gicv3_its_info = {
>>> diff --git a/hw/intc/gicv3_internal.h b/hw/intc/gicv3_internal.h
>>> index e0b06930a7..d6aaa94e4c 100644
>>> --- a/hw/intc/gicv3_internal.h
>>> +++ b/hw/intc/gicv3_internal.h
>>> @@ -238,7 +238,7 @@ 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, PHYADDRH_64K, 12, 4)
>>>  FIELD(GITS_BASER, ENTRYSIZE, 48, 5)
>>>  FIELD(GITS_BASER, OUTERCACHE, 53, 3)
>>>  FIELD(GITS_BASER, TYPE, 56, 3)
>>> @@ -246,6 +246,17 @@ FIELD(GITS_BASER, INNERCACHE, 59, 3)
>>>  FIELD(GITS_BASER, INDIRECT, 62, 1)
>>>  FIELD(GITS_BASER, VALID, 63, 1)
>>>  
>>> +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_CWRITER, RETRY, 0, 1)
>>> +FIELD(GITS_CWRITER, OFFSET, 5, 15)
>>> +
>>> +FIELD(GITS_CTLR, ENABLED, 0, 1)
>>>  FIELD(GITS_CTLR, QUIESCENT, 31, 1)
>>>  
>>>  FIELD(GITS_TYPER, PHYSICAL, 0, 1)
>>> @@ -257,6 +268,13 @@ FIELD(GITS_TYPER, PTA, 19, 1)
>>>  FIELD(GITS_TYPER, CIDBITS, 32, 4)
>>>  FIELD(GITS_TYPER, CIL, 36, 1)
>>>  
>>> +#define GITS_IDREGS           0xFFD0
>>> +
>>> +#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
>>> @@ -264,6 +282,14 @@ FIELD(GITS_TYPER, CIL, 36, 1)
>>>  #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
>> Why not naming _4K 16K 64K instead of _0, 1, 2?
> Done,as indicated above
>>> +
>>> +#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..1fd5cedbbd 100644
>>> --- a/include/hw/intc/arm_gicv3_common.h
>>> +++ b/include/hw/intc/arm_gicv3_common.h
>>> @@ -226,6 +226,9 @@ struct GICv3State {
>>>      int dev_fd; /* kvm device fd if backed by kvm vgic support */
>>>      Error *migration_blocker;
>>>  
>>> +    MemoryRegion *dma;
>>> +    AddressSpace dma_as;
>>> +
>>>      /* Distributor */
>>>  
>>>      /* for a GIC with the security extensions the NS banked
>>> version of this
>>> diff --git a/include/hw/intc/arm_gicv3_its_common.h
>>> b/include/hw/intc/arm_gicv3_its_common.h
>>> index 65d1191db1..78b1ba7e6b 100644
>>> --- a/include/hw/intc/arm_gicv3_its_common.h
>>> +++ b/include/hw/intc/arm_gicv3_its_common.h
>>> @@ -41,6 +41,32 @@
>>>  
>>>  #define GITS_TRANSLATER  0x0040
>>>  
>>> +typedef struct {
>>> +    bool valid;
>>> +    bool indirect;
>>> +    uint16_t entry_sz;
>>> +    uint32_t page_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 page_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;
>>>  
>>> @@ -63,6 +89,10 @@ struct GICv3ITSState {
>>>      uint64_t creadr;
>>>      uint64_t baser[8];
>>>  
>>> +    DevTableDesc  dt;
>>> +    CollTableDesc ct;
>>> +    CmdQDesc      cq;
>>> +
>>>      Error *migration_blocker;
>>>  };
>> Thanks
>>
>> Eric
>>>  
>>>
> 
> 
> 
> 



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

* Re: [PATCH v4 3/8] hw/intc: GICv3 ITS command queue framework
  2021-06-16 21:02     ` shashi.mallela
@ 2021-06-21 10:03       ` Eric Auger
  2021-06-28 21:58         ` shashi.mallela
  0 siblings, 1 reply; 52+ messages in thread
From: Eric Auger @ 2021-06-21 10:03 UTC (permalink / raw)
  To: shashi.mallela, peter.maydell, leif, rad; +Cc: qemu-arm, qemu-devel



On 6/16/21 11:02 PM, shashi.mallela@linaro.org wrote:
> Hi Eric,
> 
> Please find my responses inline (below):-
> 
> On Sun, 2021-06-13 at 16:13 +0200, Eric Auger wrote:
>> Hi Sashi,
>>
>> On 6/2/21 8:00 PM, Shashi Mallela 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  | 295
>>> +++++++++++++++++++++++++++++++++++++++
>>>  hw/intc/gicv3_internal.h |  37 +++++
>>>  2 files changed, 332 insertions(+)
>>>
>>> diff --git a/hw/intc/arm_gicv3_its.c b/hw/intc/arm_gicv3_its.c
>>> index af60f19c98..6551c577b3 100644
>>> --- a/hw/intc/arm_gicv3_its.c
>>> +++ b/hw/intc/arm_gicv3_its.c
>>> @@ -49,6 +49,295 @@ static uint64_t baser_base_addr(uint64_t value,
>>> uint32_t page_sz)
>>>      return result;
>>>  }
>>>  
>>> +static MemTxResult update_cte(GICv3ITSState *s, uint16_t icid,
>>> bool valid,
>>> +                              uint64_t rdbase)
>>> +{
>>> +    AddressSpace *as = &s->gicv3->dma_as;
>>> +    uint64_t value;
>>> +    uint64_t l2t_addr;
>>> +    bool valid_l2t;
>>> +    uint32_t l2t_id;
>>> +    uint32_t max_l2_entries;
>>> +    uint64_t cte = 0;
>>> +    MemTxResult res = MEMTX_OK;
>>> +
>>> +    if (!s->ct.valid) {
>> Isn't it a guest log error case. Also you return MEMTX_OK in that
>> case.
>> Is that what you want?
> Yes,because the current implementation treats all command specific
> errors as "ignored" and moves onto next command in the queue.MEMTX
> return values are significant for dma read/write status and in case of
> error we stall the command processing 
OK
>>> +        return res;
>>> +    }
>>> +
>>> +    if (valid) {
>>> +        /* add mapping entry to collection table */
>>> +        cte = (valid & VALID_MASK) |
>>> +              ((rdbase & RDBASE_PROCNUM_MASK) << 1ULL);
>> Do you really need to sanitize rdbase again?
> Not required,have rectified it.
>>> +    }
>>> +
>>> +    /*
>>> +     * 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.
>>> +     */
>>> +    if (s->ct.indirect) {
>>> +        l2t_id = icid / (s->ct.page_sz / L1TABLE_ENTRY_SIZE);
>>> +
>>> +        value = address_space_ldq_le(as,
>>> +                                     s->ct.base_addr +
>>> +                                     (l2t_id *
>>> L1TABLE_ENTRY_SIZE),
>>> +                                     MEMTXATTRS_UNSPECIFIED,
>>> &res);
>>> +
>>> +        if (res != MEMTX_OK) {
>>> +            return res;
>>> +        }
>>> +
>>> +        valid_l2t = (value >> VALID_SHIFT) & VALID_MASK;
>>> +
>>> +        if (valid_l2t) {
>>> +            max_l2_entries = s->ct.page_sz / s->ct.entry_sz;
>>> +
>>> +            l2t_addr = value & ((1ULL << 51) - 1);
>>> +
>>> +            address_space_stq_le(as, l2t_addr +
>>> +                                 ((icid % max_l2_entries) *
>>> GITS_CTE_SIZE),
>>> +                                 cte, MEMTXATTRS_UNSPECIFIED,
>>> &res);
>>> +        }
>>> +    } else {
>>> +        /* Flat level table */
>>> +        address_space_stq_le(as, s->ct.base_addr + (icid *
>>> GITS_CTE_SIZE),
>>> +                             cte, MEMTXATTRS_UNSPECIFIED, &res);
>>> +    }
>>> +    return res;
>>> +}
>>> +
>>> +static MemTxResult process_mapc(GICv3ITSState *s, uint32_t offset)
>>> +{
>>> +    AddressSpace *as = &s->gicv3->dma_as;
>>> +    uint16_t icid;
>>> +    uint64_t rdbase;
>>> +    bool valid;
>>> +    MemTxResult res = MEMTX_OK;
>>> +    uint64_t value;
>>> +
>>> +    offset += NUM_BYTES_IN_DW;
>>> +    offset += NUM_BYTES_IN_DW;
>> May be relevant to add some trace points for debuggability.
> Probably the trace functionality for ITS can be taken up as a seperate
> task/feature TODO.
Yes of course. It may just be useful for you as well to debug ;-)
>>> +
>>> +    value = address_space_ldq_le(as, s->cq.base_addr + offset,
>>> +                                 MEMTXATTRS_UNSPECIFIED, &res);
>>> +
>>> +    if (res != MEMTX_OK) {
>>> +        return res;
>>> +    }
>>> +
>>> +    icid = value & ICID_MASK;
>>> +
>>> +    rdbase = (value >> R_MAPC_RDBASE_SHIFT) & RDBASE_PROCNUM_MASK;
>> usually the mask is applied before the shift.
> Here we are extracting only 16 bit rdbase(processor number) value by
> masking with RDBASE_PROCNUM_MASK only after we have right shifted the
> rdbase offset from the 64 bit DW value.
> As an alternative,I could have used rdbase = (value &
> R_MAPC_RDBASE_MASK) to first extract the 32 bits rdbase value from DW
> and then later mask again with RDBASE_PROCNUM_MASK to narrow it down to
> 16 bit rdbase(processor number).
My comment rather was about the fact that generally the mask applied to
the shifted location and then you shift the masked field. I notived
Peter also made this comment in 4/8 (FIELD macro). You tend to use the
same pattern in different places in your series.
>>> +
>>> +    valid = (value >> VALID_SHIFT) & VALID_MASK;
>> use FIELD, see below
>>> +
>>> +    if ((icid > s->ct.max_collids) || (rdbase > s->gicv3-
>>>> num_cpu)) {
>> you also need to check against ITS_CIDBITS limit?
> CIDBITS limits is being checked through the s->ct.max_collids member
> above
Ah OK
>>> +        qemu_log_mask(LOG_GUEST_ERROR,
>>> +                      "ITS MAPC: invalid collection table
>>> attributes "
>>> +                      "icid %d rdbase %lu\n",  icid, rdbase);
>>> +        /*
>>> +         * in this implementation,in case of error
>>> +         * we ignore this command and move onto the next
>>> +         * command in the queue
>> spec says a command error occurs in that case.
> Yes,we chose to ignore the  error'ed command and move onto the next one
> in the queue as per command error options in the spec
>>> +         */
>>> +    } else {
>>> +        res = update_cte(s, icid, valid, rdbase);
>>> +    }
>>> +
>>> +    return res;
>>> +}
>>> +
>>> +static MemTxResult update_dte(GICv3ITSState *s, uint32_t devid,
>>> bool valid,
>>> +                              uint8_t size, uint64_t itt_addr)
>>> +{
>>> +    AddressSpace *as = &s->gicv3->dma_as;
>>> +    uint64_t value;
>>> +    uint64_t l2t_addr;
>>> +    bool valid_l2t;
>>> +    uint32_t l2t_id;
>>> +    uint32_t max_l2_entries;
>>> +    uint64_t dte = 0;
>>> +    MemTxResult res = MEMTX_OK;
>>> +
>>> +    if (s->dt.valid) {
>>> +        if (valid) {
>>> +            /* add mapping entry to device table */
>>> +            dte = (valid & VALID_MASK) |
>>> +                  ((size & SIZE_MASK) << 1U) |
>>> +                  ((itt_addr & ITTADDR_MASK) << 6ULL);
>>> +        }
>>> +    } else {
>>> +        return res;
>>> +    }
>>> +
>>> +    /*
>>> +     * 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.
>>> +     */
>>> +    if (s->dt.indirect) {
>>> +        l2t_id = devid / (s->dt.page_sz / L1TABLE_ENTRY_SIZE);
>>> +
>>> +        value = address_space_ldq_le(as,
>>> +                                     s->dt.base_addr +
>>> +                                     (l2t_id *
>>> L1TABLE_ENTRY_SIZE),
>>> +                                     MEMTXATTRS_UNSPECIFIED,
>>> &res);
>>> +
>>> +        if (res != MEMTX_OK) {
>>> +            return res;
>>> +        }
>>> +
>>> +        valid_l2t = (value >> VALID_SHIFT) & VALID_MASK;
>>> +
>>> +        if (valid_l2t) {
>>> +            max_l2_entries = s->dt.page_sz / s->dt.entry_sz;
>>> +
>>> +            l2t_addr = value & ((1ULL << 51) - 1);
>>> +
>>> +            address_space_stq_le(as, l2t_addr +
>>> +                                 ((devid % max_l2_entries) *
>>> GITS_DTE_SIZE),
>>> +                                 dte, MEMTXATTRS_UNSPECIFIED,
>>> &res);
>>> +        }
>>> +    } else {
>>> +        /* Flat level table */
>>> +        address_space_stq_le(as, s->dt.base_addr + (devid *
>>> GITS_DTE_SIZE),
>>> +                             dte, MEMTXATTRS_UNSPECIFIED, &res);
>>> +    }
>>> +    return res;
>>> +}
>>> +
>>> +static MemTxResult process_mapd(GICv3ITSState *s, uint64_t value,
>> you do not seem to use the input value, remove it?
> yes we are using the input value,which is the 1st DW from the command
> to extract the deviceid (devid) field below
Hum my mistake sorry.
>>> +                                uint32_t offset)
>>> +{
>>> +    AddressSpace *as = &s->gicv3->dma_as;
>>> +    uint32_t devid;
>>> +    uint8_t size;
>>> +    uint64_t itt_addr;
>>> +    bool valid;
>>> +    MemTxResult res = MEMTX_OK;
>>> +
>>> +    devid = (value >> DEVID_SHIFT) & DEVID_MASK;
>>> +
>>> +    offset += NUM_BYTES_IN_DW;
>>> +    value = address_space_ldq_le(as, s->cq.base_addr + offset,
>>> +                                 MEMTXATTRS_UNSPECIFIED, &res);
>>> +
>>> +    if (res != MEMTX_OK) {
>>> +        return res;
>>> +    }
>>> +
>>> +    size = (value & SIZE_MASK);
>>> +
>>> +    offset += NUM_BYTES_IN_DW;
>>> +    value = address_space_ldq_le(as, s->cq.base_addr + offset,
>>> +                                 MEMTXATTRS_UNSPECIFIED, &res);
>>> +
>>> +    if (res != MEMTX_OK) {
>>> +        return res;
>>> +    }
>>> +
>>> +    itt_addr = (value >> ITTADDR_SHIFT) & ITTADDR_MASK;
>> this looks weird to me, usually we apply the mask first and then
>> shift.
> from the 64 bit DW,we right shift (by 8)to align the itt_addr at 0th
> position and extract 44 bits(0 to 43) using the mask 
ditto
>>> +
>>> +    valid = (value >> VALID_SHIFT) & VALID_MASK;
>> use FIELD_EX64()?
>>> +
>>> +    if ((devid > s->dt.max_devids) ||
>>> +        (size > FIELD_EX64(s->typer, GITS_TYPER, IDBITS))) {
>> ITS_IDBITS?
> IDBITS is one of the fields in GITS_TYPER and the field naming is
> consistent with the spec definition
>>> +        qemu_log_mask(LOG_GUEST_ERROR,
>>> +                      "ITS MAPD: invalid device table attributes "
>>> +                      "devid %d or size %d\n", devid, size);
>>> +        /*
>>> +         * in this implementation, in case of error
>>> +         * we ignore this command and move onto the next
>>> +         * command in the queue
>>> +         */
>>> +    } else {
>>> +        res = update_dte(s, devid, valid, size, itt_addr);
>>> +    }
>>> +
>>> +    return res;
>>> +}
>>> +
>>> +/*
>>> + * Current implementation blocks until all
>>> + * commands are processed
>>> + */
>>> +static void 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->dma_as;
>>> +    MemTxResult res = MEMTX_OK;
>>> +    uint8_t cmd;
>>> +
>>> +    if (!(s->ctlr & ITS_CTLR_ENABLED)) {
>>> +        return;
>>> +    }
>>> +
>>> +    wr_offset = FIELD_EX64(s->cwriter, GITS_CWRITER, OFFSET);
>>> +
>>> +    if (wr_offset > s->cq.max_entries) {
>> Shouldn't this be checked on cwrite write instead?
> Yes we are checking within the cwriter write scope,just that the check
> is happening through this function (called during cwrite write)
>>> +        qemu_log_mask(LOG_GUEST_ERROR,
>>> +                      "%s: invalid write offset "
>>> +                      "%d\n", __func__, wr_offset);
>>> +        return;
>>> +    }
>>> +
>>> +    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:
>>> +            /*
>>> +             * 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.
>>> +             * Hence no further processing is required for SYNC
>>> command.
>>> +             */
>>> +            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 {
>>> +            /*
>>> +             * in this implementation,in case of dma read/write
>>> error
>>> +             * we stall the command processing
>>> +             */
>>> +            s->creadr = FIELD_DP64(s->creadr, GITS_CREADR,
>>> STALLED, 1);
>>> +            qemu_log_mask(LOG_GUEST_ERROR,
>>> +                          "%s: %x cmd processing failed!!\n",
>>> __func__, cmd);
>>> +            break;
>>> +        }
>>> +    }
>>> +}
>>> +
>>>  static void extract_table_params(GICv3ITSState *s)
>>>  {
>>>      uint16_t num_pages = 0;
>>> @@ -226,6 +515,9 @@ static MemTxResult its_writel(GICv3ITSState *s,
>>> hwaddr offset,
>>>      case GITS_CWRITER:
>>>          s->cwriter = deposit64(s->cwriter, 0, 32,
>>>                                 (value &
>>> ~R_GITS_CWRITER_RETRY_MASK));
>>> +        if (s->cwriter != s->creadr) {
>>> +            process_cmdq(s);
>> I would expect process_cmdq() to be called as well on ITS enable
> Done
>>> +        }
>>>          break;
>>>      case GITS_CWRITER + 4:
>>>          s->cwriter = deposit64(s->cwriter, 32, 32,
>>> @@ -346,6 +638,9 @@ static MemTxResult its_writell(GICv3ITSState
>>> *s, hwaddr offset,
>>>          break;
>>>      case GITS_CWRITER:
>>>          s->cwriter = value & ~R_GITS_CWRITER_RETRY_MASK;
>>> +        if (s->cwriter != s->creadr) {
>>> +            process_cmdq(s);
>>> +        }
>>>          break;
>>>      case GITS_CREADR:
>>>      case GITS_TYPER:
>>> diff --git a/hw/intc/gicv3_internal.h b/hw/intc/gicv3_internal.h
>>> index d6aaa94e4c..0932a30560 100644
>>> --- a/hw/intc/gicv3_internal.h
>>> +++ b/hw/intc/gicv3_internal.h
>>> @@ -253,6 +253,9 @@ 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)
>>>  
>>> @@ -289,6 +292,40 @@ 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_LENGTH                  16
>>> +#define ICID_MASK                 ((1U << ICID_LENGTH) - 1)
>> can't you use FIELD') as well for the ICID?
> in addition to MAPC command ICID is a common field for MAPTI,MAPI
> commands as well,hence wanted to keep it common and seperate
>>> +FIELD(MAPC, RDBASE, 16, 32)
>>> +
>>> +#define RDBASE_PROCNUM_LENGTH        16
>>> +#define RDBASE_PROCNUM_MASK       ((1ULL << RDBASE_PROCNUM_LENGTH)
>>> - 1)
>> why do we have both the RDBASE FIELD def and above defs?
> RDBASE FIELD def points to the rdbase field within the MAPC
> command,while the RDBASE_PROCNUM_ defines are used to consider 16 bit
> PE number as the target destination instead of redistributor base
> address option.
>>> +
>>> +#define DEVID_SHIFT                  32
>>> +#define DEVID_LENGTH                 32
>>> +#define DEVID_MASK                ((1ULL << DEVID_LENGTH) - 1)
>> we don't have any DEVID field in MAPC, I guess it belongs to MAPD?
> MAPC doesnt have a DEVID field ,but it is a common field in
> MAPD,INT,MAPI,MAPTI commands(at the same offset)
Yes but above there is a command saying "MAPC command fields */
>>> +
>>> +/* MAPD command fields */
>>> +#define ITTADDR_LENGTH               44
>>> +#define ITTADDR_SHIFT                 8
>>> +#define ITTADDR_MASK              ((1ULL << ITTADDR_LENGTH) - 1)
>>> +#define SIZE_MASK                 0x1f
>> Can't you homogenize the definition, use field() and/or prefix with
>> the
>> cmd name when not common to severals cmds?
> Since ITTADDR_MASK is common to both MAPD command as well as device
> table entry field,didnt want to go with field() as the MAPD tag-name in
> device table entry would be insignificant
>>
>>> +
>>> +#define VALID_SHIFT               63
>>> +#define VALID_MASK                1ULL
>>>  
>>>  /**
>>>   * Default features advertised by this version of ITS
>>>
>> Thanks
>>
>> Eric
>>
> 
> 
> 
> 
Thanks

Eric



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

* Re: [PATCH v4 4/8] hw/intc: GICv3 ITS Command processing
  2021-06-16 21:02     ` shashi.mallela
@ 2021-06-21 10:13       ` Eric Auger
  2021-06-28 22:04         ` shashi.mallela
  0 siblings, 1 reply; 52+ messages in thread
From: Eric Auger @ 2021-06-21 10:13 UTC (permalink / raw)
  To: shashi.mallela, peter.maydell, leif, rad; +Cc: qemu-arm, qemu-devel



On 6/16/21 11:02 PM, shashi.mallela@linaro.org wrote:
> Hi Eric,
> 
> Please find my responses inline (below):-
> 
> On Sun, 2021-06-13 at 17:55 +0200, Eric Auger wrote:
>> Hi Shashi,
>>
>> On 6/2/21 8:00 PM, Shashi Mallela 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            | 334
>>> +++++++++++++++++++++++++++++
>>>  hw/intc/gicv3_internal.h           |  12 ++
>>>  include/hw/intc/arm_gicv3_common.h |   2 +
>>>  3 files changed, 348 insertions(+)
>>>
>>> diff --git a/hw/intc/arm_gicv3_its.c b/hw/intc/arm_gicv3_its.c
>>> index 6551c577b3..82bb5b84ef 100644
>>> --- a/hw/intc/arm_gicv3_its.c
>>> +++ b/hw/intc/arm_gicv3_its.c
>>> @@ -28,6 +28,13 @@ 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;
>> Add a comment to explain what this enum stand for. This sounds
>> misleading to me versus the command IDs. Why don't you use the cmd id
>> then and add NONE?
> This is an internal enum used to distinguish between interrupt
> triggered via command queue and interrupt triggered via gits_translater
> write.Will add the same comment in code.
> Since NONE is only 1 command applicable for GITS_TRANSLATER,started
> with it so that in the future if any further command queue commands
> have to be added we can just extend the numbering.
>>> +
>>>  static uint64_t baser_base_addr(uint64_t value, uint32_t page_sz)
>>>  {
>>>      uint64_t result = 0;
>>> @@ -49,6 +56,315 @@ static uint64_t baser_base_addr(uint64_t value,
>>> uint32_t page_sz)
>>>      return result;
>>>  }
>>>  
>>> +static bool get_cte(GICv3ITSState *s, uint16_t icid, uint64_t
>>> *cte,
>>> +                    MemTxResult *res)
>>> +{
>>> +    AddressSpace *as = &s->gicv3->dma_as;
>>> +    uint64_t l2t_addr;
>>> +    uint64_t value;
>>> +    bool valid_l2t;
>>> +    uint32_t l2t_id;
>>> +    uint32_t max_l2_entries;
>>> +    bool status = false;
>>> +
>>> +    if (s->ct.indirect) {
>>> +        l2t_id = icid / (s->ct.page_sz / L1TABLE_ENTRY_SIZE);
>>> +
>>> +        value = address_space_ldq_le(as,
>>> +                                     s->ct.base_addr +
>>> +                                     (l2t_id *
>>> L1TABLE_ENTRY_SIZE),
>>> +                                     MEMTXATTRS_UNSPECIFIED, res);
>>> +
>>> +        if (*res == MEMTX_OK) {
>>> +            valid_l2t = (value >> VALID_SHIFT) & VALID_MASK;
>>> +
>>> +            if (valid_l2t) {
>>> +                max_l2_entries = s->ct.page_sz / s->ct.entry_sz;
>>> +
>>> +                l2t_addr = value & ((1ULL << 51) - 1);
>>> +
>>> +                *cte =  address_space_ldq_le(as, l2t_addr +
>>> +                                    ((icid % max_l2_entries) *
>>> GITS_CTE_SIZE),
>>> +                                    MEMTXATTRS_UNSPECIFIED, res);
>>> +           }
>>> +       }
>>> +    } else {
>>> +        /* Flat level table */
>>> +        *cte =  address_space_ldq_le(as, s->ct.base_addr +
>>> +                                     (icid * GITS_CTE_SIZE),
>>> +                                      MEMTXATTRS_UNSPECIFIED,
>>> res);
>>> +    }
>>> +
>>> +    if (*cte & VALID_MASK) {
>>> +        status = true;
>>> +    }
>>> +
>>> +    return status;
>>> +}
>>> +
>>> +static MemTxResult update_ite(GICv3ITSState *s, uint32_t eventid,
>>> uint64_t dte,
>>> +                              uint64_t itel, uint32_t iteh)
>> why not introducing an ite struct instead of the h/l args?based on
>> the same reason for packing individual fields within bit positions
>> instead of using more memory to store the same fields as struct
>> members.
> Will create an ite struct with existing itel & iteh as members and
> retain their bit fields processing to avoid extra params being passed.
>>> +{
>>> +    AddressSpace *as = &s->gicv3->dma_as;
>>> +    uint64_t itt_addr;
>>> +    MemTxResult res = MEMTX_OK;
>>> +
>>> +    itt_addr = (dte >> 6ULL) & ITTADDR_MASK;
>>> +    itt_addr <<= ITTADDR_SHIFT; /* 256 byte aligned */
>>> +
>>> +    address_space_stq_le(as, itt_addr + (eventid *
>>> sizeof(uint64_t)),
>>> +                         itel, MEMTXATTRS_UNSPECIFIED, &res);
>>> +
>>> +    if (res == MEMTX_OK) {
>>> +        address_space_stl_le(as, itt_addr + ((eventid +
>>> sizeof(uint64_t)) *
>>> +                             sizeof(uint32_t)), iteh,
>>> MEMTXATTRS_UNSPECIFIED,
>>> +                             &res);
>>> +    }
>>> +   return res;
>>> +}
>>> +
>>> +static bool get_ite(GICv3ITSState *s, uint32_t eventid, uint64_t
>>> dte,
>>> +                    uint16_t *icid, uint32_t *pIntid, MemTxResult
>>> *res)
>>> +{
>>> +    AddressSpace *as = &s->gicv3->dma_as;
>>> +    uint64_t itt_addr;
>>> +    bool status = false;
>>> +    uint64_t itel = 0;
>>> +    uint32_t iteh = 0;
>>> +
>>> +    itt_addr = (dte >> 6ULL) & ITTADDR_MASK;
>>> +    itt_addr <<= ITTADDR_SHIFT; /* 256 byte aligned */
>>> +
>>> +    itel = address_space_ldq_le(as, itt_addr + (eventid *
>>> sizeof(uint64_t)),
>>> +                                MEMTXATTRS_UNSPECIFIED, res);
>>> +
>>> +    if (*res == MEMTX_OK) {
>>> +        iteh = address_space_ldl_le(as, itt_addr + ((eventid +
>>> +                                    sizeof(uint64_t)) *
>>> sizeof(uint32_t)),
>>> +                                    MEMTXATTRS_UNSPECIFIED, res);
>>> +
>>> +        if (*res == MEMTX_OK) {
>>> +            if (itel & VALID_MASK) {
>>> +                if ((itel >> ITE_ENTRY_INTTYPE_SHIFT) &
>>> GITS_TYPE_PHYSICAL) {
>>> +                    *pIntid = (itel >> ITE_ENTRY_INTID_SHIFT) &
>>> +                               ITE_ENTRY_INTID_MASK;
>>> +                    *icid = iteh & ITE_ENTRY_ICID_MASK;
>>> +                    status = true;
>>> +                }
>>> +            }
>>> +        }
>>> +    }
>>> +    return status;
>>> +}
>>> +
>>> +static uint64_t get_dte(GICv3ITSState *s, uint32_t devid,
>>> MemTxResult *res)
>> maybe the code would be more readable if you were returning a strcut
>> for
>> dte/cte instead of uint64_t. The decoding of the fields would be done
>> here instead?
> Both dte and cte are 8 bytes and hence chose a single uint64_t to
> efficiently utilize the applicable bit fields in the variable instead
> of a struct(either with bitfields to match the current field layouts
> within dte/cte/ite entries or use more memory variables within the
> struct to hold each individual dte/cte/ite fields)
>>> +{
>>> +    AddressSpace *as = &s->gicv3->dma_as;
>>> +    uint64_t l2t_addr;
>>> +    uint64_t value;
>>> +    bool valid_l2t;
>>> +    uint32_t l2t_id;
>>> +    uint32_t max_l2_entries;
>>> +
>>> +    if (s->dt.indirect) {
>>> +        l2t_id = devid / (s->dt.page_sz / L1TABLE_ENTRY_SIZE);
>>> +
>>> +        value = address_space_ldq_le(as,
>>> +                                     s->dt.base_addr +
>>> +                                     (l2t_id *
>>> L1TABLE_ENTRY_SIZE),
>>> +                                     MEMTXATTRS_UNSPECIFIED, res);
>>> +
>>> +        if (*res == MEMTX_OK) {
>>> +            valid_l2t = (value >> VALID_SHIFT) & VALID_MASK;
>>> +
>>> +            if (valid_l2t) {
>>> +                max_l2_entries = s->dt.page_sz / s->dt.entry_sz;
>>> +
>>> +                l2t_addr = value & ((1ULL << 51) - 1);
>>> +
>>> +                value =  address_space_ldq_le(as, l2t_addr +
>>> +                                   ((devid % max_l2_entries) *
>>> GITS_DTE_SIZE),
>>> +                                   MEMTXATTRS_UNSPECIFIED, res);
>>> +            }
>>> +        }
>>> +    } else {
>>> +        /* Flat level table */
>>> +        value = address_space_ldq_le(as, s->dt.base_addr +
>>> +                                     (devid * GITS_DTE_SIZE),
>>> +                                     MEMTXATTRS_UNSPECIFIED, res);
>>> +    }
>>> +
>>> +    return value;
>>> +}
>> I think a common helper could be defined for get_cte and get_dte.
> was just trying to keep the code modular for getting dte and cte
> instead of various if else's within a common helper 
>>> +
>>> +static MemTxResult process_int(GICv3ITSState *s, uint64_t value,
>>> +                               uint32_t offset, ItsCmdType cmd)
>> this is a bit misleanding as INT is a command. You should rename it I
>> think. Also it is not really homogeneous with other cmds, ie. you
>> have
>> process_mapti, process_mapd, process_mac and all the remaining cmds
>> are
>> handled though this one? At least add a doc comment to explain what
>> it does.
> the naming of this function is along the lines of other ITS commands
> i.e process_xxx format where xxx is the actual ITS command.
> There is no connection between handling of process_mapd,process_mapc
> with process_int.Each of them handle their respective command
> processing independently.
Well I am not big fan of keeping that name ;-) but well.
>>> +{
>>> +    AddressSpace *as = &s->gicv3->dma_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;
>>> +    uint64_t itel = 0;
>>> +    uint32_t iteh = 0;
>>> +
>>> +    if (cmd == NONE) {
>>> +        devid = offset;
>>> +    } else {
>>> +        devid = (value >> DEVID_SHIFT) & DEVID_MASK;
>>> +
>>> +        offset += NUM_BYTES_IN_DW;
>>> +        value = address_space_ldq_le(as, s->cq.base_addr + offset,
>>> +                                     MEMTXATTRS_UNSPECIFIED,
>>> &res);
>>> +    }
>>> +
>>> +    if (res != MEMTX_OK) {
>>> +        return res;
>>> +    }
>>> +
>>> +    eventid = (value & EVENTID_MASK);
>>> +
>>> +    dte = get_dte(s, devid, &res);
>>> +
>>> +    if (res != MEMTX_OK) {
>>> +        return res;
>>> +    }
>>> +    dte_valid = dte & VALID_MASK;
>>> +
>>> +    if (dte_valid) {
>>> +        max_eventid = (1UL << (((dte >> 1U) & SIZE_MASK) + 1));
>> THE DTE format is implementation defined, and the decoding is hard to
>> follow because it is documented somewhere else.
> The decoding of fields within each of dte,cte and ite has been
> explained as comments in gicv3_internal.h via
> GITS_DTE_SIZE,GITS_CTE_SIZE, ITS_ITT_ENTRY_SIZE defines
>>> +
>>> +        ite_valid = get_ite(s, eventid, dte, &icid, &pIntid,
>>> &res);
>>> +
>>> +        if (res != MEMTX_OK) {
>>> +            return res;
>>> +        }
>>> +
>>> +        if (ite_valid) {
>>> +            cte_valid = get_cte(s, icid, &cte, &res);
>>> +        }
>>> +
>>> +        if (res != MEMTX_OK) {
>>> +            return res;
>>> +        }
>> instead of having this process_int() helper, why not having a helper
>> doing just the decoding phase, ie.
>> get_dte -> get_ite -> get_cte and collect the relevant info and
>> collect
>> error and then keep the actual cmd processing in the switch?
> As explained previously chose to keep the code modular instead of a
> large monolothic structure with all the functionality in 1 place.
> process_cmdq handles the command queue (and their read/write offsets)
> while the individual ITS command handling is done by respective
> functions

OK That's not my opinion but it is not a blocker anyway ;-)
>>> +    }
>>> +
>>> +    if ((devid > s->dt.max_devids) || !dte_valid || !ite_valid ||
>>> +            !cte_valid || (eventid > max_eventid)) {
>>> +        qemu_log_mask(LOG_GUEST_ERROR,
>>> +                      "%s: invalid interrupt translation table
>>> attributes "
>>> +                      "devid %d or eventid %d\n",
>>> +                      __func__, devid, eventid);
>> the msg does not necessarily match the error case. You mention ITT
>> issue
>> whereas the problem may come from invalid DTE, CTE, or even devid.
> will change it to consider the missing cases
>>> +        /*
>>> +         * in this implementation,in case of error
>>> +         * we ignore this command and move onto the next
>>> +         * command in the queue
>>> +         */
>> so you don't return an error?
> yes we dont,we just ignore this command and move onto next one in the
> queue
>>> +    } else {
>>> +        /*
>>> +         * Current implementation only supports rdbase == procnum
>>> +         * Hence rdbase physical address is ignored
>>> +         */
>>> +        if (cmd == DISCARD) {
>>> +            /* remove mapping from interrupt translation table */
>>> +            res = update_ite(s, eventid, dte, itel, iteh);
>> iteh and itel always are 0, why not use a struct ite with valid field
>> unset.
> based on the same reason for packing individual fields within bit
> positions instead of using more memory to store the same fields as
> struct members.
> I could however create an ite struct with existing itel & iteh as
> members and retain their bit fields processing. 
>>> +        }
>>> +    }
>>> +
>>> +    return res;
>>> +}
>>> +
>>> +static MemTxResult process_mapti(GICv3ITSState *s, uint64_t value,
>>> +                                 uint32_t offset, bool
>>> ignore_pInt)
>>> +{
>>> +    AddressSpace *as = &s->gicv3->dma_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 itel = 0;
>>> +    uint32_t iteh = 0;
>>> +    uint32_t int_spurious = INTID_SPURIOUS;
>>> +
>>> +    devid = (value >> DEVID_SHIFT) & DEVID_MASK;
>>> +    offset += NUM_BYTES_IN_DW;
>>> +    value = address_space_ldq_le(as, s->cq.base_addr + offset,
>>> +                                 MEMTXATTRS_UNSPECIFIED, &res);
>>> +
>>> +    if (res != MEMTX_OK) {
>>> +        return 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);
>>> +
>>> +    if (res != MEMTX_OK) {
>>> +        return res;
>>> +    }
>>> +
>>> +    icid = value & ICID_MASK;
>>> +
>>> +    dte = get_dte(s, devid, &res);
>>> +
>>> +    if (res != MEMTX_OK) {
>>> +        return res;
>>> +    }
>>> +    dte_valid = dte & VALID_MASK;
>>> +
>>> +    max_eventid = (1UL << (((dte >> 1U) & SIZE_MASK) + 1));
>> Again I think you would gain in readibility if get_cte were to return
>> a
>> struct and you would avoid code duplication.
> Like mentioned earlier, preferred to use a single uint64_t for cte (and
> dte) instead of a struct (either with bitfields to match the current
> field layouts within dte/cte/ite entries or use more memory variables
> within the struct to hold each individual dte/cte/ite fields).The
> layout of all these functions follows the pseudocde format defined
> under each ITS command in the spec.
>>> +
>>> +    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)))) {
>>> +        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 ite entry to interrupt translation table */
>>> +        itel = (dte_valid & VALID_MASK) | (GITS_TYPE_PHYSICAL <<
>>> +                                           ITE_ENTRY_INTTYPE_SHIFT
>>> );
>>> +
>>> +        if (ignore_pInt) {
>>> +            itel |= (eventid << ITE_ENTRY_INTID_SHIFT);
>>> +        } else {
>>> +            itel |= (pIntid << ITE_ENTRY_INTID_SHIFT);
>>> +        }
>>> +        itel |= (int_spurious << ITE_ENTRY_INTSP_SHIFT);
>>> +        iteh |= icid;
>>> +
>>> +        res = update_ite(s, eventid, dte, itel, iteh);
>>> +    }
>>> +
>>> +    return res;
>>> +}
>>> +
>>>  static MemTxResult update_cte(GICv3ITSState *s, uint16_t icid,
>>> bool valid,
>>>                                uint64_t rdbase)
>>>  {
>>> @@ -295,8 +611,10 @@ static void 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:
>>>              /*
>>> @@ -313,10 +631,13 @@ static void 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;
>>> @@ -472,7 +793,20 @@ static MemTxResult
>>> gicv3_its_translation_write(void *opaque, hwaddr offset,
>>>                                                 uint64_t data,
>>> unsigned size,
>>>                                                 MemTxAttrs attrs)
>>>  {
>>> +    GICv3ITSState *s = (GICv3ITSState *)opaque;
>>>      MemTxResult result = MEMTX_OK;
>>> +    uint32_t devid = 0;
>>> +
>>> +    switch (offset) {
>>> +    case GITS_TRANSLATER:
>>> +        if (s->ctlr & ITS_CTLR_ENABLED) {
>>> +            devid = attrs.requester_id;
>>> +            result = process_int(s, data, devid, NONE);
>>> +        }
>>> +        break;
>>> +    default:
>>> +        break;
>>> +    }
>>>  
>>>      return result;
>>>  }
>>> diff --git a/hw/intc/gicv3_internal.h b/hw/intc/gicv3_internal.h
>>> index 0932a30560..ce45cd0ef6 100644
>>> --- a/hw/intc/gicv3_internal.h
>>> +++ b/hw/intc/gicv3_internal.h
>>> @@ -324,6 +324,13 @@ FIELD(MAPC, RDBASE, 16, 32)
>>>  #define ITTADDR_MASK              ((1ULL << ITTADDR_LENGTH) - 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                1ULL
>>>  
>>> @@ -344,6 +351,11 @@ FIELD(MAPC, RDBASE, 16, 32)
>>>   * vPEID = 16 bits
>>>   */
>>>  #define ITS_ITT_ENTRY_SIZE            0xC
>>> +#define ITE_ENTRY_INTTYPE_SHIFT        1
>>> +#define ITE_ENTRY_INTID_SHIFT          2
>>> +#define ITE_ENTRY_INTID_MASK         ((1ULL << 24) - 1)
>>> +#define ITE_ENTRY_INTSP_SHIFT          26
>>> +#define ITE_ENTRY_ICID_MASK          ((1ULL << 16) - 1)
>>>  
>>>  /* 16 bits EventId */
>>>  #define ITS_IDBITS                   GICD_TYPER_IDBITS
>>> diff --git a/include/hw/intc/arm_gicv3_common.h
>>> b/include/hw/intc/arm_gicv3_common.h
>>> index 1fd5cedbbd..0715b0bc2a 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 */
>>>
>> Thanks
>>
>> Eric
>>
> 
Thanks

Eric



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

* Re: [PATCH v4 3/8] hw/intc: GICv3 ITS command queue framework
  2021-06-13 14:39   ` Eric Auger
@ 2021-06-28 15:55     ` shashi.mallela
  0 siblings, 0 replies; 52+ messages in thread
From: shashi.mallela @ 2021-06-28 15:55 UTC (permalink / raw)
  To: Eric Auger, peter.maydell, leif, rad; +Cc: qemu-arm, qemu-devel

Hi Eric,

Had missed this comment earlier.Please find my response (inline)below:-

On Sun, 2021-06-13 at 16:39 +0200, Eric Auger wrote:
> Hi,
> 
> On 6/2/21 8:00 PM, Shashi Mallela 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  | 295
> > +++++++++++++++++++++++++++++++++++++++
> >  hw/intc/gicv3_internal.h |  37 +++++
> >  2 files changed, 332 insertions(+)
> > 
> > diff --git a/hw/intc/arm_gicv3_its.c b/hw/intc/arm_gicv3_its.c
> > index af60f19c98..6551c577b3 100644
> > --- a/hw/intc/arm_gicv3_its.c
> > +++ b/hw/intc/arm_gicv3_its.c
> > @@ -49,6 +49,295 @@ static uint64_t baser_base_addr(uint64_t value,
> > uint32_t page_sz)
> >      return result;
> >  }
> >  
> > +static MemTxResult update_cte(GICv3ITSState *s, uint16_t icid,
> > bool valid,
> > +                              uint64_t rdbase)
> > +{
> > +    AddressSpace *as = &s->gicv3->dma_as;
> > +    uint64_t value;
> > +    uint64_t l2t_addr;
> > +    bool valid_l2t;
> > +    uint32_t l2t_id;
> > +    uint32_t max_l2_entries;
> > +    uint64_t cte = 0;
> > +    MemTxResult res = MEMTX_OK;
> > +
> > +    if (!s->ct.valid) {
> > +        return res;
> > +    }
> > +
> > +    if (valid) {
> > +        /* add mapping entry to collection table */
> > +        cte = (valid & VALID_MASK) |
> > +              ((rdbase & RDBASE_PROCNUM_MASK) << 1ULL);
> > +    }
> > +
> > +    /*
> > +     * 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.
> > +     */
> > +    if (s->ct.indirect) {
> > +        l2t_id = icid / (s->ct.page_sz / L1TABLE_ENTRY_SIZE);
> > +
> > +        value = address_space_ldq_le(as,
> > +                                     s->ct.base_addr +
> > +                                     (l2t_id *
> > L1TABLE_ENTRY_SIZE),
> > +                                     MEMTXATTRS_UNSPECIFIED,
> > &res);
> > +
> > +        if (res != MEMTX_OK) {
> > +            return res;
> > +        }
> > +
> > +        valid_l2t = (value >> VALID_SHIFT) & VALID_MASK;
> > +
> > +        if (valid_l2t) {
> > +            max_l2_entries = s->ct.page_sz / s->ct.entry_sz;
> > +
> > +            l2t_addr = value & ((1ULL << 51) - 1);
> > +
> > +            address_space_stq_le(as, l2t_addr +
> > +                                 ((icid % max_l2_entries) *
> > GITS_CTE_SIZE),
> > +                                 cte, MEMTXATTRS_UNSPECIFIED,
> > &res);
> > +        }
> > +    } else {
> > +        /* Flat level table */
> > +        address_space_stq_le(as, s->ct.base_addr + (icid *
> > GITS_CTE_SIZE),
> > +                             cte, MEMTXATTRS_UNSPECIFIED, &res);
> > +    }
> > +    return res;
> > +}
> 
> Looking at your DevTableDesc and CollTableDesc types again, they are
> basically the same except max_devids/max_collids. You may use a
> single
> one and it may be possible to define helpers to access an entry in
> the
> DT or CT.
will replace DevTableDesc/CollTableDesc types with a common TableDesc
type and introduce a new union member to hold one of
max_devids/max_collids to be referenced by all relevant functions.
> 
> update_cte/update_dte looks quite similar if you compute the cte and
> dte
> externally and pass a pointer to the associated TableDesc?
update_cte/update_cte will (continue) to reference their respective
tables via existing Gicv3ItsState type but through the newly defined
common TableDesc type.
Hope this helps. 
> 
> Thanks
> 
> Eric
> 
> 
> > +
> > +static MemTxResult process_mapc(GICv3ITSState *s, uint32_t offset)
> > +{
> > +    AddressSpace *as = &s->gicv3->dma_as;
> > +    uint16_t icid;
> > +    uint64_t rdbase;
> > +    bool valid;
> > +    MemTxResult res = MEMTX_OK;
> > +    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);
> > +
> > +    if (res != MEMTX_OK) {
> > +        return res;
> > +    }
> > +
> > +    icid = value & ICID_MASK;
> > +
> > +    rdbase = (value >> R_MAPC_RDBASE_SHIFT) & RDBASE_PROCNUM_MASK;
> > +
> > +    valid = (value >> VALID_SHIFT) & VALID_MASK;
> > +
> > +    if ((icid > s->ct.max_collids) || (rdbase > s->gicv3-
> > >num_cpu)) {
> > +        qemu_log_mask(LOG_GUEST_ERROR,
> > +                      "ITS MAPC: invalid collection table
> > attributes "
> > +                      "icid %d rdbase %lu\n",  icid, rdbase);
> > +        /*
> > +         * in this implementation,in case of error
> > +         * we ignore this command and move onto the next
> > +         * command in the queue
> > +         */
> > +    } else {
> > +        res = update_cte(s, icid, valid, rdbase);
> > +    }
> > +
> > +    return res;
> > +}
> > +
> > +static MemTxResult update_dte(GICv3ITSState *s, uint32_t devid,
> > bool valid,
> > +                              uint8_t size, uint64_t itt_addr)
> > +{
> > +    AddressSpace *as = &s->gicv3->dma_as;
> > +    uint64_t value;
> > +    uint64_t l2t_addr;
> > +    bool valid_l2t;
> > +    uint32_t l2t_id;
> > +    uint32_t max_l2_entries;
> > +    uint64_t dte = 0;
> > +    MemTxResult res = MEMTX_OK;
> > +
> > +    if (s->dt.valid) {
> > +        if (valid) {
> > +            /* add mapping entry to device table */
> > +            dte = (valid & VALID_MASK) |
> > +                  ((size & SIZE_MASK) << 1U) |
> > +                  ((itt_addr & ITTADDR_MASK) << 6ULL);
> > +        }
> > +    } else {
> > +        return res;
> > +    }
> > +
> > +    /*
> > +     * 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.
> > +     */
> > +    if (s->dt.indirect) {
> > +        l2t_id = devid / (s->dt.page_sz / L1TABLE_ENTRY_SIZE);
> > +
> > +        value = address_space_ldq_le(as,
> > +                                     s->dt.base_addr +
> > +                                     (l2t_id *
> > L1TABLE_ENTRY_SIZE),
> > +                                     MEMTXATTRS_UNSPECIFIED,
> > &res);
> > +
> > +        if (res != MEMTX_OK) {
> > +            return res;
> > +        }
> > +
> > +        valid_l2t = (value >> VALID_SHIFT) & VALID_MASK;
> > +
> > +        if (valid_l2t) {
> > +            max_l2_entries = s->dt.page_sz / s->dt.entry_sz;
> > +
> > +            l2t_addr = value & ((1ULL << 51) - 1);
> > +
> > +            address_space_stq_le(as, l2t_addr +
> > +                                 ((devid % max_l2_entries) *
> > GITS_DTE_SIZE),
> > +                                 dte, MEMTXATTRS_UNSPECIFIED,
> > &res);
> > +        }
> > +    } else {
> > +        /* Flat level table */
> > +        address_space_stq_le(as, s->dt.base_addr + (devid *
> > GITS_DTE_SIZE),
> > +                             dte, MEMTXATTRS_UNSPECIFIED, &res);
> > +    }
> > +    return res;
> > +}
> > +
> > +static MemTxResult process_mapd(GICv3ITSState *s, uint64_t value,
> > +                                uint32_t offset)
> > +{
> > +    AddressSpace *as = &s->gicv3->dma_as;
> > +    uint32_t devid;
> > +    uint8_t size;
> > +    uint64_t itt_addr;
> > +    bool valid;
> > +    MemTxResult res = MEMTX_OK;
> > +
> > +    devid = (value >> DEVID_SHIFT) & DEVID_MASK;
> > +
> > +    offset += NUM_BYTES_IN_DW;
> > +    value = address_space_ldq_le(as, s->cq.base_addr + offset,
> > +                                 MEMTXATTRS_UNSPECIFIED, &res);
> > +
> > +    if (res != MEMTX_OK) {
> > +        return res;
> > +    }
> > +
> > +    size = (value & SIZE_MASK);
> > +
> > +    offset += NUM_BYTES_IN_DW;
> > +    value = address_space_ldq_le(as, s->cq.base_addr + offset,
> > +                                 MEMTXATTRS_UNSPECIFIED, &res);
> > +
> > +    if (res != MEMTX_OK) {
> > +        return res;
> > +    }
> > +
> > +    itt_addr = (value >> ITTADDR_SHIFT) & ITTADDR_MASK;
> > +
> > +    valid = (value >> VALID_SHIFT) & VALID_MASK;
> > +
> > +    if ((devid > s->dt.max_devids) ||
> > +        (size > FIELD_EX64(s->typer, GITS_TYPER, IDBITS))) {
> > +        qemu_log_mask(LOG_GUEST_ERROR,
> > +                      "ITS MAPD: invalid device table attributes "
> > +                      "devid %d or size %d\n", devid, size);
> > +        /*
> > +         * in this implementation, in case of error
> > +         * we ignore this command and move onto the next
> > +         * command in the queue
> > +         */
> > +    } else {
> > +        res = update_dte(s, devid, valid, size, itt_addr);
> > +    }
> > +
> > +    return res;
> > +}
> > +
> > +/*
> > + * Current implementation blocks until all
> > + * commands are processed
> > + */
> > +static void 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->dma_as;
> > +    MemTxResult res = MEMTX_OK;
> > +    uint8_t cmd;
> > +
> > +    if (!(s->ctlr & ITS_CTLR_ENABLED)) {
> > +        return;
> > +    }
> > +
> > +    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);
> > +        return;
> > +    }
> > +
> > +    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:
> > +            /*
> > +             * 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.
> > +             * Hence no further processing is required for SYNC
> > command.
> > +             */
> > +            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 {
> > +            /*
> > +             * in this implementation,in case of dma read/write
> > error
> > +             * we stall the command processing
> > +             */
> > +            s->creadr = FIELD_DP64(s->creadr, GITS_CREADR,
> > STALLED, 1);
> > +            qemu_log_mask(LOG_GUEST_ERROR,
> > +                          "%s: %x cmd processing failed!!\n",
> > __func__, cmd);
> > +            break;
> > +        }
> > +    }
> > +}
> > +
> >  static void extract_table_params(GICv3ITSState *s)
> >  {
> >      uint16_t num_pages = 0;
> > @@ -226,6 +515,9 @@ static MemTxResult its_writel(GICv3ITSState *s,
> > hwaddr offset,
> >      case GITS_CWRITER:
> >          s->cwriter = deposit64(s->cwriter, 0, 32,
> >                                 (value &
> > ~R_GITS_CWRITER_RETRY_MASK));
> > +        if (s->cwriter != s->creadr) {
> > +            process_cmdq(s);
> > +        }
> >          break;
> >      case GITS_CWRITER + 4:
> >          s->cwriter = deposit64(s->cwriter, 32, 32,
> > @@ -346,6 +638,9 @@ static MemTxResult its_writell(GICv3ITSState
> > *s, hwaddr offset,
> >          break;
> >      case GITS_CWRITER:
> >          s->cwriter = value & ~R_GITS_CWRITER_RETRY_MASK;
> > +        if (s->cwriter != s->creadr) {
> > +            process_cmdq(s);
> > +        }
> >          break;
> >      case GITS_CREADR:
> >      case GITS_TYPER:
> > diff --git a/hw/intc/gicv3_internal.h b/hw/intc/gicv3_internal.h
> > index d6aaa94e4c..0932a30560 100644
> > --- a/hw/intc/gicv3_internal.h
> > +++ b/hw/intc/gicv3_internal.h
> > @@ -253,6 +253,9 @@ 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)
> >  
> > @@ -289,6 +292,40 @@ 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_LENGTH                  16
> > +#define ICID_MASK                 ((1U << ICID_LENGTH) - 1)
> > +FIELD(MAPC, RDBASE, 16, 32)
> > +
> > +#define RDBASE_PROCNUM_LENGTH        16
> > +#define RDBASE_PROCNUM_MASK       ((1ULL << RDBASE_PROCNUM_LENGTH)
> > - 1)
> > +
> > +#define DEVID_SHIFT                  32
> > +#define DEVID_LENGTH                 32
> > +#define DEVID_MASK                ((1ULL << DEVID_LENGTH) - 1)
> > +
> > +/* MAPD command fields */
> > +#define ITTADDR_LENGTH               44
> > +#define ITTADDR_SHIFT                 8
> > +#define ITTADDR_MASK              ((1ULL << ITTADDR_LENGTH) - 1)
> > +#define SIZE_MASK                 0x1f
> > +
> > +#define VALID_SHIFT               63
> > +#define VALID_MASK                1ULL
> >  
> >  /**
> >   * Default features advertised by this version of ITS
> > 



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

* Re: [PATCH v4 2/8] hw/intc: GICv3 ITS register definitions added
  2021-06-21  9:51       ` Eric Auger
@ 2021-06-28 21:51         ` shashi.mallela
  0 siblings, 0 replies; 52+ messages in thread
From: shashi.mallela @ 2021-06-28 21:51 UTC (permalink / raw)
  To: Eric Auger, peter.maydell, leif, rad; +Cc: qemu-arm, qemu-devel

Hi Eric,

Have taken care of your latest comment,please find response inline :-

On Mon, 2021-06-21 at 11:51 +0200, Eric Auger wrote:
> Hi Shashi,
> 
> On 6/16/21 11:02 PM, shashi.mallela@linaro.org wrote:
> > Hi Eric,
> > 
> > Please find my responses inline (below):-
> > 
> > On Sat, 2021-06-12 at 08:08 +0200, Eric Auger wrote:
> > > On 6/2/21 8:00 PM, Shashi Mallela 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                | 335
> > > > +++++++++++++++++++++++++
> > > >  hw/intc/gicv3_internal.h               |  28 ++-
> > > >  include/hw/intc/arm_gicv3_common.h     |   3 +
> > > >  include/hw/intc/arm_gicv3_its_common.h |  30 +++
> > > >  4 files changed, 395 insertions(+), 1 deletion(-)
> > > > 
> > > > diff --git a/hw/intc/arm_gicv3_its.c b/hw/intc/arm_gicv3_its.c
> > > > index 545cda3665..af60f19c98 100644
> > > > --- a/hw/intc/arm_gicv3_its.c
> > > > +++ b/hw/intc/arm_gicv3_its.c
> > > > @@ -28,6 +28,157 @@ struct GICv3ITSClass {
> > > >      void (*parent_reset)(DeviceState *dev);
> > > >  };
> > > >  
> > > > +static uint64_t baser_base_addr(uint64_t value, uint32_t
> > > > page_sz)
> > > > +{
> > > > +    uint64_t result = 0;
> > > > +
> > > > +    switch (page_sz) {
> > > > +    case GITS_ITT_PAGE_SIZE_0:
> > > > +    case GITS_ITT_PAGE_SIZE_1:
> > > > +        result = value & R_GITS_BASER_PHYADDR_MASK;
> > > Use FIELD_EX64 as well for homogeneity?
> > Done
> > > > +        break;
> > > > +
> > > > +    case GITS_ITT_PAGE_SIZE_2:
> > > > +        result = value & R_GITS_BASER_PHYADDRL_64K_MASK;
> > > here as well?
> > Done
> > > > +        result |= FIELD_EX64(value, GITS_BASER, PHYADDRH_64K)
> > > > <<
> > > > 48;
> > > > +        break;
> > > > +
> > > > +    default:
> > > > +        break;
> > > > +    }
> > > > +    return result;
> > > > +}
> > > > +
> > > > +static void extract_table_params(GICv3ITSState *s)
> > > > +{
> > > > +    uint16_t num_pages = 0;
> > > > +    uint8_t  page_sz_type;
> > > > +    uint8_t type;
> > > > +    uint32_t page_sz = 0;
> > > > +    uint64_t value;
> > > > +
> > > > +    for (int i = 0; i < 8; i++) {
> > > > +        value = s->baser[i];
> > > > +
> > > > +        if (!value) {
> > > > +            continue;
> > > > +        }
> > > > +
> > > > +        page_sz_type = FIELD_EX64(value, GITS_BASER,
> > > > PAGESIZE);
> > > > +
> > > > +        switch (page_sz_type) {
> > > > +        case 0:
> > > > +            page_sz = GITS_ITT_PAGE_SIZE_0;
> > > > +            break;
> > > > +
> > > > +        case 1:
> > > > +            page_sz = GITS_ITT_PAGE_SIZE_1;
> > > > +            break;
> > > > +
> > > > +        case 2:
> > > > +        case 3:
> > > > +            page_sz = GITS_ITT_PAGE_SIZE_2;
> > > > +            break;
> > > > +
> > > > +        default:
> > > > +            g_assert_not_reached();
> > > > +        }
> > > > +
> > > > +        num_pages = FIELD_EX64(value, GITS_BASER, SIZE);
> > >  + 1 directly? and remove num_pages + 1 below.
> > Done
> > > > +
> > > > +        type = FIELD_EX64(value, GITS_BASER, TYPE);
> > > > +
> > > > +        switch (type) {
> > > > +
> > > > +        case GITS_ITT_TYPE_DEVICE:
> > > > +            memset(&s->dt, 0 , sizeof(s->dt));
> > > > +            s->dt.valid = FIELD_EX64(value, GITS_BASER,
> > > > VALID);
> > > > +
> > > > +            if (!s->dt.valid) {
> > > > +                return;
> > > > +            }
> > > > +
> > > > +            s->dt.page_sz = page_sz;
> > > > +            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));
> > > > +
> > > > +            s->dt.base_addr = baser_base_addr(value, page_sz);
> > > > +
> > > > +            break;
> > > > +
> > > > +        case GITS_ITT_TYPE_COLLECTION:
> > > > +            memset(&s->ct, 0 , sizeof(s->ct));
> > > > +            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) {
> > > > +                return;
> > > as this is an helper routine, I think it would be better to have
> > > this
> > > check in the caller. Also you reset ct above.
> > The idea here was to keep all the GITS_BASER fields parsing and
> > extraction in one place in this function without the caller (like
> > its_writel) having to know the GITS_BASER fields format and thereby
> > split the logic between the caller and this function 
> Maybe add a doc comment explaining what the function does and in
> which
> context it is supposed to be called then.
Done
> > > > +            }
> > > > +
> > > > +            s->ct.page_sz = page_sz;
> > > > +            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);
> > > > +            }
> > > > +
> > > > +            s->ct.base_addr = baser_base_addr(value, page_sz);
> > > > +
> > > > +            break;
> > > > +
> > > > +        default:
> > > > +            break;
> > > > +        }
> > > > +    }
> > > > +}
> > > > +
> > > > +static void extract_cmdq_params(GICv3ITSState *s)
> > > > +{
> > > > +    uint16_t num_pages = 0;
> > > > +    uint64_t value = s->cbaser;
> > > > +
> > > > +    num_pages = FIELD_EX64(value, GITS_CBASER, SIZE);
> > > + 1
> > > > +
> > > > +    memset(&s->cq, 0 , sizeof(s->cq));
> > > > +    s->cq.valid = FIELD_EX64(value, GITS_CBASER, VALID);
> > > > +
> > > > +    if (s->cq.valid) {
> > > > +        s->cq.max_entries = ((num_pages + 1) *
> > > > GITS_ITT_PAGE_SIZE_0) /
> > > nit: use of GITS_ITT_PAGE_SIZE_0 is misleading as ITT stands for
> > > interrupt translation table and does not relate to CMDQ. Use 4K
> > > define
> > > instead.
> > changed the names to GITS_PAGE_SIZE_4K/16K/64K
> > > > +                             GITS_CMDQ_ENTRY_SIZE;
> > > > +        s->cq.base_addr = FIELD_EX64(value, GITS_CBASER,
> > > > PHYADDR);
> > > > +        s->cq.base_addr <<= R_GITS_CBASER_PHYADDR_SHIFT;
> > > > +    }
> > > > +}
> > > > +
> > > >  static MemTxResult gicv3_its_translation_write(void *opaque,
> > > > hwaddr offset,
> > > >                                                 uint64_t data,
> > > > unsigned size,
> > > >                                                 MemTxAttrs
> > > > attrs)
> > > > @@ -41,7 +192,73 @@ static MemTxResult its_writel(GICv3ITSState
> > > > *s,
> > > > hwaddr offset,
> > > >                                uint64_t value, MemTxAttrs
> > > > attrs)
> > > >  {
> > > >      MemTxResult result = MEMTX_OK;
> > > > +    int index;
> > > >  
> > > > +    switch (offset) {
> > > > +    case GITS_CTLR:
> > > > +        s->ctlr |= (value & ~(s->ctlr));
> > > > +
> > > > +        if (s->ctlr & ITS_CTLR_ENABLED) {
> > > > +            extract_table_params(s);
> > > > +            extract_cmdq_params(s);
> > > > +            s->creadr = 0;
> > > The KVM code also checks the he CBASER and
> > > device/collection BASER are valid
> > > we do check CBASER and device/collection BASER are valid in this
> > > implementation too (via extract_cmdq_params &
> > > extract_table_params)
> > > To be further checked in subsequent patches:
> > > - cache invalidation when turning off
> > > - process commands if turned on?
> > > - any cmd lock
> > > 
> > > > +        }
> > > > +        break;
> > > > +    case GITS_CBASER:
> > > > +        /*
> > > > +         * IMPDEF choice:- 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:
> > > > +        /*
> > > > +         * IMPDEF choice:- GITS_CBASER register becomes RO if
> > > > ITS
> > > > is
> > > > +         *                 already enabled
> > > > +         */
> > > > +        if (!(s->ctlr & ITS_CTLR_ENABLED)) {
> > > > +            s->cbaser = deposit64(s->cbaser, 32, 32, value);
> > > you need to reset creadr here also
> > Done
> > > also CWRITER should be reset to CREADR.
> > Done
> > > KVM code comment:
> > > /*
> > >  * CWRITER is architecturally UNKNOWN on reset, but we need to
> > > reset
> > >  * it to CREADR to make sure we start with an empty command
> > > buffer.
> > >  */
> > > 
> > > > +        }> +        break;
> > > > +    case GITS_CWRITER:
> > > > +        s->cwriter = deposit64(s->cwriter, 0, 32,
> > > > +                               (value &
> > > > ~R_GITS_CWRITER_RETRY_MASK));
> > > how do you implement the overflow case?
> > > "If GITS_CWRITER is written with a value outside of the valid
> > > range
> > > specified by
> > > GITS_CBASER.Physical_Address and GITS_CBASER.Size, behavior is a
> > > CONSTRAINED UNPREDICTABLE choice"
> > > for info the KVM code does not write the actual reg
> > we write the reg and log a guest error
> > > further check: process command?
> > > 
> > > > +        break;
> > > > +    case GITS_CWRITER + 4:
> > > > +        s->cwriter = deposit64(s->cwriter, 32, 32,
> > > > +                               (value &
> > > > ~R_GITS_CWRITER_RETRY_MASK));
> > > > +        break;
> > > > +    case GITS_BASER ... GITS_BASER + 0x3f:
> > > > +        /*
> > > > +         * IMPDEF choice:- GITS_BASERn register becomes RO if
> > > > ITS
> > > > is
> > > > +         *                 already enabled
> > > > +         */
> > > > +        if (!(s->ctlr & ITS_CTLR_ENABLED)) {
> > > > +            index = (offset - GITS_BASER) / 8;
> > > > +
> > > > +            if (offset & 7) {
> > > > +                s->baser[index] = deposit64(s->baser[index],
> > > > 32,
> > > > 32,
> > > > +                                            (value &
> > > > ~GITS_BASER_VAL_MASK));
> > > > +            } else {
> > > > +                s->baser[index] = deposit64(s->baser[index],
> > > > 0,
> > > > 32,
> > > > +                                            (value &
> > > > ~GITS_BASER_VAL_MASK));
> > > > +            }
> > > > +        }
> > > > +        break;
> > > > +    case GITS_IIDR:
> > > > +    case GITS_IDREGS ... GITS_IDREGS + 0x2f:
> > > > +        /* 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;
> > > >  }
> > > >  
> > > > @@ -49,7 +266,55 @@ 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 = gicv3_iidr();
> > > > +        break;
> > > > +    case GITS_IDREGS ... GITS_IDREGS + 0x2f:
> > > > +        /* ID registers */
> > > > +        *data = gicv3_idreg(offset - GITS_IDREGS);
> > > I am not sure those are the same as the gicv3
> > Yes they are, and consistent with the distributor,redistributor
> > gicv3
> > idregs implementation too in qemu (as indicated in previous patch
> > review comments)
> Ah OK
> 
> Thanks
> 
> Eric
> > > on KVM we have
> > >         case GITS_PIDR0:
> > >                 return 0x92;    /* part number, bits[7:0] */
> > >         case GITS_PIDR1:
> > >                 return 0xb4;    /* part number, bits[11:8] */
> > >         case GITS_PIDR2:
> > >                 return GIC_PIDR2_ARCH_GICv3 | 0x0b;
> > >         case GITS_PIDR4:
> > >                 return 0x40;    /* This is a 64K software visible
> > > page */
> > >         /* The following are the ID registers for (any) GIC. */
> > >         case GITS_CIDR0:
> > >                 return 0x0d;
> > >         case GITS_CIDR1:
> > >                 return 0xf0;
> > >         case GITS_CIDR2:
> > >                 return 0x05;
> > >         case GITS_CIDR3:
> > >                 return 0xb1;
> > > 
> > > 
> > > > +        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 = extract64(s->baser[index], 32, 32);
> > > > +        } else {
> > > > +            *data = extract64(s->baser[index], 0, 32);
> > > > +        }
> > > > +        break;
> > > > +    default:
> > > > +        result = MEMTX_ERROR;
> > > > +        break;
> > > > +    }
> > > >      return result;
> > > >  }
> > > >  
> > > > @@ -57,7 +322,42 @@ 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:
> > > > +        /*
> > > > +         * IMPDEF choice:- GITS_BASERn register becomes 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);
> > > > +        }
> > > > +        break;
> > > > +    case GITS_CBASER:
> > > > +        /*
> > > > +         * IMPDEF choice:- GITS_CBASER register becomes RO if
> > > > ITS
> > > > is
> > > > +         *                 already enabled
> > > > +         */
> > > > +        if (!(s->ctlr & ITS_CTLR_ENABLED)) {
> > > > +            s->cbaser = value;
> > > s->creadr = 0;
> > > cwriter = creader?
> > Done
> > > > +        }
> > > > +        break;
> > > > +    case GITS_CWRITER:
> > > > +        s->cwriter = value & ~R_GITS_CWRITER_RETRY_MASK;
> > > > +        break;
> > > > +    case GITS_CREADR:
> > > RO if GICD_CTLR.DS = 0
> > > On KVM side the write access is implemented
> > Done
> > > > +    case GITS_TYPER:
> > > > +        /* 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;
> > > >  }
> > > >  
> > > > @@ -65,7 +365,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;
> > > >  }
> > > >  
> > > > @@ -162,6 +484,9 @@ static void
> > > > gicv3_arm_its_realize(DeviceState
> > > > *dev, Error **errp)
> > > >      gicv3_its_init_mmio(s, &gicv3_its_control_ops,
> > > > &gicv3_its_translation_ops);
> > > >  
> > > >      if (s->gicv3->cpu->gicr_typer & GICR_TYPER_PLPIS) {
> > > > +        address_space_init(&s->gicv3->dma_as, s->gicv3->dma,
> > > > +                           "gicv3-its-sysmem");
> > > > +
> > > >          /* set the ITS default features supported */
> > > >          s->typer = FIELD_DP64(s->typer, GITS_TYPER, PHYSICAL,
> > > >                                GITS_TYPE_PHYSICAL);
> > > > @@ -208,6 +533,14 @@ static void gicv3_its_reset(DeviceState
> > > > *dev)
> > > >      }
> > > >  }
> > > >  
> > > > +static void gicv3_its_post_load(GICv3ITSState *s)
> > > > +{
> > > > +    if (s->ctlr & ITS_CTLR_ENABLED) {
> > > > +        extract_table_params(s);
> > > > +        extract_cmdq_params(s);
> > > > +    }
> > > > +}
> > > > +
> > > >  static Property gicv3_its_props[] = {
> > > >      DEFINE_PROP_LINK("parent-gicv3", GICv3ITSState, gicv3,
> > > > "arm-
> > > > gicv3",
> > > >                       GICv3State *),
> > > > @@ -218,10 +551,12 @@ static void
> > > > gicv3_its_class_init(ObjectClass
> > > > *klass, void *data)
> > > >  {
> > > >      DeviceClass *dc = DEVICE_CLASS(klass);
> > > >      GICv3ITSClass *ic = ARM_GICV3_ITS_CLASS(klass);
> > > > +    GICv3ITSCommonClass *icc =
> > > > ARM_GICV3_ITS_COMMON_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);
> > > > +    icc->post_load = gicv3_its_post_load;
> > > >  }
> > > >  
> > > >  static const TypeInfo gicv3_its_info = {
> > > > diff --git a/hw/intc/gicv3_internal.h
> > > > b/hw/intc/gicv3_internal.h
> > > > index e0b06930a7..d6aaa94e4c 100644
> > > > --- a/hw/intc/gicv3_internal.h
> > > > +++ b/hw/intc/gicv3_internal.h
> > > > @@ -238,7 +238,7 @@ 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, PHYADDRH_64K, 12, 4)
> > > >  FIELD(GITS_BASER, ENTRYSIZE, 48, 5)
> > > >  FIELD(GITS_BASER, OUTERCACHE, 53, 3)
> > > >  FIELD(GITS_BASER, TYPE, 56, 3)
> > > > @@ -246,6 +246,17 @@ FIELD(GITS_BASER, INNERCACHE, 59, 3)
> > > >  FIELD(GITS_BASER, INDIRECT, 62, 1)
> > > >  FIELD(GITS_BASER, VALID, 63, 1)
> > > >  
> > > > +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_CWRITER, RETRY, 0, 1)
> > > > +FIELD(GITS_CWRITER, OFFSET, 5, 15)
> > > > +
> > > > +FIELD(GITS_CTLR, ENABLED, 0, 1)
> > > >  FIELD(GITS_CTLR, QUIESCENT, 31, 1)
> > > >  
> > > >  FIELD(GITS_TYPER, PHYSICAL, 0, 1)
> > > > @@ -257,6 +268,13 @@ FIELD(GITS_TYPER, PTA, 19, 1)
> > > >  FIELD(GITS_TYPER, CIDBITS, 32, 4)
> > > >  FIELD(GITS_TYPER, CIL, 36, 1)
> > > >  
> > > > +#define GITS_IDREGS           0xFFD0
> > > > +
> > > > +#define ITS_CTLR_ENABLED               (1U)  /* ITS Enabled */
> > > > +
> > > > +#define
> > > > GITS_BASER_VAL_MASK                  (R_GITS_BASER_ENTRYSIZE_MA
> > > > SK |
> > > > \
> > > > +                                              R_GITS_BASER_TYP
> > > > E_MA
> > > > SK)
> > > > +
> > > >  #define GITS_BASER_PAGESIZE_4K                0
> > > >  #define GITS_BASER_PAGESIZE_16K               1
> > > >  #define GITS_BASER_PAGESIZE_64K               2
> > > > @@ -264,6 +282,14 @@ FIELD(GITS_TYPER, CIL, 36, 1)
> > > >  #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
> > > Why not naming _4K 16K 64K instead of _0, 1, 2?
> > Done,as indicated above
> > > > +
> > > > +#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..1fd5cedbbd 100644
> > > > --- a/include/hw/intc/arm_gicv3_common.h
> > > > +++ b/include/hw/intc/arm_gicv3_common.h
> > > > @@ -226,6 +226,9 @@ struct GICv3State {
> > > >      int dev_fd; /* kvm device fd if backed by kvm vgic support
> > > > */
> > > >      Error *migration_blocker;
> > > >  
> > > > +    MemoryRegion *dma;
> > > > +    AddressSpace dma_as;
> > > > +
> > > >      /* Distributor */
> > > >  
> > > >      /* for a GIC with the security extensions the NS banked
> > > > version of this
> > > > diff --git a/include/hw/intc/arm_gicv3_its_common.h
> > > > b/include/hw/intc/arm_gicv3_its_common.h
> > > > index 65d1191db1..78b1ba7e6b 100644
> > > > --- a/include/hw/intc/arm_gicv3_its_common.h
> > > > +++ b/include/hw/intc/arm_gicv3_its_common.h
> > > > @@ -41,6 +41,32 @@
> > > >  
> > > >  #define GITS_TRANSLATER  0x0040
> > > >  
> > > > +typedef struct {
> > > > +    bool valid;
> > > > +    bool indirect;
> > > > +    uint16_t entry_sz;
> > > > +    uint32_t page_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 page_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;
> > > >  
> > > > @@ -63,6 +89,10 @@ struct GICv3ITSState {
> > > >      uint64_t creadr;
> > > >      uint64_t baser[8];
> > > >  
> > > > +    DevTableDesc  dt;
> > > > +    CollTableDesc ct;
> > > > +    CmdQDesc      cq;
> > > > +
> > > >      Error *migration_blocker;
> > > >  };
> > > Thanks
> > > 
> > > Eric
> > > >  
> > > > 
> > 
> > 
> > 



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

* Re: [PATCH v4 3/8] hw/intc: GICv3 ITS command queue framework
  2021-06-21 10:03       ` Eric Auger
@ 2021-06-28 21:58         ` shashi.mallela
  0 siblings, 0 replies; 52+ messages in thread
From: shashi.mallela @ 2021-06-28 21:58 UTC (permalink / raw)
  To: Eric Auger, peter.maydell, leif, rad; +Cc: qemu-arm, qemu-devel

Hi Eric,

Please find my responses to your latest comments (taken care of)
inline:-

On Mon, 2021-06-21 at 12:03 +0200, Eric Auger wrote:
> 
> On 6/16/21 11:02 PM, shashi.mallela@linaro.org wrote:
> > Hi Eric,
> > 
> > Please find my responses inline (below):-
> > 
> > On Sun, 2021-06-13 at 16:13 +0200, Eric Auger wrote:
> > > Hi Sashi,
> > > 
> > > On 6/2/21 8:00 PM, Shashi Mallela 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  | 295
> > > > +++++++++++++++++++++++++++++++++++++++
> > > >  hw/intc/gicv3_internal.h |  37 +++++
> > > >  2 files changed, 332 insertions(+)
> > > > 
> > > > diff --git a/hw/intc/arm_gicv3_its.c b/hw/intc/arm_gicv3_its.c
> > > > index af60f19c98..6551c577b3 100644
> > > > --- a/hw/intc/arm_gicv3_its.c
> > > > +++ b/hw/intc/arm_gicv3_its.c
> > > > @@ -49,6 +49,295 @@ static uint64_t baser_base_addr(uint64_t
> > > > value,
> > > > uint32_t page_sz)
> > > >      return result;
> > > >  }
> > > >  
> > > > +static MemTxResult update_cte(GICv3ITSState *s, uint16_t icid,
> > > > bool valid,
> > > > +                              uint64_t rdbase)
> > > > +{
> > > > +    AddressSpace *as = &s->gicv3->dma_as;
> > > > +    uint64_t value;
> > > > +    uint64_t l2t_addr;
> > > > +    bool valid_l2t;
> > > > +    uint32_t l2t_id;
> > > > +    uint32_t max_l2_entries;
> > > > +    uint64_t cte = 0;
> > > > +    MemTxResult res = MEMTX_OK;
> > > > +
> > > > +    if (!s->ct.valid) {
> > > Isn't it a guest log error case. Also you return MEMTX_OK in that
> > > case.
> > > Is that what you want?
> > Yes,because the current implementation treats all command specific
> > errors as "ignored" and moves onto next command in the queue.MEMTX
> > return values are significant for dma read/write status and in case
> > of
> > error we stall the command processing 
> OK
> > > > +        return res;
> > > > +    }
> > > > +
> > > > +    if (valid) {
> > > > +        /* add mapping entry to collection table */
> > > > +        cte = (valid & VALID_MASK) |
> > > > +              ((rdbase & RDBASE_PROCNUM_MASK) << 1ULL);
> > > Do you really need to sanitize rdbase again?
> > Not required,have rectified it.
> > > > +    }
> > > > +
> > > > +    /*
> > > > +     * 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.
> > > > +     */
> > > > +    if (s->ct.indirect) {
> > > > +        l2t_id = icid / (s->ct.page_sz / L1TABLE_ENTRY_SIZE);
> > > > +
> > > > +        value = address_space_ldq_le(as,
> > > > +                                     s->ct.base_addr +
> > > > +                                     (l2t_id *
> > > > L1TABLE_ENTRY_SIZE),
> > > > +                                     MEMTXATTRS_UNSPECIFIED,
> > > > &res);
> > > > +
> > > > +        if (res != MEMTX_OK) {
> > > > +            return res;
> > > > +        }
> > > > +
> > > > +        valid_l2t = (value >> VALID_SHIFT) & VALID_MASK;
> > > > +
> > > > +        if (valid_l2t) {
> > > > +            max_l2_entries = s->ct.page_sz / s->ct.entry_sz;
> > > > +
> > > > +            l2t_addr = value & ((1ULL << 51) - 1);
> > > > +
> > > > +            address_space_stq_le(as, l2t_addr +
> > > > +                                 ((icid % max_l2_entries) *
> > > > GITS_CTE_SIZE),
> > > > +                                 cte, MEMTXATTRS_UNSPECIFIED,
> > > > &res);
> > > > +        }
> > > > +    } else {
> > > > +        /* Flat level table */
> > > > +        address_space_stq_le(as, s->ct.base_addr + (icid *
> > > > GITS_CTE_SIZE),
> > > > +                             cte, MEMTXATTRS_UNSPECIFIED,
> > > > &res);
> > > > +    }
> > > > +    return res;
> > > > +}
> > > > +
> > > > +static MemTxResult process_mapc(GICv3ITSState *s, uint32_t
> > > > offset)
> > > > +{
> > > > +    AddressSpace *as = &s->gicv3->dma_as;
> > > > +    uint16_t icid;
> > > > +    uint64_t rdbase;
> > > > +    bool valid;
> > > > +    MemTxResult res = MEMTX_OK;
> > > > +    uint64_t value;
> > > > +
> > > > +    offset += NUM_BYTES_IN_DW;
> > > > +    offset += NUM_BYTES_IN_DW;
> > > May be relevant to add some trace points for debuggability.
> > Probably the trace functionality for ITS can be taken up as a
> > seperate
> > task/feature TODO.
> Yes of course. It may just be useful for you as well to debug ;-)
> > > > +
> > > > +    value = address_space_ldq_le(as, s->cq.base_addr + offset,
> > > > +                                 MEMTXATTRS_UNSPECIFIED,
> > > > &res);
> > > > +
> > > > +    if (res != MEMTX_OK) {
> > > > +        return res;
> > > > +    }
> > > > +
> > > > +    icid = value & ICID_MASK;
> > > > +
> > > > +    rdbase = (value >> R_MAPC_RDBASE_SHIFT) &
> > > > RDBASE_PROCNUM_MASK;
> > > usually the mask is applied before the shift.
> > Here we are extracting only 16 bit rdbase(processor number) value
> > by
> > masking with RDBASE_PROCNUM_MASK only after we have right shifted
> > the
> > rdbase offset from the 64 bit DW value.
> > As an alternative,I could have used rdbase = (value &
> > R_MAPC_RDBASE_MASK) to first extract the 32 bits rdbase value from
> > DW
> > and then later mask again with RDBASE_PROCNUM_MASK to narrow it
> > down to
> > 16 bit rdbase(processor number).
> My comment rather was about the fact that generally the mask applied
> to
> the shifted location and then you shift the masked field. I notived
> Peter also made this comment in 4/8 (FIELD macro). You tend to use
> the
> same pattern in different places in your series.
Accepted and have made changes across all relevant sections in all
patch series 
> > > > +
> > > > +    valid = (value >> VALID_SHIFT) & VALID_MASK;
> > > use FIELD, see below
> > > > +
> > > > +    if ((icid > s->ct.max_collids) || (rdbase > s->gicv3-
> > > > > num_cpu)) {
> > > you also need to check against ITS_CIDBITS limit?
> > CIDBITS limits is being checked through the s->ct.max_collids
> > member
> > above
> Ah OK
> > > > +        qemu_log_mask(LOG_GUEST_ERROR,
> > > > +                      "ITS MAPC: invalid collection table
> > > > attributes "
> > > > +                      "icid %d rdbase %lu\n",  icid, rdbase);
> > > > +        /*
> > > > +         * in this implementation,in case of error
> > > > +         * we ignore this command and move onto the next
> > > > +         * command in the queue
> > > spec says a command error occurs in that case.
> > Yes,we chose to ignore the  error'ed command and move onto the next
> > one
> > in the queue as per command error options in the spec
> > > > +         */
> > > > +    } else {
> > > > +        res = update_cte(s, icid, valid, rdbase);
> > > > +    }
> > > > +
> > > > +    return res;
> > > > +}
> > > > +
> > > > +static MemTxResult update_dte(GICv3ITSState *s, uint32_t
> > > > devid,
> > > > bool valid,
> > > > +                              uint8_t size, uint64_t itt_addr)
> > > > +{
> > > > +    AddressSpace *as = &s->gicv3->dma_as;
> > > > +    uint64_t value;
> > > > +    uint64_t l2t_addr;
> > > > +    bool valid_l2t;
> > > > +    uint32_t l2t_id;
> > > > +    uint32_t max_l2_entries;
> > > > +    uint64_t dte = 0;
> > > > +    MemTxResult res = MEMTX_OK;
> > > > +
> > > > +    if (s->dt.valid) {
> > > > +        if (valid) {
> > > > +            /* add mapping entry to device table */
> > > > +            dte = (valid & VALID_MASK) |
> > > > +                  ((size & SIZE_MASK) << 1U) |
> > > > +                  ((itt_addr & ITTADDR_MASK) << 6ULL);
> > > > +        }
> > > > +    } else {
> > > > +        return res;
> > > > +    }
> > > > +
> > > > +    /*
> > > > +     * 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.
> > > > +     */
> > > > +    if (s->dt.indirect) {
> > > > +        l2t_id = devid / (s->dt.page_sz / L1TABLE_ENTRY_SIZE);
> > > > +
> > > > +        value = address_space_ldq_le(as,
> > > > +                                     s->dt.base_addr +
> > > > +                                     (l2t_id *
> > > > L1TABLE_ENTRY_SIZE),
> > > > +                                     MEMTXATTRS_UNSPECIFIED,
> > > > &res);
> > > > +
> > > > +        if (res != MEMTX_OK) {
> > > > +            return res;
> > > > +        }
> > > > +
> > > > +        valid_l2t = (value >> VALID_SHIFT) & VALID_MASK;
> > > > +
> > > > +        if (valid_l2t) {
> > > > +            max_l2_entries = s->dt.page_sz / s->dt.entry_sz;
> > > > +
> > > > +            l2t_addr = value & ((1ULL << 51) - 1);
> > > > +
> > > > +            address_space_stq_le(as, l2t_addr +
> > > > +                                 ((devid % max_l2_entries) *
> > > > GITS_DTE_SIZE),
> > > > +                                 dte, MEMTXATTRS_UNSPECIFIED,
> > > > &res);
> > > > +        }
> > > > +    } else {
> > > > +        /* Flat level table */
> > > > +        address_space_stq_le(as, s->dt.base_addr + (devid *
> > > > GITS_DTE_SIZE),
> > > > +                             dte, MEMTXATTRS_UNSPECIFIED,
> > > > &res);
> > > > +    }
> > > > +    return res;
> > > > +}
> > > > +
> > > > +static MemTxResult process_mapd(GICv3ITSState *s, uint64_t
> > > > value,
> > > you do not seem to use the input value, remove it?
> > yes we are using the input value,which is the 1st DW from the
> > command
> > to extract the deviceid (devid) field below
> Hum my mistake sorry.
> > > > +                                uint32_t offset)
> > > > +{
> > > > +    AddressSpace *as = &s->gicv3->dma_as;
> > > > +    uint32_t devid;
> > > > +    uint8_t size;
> > > > +    uint64_t itt_addr;
> > > > +    bool valid;
> > > > +    MemTxResult res = MEMTX_OK;
> > > > +
> > > > +    devid = (value >> DEVID_SHIFT) & DEVID_MASK;
> > > > +
> > > > +    offset += NUM_BYTES_IN_DW;
> > > > +    value = address_space_ldq_le(as, s->cq.base_addr + offset,
> > > > +                                 MEMTXATTRS_UNSPECIFIED,
> > > > &res);
> > > > +
> > > > +    if (res != MEMTX_OK) {
> > > > +        return res;
> > > > +    }
> > > > +
> > > > +    size = (value & SIZE_MASK);
> > > > +
> > > > +    offset += NUM_BYTES_IN_DW;
> > > > +    value = address_space_ldq_le(as, s->cq.base_addr + offset,
> > > > +                                 MEMTXATTRS_UNSPECIFIED,
> > > > &res);
> > > > +
> > > > +    if (res != MEMTX_OK) {
> > > > +        return res;
> > > > +    }
> > > > +
> > > > +    itt_addr = (value >> ITTADDR_SHIFT) & ITTADDR_MASK;
> > > this looks weird to me, usually we apply the mask first and then
> > > shift.
> > from the 64 bit DW,we right shift (by 8)to align the itt_addr at
> > 0th
> > position and extract 44 bits(0 to 43) using the mask 
> ditto
Accepted and taken care of as indicated in the previous response
> > > > +
> > > > +    valid = (value >> VALID_SHIFT) & VALID_MASK;
> > > use FIELD_EX64()?
> > > > +
> > > > +    if ((devid > s->dt.max_devids) ||
> > > > +        (size > FIELD_EX64(s->typer, GITS_TYPER, IDBITS))) {
> > > ITS_IDBITS?
> > IDBITS is one of the fields in GITS_TYPER and the field naming is
> > consistent with the spec definition
> > > > +        qemu_log_mask(LOG_GUEST_ERROR,
> > > > +                      "ITS MAPD: invalid device table
> > > > attributes "
> > > > +                      "devid %d or size %d\n", devid, size);
> > > > +        /*
> > > > +         * in this implementation, in case of error
> > > > +         * we ignore this command and move onto the next
> > > > +         * command in the queue
> > > > +         */
> > > > +    } else {
> > > > +        res = update_dte(s, devid, valid, size, itt_addr);
> > > > +    }
> > > > +
> > > > +    return res;
> > > > +}
> > > > +
> > > > +/*
> > > > + * Current implementation blocks until all
> > > > + * commands are processed
> > > > + */
> > > > +static void 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->dma_as;
> > > > +    MemTxResult res = MEMTX_OK;
> > > > +    uint8_t cmd;
> > > > +
> > > > +    if (!(s->ctlr & ITS_CTLR_ENABLED)) {
> > > > +        return;
> > > > +    }
> > > > +
> > > > +    wr_offset = FIELD_EX64(s->cwriter, GITS_CWRITER, OFFSET);
> > > > +
> > > > +    if (wr_offset > s->cq.max_entries) {
> > > Shouldn't this be checked on cwrite write instead?
> > Yes we are checking within the cwriter write scope,just that the
> > check
> > is happening through this function (called during cwrite write)
> > > > +        qemu_log_mask(LOG_GUEST_ERROR,
> > > > +                      "%s: invalid write offset "
> > > > +                      "%d\n", __func__, wr_offset);
> > > > +        return;
> > > > +    }
> > > > +
> > > > +    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:
> > > > +            /*
> > > > +             * 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.
> > > > +             * Hence no further processing is required for
> > > > SYNC
> > > > command.
> > > > +             */
> > > > +            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 {
> > > > +            /*
> > > > +             * in this implementation,in case of dma
> > > > read/write
> > > > error
> > > > +             * we stall the command processing
> > > > +             */
> > > > +            s->creadr = FIELD_DP64(s->creadr, GITS_CREADR,
> > > > STALLED, 1);
> > > > +            qemu_log_mask(LOG_GUEST_ERROR,
> > > > +                          "%s: %x cmd processing failed!!\n",
> > > > __func__, cmd);
> > > > +            break;
> > > > +        }
> > > > +    }
> > > > +}
> > > > +
> > > >  static void extract_table_params(GICv3ITSState *s)
> > > >  {
> > > >      uint16_t num_pages = 0;
> > > > @@ -226,6 +515,9 @@ static MemTxResult its_writel(GICv3ITSState
> > > > *s,
> > > > hwaddr offset,
> > > >      case GITS_CWRITER:
> > > >          s->cwriter = deposit64(s->cwriter, 0, 32,
> > > >                                 (value &
> > > > ~R_GITS_CWRITER_RETRY_MASK));
> > > > +        if (s->cwriter != s->creadr) {
> > > > +            process_cmdq(s);
> > > I would expect process_cmdq() to be called as well on ITS enable
> > Done
> > > > +        }
> > > >          break;
> > > >      case GITS_CWRITER + 4:
> > > >          s->cwriter = deposit64(s->cwriter, 32, 32,
> > > > @@ -346,6 +638,9 @@ static MemTxResult
> > > > its_writell(GICv3ITSState
> > > > *s, hwaddr offset,
> > > >          break;
> > > >      case GITS_CWRITER:
> > > >          s->cwriter = value & ~R_GITS_CWRITER_RETRY_MASK;
> > > > +        if (s->cwriter != s->creadr) {
> > > > +            process_cmdq(s);
> > > > +        }
> > > >          break;
> > > >      case GITS_CREADR:
> > > >      case GITS_TYPER:
> > > > diff --git a/hw/intc/gicv3_internal.h
> > > > b/hw/intc/gicv3_internal.h
> > > > index d6aaa94e4c..0932a30560 100644
> > > > --- a/hw/intc/gicv3_internal.h
> > > > +++ b/hw/intc/gicv3_internal.h
> > > > @@ -253,6 +253,9 @@ 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)
> > > >  
> > > > @@ -289,6 +292,40 @@ 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_LENGTH                  16
> > > > +#define ICID_MASK                 ((1U << ICID_LENGTH) - 1)
> > > can't you use FIELD') as well for the ICID?
> > in addition to MAPC command ICID is a common field for MAPTI,MAPI
> > commands as well,hence wanted to keep it common and seperate
> > > > +FIELD(MAPC, RDBASE, 16, 32)
> > > > +
> > > > +#define RDBASE_PROCNUM_LENGTH        16
> > > > +#define RDBASE_PROCNUM_MASK       ((1ULL <<
> > > > RDBASE_PROCNUM_LENGTH)
> > > > - 1)
> > > why do we have both the RDBASE FIELD def and above defs?
> > RDBASE FIELD def points to the rdbase field within the MAPC
> > command,while the RDBASE_PROCNUM_ defines are used to consider 16
> > bit
> > PE number as the target destination instead of redistributor base
> > address option.
> > > > +
> > > > +#define DEVID_SHIFT                  32
> > > > +#define DEVID_LENGTH                 32
> > > > +#define DEVID_MASK                ((1ULL << DEVID_LENGTH) - 1)
> > > we don't have any DEVID field in MAPC, I guess it belongs to
> > > MAPD?
> > MAPC doesnt have a DEVID field ,but it is a common field in
> > MAPD,INT,MAPI,MAPTI commands(at the same offset)
> Yes but above there is a command saying "MAPC command fields */
Have moved the DEVID defs to common section to avoid the confusion with
MAPC related define
> > > > +
> > > > +/* MAPD command fields */
> > > > +#define ITTADDR_LENGTH               44
> > > > +#define ITTADDR_SHIFT                 8
> > > > +#define ITTADDR_MASK              ((1ULL << ITTADDR_LENGTH) -
> > > > 1)
> > > > +#define SIZE_MASK                 0x1f
> > > Can't you homogenize the definition, use field() and/or prefix
> > > with
> > > the
> > > cmd name when not common to severals cmds?
> > Since ITTADDR_MASK is common to both MAPD command as well as device
> > table entry field,didnt want to go with field() as the MAPD tag-
> > name in
> > device table entry would be insignificant
> > > > +
> > > > +#define VALID_SHIFT               63
> > > > +#define VALID_MASK                1ULL
> > > >  
> > > >  /**
> > > >   * Default features advertised by this version of ITS
> > > > 
> > > Thanks
> > > 
> > > Eric
> > > 
> > 
> > 
> > 
> Thanks
> 
> Eric
> 



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

* Re: [PATCH v4 4/8] hw/intc: GICv3 ITS Command processing
  2021-06-21 10:13       ` Eric Auger
@ 2021-06-28 22:04         ` shashi.mallela
  0 siblings, 0 replies; 52+ messages in thread
From: shashi.mallela @ 2021-06-28 22:04 UTC (permalink / raw)
  To: Eric Auger, peter.maydell, leif, rad; +Cc: qemu-arm, qemu-devel

Hi Eric,

Have taken care of the pending & latest "process_int" comment(below) by
renaming it to process_its_cmd to keep it more generic since it is also
handles other ITS commands and interrupt trigger both by INT command as
well as GITS_TRANSLATER write.Have added comments for this function as
well.

Will be sharing the updated patchset with all comments addressed soon.

Thanks
Shashi

On Mon, 2021-06-21 at 12:13 +0200, Eric Auger wrote:
> 
> On 6/16/21 11:02 PM, shashi.mallela@linaro.org wrote:
> > Hi Eric,
> > 
> > Please find my responses inline (below):-
> > 
> > On Sun, 2021-06-13 at 17:55 +0200, Eric Auger wrote:
> > > Hi Shashi,
> > > 
> > > On 6/2/21 8:00 PM, Shashi Mallela 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            | 334
> > > > +++++++++++++++++++++++++++++
> > > >  hw/intc/gicv3_internal.h           |  12 ++
> > > >  include/hw/intc/arm_gicv3_common.h |   2 +
> > > >  3 files changed, 348 insertions(+)
> > > > 
> > > > diff --git a/hw/intc/arm_gicv3_its.c b/hw/intc/arm_gicv3_its.c
> > > > index 6551c577b3..82bb5b84ef 100644
> > > > --- a/hw/intc/arm_gicv3_its.c
> > > > +++ b/hw/intc/arm_gicv3_its.c
> > > > @@ -28,6 +28,13 @@ 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;
> > > Add a comment to explain what this enum stand for. This sounds
> > > misleading to me versus the command IDs. Why don't you use the
> > > cmd id
> > > then and add NONE?
> > This is an internal enum used to distinguish between interrupt
> > triggered via command queue and interrupt triggered via
> > gits_translater
> > write.Will add the same comment in code.
> > Since NONE is only 1 command applicable for GITS_TRANSLATER,started
> > with it so that in the future if any further command queue commands
> > have to be added we can just extend the numbering.
> > > > +
> > > >  static uint64_t baser_base_addr(uint64_t value, uint32_t
> > > > page_sz)
> > > >  {
> > > >      uint64_t result = 0;
> > > > @@ -49,6 +56,315 @@ static uint64_t baser_base_addr(uint64_t
> > > > value,
> > > > uint32_t page_sz)
> > > >      return result;
> > > >  }
> > > >  
> > > > +static bool get_cte(GICv3ITSState *s, uint16_t icid, uint64_t
> > > > *cte,
> > > > +                    MemTxResult *res)
> > > > +{
> > > > +    AddressSpace *as = &s->gicv3->dma_as;
> > > > +    uint64_t l2t_addr;
> > > > +    uint64_t value;
> > > > +    bool valid_l2t;
> > > > +    uint32_t l2t_id;
> > > > +    uint32_t max_l2_entries;
> > > > +    bool status = false;
> > > > +
> > > > +    if (s->ct.indirect) {
> > > > +        l2t_id = icid / (s->ct.page_sz / L1TABLE_ENTRY_SIZE);
> > > > +
> > > > +        value = address_space_ldq_le(as,
> > > > +                                     s->ct.base_addr +
> > > > +                                     (l2t_id *
> > > > L1TABLE_ENTRY_SIZE),
> > > > +                                     MEMTXATTRS_UNSPECIFIED,
> > > > res);
> > > > +
> > > > +        if (*res == MEMTX_OK) {
> > > > +            valid_l2t = (value >> VALID_SHIFT) & VALID_MASK;
> > > > +
> > > > +            if (valid_l2t) {
> > > > +                max_l2_entries = s->ct.page_sz / s-
> > > > >ct.entry_sz;
> > > > +
> > > > +                l2t_addr = value & ((1ULL << 51) - 1);
> > > > +
> > > > +                *cte =  address_space_ldq_le(as, l2t_addr +
> > > > +                                    ((icid % max_l2_entries) *
> > > > GITS_CTE_SIZE),
> > > > +                                    MEMTXATTRS_UNSPECIFIED,
> > > > res);
> > > > +           }
> > > > +       }
> > > > +    } else {
> > > > +        /* Flat level table */
> > > > +        *cte =  address_space_ldq_le(as, s->ct.base_addr +
> > > > +                                     (icid * GITS_CTE_SIZE),
> > > > +                                      MEMTXATTRS_UNSPECIFIED,
> > > > res);
> > > > +    }
> > > > +
> > > > +    if (*cte & VALID_MASK) {
> > > > +        status = true;
> > > > +    }
> > > > +
> > > > +    return status;
> > > > +}
> > > > +
> > > > +static MemTxResult update_ite(GICv3ITSState *s, uint32_t
> > > > eventid,
> > > > uint64_t dte,
> > > > +                              uint64_t itel, uint32_t iteh)
> > > why not introducing an ite struct instead of the h/l args?based
> > > on
> > > the same reason for packing individual fields within bit
> > > positions
> > > instead of using more memory to store the same fields as struct
> > > members.
> > Will create an ite struct with existing itel & iteh as members and
> > retain their bit fields processing to avoid extra params being
> > passed.
> > > > +{
> > > > +    AddressSpace *as = &s->gicv3->dma_as;
> > > > +    uint64_t itt_addr;
> > > > +    MemTxResult res = MEMTX_OK;
> > > > +
> > > > +    itt_addr = (dte >> 6ULL) & ITTADDR_MASK;
> > > > +    itt_addr <<= ITTADDR_SHIFT; /* 256 byte aligned */
> > > > +
> > > > +    address_space_stq_le(as, itt_addr + (eventid *
> > > > sizeof(uint64_t)),
> > > > +                         itel, MEMTXATTRS_UNSPECIFIED, &res);
> > > > +
> > > > +    if (res == MEMTX_OK) {
> > > > +        address_space_stl_le(as, itt_addr + ((eventid +
> > > > sizeof(uint64_t)) *
> > > > +                             sizeof(uint32_t)), iteh,
> > > > MEMTXATTRS_UNSPECIFIED,
> > > > +                             &res);
> > > > +    }
> > > > +   return res;
> > > > +}
> > > > +
> > > > +static bool get_ite(GICv3ITSState *s, uint32_t eventid,
> > > > uint64_t
> > > > dte,
> > > > +                    uint16_t *icid, uint32_t *pIntid,
> > > > MemTxResult
> > > > *res)
> > > > +{
> > > > +    AddressSpace *as = &s->gicv3->dma_as;
> > > > +    uint64_t itt_addr;
> > > > +    bool status = false;
> > > > +    uint64_t itel = 0;
> > > > +    uint32_t iteh = 0;
> > > > +
> > > > +    itt_addr = (dte >> 6ULL) & ITTADDR_MASK;
> > > > +    itt_addr <<= ITTADDR_SHIFT; /* 256 byte aligned */
> > > > +
> > > > +    itel = address_space_ldq_le(as, itt_addr + (eventid *
> > > > sizeof(uint64_t)),
> > > > +                                MEMTXATTRS_UNSPECIFIED, res);
> > > > +
> > > > +    if (*res == MEMTX_OK) {
> > > > +        iteh = address_space_ldl_le(as, itt_addr + ((eventid +
> > > > +                                    sizeof(uint64_t)) *
> > > > sizeof(uint32_t)),
> > > > +                                    MEMTXATTRS_UNSPECIFIED,
> > > > res);
> > > > +
> > > > +        if (*res == MEMTX_OK) {
> > > > +            if (itel & VALID_MASK) {
> > > > +                if ((itel >> ITE_ENTRY_INTTYPE_SHIFT) &
> > > > GITS_TYPE_PHYSICAL) {
> > > > +                    *pIntid = (itel >> ITE_ENTRY_INTID_SHIFT)
> > > > &
> > > > +                               ITE_ENTRY_INTID_MASK;
> > > > +                    *icid = iteh & ITE_ENTRY_ICID_MASK;
> > > > +                    status = true;
> > > > +                }
> > > > +            }
> > > > +        }
> > > > +    }
> > > > +    return status;
> > > > +}
> > > > +
> > > > +static uint64_t get_dte(GICv3ITSState *s, uint32_t devid,
> > > > MemTxResult *res)
> > > maybe the code would be more readable if you were returning a
> > > strcut
> > > for
> > > dte/cte instead of uint64_t. The decoding of the fields would be
> > > done
> > > here instead?
> > Both dte and cte are 8 bytes and hence chose a single uint64_t to
> > efficiently utilize the applicable bit fields in the variable
> > instead
> > of a struct(either with bitfields to match the current field
> > layouts
> > within dte/cte/ite entries or use more memory variables within the
> > struct to hold each individual dte/cte/ite fields)
> > > > +{
> > > > +    AddressSpace *as = &s->gicv3->dma_as;
> > > > +    uint64_t l2t_addr;
> > > > +    uint64_t value;
> > > > +    bool valid_l2t;
> > > > +    uint32_t l2t_id;
> > > > +    uint32_t max_l2_entries;
> > > > +
> > > > +    if (s->dt.indirect) {
> > > > +        l2t_id = devid / (s->dt.page_sz / L1TABLE_ENTRY_SIZE);
> > > > +
> > > > +        value = address_space_ldq_le(as,
> > > > +                                     s->dt.base_addr +
> > > > +                                     (l2t_id *
> > > > L1TABLE_ENTRY_SIZE),
> > > > +                                     MEMTXATTRS_UNSPECIFIED,
> > > > res);
> > > > +
> > > > +        if (*res == MEMTX_OK) {
> > > > +            valid_l2t = (value >> VALID_SHIFT) & VALID_MASK;
> > > > +
> > > > +            if (valid_l2t) {
> > > > +                max_l2_entries = s->dt.page_sz / s-
> > > > >dt.entry_sz;
> > > > +
> > > > +                l2t_addr = value & ((1ULL << 51) - 1);
> > > > +
> > > > +                value =  address_space_ldq_le(as, l2t_addr +
> > > > +                                   ((devid % max_l2_entries) *
> > > > GITS_DTE_SIZE),
> > > > +                                   MEMTXATTRS_UNSPECIFIED,
> > > > res);
> > > > +            }
> > > > +        }
> > > > +    } else {
> > > > +        /* Flat level table */
> > > > +        value = address_space_ldq_le(as, s->dt.base_addr +
> > > > +                                     (devid * GITS_DTE_SIZE),
> > > > +                                     MEMTXATTRS_UNSPECIFIED,
> > > > res);
> > > > +    }
> > > > +
> > > > +    return value;
> > > > +}
> > > I think a common helper could be defined for get_cte and get_dte.
> > was just trying to keep the code modular for getting dte and cte
> > instead of various if else's within a common helper 
> > > > +
> > > > +static MemTxResult process_int(GICv3ITSState *s, uint64_t
> > > > value,
> > > > +                               uint32_t offset, ItsCmdType
> > > > cmd)
> > > this is a bit misleanding as INT is a command. You should rename
> > > it I
> > > think. Also it is not really homogeneous with other cmds, ie. you
> > > have
> > > process_mapti, process_mapd, process_mac and all the remaining
> > > cmds
> > > are
> > > handled though this one? At least add a doc comment to explain
> > > what
> > > it does.
> > the naming of this function is along the lines of other ITS
> > commands
> > i.e process_xxx format where xxx is the actual ITS command.
> > There is no connection between handling of
> > process_mapd,process_mapc
> > with process_int.Each of them handle their respective command
> > processing independently.
> Well I am not big fan of keeping that name ;-) but well.
> > > > +{
> > > > +    AddressSpace *as = &s->gicv3->dma_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;
> > > > +    uint64_t itel = 0;
> > > > +    uint32_t iteh = 0;
> > > > +
> > > > +    if (cmd == NONE) {
> > > > +        devid = offset;
> > > > +    } else {
> > > > +        devid = (value >> DEVID_SHIFT) & DEVID_MASK;
> > > > +
> > > > +        offset += NUM_BYTES_IN_DW;
> > > > +        value = address_space_ldq_le(as, s->cq.base_addr +
> > > > offset,
> > > > +                                     MEMTXATTRS_UNSPECIFIED,
> > > > &res);
> > > > +    }
> > > > +
> > > > +    if (res != MEMTX_OK) {
> > > > +        return res;
> > > > +    }
> > > > +
> > > > +    eventid = (value & EVENTID_MASK);
> > > > +
> > > > +    dte = get_dte(s, devid, &res);
> > > > +
> > > > +    if (res != MEMTX_OK) {
> > > > +        return res;
> > > > +    }
> > > > +    dte_valid = dte & VALID_MASK;
> > > > +
> > > > +    if (dte_valid) {
> > > > +        max_eventid = (1UL << (((dte >> 1U) & SIZE_MASK) +
> > > > 1));
> > > THE DTE format is implementation defined, and the decoding is
> > > hard to
> > > follow because it is documented somewhere else.
> > The decoding of fields within each of dte,cte and ite has been
> > explained as comments in gicv3_internal.h via
> > GITS_DTE_SIZE,GITS_CTE_SIZE, ITS_ITT_ENTRY_SIZE defines
> > > > +
> > > > +        ite_valid = get_ite(s, eventid, dte, &icid, &pIntid,
> > > > &res);
> > > > +
> > > > +        if (res != MEMTX_OK) {
> > > > +            return res;
> > > > +        }
> > > > +
> > > > +        if (ite_valid) {
> > > > +            cte_valid = get_cte(s, icid, &cte, &res);
> > > > +        }
> > > > +
> > > > +        if (res != MEMTX_OK) {
> > > > +            return res;
> > > > +        }
> > > instead of having this process_int() helper, why not having a
> > > helper
> > > doing just the decoding phase, ie.
> > > get_dte -> get_ite -> get_cte and collect the relevant info and
> > > collect
> > > error and then keep the actual cmd processing in the switch?
> > As explained previously chose to keep the code modular instead of a
> > large monolothic structure with all the functionality in 1 place.
> > process_cmdq handles the command queue (and their read/write
> > offsets)
> > while the individual ITS command handling is done by respective
> > functions
> 
> OK That's not my opinion but it is not a blocker anyway ;-)
> > > > +    }
> > > > +
> > > > +    if ((devid > s->dt.max_devids) || !dte_valid || !ite_valid
> > > > ||
> > > > +            !cte_valid || (eventid > max_eventid)) {
> > > > +        qemu_log_mask(LOG_GUEST_ERROR,
> > > > +                      "%s: invalid interrupt translation table
> > > > attributes "
> > > > +                      "devid %d or eventid %d\n",
> > > > +                      __func__, devid, eventid);
> > > the msg does not necessarily match the error case. You mention
> > > ITT
> > > issue
> > > whereas the problem may come from invalid DTE, CTE, or even
> > > devid.
> > will change it to consider the missing cases
> > > > +        /*
> > > > +         * in this implementation,in case of error
> > > > +         * we ignore this command and move onto the next
> > > > +         * command in the queue
> > > > +         */
> > > so you don't return an error?
> > yes we dont,we just ignore this command and move onto next one in
> > the
> > queue
> > > > +    } else {
> > > > +        /*
> > > > +         * Current implementation only supports rdbase ==
> > > > procnum
> > > > +         * Hence rdbase physical address is ignored
> > > > +         */
> > > > +        if (cmd == DISCARD) {
> > > > +            /* remove mapping from interrupt translation table
> > > > */
> > > > +            res = update_ite(s, eventid, dte, itel, iteh);
> > > iteh and itel always are 0, why not use a struct ite with valid
> > > field
> > > unset.
> > based on the same reason for packing individual fields within bit
> > positions instead of using more memory to store the same fields as
> > struct members.
> > I could however create an ite struct with existing itel & iteh as
> > members and retain their bit fields processing. 
> > > > +        }
> > > > +    }
> > > > +
> > > > +    return res;
> > > > +}
> > > > +
> > > > +static MemTxResult process_mapti(GICv3ITSState *s, uint64_t
> > > > value,
> > > > +                                 uint32_t offset, bool
> > > > ignore_pInt)
> > > > +{
> > > > +    AddressSpace *as = &s->gicv3->dma_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 itel = 0;
> > > > +    uint32_t iteh = 0;
> > > > +    uint32_t int_spurious = INTID_SPURIOUS;
> > > > +
> > > > +    devid = (value >> DEVID_SHIFT) & DEVID_MASK;
> > > > +    offset += NUM_BYTES_IN_DW;
> > > > +    value = address_space_ldq_le(as, s->cq.base_addr + offset,
> > > > +                                 MEMTXATTRS_UNSPECIFIED,
> > > > &res);
> > > > +
> > > > +    if (res != MEMTX_OK) {
> > > > +        return 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);
> > > > +
> > > > +    if (res != MEMTX_OK) {
> > > > +        return res;
> > > > +    }
> > > > +
> > > > +    icid = value & ICID_MASK;
> > > > +
> > > > +    dte = get_dte(s, devid, &res);
> > > > +
> > > > +    if (res != MEMTX_OK) {
> > > > +        return res;
> > > > +    }
> > > > +    dte_valid = dte & VALID_MASK;
> > > > +
> > > > +    max_eventid = (1UL << (((dte >> 1U) & SIZE_MASK) + 1));
> > > Again I think you would gain in readibility if get_cte were to
> > > return
> > > a
> > > struct and you would avoid code duplication.
> > Like mentioned earlier, preferred to use a single uint64_t for cte
> > (and
> > dte) instead of a struct (either with bitfields to match the
> > current
> > field layouts within dte/cte/ite entries or use more memory
> > variables
> > within the struct to hold each individual dte/cte/ite fields).The
> > layout of all these functions follows the pseudocde format defined
> > under each ITS command in the spec.
> > > > +
> > > > +    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)))) {
> > > > +        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 ite entry to interrupt translation table */
> > > > +        itel = (dte_valid & VALID_MASK) | (GITS_TYPE_PHYSICAL
> > > > <<
> > > > +                                           ITE_ENTRY_INTTYPE_S
> > > > HIFT
> > > > );
> > > > +
> > > > +        if (ignore_pInt) {
> > > > +            itel |= (eventid << ITE_ENTRY_INTID_SHIFT);
> > > > +        } else {
> > > > +            itel |= (pIntid << ITE_ENTRY_INTID_SHIFT);
> > > > +        }
> > > > +        itel |= (int_spurious << ITE_ENTRY_INTSP_SHIFT);
> > > > +        iteh |= icid;
> > > > +
> > > > +        res = update_ite(s, eventid, dte, itel, iteh);
> > > > +    }
> > > > +
> > > > +    return res;
> > > > +}
> > > > +
> > > >  static MemTxResult update_cte(GICv3ITSState *s, uint16_t icid,
> > > > bool valid,
> > > >                                uint64_t rdbase)
> > > >  {
> > > > @@ -295,8 +611,10 @@ static void 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:
> > > >              /*
> > > > @@ -313,10 +631,13 @@ static void 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;
> > > > @@ -472,7 +793,20 @@ static MemTxResult
> > > > gicv3_its_translation_write(void *opaque, hwaddr offset,
> > > >                                                 uint64_t data,
> > > > unsigned size,
> > > >                                                 MemTxAttrs
> > > > attrs)
> > > >  {
> > > > +    GICv3ITSState *s = (GICv3ITSState *)opaque;
> > > >      MemTxResult result = MEMTX_OK;
> > > > +    uint32_t devid = 0;
> > > > +
> > > > +    switch (offset) {
> > > > +    case GITS_TRANSLATER:
> > > > +        if (s->ctlr & ITS_CTLR_ENABLED) {
> > > > +            devid = attrs.requester_id;
> > > > +            result = process_int(s, data, devid, NONE);
> > > > +        }
> > > > +        break;
> > > > +    default:
> > > > +        break;
> > > > +    }
> > > >  
> > > >      return result;
> > > >  }
> > > > diff --git a/hw/intc/gicv3_internal.h
> > > > b/hw/intc/gicv3_internal.h
> > > > index 0932a30560..ce45cd0ef6 100644
> > > > --- a/hw/intc/gicv3_internal.h
> > > > +++ b/hw/intc/gicv3_internal.h
> > > > @@ -324,6 +324,13 @@ FIELD(MAPC, RDBASE, 16, 32)
> > > >  #define ITTADDR_MASK              ((1ULL << ITTADDR_LENGTH) -
> > > > 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                1ULL
> > > >  
> > > > @@ -344,6 +351,11 @@ FIELD(MAPC, RDBASE, 16, 32)
> > > >   * vPEID = 16 bits
> > > >   */
> > > >  #define ITS_ITT_ENTRY_SIZE            0xC
> > > > +#define ITE_ENTRY_INTTYPE_SHIFT        1
> > > > +#define ITE_ENTRY_INTID_SHIFT          2
> > > > +#define ITE_ENTRY_INTID_MASK         ((1ULL << 24) - 1)
> > > > +#define ITE_ENTRY_INTSP_SHIFT          26
> > > > +#define ITE_ENTRY_ICID_MASK          ((1ULL << 16) - 1)
> > > >  
> > > >  /* 16 bits EventId */
> > > >  #define ITS_IDBITS                   GICD_TYPER_IDBITS
> > > > diff --git a/include/hw/intc/arm_gicv3_common.h
> > > > b/include/hw/intc/arm_gicv3_common.h
> > > > index 1fd5cedbbd..0715b0bc2a 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 */
> > > > 
> > > Thanks
> > > 
> > > Eric
> > > 
> Thanks
> 
> Eric
> 



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

* Re: [PATCH v4 1/8] hw/intc: GICv3 ITS initial framework
  2021-06-12  6:52   ` Eric Auger
@ 2021-07-06  7:29     ` Eric Auger
  0 siblings, 0 replies; 52+ messages in thread
From: Eric Auger @ 2021-07-06  7:29 UTC (permalink / raw)
  To: Shashi Mallela, peter.maydell, leif, rad; +Cc: qemu-arm, qemu-devel

Hi,

On 6/12/21 8:52 AM, Eric Auger wrote:
> 
> 
> On 6/2/21 8:00 PM, Shashi Mallela 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                | 240 +++++++++++++++++++++++++
>>  hw/intc/arm_gicv3_its_common.c         |   8 +-
>>  hw/intc/arm_gicv3_its_kvm.c            |   2 +-
>>  hw/intc/gicv3_internal.h               |  88 +++++++--
>>  hw/intc/meson.build                    |   1 +
>>  include/hw/intc/arm_gicv3_its_common.h |   9 +-
>>  6 files changed, 331 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..545cda3665
>> --- /dev/null
>> +++ b/hw/intc/arm_gicv3_its.c
>> @@ -0,0 +1,240 @@
>> +/*
>> + * 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 gicv3_its_translation_write(void *opaque, hwaddr offset,
>> +                                               uint64_t data, unsigned size,
>> +                                               MemTxAttrs attrs)
>> +{
>> +    MemTxResult result = MEMTX_OK;
>> +
>> +    return result;
>> +}
>> +
>> +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 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 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,
>> +    .valid.min_access_size = 4,
>> +    .valid.max_access_size = 8,
>> +    .impl.min_access_size = 4,
>> +    .impl.max_access_size = 8,
>> +    .endianness = DEVICE_NATIVE_ENDIAN,
>> +};
>> +
>> +static const MemoryRegionOps gicv3_its_translation_ops = {
>> +    .write_with_attrs = gicv3_its_translation_write,
>> +    .valid.min_access_size = 2,
>> +    .valid.max_access_size = 4,
>> +    .impl.min_access_size = 2,
>> +    .impl.max_access_size = 4,
>> +    .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);
>> +
>> +    if (s->gicv3->cpu->gicr_typer & GICR_TYPER_PLPIS) {
>> +        /* 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 - 1);
>> +        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);
>> +    }
>> +}
>> +
>> +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);
>> +
>> +        /* 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 - 1);
>> +
>> +        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 - 1);
>> +    }
>> +}
>> +
>> +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..f1657c84e0 100644
>> --- a/hw/intc/arm_gicv3_its_common.c
>> +++ b/hw/intc/arm_gicv3_its_common.c
>> @@ -50,6 +50,8 @@ 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,
>> @@ -99,14 +101,15 @@ 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,
>> +                          tops ? tops : &gicv3_its_trans_ops, s,
>>                            "translation", ITS_TRANS_SIZE);
>>  
>>      /* Our two regions are always adjacent, therefore we now combine them
>> @@ -129,7 +132,6 @@ static void gicv3_its_common_reset(DeviceState *dev)
>>      s->cbaser = 0;
>>      s->cwriter = 0;
>>      s->creadr = 0;
>> -    s->iidr = 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..e0b06930a7 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)
>> -
> Here you remove those defs without much explanation in the commit msg.
> I see you restore them in 5/8
> 
> +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)
> 
> Maybe squash this in this patch?

not addressed in v5.

Thanks

Eric
> 
> Thanks
> 
> Eric
> 
>>  #define ICC_CTLR_EL1_CBPR           (1U << 0)
>>  #define ICC_CTLR_EL1_EOIMODE        (1U << 1)
>>  #define ICC_CTLR_EL1_PMHE           (1U << 6)
>> @@ -239,6 +231,78 @@
>>  #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)
>> +
>> +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)
>> +
>> +#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
>> +
>> +/**
>> + * Default features advertised by this version of ITS
>> + */
>> +/* Physical LPIs supported */
>> +#define GITS_TYPE_PHYSICAL           (1U << 0)
>> +
>> +/*
>> + * 12 bytes Interrupt translation Table Entry size
>> + * ITE Lower 8 Bytes
>> + * Valid = 1 bit,InterruptType = 1 bit,
>> + * Size of LPI number space[considering max 24 bits],
>> + * Size of LPI number space[considering max 24 bits],
>> + * ITE Higher 4 Bytes
>> + * ICID = 16 bits,
>> + * vPEID = 16 bits
>> + */
>> +#define ITS_ITT_ENTRY_SIZE            0xC
>> +
>> +/* 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 6e52a166e3..4dcfea6aa8 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..65d1191db1 100644
>> --- a/include/hw/intc/arm_gicv3_its_common.h
>> +++ b/include/hw/intc/arm_gicv3_its_common.h
>> @@ -25,17 +25,22 @@
>>  #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
>> +
>>  struct GICv3ITSState {
>>      SysBusDevice parent_obj;
>>  
>> @@ -52,6 +57,7 @@ struct GICv3ITSState {
>>      /* Registers */
>>      uint32_t ctlr;
>>      uint32_t iidr;
>> +    uint64_t typer;
>>      uint64_t cbaser;
>>      uint64_t cwriter;
>>      uint64_t creadr;
>> @@ -62,7 +68,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;
>>



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

* Re: [PATCH v4 1/8] hw/intc: GICv3 ITS initial framework
  2021-06-11 16:21   ` Eric Auger
  2021-06-11 17:23     ` Shashi Mallela
@ 2021-07-06  7:38     ` Eric Auger
  2021-07-06 13:24       ` shashi.mallela
  1 sibling, 1 reply; 52+ messages in thread
From: Eric Auger @ 2021-07-06  7:38 UTC (permalink / raw)
  To: Shashi Mallela, peter.maydell, leif, rad; +Cc: qemu-arm, qemu-devel

Hi,

On 6/11/21 6:21 PM, Eric Auger wrote:
> Hi,
> 
> On 6/2/21 8:00 PM, Shashi Mallela 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                | 240 +++++++++++++++++++++++++
>>  hw/intc/arm_gicv3_its_common.c         |   8 +-
>>  hw/intc/arm_gicv3_its_kvm.c            |   2 +-
>>  hw/intc/gicv3_internal.h               |  88 +++++++--
>>  hw/intc/meson.build                    |   1 +
>>  include/hw/intc/arm_gicv3_its_common.h |   9 +-
>>  6 files changed, 331 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..545cda3665
>> --- /dev/null
>> +++ b/hw/intc/arm_gicv3_its.c
>> @@ -0,0 +1,240 @@
>> +/*
>> + * 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 gicv3_its_translation_write(void *opaque, hwaddr offset,
>> +                                               uint64_t data, unsigned size,
>> +                                               MemTxAttrs attrs)
>> +{
>> +    MemTxResult result = MEMTX_OK;
>> +
>> +    return result;
>> +}
>> +
>> +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 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 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,
>> +    .valid.min_access_size = 4,
>> +    .valid.max_access_size = 8,
>> +    .impl.min_access_size = 4,
>> +    .impl.max_access_size = 8,
>> +    .endianness = DEVICE_NATIVE_ENDIAN,
>> +};
>> +
>> +static const MemoryRegionOps gicv3_its_translation_ops = {
>> +    .write_with_attrs = gicv3_its_translation_write,
>> +    .valid.min_access_size = 2,
>> +    .valid.max_access_size = 4,
>> +    .impl.min_access_size = 2,
>> +    .impl.max_access_size = 4,
>> +    .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);
>> +
>> +    if (s->gicv3->cpu->gicr_typer & GICR_TYPER_PLPIS) {
>> +        /* 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 - 1);
>> +        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);
>> +    }
>> +}
>> +
>> +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);
>> +
>> +        /* 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 - 1);
>> +
>> +        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 - 1);
>> +    }
>> +}
>> +
>> +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..f1657c84e0 100644
>> --- a/hw/intc/arm_gicv3_its_common.c
>> +++ b/hw/intc/arm_gicv3_its_common.c
>> @@ -50,6 +50,8 @@ 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,
>> @@ -99,14 +101,15 @@ 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,
>> +                          tops ? tops : &gicv3_its_trans_ops, s,
>>                            "translation", ITS_TRANS_SIZE);
>>  
>>      /* Our two regions are always adjacent, therefore we now combine them
>> @@ -129,7 +132,6 @@ static void gicv3_its_common_reset(DeviceState *dev)
>>      s->cbaser = 0;
>>      s->cwriter = 0;
>>      s->creadr = 0;
>> -    s->iidr = 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..e0b06930a7 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,78 @@
>>  #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)
> Isn't it FIELD(GITS_BASER, PHYADDRH_64K, 12, 4)
> hum actually it is fixed in next patch ;-) The right value can be put
> here directly
not addressed in v5
>> +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)
>> +
>> +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)
>> +
>> +#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 may rename into GITS_BASER_TYPE_DEVICE and COLLECTION?
not addressed in v5
>> +
>> +/**
>> + * Default features advertised by this version of ITS
>> + */
>> +/* Physical LPIs supported */
>> +#define GITS_TYPE_PHYSICAL           (1U << 0)
>> +
>> +/*
>> + * 12 bytes Interrupt translation Table Entry size
>> + * ITE Lower 8 Bytes
>> + * Valid = 1 bit,InterruptType = 1 bit,
>> + * Size of LPI number space[considering max 24 bits],
>> + * Size of LPI number space[considering max 24 bits],
> repeated
not adressed in v5
>> + * ITE Higher 4 Bytes
>> + * ICID = 16 bits,
>> + * vPEID = 16 bits
> 
> for info the ABI used by the kernel can be found in linux
> Documentation/virt/kvm/devices/arm-vgic-its.rst
> 
> The ITE there is 8 bytes.
> 
> Have you considered the same?
> 
>> + */
>> +#define ITS_ITT_ENTRY_SIZE            0xC
>> +
>> +/* 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 6e52a166e3..4dcfea6aa8 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..65d1191db1 100644
>> --- a/include/hw/intc/arm_gicv3_its_common.h
>> +++ b/include/hw/intc/arm_gicv3_its_common.h
>> @@ -25,17 +25,22 @@
>>  #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
>> +
>>  struct GICv3ITSState {
>>      SysBusDevice parent_obj;
>>  
>> @@ -52,6 +57,7 @@ struct GICv3ITSState {
>>      /* Registers */
>>      uint32_t ctlr;
>>      uint32_t iidr;
>> +    uint64_t typer;
>>      uint64_t cbaser;
>>      uint64_t cwriter;
>>      uint64_t creadr;
>> @@ -62,7 +68,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;
>>
> Thanks
> 
> Eric
> 

Thanks

Eric



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

* Re: [PATCH v4 1/8] hw/intc: GICv3 ITS initial framework
  2021-07-06  7:38     ` Eric Auger
@ 2021-07-06 13:24       ` shashi.mallela
  2021-07-06 14:04         ` Eric Auger
  0 siblings, 1 reply; 52+ messages in thread
From: shashi.mallela @ 2021-07-06 13:24 UTC (permalink / raw)
  To: Eric Auger, peter.maydell, leif, rad; +Cc: qemu-arm, qemu-devel

Hi Eric,

Please find my response inline(below):-

On Tue, 2021-07-06 at 09:38 +0200, Eric Auger wrote:
> Hi,
> 
> On 6/11/21 6:21 PM, Eric Auger wrote:
> > Hi,
> > 
> > On 6/2/21 8:00 PM, Shashi Mallela 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                | 240
> > > +++++++++++++++++++++++++
> > >  hw/intc/arm_gicv3_its_common.c         |   8 +-
> > >  hw/intc/arm_gicv3_its_kvm.c            |   2 +-
> > >  hw/intc/gicv3_internal.h               |  88 +++++++--
> > >  hw/intc/meson.build                    |   1 +
> > >  include/hw/intc/arm_gicv3_its_common.h |   9 +-
> > >  6 files changed, 331 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..545cda3665
> > > --- /dev/null
> > > +++ b/hw/intc/arm_gicv3_its.c
> > > @@ -0,0 +1,240 @@
> > > +/*
> > > + * 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 gicv3_its_translation_write(void *opaque,
> > > hwaddr offset,
> > > +                                               uint64_t data,
> > > unsigned size,
> > > +                                               MemTxAttrs attrs)
> > > +{
> > > +    MemTxResult result = MEMTX_OK;
> > > +
> > > +    return result;
> > > +}
> > > +
> > > +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 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 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,
> > > +    .valid.min_access_size = 4,
> > > +    .valid.max_access_size = 8,
> > > +    .impl.min_access_size = 4,
> > > +    .impl.max_access_size = 8,
> > > +    .endianness = DEVICE_NATIVE_ENDIAN,
> > > +};
> > > +
> > > +static const MemoryRegionOps gicv3_its_translation_ops = {
> > > +    .write_with_attrs = gicv3_its_translation_write,
> > > +    .valid.min_access_size = 2,
> > > +    .valid.max_access_size = 4,
> > > +    .impl.min_access_size = 2,
> > > +    .impl.max_access_size = 4,
> > > +    .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);
> > > +
> > > +    if (s->gicv3->cpu->gicr_typer & GICR_TYPER_PLPIS) {
> > > +        /* 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 - 1);
> > > +        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);
> > > +    }
> > > +}
> > > +
> > > +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);
> > > +
> > > +        /* 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 - 1);
> > > +
> > > +        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 - 1);
> > > +    }
> > > +}
> > > +
> > > +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..f1657c84e0 100644
> > > --- a/hw/intc/arm_gicv3_its_common.c
> > > +++ b/hw/intc/arm_gicv3_its_common.c
> > > @@ -50,6 +50,8 @@ 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,
> > > @@ -99,14 +101,15 @@ 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,
> > > +                          tops ? tops : &gicv3_its_trans_ops, s,
> > >                            "translation", ITS_TRANS_SIZE);
> > >  
> > >      /* Our two regions are always adjacent, therefore we now
> > > combine them
> > > @@ -129,7 +132,6 @@ static void
> > > gicv3_its_common_reset(DeviceState *dev)
> > >      s->cbaser = 0;
> > >      s->cwriter = 0;
> > >      s->creadr = 0;
> > > -    s->iidr = 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..e0b06930a7 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,78 @@
> > >  #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)
> > Isn't it FIELD(GITS_BASER, PHYADDRH_64K, 12, 4)
> > hum actually it is fixed in next patch ;-) The right value can be
> > put
> > here directly
> not addressed in v5
will do
> > > +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)
> > > +
> > > +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)
> > > +
> > > +#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 may rename into GITS_BASER_TYPE_DEVICE and COLLECTION?
> not addressed in v5
will do
> > > +
> > > +/**
> > > + * Default features advertised by this version of ITS
> > > + */
> > > +/* Physical LPIs supported */
> > > +#define GITS_TYPE_PHYSICAL           (1U << 0)
> > > +
> > > +/*
> > > + * 12 bytes Interrupt translation Table Entry size
> > > + * ITE Lower 8 Bytes
> > > + * Valid = 1 bit,InterruptType = 1 bit,
> > > + * Size of LPI number space[considering max 24 bits],
> > > + * Size of LPI number space[considering max 24 bits],
> > repeated
> not adressed in v5
I had replied to this comment earlier,
The ITE size of 12 bytes format defined here is based on "Table 5-3 ITE
entries" in GIC specification and is generic between both GICv3 & GICv4
versions for both physical and virtual LPIs,unlike the linux kernel ABI
which has current definition only for GICv3 physical LPIs.The idea here
was to define the format once(considering future virtual LPIs too).
Do you still want me to reduce the ITE size to 8 bytes as in linux
kernel ABI (thereby leave the GICV4 fields out)? 

> > > + * ITE Higher 4 Bytes
> > > + * ICID = 16 bits,
> > > + * vPEID = 16 bits
> > 
> > for info the ABI used by the kernel can be found in linux
> > Documentation/virt/kvm/devices/arm-vgic-its.rst
> > 
> > The ITE there is 8 bytes.
> > 
> > Have you considered the same?
> > 
> > > + */
> > > +#define ITS_ITT_ENTRY_SIZE            0xC
> > > +
> > > +/* 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 6e52a166e3..4dcfea6aa8 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..65d1191db1 100644
> > > --- a/include/hw/intc/arm_gicv3_its_common.h
> > > +++ b/include/hw/intc/arm_gicv3_its_common.h
> > > @@ -25,17 +25,22 @@
> > >  #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
> > > +
> > >  struct GICv3ITSState {
> > >      SysBusDevice parent_obj;
> > >  
> > > @@ -52,6 +57,7 @@ struct GICv3ITSState {
> > >      /* Registers */
> > >      uint32_t ctlr;
> > >      uint32_t iidr;
> > > +    uint64_t typer;
> > >      uint64_t cbaser;
> > >      uint64_t cwriter;
> > >      uint64_t creadr;
> > > @@ -62,7 +68,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;
> > > 
> > Thanks
> > 
> > Eric
> > 
> 
> Thanks
> 
> Eric
> 



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

* Re: [PATCH v4 1/8] hw/intc: GICv3 ITS initial framework
  2021-07-06 13:24       ` shashi.mallela
@ 2021-07-06 14:04         ` Eric Auger
  2021-07-06 14:18           ` shashi.mallela
  0 siblings, 1 reply; 52+ messages in thread
From: Eric Auger @ 2021-07-06 14:04 UTC (permalink / raw)
  To: shashi.mallela, peter.maydell, leif, rad; +Cc: qemu-arm, qemu-devel

Hi Shashi,

On 7/6/21 3:24 PM, shashi.mallela@linaro.org wrote:
> Hi Eric,
> 
> Please find my response inline(below):-
> 
> On Tue, 2021-07-06 at 09:38 +0200, Eric Auger wrote:
>> Hi,
>>
>> On 6/11/21 6:21 PM, Eric Auger wrote:
>>> Hi,
>>>
>>> On 6/2/21 8:00 PM, Shashi Mallela 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                | 240
>>>> +++++++++++++++++++++++++
>>>>  hw/intc/arm_gicv3_its_common.c         |   8 +-
>>>>  hw/intc/arm_gicv3_its_kvm.c            |   2 +-
>>>>  hw/intc/gicv3_internal.h               |  88 +++++++--
>>>>  hw/intc/meson.build                    |   1 +
>>>>  include/hw/intc/arm_gicv3_its_common.h |   9 +-
>>>>  6 files changed, 331 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..545cda3665
>>>> --- /dev/null
>>>> +++ b/hw/intc/arm_gicv3_its.c
>>>> @@ -0,0 +1,240 @@
>>>> +/*
>>>> + * 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 gicv3_its_translation_write(void *opaque,
>>>> hwaddr offset,
>>>> +                                               uint64_t data,
>>>> unsigned size,
>>>> +                                               MemTxAttrs attrs)
>>>> +{
>>>> +    MemTxResult result = MEMTX_OK;
>>>> +
>>>> +    return result;
>>>> +}
>>>> +
>>>> +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 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 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,
>>>> +    .valid.min_access_size = 4,
>>>> +    .valid.max_access_size = 8,
>>>> +    .impl.min_access_size = 4,
>>>> +    .impl.max_access_size = 8,
>>>> +    .endianness = DEVICE_NATIVE_ENDIAN,
>>>> +};
>>>> +
>>>> +static const MemoryRegionOps gicv3_its_translation_ops = {
>>>> +    .write_with_attrs = gicv3_its_translation_write,
>>>> +    .valid.min_access_size = 2,
>>>> +    .valid.max_access_size = 4,
>>>> +    .impl.min_access_size = 2,
>>>> +    .impl.max_access_size = 4,
>>>> +    .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);
>>>> +
>>>> +    if (s->gicv3->cpu->gicr_typer & GICR_TYPER_PLPIS) {
>>>> +        /* 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 - 1);
>>>> +        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);
>>>> +    }
>>>> +}
>>>> +
>>>> +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);
>>>> +
>>>> +        /* 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 - 1);
>>>> +
>>>> +        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 - 1);
>>>> +    }
>>>> +}
>>>> +
>>>> +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..f1657c84e0 100644
>>>> --- a/hw/intc/arm_gicv3_its_common.c
>>>> +++ b/hw/intc/arm_gicv3_its_common.c
>>>> @@ -50,6 +50,8 @@ 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,
>>>> @@ -99,14 +101,15 @@ 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,
>>>> +                          tops ? tops : &gicv3_its_trans_ops, s,
>>>>                            "translation", ITS_TRANS_SIZE);
>>>>  
>>>>      /* Our two regions are always adjacent, therefore we now
>>>> combine them
>>>> @@ -129,7 +132,6 @@ static void
>>>> gicv3_its_common_reset(DeviceState *dev)
>>>>      s->cbaser = 0;
>>>>      s->cwriter = 0;
>>>>      s->creadr = 0;
>>>> -    s->iidr = 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..e0b06930a7 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,78 @@
>>>>  #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)
>>> Isn't it FIELD(GITS_BASER, PHYADDRH_64K, 12, 4)
>>> hum actually it is fixed in next patch ;-) The right value can be
>>> put
>>> here directly
>> not addressed in v5
> will do
>>>> +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)
>>>> +
>>>> +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)
>>>> +
>>>> +#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 may rename into GITS_BASER_TYPE_DEVICE and COLLECTION?
>> not addressed in v5
> will do
>>>> +
>>>> +/**
>>>> + * Default features advertised by this version of ITS
>>>> + */
>>>> +/* Physical LPIs supported */
>>>> +#define GITS_TYPE_PHYSICAL           (1U << 0)
>>>> +
>>>> +/*
>>>> + * 12 bytes Interrupt translation Table Entry size
>>>> + * ITE Lower 8 Bytes
>>>> + * Valid = 1 bit,InterruptType = 1 bit,
>>>> + * Size of LPI number space[considering max 24 bits],
>>>> + * Size of LPI number space[considering max 24 bits],
I just meant the above sentence is repeated twice ;-)

>>> repeated
>> not adressed in v5
> I had replied to this comment earlier,
> The ITE size of 12 bytes format defined here is based on "Table 5-3 ITE
> entries" in GIC specification and is generic between both GICv3 & GICv4
> versions for both physical and virtual LPIs,unlike the linux kernel ABI
> which has current definition only for GICv3 physical LPIs.The idea here
> was to define the format once(considering future virtual LPIs too).
> Do you still want me to reduce the ITE size to 8 bytes as in linux
> kernel ABI (thereby leave the GICV4 fields out)? 
no, see above

Thanks

Eric
> 
>>>> + * ITE Higher 4 Bytes
>>>> + * ICID = 16 bits,
>>>> + * vPEID = 16 bits
>>>
>>> for info the ABI used by the kernel can be found in linux
>>> Documentation/virt/kvm/devices/arm-vgic-its.rst
>>>
>>> The ITE there is 8 bytes.
>>>
>>> Have you considered the same?
>>>
>>>> + */
>>>> +#define ITS_ITT_ENTRY_SIZE            0xC
>>>> +
>>>> +/* 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 6e52a166e3..4dcfea6aa8 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..65d1191db1 100644
>>>> --- a/include/hw/intc/arm_gicv3_its_common.h
>>>> +++ b/include/hw/intc/arm_gicv3_its_common.h
>>>> @@ -25,17 +25,22 @@
>>>>  #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
>>>> +
>>>>  struct GICv3ITSState {
>>>>      SysBusDevice parent_obj;
>>>>  
>>>> @@ -52,6 +57,7 @@ struct GICv3ITSState {
>>>>      /* Registers */
>>>>      uint32_t ctlr;
>>>>      uint32_t iidr;
>>>> +    uint64_t typer;
>>>>      uint64_t cbaser;
>>>>      uint64_t cwriter;
>>>>      uint64_t creadr;
>>>> @@ -62,7 +68,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;
>>>>
>>> Thanks
>>>
>>> Eric
>>>
>>
>> Thanks
>>
>> Eric
>>
> 



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

* Re: [PATCH v4 1/8] hw/intc: GICv3 ITS initial framework
  2021-07-06 14:04         ` Eric Auger
@ 2021-07-06 14:18           ` shashi.mallela
  0 siblings, 0 replies; 52+ messages in thread
From: shashi.mallela @ 2021-07-06 14:18 UTC (permalink / raw)
  To: Eric Auger, peter.maydell, leif, rad; +Cc: qemu-arm, qemu-devel

On Tue, 2021-07-06 at 16:04 +0200, Eric Auger wrote:
> Hi Shashi,
> 
> On 7/6/21 3:24 PM, shashi.mallela@linaro.org wrote:
> > Hi Eric,
> > 
> > Please find my response inline(below):-
> > 
> > On Tue, 2021-07-06 at 09:38 +0200, Eric Auger wrote:
> > > Hi,
> > > 
> > > On 6/11/21 6:21 PM, Eric Auger wrote:
> > > > Hi,
> > > > 
> > > > On 6/2/21 8:00 PM, Shashi Mallela 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                | 240
> > > > > +++++++++++++++++++++++++
> > > > >  hw/intc/arm_gicv3_its_common.c         |   8 +-
> > > > >  hw/intc/arm_gicv3_its_kvm.c            |   2 +-
> > > > >  hw/intc/gicv3_internal.h               |  88 +++++++--
> > > > >  hw/intc/meson.build                    |   1 +
> > > > >  include/hw/intc/arm_gicv3_its_common.h |   9 +-
> > > > >  6 files changed, 331 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..545cda3665
> > > > > --- /dev/null
> > > > > +++ b/hw/intc/arm_gicv3_its.c
> > > > > @@ -0,0 +1,240 @@
> > > > > +/*
> > > > > + * 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 gicv3_its_translation_write(void *opaque,
> > > > > hwaddr offset,
> > > > > +                                               uint64_t
> > > > > data,
> > > > > unsigned size,
> > > > > +                                               MemTxAttrs
> > > > > attrs)
> > > > > +{
> > > > > +    MemTxResult result = MEMTX_OK;
> > > > > +
> > > > > +    return result;
> > > > > +}
> > > > > +
> > > > > +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 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 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,
> > > > > +    .valid.min_access_size = 4,
> > > > > +    .valid.max_access_size = 8,
> > > > > +    .impl.min_access_size = 4,
> > > > > +    .impl.max_access_size = 8,
> > > > > +    .endianness = DEVICE_NATIVE_ENDIAN,
> > > > > +};
> > > > > +
> > > > > +static const MemoryRegionOps gicv3_its_translation_ops = {
> > > > > +    .write_with_attrs = gicv3_its_translation_write,
> > > > > +    .valid.min_access_size = 2,
> > > > > +    .valid.max_access_size = 4,
> > > > > +    .impl.min_access_size = 2,
> > > > > +    .impl.max_access_size = 4,
> > > > > +    .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);
> > > > > +
> > > > > +    if (s->gicv3->cpu->gicr_typer & GICR_TYPER_PLPIS) {
> > > > > +        /* 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 - 1);
> > > > > +        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);
> > > > > +    }
> > > > > +}
> > > > > +
> > > > > +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);
> > > > > +
> > > > > +        /* 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 - 1);
> > > > > +
> > > > > +        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 - 1);
> > > > > +    }
> > > > > +}
> > > > > +
> > > > > +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..f1657c84e0 100644
> > > > > --- a/hw/intc/arm_gicv3_its_common.c
> > > > > +++ b/hw/intc/arm_gicv3_its_common.c
> > > > > @@ -50,6 +50,8 @@ 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,
> > > > > @@ -99,14 +101,15 @@ 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,
> > > > > +                          tops ? tops :
> > > > > &gicv3_its_trans_ops, s,
> > > > >                            "translation", ITS_TRANS_SIZE);
> > > > >  
> > > > >      /* Our two regions are always adjacent, therefore we now
> > > > > combine them
> > > > > @@ -129,7 +132,6 @@ static void
> > > > > gicv3_its_common_reset(DeviceState *dev)
> > > > >      s->cbaser = 0;
> > > > >      s->cwriter = 0;
> > > > >      s->creadr = 0;
> > > > > -    s->iidr = 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..e0b06930a7 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,78 @@
> > > > >  #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)
> > > > Isn't it FIELD(GITS_BASER, PHYADDRH_64K, 12, 4)
> > > > hum actually it is fixed in next patch ;-) The right value can
> > > > be
> > > > put
> > > > here directly
> > > not addressed in v5
> > will do
> > > > > +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)
> > > > > +
> > > > > +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)
> > > > > +
> > > > > +#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 may rename into GITS_BASER_TYPE_DEVICE and COLLECTION?
> > > not addressed in v5
> > will do
> > > > > +
> > > > > +/**
> > > > > + * Default features advertised by this version of ITS
> > > > > + */
> > > > > +/* Physical LPIs supported */
> > > > > +#define GITS_TYPE_PHYSICAL           (1U << 0)
> > > > > +
> > > > > +/*
> > > > > + * 12 bytes Interrupt translation Table Entry size
> > > > > + * ITE Lower 8 Bytes
> > > > > + * Valid = 1 bit,InterruptType = 1 bit,
> > > > > + * Size of LPI number space[considering max 24 bits],
> > > > > + * Size of LPI number space[considering max 24 bits],
> I just meant the above sentence is repeated twice ;-)
This is not repeated twice,but it is as defined in the GIC spec(Table
5-3),both of them refer to the interrupt number,but the 2nd one differs
in value between GICv3(programmed value of 1023) & GICv4(pIntid
doorbell value).I can add further comment to 2nd sentence to clarify.  
> 
> > > > repeated
> > > not adressed in v5
> > I had replied to this comment earlier,
> > The ITE size of 12 bytes format defined here is based on "Table 5-3 
> > ITE
> > entries" in GIC specification and is generic between both GICv3 &
> > GICv4
> > versions for both physical and virtual LPIs,unlike the linux kernel
> > ABI
> > which has current definition only for GICv3 physical LPIs.The idea
> > here
> > was to define the format once(considering future virtual LPIs too).
> > Do you still want me to reduce the ITE size to 8 bytes as in linux
> > kernel ABI (thereby leave the GICV4 fields out)? 
> no, see above
> 
> Thanks
> 
> Eric
> > > > > + * ITE Higher 4 Bytes
> > > > > + * ICID = 16 bits,
> > > > > + * vPEID = 16 bits
> > > > 
> > > > for info the ABI used by the kernel can be found in linux
> > > > Documentation/virt/kvm/devices/arm-vgic-its.rst
> > > > 
> > > > The ITE there is 8 bytes.
> > > > 
> > > > Have you considered the same?
> > > > 
> > > > > + */
> > > > > +#define ITS_ITT_ENTRY_SIZE            0xC
> > > > > +
> > > > > +/* 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 6e52a166e3..4dcfea6aa8 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..65d1191db1 100644
> > > > > --- a/include/hw/intc/arm_gicv3_its_common.h
> > > > > +++ b/include/hw/intc/arm_gicv3_its_common.h
> > > > > @@ -25,17 +25,22 @@
> > > > >  #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
> > > > > +
> > > > >  struct GICv3ITSState {
> > > > >      SysBusDevice parent_obj;
> > > > >  
> > > > > @@ -52,6 +57,7 @@ struct GICv3ITSState {
> > > > >      /* Registers */
> > > > >      uint32_t ctlr;
> > > > >      uint32_t iidr;
> > > > > +    uint64_t typer;
> > > > >      uint64_t cbaser;
> > > > >      uint64_t cwriter;
> > > > >      uint64_t creadr;
> > > > > @@ -62,7 +68,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;
> > > > > 
> > > > Thanks
> > > > 
> > > > Eric
> > > > 
> > > 
> > > Thanks
> > > 
> > > Eric
> > > 



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

* Re: [PATCH v4 7/8] hw/arm/sbsa-ref: add ITS support in SBSA GIC
  2021-06-04 15:36         ` shashi.mallela
@ 2021-07-08 19:40           ` Leif Lindholm
  2021-07-08 20:05             ` Peter Maydell
  0 siblings, 1 reply; 52+ messages in thread
From: Leif Lindholm @ 2021-07-08 19:40 UTC (permalink / raw)
  To: shashi.mallela; +Cc: peter.maydell, rad, qemu-devel, qemu-arm

Hi Shashi,

Apologies for dragging my heels over this. And the couple of GICv4-ish
patches that have been
I finally sat down today and did the cross-referencing between GIC
architecture reference manual and GIC-6/700 TRMs required to verbalise my
concerns usefully.

On Thu, Jul 08, 2021 at 18:54:00 +0100, Leif Lindholm wrote:
> On Fri, Jun 04, 2021 at 11:36:02 -0400, shashi.mallela@linaro.org wrote:
> > On Fri, 2021-06-04 at 11:42 +0100, Leif Lindholm wrote:
> > > On Thu, Jun 03, 2021 at 11:31:21 -0400, shashi.mallela@linaro.org
> > > wrote:
> > > > On Thu, 2021-06-03 at 12:42 +0100, Leif Lindholm wrote:
> > > > > On Wed, Jun 02, 2021 at 14:00:41 -0400, Shashi Mallela 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(-)
> > > > > > 
> > > > > > diff --git a/hw/arm/sbsa-ref.c b/hw/arm/sbsa-ref.c
> > > > > > index 43c19b4923..3d9c073636 100644
> > > > > > --- a/hw/arm/sbsa-ref.c
> > > > > > +++ b/hw/arm/sbsa-ref.c
> > > > > > @@ -107,6 +108,7 @@ static const MemMapEntry sbsa_ref_memmap[]
> > > > > > = {
> > > > > >      [SBSA_CPUPERIPHS] =         { 0x40000000, 0x00040000 },
> > > > > >      [SBSA_GIC_DIST] =           { 0x40060000, 0x00010000 },
> > > > > >      [SBSA_GIC_REDIST] =         { 0x40080000, 0x04000000 },
> > > > > 
> > > > > It seems customary in QEMU to flag gaps in memory space (although
> > > > > admittedly, we'd already failed to do so here). This patch leaves
> > > > > a
> > > > > gap of 0x00010000. Is there a particular reason?
> > > > > 
> > > > > > +    [SBSA_GIC_ITS] =            { 0x44090000, 0x00020000 },
> > > > > 
> > > > > And then again a gap (the one we already had).
> > > > > 
> > > > > No specific reason,but from ITS point of view tried to stay
> > > > > within 
> > > > > the GIC's 0x40060000 to 0x50000000 zone.The gap of 0x00010000
> > > > > would 
> > > > > also account for future GIC additions like virtual LPI support.
> > > 
> > > Right. I was more thinking 64kB isn't much space to extend into.
> > > Would it be worth pushing the ITS either all the way up to just below
> > > 0x50000000, 0x48000000, or 0x45000000?
> >
> > The current memory allocation size (of 67MB) for
> > redistributor(SBSA_GIC_REDIST) is already very large relative to its
> > overall required register address space.
> 
> > Hence ITS started at 0x44090000
> > (considering that redistributor space is sufficiently spaced) until
> > 0x440B0000.Future virtual LPI addition can still stay within the
> > 0x45000000 mark,

So, it turns out the practical problem here isn't the size and
placement of "buffer" regions around the ITS region - but the actual
placement of the ITS region itself, and the buffers around it.

While this is an idealised model of a GIC implementation, there are
benefits to it looking (quite) a bit like an actual ARM
implementation.

And GIC-6/700 place the ITS between the Distributor and
Redistributors. Both GICs support up to 16 ITS *per chip*:
https://developer.arm.com/documentation/100336/0106/operation/interrupt-translation-service--its
and for GIC-600 this takes up (ITScount x 2) + 1 64k pages.
For GIC-700, with GICv4.1 support, it is (ITScount x 4) + 1.

Now, I don't know if we fully want to support layout compatibility
with a "real GIC". But ideally, I would want to. At least for
sbsa-ref.

I think my summary-summary would be:
- I think we will need to introduce a compatiblity-breaking change to
  sbsa-ref.
- I think we will need to have support for more than one ITS if we're
  going to be able to use QEMU to prototype real systems.
- I think we should then start versioning sbsa-ref (like many other
  platforms already are). And there are other reasons why I would want
  to do this.
- But I think it would be unfair to hold this set back for it.

Which can be seen as a long-winded way of saying:
Reviewed-by: Leif Lindholm <leif@nuviainc.com>

/
    Leif

> > Leaving the whole area between 0x45000000 to 0x50000000
> > free for other devices.
> > are comments still recommended here? 
> 
> I would prefer comments on the sizes of gaps.
> 
> > > 
> > > Either way, the gap(s) would be good to point out with comments, and
> > > potential future use. I only noticed this one on like the third pass
> > > of reading.
> > > 
> > > /
> > >     Leif
> > > 
> > > > > >      [SBSA_SECURE_EC] =          { 0x50000000, 0x00001000 },
> > > > > >      [SBSA_GWDT_REFRESH] =       { 0x50010000, 0x00001000 },
> > > > > >      [SBSA_GWDT_CONTROL] =       { 0x50011000, 0x00001000 },
> > > > > > @@ -377,7 +379,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;
> > > > > > @@ -404,6 +419,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);
> > > > > > @@ -450,6 +469,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,
> > > > > > @@ -762,7 +782,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	[flat|nested] 52+ messages in thread

* Re: [PATCH v4 7/8] hw/arm/sbsa-ref: add ITS support in SBSA GIC
  2021-07-08 19:40           ` Leif Lindholm
@ 2021-07-08 20:05             ` Peter Maydell
  2021-07-08 22:05               ` Leif Lindholm
  0 siblings, 1 reply; 52+ messages in thread
From: Peter Maydell @ 2021-07-08 20:05 UTC (permalink / raw)
  To: Leif Lindholm
  Cc: Shashi Mallela, Radoslaw Biernacki, QEMU Developers, qemu-arm

On Thu, 8 Jul 2021 at 20:40, Leif Lindholm <leif@nuviainc.com> wrote:
> I think my summary-summary would be:
> - I think we will need to introduce a compatiblity-breaking change to
>   sbsa-ref.
> - I think we will need to have support for more than one ITS if we're
>   going to be able to use QEMU to prototype real systems.
> - I think we should then start versioning sbsa-ref (like many other
>   platforms already are). And there are other reasons why I would want
>   to do this.
> - But I think it would be unfair to hold this set back for it.

FWIW, I do not currently expect this series to make 6.1, so we
have some time to get things right.

thanks
-- PMM


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

* Re: [PATCH v4 7/8] hw/arm/sbsa-ref: add ITS support in SBSA GIC
  2021-07-08 20:05             ` Peter Maydell
@ 2021-07-08 22:05               ` Leif Lindholm
  2021-08-05 20:10                 ` shashi.mallela
  0 siblings, 1 reply; 52+ messages in thread
From: Leif Lindholm @ 2021-07-08 22:05 UTC (permalink / raw)
  To: Peter Maydell
  Cc: Shashi Mallela, Radoslaw Biernacki, QEMU Developers, qemu-arm

On Thu, Jul 08, 2021 at 21:05:02 +0100, Peter Maydell wrote:
> On Thu, 8 Jul 2021 at 20:40, Leif Lindholm <leif@nuviainc.com> wrote:
> > I think my summary-summary would be:
> > - I think we will need to introduce a compatiblity-breaking change to
> >   sbsa-ref.
> > - I think we will need to have support for more than one ITS if we're
> >   going to be able to use QEMU to prototype real systems.
> > - I think we should then start versioning sbsa-ref (like many other
> >   platforms already are). And there are other reasons why I would want
> >   to do this.
> > - But I think it would be unfair to hold this set back for it.
> 
> FWIW, I do not currently expect this series to make 6.1, so we
> have some time to get things right.

Ah, ok.

Then I would ideally like to see this patch add the ITS block between
Distributor and Redistributors regions. I think it makes the most sense
for this version to match the GIC-600 layout.

I am also going to rework those two remaining gicv4-ish patches based
on my acquired understanding, to be sent out post v6.1.

And, I would like to switch the default CPU of sbsa-ref to "max" as
part of that platform versioning, now that is supported in TF-A
(from v2.5).

/
    Leif


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

* Re: [PATCH v4 7/8] hw/arm/sbsa-ref: add ITS support in SBSA GIC
  2021-07-08 22:05               ` Leif Lindholm
@ 2021-08-05 20:10                 ` shashi.mallela
  0 siblings, 0 replies; 52+ messages in thread
From: shashi.mallela @ 2021-08-05 20:10 UTC (permalink / raw)
  To: Leif Lindholm, Peter Maydell
  Cc: Radoslaw Biernacki, QEMU Developers, qemu-arm

On Thu, 2021-07-08 at 23:05 +0100, Leif Lindholm wrote:
> On Thu, Jul 08, 2021 at 21:05:02 +0100, Peter Maydell wrote:
> > On Thu, 8 Jul 2021 at 20:40, Leif Lindholm <leif@nuviainc.com>
> > wrote:
> > > I think my summary-summary would be:
> > > - I think we will need to introduce a compatiblity-breaking
> > > change to
> > >   sbsa-ref.
> > > - I think we will need to have support for more than one ITS if
> > > we're
> > >   going to be able to use QEMU to prototype real systems.
> > > - I think we should then start versioning sbsa-ref (like many
> > > other
> > >   platforms already are). And there are other reasons why I would
> > > want
> > >   to do this.
> > > - But I think it would be unfair to hold this set back for it.
> > 
> > FWIW, I do not currently expect this series to make 6.1, so we
> > have some time to get things right.
> 
> Ah, ok.
> 
> Then I would ideally like to see this patch add the ITS block between
> Distributor and Redistributors regions. I think it makes the most
> sense
> for this version to match the GIC-600 layout.
Have added ITS block between Distributor and Redistributors
regions,with comments.
Also,added sbsa versioning support (as discussed in last call)
> 
> I am also going to rework those two remaining gicv4-ish patches based
> on my acquired understanding, to be sent out post v6.1.
> 
> And, I would like to switch the default CPU of sbsa-ref to "max" as
> part of that platform versioning, now that is supported in TF-A
> (from v2.5).
> 
> /
>     Leif



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

end of thread, other threads:[~2021-08-05 20:11 UTC | newest]

Thread overview: 52+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-06-02 18:00 [PATCH v4 0/8] GICv3 LPI and ITS feature implementation Shashi Mallela
2021-06-02 18:00 ` [PATCH v4 1/8] hw/intc: GICv3 ITS initial framework Shashi Mallela
2021-06-08 10:02   ` Peter Maydell
2021-06-11 16:21   ` Eric Auger
2021-06-11 17:23     ` Shashi Mallela
2021-07-06  7:38     ` Eric Auger
2021-07-06 13:24       ` shashi.mallela
2021-07-06 14:04         ` Eric Auger
2021-07-06 14:18           ` shashi.mallela
2021-06-12  6:52   ` Eric Auger
2021-07-06  7:29     ` Eric Auger
2021-06-02 18:00 ` [PATCH v4 2/8] hw/intc: GICv3 ITS register definitions added Shashi Mallela
2021-06-08 10:31   ` Peter Maydell
2021-06-12  6:08   ` Eric Auger
2021-06-16 21:02     ` shashi.mallela
2021-06-21  9:51       ` Eric Auger
2021-06-28 21:51         ` shashi.mallela
2021-06-02 18:00 ` [PATCH v4 3/8] hw/intc: GICv3 ITS command queue framework Shashi Mallela
2021-06-08 10:38   ` Peter Maydell
2021-06-13 14:13   ` Eric Auger
2021-06-16 21:02     ` shashi.mallela
2021-06-21 10:03       ` Eric Auger
2021-06-28 21:58         ` shashi.mallela
2021-06-13 14:39   ` Eric Auger
2021-06-28 15:55     ` shashi.mallela
2021-06-02 18:00 ` [PATCH v4 4/8] hw/intc: GICv3 ITS Command processing Shashi Mallela
2021-06-08 10:45   ` Peter Maydell
2021-06-13 15:55   ` Eric Auger
2021-06-16 21:02     ` shashi.mallela
2021-06-21 10:13       ` Eric Auger
2021-06-28 22:04         ` shashi.mallela
2021-06-02 18:00 ` [PATCH v4 5/8] hw/intc: GICv3 ITS Feature enablement Shashi Mallela
2021-06-08 10:57   ` Peter Maydell
2021-06-02 18:00 ` [PATCH v4 6/8] hw/intc: GICv3 redistributor ITS processing Shashi Mallela
2021-06-08 13:57   ` Peter Maydell
2021-06-10 23:39     ` Shashi Mallela
2021-06-11  8:30       ` Peter Maydell
2021-06-15  2:23         ` Shashi Mallela
2021-06-13 16:26   ` Eric Auger
2021-06-16 21:02     ` shashi.mallela
2021-06-02 18:00 ` [PATCH v4 7/8] hw/arm/sbsa-ref: add ITS support in SBSA GIC Shashi Mallela
2021-06-03 11:42   ` Leif Lindholm
2021-06-03 15:31     ` shashi.mallela
2021-06-04 10:42       ` Leif Lindholm
2021-06-04 15:36         ` shashi.mallela
2021-07-08 19:40           ` Leif Lindholm
2021-07-08 20:05             ` Peter Maydell
2021-07-08 22:05               ` Leif Lindholm
2021-08-05 20:10                 ` shashi.mallela
2021-06-02 18:00 ` [PATCH v4 8/8] hw/arm/virt: add ITS support in virt GIC Shashi Mallela
2021-06-08 11:00   ` Peter Maydell
2021-06-08 10:00 ` [PATCH v4 0/8] GICv3 LPI and ITS feature implementation Peter Maydell

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.