Merge branch kvm-arm64/lpi-xa-cache into kvmarm-master/next
* kvm-arm64/lpi-xa-cache: : . : New and improved LPI translation cache from Oliver Upton. : : From the cover letter: : : "As discussed [*], here is the new take on the LPI translation cache, : migrating to an xarray indexed by (devid, eventid) per ITS. : : The end result is quite satisfying, as it becomes possible to rip out : other nasties such as the lpi_list_lock. To that end, patches 2-6 aren't : _directly_ related to the translation cache cleanup, but instead are : done to enable the cleanups at the end of the series. : : I changed out my test machine from the last time so the baseline has : moved a bit, but here are the results from the vgic_lpi_stress test: : : +----------------------------+------------+-------------------+ : | Configuration | v6.8-rc1 | v6.8-rc1 + series | : +----------------------------+------------+-------------------+ : | -v 1 -d 1 -e 1 -i 1000000 | 2063296.81 | 1362602.35 | : | -v 16 -d 16 -e 16 -i 10000 | 610678.33 | 5200910.01 | : | -v 16 -d 16 -e 17 -i 10000 | 678361.53 | 5890675.51 | : | -v 32 -d 32 -e 1 -i 100000 | 580918.96 | 8304552.67 | : | -v 1 -d 1 -e 17 -i 1000 | 1512443.94 | 1425953.8 | : +----------------------------+------------+-------------------+ : : Unlike last time, no dramatic regressions at any performance point. The : regression on a single interrupt stream is to be expected, as the : overheads of SRCU and two tree traversals (kvm_io_bus_get_dev(), : translation cache xarray) are likely greater than that of a linked-list : with a single node." : . KVM: selftests: Add stress test for LPI injection KVM: selftests: Use MPIDR_HWID_BITMASK from cputype.h KVM: selftests: Add helper for enabling LPIs on a redistributor KVM: selftests: Add a minimal library for interacting with an ITS KVM: selftests: Add quadword MMIO accessors KVM: selftests: Standardise layout of GIC frames KVM: selftests: Align with kernel's GIC definitions KVM: arm64: vgic-its: Get rid of the lpi_list_lock KVM: arm64: vgic-its: Rip out the global translation cache KVM: arm64: vgic-its: Use the per-ITS translation cache for injection KVM: arm64: vgic-its: Spin off helper for finding ITS by doorbell addr KVM: arm64: vgic-its: Maintain a translation cache per ITS KVM: arm64: vgic-its: Scope translation cache invalidations to an ITS KVM: arm64: vgic-its: Get rid of vgic_copy_lpi_list() KVM: arm64: vgic-debug: Use an xarray mark for debug iterator KVM: arm64: vgic-its: Walk LPI xarray in vgic_its_cmd_handle_movall() KVM: arm64: vgic-its: Walk LPI xarray in vgic_its_invall() KVM: arm64: vgic-its: Walk LPI xarray in its_sync_lpi_pending_table() KVM: Treat the device list as an rculist Signed-off-by: Marc Zyngier <maz@kernel.org>
This commit is contained in:
@@ -28,27 +28,65 @@ struct vgic_state_iter {
|
||||
int nr_lpis;
|
||||
int dist_id;
|
||||
int vcpu_id;
|
||||
int intid;
|
||||
unsigned long intid;
|
||||
int lpi_idx;
|
||||
u32 *lpi_array;
|
||||
};
|
||||
|
||||
static void iter_next(struct vgic_state_iter *iter)
|
||||
static void iter_next(struct kvm *kvm, struct vgic_state_iter *iter)
|
||||
{
|
||||
struct vgic_dist *dist = &kvm->arch.vgic;
|
||||
|
||||
if (iter->dist_id == 0) {
|
||||
iter->dist_id++;
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Let the xarray drive the iterator after the last SPI, as the iterator
|
||||
* has exhausted the sequentially-allocated INTID space.
|
||||
*/
|
||||
if (iter->intid >= (iter->nr_spis + VGIC_NR_PRIVATE_IRQS - 1)) {
|
||||
if (iter->lpi_idx < iter->nr_lpis)
|
||||
xa_find_after(&dist->lpi_xa, &iter->intid,
|
||||
VGIC_LPI_MAX_INTID,
|
||||
LPI_XA_MARK_DEBUG_ITER);
|
||||
iter->lpi_idx++;
|
||||
return;
|
||||
}
|
||||
|
||||
iter->intid++;
|
||||
if (iter->intid == VGIC_NR_PRIVATE_IRQS &&
|
||||
++iter->vcpu_id < iter->nr_cpus)
|
||||
iter->intid = 0;
|
||||
}
|
||||
|
||||
if (iter->intid >= (iter->nr_spis + VGIC_NR_PRIVATE_IRQS)) {
|
||||
if (iter->lpi_idx < iter->nr_lpis)
|
||||
iter->intid = iter->lpi_array[iter->lpi_idx];
|
||||
iter->lpi_idx++;
|
||||
static int iter_mark_lpis(struct kvm *kvm)
|
||||
{
|
||||
struct vgic_dist *dist = &kvm->arch.vgic;
|
||||
struct vgic_irq *irq;
|
||||
unsigned long intid;
|
||||
int nr_lpis = 0;
|
||||
|
||||
xa_for_each(&dist->lpi_xa, intid, irq) {
|
||||
if (!vgic_try_get_irq_kref(irq))
|
||||
continue;
|
||||
|
||||
xa_set_mark(&dist->lpi_xa, intid, LPI_XA_MARK_DEBUG_ITER);
|
||||
nr_lpis++;
|
||||
}
|
||||
|
||||
return nr_lpis;
|
||||
}
|
||||
|
||||
static void iter_unmark_lpis(struct kvm *kvm)
|
||||
{
|
||||
struct vgic_dist *dist = &kvm->arch.vgic;
|
||||
struct vgic_irq *irq;
|
||||
unsigned long intid;
|
||||
|
||||
xa_for_each(&dist->lpi_xa, intid, irq) {
|
||||
xa_clear_mark(&dist->lpi_xa, intid, LPI_XA_MARK_DEBUG_ITER);
|
||||
vgic_put_irq(kvm, irq);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,15 +99,12 @@ static void iter_init(struct kvm *kvm, struct vgic_state_iter *iter,
|
||||
|
||||
iter->nr_cpus = nr_cpus;
|
||||
iter->nr_spis = kvm->arch.vgic.nr_spis;
|
||||
if (kvm->arch.vgic.vgic_model == KVM_DEV_TYPE_ARM_VGIC_V3) {
|
||||
iter->nr_lpis = vgic_copy_lpi_list(kvm, NULL, &iter->lpi_array);
|
||||
if (iter->nr_lpis < 0)
|
||||
iter->nr_lpis = 0;
|
||||
}
|
||||
if (kvm->arch.vgic.vgic_model == KVM_DEV_TYPE_ARM_VGIC_V3)
|
||||
iter->nr_lpis = iter_mark_lpis(kvm);
|
||||
|
||||
/* Fast forward to the right position if needed */
|
||||
while (pos--)
|
||||
iter_next(iter);
|
||||
iter_next(kvm, iter);
|
||||
}
|
||||
|
||||
static bool end_of_vgic(struct vgic_state_iter *iter)
|
||||
@@ -114,7 +149,7 @@ static void *vgic_debug_next(struct seq_file *s, void *v, loff_t *pos)
|
||||
struct vgic_state_iter *iter = kvm->arch.vgic.iter;
|
||||
|
||||
++*pos;
|
||||
iter_next(iter);
|
||||
iter_next(kvm, iter);
|
||||
if (end_of_vgic(iter))
|
||||
iter = NULL;
|
||||
return iter;
|
||||
@@ -134,13 +169,14 @@ static void vgic_debug_stop(struct seq_file *s, void *v)
|
||||
|
||||
mutex_lock(&kvm->arch.config_lock);
|
||||
iter = kvm->arch.vgic.iter;
|
||||
kfree(iter->lpi_array);
|
||||
iter_unmark_lpis(kvm);
|
||||
kfree(iter);
|
||||
kvm->arch.vgic.iter = NULL;
|
||||
mutex_unlock(&kvm->arch.config_lock);
|
||||
}
|
||||
|
||||
static void print_dist_state(struct seq_file *s, struct vgic_dist *dist)
|
||||
static void print_dist_state(struct seq_file *s, struct vgic_dist *dist,
|
||||
struct vgic_state_iter *iter)
|
||||
{
|
||||
bool v3 = dist->vgic_model == KVM_DEV_TYPE_ARM_VGIC_V3;
|
||||
|
||||
@@ -149,7 +185,7 @@ static void print_dist_state(struct seq_file *s, struct vgic_dist *dist)
|
||||
seq_printf(s, "vgic_model:\t%s\n", v3 ? "GICv3" : "GICv2");
|
||||
seq_printf(s, "nr_spis:\t%d\n", dist->nr_spis);
|
||||
if (v3)
|
||||
seq_printf(s, "nr_lpis:\t%d\n", atomic_read(&dist->lpi_count));
|
||||
seq_printf(s, "nr_lpis:\t%d\n", iter->nr_lpis);
|
||||
seq_printf(s, "enabled:\t%d\n", dist->enabled);
|
||||
seq_printf(s, "\n");
|
||||
|
||||
@@ -236,7 +272,7 @@ static int vgic_debug_show(struct seq_file *s, void *v)
|
||||
unsigned long flags;
|
||||
|
||||
if (iter->dist_id == 0) {
|
||||
print_dist_state(s, &kvm->arch.vgic);
|
||||
print_dist_state(s, &kvm->arch.vgic, iter);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -246,11 +282,13 @@ static int vgic_debug_show(struct seq_file *s, void *v)
|
||||
if (iter->vcpu_id < iter->nr_cpus)
|
||||
vcpu = kvm_get_vcpu(kvm, iter->vcpu_id);
|
||||
|
||||
/*
|
||||
* Expect this to succeed, as iter_mark_lpis() takes a reference on
|
||||
* every LPI to be visited.
|
||||
*/
|
||||
irq = vgic_get_irq(kvm, vcpu, iter->intid);
|
||||
if (!irq) {
|
||||
seq_printf(s, " LPI %4d freed\n", iter->intid);
|
||||
return 0;
|
||||
}
|
||||
if (WARN_ON_ONCE(!irq))
|
||||
return -EINVAL;
|
||||
|
||||
raw_spin_lock_irqsave(&irq->irq_lock, flags);
|
||||
print_irq_state(s, irq, vcpu);
|
||||
|
||||
@@ -53,8 +53,6 @@ void kvm_vgic_early_init(struct kvm *kvm)
|
||||
{
|
||||
struct vgic_dist *dist = &kvm->arch.vgic;
|
||||
|
||||
INIT_LIST_HEAD(&dist->lpi_translation_cache);
|
||||
raw_spin_lock_init(&dist->lpi_list_lock);
|
||||
xa_init_flags(&dist->lpi_xa, XA_FLAGS_LOCK_IRQ);
|
||||
}
|
||||
|
||||
@@ -305,9 +303,6 @@ int vgic_init(struct kvm *kvm)
|
||||
}
|
||||
}
|
||||
|
||||
if (vgic_has_its(kvm))
|
||||
vgic_lpi_translation_cache_init(kvm);
|
||||
|
||||
/*
|
||||
* If we have GICv4.1 enabled, unconditionally request enable the
|
||||
* v4 support so that we get HW-accelerated vSGIs. Otherwise, only
|
||||
@@ -361,9 +356,6 @@ static void kvm_vgic_dist_destroy(struct kvm *kvm)
|
||||
dist->vgic_cpu_base = VGIC_ADDR_UNDEF;
|
||||
}
|
||||
|
||||
if (vgic_has_its(kvm))
|
||||
vgic_lpi_translation_cache_destroy(kvm);
|
||||
|
||||
if (vgic_supports_direct_msis(kvm))
|
||||
vgic_v4_teardown(kvm);
|
||||
|
||||
|
||||
+106
-250
@@ -23,6 +23,8 @@
|
||||
#include "vgic.h"
|
||||
#include "vgic-mmio.h"
|
||||
|
||||
static struct kvm_device_ops kvm_arm_vgic_its_ops;
|
||||
|
||||
static int vgic_its_save_tables_v0(struct vgic_its *its);
|
||||
static int vgic_its_restore_tables_v0(struct vgic_its *its);
|
||||
static int vgic_its_commit_v0(struct vgic_its *its);
|
||||
@@ -67,7 +69,7 @@ static struct vgic_irq *vgic_add_lpi(struct kvm *kvm, u32 intid,
|
||||
irq->target_vcpu = vcpu;
|
||||
irq->group = 1;
|
||||
|
||||
raw_spin_lock_irqsave(&dist->lpi_list_lock, flags);
|
||||
xa_lock_irqsave(&dist->lpi_xa, flags);
|
||||
|
||||
/*
|
||||
* There could be a race with another vgic_add_lpi(), so we need to
|
||||
@@ -82,17 +84,14 @@ static struct vgic_irq *vgic_add_lpi(struct kvm *kvm, u32 intid,
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
ret = xa_err(xa_store(&dist->lpi_xa, intid, irq, 0));
|
||||
ret = xa_err(__xa_store(&dist->lpi_xa, intid, irq, 0));
|
||||
if (ret) {
|
||||
xa_release(&dist->lpi_xa, intid);
|
||||
kfree(irq);
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
atomic_inc(&dist->lpi_count);
|
||||
|
||||
out_unlock:
|
||||
raw_spin_unlock_irqrestore(&dist->lpi_list_lock, flags);
|
||||
xa_unlock_irqrestore(&dist->lpi_xa, flags);
|
||||
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
@@ -150,14 +149,6 @@ struct its_ite {
|
||||
u32 event_id;
|
||||
};
|
||||
|
||||
struct vgic_translation_cache_entry {
|
||||
struct list_head entry;
|
||||
phys_addr_t db;
|
||||
u32 devid;
|
||||
u32 eventid;
|
||||
struct vgic_irq *irq;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct vgic_its_abi - ITS abi ops and settings
|
||||
* @cte_esz: collection table entry size
|
||||
@@ -252,8 +243,10 @@ static struct its_ite *find_ite(struct vgic_its *its, u32 device_id,
|
||||
|
||||
#define GIC_LPI_OFFSET 8192
|
||||
|
||||
#define VITS_TYPER_IDBITS 16
|
||||
#define VITS_TYPER_DEVBITS 16
|
||||
#define VITS_TYPER_IDBITS 16
|
||||
#define VITS_MAX_EVENTID (BIT(VITS_TYPER_IDBITS) - 1)
|
||||
#define VITS_TYPER_DEVBITS 16
|
||||
#define VITS_MAX_DEVID (BIT(VITS_TYPER_DEVBITS) - 1)
|
||||
#define VITS_DTE_MAX_DEVID_OFFSET (BIT(14) - 1)
|
||||
#define VITS_ITE_MAX_EVENTID_OFFSET (BIT(16) - 1)
|
||||
|
||||
@@ -316,53 +309,6 @@ static int update_lpi_config(struct kvm *kvm, struct vgic_irq *irq,
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define GIC_LPI_MAX_INTID ((1 << INTERRUPT_ID_BITS_ITS) - 1)
|
||||
|
||||
/*
|
||||
* Create a snapshot of the current LPIs targeting @vcpu, so that we can
|
||||
* enumerate those LPIs without holding any lock.
|
||||
* Returns their number and puts the kmalloc'ed array into intid_ptr.
|
||||
*/
|
||||
int vgic_copy_lpi_list(struct kvm *kvm, struct kvm_vcpu *vcpu, u32 **intid_ptr)
|
||||
{
|
||||
struct vgic_dist *dist = &kvm->arch.vgic;
|
||||
XA_STATE(xas, &dist->lpi_xa, GIC_LPI_OFFSET);
|
||||
struct vgic_irq *irq;
|
||||
unsigned long flags;
|
||||
u32 *intids;
|
||||
int irq_count, i = 0;
|
||||
|
||||
/*
|
||||
* There is an obvious race between allocating the array and LPIs
|
||||
* being mapped/unmapped. If we ended up here as a result of a
|
||||
* command, we're safe (locks are held, preventing another
|
||||
* command). If coming from another path (such as enabling LPIs),
|
||||
* we must be careful not to overrun the array.
|
||||
*/
|
||||
irq_count = atomic_read(&dist->lpi_count);
|
||||
intids = kmalloc_array(irq_count, sizeof(intids[0]), GFP_KERNEL_ACCOUNT);
|
||||
if (!intids)
|
||||
return -ENOMEM;
|
||||
|
||||
raw_spin_lock_irqsave(&dist->lpi_list_lock, flags);
|
||||
rcu_read_lock();
|
||||
|
||||
xas_for_each(&xas, irq, GIC_LPI_MAX_INTID) {
|
||||
if (i == irq_count)
|
||||
break;
|
||||
/* We don't need to "get" the IRQ, as we hold the list lock. */
|
||||
if (vcpu && irq->target_vcpu != vcpu)
|
||||
continue;
|
||||
intids[i++] = irq->intid;
|
||||
}
|
||||
|
||||
rcu_read_unlock();
|
||||
raw_spin_unlock_irqrestore(&dist->lpi_list_lock, flags);
|
||||
|
||||
*intid_ptr = intids;
|
||||
return i;
|
||||
}
|
||||
|
||||
static int update_affinity(struct vgic_irq *irq, struct kvm_vcpu *vcpu)
|
||||
{
|
||||
int ret = 0;
|
||||
@@ -446,23 +392,18 @@ static u32 max_lpis_propbaser(u64 propbaser)
|
||||
static int its_sync_lpi_pending_table(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
gpa_t pendbase = GICR_PENDBASER_ADDRESS(vcpu->arch.vgic_cpu.pendbaser);
|
||||
struct vgic_dist *dist = &vcpu->kvm->arch.vgic;
|
||||
unsigned long intid, flags;
|
||||
struct vgic_irq *irq;
|
||||
int last_byte_offset = -1;
|
||||
int ret = 0;
|
||||
u32 *intids;
|
||||
int nr_irqs, i;
|
||||
unsigned long flags;
|
||||
u8 pendmask;
|
||||
|
||||
nr_irqs = vgic_copy_lpi_list(vcpu->kvm, vcpu, &intids);
|
||||
if (nr_irqs < 0)
|
||||
return nr_irqs;
|
||||
|
||||
for (i = 0; i < nr_irqs; i++) {
|
||||
xa_for_each(&dist->lpi_xa, intid, irq) {
|
||||
int byte_offset, bit_nr;
|
||||
|
||||
byte_offset = intids[i] / BITS_PER_BYTE;
|
||||
bit_nr = intids[i] % BITS_PER_BYTE;
|
||||
byte_offset = intid / BITS_PER_BYTE;
|
||||
bit_nr = intid % BITS_PER_BYTE;
|
||||
|
||||
/*
|
||||
* For contiguously allocated LPIs chances are we just read
|
||||
@@ -472,25 +413,23 @@ static int its_sync_lpi_pending_table(struct kvm_vcpu *vcpu)
|
||||
ret = kvm_read_guest_lock(vcpu->kvm,
|
||||
pendbase + byte_offset,
|
||||
&pendmask, 1);
|
||||
if (ret) {
|
||||
kfree(intids);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
last_byte_offset = byte_offset;
|
||||
}
|
||||
|
||||
irq = vgic_get_irq(vcpu->kvm, NULL, intids[i]);
|
||||
irq = vgic_get_irq(vcpu->kvm, NULL, intid);
|
||||
if (!irq)
|
||||
continue;
|
||||
|
||||
raw_spin_lock_irqsave(&irq->irq_lock, flags);
|
||||
irq->pending_latch = pendmask & (1U << bit_nr);
|
||||
if (irq->target_vcpu == vcpu)
|
||||
irq->pending_latch = pendmask & (1U << bit_nr);
|
||||
vgic_queue_irq_unlock(vcpu->kvm, irq, flags);
|
||||
vgic_put_irq(vcpu->kvm, irq);
|
||||
}
|
||||
|
||||
kfree(intids);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -566,51 +505,52 @@ static unsigned long vgic_mmio_read_its_idregs(struct kvm *kvm,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct vgic_irq *__vgic_its_check_cache(struct vgic_dist *dist,
|
||||
phys_addr_t db,
|
||||
u32 devid, u32 eventid)
|
||||
static struct vgic_its *__vgic_doorbell_to_its(struct kvm *kvm, gpa_t db)
|
||||
{
|
||||
struct vgic_translation_cache_entry *cte;
|
||||
struct kvm_io_device *kvm_io_dev;
|
||||
struct vgic_io_device *iodev;
|
||||
|
||||
list_for_each_entry(cte, &dist->lpi_translation_cache, entry) {
|
||||
/*
|
||||
* If we hit a NULL entry, there is nothing after this
|
||||
* point.
|
||||
*/
|
||||
if (!cte->irq)
|
||||
break;
|
||||
kvm_io_dev = kvm_io_bus_get_dev(kvm, KVM_MMIO_BUS, db);
|
||||
if (!kvm_io_dev)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
if (cte->db != db || cte->devid != devid ||
|
||||
cte->eventid != eventid)
|
||||
continue;
|
||||
if (kvm_io_dev->ops != &kvm_io_gic_ops)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
/*
|
||||
* Move this entry to the head, as it is the most
|
||||
* recently used.
|
||||
*/
|
||||
if (!list_is_first(&cte->entry, &dist->lpi_translation_cache))
|
||||
list_move(&cte->entry, &dist->lpi_translation_cache);
|
||||
iodev = container_of(kvm_io_dev, struct vgic_io_device, dev);
|
||||
if (iodev->iodev_type != IODEV_ITS)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
return cte->irq;
|
||||
}
|
||||
return iodev->its;
|
||||
}
|
||||
|
||||
static unsigned long vgic_its_cache_key(u32 devid, u32 eventid)
|
||||
{
|
||||
return (((unsigned long)devid) << VITS_TYPER_IDBITS) | eventid;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct vgic_irq *vgic_its_check_cache(struct kvm *kvm, phys_addr_t db,
|
||||
u32 devid, u32 eventid)
|
||||
{
|
||||
struct vgic_dist *dist = &kvm->arch.vgic;
|
||||
unsigned long cache_key = vgic_its_cache_key(devid, eventid);
|
||||
struct vgic_its *its;
|
||||
struct vgic_irq *irq;
|
||||
unsigned long flags;
|
||||
|
||||
raw_spin_lock_irqsave(&dist->lpi_list_lock, flags);
|
||||
if (devid > VITS_MAX_DEVID || eventid > VITS_MAX_EVENTID)
|
||||
return NULL;
|
||||
|
||||
irq = __vgic_its_check_cache(dist, db, devid, eventid);
|
||||
its = __vgic_doorbell_to_its(kvm, db);
|
||||
if (IS_ERR(its))
|
||||
return NULL;
|
||||
|
||||
rcu_read_lock();
|
||||
|
||||
irq = xa_load(&its->translation_cache, cache_key);
|
||||
if (!vgic_try_get_irq_kref(irq))
|
||||
irq = NULL;
|
||||
|
||||
raw_spin_unlock_irqrestore(&dist->lpi_list_lock, flags);
|
||||
rcu_read_unlock();
|
||||
|
||||
return irq;
|
||||
}
|
||||
@@ -619,41 +559,13 @@ static void vgic_its_cache_translation(struct kvm *kvm, struct vgic_its *its,
|
||||
u32 devid, u32 eventid,
|
||||
struct vgic_irq *irq)
|
||||
{
|
||||
struct vgic_dist *dist = &kvm->arch.vgic;
|
||||
struct vgic_translation_cache_entry *cte;
|
||||
unsigned long flags;
|
||||
phys_addr_t db;
|
||||
unsigned long cache_key = vgic_its_cache_key(devid, eventid);
|
||||
struct vgic_irq *old;
|
||||
|
||||
/* Do not cache a directly injected interrupt */
|
||||
if (irq->hw)
|
||||
return;
|
||||
|
||||
raw_spin_lock_irqsave(&dist->lpi_list_lock, flags);
|
||||
|
||||
if (unlikely(list_empty(&dist->lpi_translation_cache)))
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* We could have raced with another CPU caching the same
|
||||
* translation behind our back, so let's check it is not in
|
||||
* already
|
||||
*/
|
||||
db = its->vgic_its_base + GITS_TRANSLATER;
|
||||
if (__vgic_its_check_cache(dist, db, devid, eventid))
|
||||
goto out;
|
||||
|
||||
/* Always reuse the last entry (LRU policy) */
|
||||
cte = list_last_entry(&dist->lpi_translation_cache,
|
||||
typeof(*cte), entry);
|
||||
|
||||
/*
|
||||
* Caching the translation implies having an extra reference
|
||||
* to the interrupt, so drop the potential reference on what
|
||||
* was in the cache, and increment it on the new interrupt.
|
||||
*/
|
||||
if (cte->irq)
|
||||
vgic_put_irq(kvm, cte->irq);
|
||||
|
||||
/*
|
||||
* The irq refcount is guaranteed to be nonzero while holding the
|
||||
* its_lock, as the ITE (and the reference it holds) cannot be freed.
|
||||
@@ -661,39 +573,44 @@ static void vgic_its_cache_translation(struct kvm *kvm, struct vgic_its *its,
|
||||
lockdep_assert_held(&its->its_lock);
|
||||
vgic_get_irq_kref(irq);
|
||||
|
||||
cte->db = db;
|
||||
cte->devid = devid;
|
||||
cte->eventid = eventid;
|
||||
cte->irq = irq;
|
||||
|
||||
/* Move the new translation to the head of the list */
|
||||
list_move(&cte->entry, &dist->lpi_translation_cache);
|
||||
|
||||
out:
|
||||
raw_spin_unlock_irqrestore(&dist->lpi_list_lock, flags);
|
||||
/*
|
||||
* We could have raced with another CPU caching the same
|
||||
* translation behind our back, ensure we don't leak a
|
||||
* reference if that is the case.
|
||||
*/
|
||||
old = xa_store(&its->translation_cache, cache_key, irq, GFP_KERNEL_ACCOUNT);
|
||||
if (old)
|
||||
vgic_put_irq(kvm, old);
|
||||
}
|
||||
|
||||
void vgic_its_invalidate_cache(struct kvm *kvm)
|
||||
static void vgic_its_invalidate_cache(struct vgic_its *its)
|
||||
{
|
||||
struct vgic_dist *dist = &kvm->arch.vgic;
|
||||
struct vgic_translation_cache_entry *cte;
|
||||
unsigned long flags;
|
||||
struct kvm *kvm = its->dev->kvm;
|
||||
struct vgic_irq *irq;
|
||||
unsigned long idx;
|
||||
|
||||
raw_spin_lock_irqsave(&dist->lpi_list_lock, flags);
|
||||
xa_for_each(&its->translation_cache, idx, irq) {
|
||||
xa_erase(&its->translation_cache, idx);
|
||||
vgic_put_irq(kvm, irq);
|
||||
}
|
||||
}
|
||||
|
||||
list_for_each_entry(cte, &dist->lpi_translation_cache, entry) {
|
||||
/*
|
||||
* If we hit a NULL entry, there is nothing after this
|
||||
* point.
|
||||
*/
|
||||
if (!cte->irq)
|
||||
break;
|
||||
void vgic_its_invalidate_all_caches(struct kvm *kvm)
|
||||
{
|
||||
struct kvm_device *dev;
|
||||
struct vgic_its *its;
|
||||
|
||||
vgic_put_irq(kvm, cte->irq);
|
||||
cte->irq = NULL;
|
||||
rcu_read_lock();
|
||||
|
||||
list_for_each_entry_rcu(dev, &kvm->devices, vm_node) {
|
||||
if (dev->ops != &kvm_arm_vgic_its_ops)
|
||||
continue;
|
||||
|
||||
its = dev->private;
|
||||
vgic_its_invalidate_cache(its);
|
||||
}
|
||||
|
||||
raw_spin_unlock_irqrestore(&dist->lpi_list_lock, flags);
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
int vgic_its_resolve_lpi(struct kvm *kvm, struct vgic_its *its,
|
||||
@@ -725,8 +642,6 @@ int vgic_its_resolve_lpi(struct kvm *kvm, struct vgic_its *its,
|
||||
struct vgic_its *vgic_msi_to_its(struct kvm *kvm, struct kvm_msi *msi)
|
||||
{
|
||||
u64 address;
|
||||
struct kvm_io_device *kvm_io_dev;
|
||||
struct vgic_io_device *iodev;
|
||||
|
||||
if (!vgic_has_its(kvm))
|
||||
return ERR_PTR(-ENODEV);
|
||||
@@ -736,18 +651,7 @@ struct vgic_its *vgic_msi_to_its(struct kvm *kvm, struct kvm_msi *msi)
|
||||
|
||||
address = (u64)msi->address_hi << 32 | msi->address_lo;
|
||||
|
||||
kvm_io_dev = kvm_io_bus_get_dev(kvm, KVM_MMIO_BUS, address);
|
||||
if (!kvm_io_dev)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
if (kvm_io_dev->ops != &kvm_io_gic_ops)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
iodev = container_of(kvm_io_dev, struct vgic_io_device, dev);
|
||||
if (iodev->iodev_type != IODEV_ITS)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
return iodev->its;
|
||||
return __vgic_doorbell_to_its(kvm, address);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -883,7 +787,7 @@ static int vgic_its_cmd_handle_discard(struct kvm *kvm, struct vgic_its *its,
|
||||
* don't bother here since we clear the ITTE anyway and the
|
||||
* pending state is a property of the ITTE struct.
|
||||
*/
|
||||
vgic_its_invalidate_cache(kvm);
|
||||
vgic_its_invalidate_cache(its);
|
||||
|
||||
its_free_ite(kvm, ite);
|
||||
return 0;
|
||||
@@ -920,7 +824,7 @@ static int vgic_its_cmd_handle_movi(struct kvm *kvm, struct vgic_its *its,
|
||||
ite->collection = collection;
|
||||
vcpu = collection_to_vcpu(kvm, collection);
|
||||
|
||||
vgic_its_invalidate_cache(kvm);
|
||||
vgic_its_invalidate_cache(its);
|
||||
|
||||
return update_affinity(ite->irq, vcpu);
|
||||
}
|
||||
@@ -955,7 +859,7 @@ static bool vgic_its_check_id(struct vgic_its *its, u64 baser, u32 id,
|
||||
|
||||
switch (type) {
|
||||
case GITS_BASER_TYPE_DEVICE:
|
||||
if (id >= BIT_ULL(VITS_TYPER_DEVBITS))
|
||||
if (id > VITS_MAX_DEVID)
|
||||
return false;
|
||||
break;
|
||||
case GITS_BASER_TYPE_COLLECTION:
|
||||
@@ -1167,7 +1071,8 @@ static int vgic_its_cmd_handle_mapi(struct kvm *kvm, struct vgic_its *its,
|
||||
}
|
||||
|
||||
/* Requires the its_lock to be held. */
|
||||
static void vgic_its_free_device(struct kvm *kvm, struct its_device *device)
|
||||
static void vgic_its_free_device(struct kvm *kvm, struct vgic_its *its,
|
||||
struct its_device *device)
|
||||
{
|
||||
struct its_ite *ite, *temp;
|
||||
|
||||
@@ -1179,7 +1084,7 @@ static void vgic_its_free_device(struct kvm *kvm, struct its_device *device)
|
||||
list_for_each_entry_safe(ite, temp, &device->itt_head, ite_list)
|
||||
its_free_ite(kvm, ite);
|
||||
|
||||
vgic_its_invalidate_cache(kvm);
|
||||
vgic_its_invalidate_cache(its);
|
||||
|
||||
list_del(&device->dev_list);
|
||||
kfree(device);
|
||||
@@ -1191,7 +1096,7 @@ static void vgic_its_free_device_list(struct kvm *kvm, struct vgic_its *its)
|
||||
struct its_device *cur, *temp;
|
||||
|
||||
list_for_each_entry_safe(cur, temp, &its->device_list, dev_list)
|
||||
vgic_its_free_device(kvm, cur);
|
||||
vgic_its_free_device(kvm, its, cur);
|
||||
}
|
||||
|
||||
/* its lock must be held */
|
||||
@@ -1250,7 +1155,7 @@ static int vgic_its_cmd_handle_mapd(struct kvm *kvm, struct vgic_its *its,
|
||||
* by removing the mapping and re-establishing it.
|
||||
*/
|
||||
if (device)
|
||||
vgic_its_free_device(kvm, device);
|
||||
vgic_its_free_device(kvm, its, device);
|
||||
|
||||
/*
|
||||
* The spec does not say whether unmapping a not-mapped device
|
||||
@@ -1281,7 +1186,7 @@ static int vgic_its_cmd_handle_mapc(struct kvm *kvm, struct vgic_its *its,
|
||||
|
||||
if (!valid) {
|
||||
vgic_its_free_collection(its, coll_id);
|
||||
vgic_its_invalidate_cache(kvm);
|
||||
vgic_its_invalidate_cache(its);
|
||||
} else {
|
||||
struct kvm_vcpu *vcpu;
|
||||
|
||||
@@ -1372,23 +1277,19 @@ static int vgic_its_cmd_handle_inv(struct kvm *kvm, struct vgic_its *its,
|
||||
int vgic_its_invall(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
struct kvm *kvm = vcpu->kvm;
|
||||
int irq_count, i = 0;
|
||||
u32 *intids;
|
||||
struct vgic_dist *dist = &kvm->arch.vgic;
|
||||
struct vgic_irq *irq;
|
||||
unsigned long intid;
|
||||
|
||||
irq_count = vgic_copy_lpi_list(kvm, vcpu, &intids);
|
||||
if (irq_count < 0)
|
||||
return irq_count;
|
||||
|
||||
for (i = 0; i < irq_count; i++) {
|
||||
struct vgic_irq *irq = vgic_get_irq(kvm, NULL, intids[i]);
|
||||
xa_for_each(&dist->lpi_xa, intid, irq) {
|
||||
irq = vgic_get_irq(kvm, NULL, intid);
|
||||
if (!irq)
|
||||
continue;
|
||||
|
||||
update_lpi_config(kvm, irq, vcpu, false);
|
||||
vgic_put_irq(kvm, irq);
|
||||
}
|
||||
|
||||
kfree(intids);
|
||||
|
||||
if (vcpu->arch.vgic_cpu.vgic_v3.its_vpe.its_vm)
|
||||
its_invall_vpe(&vcpu->arch.vgic_cpu.vgic_v3.its_vpe);
|
||||
|
||||
@@ -1431,10 +1332,10 @@ static int vgic_its_cmd_handle_invall(struct kvm *kvm, struct vgic_its *its,
|
||||
static int vgic_its_cmd_handle_movall(struct kvm *kvm, struct vgic_its *its,
|
||||
u64 *its_cmd)
|
||||
{
|
||||
struct vgic_dist *dist = &kvm->arch.vgic;
|
||||
struct kvm_vcpu *vcpu1, *vcpu2;
|
||||
struct vgic_irq *irq;
|
||||
u32 *intids;
|
||||
int irq_count, i;
|
||||
unsigned long intid;
|
||||
|
||||
/* We advertise GITS_TYPER.PTA==0, making the address the vcpu ID */
|
||||
vcpu1 = kvm_get_vcpu_by_id(kvm, its_cmd_get_target_addr(its_cmd));
|
||||
@@ -1446,12 +1347,8 @@ static int vgic_its_cmd_handle_movall(struct kvm *kvm, struct vgic_its *its,
|
||||
if (vcpu1 == vcpu2)
|
||||
return 0;
|
||||
|
||||
irq_count = vgic_copy_lpi_list(kvm, vcpu1, &intids);
|
||||
if (irq_count < 0)
|
||||
return irq_count;
|
||||
|
||||
for (i = 0; i < irq_count; i++) {
|
||||
irq = vgic_get_irq(kvm, NULL, intids[i]);
|
||||
xa_for_each(&dist->lpi_xa, intid, irq) {
|
||||
irq = vgic_get_irq(kvm, NULL, intid);
|
||||
if (!irq)
|
||||
continue;
|
||||
|
||||
@@ -1460,9 +1357,8 @@ static int vgic_its_cmd_handle_movall(struct kvm *kvm, struct vgic_its *its,
|
||||
vgic_put_irq(kvm, irq);
|
||||
}
|
||||
|
||||
vgic_its_invalidate_cache(kvm);
|
||||
vgic_its_invalidate_cache(its);
|
||||
|
||||
kfree(intids);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1813,7 +1709,7 @@ static void vgic_mmio_write_its_ctlr(struct kvm *kvm, struct vgic_its *its,
|
||||
|
||||
its->enabled = !!(val & GITS_CTLR_ENABLE);
|
||||
if (!its->enabled)
|
||||
vgic_its_invalidate_cache(kvm);
|
||||
vgic_its_invalidate_cache(its);
|
||||
|
||||
/*
|
||||
* Try to process any pending commands. This function bails out early
|
||||
@@ -1914,47 +1810,6 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Default is 16 cached LPIs per vcpu */
|
||||
#define LPI_DEFAULT_PCPU_CACHE_SIZE 16
|
||||
|
||||
void vgic_lpi_translation_cache_init(struct kvm *kvm)
|
||||
{
|
||||
struct vgic_dist *dist = &kvm->arch.vgic;
|
||||
unsigned int sz;
|
||||
int i;
|
||||
|
||||
if (!list_empty(&dist->lpi_translation_cache))
|
||||
return;
|
||||
|
||||
sz = atomic_read(&kvm->online_vcpus) * LPI_DEFAULT_PCPU_CACHE_SIZE;
|
||||
|
||||
for (i = 0; i < sz; i++) {
|
||||
struct vgic_translation_cache_entry *cte;
|
||||
|
||||
/* An allocation failure is not fatal */
|
||||
cte = kzalloc(sizeof(*cte), GFP_KERNEL_ACCOUNT);
|
||||
if (WARN_ON(!cte))
|
||||
break;
|
||||
|
||||
INIT_LIST_HEAD(&cte->entry);
|
||||
list_add(&cte->entry, &dist->lpi_translation_cache);
|
||||
}
|
||||
}
|
||||
|
||||
void vgic_lpi_translation_cache_destroy(struct kvm *kvm)
|
||||
{
|
||||
struct vgic_dist *dist = &kvm->arch.vgic;
|
||||
struct vgic_translation_cache_entry *cte, *tmp;
|
||||
|
||||
vgic_its_invalidate_cache(kvm);
|
||||
|
||||
list_for_each_entry_safe(cte, tmp,
|
||||
&dist->lpi_translation_cache, entry) {
|
||||
list_del(&cte->entry);
|
||||
kfree(cte);
|
||||
}
|
||||
}
|
||||
|
||||
#define INITIAL_BASER_VALUE \
|
||||
(GIC_BASER_CACHEABILITY(GITS_BASER, INNER, RaWb) | \
|
||||
GIC_BASER_CACHEABILITY(GITS_BASER, OUTER, SameAsInner) | \
|
||||
@@ -1987,8 +1842,6 @@ static int vgic_its_create(struct kvm_device *dev, u32 type)
|
||||
kfree(its);
|
||||
return ret;
|
||||
}
|
||||
|
||||
vgic_lpi_translation_cache_init(dev->kvm);
|
||||
}
|
||||
|
||||
mutex_init(&its->its_lock);
|
||||
@@ -2006,6 +1859,7 @@ static int vgic_its_create(struct kvm_device *dev, u32 type)
|
||||
|
||||
INIT_LIST_HEAD(&its->device_list);
|
||||
INIT_LIST_HEAD(&its->collection_list);
|
||||
xa_init(&its->translation_cache);
|
||||
|
||||
dev->kvm->arch.vgic.msis_require_devid = true;
|
||||
dev->kvm->arch.vgic.has_its = true;
|
||||
@@ -2036,6 +1890,8 @@ static void vgic_its_destroy(struct kvm_device *kvm_dev)
|
||||
|
||||
vgic_its_free_device_list(kvm, its);
|
||||
vgic_its_free_collection_list(kvm, its);
|
||||
vgic_its_invalidate_cache(its);
|
||||
xa_destroy(&its->translation_cache);
|
||||
|
||||
mutex_unlock(&its->its_lock);
|
||||
kfree(its);
|
||||
@@ -2438,7 +2294,7 @@ static int vgic_its_restore_dte(struct vgic_its *its, u32 id,
|
||||
|
||||
ret = vgic_its_restore_itt(its, dev);
|
||||
if (ret) {
|
||||
vgic_its_free_device(its->dev->kvm, dev);
|
||||
vgic_its_free_device(its->dev->kvm, its, dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
@@ -277,7 +277,7 @@ static void vgic_mmio_write_v3r_ctlr(struct kvm_vcpu *vcpu,
|
||||
return;
|
||||
|
||||
vgic_flush_pending_lpis(vcpu);
|
||||
vgic_its_invalidate_cache(vcpu->kvm);
|
||||
vgic_its_invalidate_all_caches(vcpu->kvm);
|
||||
atomic_set_release(&vgic_cpu->ctlr, 0);
|
||||
} else {
|
||||
ctlr = atomic_cmpxchg_acquire(&vgic_cpu->ctlr, 0,
|
||||
|
||||
@@ -29,9 +29,8 @@ struct vgic_global kvm_vgic_global_state __ro_after_init = {
|
||||
* its->cmd_lock (mutex)
|
||||
* its->its_lock (mutex)
|
||||
* vgic_cpu->ap_list_lock must be taken with IRQs disabled
|
||||
* kvm->lpi_list_lock must be taken with IRQs disabled
|
||||
* vgic_dist->lpi_xa.xa_lock must be taken with IRQs disabled
|
||||
* vgic_irq->irq_lock must be taken with IRQs disabled
|
||||
* vgic_dist->lpi_xa.xa_lock must be taken with IRQs disabled
|
||||
* vgic_irq->irq_lock must be taken with IRQs disabled
|
||||
*
|
||||
* As the ap_list_lock might be taken from the timer interrupt handler,
|
||||
* we have to disable IRQs before taking this lock and everything lower
|
||||
@@ -126,7 +125,6 @@ void vgic_put_irq(struct kvm *kvm, struct vgic_irq *irq)
|
||||
__xa_erase(&dist->lpi_xa, irq->intid);
|
||||
xa_unlock_irqrestore(&dist->lpi_xa, flags);
|
||||
|
||||
atomic_dec(&dist->lpi_count);
|
||||
kfree_rcu(irq, rcu);
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
#define INTERRUPT_ID_BITS_SPIS 10
|
||||
#define INTERRUPT_ID_BITS_ITS 16
|
||||
#define VGIC_LPI_MAX_INTID ((1 << INTERRUPT_ID_BITS_ITS) - 1)
|
||||
#define VGIC_PRI_BITS 5
|
||||
|
||||
#define vgic_irq_is_sgi(intid) ((intid) < VGIC_NR_SGIS)
|
||||
@@ -330,14 +331,11 @@ static inline bool vgic_dist_overlap(struct kvm *kvm, gpa_t base, size_t size)
|
||||
}
|
||||
|
||||
bool vgic_lpis_enabled(struct kvm_vcpu *vcpu);
|
||||
int vgic_copy_lpi_list(struct kvm *kvm, struct kvm_vcpu *vcpu, u32 **intid_ptr);
|
||||
int vgic_its_resolve_lpi(struct kvm *kvm, struct vgic_its *its,
|
||||
u32 devid, u32 eventid, struct vgic_irq **irq);
|
||||
struct vgic_its *vgic_msi_to_its(struct kvm *kvm, struct kvm_msi *msi);
|
||||
int vgic_its_inject_cached_translation(struct kvm *kvm, struct kvm_msi *msi);
|
||||
void vgic_lpi_translation_cache_init(struct kvm *kvm);
|
||||
void vgic_lpi_translation_cache_destroy(struct kvm *kvm);
|
||||
void vgic_its_invalidate_cache(struct kvm *kvm);
|
||||
void vgic_its_invalidate_all_caches(struct kvm *kvm);
|
||||
|
||||
/* GICv4.1 MMIO interface */
|
||||
int vgic_its_inv_lpi(struct kvm *kvm, struct vgic_irq *irq);
|
||||
|
||||
@@ -210,6 +210,12 @@ struct vgic_its {
|
||||
struct mutex its_lock;
|
||||
struct list_head device_list;
|
||||
struct list_head collection_list;
|
||||
|
||||
/*
|
||||
* Caches the (device_id, event_id) -> vgic_irq translation for
|
||||
* LPIs that are mapped and enabled.
|
||||
*/
|
||||
struct xarray translation_cache;
|
||||
};
|
||||
|
||||
struct vgic_state_iter;
|
||||
@@ -274,13 +280,8 @@ struct vgic_dist {
|
||||
*/
|
||||
u64 propbaser;
|
||||
|
||||
/* Protects the lpi_list. */
|
||||
raw_spinlock_t lpi_list_lock;
|
||||
#define LPI_XA_MARK_DEBUG_ITER XA_MARK_0
|
||||
struct xarray lpi_xa;
|
||||
atomic_t lpi_count;
|
||||
|
||||
/* LPI translation cache */
|
||||
struct list_head lpi_translation_cache;
|
||||
|
||||
/* used by vgic-debug */
|
||||
struct vgic_state_iter *iter;
|
||||
|
||||
@@ -45,6 +45,7 @@ LIBKVM_x86_64 += lib/x86_64/vmx.c
|
||||
|
||||
LIBKVM_aarch64 += lib/aarch64/gic.c
|
||||
LIBKVM_aarch64 += lib/aarch64/gic_v3.c
|
||||
LIBKVM_aarch64 += lib/aarch64/gic_v3_its.c
|
||||
LIBKVM_aarch64 += lib/aarch64/handlers.S
|
||||
LIBKVM_aarch64 += lib/aarch64/processor.c
|
||||
LIBKVM_aarch64 += lib/aarch64/spinlock.c
|
||||
@@ -157,6 +158,7 @@ TEST_GEN_PROGS_aarch64 += aarch64/smccc_filter
|
||||
TEST_GEN_PROGS_aarch64 += aarch64/vcpu_width_config
|
||||
TEST_GEN_PROGS_aarch64 += aarch64/vgic_init
|
||||
TEST_GEN_PROGS_aarch64 += aarch64/vgic_irq
|
||||
TEST_GEN_PROGS_aarch64 += aarch64/vgic_lpi_stress
|
||||
TEST_GEN_PROGS_aarch64 += aarch64/vpmu_counter_access
|
||||
TEST_GEN_PROGS_aarch64 += access_tracking_perf_test
|
||||
TEST_GEN_PROGS_aarch64 += arch_timer
|
||||
|
||||
@@ -14,9 +14,6 @@
|
||||
#include "timer_test.h"
|
||||
#include "vgic.h"
|
||||
|
||||
#define GICD_BASE_GPA 0x8000000ULL
|
||||
#define GICR_BASE_GPA 0x80A0000ULL
|
||||
|
||||
enum guest_stage {
|
||||
GUEST_STAGE_VTIMER_CVAL = 1,
|
||||
GUEST_STAGE_VTIMER_TVAL,
|
||||
@@ -149,8 +146,7 @@ static void guest_code(void)
|
||||
|
||||
local_irq_disable();
|
||||
|
||||
gic_init(GIC_V3, test_args.nr_vcpus,
|
||||
(void *)GICD_BASE_GPA, (void *)GICR_BASE_GPA);
|
||||
gic_init(GIC_V3, test_args.nr_vcpus);
|
||||
|
||||
timer_set_ctl(VIRTUAL, CTL_IMASK);
|
||||
timer_set_ctl(PHYSICAL, CTL_IMASK);
|
||||
@@ -209,7 +205,7 @@ struct kvm_vm *test_vm_create(void)
|
||||
vcpu_init_descriptor_tables(vcpus[i]);
|
||||
|
||||
test_init_timer_irq(vm);
|
||||
gic_fd = vgic_v3_setup(vm, nr_vcpus, 64, GICD_BASE_GPA, GICR_BASE_GPA);
|
||||
gic_fd = vgic_v3_setup(vm, nr_vcpus, 64);
|
||||
__TEST_REQUIRE(gic_fd >= 0, "Failed to create vgic-v3");
|
||||
|
||||
/* Make all the test's cmdline args visible to the guest */
|
||||
|
||||
@@ -13,7 +13,9 @@
|
||||
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/psci.h>
|
||||
#include <asm/cputype.h>
|
||||
|
||||
#include "kvm_util.h"
|
||||
#include "processor.h"
|
||||
|
||||
@@ -19,9 +19,6 @@
|
||||
#include "gic_v3.h"
|
||||
#include "vgic.h"
|
||||
|
||||
#define GICD_BASE_GPA 0x08000000ULL
|
||||
#define GICR_BASE_GPA 0x080A0000ULL
|
||||
|
||||
/*
|
||||
* Stores the user specified args; it's passed to the guest and to every test
|
||||
* function.
|
||||
@@ -49,9 +46,6 @@ struct test_args {
|
||||
#define IRQ_DEFAULT_PRIO (LOWEST_PRIO - 1)
|
||||
#define IRQ_DEFAULT_PRIO_REG (IRQ_DEFAULT_PRIO << KVM_PRIO_SHIFT) /* 0xf0 */
|
||||
|
||||
static void *dist = (void *)GICD_BASE_GPA;
|
||||
static void *redist = (void *)GICR_BASE_GPA;
|
||||
|
||||
/*
|
||||
* The kvm_inject_* utilities are used by the guest to ask the host to inject
|
||||
* interrupts (e.g., using the KVM_IRQ_LINE ioctl).
|
||||
@@ -152,7 +146,7 @@ static void reset_stats(void)
|
||||
|
||||
static uint64_t gic_read_ap1r0(void)
|
||||
{
|
||||
uint64_t reg = read_sysreg_s(SYS_ICV_AP1R0_EL1);
|
||||
uint64_t reg = read_sysreg_s(SYS_ICC_AP1R0_EL1);
|
||||
|
||||
dsb(sy);
|
||||
return reg;
|
||||
@@ -160,7 +154,7 @@ static uint64_t gic_read_ap1r0(void)
|
||||
|
||||
static void gic_write_ap1r0(uint64_t val)
|
||||
{
|
||||
write_sysreg_s(val, SYS_ICV_AP1R0_EL1);
|
||||
write_sysreg_s(val, SYS_ICC_AP1R0_EL1);
|
||||
isb();
|
||||
}
|
||||
|
||||
@@ -478,7 +472,7 @@ static void guest_code(struct test_args *args)
|
||||
bool level_sensitive = args->level_sensitive;
|
||||
struct kvm_inject_desc *f, *inject_fns;
|
||||
|
||||
gic_init(GIC_V3, 1, dist, redist);
|
||||
gic_init(GIC_V3, 1);
|
||||
|
||||
for (i = 0; i < nr_irqs; i++)
|
||||
gic_irq_enable(i);
|
||||
@@ -764,8 +758,7 @@ static void test_vgic(uint32_t nr_irqs, bool level_sensitive, bool eoi_split)
|
||||
memcpy(addr_gva2hva(vm, args_gva), &args, sizeof(args));
|
||||
vcpu_args_set(vcpu, 1, args_gva);
|
||||
|
||||
gic_fd = vgic_v3_setup(vm, 1, nr_irqs,
|
||||
GICD_BASE_GPA, GICR_BASE_GPA);
|
||||
gic_fd = vgic_v3_setup(vm, 1, nr_irqs);
|
||||
__TEST_REQUIRE(gic_fd >= 0, "Failed to create vgic-v3, skipping");
|
||||
|
||||
vm_install_exception_handler(vm, VECTOR_IRQ_CURRENT,
|
||||
|
||||
@@ -0,0 +1,410 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* vgic_lpi_stress - Stress test for KVM's ITS emulation
|
||||
*
|
||||
* Copyright (c) 2024 Google LLC
|
||||
*/
|
||||
|
||||
#include <linux/sizes.h>
|
||||
#include <pthread.h>
|
||||
#include <stdatomic.h>
|
||||
#include <sys/sysinfo.h>
|
||||
|
||||
#include "kvm_util.h"
|
||||
#include "gic.h"
|
||||
#include "gic_v3.h"
|
||||
#include "gic_v3_its.h"
|
||||
#include "processor.h"
|
||||
#include "ucall.h"
|
||||
#include "vgic.h"
|
||||
|
||||
#define TEST_MEMSLOT_INDEX 1
|
||||
|
||||
#define GIC_LPI_OFFSET 8192
|
||||
|
||||
static size_t nr_iterations = 1000;
|
||||
static vm_paddr_t gpa_base;
|
||||
|
||||
static struct kvm_vm *vm;
|
||||
static struct kvm_vcpu **vcpus;
|
||||
static int gic_fd, its_fd;
|
||||
|
||||
static struct test_data {
|
||||
bool request_vcpus_stop;
|
||||
u32 nr_cpus;
|
||||
u32 nr_devices;
|
||||
u32 nr_event_ids;
|
||||
|
||||
vm_paddr_t device_table;
|
||||
vm_paddr_t collection_table;
|
||||
vm_paddr_t cmdq_base;
|
||||
void *cmdq_base_va;
|
||||
vm_paddr_t itt_tables;
|
||||
|
||||
vm_paddr_t lpi_prop_table;
|
||||
vm_paddr_t lpi_pend_tables;
|
||||
} test_data = {
|
||||
.nr_cpus = 1,
|
||||
.nr_devices = 1,
|
||||
.nr_event_ids = 16,
|
||||
};
|
||||
|
||||
static void guest_irq_handler(struct ex_regs *regs)
|
||||
{
|
||||
u32 intid = gic_get_and_ack_irq();
|
||||
|
||||
if (intid == IAR_SPURIOUS)
|
||||
return;
|
||||
|
||||
GUEST_ASSERT(intid >= GIC_LPI_OFFSET);
|
||||
gic_set_eoi(intid);
|
||||
}
|
||||
|
||||
static void guest_setup_its_mappings(void)
|
||||
{
|
||||
u32 coll_id, device_id, event_id, intid = GIC_LPI_OFFSET;
|
||||
u32 nr_events = test_data.nr_event_ids;
|
||||
u32 nr_devices = test_data.nr_devices;
|
||||
u32 nr_cpus = test_data.nr_cpus;
|
||||
|
||||
for (coll_id = 0; coll_id < nr_cpus; coll_id++)
|
||||
its_send_mapc_cmd(test_data.cmdq_base_va, coll_id, coll_id, true);
|
||||
|
||||
/* Round-robin the LPIs to all of the vCPUs in the VM */
|
||||
coll_id = 0;
|
||||
for (device_id = 0; device_id < nr_devices; device_id++) {
|
||||
vm_paddr_t itt_base = test_data.itt_tables + (device_id * SZ_64K);
|
||||
|
||||
its_send_mapd_cmd(test_data.cmdq_base_va, device_id,
|
||||
itt_base, SZ_64K, true);
|
||||
|
||||
for (event_id = 0; event_id < nr_events; event_id++) {
|
||||
its_send_mapti_cmd(test_data.cmdq_base_va, device_id,
|
||||
event_id, coll_id, intid++);
|
||||
|
||||
coll_id = (coll_id + 1) % test_data.nr_cpus;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void guest_invalidate_all_rdists(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < test_data.nr_cpus; i++)
|
||||
its_send_invall_cmd(test_data.cmdq_base_va, i);
|
||||
}
|
||||
|
||||
static void guest_setup_gic(void)
|
||||
{
|
||||
static atomic_int nr_cpus_ready = 0;
|
||||
u32 cpuid = guest_get_vcpuid();
|
||||
|
||||
gic_init(GIC_V3, test_data.nr_cpus);
|
||||
gic_rdist_enable_lpis(test_data.lpi_prop_table, SZ_64K,
|
||||
test_data.lpi_pend_tables + (cpuid * SZ_64K));
|
||||
|
||||
atomic_fetch_add(&nr_cpus_ready, 1);
|
||||
|
||||
if (cpuid > 0)
|
||||
return;
|
||||
|
||||
while (atomic_load(&nr_cpus_ready) < test_data.nr_cpus)
|
||||
cpu_relax();
|
||||
|
||||
its_init(test_data.collection_table, SZ_64K,
|
||||
test_data.device_table, SZ_64K,
|
||||
test_data.cmdq_base, SZ_64K);
|
||||
|
||||
guest_setup_its_mappings();
|
||||
guest_invalidate_all_rdists();
|
||||
}
|
||||
|
||||
static void guest_code(size_t nr_lpis)
|
||||
{
|
||||
guest_setup_gic();
|
||||
|
||||
GUEST_SYNC(0);
|
||||
|
||||
/*
|
||||
* Don't use WFI here to avoid blocking the vCPU thread indefinitely and
|
||||
* never getting the stop signal.
|
||||
*/
|
||||
while (!READ_ONCE(test_data.request_vcpus_stop))
|
||||
cpu_relax();
|
||||
|
||||
GUEST_DONE();
|
||||
}
|
||||
|
||||
static void setup_memslot(void)
|
||||
{
|
||||
size_t pages;
|
||||
size_t sz;
|
||||
|
||||
/*
|
||||
* For the ITS:
|
||||
* - A single level device table
|
||||
* - A single level collection table
|
||||
* - The command queue
|
||||
* - An ITT for each device
|
||||
*/
|
||||
sz = (3 + test_data.nr_devices) * SZ_64K;
|
||||
|
||||
/*
|
||||
* For the redistributors:
|
||||
* - A shared LPI configuration table
|
||||
* - An LPI pending table for each vCPU
|
||||
*/
|
||||
sz += (1 + test_data.nr_cpus) * SZ_64K;
|
||||
|
||||
pages = sz / vm->page_size;
|
||||
gpa_base = ((vm_compute_max_gfn(vm) + 1) * vm->page_size) - sz;
|
||||
vm_userspace_mem_region_add(vm, VM_MEM_SRC_ANONYMOUS, gpa_base,
|
||||
TEST_MEMSLOT_INDEX, pages, 0);
|
||||
}
|
||||
|
||||
#define LPI_PROP_DEFAULT_PRIO 0xa0
|
||||
|
||||
static void configure_lpis(void)
|
||||
{
|
||||
size_t nr_lpis = test_data.nr_devices * test_data.nr_event_ids;
|
||||
u8 *tbl = addr_gpa2hva(vm, test_data.lpi_prop_table);
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < nr_lpis; i++) {
|
||||
tbl[i] = LPI_PROP_DEFAULT_PRIO |
|
||||
LPI_PROP_GROUP1 |
|
||||
LPI_PROP_ENABLED;
|
||||
}
|
||||
}
|
||||
|
||||
static void setup_test_data(void)
|
||||
{
|
||||
size_t pages_per_64k = vm_calc_num_guest_pages(vm->mode, SZ_64K);
|
||||
u32 nr_devices = test_data.nr_devices;
|
||||
u32 nr_cpus = test_data.nr_cpus;
|
||||
vm_paddr_t cmdq_base;
|
||||
|
||||
test_data.device_table = vm_phy_pages_alloc(vm, pages_per_64k,
|
||||
gpa_base,
|
||||
TEST_MEMSLOT_INDEX);
|
||||
|
||||
test_data.collection_table = vm_phy_pages_alloc(vm, pages_per_64k,
|
||||
gpa_base,
|
||||
TEST_MEMSLOT_INDEX);
|
||||
|
||||
cmdq_base = vm_phy_pages_alloc(vm, pages_per_64k, gpa_base,
|
||||
TEST_MEMSLOT_INDEX);
|
||||
virt_map(vm, cmdq_base, cmdq_base, pages_per_64k);
|
||||
test_data.cmdq_base = cmdq_base;
|
||||
test_data.cmdq_base_va = (void *)cmdq_base;
|
||||
|
||||
test_data.itt_tables = vm_phy_pages_alloc(vm, pages_per_64k * nr_devices,
|
||||
gpa_base, TEST_MEMSLOT_INDEX);
|
||||
|
||||
test_data.lpi_prop_table = vm_phy_pages_alloc(vm, pages_per_64k,
|
||||
gpa_base, TEST_MEMSLOT_INDEX);
|
||||
configure_lpis();
|
||||
|
||||
test_data.lpi_pend_tables = vm_phy_pages_alloc(vm, pages_per_64k * nr_cpus,
|
||||
gpa_base, TEST_MEMSLOT_INDEX);
|
||||
|
||||
sync_global_to_guest(vm, test_data);
|
||||
}
|
||||
|
||||
static void setup_gic(void)
|
||||
{
|
||||
gic_fd = vgic_v3_setup(vm, test_data.nr_cpus, 64);
|
||||
__TEST_REQUIRE(gic_fd >= 0, "Failed to create GICv3");
|
||||
|
||||
its_fd = vgic_its_setup(vm);
|
||||
}
|
||||
|
||||
static void signal_lpi(u32 device_id, u32 event_id)
|
||||
{
|
||||
vm_paddr_t db_addr = GITS_BASE_GPA + GITS_TRANSLATER;
|
||||
|
||||
struct kvm_msi msi = {
|
||||
.address_lo = db_addr,
|
||||
.address_hi = db_addr >> 32,
|
||||
.data = event_id,
|
||||
.devid = device_id,
|
||||
.flags = KVM_MSI_VALID_DEVID,
|
||||
};
|
||||
|
||||
/*
|
||||
* KVM_SIGNAL_MSI returns 1 if the MSI wasn't 'blocked' by the VM,
|
||||
* which for arm64 implies having a valid translation in the ITS.
|
||||
*/
|
||||
TEST_ASSERT(__vm_ioctl(vm, KVM_SIGNAL_MSI, &msi) == 1,
|
||||
"KVM_SIGNAL_MSI ioctl failed");
|
||||
}
|
||||
|
||||
static pthread_barrier_t test_setup_barrier;
|
||||
|
||||
static void *lpi_worker_thread(void *data)
|
||||
{
|
||||
u32 device_id = (size_t)data;
|
||||
u32 event_id;
|
||||
size_t i;
|
||||
|
||||
pthread_barrier_wait(&test_setup_barrier);
|
||||
|
||||
for (i = 0; i < nr_iterations; i++)
|
||||
for (event_id = 0; event_id < test_data.nr_event_ids; event_id++)
|
||||
signal_lpi(device_id, event_id);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void *vcpu_worker_thread(void *data)
|
||||
{
|
||||
struct kvm_vcpu *vcpu = data;
|
||||
struct ucall uc;
|
||||
|
||||
while (true) {
|
||||
vcpu_run(vcpu);
|
||||
|
||||
switch (get_ucall(vcpu, &uc)) {
|
||||
case UCALL_SYNC:
|
||||
pthread_barrier_wait(&test_setup_barrier);
|
||||
continue;
|
||||
case UCALL_DONE:
|
||||
return NULL;
|
||||
case UCALL_ABORT:
|
||||
REPORT_GUEST_ASSERT(uc);
|
||||
break;
|
||||
default:
|
||||
TEST_FAIL("Unknown ucall: %lu", uc.cmd);
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void report_stats(struct timespec delta)
|
||||
{
|
||||
double nr_lpis;
|
||||
double time;
|
||||
|
||||
nr_lpis = test_data.nr_devices * test_data.nr_event_ids * nr_iterations;
|
||||
|
||||
time = delta.tv_sec;
|
||||
time += ((double)delta.tv_nsec) / NSEC_PER_SEC;
|
||||
|
||||
pr_info("Rate: %.2f LPIs/sec\n", nr_lpis / time);
|
||||
}
|
||||
|
||||
static void run_test(void)
|
||||
{
|
||||
u32 nr_devices = test_data.nr_devices;
|
||||
u32 nr_vcpus = test_data.nr_cpus;
|
||||
pthread_t *lpi_threads = malloc(nr_devices * sizeof(pthread_t));
|
||||
pthread_t *vcpu_threads = malloc(nr_vcpus * sizeof(pthread_t));
|
||||
struct timespec start, delta;
|
||||
size_t i;
|
||||
|
||||
TEST_ASSERT(lpi_threads && vcpu_threads, "Failed to allocate pthread arrays");
|
||||
|
||||
pthread_barrier_init(&test_setup_barrier, NULL, nr_vcpus + nr_devices + 1);
|
||||
|
||||
for (i = 0; i < nr_vcpus; i++)
|
||||
pthread_create(&vcpu_threads[i], NULL, vcpu_worker_thread, vcpus[i]);
|
||||
|
||||
for (i = 0; i < nr_devices; i++)
|
||||
pthread_create(&lpi_threads[i], NULL, lpi_worker_thread, (void *)i);
|
||||
|
||||
pthread_barrier_wait(&test_setup_barrier);
|
||||
|
||||
clock_gettime(CLOCK_MONOTONIC, &start);
|
||||
|
||||
for (i = 0; i < nr_devices; i++)
|
||||
pthread_join(lpi_threads[i], NULL);
|
||||
|
||||
delta = timespec_elapsed(start);
|
||||
write_guest_global(vm, test_data.request_vcpus_stop, true);
|
||||
|
||||
for (i = 0; i < nr_vcpus; i++)
|
||||
pthread_join(vcpu_threads[i], NULL);
|
||||
|
||||
report_stats(delta);
|
||||
}
|
||||
|
||||
static void setup_vm(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
vcpus = malloc(test_data.nr_cpus * sizeof(struct kvm_vcpu));
|
||||
TEST_ASSERT(vcpus, "Failed to allocate vCPU array");
|
||||
|
||||
vm = vm_create_with_vcpus(test_data.nr_cpus, guest_code, vcpus);
|
||||
|
||||
vm_init_descriptor_tables(vm);
|
||||
for (i = 0; i < test_data.nr_cpus; i++)
|
||||
vcpu_init_descriptor_tables(vcpus[i]);
|
||||
|
||||
vm_install_exception_handler(vm, VECTOR_IRQ_CURRENT, guest_irq_handler);
|
||||
|
||||
setup_memslot();
|
||||
|
||||
setup_gic();
|
||||
|
||||
setup_test_data();
|
||||
}
|
||||
|
||||
static void destroy_vm(void)
|
||||
{
|
||||
close(its_fd);
|
||||
close(gic_fd);
|
||||
kvm_vm_free(vm);
|
||||
free(vcpus);
|
||||
}
|
||||
|
||||
static void pr_usage(const char *name)
|
||||
{
|
||||
pr_info("%s [-v NR_VCPUS] [-d NR_DEVICES] [-e NR_EVENTS] [-i ITERS] -h\n", name);
|
||||
pr_info(" -v:\tnumber of vCPUs (default: %u)\n", test_data.nr_cpus);
|
||||
pr_info(" -d:\tnumber of devices (default: %u)\n", test_data.nr_devices);
|
||||
pr_info(" -e:\tnumber of event IDs per device (default: %u)\n", test_data.nr_event_ids);
|
||||
pr_info(" -i:\tnumber of iterations (default: %lu)\n", nr_iterations);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
u32 nr_threads;
|
||||
int c;
|
||||
|
||||
while ((c = getopt(argc, argv, "hv:d:e:i:")) != -1) {
|
||||
switch (c) {
|
||||
case 'v':
|
||||
test_data.nr_cpus = atoi(optarg);
|
||||
break;
|
||||
case 'd':
|
||||
test_data.nr_devices = atoi(optarg);
|
||||
break;
|
||||
case 'e':
|
||||
test_data.nr_event_ids = atoi(optarg);
|
||||
break;
|
||||
case 'i':
|
||||
nr_iterations = strtoul(optarg, NULL, 0);
|
||||
break;
|
||||
case 'h':
|
||||
default:
|
||||
pr_usage(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
nr_threads = test_data.nr_cpus + test_data.nr_devices;
|
||||
if (nr_threads > get_nprocs())
|
||||
pr_info("WARNING: running %u threads on %d CPUs; performance is degraded.\n",
|
||||
nr_threads, get_nprocs());
|
||||
|
||||
setup_vm();
|
||||
|
||||
run_test();
|
||||
|
||||
destroy_vm();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -404,9 +404,6 @@ static void guest_code(uint64_t expected_pmcr_n)
|
||||
GUEST_DONE();
|
||||
}
|
||||
|
||||
#define GICD_BASE_GPA 0x8000000ULL
|
||||
#define GICR_BASE_GPA 0x80A0000ULL
|
||||
|
||||
/* Create a VM that has one vCPU with PMUv3 configured. */
|
||||
static void create_vpmu_vm(void *guest_code)
|
||||
{
|
||||
@@ -438,8 +435,7 @@ static void create_vpmu_vm(void *guest_code)
|
||||
init.features[0] |= (1 << KVM_ARM_VCPU_PMU_V3);
|
||||
vpmu_vm.vcpu = aarch64_vcpu_add(vpmu_vm.vm, 0, &init, guest_code);
|
||||
vcpu_init_descriptor_tables(vpmu_vm.vcpu);
|
||||
vpmu_vm.gic_fd = vgic_v3_setup(vpmu_vm.vm, 1, 64,
|
||||
GICD_BASE_GPA, GICR_BASE_GPA);
|
||||
vpmu_vm.gic_fd = vgic_v3_setup(vpmu_vm.vm, 1, 64);
|
||||
__TEST_REQUIRE(vpmu_vm.gic_fd >= 0,
|
||||
"Failed to create vgic-v3, skipping");
|
||||
|
||||
|
||||
@@ -22,9 +22,6 @@
|
||||
#ifdef __aarch64__
|
||||
#include "aarch64/vgic.h"
|
||||
|
||||
#define GICD_BASE_GPA 0x8000000ULL
|
||||
#define GICR_BASE_GPA 0x80A0000ULL
|
||||
|
||||
static int gic_fd;
|
||||
|
||||
static void arch_setup_vm(struct kvm_vm *vm, unsigned int nr_vcpus)
|
||||
@@ -33,7 +30,7 @@ static void arch_setup_vm(struct kvm_vm *vm, unsigned int nr_vcpus)
|
||||
* The test can still run even if hardware does not support GICv3, as it
|
||||
* is only an optimization to reduce guest exits.
|
||||
*/
|
||||
gic_fd = vgic_v3_setup(vm, nr_vcpus, 64, GICD_BASE_GPA, GICR_BASE_GPA);
|
||||
gic_fd = vgic_v3_setup(vm, nr_vcpus, 64);
|
||||
}
|
||||
|
||||
static void arch_cleanup_vm(struct kvm_vm *vm)
|
||||
|
||||
@@ -6,11 +6,26 @@
|
||||
#ifndef SELFTEST_KVM_GIC_H
|
||||
#define SELFTEST_KVM_GIC_H
|
||||
|
||||
#include <asm/kvm.h>
|
||||
|
||||
enum gic_type {
|
||||
GIC_V3,
|
||||
GIC_TYPE_MAX,
|
||||
};
|
||||
|
||||
/*
|
||||
* Note that the redistributor frames are at the end, as the range scales
|
||||
* with the number of vCPUs in the VM.
|
||||
*/
|
||||
#define GITS_BASE_GPA 0x8000000ULL
|
||||
#define GICD_BASE_GPA (GITS_BASE_GPA + KVM_VGIC_V3_ITS_SIZE)
|
||||
#define GICR_BASE_GPA (GICD_BASE_GPA + KVM_VGIC_V3_DIST_SIZE)
|
||||
|
||||
/* The GIC is identity-mapped into the guest at the time of setup. */
|
||||
#define GITS_BASE_GVA ((volatile void *)GITS_BASE_GPA)
|
||||
#define GICD_BASE_GVA ((volatile void *)GICD_BASE_GPA)
|
||||
#define GICR_BASE_GVA ((volatile void *)GICR_BASE_GPA)
|
||||
|
||||
#define MIN_SGI 0
|
||||
#define MIN_PPI 16
|
||||
#define MIN_SPI 32
|
||||
@@ -21,8 +36,7 @@ enum gic_type {
|
||||
#define INTID_IS_PPI(intid) (MIN_PPI <= (intid) && (intid) < MIN_SPI)
|
||||
#define INTID_IS_SPI(intid) (MIN_SPI <= (intid) && (intid) <= MAX_SPI)
|
||||
|
||||
void gic_init(enum gic_type type, unsigned int nr_cpus,
|
||||
void *dist_base, void *redist_base);
|
||||
void gic_init(enum gic_type type, unsigned int nr_cpus);
|
||||
void gic_irq_enable(unsigned int intid);
|
||||
void gic_irq_disable(unsigned int intid);
|
||||
unsigned int gic_get_and_ack_irq(void);
|
||||
@@ -44,4 +58,7 @@ void gic_irq_clear_pending(unsigned int intid);
|
||||
bool gic_irq_get_pending(unsigned int intid);
|
||||
void gic_irq_set_config(unsigned int intid, bool is_edge);
|
||||
|
||||
void gic_rdist_enable_lpis(vm_paddr_t cfg_table, size_t cfg_table_size,
|
||||
vm_paddr_t pend_table);
|
||||
|
||||
#endif /* SELFTEST_KVM_GIC_H */
|
||||
|
||||
@@ -1,82 +1,604 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* ARM Generic Interrupt Controller (GIC) v3 specific defines
|
||||
* Copyright (C) 2013, 2014 ARM Limited, All Rights Reserved.
|
||||
* Author: Marc Zyngier <marc.zyngier@arm.com>
|
||||
*/
|
||||
|
||||
#ifndef SELFTEST_KVM_GICV3_H
|
||||
#define SELFTEST_KVM_GICV3_H
|
||||
|
||||
#include <asm/sysreg.h>
|
||||
#ifndef __SELFTESTS_GIC_V3_H
|
||||
#define __SELFTESTS_GIC_V3_H
|
||||
|
||||
/*
|
||||
* Distributor registers
|
||||
* Distributor registers. We assume we're running non-secure, with ARE
|
||||
* being set. Secure-only and non-ARE registers are not described.
|
||||
*/
|
||||
#define GICD_CTLR 0x0000
|
||||
#define GICD_TYPER 0x0004
|
||||
#define GICD_IIDR 0x0008
|
||||
#define GICD_TYPER2 0x000C
|
||||
#define GICD_STATUSR 0x0010
|
||||
#define GICD_SETSPI_NSR 0x0040
|
||||
#define GICD_CLRSPI_NSR 0x0048
|
||||
#define GICD_SETSPI_SR 0x0050
|
||||
#define GICD_CLRSPI_SR 0x0058
|
||||
#define GICD_IGROUPR 0x0080
|
||||
#define GICD_ISENABLER 0x0100
|
||||
#define GICD_ICENABLER 0x0180
|
||||
#define GICD_ISPENDR 0x0200
|
||||
#define GICD_ICPENDR 0x0280
|
||||
#define GICD_ICACTIVER 0x0380
|
||||
#define GICD_ISACTIVER 0x0300
|
||||
#define GICD_ICACTIVER 0x0380
|
||||
#define GICD_IPRIORITYR 0x0400
|
||||
#define GICD_ICFGR 0x0C00
|
||||
#define GICD_IGRPMODR 0x0D00
|
||||
#define GICD_NSACR 0x0E00
|
||||
#define GICD_IGROUPRnE 0x1000
|
||||
#define GICD_ISENABLERnE 0x1200
|
||||
#define GICD_ICENABLERnE 0x1400
|
||||
#define GICD_ISPENDRnE 0x1600
|
||||
#define GICD_ICPENDRnE 0x1800
|
||||
#define GICD_ISACTIVERnE 0x1A00
|
||||
#define GICD_ICACTIVERnE 0x1C00
|
||||
#define GICD_IPRIORITYRnE 0x2000
|
||||
#define GICD_ICFGRnE 0x3000
|
||||
#define GICD_IROUTER 0x6000
|
||||
#define GICD_IROUTERnE 0x8000
|
||||
#define GICD_IDREGS 0xFFD0
|
||||
#define GICD_PIDR2 0xFFE8
|
||||
|
||||
#define ESPI_BASE_INTID 4096
|
||||
|
||||
/*
|
||||
* The assumption is that the guest runs in a non-secure mode.
|
||||
* The following bits of GICD_CTLR are defined accordingly.
|
||||
* Those registers are actually from GICv2, but the spec demands that they
|
||||
* are implemented as RES0 if ARE is 1 (which we do in KVM's emulated GICv3).
|
||||
*/
|
||||
#define GICD_ITARGETSR 0x0800
|
||||
#define GICD_SGIR 0x0F00
|
||||
#define GICD_CPENDSGIR 0x0F10
|
||||
#define GICD_SPENDSGIR 0x0F20
|
||||
|
||||
#define GICD_CTLR_RWP (1U << 31)
|
||||
#define GICD_CTLR_nASSGIreq (1U << 8)
|
||||
#define GICD_CTLR_DS (1U << 6)
|
||||
#define GICD_CTLR_ARE_NS (1U << 4)
|
||||
#define GICD_CTLR_ENABLE_G1A (1U << 1)
|
||||
#define GICD_CTLR_ENABLE_G1 (1U << 0)
|
||||
|
||||
#define GICD_TYPER_SPIS(typer) ((((typer) & 0x1f) + 1) * 32)
|
||||
#define GICD_INT_DEF_PRI_X4 0xa0a0a0a0
|
||||
#define GICD_IIDR_IMPLEMENTER_SHIFT 0
|
||||
#define GICD_IIDR_IMPLEMENTER_MASK (0xfff << GICD_IIDR_IMPLEMENTER_SHIFT)
|
||||
#define GICD_IIDR_REVISION_SHIFT 12
|
||||
#define GICD_IIDR_REVISION_MASK (0xf << GICD_IIDR_REVISION_SHIFT)
|
||||
#define GICD_IIDR_VARIANT_SHIFT 16
|
||||
#define GICD_IIDR_VARIANT_MASK (0xf << GICD_IIDR_VARIANT_SHIFT)
|
||||
#define GICD_IIDR_PRODUCT_ID_SHIFT 24
|
||||
#define GICD_IIDR_PRODUCT_ID_MASK (0xff << GICD_IIDR_PRODUCT_ID_SHIFT)
|
||||
|
||||
|
||||
/*
|
||||
* Redistributor registers
|
||||
* In systems with a single security state (what we emulate in KVM)
|
||||
* the meaning of the interrupt group enable bits is slightly different
|
||||
*/
|
||||
#define GICR_CTLR 0x000
|
||||
#define GICR_WAKER 0x014
|
||||
#define GICD_CTLR_ENABLE_SS_G1 (1U << 1)
|
||||
#define GICD_CTLR_ENABLE_SS_G0 (1U << 0)
|
||||
|
||||
#define GICR_CTLR_RWP (1U << 3)
|
||||
#define GICD_TYPER_RSS (1U << 26)
|
||||
#define GICD_TYPER_LPIS (1U << 17)
|
||||
#define GICD_TYPER_MBIS (1U << 16)
|
||||
#define GICD_TYPER_ESPI (1U << 8)
|
||||
|
||||
#define GICD_TYPER_ID_BITS(typer) ((((typer) >> 19) & 0x1f) + 1)
|
||||
#define GICD_TYPER_NUM_LPIS(typer) ((((typer) >> 11) & 0x1f) + 1)
|
||||
#define GICD_TYPER_SPIS(typer) ((((typer) & 0x1f) + 1) * 32)
|
||||
#define GICD_TYPER_ESPIS(typer) \
|
||||
(((typer) & GICD_TYPER_ESPI) ? GICD_TYPER_SPIS((typer) >> 27) : 0)
|
||||
|
||||
#define GICD_TYPER2_nASSGIcap (1U << 8)
|
||||
#define GICD_TYPER2_VIL (1U << 7)
|
||||
#define GICD_TYPER2_VID GENMASK(4, 0)
|
||||
|
||||
#define GICD_IROUTER_SPI_MODE_ONE (0U << 31)
|
||||
#define GICD_IROUTER_SPI_MODE_ANY (1U << 31)
|
||||
|
||||
#define GIC_PIDR2_ARCH_MASK 0xf0
|
||||
#define GIC_PIDR2_ARCH_GICv3 0x30
|
||||
#define GIC_PIDR2_ARCH_GICv4 0x40
|
||||
|
||||
#define GIC_V3_DIST_SIZE 0x10000
|
||||
|
||||
#define GIC_PAGE_SIZE_4K 0ULL
|
||||
#define GIC_PAGE_SIZE_16K 1ULL
|
||||
#define GIC_PAGE_SIZE_64K 2ULL
|
||||
#define GIC_PAGE_SIZE_MASK 3ULL
|
||||
|
||||
/*
|
||||
* Re-Distributor registers, offsets from RD_base
|
||||
*/
|
||||
#define GICR_CTLR GICD_CTLR
|
||||
#define GICR_IIDR 0x0004
|
||||
#define GICR_TYPER 0x0008
|
||||
#define GICR_STATUSR GICD_STATUSR
|
||||
#define GICR_WAKER 0x0014
|
||||
#define GICR_SETLPIR 0x0040
|
||||
#define GICR_CLRLPIR 0x0048
|
||||
#define GICR_PROPBASER 0x0070
|
||||
#define GICR_PENDBASER 0x0078
|
||||
#define GICR_INVLPIR 0x00A0
|
||||
#define GICR_INVALLR 0x00B0
|
||||
#define GICR_SYNCR 0x00C0
|
||||
#define GICR_IDREGS GICD_IDREGS
|
||||
#define GICR_PIDR2 GICD_PIDR2
|
||||
|
||||
#define GICR_CTLR_ENABLE_LPIS (1UL << 0)
|
||||
#define GICR_CTLR_CES (1UL << 1)
|
||||
#define GICR_CTLR_IR (1UL << 2)
|
||||
#define GICR_CTLR_RWP (1UL << 3)
|
||||
|
||||
#define GICR_TYPER_CPU_NUMBER(r) (((r) >> 8) & 0xffff)
|
||||
|
||||
#define EPPI_BASE_INTID 1056
|
||||
|
||||
#define GICR_TYPER_NR_PPIS(r) \
|
||||
({ \
|
||||
unsigned int __ppinum = ((r) >> 27) & 0x1f; \
|
||||
unsigned int __nr_ppis = 16; \
|
||||
if (__ppinum == 1 || __ppinum == 2) \
|
||||
__nr_ppis += __ppinum * 32; \
|
||||
\
|
||||
__nr_ppis; \
|
||||
})
|
||||
|
||||
#define GICR_WAKER_ProcessorSleep (1U << 1)
|
||||
#define GICR_WAKER_ChildrenAsleep (1U << 2)
|
||||
|
||||
#define GIC_BASER_CACHE_nCnB 0ULL
|
||||
#define GIC_BASER_CACHE_SameAsInner 0ULL
|
||||
#define GIC_BASER_CACHE_nC 1ULL
|
||||
#define GIC_BASER_CACHE_RaWt 2ULL
|
||||
#define GIC_BASER_CACHE_RaWb 3ULL
|
||||
#define GIC_BASER_CACHE_WaWt 4ULL
|
||||
#define GIC_BASER_CACHE_WaWb 5ULL
|
||||
#define GIC_BASER_CACHE_RaWaWt 6ULL
|
||||
#define GIC_BASER_CACHE_RaWaWb 7ULL
|
||||
#define GIC_BASER_CACHE_MASK 7ULL
|
||||
#define GIC_BASER_NonShareable 0ULL
|
||||
#define GIC_BASER_InnerShareable 1ULL
|
||||
#define GIC_BASER_OuterShareable 2ULL
|
||||
#define GIC_BASER_SHAREABILITY_MASK 3ULL
|
||||
|
||||
#define GIC_BASER_CACHEABILITY(reg, inner_outer, type) \
|
||||
(GIC_BASER_CACHE_##type << reg##_##inner_outer##_CACHEABILITY_SHIFT)
|
||||
|
||||
#define GIC_BASER_SHAREABILITY(reg, type) \
|
||||
(GIC_BASER_##type << reg##_SHAREABILITY_SHIFT)
|
||||
|
||||
/* encode a size field of width @w containing @n - 1 units */
|
||||
#define GIC_ENCODE_SZ(n, w) (((unsigned long)(n) - 1) & GENMASK_ULL(((w) - 1), 0))
|
||||
|
||||
#define GICR_PROPBASER_SHAREABILITY_SHIFT (10)
|
||||
#define GICR_PROPBASER_INNER_CACHEABILITY_SHIFT (7)
|
||||
#define GICR_PROPBASER_OUTER_CACHEABILITY_SHIFT (56)
|
||||
#define GICR_PROPBASER_SHAREABILITY_MASK \
|
||||
GIC_BASER_SHAREABILITY(GICR_PROPBASER, SHAREABILITY_MASK)
|
||||
#define GICR_PROPBASER_INNER_CACHEABILITY_MASK \
|
||||
GIC_BASER_CACHEABILITY(GICR_PROPBASER, INNER, MASK)
|
||||
#define GICR_PROPBASER_OUTER_CACHEABILITY_MASK \
|
||||
GIC_BASER_CACHEABILITY(GICR_PROPBASER, OUTER, MASK)
|
||||
#define GICR_PROPBASER_CACHEABILITY_MASK GICR_PROPBASER_INNER_CACHEABILITY_MASK
|
||||
|
||||
#define GICR_PROPBASER_InnerShareable \
|
||||
GIC_BASER_SHAREABILITY(GICR_PROPBASER, InnerShareable)
|
||||
|
||||
#define GICR_PROPBASER_nCnB GIC_BASER_CACHEABILITY(GICR_PROPBASER, INNER, nCnB)
|
||||
#define GICR_PROPBASER_nC GIC_BASER_CACHEABILITY(GICR_PROPBASER, INNER, nC)
|
||||
#define GICR_PROPBASER_RaWt GIC_BASER_CACHEABILITY(GICR_PROPBASER, INNER, RaWt)
|
||||
#define GICR_PROPBASER_RaWb GIC_BASER_CACHEABILITY(GICR_PROPBASER, INNER, RaWb)
|
||||
#define GICR_PROPBASER_WaWt GIC_BASER_CACHEABILITY(GICR_PROPBASER, INNER, WaWt)
|
||||
#define GICR_PROPBASER_WaWb GIC_BASER_CACHEABILITY(GICR_PROPBASER, INNER, WaWb)
|
||||
#define GICR_PROPBASER_RaWaWt GIC_BASER_CACHEABILITY(GICR_PROPBASER, INNER, RaWaWt)
|
||||
#define GICR_PROPBASER_RaWaWb GIC_BASER_CACHEABILITY(GICR_PROPBASER, INNER, RaWaWb)
|
||||
|
||||
#define GICR_PROPBASER_IDBITS_MASK (0x1f)
|
||||
#define GICR_PROPBASER_ADDRESS(x) ((x) & GENMASK_ULL(51, 12))
|
||||
#define GICR_PENDBASER_ADDRESS(x) ((x) & GENMASK_ULL(51, 16))
|
||||
|
||||
#define GICR_PENDBASER_SHAREABILITY_SHIFT (10)
|
||||
#define GICR_PENDBASER_INNER_CACHEABILITY_SHIFT (7)
|
||||
#define GICR_PENDBASER_OUTER_CACHEABILITY_SHIFT (56)
|
||||
#define GICR_PENDBASER_SHAREABILITY_MASK \
|
||||
GIC_BASER_SHAREABILITY(GICR_PENDBASER, SHAREABILITY_MASK)
|
||||
#define GICR_PENDBASER_INNER_CACHEABILITY_MASK \
|
||||
GIC_BASER_CACHEABILITY(GICR_PENDBASER, INNER, MASK)
|
||||
#define GICR_PENDBASER_OUTER_CACHEABILITY_MASK \
|
||||
GIC_BASER_CACHEABILITY(GICR_PENDBASER, OUTER, MASK)
|
||||
#define GICR_PENDBASER_CACHEABILITY_MASK GICR_PENDBASER_INNER_CACHEABILITY_MASK
|
||||
|
||||
#define GICR_PENDBASER_InnerShareable \
|
||||
GIC_BASER_SHAREABILITY(GICR_PENDBASER, InnerShareable)
|
||||
|
||||
#define GICR_PENDBASER_nCnB GIC_BASER_CACHEABILITY(GICR_PENDBASER, INNER, nCnB)
|
||||
#define GICR_PENDBASER_nC GIC_BASER_CACHEABILITY(GICR_PENDBASER, INNER, nC)
|
||||
#define GICR_PENDBASER_RaWt GIC_BASER_CACHEABILITY(GICR_PENDBASER, INNER, RaWt)
|
||||
#define GICR_PENDBASER_RaWb GIC_BASER_CACHEABILITY(GICR_PENDBASER, INNER, RaWb)
|
||||
#define GICR_PENDBASER_WaWt GIC_BASER_CACHEABILITY(GICR_PENDBASER, INNER, WaWt)
|
||||
#define GICR_PENDBASER_WaWb GIC_BASER_CACHEABILITY(GICR_PENDBASER, INNER, WaWb)
|
||||
#define GICR_PENDBASER_RaWaWt GIC_BASER_CACHEABILITY(GICR_PENDBASER, INNER, RaWaWt)
|
||||
#define GICR_PENDBASER_RaWaWb GIC_BASER_CACHEABILITY(GICR_PENDBASER, INNER, RaWaWb)
|
||||
|
||||
#define GICR_PENDBASER_PTZ BIT_ULL(62)
|
||||
|
||||
/*
|
||||
* Redistributor registers, offsets from SGI base
|
||||
* Re-Distributor registers, offsets from SGI_base
|
||||
*/
|
||||
#define GICR_IGROUPR0 GICD_IGROUPR
|
||||
#define GICR_ISENABLER0 GICD_ISENABLER
|
||||
#define GICR_ICENABLER0 GICD_ICENABLER
|
||||
#define GICR_ISPENDR0 GICD_ISPENDR
|
||||
#define GICR_ICPENDR0 GICD_ICPENDR
|
||||
#define GICR_ISACTIVER0 GICD_ISACTIVER
|
||||
#define GICR_ICACTIVER0 GICD_ICACTIVER
|
||||
#define GICR_ICENABLER GICD_ICENABLER
|
||||
#define GICR_ICACTIVER GICD_ICACTIVER
|
||||
#define GICR_IPRIORITYR0 GICD_IPRIORITYR
|
||||
#define GICR_ICFGR0 GICD_ICFGR
|
||||
#define GICR_IGRPMODR0 GICD_IGRPMODR
|
||||
#define GICR_NSACR GICD_NSACR
|
||||
|
||||
/* CPU interface registers */
|
||||
#define SYS_ICC_PMR_EL1 sys_reg(3, 0, 4, 6, 0)
|
||||
#define SYS_ICC_IAR1_EL1 sys_reg(3, 0, 12, 12, 0)
|
||||
#define SYS_ICC_EOIR1_EL1 sys_reg(3, 0, 12, 12, 1)
|
||||
#define SYS_ICC_DIR_EL1 sys_reg(3, 0, 12, 11, 1)
|
||||
#define SYS_ICC_CTLR_EL1 sys_reg(3, 0, 12, 12, 4)
|
||||
#define SYS_ICC_SRE_EL1 sys_reg(3, 0, 12, 12, 5)
|
||||
#define SYS_ICC_GRPEN1_EL1 sys_reg(3, 0, 12, 12, 7)
|
||||
#define GICR_TYPER_PLPIS (1U << 0)
|
||||
#define GICR_TYPER_VLPIS (1U << 1)
|
||||
#define GICR_TYPER_DIRTY (1U << 2)
|
||||
#define GICR_TYPER_DirectLPIS (1U << 3)
|
||||
#define GICR_TYPER_LAST (1U << 4)
|
||||
#define GICR_TYPER_RVPEID (1U << 7)
|
||||
#define GICR_TYPER_COMMON_LPI_AFF GENMASK_ULL(25, 24)
|
||||
#define GICR_TYPER_AFFINITY GENMASK_ULL(63, 32)
|
||||
|
||||
#define SYS_ICV_AP1R0_EL1 sys_reg(3, 0, 12, 9, 0)
|
||||
#define GICR_INVLPIR_INTID GENMASK_ULL(31, 0)
|
||||
#define GICR_INVLPIR_VPEID GENMASK_ULL(47, 32)
|
||||
#define GICR_INVLPIR_V GENMASK_ULL(63, 63)
|
||||
|
||||
#define ICC_PMR_DEF_PRIO 0xf0
|
||||
#define GICR_INVALLR_VPEID GICR_INVLPIR_VPEID
|
||||
#define GICR_INVALLR_V GICR_INVLPIR_V
|
||||
|
||||
#define GIC_V3_REDIST_SIZE 0x20000
|
||||
|
||||
#define LPI_PROP_GROUP1 (1 << 1)
|
||||
#define LPI_PROP_ENABLED (1 << 0)
|
||||
|
||||
/*
|
||||
* Re-Distributor registers, offsets from VLPI_base
|
||||
*/
|
||||
#define GICR_VPROPBASER 0x0070
|
||||
|
||||
#define GICR_VPROPBASER_IDBITS_MASK 0x1f
|
||||
|
||||
#define GICR_VPROPBASER_SHAREABILITY_SHIFT (10)
|
||||
#define GICR_VPROPBASER_INNER_CACHEABILITY_SHIFT (7)
|
||||
#define GICR_VPROPBASER_OUTER_CACHEABILITY_SHIFT (56)
|
||||
|
||||
#define GICR_VPROPBASER_SHAREABILITY_MASK \
|
||||
GIC_BASER_SHAREABILITY(GICR_VPROPBASER, SHAREABILITY_MASK)
|
||||
#define GICR_VPROPBASER_INNER_CACHEABILITY_MASK \
|
||||
GIC_BASER_CACHEABILITY(GICR_VPROPBASER, INNER, MASK)
|
||||
#define GICR_VPROPBASER_OUTER_CACHEABILITY_MASK \
|
||||
GIC_BASER_CACHEABILITY(GICR_VPROPBASER, OUTER, MASK)
|
||||
#define GICR_VPROPBASER_CACHEABILITY_MASK \
|
||||
GICR_VPROPBASER_INNER_CACHEABILITY_MASK
|
||||
|
||||
#define GICR_VPROPBASER_InnerShareable \
|
||||
GIC_BASER_SHAREABILITY(GICR_VPROPBASER, InnerShareable)
|
||||
|
||||
#define GICR_VPROPBASER_nCnB GIC_BASER_CACHEABILITY(GICR_VPROPBASER, INNER, nCnB)
|
||||
#define GICR_VPROPBASER_nC GIC_BASER_CACHEABILITY(GICR_VPROPBASER, INNER, nC)
|
||||
#define GICR_VPROPBASER_RaWt GIC_BASER_CACHEABILITY(GICR_VPROPBASER, INNER, RaWt)
|
||||
#define GICR_VPROPBASER_RaWb GIC_BASER_CACHEABILITY(GICR_VPROPBASER, INNER, RaWb)
|
||||
#define GICR_VPROPBASER_WaWt GIC_BASER_CACHEABILITY(GICR_VPROPBASER, INNER, WaWt)
|
||||
#define GICR_VPROPBASER_WaWb GIC_BASER_CACHEABILITY(GICR_VPROPBASER, INNER, WaWb)
|
||||
#define GICR_VPROPBASER_RaWaWt GIC_BASER_CACHEABILITY(GICR_VPROPBASER, INNER, RaWaWt)
|
||||
#define GICR_VPROPBASER_RaWaWb GIC_BASER_CACHEABILITY(GICR_VPROPBASER, INNER, RaWaWb)
|
||||
|
||||
/*
|
||||
* GICv4.1 VPROPBASER reinvention. A subtle mix between the old
|
||||
* VPROPBASER and ITS_BASER. Just not quite any of the two.
|
||||
*/
|
||||
#define GICR_VPROPBASER_4_1_VALID (1ULL << 63)
|
||||
#define GICR_VPROPBASER_4_1_ENTRY_SIZE GENMASK_ULL(61, 59)
|
||||
#define GICR_VPROPBASER_4_1_INDIRECT (1ULL << 55)
|
||||
#define GICR_VPROPBASER_4_1_PAGE_SIZE GENMASK_ULL(54, 53)
|
||||
#define GICR_VPROPBASER_4_1_Z (1ULL << 52)
|
||||
#define GICR_VPROPBASER_4_1_ADDR GENMASK_ULL(51, 12)
|
||||
#define GICR_VPROPBASER_4_1_SIZE GENMASK_ULL(6, 0)
|
||||
|
||||
#define GICR_VPENDBASER 0x0078
|
||||
|
||||
#define GICR_VPENDBASER_SHAREABILITY_SHIFT (10)
|
||||
#define GICR_VPENDBASER_INNER_CACHEABILITY_SHIFT (7)
|
||||
#define GICR_VPENDBASER_OUTER_CACHEABILITY_SHIFT (56)
|
||||
#define GICR_VPENDBASER_SHAREABILITY_MASK \
|
||||
GIC_BASER_SHAREABILITY(GICR_VPENDBASER, SHAREABILITY_MASK)
|
||||
#define GICR_VPENDBASER_INNER_CACHEABILITY_MASK \
|
||||
GIC_BASER_CACHEABILITY(GICR_VPENDBASER, INNER, MASK)
|
||||
#define GICR_VPENDBASER_OUTER_CACHEABILITY_MASK \
|
||||
GIC_BASER_CACHEABILITY(GICR_VPENDBASER, OUTER, MASK)
|
||||
#define GICR_VPENDBASER_CACHEABILITY_MASK \
|
||||
GICR_VPENDBASER_INNER_CACHEABILITY_MASK
|
||||
|
||||
#define GICR_VPENDBASER_NonShareable \
|
||||
GIC_BASER_SHAREABILITY(GICR_VPENDBASER, NonShareable)
|
||||
|
||||
#define GICR_VPENDBASER_InnerShareable \
|
||||
GIC_BASER_SHAREABILITY(GICR_VPENDBASER, InnerShareable)
|
||||
|
||||
#define GICR_VPENDBASER_nCnB GIC_BASER_CACHEABILITY(GICR_VPENDBASER, INNER, nCnB)
|
||||
#define GICR_VPENDBASER_nC GIC_BASER_CACHEABILITY(GICR_VPENDBASER, INNER, nC)
|
||||
#define GICR_VPENDBASER_RaWt GIC_BASER_CACHEABILITY(GICR_VPENDBASER, INNER, RaWt)
|
||||
#define GICR_VPENDBASER_RaWb GIC_BASER_CACHEABILITY(GICR_VPENDBASER, INNER, RaWb)
|
||||
#define GICR_VPENDBASER_WaWt GIC_BASER_CACHEABILITY(GICR_VPENDBASER, INNER, WaWt)
|
||||
#define GICR_VPENDBASER_WaWb GIC_BASER_CACHEABILITY(GICR_VPENDBASER, INNER, WaWb)
|
||||
#define GICR_VPENDBASER_RaWaWt GIC_BASER_CACHEABILITY(GICR_VPENDBASER, INNER, RaWaWt)
|
||||
#define GICR_VPENDBASER_RaWaWb GIC_BASER_CACHEABILITY(GICR_VPENDBASER, INNER, RaWaWb)
|
||||
|
||||
#define GICR_VPENDBASER_Dirty (1ULL << 60)
|
||||
#define GICR_VPENDBASER_PendingLast (1ULL << 61)
|
||||
#define GICR_VPENDBASER_IDAI (1ULL << 62)
|
||||
#define GICR_VPENDBASER_Valid (1ULL << 63)
|
||||
|
||||
/*
|
||||
* GICv4.1 VPENDBASER, used for VPE residency. On top of these fields,
|
||||
* also use the above Valid, PendingLast and Dirty.
|
||||
*/
|
||||
#define GICR_VPENDBASER_4_1_DB (1ULL << 62)
|
||||
#define GICR_VPENDBASER_4_1_VGRP0EN (1ULL << 59)
|
||||
#define GICR_VPENDBASER_4_1_VGRP1EN (1ULL << 58)
|
||||
#define GICR_VPENDBASER_4_1_VPEID GENMASK_ULL(15, 0)
|
||||
|
||||
#define GICR_VSGIR 0x0080
|
||||
|
||||
#define GICR_VSGIR_VPEID GENMASK(15, 0)
|
||||
|
||||
#define GICR_VSGIPENDR 0x0088
|
||||
|
||||
#define GICR_VSGIPENDR_BUSY (1U << 31)
|
||||
#define GICR_VSGIPENDR_PENDING GENMASK(15, 0)
|
||||
|
||||
/*
|
||||
* ITS registers, offsets from ITS_base
|
||||
*/
|
||||
#define GITS_CTLR 0x0000
|
||||
#define GITS_IIDR 0x0004
|
||||
#define GITS_TYPER 0x0008
|
||||
#define GITS_MPIDR 0x0018
|
||||
#define GITS_CBASER 0x0080
|
||||
#define GITS_CWRITER 0x0088
|
||||
#define GITS_CREADR 0x0090
|
||||
#define GITS_BASER 0x0100
|
||||
#define GITS_IDREGS_BASE 0xffd0
|
||||
#define GITS_PIDR0 0xffe0
|
||||
#define GITS_PIDR1 0xffe4
|
||||
#define GITS_PIDR2 GICR_PIDR2
|
||||
#define GITS_PIDR4 0xffd0
|
||||
#define GITS_CIDR0 0xfff0
|
||||
#define GITS_CIDR1 0xfff4
|
||||
#define GITS_CIDR2 0xfff8
|
||||
#define GITS_CIDR3 0xfffc
|
||||
|
||||
#define GITS_TRANSLATER 0x10040
|
||||
|
||||
#define GITS_SGIR 0x20020
|
||||
|
||||
#define GITS_SGIR_VPEID GENMASK_ULL(47, 32)
|
||||
#define GITS_SGIR_VINTID GENMASK_ULL(3, 0)
|
||||
|
||||
#define GITS_CTLR_ENABLE (1U << 0)
|
||||
#define GITS_CTLR_ImDe (1U << 1)
|
||||
#define GITS_CTLR_ITS_NUMBER_SHIFT 4
|
||||
#define GITS_CTLR_ITS_NUMBER (0xFU << GITS_CTLR_ITS_NUMBER_SHIFT)
|
||||
#define GITS_CTLR_QUIESCENT (1U << 31)
|
||||
|
||||
#define GITS_TYPER_PLPIS (1UL << 0)
|
||||
#define GITS_TYPER_VLPIS (1UL << 1)
|
||||
#define GITS_TYPER_ITT_ENTRY_SIZE_SHIFT 4
|
||||
#define GITS_TYPER_ITT_ENTRY_SIZE GENMASK_ULL(7, 4)
|
||||
#define GITS_TYPER_IDBITS_SHIFT 8
|
||||
#define GITS_TYPER_DEVBITS_SHIFT 13
|
||||
#define GITS_TYPER_DEVBITS GENMASK_ULL(17, 13)
|
||||
#define GITS_TYPER_PTA (1UL << 19)
|
||||
#define GITS_TYPER_HCC_SHIFT 24
|
||||
#define GITS_TYPER_HCC(r) (((r) >> GITS_TYPER_HCC_SHIFT) & 0xff)
|
||||
#define GITS_TYPER_VMOVP (1ULL << 37)
|
||||
#define GITS_TYPER_VMAPP (1ULL << 40)
|
||||
#define GITS_TYPER_SVPET GENMASK_ULL(42, 41)
|
||||
|
||||
#define GITS_IIDR_REV_SHIFT 12
|
||||
#define GITS_IIDR_REV_MASK (0xf << GITS_IIDR_REV_SHIFT)
|
||||
#define GITS_IIDR_REV(r) (((r) >> GITS_IIDR_REV_SHIFT) & 0xf)
|
||||
#define GITS_IIDR_PRODUCTID_SHIFT 24
|
||||
|
||||
#define GITS_CBASER_VALID (1ULL << 63)
|
||||
#define GITS_CBASER_SHAREABILITY_SHIFT (10)
|
||||
#define GITS_CBASER_INNER_CACHEABILITY_SHIFT (59)
|
||||
#define GITS_CBASER_OUTER_CACHEABILITY_SHIFT (53)
|
||||
#define GITS_CBASER_SHAREABILITY_MASK \
|
||||
GIC_BASER_SHAREABILITY(GITS_CBASER, SHAREABILITY_MASK)
|
||||
#define GITS_CBASER_INNER_CACHEABILITY_MASK \
|
||||
GIC_BASER_CACHEABILITY(GITS_CBASER, INNER, MASK)
|
||||
#define GITS_CBASER_OUTER_CACHEABILITY_MASK \
|
||||
GIC_BASER_CACHEABILITY(GITS_CBASER, OUTER, MASK)
|
||||
#define GITS_CBASER_CACHEABILITY_MASK GITS_CBASER_INNER_CACHEABILITY_MASK
|
||||
|
||||
#define GITS_CBASER_InnerShareable \
|
||||
GIC_BASER_SHAREABILITY(GITS_CBASER, InnerShareable)
|
||||
|
||||
#define GITS_CBASER_nCnB GIC_BASER_CACHEABILITY(GITS_CBASER, INNER, nCnB)
|
||||
#define GITS_CBASER_nC GIC_BASER_CACHEABILITY(GITS_CBASER, INNER, nC)
|
||||
#define GITS_CBASER_RaWt GIC_BASER_CACHEABILITY(GITS_CBASER, INNER, RaWt)
|
||||
#define GITS_CBASER_RaWb GIC_BASER_CACHEABILITY(GITS_CBASER, INNER, RaWb)
|
||||
#define GITS_CBASER_WaWt GIC_BASER_CACHEABILITY(GITS_CBASER, INNER, WaWt)
|
||||
#define GITS_CBASER_WaWb GIC_BASER_CACHEABILITY(GITS_CBASER, INNER, WaWb)
|
||||
#define GITS_CBASER_RaWaWt GIC_BASER_CACHEABILITY(GITS_CBASER, INNER, RaWaWt)
|
||||
#define GITS_CBASER_RaWaWb GIC_BASER_CACHEABILITY(GITS_CBASER, INNER, RaWaWb)
|
||||
|
||||
#define GITS_CBASER_ADDRESS(cbaser) ((cbaser) & GENMASK_ULL(51, 12))
|
||||
|
||||
#define GITS_BASER_NR_REGS 8
|
||||
|
||||
#define GITS_BASER_VALID (1ULL << 63)
|
||||
#define GITS_BASER_INDIRECT (1ULL << 62)
|
||||
|
||||
#define GITS_BASER_INNER_CACHEABILITY_SHIFT (59)
|
||||
#define GITS_BASER_OUTER_CACHEABILITY_SHIFT (53)
|
||||
#define GITS_BASER_INNER_CACHEABILITY_MASK \
|
||||
GIC_BASER_CACHEABILITY(GITS_BASER, INNER, MASK)
|
||||
#define GITS_BASER_CACHEABILITY_MASK GITS_BASER_INNER_CACHEABILITY_MASK
|
||||
#define GITS_BASER_OUTER_CACHEABILITY_MASK \
|
||||
GIC_BASER_CACHEABILITY(GITS_BASER, OUTER, MASK)
|
||||
#define GITS_BASER_SHAREABILITY_MASK \
|
||||
GIC_BASER_SHAREABILITY(GITS_BASER, SHAREABILITY_MASK)
|
||||
|
||||
#define GITS_BASER_nCnB GIC_BASER_CACHEABILITY(GITS_BASER, INNER, nCnB)
|
||||
#define GITS_BASER_nC GIC_BASER_CACHEABILITY(GITS_BASER, INNER, nC)
|
||||
#define GITS_BASER_RaWt GIC_BASER_CACHEABILITY(GITS_BASER, INNER, RaWt)
|
||||
#define GITS_BASER_RaWb GIC_BASER_CACHEABILITY(GITS_BASER, INNER, RaWb)
|
||||
#define GITS_BASER_WaWt GIC_BASER_CACHEABILITY(GITS_BASER, INNER, WaWt)
|
||||
#define GITS_BASER_WaWb GIC_BASER_CACHEABILITY(GITS_BASER, INNER, WaWb)
|
||||
#define GITS_BASER_RaWaWt GIC_BASER_CACHEABILITY(GITS_BASER, INNER, RaWaWt)
|
||||
#define GITS_BASER_RaWaWb GIC_BASER_CACHEABILITY(GITS_BASER, INNER, RaWaWb)
|
||||
|
||||
#define GITS_BASER_TYPE_SHIFT (56)
|
||||
#define GITS_BASER_TYPE(r) (((r) >> GITS_BASER_TYPE_SHIFT) & 7)
|
||||
#define GITS_BASER_ENTRY_SIZE_SHIFT (48)
|
||||
#define GITS_BASER_ENTRY_SIZE(r) ((((r) >> GITS_BASER_ENTRY_SIZE_SHIFT) & 0x1f) + 1)
|
||||
#define GITS_BASER_ENTRY_SIZE_MASK GENMASK_ULL(52, 48)
|
||||
#define GITS_BASER_PHYS_52_to_48(phys) \
|
||||
(((phys) & GENMASK_ULL(47, 16)) | (((phys) >> 48) & 0xf) << 12)
|
||||
#define GITS_BASER_ADDR_48_to_52(baser) \
|
||||
(((baser) & GENMASK_ULL(47, 16)) | (((baser) >> 12) & 0xf) << 48)
|
||||
|
||||
#define GITS_BASER_SHAREABILITY_SHIFT (10)
|
||||
#define GITS_BASER_InnerShareable \
|
||||
GIC_BASER_SHAREABILITY(GITS_BASER, InnerShareable)
|
||||
#define GITS_BASER_PAGE_SIZE_SHIFT (8)
|
||||
#define __GITS_BASER_PSZ(sz) (GIC_PAGE_SIZE_ ## sz << GITS_BASER_PAGE_SIZE_SHIFT)
|
||||
#define GITS_BASER_PAGE_SIZE_4K __GITS_BASER_PSZ(4K)
|
||||
#define GITS_BASER_PAGE_SIZE_16K __GITS_BASER_PSZ(16K)
|
||||
#define GITS_BASER_PAGE_SIZE_64K __GITS_BASER_PSZ(64K)
|
||||
#define GITS_BASER_PAGE_SIZE_MASK __GITS_BASER_PSZ(MASK)
|
||||
#define GITS_BASER_PAGES_MAX 256
|
||||
#define GITS_BASER_PAGES_SHIFT (0)
|
||||
#define GITS_BASER_NR_PAGES(r) (((r) & 0xff) + 1)
|
||||
|
||||
#define GITS_BASER_TYPE_NONE 0
|
||||
#define GITS_BASER_TYPE_DEVICE 1
|
||||
#define GITS_BASER_TYPE_VCPU 2
|
||||
#define GITS_BASER_TYPE_RESERVED3 3
|
||||
#define GITS_BASER_TYPE_COLLECTION 4
|
||||
#define GITS_BASER_TYPE_RESERVED5 5
|
||||
#define GITS_BASER_TYPE_RESERVED6 6
|
||||
#define GITS_BASER_TYPE_RESERVED7 7
|
||||
|
||||
#define GITS_LVL1_ENTRY_SIZE (8UL)
|
||||
|
||||
/*
|
||||
* ITS commands
|
||||
*/
|
||||
#define GITS_CMD_MAPD 0x08
|
||||
#define GITS_CMD_MAPC 0x09
|
||||
#define GITS_CMD_MAPTI 0x0a
|
||||
#define GITS_CMD_MAPI 0x0b
|
||||
#define GITS_CMD_MOVI 0x01
|
||||
#define GITS_CMD_DISCARD 0x0f
|
||||
#define GITS_CMD_INV 0x0c
|
||||
#define GITS_CMD_MOVALL 0x0e
|
||||
#define GITS_CMD_INVALL 0x0d
|
||||
#define GITS_CMD_INT 0x03
|
||||
#define GITS_CMD_CLEAR 0x04
|
||||
#define GITS_CMD_SYNC 0x05
|
||||
|
||||
/*
|
||||
* GICv4 ITS specific commands
|
||||
*/
|
||||
#define GITS_CMD_GICv4(x) ((x) | 0x20)
|
||||
#define GITS_CMD_VINVALL GITS_CMD_GICv4(GITS_CMD_INVALL)
|
||||
#define GITS_CMD_VMAPP GITS_CMD_GICv4(GITS_CMD_MAPC)
|
||||
#define GITS_CMD_VMAPTI GITS_CMD_GICv4(GITS_CMD_MAPTI)
|
||||
#define GITS_CMD_VMOVI GITS_CMD_GICv4(GITS_CMD_MOVI)
|
||||
#define GITS_CMD_VSYNC GITS_CMD_GICv4(GITS_CMD_SYNC)
|
||||
/* VMOVP, VSGI and INVDB are the odd ones, as they dont have a physical counterpart */
|
||||
#define GITS_CMD_VMOVP GITS_CMD_GICv4(2)
|
||||
#define GITS_CMD_VSGI GITS_CMD_GICv4(3)
|
||||
#define GITS_CMD_INVDB GITS_CMD_GICv4(0xe)
|
||||
|
||||
/*
|
||||
* ITS error numbers
|
||||
*/
|
||||
#define E_ITS_MOVI_UNMAPPED_INTERRUPT 0x010107
|
||||
#define E_ITS_MOVI_UNMAPPED_COLLECTION 0x010109
|
||||
#define E_ITS_INT_UNMAPPED_INTERRUPT 0x010307
|
||||
#define E_ITS_CLEAR_UNMAPPED_INTERRUPT 0x010507
|
||||
#define E_ITS_MAPD_DEVICE_OOR 0x010801
|
||||
#define E_ITS_MAPD_ITTSIZE_OOR 0x010802
|
||||
#define E_ITS_MAPC_PROCNUM_OOR 0x010902
|
||||
#define E_ITS_MAPC_COLLECTION_OOR 0x010903
|
||||
#define E_ITS_MAPTI_UNMAPPED_DEVICE 0x010a04
|
||||
#define E_ITS_MAPTI_ID_OOR 0x010a05
|
||||
#define E_ITS_MAPTI_PHYSICALID_OOR 0x010a06
|
||||
#define E_ITS_INV_UNMAPPED_INTERRUPT 0x010c07
|
||||
#define E_ITS_INVALL_UNMAPPED_COLLECTION 0x010d09
|
||||
#define E_ITS_MOVALL_PROCNUM_OOR 0x010e01
|
||||
#define E_ITS_DISCARD_UNMAPPED_INTERRUPT 0x010f07
|
||||
|
||||
/*
|
||||
* CPU interface registers
|
||||
*/
|
||||
#define ICC_CTLR_EL1_EOImode_SHIFT (1)
|
||||
#define ICC_CTLR_EL1_EOImode_drop_dir (0U << ICC_CTLR_EL1_EOImode_SHIFT)
|
||||
#define ICC_CTLR_EL1_EOImode_drop (1U << ICC_CTLR_EL1_EOImode_SHIFT)
|
||||
#define ICC_CTLR_EL1_EOImode_MASK (1 << ICC_CTLR_EL1_EOImode_SHIFT)
|
||||
#define ICC_CTLR_EL1_CBPR_SHIFT 0
|
||||
#define ICC_CTLR_EL1_CBPR_MASK (1 << ICC_CTLR_EL1_CBPR_SHIFT)
|
||||
#define ICC_CTLR_EL1_PMHE_SHIFT 6
|
||||
#define ICC_CTLR_EL1_PMHE_MASK (1 << ICC_CTLR_EL1_PMHE_SHIFT)
|
||||
#define ICC_CTLR_EL1_PRI_BITS_SHIFT 8
|
||||
#define ICC_CTLR_EL1_PRI_BITS_MASK (0x7 << ICC_CTLR_EL1_PRI_BITS_SHIFT)
|
||||
#define ICC_CTLR_EL1_ID_BITS_SHIFT 11
|
||||
#define ICC_CTLR_EL1_ID_BITS_MASK (0x7 << ICC_CTLR_EL1_ID_BITS_SHIFT)
|
||||
#define ICC_CTLR_EL1_SEIS_SHIFT 14
|
||||
#define ICC_CTLR_EL1_SEIS_MASK (0x1 << ICC_CTLR_EL1_SEIS_SHIFT)
|
||||
#define ICC_CTLR_EL1_A3V_SHIFT 15
|
||||
#define ICC_CTLR_EL1_A3V_MASK (0x1 << ICC_CTLR_EL1_A3V_SHIFT)
|
||||
#define ICC_CTLR_EL1_RSS (0x1 << 18)
|
||||
#define ICC_CTLR_EL1_ExtRange (0x1 << 19)
|
||||
#define ICC_PMR_EL1_SHIFT 0
|
||||
#define ICC_PMR_EL1_MASK (0xff << ICC_PMR_EL1_SHIFT)
|
||||
#define ICC_BPR0_EL1_SHIFT 0
|
||||
#define ICC_BPR0_EL1_MASK (0x7 << ICC_BPR0_EL1_SHIFT)
|
||||
#define ICC_BPR1_EL1_SHIFT 0
|
||||
#define ICC_BPR1_EL1_MASK (0x7 << ICC_BPR1_EL1_SHIFT)
|
||||
#define ICC_IGRPEN0_EL1_SHIFT 0
|
||||
#define ICC_IGRPEN0_EL1_MASK (1 << ICC_IGRPEN0_EL1_SHIFT)
|
||||
#define ICC_IGRPEN1_EL1_SHIFT 0
|
||||
#define ICC_IGRPEN1_EL1_MASK (1 << ICC_IGRPEN1_EL1_SHIFT)
|
||||
#define ICC_SRE_EL1_DIB (1U << 2)
|
||||
#define ICC_SRE_EL1_DFB (1U << 1)
|
||||
#define ICC_SRE_EL1_SRE (1U << 0)
|
||||
|
||||
#define ICC_IGRPEN1_EL1_ENABLE (1U << 0)
|
||||
/* These are for GICv2 emulation only */
|
||||
#define GICH_LR_VIRTUALID (0x3ffUL << 0)
|
||||
#define GICH_LR_PHYSID_CPUID_SHIFT (10)
|
||||
#define GICH_LR_PHYSID_CPUID (7UL << GICH_LR_PHYSID_CPUID_SHIFT)
|
||||
|
||||
#define GICV3_MAX_CPUS 512
|
||||
#define ICC_IAR1_EL1_SPURIOUS 0x3ff
|
||||
|
||||
#endif /* SELFTEST_KVM_GICV3_H */
|
||||
#define ICC_SRE_EL2_SRE (1 << 0)
|
||||
#define ICC_SRE_EL2_ENABLE (1 << 3)
|
||||
|
||||
#define ICC_SGI1R_TARGET_LIST_SHIFT 0
|
||||
#define ICC_SGI1R_TARGET_LIST_MASK (0xffff << ICC_SGI1R_TARGET_LIST_SHIFT)
|
||||
#define ICC_SGI1R_AFFINITY_1_SHIFT 16
|
||||
#define ICC_SGI1R_AFFINITY_1_MASK (0xff << ICC_SGI1R_AFFINITY_1_SHIFT)
|
||||
#define ICC_SGI1R_SGI_ID_SHIFT 24
|
||||
#define ICC_SGI1R_SGI_ID_MASK (0xfULL << ICC_SGI1R_SGI_ID_SHIFT)
|
||||
#define ICC_SGI1R_AFFINITY_2_SHIFT 32
|
||||
#define ICC_SGI1R_AFFINITY_2_MASK (0xffULL << ICC_SGI1R_AFFINITY_2_SHIFT)
|
||||
#define ICC_SGI1R_IRQ_ROUTING_MODE_BIT 40
|
||||
#define ICC_SGI1R_RS_SHIFT 44
|
||||
#define ICC_SGI1R_RS_MASK (0xfULL << ICC_SGI1R_RS_SHIFT)
|
||||
#define ICC_SGI1R_AFFINITY_3_SHIFT 48
|
||||
#define ICC_SGI1R_AFFINITY_3_MASK (0xffULL << ICC_SGI1R_AFFINITY_3_SHIFT)
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
|
||||
#ifndef __SELFTESTS_GIC_V3_ITS_H__
|
||||
#define __SELFTESTS_GIC_V3_ITS_H__
|
||||
|
||||
#include <linux/sizes.h>
|
||||
|
||||
void its_init(vm_paddr_t coll_tbl, size_t coll_tbl_sz,
|
||||
vm_paddr_t device_tbl, size_t device_tbl_sz,
|
||||
vm_paddr_t cmdq, size_t cmdq_size);
|
||||
|
||||
void its_send_mapd_cmd(void *cmdq_base, u32 device_id, vm_paddr_t itt_base,
|
||||
size_t itt_size, bool valid);
|
||||
void its_send_mapc_cmd(void *cmdq_base, u32 vcpu_id, u32 collection_id, bool valid);
|
||||
void its_send_mapti_cmd(void *cmdq_base, u32 device_id, u32 event_id,
|
||||
u32 collection_id, u32 intid);
|
||||
void its_send_invall_cmd(void *cmdq_base, u32 collection_id);
|
||||
|
||||
#endif // __SELFTESTS_GIC_V3_ITS_H__
|
||||
@@ -58,8 +58,6 @@
|
||||
MAIR_ATTRIDX(MAIR_ATTR_NORMAL, MT_NORMAL) | \
|
||||
MAIR_ATTRIDX(MAIR_ATTR_NORMAL_WT, MT_NORMAL_WT))
|
||||
|
||||
#define MPIDR_HWID_BITMASK (0xff00fffffful)
|
||||
|
||||
void aarch64_vcpu_setup(struct kvm_vcpu *vcpu, struct kvm_vcpu_init *init);
|
||||
struct kvm_vcpu *aarch64_vcpu_add(struct kvm_vm *vm, uint32_t vcpu_id,
|
||||
struct kvm_vcpu_init *init, void *guest_code);
|
||||
@@ -177,11 +175,28 @@ static __always_inline u32 __raw_readl(const volatile void *addr)
|
||||
return val;
|
||||
}
|
||||
|
||||
static __always_inline void __raw_writeq(u64 val, volatile void *addr)
|
||||
{
|
||||
asm volatile("str %0, [%1]" : : "rZ" (val), "r" (addr));
|
||||
}
|
||||
|
||||
static __always_inline u64 __raw_readq(const volatile void *addr)
|
||||
{
|
||||
u64 val;
|
||||
asm volatile("ldr %0, [%1]" : "=r" (val) : "r" (addr));
|
||||
return val;
|
||||
}
|
||||
|
||||
#define writel_relaxed(v,c) ((void)__raw_writel((__force u32)cpu_to_le32(v),(c)))
|
||||
#define readl_relaxed(c) ({ u32 __r = le32_to_cpu((__force __le32)__raw_readl(c)); __r; })
|
||||
#define writeq_relaxed(v,c) ((void)__raw_writeq((__force u64)cpu_to_le64(v),(c)))
|
||||
#define readq_relaxed(c) ({ u64 __r = le64_to_cpu((__force __le64)__raw_readq(c)); __r; })
|
||||
|
||||
#define writel(v,c) ({ __iowmb(); writel_relaxed((v),(c));})
|
||||
#define readl(c) ({ u32 __v = readl_relaxed(c); __iormb(__v); __v; })
|
||||
#define writeq(v,c) ({ __iowmb(); writeq_relaxed((v),(c));})
|
||||
#define readq(c) ({ u64 __v = readq_relaxed(c); __iormb(__v); __v; })
|
||||
|
||||
|
||||
static inline void local_irq_enable(void)
|
||||
{
|
||||
|
||||
@@ -16,8 +16,7 @@
|
||||
((uint64_t)(flags) << 12) | \
|
||||
index)
|
||||
|
||||
int vgic_v3_setup(struct kvm_vm *vm, unsigned int nr_vcpus, uint32_t nr_irqs,
|
||||
uint64_t gicd_base_gpa, uint64_t gicr_base_gpa);
|
||||
int vgic_v3_setup(struct kvm_vm *vm, unsigned int nr_vcpus, uint32_t nr_irqs);
|
||||
|
||||
#define VGIC_MAX_RESERVED 1023
|
||||
|
||||
@@ -33,4 +32,6 @@ void kvm_irq_write_isactiver(int gic_fd, uint32_t intid, struct kvm_vcpu *vcpu);
|
||||
|
||||
#define KVM_IRQCHIP_NUM_PINS (1020 - 32)
|
||||
|
||||
int vgic_its_setup(struct kvm_vm *vm);
|
||||
|
||||
#endif // SELFTEST_KVM_VGIC_H
|
||||
|
||||
@@ -17,13 +17,12 @@
|
||||
static const struct gic_common_ops *gic_common_ops;
|
||||
static struct spinlock gic_lock;
|
||||
|
||||
static void gic_cpu_init(unsigned int cpu, void *redist_base)
|
||||
static void gic_cpu_init(unsigned int cpu)
|
||||
{
|
||||
gic_common_ops->gic_cpu_init(cpu, redist_base);
|
||||
gic_common_ops->gic_cpu_init(cpu);
|
||||
}
|
||||
|
||||
static void
|
||||
gic_dist_init(enum gic_type type, unsigned int nr_cpus, void *dist_base)
|
||||
static void gic_dist_init(enum gic_type type, unsigned int nr_cpus)
|
||||
{
|
||||
const struct gic_common_ops *gic_ops = NULL;
|
||||
|
||||
@@ -40,7 +39,7 @@ gic_dist_init(enum gic_type type, unsigned int nr_cpus, void *dist_base)
|
||||
|
||||
GUEST_ASSERT(gic_ops);
|
||||
|
||||
gic_ops->gic_init(nr_cpus, dist_base);
|
||||
gic_ops->gic_init(nr_cpus);
|
||||
gic_common_ops = gic_ops;
|
||||
|
||||
/* Make sure that the initialized data is visible to all the vCPUs */
|
||||
@@ -49,18 +48,15 @@ gic_dist_init(enum gic_type type, unsigned int nr_cpus, void *dist_base)
|
||||
spin_unlock(&gic_lock);
|
||||
}
|
||||
|
||||
void gic_init(enum gic_type type, unsigned int nr_cpus,
|
||||
void *dist_base, void *redist_base)
|
||||
void gic_init(enum gic_type type, unsigned int nr_cpus)
|
||||
{
|
||||
uint32_t cpu = guest_get_vcpuid();
|
||||
|
||||
GUEST_ASSERT(type < GIC_TYPE_MAX);
|
||||
GUEST_ASSERT(dist_base);
|
||||
GUEST_ASSERT(redist_base);
|
||||
GUEST_ASSERT(nr_cpus);
|
||||
|
||||
gic_dist_init(type, nr_cpus, dist_base);
|
||||
gic_cpu_init(cpu, redist_base);
|
||||
gic_dist_init(type, nr_cpus);
|
||||
gic_cpu_init(cpu);
|
||||
}
|
||||
|
||||
void gic_irq_enable(unsigned int intid)
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
#define SELFTEST_KVM_GIC_PRIVATE_H
|
||||
|
||||
struct gic_common_ops {
|
||||
void (*gic_init)(unsigned int nr_cpus, void *dist_base);
|
||||
void (*gic_cpu_init)(unsigned int cpu, void *redist_base);
|
||||
void (*gic_init)(unsigned int nr_cpus);
|
||||
void (*gic_cpu_init)(unsigned int cpu);
|
||||
void (*gic_irq_enable)(unsigned int intid);
|
||||
void (*gic_irq_disable)(unsigned int intid);
|
||||
uint64_t (*gic_read_iar)(void);
|
||||
|
||||
@@ -9,12 +9,21 @@
|
||||
#include "processor.h"
|
||||
#include "delay.h"
|
||||
|
||||
#include "gic.h"
|
||||
#include "gic_v3.h"
|
||||
#include "gic_private.h"
|
||||
|
||||
#define GICV3_MAX_CPUS 512
|
||||
|
||||
#define GICD_INT_DEF_PRI 0xa0
|
||||
#define GICD_INT_DEF_PRI_X4 ((GICD_INT_DEF_PRI << 24) |\
|
||||
(GICD_INT_DEF_PRI << 16) |\
|
||||
(GICD_INT_DEF_PRI << 8) |\
|
||||
GICD_INT_DEF_PRI)
|
||||
|
||||
#define ICC_PMR_DEF_PRIO 0xf0
|
||||
|
||||
struct gicv3_data {
|
||||
void *dist_base;
|
||||
void *redist_base[GICV3_MAX_CPUS];
|
||||
unsigned int nr_cpus;
|
||||
unsigned int nr_spis;
|
||||
};
|
||||
@@ -35,17 +44,23 @@ static void gicv3_gicd_wait_for_rwp(void)
|
||||
{
|
||||
unsigned int count = 100000; /* 1s */
|
||||
|
||||
while (readl(gicv3_data.dist_base + GICD_CTLR) & GICD_CTLR_RWP) {
|
||||
while (readl(GICD_BASE_GVA + GICD_CTLR) & GICD_CTLR_RWP) {
|
||||
GUEST_ASSERT(count--);
|
||||
udelay(10);
|
||||
}
|
||||
}
|
||||
|
||||
static void gicv3_gicr_wait_for_rwp(void *redist_base)
|
||||
static inline volatile void *gicr_base_cpu(uint32_t cpu)
|
||||
{
|
||||
/* Align all the redistributors sequentially */
|
||||
return GICR_BASE_GVA + cpu * SZ_64K * 2;
|
||||
}
|
||||
|
||||
static void gicv3_gicr_wait_for_rwp(uint32_t cpu)
|
||||
{
|
||||
unsigned int count = 100000; /* 1s */
|
||||
|
||||
while (readl(redist_base + GICR_CTLR) & GICR_CTLR_RWP) {
|
||||
while (readl(gicr_base_cpu(cpu) + GICR_CTLR) & GICR_CTLR_RWP) {
|
||||
GUEST_ASSERT(count--);
|
||||
udelay(10);
|
||||
}
|
||||
@@ -56,7 +71,7 @@ static void gicv3_wait_for_rwp(uint32_t cpu_or_dist)
|
||||
if (cpu_or_dist & DIST_BIT)
|
||||
gicv3_gicd_wait_for_rwp();
|
||||
else
|
||||
gicv3_gicr_wait_for_rwp(gicv3_data.redist_base[cpu_or_dist]);
|
||||
gicv3_gicr_wait_for_rwp(cpu_or_dist);
|
||||
}
|
||||
|
||||
static enum gicv3_intid_range get_intid_range(unsigned int intid)
|
||||
@@ -116,15 +131,15 @@ static void gicv3_set_eoi_split(bool split)
|
||||
|
||||
uint32_t gicv3_reg_readl(uint32_t cpu_or_dist, uint64_t offset)
|
||||
{
|
||||
void *base = cpu_or_dist & DIST_BIT ? gicv3_data.dist_base
|
||||
: sgi_base_from_redist(gicv3_data.redist_base[cpu_or_dist]);
|
||||
volatile void *base = cpu_or_dist & DIST_BIT ? GICD_BASE_GVA
|
||||
: sgi_base_from_redist(gicr_base_cpu(cpu_or_dist));
|
||||
return readl(base + offset);
|
||||
}
|
||||
|
||||
void gicv3_reg_writel(uint32_t cpu_or_dist, uint64_t offset, uint32_t reg_val)
|
||||
{
|
||||
void *base = cpu_or_dist & DIST_BIT ? gicv3_data.dist_base
|
||||
: sgi_base_from_redist(gicv3_data.redist_base[cpu_or_dist]);
|
||||
volatile void *base = cpu_or_dist & DIST_BIT ? GICD_BASE_GVA
|
||||
: sgi_base_from_redist(gicr_base_cpu(cpu_or_dist));
|
||||
writel(reg_val, base + offset);
|
||||
}
|
||||
|
||||
@@ -263,7 +278,7 @@ static bool gicv3_irq_get_pending(uint32_t intid)
|
||||
return gicv3_read_reg(intid, GICD_ISPENDR, 32, 1);
|
||||
}
|
||||
|
||||
static void gicv3_enable_redist(void *redist_base)
|
||||
static void gicv3_enable_redist(volatile void *redist_base)
|
||||
{
|
||||
uint32_t val = readl(redist_base + GICR_WAKER);
|
||||
unsigned int count = 100000; /* 1s */
|
||||
@@ -278,21 +293,15 @@ static void gicv3_enable_redist(void *redist_base)
|
||||
}
|
||||
}
|
||||
|
||||
static inline void *gicr_base_cpu(void *redist_base, uint32_t cpu)
|
||||
static void gicv3_cpu_init(unsigned int cpu)
|
||||
{
|
||||
/* Align all the redistributors sequentially */
|
||||
return redist_base + cpu * SZ_64K * 2;
|
||||
}
|
||||
|
||||
static void gicv3_cpu_init(unsigned int cpu, void *redist_base)
|
||||
{
|
||||
void *sgi_base;
|
||||
volatile void *sgi_base;
|
||||
unsigned int i;
|
||||
void *redist_base_cpu;
|
||||
volatile void *redist_base_cpu;
|
||||
|
||||
GUEST_ASSERT(cpu < gicv3_data.nr_cpus);
|
||||
|
||||
redist_base_cpu = gicr_base_cpu(redist_base, cpu);
|
||||
redist_base_cpu = gicr_base_cpu(cpu);
|
||||
sgi_base = sgi_base_from_redist(redist_base_cpu);
|
||||
|
||||
gicv3_enable_redist(redist_base_cpu);
|
||||
@@ -310,7 +319,7 @@ static void gicv3_cpu_init(unsigned int cpu, void *redist_base)
|
||||
writel(GICD_INT_DEF_PRI_X4,
|
||||
sgi_base + GICR_IPRIORITYR0 + i);
|
||||
|
||||
gicv3_gicr_wait_for_rwp(redist_base_cpu);
|
||||
gicv3_gicr_wait_for_rwp(cpu);
|
||||
|
||||
/* Enable the GIC system register (ICC_*) access */
|
||||
write_sysreg_s(read_sysreg_s(SYS_ICC_SRE_EL1) | ICC_SRE_EL1_SRE,
|
||||
@@ -320,18 +329,15 @@ static void gicv3_cpu_init(unsigned int cpu, void *redist_base)
|
||||
write_sysreg_s(ICC_PMR_DEF_PRIO, SYS_ICC_PMR_EL1);
|
||||
|
||||
/* Enable non-secure Group-1 interrupts */
|
||||
write_sysreg_s(ICC_IGRPEN1_EL1_ENABLE, SYS_ICC_GRPEN1_EL1);
|
||||
|
||||
gicv3_data.redist_base[cpu] = redist_base_cpu;
|
||||
write_sysreg_s(ICC_IGRPEN1_EL1_MASK, SYS_ICC_IGRPEN1_EL1);
|
||||
}
|
||||
|
||||
static void gicv3_dist_init(void)
|
||||
{
|
||||
void *dist_base = gicv3_data.dist_base;
|
||||
unsigned int i;
|
||||
|
||||
/* Disable the distributor until we set things up */
|
||||
writel(0, dist_base + GICD_CTLR);
|
||||
writel(0, GICD_BASE_GVA + GICD_CTLR);
|
||||
gicv3_gicd_wait_for_rwp();
|
||||
|
||||
/*
|
||||
@@ -339,33 +345,32 @@ static void gicv3_dist_init(void)
|
||||
* Also, deactivate and disable them.
|
||||
*/
|
||||
for (i = 32; i < gicv3_data.nr_spis; i += 32) {
|
||||
writel(~0, dist_base + GICD_IGROUPR + i / 8);
|
||||
writel(~0, dist_base + GICD_ICACTIVER + i / 8);
|
||||
writel(~0, dist_base + GICD_ICENABLER + i / 8);
|
||||
writel(~0, GICD_BASE_GVA + GICD_IGROUPR + i / 8);
|
||||
writel(~0, GICD_BASE_GVA + GICD_ICACTIVER + i / 8);
|
||||
writel(~0, GICD_BASE_GVA + GICD_ICENABLER + i / 8);
|
||||
}
|
||||
|
||||
/* Set a default priority for all the SPIs */
|
||||
for (i = 32; i < gicv3_data.nr_spis; i += 4)
|
||||
writel(GICD_INT_DEF_PRI_X4,
|
||||
dist_base + GICD_IPRIORITYR + i);
|
||||
GICD_BASE_GVA + GICD_IPRIORITYR + i);
|
||||
|
||||
/* Wait for the settings to sync-in */
|
||||
gicv3_gicd_wait_for_rwp();
|
||||
|
||||
/* Finally, enable the distributor globally with ARE */
|
||||
writel(GICD_CTLR_ARE_NS | GICD_CTLR_ENABLE_G1A |
|
||||
GICD_CTLR_ENABLE_G1, dist_base + GICD_CTLR);
|
||||
GICD_CTLR_ENABLE_G1, GICD_BASE_GVA + GICD_CTLR);
|
||||
gicv3_gicd_wait_for_rwp();
|
||||
}
|
||||
|
||||
static void gicv3_init(unsigned int nr_cpus, void *dist_base)
|
||||
static void gicv3_init(unsigned int nr_cpus)
|
||||
{
|
||||
GUEST_ASSERT(nr_cpus <= GICV3_MAX_CPUS);
|
||||
|
||||
gicv3_data.nr_cpus = nr_cpus;
|
||||
gicv3_data.dist_base = dist_base;
|
||||
gicv3_data.nr_spis = GICD_TYPER_SPIS(
|
||||
readl(gicv3_data.dist_base + GICD_TYPER));
|
||||
readl(GICD_BASE_GVA + GICD_TYPER));
|
||||
if (gicv3_data.nr_spis > 1020)
|
||||
gicv3_data.nr_spis = 1020;
|
||||
|
||||
@@ -396,3 +401,27 @@ const struct gic_common_ops gicv3_ops = {
|
||||
.gic_irq_get_pending = gicv3_irq_get_pending,
|
||||
.gic_irq_set_config = gicv3_irq_set_config,
|
||||
};
|
||||
|
||||
void gic_rdist_enable_lpis(vm_paddr_t cfg_table, size_t cfg_table_size,
|
||||
vm_paddr_t pend_table)
|
||||
{
|
||||
volatile void *rdist_base = gicr_base_cpu(guest_get_vcpuid());
|
||||
|
||||
u32 ctlr;
|
||||
u64 val;
|
||||
|
||||
val = (cfg_table |
|
||||
GICR_PROPBASER_InnerShareable |
|
||||
GICR_PROPBASER_RaWaWb |
|
||||
((ilog2(cfg_table_size) - 1) & GICR_PROPBASER_IDBITS_MASK));
|
||||
writeq_relaxed(val, rdist_base + GICR_PROPBASER);
|
||||
|
||||
val = (pend_table |
|
||||
GICR_PENDBASER_InnerShareable |
|
||||
GICR_PENDBASER_RaWaWb);
|
||||
writeq_relaxed(val, rdist_base + GICR_PENDBASER);
|
||||
|
||||
ctlr = readl_relaxed(rdist_base + GICR_CTLR);
|
||||
ctlr |= GICR_CTLR_ENABLE_LPIS;
|
||||
writel_relaxed(ctlr, rdist_base + GICR_CTLR);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,248 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Guest ITS library, generously donated by drivers/irqchip/irq-gic-v3-its.c
|
||||
* over in the kernel tree.
|
||||
*/
|
||||
|
||||
#include <linux/kvm.h>
|
||||
#include <linux/sizes.h>
|
||||
#include <asm/kvm_para.h>
|
||||
#include <asm/kvm.h>
|
||||
|
||||
#include "kvm_util.h"
|
||||
#include "vgic.h"
|
||||
#include "gic.h"
|
||||
#include "gic_v3.h"
|
||||
#include "processor.h"
|
||||
|
||||
static u64 its_read_u64(unsigned long offset)
|
||||
{
|
||||
return readq_relaxed(GITS_BASE_GVA + offset);
|
||||
}
|
||||
|
||||
static void its_write_u64(unsigned long offset, u64 val)
|
||||
{
|
||||
writeq_relaxed(val, GITS_BASE_GVA + offset);
|
||||
}
|
||||
|
||||
static u32 its_read_u32(unsigned long offset)
|
||||
{
|
||||
return readl_relaxed(GITS_BASE_GVA + offset);
|
||||
}
|
||||
|
||||
static void its_write_u32(unsigned long offset, u32 val)
|
||||
{
|
||||
writel_relaxed(val, GITS_BASE_GVA + offset);
|
||||
}
|
||||
|
||||
static unsigned long its_find_baser(unsigned int type)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < GITS_BASER_NR_REGS; i++) {
|
||||
u64 baser;
|
||||
unsigned long offset = GITS_BASER + (i * sizeof(baser));
|
||||
|
||||
baser = its_read_u64(offset);
|
||||
if (GITS_BASER_TYPE(baser) == type)
|
||||
return offset;
|
||||
}
|
||||
|
||||
GUEST_FAIL("Couldn't find an ITS BASER of type %u", type);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void its_install_table(unsigned int type, vm_paddr_t base, size_t size)
|
||||
{
|
||||
unsigned long offset = its_find_baser(type);
|
||||
u64 baser;
|
||||
|
||||
baser = ((size / SZ_64K) - 1) |
|
||||
GITS_BASER_PAGE_SIZE_64K |
|
||||
GITS_BASER_InnerShareable |
|
||||
base |
|
||||
GITS_BASER_RaWaWb |
|
||||
GITS_BASER_VALID;
|
||||
|
||||
its_write_u64(offset, baser);
|
||||
}
|
||||
|
||||
static void its_install_cmdq(vm_paddr_t base, size_t size)
|
||||
{
|
||||
u64 cbaser;
|
||||
|
||||
cbaser = ((size / SZ_4K) - 1) |
|
||||
GITS_CBASER_InnerShareable |
|
||||
base |
|
||||
GITS_CBASER_RaWaWb |
|
||||
GITS_CBASER_VALID;
|
||||
|
||||
its_write_u64(GITS_CBASER, cbaser);
|
||||
}
|
||||
|
||||
void its_init(vm_paddr_t coll_tbl, size_t coll_tbl_sz,
|
||||
vm_paddr_t device_tbl, size_t device_tbl_sz,
|
||||
vm_paddr_t cmdq, size_t cmdq_size)
|
||||
{
|
||||
u32 ctlr;
|
||||
|
||||
its_install_table(GITS_BASER_TYPE_COLLECTION, coll_tbl, coll_tbl_sz);
|
||||
its_install_table(GITS_BASER_TYPE_DEVICE, device_tbl, device_tbl_sz);
|
||||
its_install_cmdq(cmdq, cmdq_size);
|
||||
|
||||
ctlr = its_read_u32(GITS_CTLR);
|
||||
ctlr |= GITS_CTLR_ENABLE;
|
||||
its_write_u32(GITS_CTLR, ctlr);
|
||||
}
|
||||
|
||||
struct its_cmd_block {
|
||||
union {
|
||||
u64 raw_cmd[4];
|
||||
__le64 raw_cmd_le[4];
|
||||
};
|
||||
};
|
||||
|
||||
static inline void its_fixup_cmd(struct its_cmd_block *cmd)
|
||||
{
|
||||
/* Let's fixup BE commands */
|
||||
cmd->raw_cmd_le[0] = cpu_to_le64(cmd->raw_cmd[0]);
|
||||
cmd->raw_cmd_le[1] = cpu_to_le64(cmd->raw_cmd[1]);
|
||||
cmd->raw_cmd_le[2] = cpu_to_le64(cmd->raw_cmd[2]);
|
||||
cmd->raw_cmd_le[3] = cpu_to_le64(cmd->raw_cmd[3]);
|
||||
}
|
||||
|
||||
static void its_mask_encode(u64 *raw_cmd, u64 val, int h, int l)
|
||||
{
|
||||
u64 mask = GENMASK_ULL(h, l);
|
||||
*raw_cmd &= ~mask;
|
||||
*raw_cmd |= (val << l) & mask;
|
||||
}
|
||||
|
||||
static void its_encode_cmd(struct its_cmd_block *cmd, u8 cmd_nr)
|
||||
{
|
||||
its_mask_encode(&cmd->raw_cmd[0], cmd_nr, 7, 0);
|
||||
}
|
||||
|
||||
static void its_encode_devid(struct its_cmd_block *cmd, u32 devid)
|
||||
{
|
||||
its_mask_encode(&cmd->raw_cmd[0], devid, 63, 32);
|
||||
}
|
||||
|
||||
static void its_encode_event_id(struct its_cmd_block *cmd, u32 id)
|
||||
{
|
||||
its_mask_encode(&cmd->raw_cmd[1], id, 31, 0);
|
||||
}
|
||||
|
||||
static void its_encode_phys_id(struct its_cmd_block *cmd, u32 phys_id)
|
||||
{
|
||||
its_mask_encode(&cmd->raw_cmd[1], phys_id, 63, 32);
|
||||
}
|
||||
|
||||
static void its_encode_size(struct its_cmd_block *cmd, u8 size)
|
||||
{
|
||||
its_mask_encode(&cmd->raw_cmd[1], size, 4, 0);
|
||||
}
|
||||
|
||||
static void its_encode_itt(struct its_cmd_block *cmd, u64 itt_addr)
|
||||
{
|
||||
its_mask_encode(&cmd->raw_cmd[2], itt_addr >> 8, 51, 8);
|
||||
}
|
||||
|
||||
static void its_encode_valid(struct its_cmd_block *cmd, int valid)
|
||||
{
|
||||
its_mask_encode(&cmd->raw_cmd[2], !!valid, 63, 63);
|
||||
}
|
||||
|
||||
static void its_encode_target(struct its_cmd_block *cmd, u64 target_addr)
|
||||
{
|
||||
its_mask_encode(&cmd->raw_cmd[2], target_addr >> 16, 51, 16);
|
||||
}
|
||||
|
||||
static void its_encode_collection(struct its_cmd_block *cmd, u16 col)
|
||||
{
|
||||
its_mask_encode(&cmd->raw_cmd[2], col, 15, 0);
|
||||
}
|
||||
|
||||
#define GITS_CMDQ_POLL_ITERATIONS 0
|
||||
|
||||
static void its_send_cmd(void *cmdq_base, struct its_cmd_block *cmd)
|
||||
{
|
||||
u64 cwriter = its_read_u64(GITS_CWRITER);
|
||||
struct its_cmd_block *dst = cmdq_base + cwriter;
|
||||
u64 cbaser = its_read_u64(GITS_CBASER);
|
||||
size_t cmdq_size;
|
||||
u64 next;
|
||||
int i;
|
||||
|
||||
cmdq_size = ((cbaser & 0xFF) + 1) * SZ_4K;
|
||||
|
||||
its_fixup_cmd(cmd);
|
||||
|
||||
WRITE_ONCE(*dst, *cmd);
|
||||
dsb(ishst);
|
||||
next = (cwriter + sizeof(*cmd)) % cmdq_size;
|
||||
its_write_u64(GITS_CWRITER, next);
|
||||
|
||||
/*
|
||||
* Polling isn't necessary considering KVM's ITS emulation at the time
|
||||
* of writing this, as the CMDQ is processed synchronously after a write
|
||||
* to CWRITER.
|
||||
*/
|
||||
for (i = 0; its_read_u64(GITS_CREADR) != next; i++) {
|
||||
__GUEST_ASSERT(i < GITS_CMDQ_POLL_ITERATIONS,
|
||||
"ITS didn't process command at offset %lu after %d iterations\n",
|
||||
cwriter, i);
|
||||
|
||||
cpu_relax();
|
||||
}
|
||||
}
|
||||
|
||||
void its_send_mapd_cmd(void *cmdq_base, u32 device_id, vm_paddr_t itt_base,
|
||||
size_t itt_size, bool valid)
|
||||
{
|
||||
struct its_cmd_block cmd = {};
|
||||
|
||||
its_encode_cmd(&cmd, GITS_CMD_MAPD);
|
||||
its_encode_devid(&cmd, device_id);
|
||||
its_encode_size(&cmd, ilog2(itt_size) - 1);
|
||||
its_encode_itt(&cmd, itt_base);
|
||||
its_encode_valid(&cmd, valid);
|
||||
|
||||
its_send_cmd(cmdq_base, &cmd);
|
||||
}
|
||||
|
||||
void its_send_mapc_cmd(void *cmdq_base, u32 vcpu_id, u32 collection_id, bool valid)
|
||||
{
|
||||
struct its_cmd_block cmd = {};
|
||||
|
||||
its_encode_cmd(&cmd, GITS_CMD_MAPC);
|
||||
its_encode_collection(&cmd, collection_id);
|
||||
its_encode_target(&cmd, vcpu_id);
|
||||
its_encode_valid(&cmd, valid);
|
||||
|
||||
its_send_cmd(cmdq_base, &cmd);
|
||||
}
|
||||
|
||||
void its_send_mapti_cmd(void *cmdq_base, u32 device_id, u32 event_id,
|
||||
u32 collection_id, u32 intid)
|
||||
{
|
||||
struct its_cmd_block cmd = {};
|
||||
|
||||
its_encode_cmd(&cmd, GITS_CMD_MAPTI);
|
||||
its_encode_devid(&cmd, device_id);
|
||||
its_encode_event_id(&cmd, event_id);
|
||||
its_encode_phys_id(&cmd, intid);
|
||||
its_encode_collection(&cmd, collection_id);
|
||||
|
||||
its_send_cmd(cmdq_base, &cmd);
|
||||
}
|
||||
|
||||
void its_send_invall_cmd(void *cmdq_base, u32 collection_id)
|
||||
{
|
||||
struct its_cmd_block cmd = {};
|
||||
|
||||
its_encode_cmd(&cmd, GITS_CMD_INVALL);
|
||||
its_encode_collection(&cmd, collection_id);
|
||||
|
||||
its_send_cmd(cmdq_base, &cmd);
|
||||
}
|
||||
@@ -3,8 +3,10 @@
|
||||
* ARM Generic Interrupt Controller (GIC) v3 host support
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/kvm.h>
|
||||
#include <linux/sizes.h>
|
||||
#include <asm/cputype.h>
|
||||
#include <asm/kvm_para.h>
|
||||
#include <asm/kvm.h>
|
||||
|
||||
@@ -19,8 +21,6 @@
|
||||
* Input args:
|
||||
* vm - KVM VM
|
||||
* nr_vcpus - Number of vCPUs supported by this VM
|
||||
* gicd_base_gpa - Guest Physical Address of the Distributor region
|
||||
* gicr_base_gpa - Guest Physical Address of the Redistributor region
|
||||
*
|
||||
* Output args: None
|
||||
*
|
||||
@@ -30,11 +30,10 @@
|
||||
* redistributor regions of the guest. Since it depends on the number of
|
||||
* vCPUs for the VM, it must be called after all the vCPUs have been created.
|
||||
*/
|
||||
int vgic_v3_setup(struct kvm_vm *vm, unsigned int nr_vcpus, uint32_t nr_irqs,
|
||||
uint64_t gicd_base_gpa, uint64_t gicr_base_gpa)
|
||||
int vgic_v3_setup(struct kvm_vm *vm, unsigned int nr_vcpus, uint32_t nr_irqs)
|
||||
{
|
||||
int gic_fd;
|
||||
uint64_t redist_attr;
|
||||
uint64_t attr;
|
||||
struct list_head *iter;
|
||||
unsigned int nr_gic_pages, nr_vcpus_created = 0;
|
||||
|
||||
@@ -60,18 +59,19 @@ int vgic_v3_setup(struct kvm_vm *vm, unsigned int nr_vcpus, uint32_t nr_irqs,
|
||||
kvm_device_attr_set(gic_fd, KVM_DEV_ARM_VGIC_GRP_CTRL,
|
||||
KVM_DEV_ARM_VGIC_CTRL_INIT, NULL);
|
||||
|
||||
attr = GICD_BASE_GPA;
|
||||
kvm_device_attr_set(gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR,
|
||||
KVM_VGIC_V3_ADDR_TYPE_DIST, &gicd_base_gpa);
|
||||
KVM_VGIC_V3_ADDR_TYPE_DIST, &attr);
|
||||
nr_gic_pages = vm_calc_num_guest_pages(vm->mode, KVM_VGIC_V3_DIST_SIZE);
|
||||
virt_map(vm, gicd_base_gpa, gicd_base_gpa, nr_gic_pages);
|
||||
virt_map(vm, GICD_BASE_GPA, GICD_BASE_GPA, nr_gic_pages);
|
||||
|
||||
/* Redistributor setup */
|
||||
redist_attr = REDIST_REGION_ATTR_ADDR(nr_vcpus, gicr_base_gpa, 0, 0);
|
||||
attr = REDIST_REGION_ATTR_ADDR(nr_vcpus, GICR_BASE_GPA, 0, 0);
|
||||
kvm_device_attr_set(gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR,
|
||||
KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION, &redist_attr);
|
||||
KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION, &attr);
|
||||
nr_gic_pages = vm_calc_num_guest_pages(vm->mode,
|
||||
KVM_VGIC_V3_REDIST_SIZE * nr_vcpus);
|
||||
virt_map(vm, gicr_base_gpa, gicr_base_gpa, nr_gic_pages);
|
||||
virt_map(vm, GICR_BASE_GPA, GICR_BASE_GPA, nr_gic_pages);
|
||||
|
||||
kvm_device_attr_set(gic_fd, KVM_DEV_ARM_VGIC_GRP_CTRL,
|
||||
KVM_DEV_ARM_VGIC_CTRL_INIT, NULL);
|
||||
@@ -168,3 +168,21 @@ void kvm_irq_write_isactiver(int gic_fd, uint32_t intid, struct kvm_vcpu *vcpu)
|
||||
{
|
||||
vgic_poke_irq(gic_fd, intid, vcpu, GICD_ISACTIVER);
|
||||
}
|
||||
|
||||
int vgic_its_setup(struct kvm_vm *vm)
|
||||
{
|
||||
int its_fd = kvm_create_device(vm, KVM_DEV_TYPE_ARM_VGIC_ITS);
|
||||
u64 attr;
|
||||
|
||||
attr = GITS_BASE_GPA;
|
||||
kvm_device_attr_set(its_fd, KVM_DEV_ARM_VGIC_GRP_ADDR,
|
||||
KVM_VGIC_ITS_ADDR_TYPE, &attr);
|
||||
|
||||
kvm_device_attr_set(its_fd, KVM_DEV_ARM_VGIC_GRP_CTRL,
|
||||
KVM_DEV_ARM_VGIC_CTRL_INIT, NULL);
|
||||
|
||||
virt_map(vm, GITS_BASE_GPA, GITS_BASE_GPA,
|
||||
vm_calc_num_guest_pages(vm->mode, KVM_VGIC_V3_ITS_SIZE));
|
||||
|
||||
return its_fd;
|
||||
}
|
||||
|
||||
+11
-3
@@ -1329,6 +1329,12 @@ static void kvm_destroy_devices(struct kvm *kvm)
|
||||
* We do not need to take the kvm->lock here, because nobody else
|
||||
* has a reference to the struct kvm at this point and therefore
|
||||
* cannot access the devices list anyhow.
|
||||
*
|
||||
* The device list is generally managed as an rculist, but list_del()
|
||||
* is used intentionally here. If a bug in KVM introduced a reader that
|
||||
* was not backed by a reference on the kvm struct, the hope is that
|
||||
* it'd consume the poisoned forward pointer instead of suffering a
|
||||
* use-after-free, even though this cannot be guaranteed.
|
||||
*/
|
||||
list_for_each_entry_safe(dev, tmp, &kvm->devices, vm_node) {
|
||||
list_del(&dev->vm_node);
|
||||
@@ -4725,7 +4731,8 @@ static int kvm_device_release(struct inode *inode, struct file *filp)
|
||||
|
||||
if (dev->ops->release) {
|
||||
mutex_lock(&kvm->lock);
|
||||
list_del(&dev->vm_node);
|
||||
list_del_rcu(&dev->vm_node);
|
||||
synchronize_rcu();
|
||||
dev->ops->release(dev);
|
||||
mutex_unlock(&kvm->lock);
|
||||
}
|
||||
@@ -4808,7 +4815,7 @@ static int kvm_ioctl_create_device(struct kvm *kvm,
|
||||
kfree(dev);
|
||||
return ret;
|
||||
}
|
||||
list_add(&dev->vm_node, &kvm->devices);
|
||||
list_add_rcu(&dev->vm_node, &kvm->devices);
|
||||
mutex_unlock(&kvm->lock);
|
||||
|
||||
if (ops->init)
|
||||
@@ -4819,7 +4826,8 @@ static int kvm_ioctl_create_device(struct kvm *kvm,
|
||||
if (ret < 0) {
|
||||
kvm_put_kvm_no_destroy(kvm);
|
||||
mutex_lock(&kvm->lock);
|
||||
list_del(&dev->vm_node);
|
||||
list_del_rcu(&dev->vm_node);
|
||||
synchronize_rcu();
|
||||
if (ops->release)
|
||||
ops->release(dev);
|
||||
mutex_unlock(&kvm->lock);
|
||||
|
||||
@@ -366,6 +366,8 @@ static int kvm_vfio_create(struct kvm_device *dev, u32 type)
|
||||
struct kvm_device *tmp;
|
||||
struct kvm_vfio *kv;
|
||||
|
||||
lockdep_assert_held(&dev->kvm->lock);
|
||||
|
||||
/* Only one VFIO "device" per VM */
|
||||
list_for_each_entry(tmp, &dev->kvm->devices, vm_node)
|
||||
if (tmp->ops == &kvm_vfio_ops)
|
||||
|
||||
Reference in New Issue
Block a user