diff mbox series

[XEN,v2,14/25] arm: new VGIC: its: Introduce ITS emulation file with MMIO framework

Message ID 86c18cb30b6dbaff9561c2ec1def9f8389f33ebf.1699618395.git.mykyta_poturai@epam.com (mailing list archive)
State New, archived
Headers show
Series arm: Add GICv3 support to the New VGIC | expand

Commit Message

Mykyta Poturai Nov. 10, 2023, 12:56 p.m. UTC
The ARM GICv3 ITS emulation code goes into a separate file, but needs
to be connected to the GICv3 emulation, of which it is an option.
The ITS MMIO handlers require the respective ITS pointer to be passed in,
so we amend the existing VGIC MMIO framework to let it cope with that.
Also we introduce the basic ITS data structure and initialize it, but
don't return any success yet, as we are not yet ready for the show.

Based on Linux commit 59c5ab40989afa by 59c5ab40989afa

Signed-off-by: Mykyta Poturai <mykyta_poturai@epam.com>
---
 xen/arch/arm/include/asm/gic_v3_defs.h |   3 +
 xen/arch/arm/include/asm/gic_v3_its.h  |   1 +
 xen/arch/arm/include/asm/new_vgic.h    |  13 ++
 xen/arch/arm/vgic/vgic-its.c           | 160 +++++++++++++++++++++++++
 xen/arch/arm/vgic/vgic-mmio-v3.c       |  52 +++++++-
 xen/arch/arm/vgic/vgic-mmio.c          |   6 +
 xen/arch/arm/vgic/vgic-mmio.h          |  11 ++
 7 files changed, 245 insertions(+), 1 deletion(-)
 create mode 100644 xen/arch/arm/vgic/vgic-its.c
diff mbox series

Patch

diff --git a/xen/arch/arm/include/asm/gic_v3_defs.h b/xen/arch/arm/include/asm/gic_v3_defs.h
index 1e81687818..3f1f59d1c7 100644
--- a/xen/arch/arm/include/asm/gic_v3_defs.h
+++ b/xen/arch/arm/include/asm/gic_v3_defs.h
@@ -112,6 +112,9 @@ 
 #define GICR_NSACR                   (0x0E00)
 
 #define GICR_CTLR_ENABLE_LPIS        (1U << 0)
+#define GICR_CTLR_CES                (1UL << 1)
+#define GICR_CTLR_IR                 (1UL << 2)
+#define GICR_CTLR_RWP                (1UL << 3)
 
 #define GICR_TYPER_PLPIS             (1U << 0)
 #define GICR_TYPER_VLPIS             (1U << 1)
diff --git a/xen/arch/arm/include/asm/gic_v3_its.h b/xen/arch/arm/include/asm/gic_v3_its.h
index f61a37a8fa..4e857cac1a 100644
--- a/xen/arch/arm/include/asm/gic_v3_its.h
+++ b/xen/arch/arm/include/asm/gic_v3_its.h
@@ -35,6 +35,7 @@ 
 #define GITS_BASER5                     0x128
 #define GITS_BASER6                     0x130
 #define GITS_BASER7                     0x138
+#define GITS_IDREGS_BASE                0xffd0
 #define GITS_PIDR2                      GICR_PIDR2
 
 /* Register bits */
diff --git a/xen/arch/arm/include/asm/new_vgic.h b/xen/arch/arm/include/asm/new_vgic.h
index b037b6cf61..ec2882dcf1 100644
--- a/xen/arch/arm/include/asm/new_vgic.h
+++ b/xen/arch/arm/include/asm/new_vgic.h
@@ -34,6 +34,7 @@ 
 #define VGIC_MIN_LPI            8192
 #define VGIC_V3_DIST_SIZE       SZ_64K
 #define VGIC_V3_REDIST_SIZE     (2 * SZ_64K)
+#define VGIC_V3_ITS_SIZE        (2 * SZ_64K)
 
 #define irq_is_ppi(irq) ((irq) >= VGIC_NR_SGIS && (irq) < VGIC_NR_PRIVATE_IRQS)
 #define irq_is_spi(irq) ((irq) >= VGIC_NR_PRIVATE_IRQS && \
