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

[Xen-devel] [RFC PATCH v3 11/18] xen/arm: ITS: Add GITS registers emulation



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

Emulate GITS* registers and handle LPI configuration
table update trap.

Signed-off-by: Vijaya Kumar K <Vijaya.Kumar@xxxxxxxxxxxxxxxxxx>
---
 xen/arch/arm/vgic-v3-its.c    |  516 +++++++++++++++++++++++++++++++++++++++++
 xen/include/asm-arm/gic-its.h |   14 ++
 2 files changed, 530 insertions(+)

diff --git a/xen/arch/arm/vgic-v3-its.c b/xen/arch/arm/vgic-v3-its.c
index 0671434..fa9dccc 100644
--- a/xen/arch/arm/vgic-v3-its.c
+++ b/xen/arch/arm/vgic-v3-its.c
@@ -63,6 +63,46 @@ static void dump_cmd(its_cmd_block *cmd)
 }
 #endif
 
+void vgic_its_disable_lpis(struct vcpu *v, uint32_t vlpi)
+{
+    struct pending_irq *p;
+    unsigned long flags;
+
+    p = irq_to_pending(v, vlpi);
+    clear_bit(GIC_IRQ_GUEST_ENABLED, &p->status);
+    gic_remove_from_queues(v, vlpi);
+    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 vlpi, uint8_t priority)
+{
+    struct pending_irq *p;
+    unsigned long flags;
+
+    /* Get plpi for the given vlpi */
+    p = irq_to_pending(v, vlpi);
+    p->priority = priority;
+    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, irq_to_virq(p->desc), 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);
+    }
+}
 /* ITS device table helper functions */
 int vits_vdevice_entry(struct domain *d, uint32_t dev_id,
                        struct vdevice_table *entry, int set)
@@ -649,6 +689,482 @@ err:
     return 0;
 }
 
