[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-devel] [PATCH v2 5/8] xen: arm: add interfaces to save/restore the state of a PPI.
Make use of the GICD I[SC]ACTIVER registers to save and restore the active state of the interrupt. For edge triggered interrupts we also need to context switch the pending bit via I[SC]PENDR. Note that for level triggered interrupts SPENDR sets a latch which is only cleared by ICPENDR (and not by h/w state changes), therefore we do not want to context switch the pending state for level PPIs -- instead we rely on the context switch of the peripheral to restore the correct level. Tested on GIC v2 only. Unused as yet, will be used by the vtimer code shortly. Signed-off-by: Ian Campbell <ian.campbell@xxxxxxxxxx> --- xen/arch/arm/gic-v2.c | 67 ++++++++++++++++++++++++++++++++++++++++++++ xen/arch/arm/gic-v3.c | 67 ++++++++++++++++++++++++++++++++++++++++++++ xen/arch/arm/gic.c | 54 +++++++++++++++++++++++++++++++++++ xen/arch/arm/irq.c | 7 +++++ xen/include/asm-arm/domain.h | 11 ++++++++ xen/include/asm-arm/gic.h | 16 +++++++++++ xen/include/asm-arm/irq.h | 3 ++ 7 files changed, 225 insertions(+) diff --git a/xen/arch/arm/gic-v2.c b/xen/arch/arm/gic-v2.c index 01e36b5..5308c35 100644 --- a/xen/arch/arm/gic-v2.c +++ b/xen/arch/arm/gic-v2.c @@ -81,6 +81,9 @@ static DEFINE_PER_CPU(u8, gic_cpu_id); /* Maximum cpu interface per GIC */ #define NR_GIC_CPU_IF 8 +static void gicv2_irq_enable(struct irq_desc *desc); +static void gicv2_irq_disable(struct irq_desc *desc); + static inline void writeb_gicd(uint8_t val, unsigned int offset) { writeb_relaxed(val, gicv2.map_dbase + offset); @@ -149,6 +152,37 @@ static void gicv2_save_state(struct vcpu *v) writel_gich(0, GICH_HCR); } +static void gicv2_save_and_mask_hwppi(struct irq_desc *desc, + struct hwppi_state *s) +{ + const unsigned int mask = (1u << desc->irq); + const unsigned int pendingr = readl_gicd(GICD_ISPENDR); + const unsigned int activer = readl_gicd(GICD_ISACTIVER); + const unsigned int enabler = readl_gicd(GICD_ISENABLER); + const bool is_edge = !!(desc->arch.type & DT_IRQ_TYPE_EDGE_BOTH); + + s->active = !!(activer & mask); + s->enabled = !!(enabler & mask); + s->pending = !!(pendingr & mask); + + /* Write a 1 to IC...R to clear the corresponding bit of state */ + if ( s->active ) + writel_gicd(mask, GICD_ICACTIVER); + /* + * For an edge interrupt clear the pending state, for a level interrupt + * this clears the latch there is no need since saving the peripheral state + * (and/or restoring the next VCPU) will cause the correct action. + */ + if ( is_edge && s->pending ) + writel_gicd(mask, GICD_ICPENDR); + + if ( s->enabled ) + gicv2_irq_disable(desc); + + ASSERT(!(readl_gicd(GICD_ISACTIVER) & mask)); + ASSERT(!(readl_gicd(GICD_ISENABLER) & mask)); +} + static void gicv2_restore_state(const struct vcpu *v) { int i; @@ -161,6 +195,37 @@ static void gicv2_restore_state(const struct vcpu *v) writel_gich(GICH_HCR_EN, GICH_HCR); } +static void gicv2_restore_hwppi(struct irq_desc *desc, + const struct hwppi_state *s) +{ + const unsigned int mask = (1u << desc->irq); + const bool is_edge = !!(desc->arch.type & DT_IRQ_TYPE_EDGE_BOTH); + + /* + * The IRQ must always have been set inactive and masked etc by + * the saving of the previous state via save_and_mask_hwppi. + */ + ASSERT(!(readl_gicd(GICD_ISACTIVER) & mask)); + ASSERT(!(readl_gicd(GICD_ISENABLER) & mask)); + + if ( s->active ) + writel_gicd(mask, GICD_ICACTIVER); + + /* + * Restore pending state for edge triggered interrupts only. For + * level triggered interrupts the level will be restored as + * necessary by restoring the state of the relevant peripheral. + * + * For a level triggered interrupt ISPENDR acts as a *latch* which + * is only cleared by ICPENDR (i.e. the input level is no longer + * relevant). We certainly do not want that here. + */ + if ( is_edge && s->pending ) + writel_gicd(mask, GICD_ISPENDR); + if ( s->enabled ) + gicv2_irq_enable(desc); +} + static void gicv2_dump_state(const struct vcpu *v) { int i; @@ -744,7 +809,9 @@ const static struct gic_hw_operations gicv2_ops = { .init = gicv2_init, .secondary_init = gicv2_secondary_cpu_init, .save_state = gicv2_save_state, + .save_and_mask_hwppi = gicv2_save_and_mask_hwppi, .restore_state = gicv2_restore_state, + .restore_hwppi = gicv2_restore_hwppi, .dump_state = gicv2_dump_state, .gic_host_irq_type = &gicv2_host_irq_type, .gic_guest_irq_type = &gicv2_guest_irq_type, diff --git a/xen/arch/arm/gic-v3.c b/xen/arch/arm/gic-v3.c index 4fe0c37..cfe705a 100644 --- a/xen/arch/arm/gic-v3.c +++ b/xen/arch/arm/gic-v3.c @@ -61,6 +61,9 @@ static DEFINE_PER_CPU(void __iomem*, rbase); #define GICD_RDIST_BASE (this_cpu(rbase)) #define GICD_RDIST_SGI_BASE (GICD_RDIST_BASE + SZ_64K) +void gicv3_irq_enable(struct irq_desc *desc); +void gicv3_irq_disable(struct irq_desc *desc); + /* * Saves all 16(Max) LR registers. Though number of LRs implemented * is implementation specific. @@ -373,6 +376,37 @@ static void gicv3_save_state(struct vcpu *v) v->arch.gic.v3.sre_el1 = READ_SYSREG32(ICC_SRE_EL1); } +static void gicv3_save_and_mask_hwppi(struct irq_desc *desc, + struct hwppi_state *s) +{ + const unsigned int mask = (1u << desc->irq); + const unsigned int pendingr = readl_relaxed(GICD_RDIST_SGI_BASE + GICR_ISPENDR); + const unsigned int activer = readl_gicd(GICD_RDIST_SGI_BASE + GICR_ISACTIVER); + const unsigned int enabler = readl_gicd(GICD_RDIST_SGI_BASE + GICR_ISENABLER); + const bool is_edge = !!(desc->arch.type & DT_IRQ_TYPE_EDGE_BOTH); + + s->active = !!(activer & mask); + s->enabled = !!(enabler & mask); + s->pending = !!(pendingr & mask); + + /* Write a 1 to IC...R to clear the corresponding bit of state */ + if ( s->active ) + writel_relaxed(mask, GICD_RDIST_SGI_BASE + GICR_ICACTIVER); + /* + * For an edge interrupt clear the pending state, for a level interrupt + * this clears the latch there is no need since saving the peripheral state + * (and/or restoring the next VCPU) will cause the correct action. + */ + if ( is_edge && s->pending ) + writel_relaxed(mask, GICD_RDIST_SGI_BASE + GICR_ICPENDR); + + if ( s->enabled ) + gicv3_irq_disable(desc); + + ASSERT(!(readl_relaxed(GICD_RDIST_SGI_BASE + GICR_ISACTIVER) & mask)); + ASSERT(!(readl_relaxed(GICD_RDIST_SGI_BASE + GICR_ISENABLER) & mask)); +} + static void gicv3_restore_state(const struct vcpu *v) { uint32_t val; @@ -399,6 +433,37 @@ static void gicv3_restore_state(const struct vcpu *v) dsb(sy); } +static void gicv3_restore_hwppi(struct irq_desc *desc, + const struct hwppi_state *s) +{ + const unsigned int mask = (1u << desc->irq); + const bool is_edge = !!(desc->arch.type & DT_IRQ_TYPE_EDGE_BOTH); + + /* + * The IRQ must always have been set inactive and masked etc by + * the saving of the previous state via save_and_mask_hwppi. + */ + ASSERT(!(readl_relaxed(GICD_RDIST_SGI_BASE + GICR_ISACTIVER_ISACTIVER) & mask)); + ASSERT(!(readl_relaxed(GICD_RDIST_SGI_BASE + GICR_ISACTIVER_ISENABLER) & mask)); + + if ( s->active ) + writel_relaxed(mask, GICD_RDIST_SGI_BASE + GICR_ISACTIVER_ICACTIVER); + + /* + * Restore pending state for edge triggered interrupts only. For + * level triggered interrupts the level will be restored as + * necessary by restoring the state of the relevant peripheral. + * + * For a level triggered interrupt ISPENDR acts as a *latch* which + * is only cleared by ICPENDR (i.e. the input level is no longer + * relevant). We certainly do not want that here. + */ + if ( is_edge && s->pending ) + writel_relaxed(mask, GICD_RDIST_SGI_BASE + GICR_ISACTIVER_ISPENDR); + if ( s->enabled ) + gicv3_irq_enable(desc); +} + static void gicv3_dump_state(const struct vcpu *v) { int i; @@ -1296,7 +1361,9 @@ static const struct gic_hw_operations gicv3_ops = { .info = &gicv3_info, .init = gicv3_init, .save_state = gicv3_save_state, + .save_and_mask_hwppi = gicv3_save_and_mask_hwppi, .restore_state = gicv3_restore_state, + .restore_hwppi = gicv3_restore_hwppi, .dump_state = gicv3_dump_state, .gic_host_irq_type = &gicv3_host_irq_type, .gic_guest_irq_type = &gicv3_guest_irq_type, diff --git a/xen/arch/arm/gic.c b/xen/arch/arm/gic.c index b0b0999..e294e89 100644 --- a/xen/arch/arm/gic.c +++ b/xen/arch/arm/gic.c @@ -67,6 +67,17 @@ unsigned int gic_number_lines(void) return gic_hw_ops->info->nr_lines; } +void gic_hwppi_state_init(struct hwppi_state *s, unsigned irq) +{ + memset(s, 0, sizeof(*s)); + s->irq = irq; +} + +void gic_hwppi_set_pending(struct hwppi_state *s) +{ + s->pending = true; +} + void gic_save_state(struct vcpu *v) { ASSERT(!local_irq_is_enabled()); @@ -81,6 +92,25 @@ void gic_save_state(struct vcpu *v) isb(); } +void gic_save_and_mask_hwppi(struct vcpu *v, const unsigned virq, + struct hwppi_state *s) +{ + struct pending_irq *p = irq_to_pending(v, virq); + struct irq_desc *desc = p->desc; + + spin_lock(&desc->lock); + + ASSERT(virq >= 16 && virq < 32); + ASSERT(desc->irq >= 16 && desc->irq < 32); + ASSERT(!is_idle_vcpu(v)); + + s->inprogress = test_and_clear_bit(_IRQ_INPROGRESS, &desc->status); + + gic_hw_ops->save_and_mask_hwppi(desc, s); + + spin_unlock(&desc->lock); +} + void gic_restore_state(struct vcpu *v) { ASSERT(!local_irq_is_enabled()); @@ -94,6 +124,30 @@ void gic_restore_state(struct vcpu *v) gic_restore_pending_irqs(v); } +void gic_restore_hwppi(struct vcpu *v, + const unsigned virq, + const struct hwppi_state *s) +{ + struct pending_irq *p = irq_to_pending(v, virq); + struct irq_desc *desc = irq_to_desc(s->irq); + + spin_lock(&desc->lock); + + ASSERT(virq >= 16 && virq < 32); + ASSERT(!is_idle_vcpu(v)); + + p->desc = desc; /* Migrate to new physical processor */ + + irq_set_virq(desc, virq); + + gic_hw_ops->restore_hwppi(desc, s); + + if ( s->inprogress ) + set_bit(_IRQ_INPROGRESS, &desc->status); + + spin_unlock(&desc->lock); +} + /* * needs to be called with a valid cpu_mask, ie each cpu in the mask has * already called gic_cpu_init diff --git a/xen/arch/arm/irq.c b/xen/arch/arm/irq.c index 5b073d1..95be10e 100644 --- a/xen/arch/arm/irq.c +++ b/xen/arch/arm/irq.c @@ -138,6 +138,13 @@ static inline struct irq_guest *irq_get_guest_info(struct irq_desc *desc) return desc->action->dev_id; } +void irq_set_virq(struct irq_desc *desc, unsigned virq) +{ + struct irq_guest *info = irq_get_guest_info(desc); + ASSERT(test_bit(_IRQ_PER_CPU, &desc->status)); + info->virq = virq; +} + static inline struct domain *irq_get_domain(struct irq_desc *desc) { return irq_get_guest_info(desc)->d; diff --git a/xen/include/asm-arm/domain.h b/xen/include/asm-arm/domain.h index c56f06e..550e28b 100644 --- a/xen/include/asm-arm/domain.h +++ b/xen/include/asm-arm/domain.h @@ -34,6 +34,17 @@ enum domain_type { extern int dom0_11_mapping; #define is_domain_direct_mapped(d) ((d) == hardware_domain && dom0_11_mapping) +struct hwppi_state { + /* h/w state */ + unsigned irq; + unsigned long enabled:1; + unsigned long pending:1; + unsigned long active:1; + + /* Xen s/w state */ + unsigned long inprogress:1; +}; + struct vtimer { struct vcpu *v; int irq; diff --git a/xen/include/asm-arm/gic.h b/xen/include/asm-arm/gic.h index 0116481..fe9af3e 100644 --- a/xen/include/asm-arm/gic.h +++ b/xen/include/asm-arm/gic.h @@ -258,6 +258,20 @@ extern int gicv_setup(struct domain *d); extern void gic_save_state(struct vcpu *v); extern void gic_restore_state(struct vcpu *v); +/* + * Save/restore the state of a single PPI which must be routed to + * <current-vcpu> (that is, is defined to be injected to the current + * vcpu). + */ +struct hwppi_state; +extern void gic_hwppi_state_init(struct hwppi_state *s, unsigned irq); +extern void gic_hwppi_set_pending(struct hwppi_state *s); +extern void gic_save_and_mask_hwppi(struct vcpu *v, unsigned irq, + struct hwppi_state *s); +extern void gic_restore_hwppi(struct vcpu *v, + const unsigned virq, + const struct hwppi_state *s); + /* SGI (AKA IPIs) */ enum gic_sgi { GIC_SGI_EVENT_CHECK = 0, @@ -308,8 +322,10 @@ struct gic_hw_operations { int (*init)(void); /* Save GIC registers */ void (*save_state)(struct vcpu *); + void (*save_and_mask_hwppi)(struct irq_desc *desc, struct hwppi_state *s); /* Restore GIC registers */ void (*restore_state)(const struct vcpu *); + void (*restore_hwppi)(struct irq_desc *desc, const struct hwppi_state *s); /* Dump GIC LR register information */ void (*dump_state)(const struct vcpu *); diff --git a/xen/include/asm-arm/irq.h b/xen/include/asm-arm/irq.h index f33c331..d354e3b 100644 --- a/xen/include/asm-arm/irq.h +++ b/xen/include/asm-arm/irq.h @@ -43,6 +43,7 @@ void init_secondary_IRQ(void); int route_irq_to_guest(struct domain *d, unsigned int virq, unsigned int irq, const char *devname); +int route_irq_to_current_vcpu(unsigned int irq, const char *devname); int release_guest_irq(struct domain *d, unsigned int irq); void arch_move_irqs(struct vcpu *v); @@ -56,6 +57,8 @@ int platform_get_irq(const struct dt_device_node *device, int index); void irq_set_affinity(struct irq_desc *desc, const cpumask_t *cpu_mask); +void irq_set_virq(struct irq_desc *desc, unsigned virq); + #endif /* _ASM_HW_IRQ_H */ /* * Local variables: -- 2.1.4 _______________________________________________ Xen-devel mailing list Xen-devel@xxxxxxxxxxxxx http://lists.xen.org/xen-devel
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |