[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Xen-devel] [RFC PATCH 10/19] xen/arm: its: Add virtual ITS command support



From: Vijaya Kumar K <Vijaya.Kumar@xxxxxxxxxxxxxxxxxx>

Add Virtual ITS command processing support to
Virtual ITS driver. Also add API's to in physical
ITS driver to send commands from Virtual ITS driver.

For now, this driver can handle one physical ITS node.
If platform has more than one physical ITS node, it
need changes at the PCI device level to map pci device
to physical ITS node.

Signed-off-by: Vijaya Kumar K <Vijaya.Kumar@xxxxxxxxxxxxxxxxxx>
---
 xen/arch/arm/gic-v3-its.c     |  100 ++++-
 xen/arch/arm/vgic-v3-its.c    |  839 +++++++++++++++++++++++++++++++++++++++++
 xen/include/asm-arm/domain.h  |    2 +
 xen/include/asm-arm/gic-its.h |   76 ++++
 4 files changed, 1016 insertions(+), 1 deletion(-)

diff --git a/xen/arch/arm/gic-v3-its.c b/xen/arch/arm/gic-v3-its.c
index b1c599f..68bb7ba 100644
--- a/xen/arch/arm/gic-v3-its.c
+++ b/xen/arch/arm/gic-v3-its.c
@@ -41,6 +41,14 @@
 #include <asm/gic_v3_defs.h>
 #include <asm/gic-its.h>
 