+static int vgic_v3_gits_lpi_mmio_read(struct vcpu *v, mmio_info_t *info)
+{
+    uint32_t offset;
+    struct hsr_dabt dabt = info->dabt;
+    struct cpu_user_regs *regs = guest_cpu_user_regs();
+    register_t *r = select_user_reg(regs, dabt.reg);
+    uint8_t cfg;
+
+    offset = info->gpa -
+             (v->domain->arch.vits->propbase & 0xfffffffff000UL);
+
+    if ( offset < SZ_64K )
+    {
+        DPRINTK("vITS:d%dv%d LPI Table read offset 0x%x\n",
+                v->domain->domain_id, v->vcpu_id, offset);
+        cfg = readb_relaxed(v->domain->arch.vits->prop_page + offset);
+        *r = cfg;
+        return 1;
+    }
+    else
+        dprintk(XENLOG_G_ERR, "vITS:d%dv%d LPI Table read with wrong offset 
0x%x\n",
+                v->domain->domain_id, v->vcpu_id, offset);
+
+
+    return 0;
+}
+
+static int vgic_v3_gits_lpi_mmio_write(struct vcpu *v, mmio_info_t *info)
+{
+    uint32_t offset;
+    uint32_t vid;
+    uint8_t cfg;
+    bool_t enable;
+    struct hsr_dabt dabt = info->dabt;
+    struct cpu_user_regs *regs = guest_cpu_user_regs();
+    register_t *r = select_user_reg(regs, dabt.reg);
+
+    offset = info->gpa -
+             (v->domain->arch.vits->propbase & 0xfffffffff000UL);
+
+    vid = offset + NR_GIC_LPI;
+    if ( offset < SZ_64K )
+    {
+        DPRINTK("vITS:d%dv%d LPI Table write offset 0x%x\n",
+                v->domain->domain_id, v->vcpu_id, offset);
+        cfg = readb_relaxed(v->domain->arch.vits->prop_page + offset);
+        enable = (cfg & *r) & 0x1;
+
+        if ( !enable )
+             vgic_its_enable_lpis(v, vid,  (*r & 0xfc));
+        else
+             vgic_its_disable_lpis(v, vid);
+
+        /* Update virtual prop page */
+        writeb_relaxed((*r & 0xff),
+                        v->domain->arch.vits->prop_page + offset);
+        
+        return 1;
+    }
+    else
+        dprintk(XENLOG_G_ERR, "vITS:d%dv%d LPI Table invalid write @ 0x%x\n",
+                v->domain->domain_id, v->vcpu_id, offset);
+
+    return 0; 
+}
+
+static const struct mmio_handler_ops vgic_gits_lpi_mmio_handler = {
+    .read_handler  = vgic_v3_gits_lpi_mmio_read,
+    .write_handler = vgic_v3_gits_lpi_mmio_write,
+};
+
+int vgic_its_unmap_lpi_prop(struct vcpu *v)
+{
+    paddr_t maddr;
+    uint32_t lpi_size;
+    int i;
+    
+    maddr = v->domain->arch.vits->propbase & 0xfffffffff000UL;
+    lpi_size = 1UL << ((v->domain->arch.vits->propbase & 0x1f) + 1);
+
+    DPRINTK("vITS:d%dv%d Unmap guest LPI conf table maddr 0x%lx lpi_size 
0x%x\n", 
+             v->domain->domain_id, v->vcpu_id, maddr, lpi_size);
+
+    if ( lpi_size < SZ_64K )
+    {
+        dprintk(XENLOG_G_ERR, "vITS:d%dv%d LPI Prop page < 64K\n",
+                v->domain->domain_id, v->vcpu_id);
+        return 0;
+    }
+
+    /* XXX: As per 4.8.9 each re-distributor shares a common LPI configuration 
table 
+     * So one set of mmio handlers to manage configuration table is enough
+     */
+    for ( i = 0; i < lpi_size / PAGE_SIZE; i++ )
+        guest_physmap_remove_page(v->domain, paddr_to_pfn(maddr),
+                                gmfn_to_mfn(v->domain, paddr_to_pfn(maddr)), 
0);
+
+    /* Register mmio handlers for this region */
+    register_mmio_handler(v->domain, &vgic_gits_lpi_mmio_handler,
+                          maddr, lpi_size);
+
+    /* Allocate Virtual LPI Property table */
+    v->domain->arch.vits->prop_page =
+        alloc_xenheap_pages(get_order_from_bytes(lpi_size), 0);
+    if ( !v->domain->arch.vits->prop_page )
+    {
+        dprintk(XENLOG_G_ERR, "vITS:d%dv%d Failed to allocate LPI Prop page\n",
+                v->domain->domain_id, v->vcpu_id);
+        return 0;
+    }
+
+    memset(v->domain->arch.vits->prop_page, 0xa2, lpi_size);
+
+    return 1;
+}
+
+static inline void vits_spin_lock(struct vgic_its *vits)
+{
+    spin_lock(&vits->lock);
+}
+
+static inline void vits_spin_unlock(struct vgic_its *vits)
+{
+    spin_unlock(&vits->lock);
+}
+
+static int vgic_v3_gits_mmio_read(struct vcpu *v, mmio_info_t *info)
+{
+    struct vgic_its *vits;
+    struct hsr_dabt dabt = info->dabt;
+    struct cpu_user_regs *regs = guest_cpu_user_regs();
+    register_t *r = select_user_reg(regs, dabt.reg);
+    uint64_t val = 0;
+    uint32_t index, gits_reg;
+
+    vits = v->domain->arch.vits;
+
+    gits_reg = info->gpa - vits->phys_base;
+
+    if ( gits_reg >= SZ_64K )
+    {
+        gdprintk(XENLOG_G_WARNING,
+                 "vITS:d%dv%d unknown gpa read address %"PRIpaddr"\n",
+                 v->domain->domain_id, v->vcpu_id, info->gpa);
+        return 0;
+    }
+
+    switch ( gits_reg )
+    {
+    case GITS_CTLR:
+        if ( dabt.size != DABT_WORD )
+            goto bad_width;
+        *r = 0;
+        return 1;
+    case GITS_IIDR:
+        if ( dabt.size != DABT_WORD )
+            goto bad_width;
+        *r = 0;
+        return 1;
+    case GITS_TYPER:
+        vits_spin_lock(vits);
+        val = (((v->domain->max_vcpus + 1) << GITS_TYPER_HCC_SHIFT ) |
+                 VITS_GITS_DEV_BITS | VITS_GITS_ID_BITS              |
+                 VITS_GITS_ITT_SIZE | VITS_GITS_DISTRIBUTED          |
+                 VITS_GITS_PLPIS);
+        if ( dabt.size == DABT_DOUBLE_WORD )
+            *r = val;
+        else if ( dabt.size == DABT_WORD )
+            *r = (u32)val;
+        else
+        {
+            vits_spin_unlock(vits);
+            goto bad_width;
+        }
+        vits_spin_unlock(vits);
+        return 1;
+    case GITS_TYPER + 4:
+        if (dabt.size != DABT_WORD ) goto bad_width;
+        vits_spin_lock(vits);
+        val = (((v->domain->max_vcpus + 1) << GITS_TYPER_HCC_SHIFT ) |
+                 VITS_GITS_DEV_BITS | VITS_GITS_ID_BITS              |
+                 VITS_GITS_ITT_SIZE | VITS_GITS_DISTRIBUTED          |
+                 VITS_GITS_PLPIS);
+        *r = (u32)(val >> 32);
+        vits_spin_unlock(vits);
+        return 1;
+    case 0x0010 ... 0x007c:
+    case 0xc000 ... 0xffcc:
+        /* Implementation defined -- read ignored */
+        dprintk(XENLOG_ERR,
+                "vITS:d%dv%d read unknown 0x000c - 0x007c r%d offset %#08x\n",
+                v->domain->domain_id, v->vcpu_id, dabt.reg, gits_reg);
+        goto read_as_zero;
+    case GITS_CBASER:
+        /* XXX: Only read support 32/64-bit access */
+        vits_spin_lock(vits);
+        if ( dabt.size == DABT_DOUBLE_WORD )
+            *r = vits->cmd_base && 0xc7ffffffffffffffUL;
+        else if ( dabt.size == DABT_WORD )
+            *r = (u32)vits->cmd_base;
+        else
+        {
+            vits_spin_unlock(vits);
+            goto bad_width;
+        }
+        vits_spin_unlock(vits);
+        return 1;
+    case GITS_CBASER + 4:
+        if (dabt.size != DABT_WORD )
+            goto bad_width;
+        vits_spin_lock(vits);
+        *r = (u32)(vits->cmd_base >> 32);
+        vits_spin_unlock(vits);
+        return 1;
+    case GITS_CWRITER:
+        /* XXX: Only read support 32/64-bit access */
+        vits_spin_lock(vits);
+        if ( dabt.size == DABT_DOUBLE_WORD )
+            *r = vits->cmd_write;
+        else if ( dabt.size == DABT_WORD )
+            *r = (u32)vits->cmd_write;
+        else
+        {
+            vits_spin_unlock(vits);
+            goto bad_width;
+        }
+        vits_spin_unlock(vits);
+        return 1;
+    case GITS_CWRITER + 4:
+        if ( dabt.size != DABT_WORD )
+            goto bad_width;
+        vits_spin_lock(vits);
+        *r = (u32)(vits->cmd_write >> 32);
+        vits_spin_unlock(vits);
+        return 1;
+    case GITS_CREADR:
+        /* XXX: Only read support 32/64-bit access */
+        vits_spin_lock(vits);
+        if ( dabt.size == DABT_DOUBLE_WORD )
+            *r = vits->cmd_read;
+        else if ( dabt.size == DABT_WORD )
+            *r = (u32)vits->cmd_read;
+        else
+        {
+            vits_spin_unlock(vits);
+            goto bad_width;
+        }
+        vits_spin_unlock(vits);
+        return 1;
+    case GITS_CREADR + 4:
+        if ( dabt.size != DABT_WORD )
+            goto bad_width;
+        vits_spin_lock(vits);
+        *r = (u32)(vits->cmd_read >> 32);
+        vits_spin_unlock(vits);
+        return 1;
+    case 0x0098 ... 0x009c:
+    case 0x00a0 ... 0x00fc:
+    case 0x0140 ... 0xbffc:
+        /* Reserved -- read ignored */
+        dprintk(XENLOG_ERR,
+                "vITS:d%dv%d read unknown 0x0098-9c or 0x00a0-fc r%d offset 
%#08x\n",
+                v->domain->domain_id, v->vcpu_id, dabt.reg, gits_reg);
+        goto read_as_zero;
+    case GITS_BASER ... GITS_BASERN:
+        /* Supports only 64-bit access */
+        if ( dabt.size != DABT_DOUBLE_WORD )
+            goto bad_width;
+        if ( (gits_reg % 8) != 0 )
+            goto bad_width;
+        vits_spin_lock(vits);
+        index = (gits_reg - GITS_BASER) / 8;
+        *r = vits->baser[index];
+        vits_spin_unlock(vits);
+        return 1;
+    case GITS_PIDR0:
+        if ( dabt.size != DABT_WORD )
+            goto bad_width;
+        *r = GITS_PIDR0_VAL;
+        return 1;
+    case GITS_PIDR1:
+        if ( dabt.size != DABT_WORD )
+            goto bad_width;
+        *r = GITS_PIDR1_VAL;
+        return 1;
+    case GITS_PIDR2:
+        if ( dabt.size != DABT_WORD )
+            goto bad_width;
+        *r = GITS_PIDR2_VAL;
+        return 1;
+    case GITS_PIDR3:
+        if ( dabt.size != DABT_WORD )
+            goto bad_width;
+        *r = GITS_PIDR3_VAL;
+        return 1;
+    case GITS_PIDR4:
+        if ( dabt.size != DABT_WORD )
+            goto bad_width;
+        *r = GITS_PIDR4_VAL;
+        return 1;
+    case GITS_PIDR5 ... GITS_PIDR7:
+        goto read_as_zero;
+   default:
+        dprintk(XENLOG_G_ERR, "vITS:d%dv%d unhandled read r%d offset %#08x\n",
+                v->domain->domain_id, v->vcpu_id, dabt.reg, gits_reg);
+        return 0;
+    }
+
+bad_width:
+    dprintk(XENLOG_G_ERR, "vITS:d%dv%d bad read width %d r%d offset %#08x\n",
+           v->domain->domain_id, v->vcpu_id, dabt.size, dabt.reg, gits_reg);
+    domain_crash_synchronous();
+    return 0;
+
+read_as_zero:
+    if ( dabt.size != DABT_WORD )
+       goto bad_width;
+    *r = 0;
+    return 1;
+}
+
+#define GITS_BASER_MASK  (~((0x7UL << GITS_BASER_TYPE_SHIFT) | \
+                         (0xffUL << GITS_BASER_ENTRY_SIZE_SHIFT) | \
+                         (0x3UL << GITS_BASER_SHAREABILITY_SHIFT)))
+
+static int vgic_v3_gits_mmio_write(struct vcpu *v, mmio_info_t *info)
+{
+    struct vgic_its *vits;
+    struct hsr_dabt dabt = info->dabt;
+    struct cpu_user_regs *regs = guest_cpu_user_regs();
+    register_t *r = select_user_reg(regs, dabt.reg);
+    int ret;
+    uint32_t index, gits_reg, sz, psz;
+    uint64_t val;
+
+    vits = v->domain->arch.vits;
+
+    gits_reg = info->gpa - vits->phys_base;
+
+    if ( gits_reg >= SZ_64K )
+    {
+        gdprintk(XENLOG_G_WARNING,
+                 "vITS:d%dv%d unknown gpa write address %"PRIpaddr"\n",
+                 v->domain->domain_id, v->vcpu_id, info->gpa);
+        return 0;
+    }
+    switch ( gits_reg )
+    {
+    case GITS_CTLR:
+        if ( dabt.size != DABT_WORD )
+            goto bad_width;
+        vits_spin_lock(vits);
+        vits->ctrl = *r;
+        vits_spin_unlock(vits);
+        return 1;
+    case GITS_IIDR:
+        /* R0 -- write ignored */
+        goto write_ignore;
+    case GITS_TYPER:
+    case GITS_TYPER + 4:
+        /* R0 -- write ignored */
+        goto write_ignore;
+    case 0x0010 ... 0x007c:
+    case 0xc000 ... 0xffcc:
+        /* Implementation defined -- write ignored */
+        dprintk(XENLOG_G_ERR,
+                "vITS:d%dv%d write to unknown 0x000c - 0x007c r%d offset 
%#08x\n",
+                v->domain->domain_id, v->vcpu_id, dabt.reg, gits_reg);
+        goto write_ignore;
+    case GITS_CBASER:
+        if ( dabt.size != DABT_DOUBLE_WORD )
+            goto bad_width;
+        vits_spin_lock(vits);
+        vits->cmd_base = *r;
+        vits->cmd_qsize  =  SZ_4K * ((*r & 0xff) + 1);
+        vits_spin_unlock(vits);
+        return 1;
+    case GITS_CBASER + 4:
+         /* XXX: Does not support word write */
+        goto bad_width;
+    case GITS_CWRITER:
+        vits_spin_lock(vits);
+        if ( dabt.size == DABT_DOUBLE_WORD )
+            vits->cmd_write = *r;
+        else if ( dabt.size == DABT_WORD)
+        {
+            val = vits->cmd_write & 0xffffffff00000000UL;
+            val = (*r) | val;
+            vits->cmd_write =  val;
+        }
+        else
+        {
+            vits_spin_unlock(vits);
+            goto bad_width;
+        }
+        ret = vgic_its_process_cmd(v, vits);
+        vits_spin_unlock(vits);
+        return ret;
+    case GITS_CWRITER + 4:
+        if (dabt.size != DABT_WORD )
+            goto bad_width;
+        vits_spin_lock(vits);
+        val = vits->cmd_write & 0xffffffffUL;
+        val = ((*r & 0xffffffffUL) << 32) | val;
+        vits->cmd_write =  val;
+        ret = vgic_its_process_cmd(v, vits);
+        vits_spin_unlock(vits);
+        return ret;
+    case GITS_CREADR:
+        /* R0 -- write ignored */
+        goto write_ignore;
+    case 0x0098 ... 0x009c:
+    case 0x00a0 ... 0x00fc:
+    case 0x0140 ... 0xbffc:
+        /* Reserved -- write ignored */
+        dprintk(XENLOG_G_ERR,
+                "vITS:d%dv%d write to unknown 0x98-9c or 0xa0-fc r%d offset 
%#08x\n",
+                v->domain->domain_id, v->vcpu_id, dabt.reg, gits_reg);
+        goto write_ignore;
+    case GITS_BASER0:
+        if ( dabt.size != DABT_DOUBLE_WORD )
+            goto bad_width;
+        vits_spin_lock(vits);
+        vits->baser[0] = vits->baser[0] | (GITS_BASER_MASK & *r);
+        vits->dt_ipa = vits->baser[0] & 0xfffffffff000UL;
+        psz = (vits->baser[0] >> GITS_BASER_PAGE_SIZE_SHIFT) &
+               GITS_BASER_PAGE_SIZE_MASK_VAL;
+        if ( psz == GITS_BASER_PAGE_SIZE_4K_VAL )
+            sz = 4;
+        else if ( psz == GITS_BASER_PAGE_SIZE_16K_VAL )
+            sz = 16;
+        else
+            sz = 64;
+
+        vits->dt_size = (vits->baser[0] & GITS_BASER_PAGES_MASK_VAL)
+                        * sz * SZ_1K;
+        vits_spin_unlock(vits);
+        return 1;
+    case GITS_BASER1 ... GITS_BASERN:
+        /* Nothing to do with this values. Just store and emulate */
+        if ( dabt.size != DABT_DOUBLE_WORD )
+            goto bad_width;
+        if ( (gits_reg % 8) != 0 )
+            goto bad_width;
+        vits_spin_lock(vits);
+        index = (gits_reg - GITS_BASER) / 8;
+        vits->baser[index] = *r;
+        vits_spin_unlock(vits);
+        return 1;
+    case GITS_PIDR7 ... GITS_PIDR0:
+        /* R0 -- write ignored */
+        goto write_ignore;
+   default:
+        dprintk(XENLOG_G_ERR, "vITS:d%dv%d unhandled write r%d offset %#08x\n",
+                v->domain->domain_id, v->vcpu_id, dabt.reg, gits_reg);
+        return 0;
+    }
+
+bad_width:
+    dprintk(XENLOG_G_ERR, "vITS:d%dv%d bad write width %d r%d offset %#08x\n",
+            v->domain->domain_id, v->vcpu_id, dabt.size, dabt.reg, gits_reg);
+    domain_crash_synchronous();
+    return 0;
+
+write_ignore:
+    if ( dabt.size != DABT_WORD ) goto bad_width;
+    *r = 0;
+    return 1;
+}
+
+
+static const struct mmio_handler_ops vgic_gits_mmio_handler = {
+    .read_handler  = vgic_v3_gits_mmio_read,
+    .write_handler = vgic_v3_gits_mmio_write,
+};
+
 /*
  * Local variables:
  * mode: C
diff --git a/xen/include/asm-arm/gic-its.h b/xen/include/asm-arm/gic-its.h
index 8f898a6..3271477 100644
--- a/xen/include/asm-arm/gic-its.h
+++ b/xen/include/asm-arm/gic-its.h
@@ -38,6 +38,8 @@ struct its_collection {
 struct vgic_its
 {
    spinlock_t lock;
+   /* Emulation of BASER */
+   paddr_t baser[8];
    /* Command queue base */
    paddr_t cmd_base;
    /* Command queue write pointer */
@@ -48,8 +50,20 @@ struct vgic_its
    paddr_t cmd_read;
    /* Command queue size */
    unsigned long cmd_qsize;
+   /* ITS mmio physical base */
+   paddr_t phys_base;
+   /* ITS mmio physical size */
+   unsigned long phys_size;
    /* ITS physical node */
    struct its_node *its;
+   /* GICR ctrl register */
+   uint32_t ctrl;
+   /* LPI propbase */
+   paddr_t propbase;
+   /* percpu pendbase */
+   paddr_t pendbase[MAX_VIRT_CPUS];
+   /* Virtual LPI property table */
+   void * prop_page;
    /* vITT device table ipa */
    paddr_t dt_ipa;
    /* vITT device table size */
-- 
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®.