Files
xen/xen/arch/arm/include/asm/vgic.h
T
Andrew Cooper 8673bedd76 ARM/sysctl: Expose the supported guest GIC modes in physinfo
In preparation to simplify the domain creation logic surrounding GIC
version.

On a GICv3 host, also report support for GICv2-compatible guests when
the hardware's vGICv2 compatibility mode is enabled, rather than just
the native GIC version.

Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com>
Signed-off-by: Julian Vetter <julian.vetter@vates.tech>
Reviewed-by: Michal Orzel <michal.orzel@amd.com>
2026-10-14 16:32:32 +02:00

465 lines
17 KiB
C

/*
* ARM Virtual Generic Interrupt Controller support
*
* Ian Campbell <ian.campbell@citrix.com>
* Copyright (c) 2011 Citrix Systems.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef __ASM_ARM_VGIC_H__
#define __ASM_ARM_VGIC_H__
#ifdef CONFIG_NEW_VGIC
#include <asm/new_vgic.h>
#else
#include <xen/bug.h>
#include <xen/radix-tree.h>
#include <xen/rbtree.h>
struct pending_irq
{
/*
* The following two states track the lifecycle of the guest irq.
* However because we are not sure and we don't want to track
* whether an irq added to an LR register is PENDING or ACTIVE, the
* following states are just an approximation.
*
* GIC_IRQ_GUEST_QUEUED: the irq is asserted and queued for
* injection into the guest's LRs.
*
* GIC_IRQ_GUEST_VISIBLE: the irq has been added to an LR register,
* therefore the guest is aware of it. From the guest point of view
* the irq can be pending (if the guest has not acked the irq yet)
* or active (after acking the irq).
*
* In order for the state machine to be fully accurate, for level
* interrupts, we should keep the interrupt's pending state until
* the guest deactivates the irq. However because we are not sure
* when that happens, we instead track whether there is an interrupt
* queued using GIC_IRQ_GUEST_QUEUED. We clear it when we add it to
* an LR register. We set it when we receive another interrupt
* notification. Therefore it is possible to set
* GIC_IRQ_GUEST_QUEUED while the irq is GIC_IRQ_GUEST_VISIBLE. We
* could also change the state of the guest irq in the LR register
* from active to active and pending, but for simplicity we simply
* inject a second irq after the guest EOIs the first one.
*
*
* An additional state is used to keep track of whether the guest
* irq is enabled at the vgicd level:
*
* GIC_IRQ_GUEST_ENABLED: the guest IRQ is enabled at the VGICD
* level (GICD_ICENABLER/GICD_ISENABLER).
*
* GIC_IRQ_GUEST_MIGRATING: the irq is being migrated to a different
* vcpu while it is still inflight and on an GICH_LR register on the
* old vcpu.
*
* GIC_IRQ_GUEST_PRISTINE_LPI: the IRQ is a newly mapped LPI, which
* has never been in an LR before. This means that any trace of an
* LPI with the same number in an LR must be from an older LPI, which
* has been unmapped before.
*
*/
#define GIC_IRQ_GUEST_QUEUED 0
#define GIC_IRQ_GUEST_ACTIVE 1
#define GIC_IRQ_GUEST_VISIBLE 2
#define GIC_IRQ_GUEST_ENABLED 3
#define GIC_IRQ_GUEST_MIGRATING 4
#define GIC_IRQ_GUEST_PRISTINE_LPI 5
unsigned long status;
struct irq_desc *desc; /* only set if the irq corresponds to a physical irq */
unsigned int irq;
#define GIC_INVALID_LR (uint8_t)~0
uint8_t lr;
uint8_t priority;
uint8_t lpi_priority; /* Caches the priority if this is an LPI. */
uint8_t lpi_vcpu_id; /* The VCPU for an LPI. */
/* inflight is used to append instances of pending_irq to
* vgic.inflight_irqs */
struct list_head inflight;
/* lr_queue is used to append instances of pending_irq to
* lr_pending. lr_pending is a per vcpu queue, therefore lr_queue
* accesses are protected with the vgic lock.
* TODO: when implementing irq migration, taking only the current
* vgic lock is not going to be enough. */
struct list_head lr_queue;
};
#define NR_INTERRUPT_PER_RANK 32
#define INTERRUPT_RANK_MASK (NR_INTERRUPT_PER_RANK - 1)
/* Represents state corresponding to a block of 32 interrupts */
struct vgic_irq_rank {
spinlock_t lock; /* Covers access to all other members of this struct */
uint8_t index;
uint32_t ienable;
uint32_t icfg[2];
/*
* Provide efficient access to the priority of an vIRQ while keeping
* the emulation simple.
* Note, this is working fine as long as Xen is using little endian.
*/
union {
uint8_t priority[32];
uint32_t ipriorityr[8];
};
/*
* It's more convenient to store a target VCPU per vIRQ
* than the register ITARGETSR/IROUTER itself.
* Use atomic operations to read/write the vcpu fields to avoid
* taking the rank lock.
*/
uint8_t vcpu[32];
};
struct vgic_dist {
/* Version of the vGIC */
enum gic_version version;
/* GIC HW version specific vGIC driver handler */
const struct vgic_ops *handler;
/*
* Covers access to other members of this struct _except_ for
* shared_irqs where each member contains its own locking.
*
* If both class of lock is required then this lock must be
* taken first. If multiple rank locks are required (including
* the per-vcpu private_irqs rank) then they must be taken in
* rank order.
*/
spinlock_t lock;
uint32_t ctlr;
int nr_spis; /* Number of SPIs */
/*
* Bitmap of allocated IRQs with the following index mapping:
* Local IRQs [0..31]
* Regular SPIs [32..nr_spis + 31]
* Optional, if supported:
* Extended SPIs [nr_spis + 32..nr_spis + nr_espis + 31]
*/
unsigned long *allocated_irqs;
struct vgic_irq_rank *shared_irqs;
#ifdef CONFIG_GICV3_ESPI
struct vgic_irq_rank *ext_shared_irqs;
unsigned int nr_espis; /* Number of extended SPIs */
#endif
/*
* SPIs are domain global, SGIs and PPIs are per-VCPU and stored in
* struct arch_vcpu. The index mapping is as follows:
* Regular SPIs [0..nr_spis - 1]
* Optional, if supported:
* eSPIs [nr_spis..nr_spis + nr_espis - 1]
*/
struct pending_irq *pending_irqs;
/* Base address for guest GIC */
paddr_t dbase; /* Distributor base address */
paddr_t cbase; /* CPU interface base address */
paddr_t csize; /* CPU interface size */
paddr_t vbase; /* Virtual CPU interface base address */
#ifdef CONFIG_GICV3
/* GIC V3 addressing */
/* List of contiguous occupied by the redistributors */
struct vgic_rdist_region {
paddr_t base; /* Base address */
paddr_t size; /* Size */
unsigned int first_cpu; /* First CPU handled */
} *rdist_regions;
int nr_regions; /* Number of rdist regions */
unsigned long int nr_lpis;
uint64_t rdist_propbase;
struct rb_root its_devices; /* Devices mapped to an ITS */
spinlock_t its_devices_lock; /* Protects the its_devices tree */
struct radix_tree_root pend_lpi_tree; /* Stores struct pending_irq's */
rwlock_t pend_lpi_tree_lock; /* Protects the pend_lpi_tree */
struct list_head vits_list; /* List of virtual ITSes */
unsigned int intid_bits;
/*
* TODO: if there are more bool's being added below, consider
* a flags variable instead.
*/
bool rdists_enabled; /* Is any redistributor enabled? */
bool has_its;
#endif
};
struct vgic_cpu {
/*
* SGIs and PPIs are per-VCPU, SPIs are domain global and in
* struct arch_domain.
*/
struct pending_irq pending_irqs[32];
struct vgic_irq_rank *private_irqs;
/* This list is ordered by IRQ priority and it is used to keep
* track of the IRQs that the VGIC injected into the guest.
* Depending on the availability of LR registers, the IRQs might
* actually be in an LR, and therefore injected into the guest,
* or queued in gic.lr_pending.
* As soon as an IRQ is EOI'd by the guest and removed from the
* corresponding LR it is also removed from this list. */
struct list_head inflight_irqs;
/* lr_pending is used to queue IRQs (struct pending_irq) that the
* vgic tried to inject in the guest (calling gic_raise_guest_irq) but
* no LRs were available at the time.
* As soon as an LR is freed we remove the first IRQ from this
* list and write it to the LR register.
* lr_pending is a subset of vgic.inflight_irqs. */
struct list_head lr_pending;
spinlock_t lock;
/* GICv3: redistributor base and flags for this vCPU */
paddr_t rdist_base;
uint64_t rdist_pendbase;
#define VGIC_V3_RDIST_LAST (1 << 0) /* last vCPU of the rdist */
#define VGIC_V3_LPIS_ENABLED (1 << 1)
uint8_t flags;
};
struct sgi_target {
uint8_t aff1;
uint16_t list;
};
static inline void sgi_target_init(struct sgi_target *sgi_target)
{
sgi_target->aff1 = 0;
sgi_target->list = 0;
}
struct vgic_ops {
/* Initialize vGIC */
int (*vcpu_init)(struct vcpu *v);
/* Domain specific initialization of vGIC */
int (*domain_init)(struct domain *d);
/* Release resources that were allocated by domain_init */
void (*domain_free)(struct domain *d);
/* vGIC sysreg/cpregs emulate */
bool (*emulate_reg)(struct cpu_user_regs *regs, union hsr hsr);
/* lookup the struct pending_irq for a given LPI interrupt */
struct pending_irq *(*lpi_to_pending)(struct domain *d, unsigned int vlpi);
int (*lpi_get_priority)(struct domain *d, uint32_t vlpi);
};
/* 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)
#endif
#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)
#define vgic_lock(v) spin_lock_irq(&(v)->domain->arch.vgic.lock)
#define vgic_unlock(v) spin_unlock_irq(&(v)->domain->arch.vgic.lock)
#define vgic_lock_rank(v, r, flags) spin_lock_irqsave(&(r)->lock, flags)
#define vgic_unlock_rank(v, r, flags) spin_unlock_irqrestore(&(r)->lock, flags)
/*
* Rank containing GICD_<FOO><n> for GICD_<FOO> with
* <b>-bits-per-interrupt
*/
static inline unsigned int REG_RANK_NR(unsigned int b, unsigned int n)
{
switch ( b )
{
/*
* IRQ ranks are of size 32. So n cannot be shifted beyond 5 for 32
* and above. For 64-bit n is already shifted DBAT_DOUBLE_WORD
* by the caller
*/
case 64:
case 32: return n >> 5;
case 16: return n >> 4;
case 8: return n >> 3;
case 4: return n >> 2;
case 2: return n >> 1;
case 1: return n;
default: BUG();
}
}
enum gic_sgi_mode;
static inline paddr_t vgic_cpu_base(const struct vgic_dist *vgic)
{
return vgic->cbase;
}
static inline paddr_t vgic_dist_base(const struct vgic_dist *vgic)
{
return vgic->dbase;
}
/*
* Offset of GICD_<FOO><n> with its rank, for GICD_<FOO> size <s> with
* <b>-bits-per-interrupt.
*/
#define REG_RANK_INDEX(b, n, s) ((((n) >> (s)) & ((b) - 1)) % 32)
extern struct vcpu *vgic_get_target_vcpu(struct vcpu *v, unsigned int virq);
extern void vgic_remove_irq_from_queues(struct vcpu *v, struct pending_irq *p);
extern void gic_remove_from_lr_pending(struct vcpu *v, struct pending_irq *p);
extern void vgic_init_pending_irq(struct pending_irq *p, unsigned int virq);
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);
extern struct vgic_irq_rank *vgic_rank_offset(struct vcpu *v,
unsigned int b,
unsigned int n,
unsigned int s);
extern struct vgic_irq_rank *vgic_ext_rank_offset(struct vcpu *v,
unsigned int b,
unsigned int n,
unsigned int s);
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);
extern void vgic_set_irqs_pending(struct vcpu *v, uint32_t r,
unsigned int rank);
extern void register_vgic_ops(struct domain *d, const struct vgic_ops *ops);
int vgic_v2_init(struct domain *d, unsigned int *mmio_count);
int vgic_v3_init(struct domain *d, unsigned int *mmio_count);
extern bool vgic_to_sgi(struct vcpu *v, register_t sgir,
enum gic_sgi_mode irqmode, int virq,
const struct sgi_target *target);
extern bool vgic_migrate_irq(struct vcpu *old, struct vcpu *new, unsigned int irq);
extern void vgic_check_inflight_irqs_pending(struct vcpu *v,
unsigned int rank, uint32_t r);
#endif /* !CONFIG_NEW_VGIC */
/*** Common VGIC functions used by Xen arch code ****/
/*
* In the moment vgic_num_irqs() just covers SPIs and the private IRQs,
* as it's mostly used for allocating the pending_irq and irq_desc array,
* in which LPIs don't participate.
*/
#define vgic_num_irqs(d) ((d)->arch.vgic.nr_spis + 32)
/* Maximum number of IRQs supported by vGIC */
#define VGIC_MAX_IRQS 992U
/* Default number of vGIC SPIs. 32 are substracted to cover local IRQs. */
#define VGIC_DEF_NR_SPIS (min(gic_number_lines(), VGIC_MAX_IRQS) - 32)
static inline unsigned int vgic_def_nr_spis(void)
{
#ifdef CONFIG_GICV3_ESPI
/*
* Check if the hardware supports extended SPIs (even if the appropriate
* config is set). If not, the common SPI range will be used. Otherwise
* return the maximum eSPI INTID, supported by HW GIC, subtracted by 32.
* For Dom0 and started at boot time DomUs we will add back this value
* during VGIC initialization. This ensures consistent handling for Dom0
* and other domains. For the regular SPI range interrupts in this case,
* the maximum value of VGIC_DEF_NR_SPIS will be used.
*/
if ( gic_number_espis() > 0 )
return ESPI_BASE_INTID + min(gic_number_espis(), 1024U) - 32;
#endif
return VGIC_DEF_NR_SPIS;
}
extern bool vgic_is_valid_line(struct domain *d, unsigned int virq);
static inline bool vgic_is_spi(struct domain *d, unsigned int virq)
{
return virq >= NR_LOCAL_IRQS && vgic_is_valid_line(d, virq);
}
/*
* Allocate a guest VIRQ
* - spi == 0 => allocate a PPI. It will be the same on every vCPU
* - spi == 1 => allocate an SPI
*/
extern int vgic_allocate_virq(struct domain *d, bool spi);
/* Reserve a specific guest vIRQ */
extern bool vgic_reserve_virq(struct domain *d, unsigned int virq);
extern void vgic_free_virq(struct domain *d, unsigned int virq);
static inline int vgic_allocate_ppi(struct domain *d)
{
return vgic_allocate_virq(d, false /* ppi */);
}
static inline int vgic_allocate_spi(struct domain *d)
{
return vgic_allocate_virq(d, true /* spi */);
}
struct irq_desc *vgic_get_hw_irq_desc(struct domain *d, struct vcpu *v,
unsigned int virq);
int vgic_connect_hw_irq(struct domain *d, struct vcpu *v, unsigned int virq,
struct irq_desc *desc, bool connect);
bool vgic_evtchn_irq_pending(struct vcpu *v);
int domain_vgic_register(struct domain *d, unsigned int *mmio_count);
int domain_vgic_init(struct domain *d, unsigned int nr_spis);
void domain_vgic_free(struct domain *d);
int vcpu_vgic_init(struct vcpu *v);
void vcpu_vgic_free(struct vcpu *v);
void vgic_inject_irq(struct domain *d, struct vcpu *v, unsigned int virq,
bool level);
extern void vgic_clear_pending_irqs(struct vcpu *v);
extern bool vgic_emulate(struct cpu_user_regs *regs, union hsr hsr);
/* Maximum vCPUs for a specific vGIC version, or 0 for unsupported. */
unsigned int vgic_max_vcpus(unsigned int domctl_vgic_version);
void vgic_v2_setup_hw(paddr_t dbase, paddr_t cbase, paddr_t csize,
paddr_t vbase, uint32_t aliased_offset);
#ifdef CONFIG_VGICV2
bool vgic_v2_hw_enabled(void);
#else
static inline bool vgic_v2_hw_enabled(void) { return false; }
#endif
#ifdef CONFIG_GICV3
struct rdist_region;
void vgic_v3_setup_hw(paddr_t dbase,
unsigned int nr_rdist_regions,
const struct rdist_region *regions,
unsigned int intid_bits);
#endif
void vgic_sync_to_lrs(void);
void vgic_sync_from_lrs(struct vcpu *v);
int vgic_vcpu_pending_irq(struct vcpu *v);
#endif /* __ASM_ARM_VGIC_H__ */
/*
* Local variables:
* mode: C
* c-file-style: "BSD"
* c-basic-offset: 4
* indent-tabs-mode: nil
* End:
*/