+//#define DEBUG_GIC_ITS
+
+#ifdef DEBUG_GIC_ITS
+# define DPRINTK(fmt, args...) printk(XENLOG_DEBUG fmt, ##args)
+#else
+# define DPRINTK(fmt, args...) do {} while ( 0 )
+#endif
+
 #define its_print(lvl, fmt, ...)                                        \
     printk(lvl "GIC-ITS:" fmt, ## __VA_ARGS__)
 
@@ -59,6 +67,8 @@
 
 #define RDIST_FLAGS_PROPBASE_NEEDS_FLUSHING    (1 << 0)
 
+struct its_node *its;
+
 /*
  * Collection structure - just an ID, and a redistributor address to
  * ping. We use one per CPU as a bag of interrupts assigned to this
@@ -67,6 +77,7 @@
 struct its_collection {
     u64 target_address;
     u16 col_id;
+    u16 valid;
 };
 
 /*
@@ -79,12 +90,14 @@ struct its_node {
     struct                list_head entry;
     void __iomem          *base;
     unsigned long         phys_base;
+    unsigned long         phys_size;
     struct its_cmd_block  *cmd_base;
     struct its_cmd_block  *cmd_write;
     void                  *tables[GITS_BASER_NR_REGS];
     struct its_collection *collections;
     u64                   flags;
     u32                   ite_size;
+    u32                   nr_collections;
 };
 
 #define ITS_ITT_ALIGN        SZ_256
@@ -163,6 +176,68 @@ static struct its_collection *its_build_invall_cmd(struct 
its_cmd_block *cmd,
     return NULL;
 }
 
+int its_check_target(uint64_t vta)
+{
+    int i;
+    struct its_collection *col;
+
+    for ( i = 0; i < its->nr_collections; i++ )
+    {
+        col = &its->collections[i];
+        if ( col->valid && col->target_address == vta )
+        {
+             DPRINTK("ITS: Found valid pta entry 0x%lx for vta 0x%lx\n",
+                     col->target_address, vta);
+             return 0;
+        }
+    }
+
+    DPRINTK("ITS: Cannot find valid pta entry for vta 0x%lx\n", vta);
+    return 1;
+}
+
+int its_get_target(uint8_t pcid, uint64_t *pta)
+{
+    int i;
+    struct its_collection *col;
+
+    for ( i = 0; i < its->nr_collections; i++ )
+    {
+        col = &its->collections[i];
+        if ( col->valid && col->col_id == pcid )
+        {
+             *pta = col->target_address;
+             DPRINTK("ITS: Found valid pta entry 0x%lx for vta 0x%lx\n",
+                     col->target_address, vta);
+             return 0;
+        }
+    }
+
+    DPRINTK("ITS: Cannot find valid pta entry for vta 0x%lx\n", vta);
+    return 1;
+}
+
+int its_get_physical_cid(uint32_t *col_id, uint64_t vta)
+{
+    int i;
+    struct its_collection *col;
+
+    for ( i = 0; i < its->nr_collections; i++ )
+    {
+        col = &its->collections[i];
+        if ( col->valid && col->target_address == vta )
+        {
+            *col_id = col->col_id;
+             DPRINTK("ITS: Found pta 0x%lx vta 0x%lx pcol_id %d\n",
+                     col->target_address, vta, col->col_id);
+             return 0;
+        }
+    }
+
+    DPRINTK("ITS: Cannot find pta entry for vta 0x%lx\n", vta);
+    return 1;
+}
+
 static u64 its_cmd_ptr_to_offset(struct its_node *its,
                                  struct its_cmd_block *ptr)
 {
@@ -513,6 +588,29 @@ static int its_alloc_collections(struct its_node *its)
     return 0;
 }
 
+int gic_its_send_cmd(struct vcpu *v, struct its_cmd_block *phys_cmd)
+{
+    struct its_cmd_block *cmd, *next_cmd;
+
+    spin_lock(&its->lock);
+
+    cmd = its_allocate_entry(its);
+    if ( !cmd )
+        return 0;        /* We're soooooo screewed... */
+
+    cmd->raw_cmd[0] = phys_cmd->raw_cmd[0];
+    cmd->raw_cmd[1] = phys_cmd->raw_cmd[1];
+    cmd->raw_cmd[2] = phys_cmd->raw_cmd[2];
+    cmd->raw_cmd[3] = phys_cmd->raw_cmd[3];
+    its_flush_cmd(its, cmd);
+
+    next_cmd = its_post_commands(its);
+    spin_unlock(&its->lock);
+
+    its_wait_for_range_completion(its, cmd, next_cmd);
+    return 1;
+}
+
 static void its_cpu_init_lpis(void)
 {
     void __iomem *rbase = gic_data_rdist_rd_base();
@@ -625,7 +723,6 @@ static void its_cpu_init_collection(void)
 static int its_probe(struct dt_device_node *node)
 {
     paddr_t its_addr, its_size;
-    struct its_node *its;
     void __iomem *its_base;
     u32 val;
     u64 baser, tmp;
@@ -664,6 +761,7 @@ static int its_probe(struct dt_device_node *node)
     INIT_LIST_HEAD(&its->entry);
     its->base = its_base;
     its->phys_base = its_addr;
+    its->phys_size = its_size;
     its->ite_size = ((readl_relaxed(its_base + GITS_TYPER) >> 4) & 0xf) + 1;
 
     its->cmd_base = xzalloc_bytes(ITS_CMD_QUEUE_SZ);
diff --git a/xen/arch/arm/vgic-v3-its.c b/xen/arch/arm/vgic-v3-its.c
index 08e9787..4babb2a 100644
--- a/xen/arch/arm/vgic-v3-its.c
+++ b/xen/arch/arm/vgic-v3-its.c
@@ -38,6 +38,22 @@
 #include <asm/vgic.h>
 #include <asm/gic-its.h>
 
+/* GITS register definitions */
+#define VITS_GITS_TYPER_HCC       (0xffU << 24)
+#define VITS_GITS_TYPER_PTA_SHIFT (19)
+#define VITS_GITS_DEV_BITS        (0x14U << 13)
+#define VITS_GITS_ID_BITS         (0x13U << 8)
+#define VITS_GITS_ITT_SIZE        (0x7U << 4)
+#define VITS_GITS_DISTRIBUTED     (0x1U << 3)
+#define VITS_GITS_PLPIS           (0x1U << 0)
+
+/* GITS_PIDRn register values for ARM implementations */
+#define GITS_PIDR0_VAL            (0x94)
+#define GITS_PIDR1_VAL            (0xb4)
+#define GITS_PIDR2_VAL            (0x3b)
+#define GITS_PIDR3_VAL            (0x00)
+#define GITS_PIDR4_VAL            (0x04)
+
 //#define DEBUG_ITS
 
 #ifdef DEBUG_ITS
@@ -157,6 +173,829 @@ void its_lpi_free(struct its_lpi_chunk *chunk)
     xfree(chunk);
 }
 
+void vgic_its_disable_lpis(struct vcpu *v, uint32_t lpi)
+{
+    struct pending_irq *p;
+    unsigned long flags;
+
+    p = irq_to_pending(v, lpi);
+    clear_bit(GIC_IRQ_GUEST_ENABLED, &p->status);
+    gic_remove_from_queues(v, lpi);
+    if ( p->desc != NULL )
+    {
+        spin_lock_irqsave(&p->desc->lock, flags);
+        p->desc->handler->disable(p->desc);
+        spin_unlock_irqrestore(&p->desc->lock, flags);
+    }
+}
+
+void vgic_its_enable_lpis(struct vcpu *v, uint32_t lpi)
+{
+    struct pending_irq *p;
+    unsigned long flags;
+
+    p = irq_to_pending(v, lpi);
+    set_bit(GIC_IRQ_GUEST_ENABLED, &p->status);
+
+    spin_lock_irqsave(&v->arch.vgic.lock, flags);
+
+    if ( !list_empty(&p->inflight) &&
+         !test_bit(GIC_IRQ_GUEST_VISIBLE, &p->status) )
+        gic_raise_guest_irq(v, p->desc->virq, p->priority);
+
+    spin_unlock_irqrestore(&v->arch.vgic.lock, flags);
+    if ( p->desc != NULL )
+    {
+        spin_lock_irqsave(&p->desc->lock, flags);
+        p->desc->handler->enable(p->desc);
+        spin_unlock_irqrestore(&p->desc->lock, flags);
+    }
+}
+
+static int its_alloc_device_irq(struct vits_device *dev, uint32_t id,
+                                uint32_t *plpi, uint32_t vlpi, uint32_t 
vcol_id)
+{
+    struct its_lpi_chunk *chunk;
+    int idx, i;
+
+    /* Check device irq list before allocating new lpi chunk */
+    list_for_each_entry( chunk, &dev->hwirq_list, entry )
+    {
+        /* Check if device has entry for this vlpi. If so return the old plpi 
*/
+        for ( i = 0; i < IRQ_PER_CHUNK; i++ )
+        {
+            if ( chunk->vid[i] == vlpi && chunk->vcol_id[i] == vcol_id )
+            {
+                 *plpi = chunk->pid[i];
+                 DPRINTK("Found plpi %d for device 0x%x with vlpi %d id %d\n",
+                          *plpi, dev->dev_id, vlpi, chunk->id[i]);
+                 return 0;
+            }
+        }
+    }
+
+    chunk = list_first_entry_or_null(&dev->hwirq_list,
+                                     struct its_lpi_chunk, entry);
+again:
+    if ( !chunk )
+    {   /* No allocated chunk */
+        chunk = its_lpi_alloc();
+        if (!chunk)
+            return -ENOSPC;
+        list_add(&chunk->entry, &dev->hwirq_list);
+        DPRINTK("vITS: New LPI chunk added to device 0x%x\n", dev->dev_id);
+    }
+
+    idx = find_first_zero_bit(&chunk->lpi_map, IRQS_PER_CHUNK);
+    if ( idx == IRQS_PER_CHUNK )
+    { /* Need to allocate a new chunk */
+        chunk = NULL;
+        goto again;
+    }
+
+    set_bit(idx, &chunk->lpi_map);
+
+    DPRINTK("vITS: LPI chunk base %d chunk map 0x%lx for idx %d added to 
device 0x%x\n",
+             chunk->lpi_base, chunk->lpi_map, idx, dev->dev_id);
+    /* hwirq is free plpi allocated */
+    *plpi = chunk->lpi_base + idx;
+
+    chunk->pid[idx] = *plpi;
+    chunk->vid[idx] = vlpi;
+    chunk->id[idx]  = id;
+    chunk->vcol_id[idx] = vcol_id;
+
+    DPRINTK("Allocated plpi %d for device 0x%x with vlpi %d id %d\n",
+            *plpi, dev->dev_id, vlpi, id);
+
+    return 0;
+}
+
+/* Should be called with its lock held */
+static void vgic_its_unmap_id(struct vcpu *v, struct vits_device *dev,
+                              uint32_t id, int trash)
+{
+    struct its_lpi_chunk *chunk;
+    int i;
+
+    DPRINTK("vITS: unmap id for device 0x%x id %d trash %d\n",
+             dev->dev_id, id, trash);
+    list_for_each_entry( chunk, &dev->hwirq_list, entry )
+    {
+        for ( i = 0; i < IRQS_PER_CHUNK; i++ )
+        {
+            /* If trash is set. ignore vid check */
+            if ( (chunk->id[i] == id || trash)
+                 && test_bit(i, &chunk->lpi_map) )
+            {
+                DPRINTK("vITS: un mapped id for device 0x%x id %d lpi %d\n",
+                        dev->dev_id, id, chunk->pid[i]);
+                vgic_its_disable_lpis(v, chunk->pid[i]);
+                release_irq(chunk->pid[i], v->domain);
+                chunk->pid[i] = 0;
+                chunk->id[i] = 0;
+                chunk->vcol_id[i] = 0;
+                clear_bit(i, &chunk->lpi_map);
+                goto out;
+            }
+        }
+    }
+
+    dprintk(XENLOG_ERR, "vITS: id %d not found for device 0x%x to unmap\n",
+           id, dev->dev_id);
+    return;
+out:
+    if ( bitmap_empty(&chunk->lpi_map, IRQS_PER_CHUNK) )
+    {
+        list_del(&chunk->entry);
+        DPRINTK("vITS: Freeing lpi chunk\n");
+        its_lpi_free(chunk);
+    }
+    /* XXX: Device entry is not removed on empty lpi list */
+}
+
+static int vgic_its_check_device_id(struct vits_device *dev, uint32_t id)
+{
+    struct its_lpi_chunk *chunk;
+    int i;
+
+    list_for_each_entry( chunk, &dev->hwirq_list, entry )
+    {
+        for ( i = 0; i < IRQS_PER_CHUNK; i++ )
+        {
+            if ( test_bit(i, &chunk->lpi_map) && chunk->id[i] == id )
+                return 0;
+        }
+    }
+
+    return 1;
+}
+
+static int vgic_its_check_vid_entry(struct vcpu *v, uint32_t vid)
+{
+    struct domain *d = v->domain;
+    struct vits_device *dev;
+    struct its_lpi_chunk *chunk;
+    int i;
+
+    list_for_each_entry(dev, &d->arch.vits->vits_dev_list, entry)
+    {
+        list_for_each_entry( chunk, &dev->hwirq_list, entry )
+        {
+            for ( i = 0; i < IRQS_PER_CHUNK; i++ )
+            {
+                if ( chunk->vid[i] == vid )
+                {
+                    DPRINTK("vITS: Found vid entry %d for device 0x%x\n",
+                            vid, dev->dev_id);
+                    return 0;
+                }
+            }
+        }
+    }
+    DPRINTK("vITS: Not found vid entry %d for any device\n", vid);
+
+    return 1;
+}
+
+static struct vits_device * vgic_its_check_device(struct vcpu *v, int dev_id)
+{
+    struct domain *d = v->domain;
+    struct vits_device *dev = NULL, *tmp;
+
+    list_for_each_entry(tmp, &d->arch.vits->vits_dev_list, entry)
+    {
+        if ( tmp->dev_id == dev_id )
+        {
+            DPRINTK("vITS: Found device 0x%x\n", dev_id);
+            dev = tmp;
+            break;
+        }
+    }
+
+    return dev;
+}
+
+static int vgic_its_check_cid(struct vcpu *v, uint8_t vcid, uint32_t *pcid)
+{
+    struct domain *d = v->domain;
+    uint32_t nmap = d->arch.vits->cid_map.nr_cid;
+    int i;
+
+    for ( i = 0; i < nmap; i++ )
+    {
+        if ( vcid == d->arch.vits->cid_map.vcid[i] )
+        {
+            *pcid = d->arch.vits->cid_map.pcid[i];
+            DPRINTK("vITS: Found vcid %d for vcid %d\n", *pcid,
+                     d->arch.vits->cid_map.vcid[i]);
+            return 0;
+        }
+    }
+
+    return 1;
+}
+
+static uint64_t vgic_its_get_pta(struct vcpu *v, uint64_t vta)
+{
+
+    struct domain *d = v->domain;
+    uint32_t nmap = d->arch.vits->cid_map.nr_cid;
+    int i;
+    uint8_t pcid;
+    uint64_t pta;
+
+    for ( i = 0; i < nmap; i++ )
+    {
+        if ( vta == d->arch.vits->cid_map.vta[i] )
+        {
+            pcid = d->arch.vits->cid_map.pcid[i];
+            DPRINTK("vITS: Found vcid %d for vta 0x%lx\n", pcid,
+                     d->arch.vits->cid_map.vta[i]);
+            if ( its_get_target(pcid, &pta) )
+                BUG_ON(1);
+            return pta;
+        }
+    }
+
+    BUG_ON(1);
+    return 1;
+}
+
+static int vgic_its_build_mapd_cmd(struct vcpu *v,
+                                    struct its_cmd_block *virt_cmd,
+                                    struct its_cmd_block *phys_cmd)
+{
+    unsigned long itt_addr;
+
+    itt_addr = its_decode_itt(virt_cmd);
+    /* Get ITT PA from ITT IPA */
+    itt_addr = p2m_lookup(v->domain, itt_addr, NULL);
+    its_encode_cmd(phys_cmd, GITS_CMD_MAPD);
+    its_encode_devid(phys_cmd, its_decode_devid(v->domain, virt_cmd));
+    its_encode_size(phys_cmd, its_decode_size(virt_cmd));
+    its_encode_itt(phys_cmd, itt_addr);
+    its_encode_valid(phys_cmd, its_decode_valid(virt_cmd));
+
+    DPRINTK("vITS: Build MAPD with itt_addr 0x%lx devId %d\n",itt_addr,
+            its_decode_devid(v->domain, virt_cmd));
+
+    return 0;
+}
+
+static int vgic_its_build_sync_cmd(struct vcpu *v,
+                                   struct its_cmd_block *virt_cmd,
+                                   struct its_cmd_block *phys_cmd)
+{
+    uint64_t pta;
+
+    its_encode_cmd(phys_cmd, GITS_CMD_SYNC);
+
+    if ( is_hardware_domain(current->domain) )
+    {
+        if ( its_check_target(its_decode_target(virt_cmd)) )
+        {
+            dprintk(XENLOG_ERR, "vITS: SYNC: Wrong Target Address\n");
+            return 1;
+        }
+        its_encode_target(phys_cmd, its_decode_target(virt_cmd));
+    }
+    else
+    {
+        pta = vgic_its_get_pta(v, its_decode_target(virt_cmd));
+        its_encode_target(phys_cmd, pta);
+    }
+
+    return 0;
+}
+static int vgic_its_build_mapvi_cmd(struct vcpu *v,
+                                    struct its_cmd_block *virt_cmd,
+                                    struct its_cmd_block *phys_cmd)
+{
+    struct domain *d = v->domain;
+    struct vits_device *dev;
+    uint32_t pcol_id;
+    uint32_t pid;
+    struct irq_desc *desc;
+    uint32_t dev_id = its_decode_devid(v->domain,virt_cmd);
+    uint32_t id = its_decode_event_id(virt_cmd);
+    uint8_t vcol_id = its_decode_collection(virt_cmd);
+    uint32_t vid = its_decode_phys_id(virt_cmd);
+    uint8_t cmd = its_decode_cmd(virt_cmd);
+
+    DPRINTK("vITS: MAPVI: dev_id 0x%x vcol_id %d vid %d \n",
+             dev_id, vcol_id, vid);
+
+    /* Search if device entry exists */
+    dev = vgic_its_check_device(v, dev_id);
+    if ( dev == NULL )
+    {
+        dprintk(XENLOG_ERR, "vITS: MAPVI: Fail to find device 0x%x\n", dev_id);
+        return 1;
+    }
+
+    /* Check if Collection id exists */
+    if ( vgic_its_check_cid(v, vcol_id, &pcol_id) )
+    {
+        dprintk(XENLOG_ERR, "vITS: MAPVI: with wrong Collection %d\n", 
vcol_id);
+        return 1;
+    }
+
+    /* Check if PID is exists for this VID by any device on this domain */
+    if ( !vgic_its_check_vid_entry(v, vid) )
+    {
+        dprintk(XENLOG_ERR, "vITS: MAPVI: with wrong vID %d\n", vid);
+        return 1;
+    }
+    if ( its_alloc_device_irq(dev, id, &pid, vid, vcol_id) )
+    {
+        dprintk(XENLOG_ERR, "vITS: MAPVI: Failed to alloc irq\n");
+        return 1;
+    }
+
+    route_irq_to_guest(d, pid, "LPI");
+
+    desc = irq_to_desc(pid);
+
+     /* Assign device structure to desc data */
+    desc->data = dev;
+    desc->virq = vid;
+
+    its_encode_cmd(phys_cmd, GITS_CMD_MAPVI);
+    its_encode_devid(phys_cmd, dev_id);
+
+    if ( cmd == GITS_CMD_MAPI )
+        its_encode_id(phys_cmd, vid);
+    else
+        its_encode_id(phys_cmd, its_decode_event_id(virt_cmd));
+
+    its_encode_phys_id(phys_cmd, pid);
+    its_encode_collection(phys_cmd, pcol_id);
+
+    return 0;
+}
+
+static int vgic_its_build_movi_cmd(struct vcpu *v,
+                                   struct its_cmd_block *virt_cmd,
+                                   struct its_cmd_block *phys_cmd)
+{
+    uint32_t pcol_id;
+    struct vits_device *dev;
+    uint32_t dev_id = its_decode_devid(v->domain, virt_cmd);
+    uint8_t vcol_id = its_decode_collection(virt_cmd);
+    uint32_t id = its_decode_event_id(virt_cmd);
+
+    DPRINTK("vITS: MOVI: dev_id 0x%x vcol_id %d\n", dev_id, vcol_id);
+    /* Search if device entry exists */
+    dev = vgic_its_check_device(v, dev_id);
+    if ( dev == NULL )
+    {
+        dprintk(XENLOG_ERR, "vITS: MOVI: Failed to find device 0x%x\n", 
dev_id);
+        return 1;
+    }
+
+    /* Check if Collection id exists */
+    if ( vgic_its_check_cid(v, vcol_id, &pcol_id) )
+    {
+        dprintk(XENLOG_ERR, "vITS: MOVI: with wrong Collection %d\n", vcol_id);
+        return 1;
+    }
+
+    if ( vgic_its_check_device_id(dev, id) )
+    {
+        dprintk(XENLOG_ERR, "vITS: MOVI: Invalid ID %d\n", id);
+        return 1;
+    }
+
+    its_encode_cmd(phys_cmd, GITS_CMD_MOVI);
+    its_encode_devid(phys_cmd, dev_id);
+    its_encode_id(phys_cmd, id);
+    its_encode_collection(phys_cmd, pcol_id);
+
+    return 0;
+}
+
+
+static int vgic_its_build_discard_cmd(struct vcpu *v,
+                                      struct its_cmd_block *virt_cmd,
+                                      struct its_cmd_block *phys_cmd)
+{
+    struct vits_device *dev;
+    uint32_t id = its_decode_event_id(virt_cmd);
+    uint32_t dev_id = its_decode_devid(v->domain, virt_cmd);
+
+    DPRINTK("vITS: DISCARD: dev_id 0x%x id %d\n", dev_id, id);
+    /* Search if device entry exists */
+    dev = vgic_its_check_device(v, dev_id);
+    if ( dev == NULL )
+    {
+        dprintk(XENLOG_ERR, "vITS: DISCARD: Failed to find device 0x%x\n",
+                dev_id);
+        return 1;
+    }
+
+    if ( vgic_its_check_device_id(dev, id) )
+    {
+        dprintk(XENLOG_ERR, "vITS: DISCARD: Invalid vID %d\n", id);
+        return 1;
+    }
+
+    /* Check if PID is exists for this VID for this device and unmap it */
+    vgic_its_unmap_id(v, dev, id, 0);
+
+    /* Fetch and encode cmd */
+    its_encode_cmd(phys_cmd, GITS_CMD_DISCARD);
+    its_encode_devid(phys_cmd, its_decode_devid(v->domain, virt_cmd));
+    its_encode_id(phys_cmd, its_decode_event_id(virt_cmd));
+
+    return 0;
+}
+
+static int vgic_its_build_inv_cmd(struct vcpu *v,
+                                  struct its_cmd_block *virt_cmd,
+                                  struct its_cmd_block *phys_cmd)
+{
+    struct vits_device *dev;
+    uint32_t dev_id = its_decode_devid(v->domain, virt_cmd);
+    uint32_t id = its_decode_event_id(virt_cmd);
+
+    DPRINTK("vITS: INV: dev_id 0x%x id %d\n",dev_id, id);
+    /* Search if device entry exists */
+    dev = vgic_its_check_device(v, dev_id);
+    if ( dev == NULL )
+    {
+        dprintk(XENLOG_ERR, "vITS: INV: Failed to find device 0x%x\n", dev_id);
+        return 1;
+    }
+
+    if ( vgic_its_check_device_id(dev, id) )
+    {
+        dprintk(XENLOG_ERR, "vITS: INV: Invalid ID %d\n", id);
+        return 1;
+    }
+
+    its_encode_cmd(phys_cmd, GITS_CMD_INV);
+    its_encode_devid(phys_cmd, dev_id);
+    its_encode_id(phys_cmd, id);
+
+    return 0;
+}
+
+static int vgic_its_build_clear_cmd(struct vcpu *v,
+                                    struct its_cmd_block *virt_cmd,
+                                    struct its_cmd_block *phys_cmd)
+{
+    struct vits_device *dev;
+    uint32_t dev_id = its_decode_devid(v->domain, virt_cmd);
+    uint32_t id = its_decode_event_id(virt_cmd);
+
+    DPRINTK("vITS: CLEAR: dev_id 0x%x id %d\n", dev_id, id);
+    /* Search if device entry exists */
+    dev = vgic_its_check_device(v, dev_id);
+    if ( dev == NULL )
+    {
+        dprintk(XENLOG_ERR, "vITS: CLEAR: Fail to find device 0x%x\n", dev_id);
+        return 1;
+    }
+
+    if ( vgic_its_check_device_id(dev, id) )
+    {
+        dprintk(XENLOG_ERR, "vITS: CLEAR: Invalid ID %d\n", id);
+        return 1;
+    }
+
+    its_encode_cmd(phys_cmd, GITS_CMD_INV);
+    its_encode_id(phys_cmd, id);
+
+    return 0;
+}
+
+static int vgic_its_build_invall_cmd(struct vcpu *v,
+                                     struct its_cmd_block *virt_cmd,
+                                     struct its_cmd_block *phys_cmd)
+{
+    uint32_t pcol_id;
+    uint8_t vcol_id = its_decode_collection(virt_cmd);
+
+    DPRINTK("vITS: INVALL: vCID %d\n", vcol_id);
+    /* Check if Collection id exists */
+    if ( vgic_its_check_cid(v, vcol_id, &pcol_id) )
+    {
+        dprintk(XENLOG_ERR, "vITS: INVALL: Wrong Collection %d\n", vcol_id);
+        return 1;
+    }
+
+    its_encode_cmd(phys_cmd, GITS_CMD_INVALL);
+    its_encode_collection(phys_cmd, pcol_id);
+
+    return 0;
+}
+
+static int vgic_its_build_int_cmd(struct vcpu *v,
+                                  struct its_cmd_block *virt_cmd,
+                                  struct its_cmd_block *phys_cmd)
+{
+    uint32_t dev_id = its_decode_devid(v->domain, virt_cmd);
+    struct vits_device *dev;
+    uint32_t id = its_decode_event_id(virt_cmd);
+
+    DPRINTK("vITS: INT: Device 0x%x id %d\n", its_decode_devid(v->domain, 
virt_cmd), id);
+    /* Search if device entry exists */
+    dev = vgic_its_check_device(v, dev_id);
+    if ( dev == NULL )
+    {
+        dprintk(XENLOG_ERR, "vITS: INT: Failed to find device 0x%x\n", dev_id);
+        return 1;
+    }
+
+    if ( vgic_its_check_device_id(dev, id) )
+    {
+        dprintk(XENLOG_ERR, "vITS: INT: Invalid ID %d\n", id);
+        return 1;
+    }
+
+    its_encode_cmd(phys_cmd, GITS_CMD_INT);
+    its_encode_devid(phys_cmd, its_decode_devid(v->domain, virt_cmd));
+    its_encode_id(phys_cmd, its_decode_event_id(virt_cmd));
+
+    return 0;
+}
+
+static void vgic_its_free_device(struct vits_device *dev)
+{
+        xfree(dev);
+}
+
+static int vgic_its_add_device(struct vcpu *v, struct its_cmd_block *virt_cmd)
+{
+    struct domain *d = v->domain;
+    struct vits_device *dev;
+
+    /* Allocate device only if valid bit is set */
+    if ( its_decode_valid(virt_cmd) )
+    {
+        dev = xzalloc(struct vits_device);
+        if ( dev == NULL )
+           return ENOMEM;
+
+        dev->dev_id = its_decode_devid(d, virt_cmd);
+        dev->itt_size = its_decode_size(virt_cmd);
+        dev->itt_addr = its_decode_itt(virt_cmd);
+        INIT_LIST_HEAD(&dev->entry);
+        INIT_LIST_HEAD(&dev->hwirq_list);
+
+        list_add(&dev->entry, &d->arch.vits->vits_dev_list);
+        DPRINTK("vITS: Added device dev_id 0x%x\n", 
its_decode_devid(v->domain, virt_cmd));
+    }
+    else
+    {
+        /* Search if device entry exists */
+        dev = vgic_its_check_device(v, its_decode_devid(v->domain, virt_cmd));
+        if ( dev == NULL )
+        {
+            dprintk(XENLOG_ERR, "vITS: Failed to find device 0x%x\n",
+                    dev->dev_id);
+            return 1;
+        }
+
+        /* Clear all lpis of this device */
+        vgic_its_unmap_id(v, dev, 0, 1);
+
+        list_del(&dev->entry);
+        vgic_its_free_device(dev);
+        DPRINTK("vITS: Removed device dev_id 0x%x\n", 
its_decode_devid(v->domain, virt_cmd));
+    }
+
+    return 0;
+}
+
+static int vgic_its_process_mapc(struct vcpu *v, struct its_cmd_block 
*virt_cmd)
+{
+    uint32_t pcid = 0;
+    int idx;
+    struct domain *d = v->domain;
+    uint32_t nmap;
+    uint8_t vcol_id;
+    uint64_t vta = 0;
+
+    nmap = d->arch.vits->cid_map.nr_cid;
+    vcol_id = its_decode_collection(virt_cmd);
+    vta = its_decode_target(virt_cmd);
+
+    for ( idx = 0; idx < nmap; idx++ )
+    {
+        if ( vcol_id == d->arch.vits->cid_map.vcid[idx] )
+            break;
+    }
+    if ( idx == nmap )
+        d->arch.vits->cid_map.vcid[idx] = vcol_id;
+
+    /* Add new entry */
+    if ( is_hardware_domain(d) )
+    {
+        if ( its_get_physical_cid(&pcid, vta) )
+            BUG_ON(1);
+        d->arch.vits->cid_map.pcid[idx] = pcid;
+        d->arch.vits->cid_map.vta[idx] = vta;
+        d->arch.vits->cid_map.nr_cid++;
+    }
+    else
+    {
+        /* For domU vta != pta so set vcid = pcid.
+           No need to check against pcid mapping from physical its driver.
+         */
+        d->arch.vits->cid_map.pcid[idx] = vcol_id;
+        d->arch.vits->cid_map.vta[idx] = vta;
+        d->arch.vits->cid_map.nr_cid++;
+    }
+
+    DPRINTK("vITS: MAPC: vCID %d vTA 0x%lx added @idx 0x%x \n",
+             vcol_id, vta, idx);
+    return 0;
+}
+
+static void vgic_its_update_read_ptr(struct vcpu *v)
+{
+    v->domain->arch.vits->cmd_read = v->domain->arch.vits->cmd_write;
+}
+
+#ifdef DEBUG_ITS
+char *cmd_str[] = {
+        [GITS_CMD_MOVI]    = "MOVI",
+        [GITS_CMD_INT]     = "INT",
+        [GITS_CMD_CLEAR]   = "CLEAR",
+        [GITS_CMD_SYNC]    = "SYNC",
+        [GITS_CMD_MAPD]    = "MAPD",
+        [GITS_CMD_MAPC]    = "MAPC",
+        [GITS_CMD_MAPVI]   = "MAPVI",
+        [GITS_CMD_MAPI]    = "MAPI",
+        [GITS_CMD_INV]     = "INV",
+        [GITS_CMD_INVALL]  = "INVALL",
+        [GITS_CMD_MOVALL]  = "MOVALL",
+        [GITS_CMD_DISCARD] = "DISCARD",
+    };
+#endif
+static int vgic_its_parse_its_command(struct vcpu *v,
+                                      struct its_cmd_block *virt_cmd)
+{
+    uint8_t cmd = its_decode_cmd(virt_cmd);
+    struct its_cmd_block phys_cmd;
+    int send_flag = 1, ret;
+
+#ifdef DEBUG_ITS
+    DPRINTK("vITS: Received cmd %s (0x%x)\n", cmd_str[cmd], cmd);
+    DPRINTK("Dump Virt cmd: ");
+    dump_cmd(virt_cmd);
+#endif
+
+    memset(&phys_cmd, 0x0, sizeof(struct its_cmd_block));
+    switch ( cmd )
+    {
+    case GITS_CMD_MAPD:
+        /* create virtual device entry */
+        if ( vgic_its_add_device(v, virt_cmd) )
+            return ENODEV;
+        ret = vgic_its_build_mapd_cmd(v, virt_cmd, &phys_cmd);
+        break;
+    case GITS_CMD_MAPC:
+        /* Physical ITS driver already mapped physical Collection */
+        send_flag = 0;
+        ret =  vgic_its_process_mapc(v, virt_cmd);
+        break;
+    case GITS_CMD_MAPI:
+        /* MAPI is same as MAPVI */
+    case GITS_CMD_MAPVI:
+        ret = vgic_its_build_mapvi_cmd(v, virt_cmd, &phys_cmd);
+        break;
+    case GITS_CMD_MOVI:
+        ret = vgic_its_build_movi_cmd(v, virt_cmd, &phys_cmd);
+        break;
+    case GITS_CMD_DISCARD:
+        ret = vgic_its_build_discard_cmd(v, virt_cmd, &phys_cmd);
+        break;
+    case GITS_CMD_INV:
+        ret = vgic_its_build_inv_cmd(v, virt_cmd, &phys_cmd);
+        break;
+    case GITS_CMD_INVALL:
+        ret = vgic_its_build_invall_cmd(v, virt_cmd, &phys_cmd);
+        break;
+    case GITS_CMD_INT:
+        ret = vgic_its_build_int_cmd(v, virt_cmd, &phys_cmd);
+        break;
+    case GITS_CMD_CLEAR:
+        ret = vgic_its_build_clear_cmd(v, virt_cmd, &phys_cmd);
+        break;
+    case GITS_CMD_SYNC:
+        ret = vgic_its_build_sync_cmd(v, virt_cmd, &phys_cmd);
+        break;
+        /*TODO:  GITS_CMD_MOVALL not implemented */
+    default:
+       dprintk(XENLOG_ERR, "vITS: Unhandled command cmd %d\n", cmd);
+       return 1;
+    }
+
+#ifdef DEBUG_ITS
+    DPRINTK("Dump Phys cmd: ");
+    dump_cmd(&phys_cmd);
+#endif
+
+    if ( ret )
+    {
+       dprintk(XENLOG_ERR, "vITS: Failed to handle cmd %d\n", cmd);
+       return 1;
+    }
+
+    if ( send_flag )
+    {
+       if ( !gic_its_send_cmd(v, &phys_cmd) )
+       {
+           dprintk(XENLOG_ERR, "vITS: Failed to push cmd %d\n", cmd);
+           return 1;
+       }
+    }
+
+    return 0;
+}
+/* Called with its lock held */
+static int vgic_its_read_virt_cmd(struct vcpu *v,
+                                  struct its_cmd_block *virt_cmd)
+{
+    struct page_info * page;
+    void *p;
+    paddr_t paddr;
+    paddr_t maddr = v->domain->arch.vits->cmd_base & 0xfffffffff000UL;
+    uint64_t offset;
+
+    /* CMD Q can be more than 1 page. Map only page that is required */
+    maddr = ((v->domain->arch.vits->cmd_base & 0xfffffffff000UL) +
+              v->domain->arch.vits->cmd_write_save ) & PAGE_MASK;
+
+    paddr = p2m_lookup(v->domain, maddr, NULL);
+
+    DPRINTK("vITS: Mapping CMD Q maddr 0x%lx paddr 0x%lx write_save 0x%lx \n",
+            maddr, paddr, v->domain->arch.vits->cmd_write_save);
+    page = get_page_from_paddr(v->domain, paddr, 0);
+    if ( page == NULL )
+    {
+        dprintk(XENLOG_ERR, "vITS: Failed to get command page\n");
+        return 1;
+    }
+
+    p = __map_domain_page(page);
+
+    /* Offset within the mapped 4K page to read */
+    offset = v->domain->arch.vits->cmd_write_save & 0xfff;
+
+    memcpy(virt_cmd, p + offset, sizeof(struct its_cmd_block));
+
+    /* No command queue is created by vits to check on Q full */
+    v->domain->arch.vits->cmd_write_save += 0x20;
+    if ( v->domain->arch.vits->cmd_write_save ==
+         v->domain->arch.vits->cmd_qsize )
+    {
+         DPRINTK("vITS: Reset write_save 0x%lx qsize 0x%lx \n",
+                 v->domain->arch.vits->cmd_write_save,
+                 v->domain->arch.vits->cmd_qsize);
+         v->domain->arch.vits->cmd_write_save = 0x0;
+    }
+
+    unmap_domain_page(p);
+    put_page(page);
+
+    return 0;
+}
+
+int vgic_its_process_cmd(struct vcpu *v)
+{
+    struct its_cmd_block virt_cmd;
+    struct domain *d = v->domain;
+
+    /* XXX: Currently we are processing one cmd at a time */
+    ASSERT(spin_is_locked(&d->arch.vits->lock));
+
+    do {
+        if ( vgic_its_read_virt_cmd(v, &virt_cmd) )
+            goto err;
+        if ( vgic_its_parse_its_command(v, &virt_cmd) )
+            goto err;
+    } while ( v->domain->arch.vits->cmd_write !=
+              v->domain->arch.vits->cmd_write_save );
+
+    v->domain->arch.vits->cmd_write_save = v->domain->arch.vits->cmd_write;
+    DPRINTK("vITS: write_save 0x%lx write 0x%lx \n",
+            v->domain->arch.vits->cmd_write_save,
+            v->domain->arch.vits->cmd_write);
+    /* XXX: Currently we are processing one cmd at a time */
+    vgic_its_update_read_ptr(v);
+
+    dsb(ishst);
+
+    return 1;
+err:
+    dprintk(XENLOG_ERR, "vITS: Failed to process guest cmd\n");
+    return 0;
+}
+
 /*
  * Local variables:
  * mode: C
diff --git a/xen/include/asm-arm/domain.h b/xen/include/asm-arm/domain.h
index 9018c6a..d02c200 100644
--- a/xen/include/asm-arm/domain.h
+++ b/xen/include/asm-arm/domain.h
@@ -109,6 +109,8 @@ struct arch_domain
 #endif
     } vgic;
 
+    struct vgic_its *vits;
+
     struct vuart {
 #define VUART_BUF_SIZE 128
         char                        *buf;
diff --git a/xen/include/asm-arm/gic-its.h b/xen/include/asm-arm/gic-its.h
index 2fca1ef..0b2f95e 100644
--- a/xen/include/asm-arm/gic-its.h
+++ b/xen/include/asm-arm/gic-its.h
@@ -24,6 +24,67 @@
 
 #define IRQ_PER_CHUNK 32
 
+/* ITS device structure */
+struct vits_device
+{
+    struct list_head entry;
+    /* Device id */
+    uint32_t  dev_id;
+    /* ITT address */
+    paddr_t itt_addr;
+    /* ITT size */
+    unsigned long itt_size;
+    /* LPIs assigned to device */
+    struct list_head hwirq_list;
+};
+
+struct cid_mapping
+{
+    /* Number of collections mapped */
+    uint8_t nr_cid;
+    /* XXX: assume one collection id per vcpu. can set to MAX_VCPUS? */
+    /* Virtual collection id */
+    uint8_t vcid[32];
+    /* Physical collection id */
+    uint8_t pcid[32];
+    /* Virtual target address of this collection id */
+    uint64_t vta[32];
+};
+
+/* Per domain ITS struct */
+struct vgic_its
+{
+   spinlock_t lock;
+   /* Emulation of BASER */
+   paddr_t baser[8];
+   /* Command queue base */
+   paddr_t cmd_base;
+   /* Command queue write pointer */
+   paddr_t cmd_write;
+   /* Command queue write saved pointer */
+   paddr_t cmd_write_save;
+   /* Command queue read pointer */
+   paddr_t cmd_read;
+   /* Command queue size */
+   unsigned long cmd_qsize;
+   /* LPI propbase */
+   paddr_t lpi_propbase;
+   /* percpu pendbase */
+   paddr_t lpi_pendbase[MAX_VIRT_CPUS];
+   /* Virtual LPI property table */
+   void * lpi_prop_page;
+   /* ITS mmio physical base */
+   paddr_t phys_base;
+   /* ITS mmio physical size */
+   unsigned long phys_size;
+   /* gicr ctrl register */
+   uint32_t ctrl;
+   /* Device list. This dev list will hold vlpi, lpi, Devid */
+   struct list_head vits_dev_list;
+   /* Virtual to Physical Collection id mapping */
+   struct cid_mapping cid_map;
+};
+
 /*
  * Our LPI allocation unit - Chunks of 32 IDs
  */
@@ -107,6 +168,12 @@ static inline void its_encode_devid(struct its_cmd_block 
*cmd, u32 devid)
     cmd->raw_cmd[0] |= ((u64)devid) << 32;
 }
 
