Hello Oleksandr,

Thank you for your close review.

On 23.08.25 17:39, Oleksandr Tyshchenko wrote:
> 
> 
> On 07.08.25 15:33, Leonid Komarianskyi wrote:
> 
> 
> Hello Leonid
> 
>> This change introduces resource management in the VGIC to handle
>> extended SPIs introduced in GICv3.1. The pending_irqs and
>> allocated_irqs arrays are resized to support the required
>> number of eSPIs, based on what is supported by the hardware and
>> requested by the guest. A new field, ext_shared_irqs, is added
>> to the VGIC structure to store information about eSPIs, similar
>> to how shared_irqs is used for regular SPIs.
>>
>> Since the eSPI range starts at INTID 4096 and INTIDs between 1025
>> and 4095 are reserved, helper macros are introduced to simplify the
>> transformation of indices and to enable easier access to eSPI-specific
>> resources. These changes prepare the VGIC for processing eSPIs as
>> required by future functionality.
>>
>> The initialization and deinitialization paths for vgic have been updated
>> to allocate and free these resources appropriately. Additionally,
>> updated handling of INTIDs greater than 1024, passed from the toolstack
>> during domain creation, and verification logic ensures only valid SPI or
>> eSPI INTIDs are used.
>>
>> The existing SPI behavior remains unaffected when guests do not request
>> eSPIs, GIC hardware does not support them, or the CONFIG_GICV3_ESPI
>> option is disabled.
>>
>> Signed-off-by: Leonid Komarianskyi <leonid_komarians...@epam.com>
>>
>> ---
>> Changes in V2:
>> - change is_espi_rank to is_valid_espi_rank to verify whether the array
>>    element ext_shared_irqs exists. The previous version, is_espi_rank,
>>    only checked if the rank index was less than the maximum possible eSPI
>>    rank index, but this could potentially result in accessing a
>>    non-existing array element. To address this, is_valid_espi_rank was
>>    introduced, which ensures that the required eSPI rank exists
>> - move gic_number_espis to
>>    xen/arm: gicv3: implement handling of GICv3.1 eSPI
>> - update vgic_is_valid_irq checks to allow operating with eSPIs
>> - remove redundant newline in vgic_allocate_virq
>> ---
>>   xen/arch/arm/include/asm/vgic.h |  18 ++++
>>   xen/arch/arm/vgic.c             | 145 ++++++++++++++++++++++++++++++++
>>   2 files changed, 163 insertions(+)
>>
>> diff --git a/xen/arch/arm/include/asm/vgic.h b/xen/arch/arm/include/ 
>> asm/vgic.h
>> index 45201f4ca5..9fa4523018 100644
>> --- a/xen/arch/arm/include/asm/vgic.h
>> +++ b/xen/arch/arm/include/asm/vgic.h
>> @@ -146,6 +146,10 @@ struct vgic_dist {
>>       int nr_spis; /* Number of SPIs */
>>       unsigned long *allocated_irqs; /* bitmap of IRQs allocated */
>>       struct vgic_irq_rank *shared_irqs;
>> +#ifdef CONFIG_GICV3_ESPI
>> +    struct vgic_irq_rank *ext_shared_irqs;
>> +    int nr_espis; /* Number of extended SPIs */
>> +#endif
>>       /*
>>        * SPIs are domain global, SGIs and PPIs are per-VCPU and stored in
>>        * struct arch_vcpu.
>> @@ -243,6 +247,14 @@ struct vgic_ops {
>>   /* Number of ranks of interrupt registers for a domain */
>>   #define DOMAIN_NR_RANKS(d) (((d)->arch.vgic.nr_spis+31)/32)
>> +#ifdef CONFIG_GICV3_ESPI
>> +#define DOMAIN_NR_EXT_RANKS(d) (((d)->arch.vgic.nr_espis+31)/32)
>> +#define EXT_RANK_MIN (ESPI_BASE_INTID/32)
>> +#define EXT_RANK_MAX ((ESPI_MAX_INTID+31)/32)
>> +#define EXT_RANK_NUM2IDX(num) ((num)-EXT_RANK_MIN)
>> +#define EXT_RANK_IDX2NUM(idx) ((idx)+EXT_RANK_MIN)
>> +#endif
>> +
>>   #define vgic_lock(v)   spin_lock_irq(&(v)->domain->arch.vgic.lock)
>>   #define vgic_unlock(v) spin_unlock_irq(&(v)->domain->arch.vgic.lock)
>> @@ -302,6 +314,12 @@ extern struct vgic_irq_rank 
>> *vgic_rank_offset(struct vcpu *v,
>>                                                 unsigned int b,
>>                                                 unsigned int n,
>>                                                 unsigned int s);
>> +#ifdef CONFIG_GICV3_ESPI
>> +extern struct vgic_irq_rank *vgic_ext_rank_offset(struct vcpu *v,
>> +                                                  unsigned int b,
>> +                                                  unsigned int n,
>> +                                                  unsigned int s);
>> +#endif
>>   extern struct vgic_irq_rank *vgic_rank_irq(struct vcpu *v, unsigned 
>> int irq);
>>   extern void vgic_disable_irqs(struct vcpu *v, uint32_t r, unsigned 
>> int n);
>>   extern void vgic_enable_irqs(struct vcpu *v, uint32_t r, unsigned 
>> int n);
>> diff --git a/xen/arch/arm/vgic.c b/xen/arch/arm/vgic.c
>> index 48fbaf56fb..1a6c765af9 100644
>> --- a/xen/arch/arm/vgic.c
>> +++ b/xen/arch/arm/vgic.c
>> @@ -27,9 +27,26 @@
>>   bool vgic_is_valid_irq(struct domain *d, unsigned int virq)
>>   {
>> +#ifdef CONFIG_GICV3_ESPI
>> +    if ( virq >= ESPI_BASE_INTID && virq < ESPI_IDX2INTID(d- 
>> >arch.vgic.nr_espis) )
>> +        return true;
>> +#endif
>> +
>>       return virq < vgic_num_irqs(d);
>>   }
>> +#ifdef CONFIG_GICV3_ESPI
>> +/*
>> + * Since eSPI indexes start from 4096 and numbers from 1024 to
>> + * 4095 are forbidden, we need to check both lower and upper
>> + * limits for ranks.
>> + */
>> +static inline bool is_valid_espi_rank(struct domain *d, unsigned int 
>> rank)
>> +{
>> +    return ( rank >= EXT_RANK_MIN && EXT_RANK_NUM2IDX(rank) < 
>> DOMAIN_NR_EXT_RANKS(d) );
>> +}
>> +#endif
>> +
>>   static inline struct vgic_irq_rank *vgic_get_rank(struct vcpu *v,
>>                                                     unsigned int rank)
>>   {
>> @@ -37,6 +54,10 @@ static inline struct vgic_irq_rank 
>> *vgic_get_rank(struct vcpu *v,
>>           return v->arch.vgic.private_irqs;
>>       else if ( rank <= DOMAIN_NR_RANKS(v->domain) )
>>           return &v->domain->arch.vgic.shared_irqs[rank - 1];
>> +#ifdef CONFIG_GICV3_ESPI
>> +    else if ( is_valid_espi_rank(v->domain, rank) )
>> +        return &v->domain- 
>> >arch.vgic.ext_shared_irqs[EXT_RANK_NUM2IDX(rank)];
>> +#endif
>>       else
>>           return NULL;
>>   }
>> @@ -53,6 +74,16 @@ struct vgic_irq_rank *vgic_rank_offset(struct vcpu 
>> *v, unsigned int b,
>>       return vgic_get_rank(v, rank);
>>   }
>> +#ifdef CONFIG_GICV3_ESPI
>> +struct vgic_irq_rank *vgic_ext_rank_offset(struct vcpu *v, unsigned 
>> int b,
>> +                                           unsigned int n, unsigned 
>> int s)
>> +{
>> +    unsigned int rank = REG_RANK_NR(b, (n >> s));
>> +
>> +    return vgic_get_rank(v, rank + EXT_RANK_MIN);
>> +}
>> +#endif
>> +
>>   struct vgic_irq_rank *vgic_rank_irq(struct vcpu *v, unsigned int irq)
>>   {
>>       unsigned int rank = irq / 32;
>> @@ -117,6 +148,29 @@ int domain_vgic_register(struct domain *d, 
>> unsigned int *mmio_count)
>>       return 0;
>>   }
>> +#ifdef CONFIG_GICV3_ESPI
>> +static int init_vgic_espi(struct domain *d)
>> +{
>> +    int i;
> 
> please use unsigned int if "i" cannot be negative
> 

Sure, I will change the type to unsigned int in V3.

>> +
>> +    if ( d->arch.vgic.nr_espis == 0 )
>> +        return 0;
>> +
>> +    d->arch.vgic.ext_shared_irqs =
>> +        xzalloc_array(struct vgic_irq_rank, DOMAIN_NR_EXT_RANKS(d));
>> +    if ( d->arch.vgic.ext_shared_irqs == NULL )
>> +        return -ENOMEM;
>> +
>> +    for ( i = 0; i < d->arch.vgic.nr_espis; i++ )
>> +        vgic_init_pending_irq(&d->arch.vgic.pending_irqs[i + d- 
>> >arch.vgic.nr_spis], ESPI_IDX2INTID(i));
>> +
>> +    for ( i = 0; i < DOMAIN_NR_EXT_RANKS(d); i++ )
>> +        vgic_rank_init(&d->arch.vgic.ext_shared_irqs[i], i, 0);
>> +
>> +    return 0;
>> +}
>> +#endif
>> +
>>   int domain_vgic_init(struct domain *d, unsigned int nr_spis)
>>   {
>>       int i;
>> @@ -131,6 +185,30 @@ int domain_vgic_init(struct domain *d, unsigned 
>> int nr_spis)
>>        */
>>       nr_spis = ROUNDUP(nr_spis, 32);
>> +#ifdef CONFIG_GICV3_ESPI
>> +    if ( nr_spis > ESPI_MAX_INTID )
>> +        return -EINVAL;
>> +
>> +    if ( is_espi(nr_spis) )
>> +    {
>> +        /*
>> +         * During domain creation, the toolstack specifies the 
>> maximum INTID,
> 
> domain_vgic_init() is also called for dom0less DomUs (if present), which 
> are created at Xen boot. So the toolstack might even be absent on the 
> target system.
> 

Thank you for pointing this out. I revised the patch series and found 
that I did not change the behavior for dom0less builds. I will fix it in 
V3 and update the comment.

>> +         * which is defined in the domain config subtracted by 32. To 
>> compute the
>> +         * actual number of eSPI that will be usable for, add back 32.
>> +         */
>> +        d->arch.vgic.nr_espis = min(nr_spis - ESPI_BASE_INTID + 32, 
>> 1024U);
>> +        /* Verify if GIC HW can handle provided INTID */
>> +        if ( d->arch.vgic.nr_espis > gic_number_espis() )
>> +            return -EINVAL;
>> +        /* Set the maximum available number for defult SPI to pass 
>> the next check */
>> +        nr_spis = VGIC_DEF_NR_SPIS;
>> +    } else
>> +    {
>> +        /* Domain will use the regular SPI range */
>> +        d->arch.vgic.nr_espis = 0;
>> +    }
>> +#endif
>> +
>>       /* Limit the number of virtual SPIs supported to (1020 - 32) = 
>> 988  */
>>       if ( nr_spis > (1020 - NR_LOCAL_IRQS) )
>>           return -EINVAL;
>> @@ -145,7 +223,12 @@ int domain_vgic_init(struct domain *d, unsigned 
>> int nr_spis)
>>           return -ENOMEM;
>>       d->arch.vgic.pending_irqs =
>> +#ifdef CONFIG_GICV3_ESPI
>> +        xzalloc_array(struct pending_irq, d->arch.vgic.nr_spis +
>> +                      d->arch.vgic.nr_espis);
>> +#else
>>           xzalloc_array(struct pending_irq, d->arch.vgic.nr_spis);
>> +#endif
>>       if ( d->arch.vgic.pending_irqs == NULL )
>>           return -ENOMEM;
>> @@ -156,12 +239,23 @@ int domain_vgic_init(struct domain *d, unsigned 
>> int nr_spis)
>>       for ( i = 0; i < DOMAIN_NR_RANKS(d); i++ )
>>           vgic_rank_init(&d->arch.vgic.shared_irqs[i], i + 1, 0);
>> +#ifdef CONFIG_GICV3_ESPI
>> +    ret = init_vgic_espi(d);
>> +    if ( ret )
>> +        return ret;
>> +#endif
>> +
>>       ret = d->arch.vgic.handler->domain_init(d);
>>       if ( ret )
>>           return ret;
>>       d->arch.vgic.allocated_irqs =
>> +#ifdef CONFIG_GICV3_ESPI
>> +        xzalloc_array(unsigned long, BITS_TO_LONGS(vgic_num_irqs(d) +
>> +                      d->arch.vgic.nr_espis));
>> +#else
>>           xzalloc_array(unsigned long, BITS_TO_LONGS(vgic_num_irqs(d)));
>> +#endif
>>       if ( !d->arch.vgic.allocated_irqs )
>>           return -ENOMEM;
>> @@ -195,9 +289,27 @@ void domain_vgic_free(struct domain *d)
>>           }
>>       }
>> +#ifdef CONFIG_GICV3_ESPI
>> +    for ( i = 0; i < (d->arch.vgic.nr_espis); i++ )
> 
> NIT: no need for () around d->arch.vgic.nr_espis
> 

I will remove them in V3.

>> +    {
>> +        struct pending_irq *p = spi_to_pending(d, ESPI_IDX2INTID(i));
>> +
>> +        if ( p->desc )
>> +        {
>> +            ret = release_guest_irq(d, p->irq);
>> +            if ( ret )
>> +                dprintk(XENLOG_G_WARNING, "d%u: Failed to release 
>> virq %u ret = %d\n",
>> +                        d->domain_id, p->irq, ret);
>> +        }
>> +    }
>> +#endif
>> +
>>       if ( d->arch.vgic.handler )
>>           d->arch.vgic.handler->domain_free(d);
>>       xfree(d->arch.vgic.shared_irqs);
>> +#ifdef CONFIG_GICV3_ESPI
>> +    xfree(d->arch.vgic.ext_shared_irqs);
>> +#endif
>>       xfree(d->arch.vgic.pending_irqs);
>>       xfree(d->arch.vgic.allocated_irqs);
>>   }
>> @@ -331,6 +443,17 @@ void arch_move_irqs(struct vcpu *v)
>>           if ( v_target == v && !test_bit(GIC_IRQ_GUEST_MIGRATING, &p- 
>> >status) )
>>               irq_set_affinity(p->desc, cpu_mask);
>>       }
>> +
>> +#ifdef CONFIG_GICV3_ESPI
>> +    for ( i = ESPI_BASE_INTID; i < (d)->arch.vgic.nr_espis; i++ )
> 
> NIT: no need for () around d

I will remove them in V3.

> 
> [snip]

Best regards,
Leonid

Reply via email to