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

[Xen-devel] [PATCH v4 12/17] xen/arm: ITS: Initialize LPI irq descriptors and route



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

Allocate and initialize irq descriptor for LPIs and
route LPIs to guest

Signed-off-by: Vijaya Kumar K <Vijaya.Kumar@xxxxxxxxxxxxxxxxxx>
---
v4: - Merge patch #16
    - Changed commit message
---
 xen/arch/arm/gic-v3.c         |    2 +-
 xen/arch/arm/gic.c            |   24 +++++++--
 xen/arch/arm/irq.c            |   44 +++++++++++++----
 xen/arch/arm/vgic-v3-its.c    |    9 ++++
 xen/arch/arm/vgic-v3.c        |   15 ++++--
 xen/arch/arm/vgic.c           |  110 ++++++++++++++++++++++++++++++++++++++---
 xen/include/asm-arm/domain.h  |    3 ++
 xen/include/asm-arm/gic-its.h |    1 +
 xen/include/asm-arm/gic.h     |    7 ++-
 xen/include/asm-arm/vgic.h    |    1 +
 10 files changed, 192 insertions(+), 24 deletions(-)

diff --git a/xen/arch/arm/gic-v3.c b/xen/arch/arm/gic-v3.c
index e6004d2..53554e6 100644
--- a/xen/arch/arm/gic-v3.c
+++ b/xen/arch/arm/gic-v3.c
@@ -895,7 +895,7 @@ static void gicv3_update_lr(int lr, const struct 
pending_irq *p,
     val |= ((uint64_t)p->priority & 0xff) << GICH_LR_PRIORITY_SHIFT;
     val |= ((uint64_t)p->irq & GICH_LR_VIRTUAL_MASK) << GICH_LR_VIRTUAL_SHIFT;
 
-   if ( p->desc != NULL )
+   if ( p->desc != NULL && !(is_lpi(p->irq)) )
        val |= GICH_LR_HW | (((uint64_t)p->desc->irq & GICH_LR_PHYSICAL_MASK)
                            << GICH_LR_PHYSICAL_SHIFT);
 
diff --git a/xen/arch/arm/gic.c b/xen/arch/arm/gic.c
index 3ebadcf..92d2be9 100644
--- a/xen/arch/arm/gic.c
+++ b/xen/arch/arm/gic.c
@@ -68,11 +68,18 @@ enum gic_version gic_hw_version(void)
    return gic_hw_ops->info->hw_version;
 }
 
+/* Only validates PPIs/SGIs/SPIs supported */
 unsigned int gic_number_lines(void)
 {
     return gic_hw_ops->info->nr_lines;
 }
 