@@ -95,6 +96,7 @@  struct vgic_irq {
 enum iodev_type {
     IODEV_DIST,
     IODEV_REDIST,
+    IODEV_ITS,
 };
 
 struct vgic_redist_region {
@@ -111,6 +113,16 @@  struct vgic_io_device {
     const struct vgic_register_region *regions;
     enum iodev_type iodev_type;
     unsigned int nr_regions;
+    struct vgic_its *its;
+};
+
+struct vgic_its {
+    /* The base address of the ITS control register frame */
+    paddr_t vgic_its_base;
+
+    bool enabled;
+    struct vgic_io_device iodev;
+    paddr_t doorbell_address;
 };
 
 struct vgic_dist {
@@ -150,6 +162,7 @@  struct vgic_dist {
     struct vgic_io_device   dist_iodev;
 
     bool                has_its;
+    struct vgic_its     *its;
 
     /*
      * Contains the attributes and gpa of the LPI configuration table.
diff --git a/xen/arch/arm/vgic/vgic-its.c b/xen/arch/arm/vgic/vgic-its.c
new file mode 100644
index 0000000000..0ae6048456
--- /dev/null
+++ b/xen/arch/arm/vgic/vgic-its.c
@@ -0,0 +1,160 @@ 
+/*
+ * Imported from Linux ("new" KVM VGIC) and heavily adapted to Xen.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <xen/sched.h>
+#include <xen/guest_access.h>
+#include <xen/sizes.h>
+#include <xen/err.h>
+#include <xen/list_sort.h>
+#include <asm/page.h>
+#include <asm/new_vgic.h>
+#include <asm/gic_v3_defs.h>
+#include <asm/gic_v3_its.h>
+
+#include "vgic.h"
+#include "vgic-mmio.h"
+
+static unsigned long its_mmio_read_raz(struct domain *d, struct vgic_its *its,
+                              paddr_t addr, unsigned int len)
+{
+    return 0;
+}
+
+static void its_mmio_write_wi(struct domain *d, struct vgic_its *its,
+                              paddr_t addr, unsigned int len, unsigned long val)
+{
+    /* Ignore */
+}
+
+#define REGISTER_ITS_DESC(off, rd, wr, length, acc)                            \
+    {                                                                          \
+        .reg_offset = off, .len = length, .access_flags = acc, .its_read = rd, \
+        .its_write = wr,                                                       \
+    }
+
+static struct vgic_register_region its_registers[] = {
+    REGISTER_ITS_DESC(GITS_CTLR,
+                        its_mmio_read_raz, its_mmio_write_wi, 4,
+                        VGIC_ACCESS_32bit),
+    REGISTER_ITS_DESC(GITS_IIDR,
+                        its_mmio_read_raz, its_mmio_write_wi, 4,
+                        VGIC_ACCESS_32bit),
+    REGISTER_ITS_DESC(GITS_TYPER,
+                        its_mmio_read_raz, its_mmio_write_wi, 8,
+                        VGIC_ACCESS_64bit | VGIC_ACCESS_32bit),
+    REGISTER_ITS_DESC(GITS_CBASER,
+                        its_mmio_read_raz, its_mmio_write_wi, 8,
+                        VGIC_ACCESS_64bit | VGIC_ACCESS_32bit),
+    REGISTER_ITS_DESC(GITS_CWRITER, 
+                        its_mmio_read_raz, its_mmio_write_wi, 8,
+                        VGIC_ACCESS_64bit | VGIC_ACCESS_32bit),
+    REGISTER_ITS_DESC(GITS_CREADR,
+                        its_mmio_read_raz, its_mmio_write_wi, 8,
+                        VGIC_ACCESS_64bit | VGIC_ACCESS_32bit),
+    REGISTER_ITS_DESC(GITS_BASER0,
+                        its_mmio_read_raz, its_mmio_write_wi, 0x40,
+                        VGIC_ACCESS_64bit | VGIC_ACCESS_32bit),
+    REGISTER_ITS_DESC(GITS_IDREGS_BASE,
+                        its_mmio_read_raz, its_mmio_write_wi, 0x30,
+                        VGIC_ACCESS_32bit),
+};
+
+static int vgic_register_its_iodev(struct domain *d, struct vgic_its *its,
+                                   u64 addr)
+{
+    struct vgic_io_device *iodev = &its->iodev;
+    int ret                      = 0;
+
+    if ( !IS_VGIC_ADDR_UNDEF(its->vgic_its_base) )
+    {
+        ret = -EBUSY;
+        goto out;
+    }
+
+    its->vgic_its_base    = addr;
+    its->doorbell_address = addr + ITS_DOORBELL_OFFSET;
+    iodev->regions        = its_registers;
+    iodev->nr_regions     = ARRAY_SIZE(its_registers);
+
+    iodev->base_fn        = gaddr_to_gfn(its->vgic_its_base);
+    iodev->iodev_type     = IODEV_ITS;
+    iodev->its            = its;
+    register_mmio_handler(d, &vgic_io_ops, its->vgic_its_base, VGIC_V3_ITS_SIZE,
+                          iodev);
+out:
+    return ret;
+}
+
+static int vgic_its_create(struct domain *d, u64 addr)
+{
+    struct vgic_its *its;
+
+    its = xzalloc(struct vgic_its);
+    if ( !its )
+        return -ENOMEM;
+
+    d->arch.vgic.its = its;
+
+    its->vgic_its_base = VGIC_ADDR_UNDEF;
+
+    d->arch.vgic.msis_require_devid = true;
+    d->arch.vgic.has_its            = true;
+    its->enabled                    = false;
+
+    vgic_register_its_iodev(d, its, addr);
+
+    its->doorbell_address = addr + ITS_DOORBELL_OFFSET;
+
+    return 0;
+}
+
+/*
+ * For a hardware domain, this will iterate over the host ITSes
+ * and map one virtual ITS per host ITS at the same address.
+ */
+int vgic_v3_its_init_domain(struct domain *d)
+{
+    int ret;
+
+    if ( is_hardware_domain(d) )
+    {
+        struct host_its *hw_its;
+
+        list_for_each_entry(hw_its, &host_its_list, entry)
+        {
+            /*
+             * For each host ITS create a virtual ITS using the same
+             * base and thus doorbell address.
+             * Use the same number of device ID and event ID bits as the host.
+             */
+            ret = vgic_its_create(d, hw_its->addr);
+            if ( ret )
+                return ret;
+            else
+                d->arch.vgic.has_its = true;
+        }
+    }
+
+    return 0;
+}
+
+void vgic_v3_its_free_domain(struct domain *d)
+{
+    struct vgic_its *its = d->arch.vgic.its;
+
+    xfree(its);
+    d->arch.vgic.its = NULL;
+}
diff --git a/xen/arch/arm/vgic/vgic-mmio-v3.c b/xen/arch/arm/vgic/vgic-mmio-v3.c
index 2fb44cfe6a..4bf8c21203 100644
--- a/xen/arch/arm/vgic/vgic-mmio-v3.c
+++ b/xen/arch/arm/vgic/vgic-mmio-v3.c
@@ -22,6 +22,16 @@ 
 #include "vgic.h"
 #include "vgic-mmio.h"
 
+bool vgic_has_its(struct domain *d)
+{
+    struct vgic_dist *dist = &d->arch.vgic;
+
+    if ( dist->version != GIC_V3 )
+        return false;
+
+    return false;
+}
+
 static struct vcpu *mpidr_to_vcpu(struct domain *d, unsigned long mpidr)
 {
     struct vcpu *vcpu;
@@ -381,6 +391,46 @@  static unsigned long vgic_mmio_read_v3_idregs(struct vcpu *vcpu, paddr_t addr,
     return 0;
 }
 
+static unsigned long vgic_mmio_read_v3r_ctlr(struct vcpu *vcpu, paddr_t addr,
+                                             unsigned int len)
+{
+    struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic;
+    unsigned long val;
+
+    val = atomic_read(&vgic_cpu->ctlr);
+    val |= GICR_CTLR_IR | GICR_CTLR_CES;
+
+    return val;
+}
+
+static void vgic_mmio_write_v3r_ctlr(struct vcpu *vcpu, paddr_t addr,
+                                     unsigned int len, unsigned long val)
+{
+    struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic;
+    uint32_t ctlr;
+
+    if ( !vgic_has_its(vcpu->domain) )
+        return;
+
+    if ( !(val & GICR_CTLR_ENABLE_LPIS) )
+    {
+        /*
+		 * Don't disable if RWP is set, as there already an
+		 * ongoing disable. Funky guest...
+		 */
+        ctlr = atomic_cmpxchg(&vgic_cpu->ctlr, GICR_CTLR_ENABLE_LPIS,
+                              GICR_CTLR_RWP);
+        if ( ctlr != GICR_CTLR_ENABLE_LPIS )
+            return;
+    }
+    else
+    {
+        ctlr = atomic_cmpxchg(&vgic_cpu->ctlr, 0, GICR_CTLR_ENABLE_LPIS);
+        if ( ctlr != 0 )
+            return;
+    }
+}
+
 bool vgic_lpis_enabled(struct vcpu *vcpu)
 {
     struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic;
@@ -584,7 +634,7 @@  static const struct vgic_register_region vgic_v3_dist_registers[] = {
 static const struct vgic_register_region vgic_v3_rd_registers[] = {
     /* RD_base registers */
     REGISTER_DESC_WITH_LENGTH(GICR_CTLR,
-        vgic_mmio_read_raz, vgic_mmio_write_wi, 4,
+        vgic_mmio_read_v3r_ctlr, vgic_mmio_write_v3r_ctlr, 4,
         VGIC_ACCESS_32bit),
     REGISTER_DESC_WITH_LENGTH(GICR_STATUSR,
         vgic_mmio_read_raz, vgic_mmio_write_wi, 4,
diff --git a/xen/arch/arm/vgic/vgic-mmio.c b/xen/arch/arm/vgic/vgic-mmio.c
index 1c3f861887..7a28be53bc 100644
--- a/xen/arch/arm/vgic/vgic-mmio.c
+++ b/xen/arch/arm/vgic/vgic-mmio.c
@@ -570,6 +570,9 @@  static int dispatch_mmio_read(struct vcpu *vcpu, mmio_info_t *info,
     case IODEV_REDIST:
         data = region->read(iodev->redist_vcpu, addr, len);
         break;
+    case IODEV_ITS:
+        data = region->its_read(vcpu->domain, iodev->its, addr, len);;
+        break;
     }
 
     memcpy(r, &data, len);
@@ -598,6 +601,9 @@  static int dispatch_mmio_write(struct vcpu *vcpu, mmio_info_t *info,
     case IODEV_REDIST:
         region->write(iodev->redist_vcpu, addr, len, data);
         break;
+    case IODEV_ITS:
+        region->its_write(vcpu->domain, iodev->its, addr, len, data);
+        break;
     }
 
     return 1;
diff --git a/xen/arch/arm/vgic/vgic-mmio.h b/xen/arch/arm/vgic/vgic-mmio.h
index 3566cf237c..0a8deb46ba 100644
--- a/xen/arch/arm/vgic/vgic-mmio.h
+++ b/xen/arch/arm/vgic/vgic-mmio.h
@@ -21,10 +21,21 @@  struct vgic_register_region {
     unsigned int len;
     unsigned int bits_per_irq;
     unsigned int access_flags;
+
+    union {
     unsigned long (*read)(struct vcpu *vcpu, paddr_t addr,
                           unsigned int len);
+    unsigned long (*its_read)(struct domain *d, struct vgic_its *its,
+                    paddr_t addr, unsigned int len);
+    };
+
+    union {
     void (*write)(struct vcpu *vcpu, paddr_t addr,
                   unsigned int len, unsigned long val);
+    void (*its_write)(struct domain *d, struct vgic_its *its,
+                paddr_t addr, unsigned int len,
+                unsigned long val);
+    };
 };
 
 extern struct mmio_handler_ops vgic_io_ops;