mirror of
https://github.com/xen-project/xen.git
synced 2026-10-28 02:01:24 +01:00
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>
465 lines
17 KiB
C
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:
|
|
*/
|