+static inline void its_encode_id(struct its_cmd_block *cmd, uint32_t id)
+{
+    cmd->raw_cmd[1] &= ~0xffffffffULL;
+    cmd->raw_cmd[1] |= id;
+}
+
 static inline void its_encode_event_id(struct its_cmd_block *cmd, u32 id)
 {
     cmd->raw_cmd[1] &= ~0xffffffffULL;
@@ -158,6 +225,15 @@ static inline void its_fixup_cmd(struct its_cmd_block *cmd)
     cmd->raw_cmd[3] = cpu_to_le64(cmd->raw_cmd[3]);
 }
 
+void vgic_its_enable_lpis(struct vcpu *v, uint32_t lpi);
+int vgic_its_get_pid(struct vcpu *v, uint32_t vid, uint32_t *pid);
+uint8_t vgic_its_get_priority(struct vcpu *v, uint32_t pid);
+
+int its_check_target(uint64_t vta);
+int its_get_target(uint8_t pcid, uint64_t *pta);
+int its_get_physical_cid(uint32_t *col_id, uint64_t ta);
+int gic_its_send_cmd(struct vcpu *v, struct its_cmd_block *phys_cmd);
+
 #endif /* __ASM_ARM_GIC_ITS_H__ */
 
 /*
-- 
1.7.9.5


_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxx
http://lists.xen.org/xen-devel


 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.