+/* Validates PPIs/SGIs/SPIs/LPIs supported */
+bool_t gic_is_valid_irq(unsigned int irq)
+{
+    return ((irq < gic_hw_ops->info->nr_lines) && is_lpi(irq));
+}
+
 unsigned int gic_nr_id_bits(void)
 {
     return gic_hw_ops->info->nr_id_bits;
@@ -148,7 +155,7 @@ void gic_route_irq_to_xen(struct irq_desc *desc, const 
cpumask_t *cpu_mask,
 {
     ASSERT(priority <= 0xff);     /* Only 8 bits of priority */
     /* Can't route interrupts that don't exist */
-    ASSERT(desc->irq < gic_number_lines() || is_lpi(desc->irq));
+    ASSERT(gic_is_valid_irq(desc->irq));
     ASSERT(test_bit(_IRQ_DISABLED, &desc->status));
     ASSERT(spin_is_locked(&desc->lock));
 
@@ -160,6 +167,17 @@ void gic_route_irq_to_xen(struct irq_desc *desc, const 
cpumask_t *cpu_mask,
 /* Program the GIC to route an interrupt to a guest
  *   - desc.lock must be held
  */
+int gic_route_lpi_to_guest(struct domain *d, unsigned int virq,
+                           struct irq_desc *desc, unsigned int priority)
+{
+    ASSERT(spin_is_locked(&desc->lock));
+
+    desc->handler = get_guest_hw_irq_controller(desc->irq);
+    set_bit(_IRQ_GUEST, &desc->status);
+
+    return 0;
+}
+
 int gic_route_irq_to_guest(struct domain *d, unsigned int virq,
                            struct irq_desc *desc, unsigned int priority)
 {
@@ -454,7 +472,7 @@ static void gic_update_one_lr(struct vcpu *v, int i)
         if ( test_bit(GIC_IRQ_GUEST_ENABLED, &p->status) &&
              test_and_clear_bit(GIC_IRQ_GUEST_QUEUED, &p->status) )
         {
-            if ( p->desc == NULL )
+            if ( p->desc == NULL  || is_lpi(irq) )
             {
                  lr_val.state |= GICH_LR_PENDING;
                  gic_hw_ops->write_lr(i, &lr_val);
@@ -677,7 +695,7 @@ void gic_interrupt(struct cpu_user_regs *regs, int is_fiq)
         /* Reading IRQ will ACK it */
         irq = gic_hw_ops->read_irq();
 
-        if ( likely(irq >= 16 && irq < 1020) )
+        if ( (likely(irq >= 16 && irq < 1020)) || is_lpi(irq) )
         {
             local_irq_enable();
             do_IRQ(regs, irq, is_fiq);
diff --git a/xen/arch/arm/irq.c b/xen/arch/arm/irq.c
index 3806d98..c8ea627 100644
--- a/xen/arch/arm/irq.c
+++ b/xen/arch/arm/irq.c
@@ -62,12 +62,21 @@ hw_irq_controller no_irq_type = {
 };
 
 static irq_desc_t irq_desc[NR_IRQS];
+#ifdef CONFIG_ARM_64
+static irq_desc_t irq_desc_lpi[NR_GIC_LPI];
+#endif
 static DEFINE_PER_CPU(irq_desc_t[NR_LOCAL_IRQS], local_irq_desc);
 
 irq_desc_t *__irq_to_desc(int irq)
 {
     if (irq < NR_LOCAL_IRQS) return &this_cpu(local_irq_desc)[irq];
-    return &irq_desc[irq-NR_LOCAL_IRQS];
+    else if ( irq >= NR_LOCAL_IRQS && irq < NR_IRQS)
+        return &irq_desc[irq-NR_LOCAL_IRQS];
+#ifdef CONFIG_ARM_64
+    else if ( is_lpi(irq) )
+        return &irq_desc_lpi[irq - NR_GIC_LPI];
+#endif
+    return NULL;
 }
 
 int __init arch_init_one_irq_desc(struct irq_desc *desc)
@@ -88,6 +97,15 @@ static int __init init_irq_data(void)
         desc->action  = NULL;
     }
 
+#ifdef CONFIG_ARM_64
+    for ( irq = NR_GIC_LPI; irq < MAX_LPI; irq++ )
+    {
+        struct irq_desc *desc = irq_to_desc(irq);
+        init_one_irq_desc(desc);
+        desc->irq = irq;
+        desc->action  = NULL;
+    }
+#endif
     return 0;
 }
 
@@ -208,7 +226,7 @@ int request_irq(unsigned int irq, unsigned int irqflags,
      * which interrupt is which (messes up the interrupt freeing
      * logic etc).
      */
-    if ( irq >= nr_irqs )
+    if ( irq >= nr_irqs && !is_lpi(irq) )
         return -EINVAL;
     if ( !handler )
         return -EINVAL;
@@ -267,9 +285,14 @@ void do_IRQ(struct cpu_user_regs *regs, unsigned int irq, 
int is_fiq)
         set_bit(_IRQ_INPROGRESS, &desc->status);
         desc->arch.eoi_cpu = smp_processor_id();
 
+#ifdef CONFIG_ARM_64
+        if ( is_lpi(irq) )
+            vgic_vcpu_inject_lpi(info->d, irq);
+        else
+#endif
         /* the irq cannot be a PPI, we only support delivery of SPIs to
          * guests */
-        vgic_vcpu_inject_spi(info->d, info->virq);
+            vgic_vcpu_inject_spi(info->d, info->virq);
         goto out_no_end;
     }
 
@@ -436,7 +459,8 @@ err:
 bool_t is_assignable_irq(unsigned int irq)
 {
     /* For now, we can only route SPIs to the guest */
-    return ((irq >= NR_LOCAL_IRQS) && (irq < gic_number_lines()));
+    return (((irq >= NR_LOCAL_IRQS) && (irq < gic_number_lines())) ||
+              is_lpi(irq));
 }
 
 /*
@@ -452,7 +476,7 @@ int route_irq_to_guest(struct domain *d, unsigned int virq,
     unsigned long flags;
     int retval = 0;
 
-    if ( virq >= vgic_num_irqs(d) )
+    if ( virq >= vgic_num_irqs(d) && !is_lpi(irq) )
     {
         printk(XENLOG_G_ERR
                "the vIRQ number %u is too high for domain %u (max = %u)\n",
@@ -460,10 +484,10 @@ int route_irq_to_guest(struct domain *d, unsigned int 
virq,
         return -EINVAL;
     }
 
-    /* Only routing to virtual SPIs is supported */
+    /* Only routing to virtual SPIs/LPIs is supported */
     if ( virq < NR_LOCAL_IRQS )
     {
-        printk(XENLOG_G_ERR "IRQ can only be routed to an SPI\n");
+        printk(XENLOG_G_ERR "IRQ can only be routed to an SPI/LPI\n");
         return -EINVAL;
     }
 
@@ -537,8 +561,10 @@ int route_irq_to_guest(struct domain *d, unsigned int virq,
     retval = __setup_irq(desc, 0, action);
     if ( retval )
         goto out;
-
-    retval = gic_route_irq_to_guest(d, virq, desc, GIC_PRI_IRQ);
+    if ( is_lpi(irq) )
+        retval = gic_route_lpi_to_guest(d, virq, desc, GIC_PRI_IRQ);
+    else
+        retval = gic_route_irq_to_guest(d, virq, desc, GIC_PRI_IRQ);
 
     spin_unlock_irqrestore(&desc->lock, flags);
 
diff --git a/xen/arch/arm/vgic-v3-its.c b/xen/arch/arm/vgic-v3-its.c
index bbcc7bb..4649b07 100644
--- a/xen/arch/arm/vgic-v3-its.c
+++ b/xen/arch/arm/vgic-v3-its.c
@@ -625,6 +625,15 @@ err:
     return 0;
 }
 
+uint8_t vgic_its_get_priority(struct vcpu *v, uint32_t pid)
+{
+    uint8_t priority;
+
+    priority =  readb_relaxed(v->domain->arch.vits->prop_page + pid);
+    priority &= LPI_PRIORITY_MASK;
+
+    return priority;
+}
 static int vgic_v3_gits_lpi_mmio_read(struct vcpu *v, mmio_info_t *info)
 {
     uint32_t offset;
diff --git a/xen/arch/arm/vgic-v3.c b/xen/arch/arm/vgic-v3.c
index 25b69a0..4e14439 100644
--- a/xen/arch/arm/vgic-v3.c
+++ b/xen/arch/arm/vgic-v3.c
@@ -1111,12 +1111,19 @@ static const struct mmio_handler_ops 
vgic_distr_mmio_handler = {
 
 static int vgic_v3_get_irq_priority(struct vcpu *v, unsigned int irq)
 {
-    int priority;
-    struct vgic_irq_rank *rank = vgic_rank_irq(v, irq);
+    int priority = 0;
+    struct vgic_irq_rank *rank;
 
-    ASSERT(spin_is_locked(&rank->lock));
-    priority = vgic_byte_read(rank->ipriority[REG_RANK_INDEX(8,
+    if ( !is_lpi(irq) )
+    {
+        rank = vgic_rank_irq(v, irq);
+
+        ASSERT(spin_is_locked(&rank->lock));
+        priority = vgic_byte_read(rank->ipriority[REG_RANK_INDEX(8,
                                               irq, DABT_WORD)], 0, irq & 0x3);
+    }
+    if ( is_lpi(irq) && gic_lpi_supported() )
+        priority = vgic_its_get_priority(v, irq);
 
     return priority;
 }
diff --git a/xen/arch/arm/vgic.c b/xen/arch/arm/vgic.c
index a5f66f6..8190a46 100644
--- a/xen/arch/arm/vgic.c
+++ b/xen/arch/arm/vgic.c
@@ -30,6 +30,7 @@
 
 #include <asm/mmio.h>
 #include <asm/gic.h>
+#include <asm/gic-its.h>
 #include <asm/vgic.h>
 
 static inline struct vgic_irq_rank *vgic_get_rank(struct vcpu *v, int rank)
@@ -111,6 +112,15 @@ int domain_vgic_init(struct domain *d, unsigned int 
nr_spis)
     for (i=0; i<d->arch.vgic.nr_spis; i++)
         vgic_init_pending_irq(&d->arch.vgic.pending_irqs[i], i + 32);
 
+#ifdef CONFIG_ARM_64
+    d->arch.vgic.pending_lpis = xzalloc_array(struct pending_irq, NR_GIC_LPI);
+    if ( d->arch.vgic.pending_lpis == NULL )
+        return -ENOMEM;
+
+    for ( i = 0; i < NR_GIC_LPI; i++ )
+        vgic_init_pending_irq(&d->arch.vgic.pending_lpis[i], i);
+#endif
+
     for (i=0; i<DOMAIN_NR_RANKS(d); i++)
         spin_lock_init(&d->arch.vgic.shared_irqs[i].lock);
 
@@ -157,6 +167,7 @@ void domain_vgic_free(struct domain *d)
 #ifdef CONFIG_ARM_64
     free_xenheap_pages(d->arch.vits->prop_page,
                        get_order_from_bytes(d->arch.vits->prop_size));
+    xfree(d->arch.vgic.pending_lpis);
 #endif
 }
 
@@ -381,13 +392,17 @@ int vgic_to_sgi(struct vcpu *v, register_t sgir, enum 
gic_sgi_mode irqmode, int
 
 struct pending_irq *irq_to_pending(struct vcpu *v, unsigned int irq)
 {
-    struct pending_irq *n;
+    struct pending_irq *n = NULL;
     /* Pending irqs allocation strategy: the first vgic.nr_spis irqs
      * are used for SPIs; the rests are used for per cpu irqs */
     if ( irq < 32 )
         n = &v->arch.vgic.pending_irqs[irq];
-    else
+    else if ( irq < NR_IRQS )
         n = &v->domain->arch.vgic.pending_irqs[irq - 32];
+#ifdef CONFIG_ARM_64
+    else if ( is_lpi(irq) )
+        n = &v->domain->arch.vgic.pending_lpis[irq - FIRST_GIC_LPI];
+#endif
     return n;
 }
 
@@ -413,14 +428,20 @@ void vgic_clear_pending_irqs(struct vcpu *v)
 void vgic_vcpu_inject_irq(struct vcpu *v, unsigned int virq)
 {
     uint8_t priority;
-    struct vgic_irq_rank *rank = vgic_rank_irq(v, virq);
+    struct vgic_irq_rank *rank;
     struct pending_irq *iter, *n = irq_to_pending(v, virq);
     unsigned long flags;
     bool_t running;
 
-    vgic_lock_rank(v, rank, flags);
-    priority = v->domain->arch.vgic.handler->get_irq_priority(v, virq);
-    vgic_unlock_rank(v, rank, flags);
+    if ( virq < NR_GIC_LPI )
+    {
+        rank = vgic_rank_irq(v, virq);
+        vgic_lock_rank(v, rank, flags);
+        priority = v->domain->arch.vgic.handler->get_irq_priority(v, virq);
+        vgic_unlock_rank(v, rank, flags);
+    }
+    else
+        priority = v->domain->arch.vgic.handler->get_irq_priority(v, virq);
 
     spin_lock_irqsave(&v->arch.vgic.lock, flags);
 
@@ -477,6 +498,83 @@ void vgic_vcpu_inject_spi(struct domain *d, unsigned int 
virq)
     vgic_vcpu_inject_irq(v, virq);
 }
 
+#ifdef CONFIG_ARM_64
+void vgic_vcpu_inject_lpi(struct domain *d, unsigned int irq)
+{
+    struct irq_desc *desc;
+    struct pending_irq *p;
+    struct its_device *dev;
+    struct vits_device *vdev;
+    struct vdevice_table dt_entry;
+    struct vitt vitt_entry;
+    uint32_t devid, col_id;
+    int event;
+
+    desc = irq_to_desc(irq);
+    event =  irq_to_virq(desc);
+
+    dev = get_irq_device(desc);
+    devid = dev->virt_device_id;
+    event = irq - dev->lpi_base;
+    if ( (event < 0  && event > dev->nr_lpis) ||
+         (dev->domain_id != d->domain_id) )
+    {
+        dprintk(XENLOG_WARNING,
+               "LPI %d received for dev 0x%x is not assigned..dropping\n",
+               irq, devid);
+        return;
+    }
+
+    vdev = vits_find_device(&d->arch.vits->dev_root, devid);
+    if ( !vdev )
+    {
+        dprintk(XENLOG_WARNING,
+                "LPI %d received for dev 0x%x not assigned..dropping\n",
+                irq, devid);
+        return;
+    }
+
+    if ( vits_get_vdevice_entry(d, devid, &dt_entry) )
+    {
+        dprintk(XENLOG_WARNING,
+                "Failed to read device table entry for dev 0x%x ..dropping\n",
+                devid);
+        return;
+    }
+    if ( dt_entry.vitt_ipa == INVALID_PADDR )
+    {
+        dprintk(XENLOG_WARNING,
+                "LPI %d received for dev 0x%x which is disabled..dropping\n",
+                irq, devid);
+        return;
+    }
+
+    if ( vits_get_vitt_entry(d, devid, event, &vitt_entry) )
+    {
+        dprintk(XENLOG_WARNING,
+                "LPI %d received for dev 0x%x with invalid entry..dropping\n",
+                irq, devid);
+        return;
+    }
+
+    col_id = vitt_entry.vcollection;
+
+    if ( !vitt_entry.valid || col_id > d->max_vcpus ||
+         vitt_entry.vlpi > its_get_nr_events() )
+    {
+        dprintk(XENLOG_WARNING,
+                "LPI %d received for dev 0x%x is not valid..dropping\n",
+                irq, devid);
+        return;
+    }
+
+    p = irq_to_pending(d->vcpu[0], vitt_entry.vlpi);
+    p->desc = desc;
+
+    vgic_vcpu_inject_irq(d->vcpu[col_id], vitt_entry.vlpi);
+}
+#endif
+
 void arch_evtchn_inject(struct vcpu *v)
 {
     vgic_vcpu_inject_irq(v, v->domain->arch.evtchn_irq);
diff --git a/xen/include/asm-arm/domain.h b/xen/include/asm-arm/domain.h
index 49db7f0..a4bf8f6 100644
--- a/xen/include/asm-arm/domain.h
+++ b/xen/include/asm-arm/domain.h
@@ -98,6 +98,9 @@ struct arch_domain
          * struct arch_vcpu.
          */
         struct pending_irq *pending_irqs;
+#ifdef CONFIG_ARM_64
+        struct pending_irq *pending_lpis;
+#endif
         /* Base address for guest GIC */
         paddr_t dbase; /* Distributor base address */
         paddr_t cbase; /* CPU base address */
diff --git a/xen/include/asm-arm/gic-its.h b/xen/include/asm-arm/gic-its.h
index a79b70f..fbed905 100644
--- a/xen/include/asm-arm/gic-its.h
+++ b/xen/include/asm-arm/gic-its.h
@@ -277,6 +277,7 @@ struct vits_device *vits_find_device(struct rb_root *root, 
uint32_t devid);
 int vits_insert_device(struct rb_root *root, struct vits_device *dev);
 void vits_remove_device(struct rb_root *root, struct vits_device *dev);
 int vits_unmap_lpi_prop(struct vcpu *v);
+uint8_t vgic_its_get_priority(struct vcpu *v, uint32_t pid);
 
 #endif /* __ASM_ARM_GIC_ITS_H__ */
 /*
diff --git a/xen/include/asm-arm/gic.h b/xen/include/asm-arm/gic.h
index 69bf1ff..537ed3d 100644
--- a/xen/include/asm-arm/gic.h
+++ b/xen/include/asm-arm/gic.h
@@ -226,6 +226,9 @@ extern void gic_route_irq_to_xen(struct irq_desc *desc, 
const cpumask_t *cpu_mas
 extern int gic_route_irq_to_guest(struct domain *, unsigned int virq,
                                   struct irq_desc *desc,
                                   unsigned int priority);
+extern int gic_route_lpi_to_guest(struct domain *d, unsigned int virq,
+                                   struct irq_desc *desc,
+                                   unsigned int priority);
 
 /* Remove an IRQ passthrough to a guest */
 int gic_remove_irq_from_guest(struct domain *d, unsigned int virq,
@@ -282,8 +285,10 @@ extern void send_SGI_allbutself(enum gic_sgi sgi);
 /* print useful debug info */
 extern void gic_dump_info(struct vcpu *v);
 
-/* Number of interrupt lines */
+/* Number of interrupt lines (SPIs)*/
 extern unsigned int gic_number_lines(void);
+/* Check if irq is valid SPI or LPI */
+bool_t gic_is_valid_irq(unsigned int irq);
 /* Number of interrupt id bits supported */
 extern unsigned int gic_nr_id_bits(void);
 /* LPI support info */
diff --git a/xen/include/asm-arm/vgic.h b/xen/include/asm-arm/vgic.h
index 8d22532..f8928ab 100644
--- a/xen/include/asm-arm/vgic.h
+++ b/xen/include/asm-arm/vgic.h
@@ -183,6 +183,7 @@ extern int vcpu_vgic_init(struct vcpu *v);
 extern struct vcpu *vgic_get_target_vcpu(struct vcpu *v, unsigned int irq);
 extern void vgic_vcpu_inject_irq(struct vcpu *v, unsigned int virq);
 extern void vgic_vcpu_inject_spi(struct domain *d, unsigned int virq);
+extern void vgic_vcpu_inject_lpi(struct domain *d, unsigned int virq);
 extern void vgic_clear_pending_irqs(struct vcpu *v);
 extern struct pending_irq *irq_to_pending(struct vcpu *v, unsigned int irq);
 extern struct pending_irq *spi_to_pending(struct domain *d, unsigned int irq);
-- 
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®.