KVM: arm64: GICv2: Handle deactivation via GICV_DIR traps
Add the plumbing of GICv2 interrupt deactivation via GICV_DIR. This requires adding a new device so that we can easily decode the DIR address. The deactivation itself is very similar to the GICv3 version. Tested-by: Fuad Tabba <tabba@google.com> Signed-off-by: Marc Zyngier <maz@kernel.org> Tested-by: Mark Brown <broonie@kernel.org> Link: https://msgid.link/20251120172540.2267180-39-maz@kernel.org Signed-off-by: Oliver Upton <oupton@kernel.org>pull/1354/merge
parent
281c6c06e2
commit
255de897e7
|
|
@ -359,6 +359,16 @@ static void vgic_mmio_write_vcpuif(struct kvm_vcpu *vcpu,
|
|||
vgic_set_vmcr(vcpu, &vmcr);
|
||||
}
|
||||
|
||||
static void vgic_mmio_write_dir(struct kvm_vcpu *vcpu,
|
||||
gpa_t addr, unsigned int len,
|
||||
unsigned long val)
|
||||
{
|
||||
if (kvm_vgic_global_state.type == VGIC_V2)
|
||||
vgic_v2_deactivate(vcpu, val);
|
||||
else
|
||||
vgic_v3_deactivate(vcpu, val);
|
||||
}
|
||||
|
||||
static unsigned long vgic_mmio_read_apr(struct kvm_vcpu *vcpu,
|
||||
gpa_t addr, unsigned int len)
|
||||
{
|
||||
|
|
@ -482,6 +492,10 @@ static const struct vgic_register_region vgic_v2_cpu_registers[] = {
|
|||
REGISTER_DESC_WITH_LENGTH(GIC_CPU_IDENT,
|
||||
vgic_mmio_read_vcpuif, vgic_mmio_write_vcpuif, 4,
|
||||
VGIC_ACCESS_32bit),
|
||||
REGISTER_DESC_WITH_LENGTH_UACCESS(GIC_CPU_DEACTIVATE,
|
||||
vgic_mmio_read_raz, vgic_mmio_write_dir,
|
||||
vgic_mmio_read_raz, vgic_mmio_uaccess_write_wi,
|
||||
4, VGIC_ACCESS_32bit),
|
||||
};
|
||||
|
||||
unsigned int vgic_v2_init_dist_iodev(struct vgic_io_device *dev)
|
||||
|
|
@ -494,6 +508,16 @@ unsigned int vgic_v2_init_dist_iodev(struct vgic_io_device *dev)
|
|||
return SZ_4K;
|
||||
}
|
||||
|
||||
unsigned int vgic_v2_init_cpuif_iodev(struct vgic_io_device *dev)
|
||||
{
|
||||
dev->regions = vgic_v2_cpu_registers;
|
||||
dev->nr_regions = ARRAY_SIZE(vgic_v2_cpu_registers);
|
||||
|
||||
kvm_iodevice_init(&dev->dev, &kvm_io_gic_ops);
|
||||
|
||||
return KVM_VGIC_V2_CPU_SIZE;
|
||||
}
|
||||
|
||||
int vgic_v2_has_attr_regs(struct kvm_device *dev, struct kvm_device_attr *attr)
|
||||
{
|
||||
const struct vgic_register_region *region;
|
||||
|
|
|
|||
|
|
@ -213,6 +213,7 @@ void vgic_write_irq_line_level_info(struct kvm_vcpu *vcpu, u32 intid,
|
|||
const u32 val);
|
||||
|
||||
unsigned int vgic_v2_init_dist_iodev(struct vgic_io_device *dev);
|
||||
unsigned int vgic_v2_init_cpuif_iodev(struct vgic_io_device *dev);
|
||||
|
||||
unsigned int vgic_v3_init_dist_iodev(struct vgic_io_device *dev);
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
#include <kvm/arm_vgic.h>
|
||||
#include <asm/kvm_mmu.h>
|
||||
|
||||
#include "vgic-mmio.h"
|
||||
#include "vgic.h"
|
||||
|
||||
static inline void vgic_v2_write_lr(int lr, u32 val)
|
||||
|
|
@ -147,6 +148,79 @@ void vgic_v2_fold_lr_state(struct kvm_vcpu *vcpu)
|
|||
cpuif->used_lrs = 0;
|
||||
}
|
||||
|
||||
void vgic_v2_deactivate(struct kvm_vcpu *vcpu, u32 val)
|
||||
{
|
||||
struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
|
||||
struct vgic_v2_cpu_if *cpuif = &vgic_cpu->vgic_v2;
|
||||
struct kvm_vcpu *target_vcpu = NULL;
|
||||
bool mmio = false;
|
||||
struct vgic_irq *irq;
|
||||
unsigned long flags;
|
||||
u64 lr = 0;
|
||||
u8 cpuid;
|
||||
|
||||
/* Snapshot CPUID, and remove it from the INTID */
|
||||
cpuid = FIELD_GET(GENMASK_ULL(12, 10), val);
|
||||
val &= ~GENMASK_ULL(12, 10);
|
||||
|
||||
/* We only deal with DIR when EOIMode==1 */
|
||||
if (!(cpuif->vgic_vmcr & GICH_VMCR_EOI_MODE_MASK))
|
||||
return;
|
||||
|
||||
/* Make sure we're in the same context as LR handling */
|
||||
local_irq_save(flags);
|
||||
|
||||
irq = vgic_get_vcpu_irq(vcpu, val);
|
||||
if (WARN_ON_ONCE(!irq))
|
||||
goto out;
|
||||
|
||||
/* See the corresponding v3 code for the rationale */
|
||||
scoped_guard(raw_spinlock, &irq->irq_lock) {
|
||||
target_vcpu = irq->vcpu;
|
||||
|
||||
/* Not on any ap_list? */
|
||||
if (!target_vcpu)
|
||||
goto put;
|
||||
|
||||
/*
|
||||
* Urgh. We're deactivating something that we cannot
|
||||
* observe yet... Big hammer time.
|
||||
*/
|
||||
if (irq->on_lr) {
|
||||
mmio = true;
|
||||
goto put;
|
||||
}
|
||||
|
||||
/* SGI: check that the cpuid matches */
|
||||
if (val < VGIC_NR_SGIS && irq->active_source != cpuid) {
|
||||
target_vcpu = NULL;
|
||||
goto put;
|
||||
}
|
||||
|
||||
/* (with a Dalek voice) DEACTIVATE!!!! */
|
||||
lr = vgic_v2_compute_lr(vcpu, irq) & ~GICH_LR_ACTIVE_BIT;
|
||||
}
|
||||
|
||||
if (lr & GICH_LR_HW)
|
||||
writel_relaxed(FIELD_GET(GICH_LR_PHYSID_CPUID, lr),
|
||||
kvm_vgic_global_state.gicc_base + GIC_CPU_DEACTIVATE);
|
||||
|
||||
vgic_v2_fold_lr(vcpu, lr);
|
||||
|
||||
put:
|
||||
vgic_put_irq(vcpu->kvm, irq);
|
||||
|
||||
out:
|
||||
local_irq_restore(flags);
|
||||
|
||||
if (mmio)
|
||||
vgic_mmio_write_cactive(vcpu, (val / 32) * 4, 4, BIT(val % 32));
|
||||
|
||||
/* Force the ap_list to be pruned */
|
||||
if (target_vcpu)
|
||||
kvm_make_request(KVM_REQ_VGIC_PROCESS_UPDATE, target_vcpu);
|
||||
}
|
||||
|
||||
static u32 vgic_v2_compute_lr(struct kvm_vcpu *vcpu, struct vgic_irq *irq)
|
||||
{
|
||||
u32 val = irq->intid;
|
||||
|
|
@ -346,6 +420,7 @@ static bool vgic_v2_check_base(gpa_t dist_base, gpa_t cpu_base)
|
|||
int vgic_v2_map_resources(struct kvm *kvm)
|
||||
{
|
||||
struct vgic_dist *dist = &kvm->arch.vgic;
|
||||
unsigned int len;
|
||||
int ret = 0;
|
||||
|
||||
if (IS_VGIC_ADDR_UNDEF(dist->vgic_dist_base) ||
|
||||
|
|
@ -369,6 +444,16 @@ int vgic_v2_map_resources(struct kvm *kvm)
|
|||
return ret;
|
||||
}
|
||||
|
||||
len = vgic_v2_init_cpuif_iodev(&dist->cpuif_iodev);
|
||||
dist->cpuif_iodev.base_addr = dist->vgic_cpu_base;
|
||||
dist->cpuif_iodev.iodev_type = IODEV_CPUIF;
|
||||
dist->cpuif_iodev.redist_vcpu = NULL;
|
||||
|
||||
ret = kvm_io_bus_register_dev(kvm, KVM_MMIO_BUS, dist->vgic_cpu_base,
|
||||
len, &dist->cpuif_iodev.dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (!static_branch_unlikely(&vgic_v2_cpuif_trap)) {
|
||||
ret = kvm_phys_addr_ioremap(kvm, dist->vgic_cpu_base,
|
||||
kvm_vgic_global_state.vcpu_base,
|
||||
|
|
|
|||
|
|
@ -277,6 +277,7 @@ int vgic_check_iorange(struct kvm *kvm, phys_addr_t ioaddr,
|
|||
|
||||
void vgic_v2_fold_lr_state(struct kvm_vcpu *vcpu);
|
||||
void vgic_v2_populate_lr(struct kvm_vcpu *vcpu, struct vgic_irq *irq, int lr);
|
||||
void vgic_v2_deactivate(struct kvm_vcpu *vcpu, u32 val);
|
||||
void vgic_v2_clear_lr(struct kvm_vcpu *vcpu, int lr);
|
||||
void vgic_v2_configure_hcr(struct kvm_vcpu *vcpu, struct ap_list_summary *als);
|
||||
int vgic_v2_has_attr_regs(struct kvm_device *dev, struct kvm_device_attr *attr);
|
||||
|
|
|
|||
|
|
@ -287,6 +287,7 @@ struct vgic_dist {
|
|||
struct vgic_irq *spis;
|
||||
|
||||
struct vgic_io_device dist_iodev;
|
||||
struct vgic_io_device cpuif_iodev;
|
||||
|
||||
bool has_its;
|
||||
bool table_write_in_progress;
|
||||
|
|
|
|||
Loading…
Reference in New Issue