diff --git a/xen/arch/arm/vgic.c b/xen/arch/arm/vgic.c index 0ba13e18da..acded6a40f 100644 --- a/xen/arch/arm/vgic.c +++ b/xen/arch/arm/vgic.c @@ -25,6 +25,21 @@ #include +/* + * The allocated_irqs bitmap is compressed: eSPI bits immediately follow + * regular IRQ bits, skipping the gap in the INTID space. + * + * +-----------+-----------+-------------------+-------------------+ + * | SGIs | PPIs | SPIs | eSPIs | + * +-----------+-----------+-------------------+-------------------+ + * 0 16 32 vgic_num_irqs(d) + * + * INTID ESPI_BASE_INTID maps to bitmap index vgic_num_irqs(d). + * The following idx_to_virq() and virq_to_idx() convert between INTIDs + * and bitmap indexes. + * + * See also the allocated_irqs comment in struct vgic_dist. + */ static inline unsigned int idx_to_virq(struct domain *d, unsigned int idx) { if ( idx >= vgic_num_irqs(d) ) @@ -33,6 +48,16 @@ static inline unsigned int idx_to_virq(struct domain *d, unsigned int idx) return idx; } +static inline unsigned int virq_to_idx(struct domain *d, unsigned int virq) +{ + ASSERT(IS_ENABLED(CONFIG_GICV3_ESPI) || !is_espi(virq)); + + if ( IS_ENABLED(CONFIG_GICV3_ESPI) && is_espi(virq) ) + return espi_intid_to_idx(virq) + vgic_num_irqs(d); + + return virq; +} + bool vgic_is_valid_line(struct domain *d, unsigned int virq) { #ifdef CONFIG_GICV3_ESPI @@ -854,19 +879,11 @@ bool vgic_emulate(struct cpu_user_regs *regs, union hsr hsr) bool vgic_reserve_virq(struct domain *d, unsigned int virq) { - unsigned int idx = virq; - if ( !vgic_is_valid_line(d, virq) ) return false; - if ( is_espi(virq) ) - { - unsigned int num_regular_irqs = vgic_num_irqs(d); - - idx = espi_intid_to_idx(virq) + num_regular_irqs; - } - - return !test_and_set_bit(idx, d->arch.vgic.allocated_irqs); + return !test_and_set_bit(virq_to_idx(d, virq), + d->arch.vgic.allocated_irqs); } int vgic_allocate_virq(struct domain *d, bool spi) @@ -903,7 +920,10 @@ int vgic_allocate_virq(struct domain *d, bool spi) void vgic_free_virq(struct domain *d, unsigned int virq) { - clear_bit(virq, d->arch.vgic.allocated_irqs); + if ( !vgic_is_valid_line(d, virq) ) + return; + + clear_bit(virq_to_idx(d, virq), d->arch.vgic.allocated_irqs); } unsigned int vgic_max_vcpus(unsigned int domctl_vgic_version) diff --git a/xen/arch/arm/vgic/vgic.c b/xen/arch/arm/vgic/vgic.c index ba029b8a3b..84212bbefe 100644 --- a/xen/arch/arm/vgic/vgic.c +++ b/xen/arch/arm/vgic/vgic.c @@ -712,7 +712,7 @@ bool vgic_evtchn_irq_pending(struct vcpu *v) bool vgic_reserve_virq(struct domain *d, unsigned int virq) { - if ( virq >= vgic_num_irqs(d) ) + if ( !vgic_is_valid_line(d, virq) ) return false; return !test_and_set_bit(virq, d->arch.vgic.allocated_irqs); @@ -756,6 +756,9 @@ int vgic_allocate_virq(struct domain *d, bool spi) void vgic_free_virq(struct domain *d, unsigned int virq) { + if ( !vgic_is_valid_line(d, virq) ) + return; + clear_bit(virq, d->arch.vgic.allocated_irqs); }