162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * VGICv3 MMIO handling functions 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/bitfield.h> 762306a36Sopenharmony_ci#include <linux/irqchip/arm-gic-v3.h> 862306a36Sopenharmony_ci#include <linux/kvm.h> 962306a36Sopenharmony_ci#include <linux/kvm_host.h> 1062306a36Sopenharmony_ci#include <linux/interrupt.h> 1162306a36Sopenharmony_ci#include <kvm/iodev.h> 1262306a36Sopenharmony_ci#include <kvm/arm_vgic.h> 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include <asm/kvm_emulate.h> 1562306a36Sopenharmony_ci#include <asm/kvm_arm.h> 1662306a36Sopenharmony_ci#include <asm/kvm_mmu.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include "vgic.h" 1962306a36Sopenharmony_ci#include "vgic-mmio.h" 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci/* extract @num bytes at @offset bytes offset in data */ 2262306a36Sopenharmony_ciunsigned long extract_bytes(u64 data, unsigned int offset, 2362306a36Sopenharmony_ci unsigned int num) 2462306a36Sopenharmony_ci{ 2562306a36Sopenharmony_ci return (data >> (offset * 8)) & GENMASK_ULL(num * 8 - 1, 0); 2662306a36Sopenharmony_ci} 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci/* allows updates of any half of a 64-bit register (or the whole thing) */ 2962306a36Sopenharmony_ciu64 update_64bit_reg(u64 reg, unsigned int offset, unsigned int len, 3062306a36Sopenharmony_ci unsigned long val) 3162306a36Sopenharmony_ci{ 3262306a36Sopenharmony_ci int lower = (offset & 4) * 8; 3362306a36Sopenharmony_ci int upper = lower + 8 * len - 1; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci reg &= ~GENMASK_ULL(upper, lower); 3662306a36Sopenharmony_ci val &= GENMASK_ULL(len * 8 - 1, 0); 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci return reg | ((u64)val << lower); 3962306a36Sopenharmony_ci} 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_cibool vgic_has_its(struct kvm *kvm) 4262306a36Sopenharmony_ci{ 4362306a36Sopenharmony_ci struct vgic_dist *dist = &kvm->arch.vgic; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci if (dist->vgic_model != KVM_DEV_TYPE_ARM_VGIC_V3) 4662306a36Sopenharmony_ci return false; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci return dist->has_its; 4962306a36Sopenharmony_ci} 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_cibool vgic_supports_direct_msis(struct kvm *kvm) 5262306a36Sopenharmony_ci{ 5362306a36Sopenharmony_ci return (kvm_vgic_global_state.has_gicv4_1 || 5462306a36Sopenharmony_ci (kvm_vgic_global_state.has_gicv4 && vgic_has_its(kvm))); 5562306a36Sopenharmony_ci} 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci/* 5862306a36Sopenharmony_ci * The Revision field in the IIDR have the following meanings: 5962306a36Sopenharmony_ci * 6062306a36Sopenharmony_ci * Revision 2: Interrupt groups are guest-configurable and signaled using 6162306a36Sopenharmony_ci * their configured groups. 6262306a36Sopenharmony_ci */ 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_cistatic unsigned long vgic_mmio_read_v3_misc(struct kvm_vcpu *vcpu, 6562306a36Sopenharmony_ci gpa_t addr, unsigned int len) 6662306a36Sopenharmony_ci{ 6762306a36Sopenharmony_ci struct vgic_dist *vgic = &vcpu->kvm->arch.vgic; 6862306a36Sopenharmony_ci u32 value = 0; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci switch (addr & 0x0c) { 7162306a36Sopenharmony_ci case GICD_CTLR: 7262306a36Sopenharmony_ci if (vgic->enabled) 7362306a36Sopenharmony_ci value |= GICD_CTLR_ENABLE_SS_G1; 7462306a36Sopenharmony_ci value |= GICD_CTLR_ARE_NS | GICD_CTLR_DS; 7562306a36Sopenharmony_ci if (vgic->nassgireq) 7662306a36Sopenharmony_ci value |= GICD_CTLR_nASSGIreq; 7762306a36Sopenharmony_ci break; 7862306a36Sopenharmony_ci case GICD_TYPER: 7962306a36Sopenharmony_ci value = vgic->nr_spis + VGIC_NR_PRIVATE_IRQS; 8062306a36Sopenharmony_ci value = (value >> 5) - 1; 8162306a36Sopenharmony_ci if (vgic_has_its(vcpu->kvm)) { 8262306a36Sopenharmony_ci value |= (INTERRUPT_ID_BITS_ITS - 1) << 19; 8362306a36Sopenharmony_ci value |= GICD_TYPER_LPIS; 8462306a36Sopenharmony_ci } else { 8562306a36Sopenharmony_ci value |= (INTERRUPT_ID_BITS_SPIS - 1) << 19; 8662306a36Sopenharmony_ci } 8762306a36Sopenharmony_ci break; 8862306a36Sopenharmony_ci case GICD_TYPER2: 8962306a36Sopenharmony_ci if (kvm_vgic_global_state.has_gicv4_1 && gic_cpuif_has_vsgi()) 9062306a36Sopenharmony_ci value = GICD_TYPER2_nASSGIcap; 9162306a36Sopenharmony_ci break; 9262306a36Sopenharmony_ci case GICD_IIDR: 9362306a36Sopenharmony_ci value = (PRODUCT_ID_KVM << GICD_IIDR_PRODUCT_ID_SHIFT) | 9462306a36Sopenharmony_ci (vgic->implementation_rev << GICD_IIDR_REVISION_SHIFT) | 9562306a36Sopenharmony_ci (IMPLEMENTER_ARM << GICD_IIDR_IMPLEMENTER_SHIFT); 9662306a36Sopenharmony_ci break; 9762306a36Sopenharmony_ci default: 9862306a36Sopenharmony_ci return 0; 9962306a36Sopenharmony_ci } 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci return value; 10262306a36Sopenharmony_ci} 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_cistatic void vgic_mmio_write_v3_misc(struct kvm_vcpu *vcpu, 10562306a36Sopenharmony_ci gpa_t addr, unsigned int len, 10662306a36Sopenharmony_ci unsigned long val) 10762306a36Sopenharmony_ci{ 10862306a36Sopenharmony_ci struct vgic_dist *dist = &vcpu->kvm->arch.vgic; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci switch (addr & 0x0c) { 11162306a36Sopenharmony_ci case GICD_CTLR: { 11262306a36Sopenharmony_ci bool was_enabled, is_hwsgi; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci mutex_lock(&vcpu->kvm->arch.config_lock); 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci was_enabled = dist->enabled; 11762306a36Sopenharmony_ci is_hwsgi = dist->nassgireq; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci dist->enabled = val & GICD_CTLR_ENABLE_SS_G1; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci /* Not a GICv4.1? No HW SGIs */ 12262306a36Sopenharmony_ci if (!kvm_vgic_global_state.has_gicv4_1 || !gic_cpuif_has_vsgi()) 12362306a36Sopenharmony_ci val &= ~GICD_CTLR_nASSGIreq; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci /* Dist stays enabled? nASSGIreq is RO */ 12662306a36Sopenharmony_ci if (was_enabled && dist->enabled) { 12762306a36Sopenharmony_ci val &= ~GICD_CTLR_nASSGIreq; 12862306a36Sopenharmony_ci val |= FIELD_PREP(GICD_CTLR_nASSGIreq, is_hwsgi); 12962306a36Sopenharmony_ci } 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci /* Switching HW SGIs? */ 13262306a36Sopenharmony_ci dist->nassgireq = val & GICD_CTLR_nASSGIreq; 13362306a36Sopenharmony_ci if (is_hwsgi != dist->nassgireq) 13462306a36Sopenharmony_ci vgic_v4_configure_vsgis(vcpu->kvm); 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci if (kvm_vgic_global_state.has_gicv4_1 && 13762306a36Sopenharmony_ci was_enabled != dist->enabled) 13862306a36Sopenharmony_ci kvm_make_all_cpus_request(vcpu->kvm, KVM_REQ_RELOAD_GICv4); 13962306a36Sopenharmony_ci else if (!was_enabled && dist->enabled) 14062306a36Sopenharmony_ci vgic_kick_vcpus(vcpu->kvm); 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci mutex_unlock(&vcpu->kvm->arch.config_lock); 14362306a36Sopenharmony_ci break; 14462306a36Sopenharmony_ci } 14562306a36Sopenharmony_ci case GICD_TYPER: 14662306a36Sopenharmony_ci case GICD_TYPER2: 14762306a36Sopenharmony_ci case GICD_IIDR: 14862306a36Sopenharmony_ci /* This is at best for documentation purposes... */ 14962306a36Sopenharmony_ci return; 15062306a36Sopenharmony_ci } 15162306a36Sopenharmony_ci} 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_cistatic int vgic_mmio_uaccess_write_v3_misc(struct kvm_vcpu *vcpu, 15462306a36Sopenharmony_ci gpa_t addr, unsigned int len, 15562306a36Sopenharmony_ci unsigned long val) 15662306a36Sopenharmony_ci{ 15762306a36Sopenharmony_ci struct vgic_dist *dist = &vcpu->kvm->arch.vgic; 15862306a36Sopenharmony_ci u32 reg; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci switch (addr & 0x0c) { 16162306a36Sopenharmony_ci case GICD_TYPER2: 16262306a36Sopenharmony_ci if (val != vgic_mmio_read_v3_misc(vcpu, addr, len)) 16362306a36Sopenharmony_ci return -EINVAL; 16462306a36Sopenharmony_ci return 0; 16562306a36Sopenharmony_ci case GICD_IIDR: 16662306a36Sopenharmony_ci reg = vgic_mmio_read_v3_misc(vcpu, addr, len); 16762306a36Sopenharmony_ci if ((reg ^ val) & ~GICD_IIDR_REVISION_MASK) 16862306a36Sopenharmony_ci return -EINVAL; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci reg = FIELD_GET(GICD_IIDR_REVISION_MASK, reg); 17162306a36Sopenharmony_ci switch (reg) { 17262306a36Sopenharmony_ci case KVM_VGIC_IMP_REV_2: 17362306a36Sopenharmony_ci case KVM_VGIC_IMP_REV_3: 17462306a36Sopenharmony_ci dist->implementation_rev = reg; 17562306a36Sopenharmony_ci return 0; 17662306a36Sopenharmony_ci default: 17762306a36Sopenharmony_ci return -EINVAL; 17862306a36Sopenharmony_ci } 17962306a36Sopenharmony_ci case GICD_CTLR: 18062306a36Sopenharmony_ci /* Not a GICv4.1? No HW SGIs */ 18162306a36Sopenharmony_ci if (!kvm_vgic_global_state.has_gicv4_1) 18262306a36Sopenharmony_ci val &= ~GICD_CTLR_nASSGIreq; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci dist->enabled = val & GICD_CTLR_ENABLE_SS_G1; 18562306a36Sopenharmony_ci dist->nassgireq = val & GICD_CTLR_nASSGIreq; 18662306a36Sopenharmony_ci return 0; 18762306a36Sopenharmony_ci } 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci vgic_mmio_write_v3_misc(vcpu, addr, len, val); 19062306a36Sopenharmony_ci return 0; 19162306a36Sopenharmony_ci} 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_cistatic unsigned long vgic_mmio_read_irouter(struct kvm_vcpu *vcpu, 19462306a36Sopenharmony_ci gpa_t addr, unsigned int len) 19562306a36Sopenharmony_ci{ 19662306a36Sopenharmony_ci int intid = VGIC_ADDR_TO_INTID(addr, 64); 19762306a36Sopenharmony_ci struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, NULL, intid); 19862306a36Sopenharmony_ci unsigned long ret = 0; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci if (!irq) 20162306a36Sopenharmony_ci return 0; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci /* The upper word is RAZ for us. */ 20462306a36Sopenharmony_ci if (!(addr & 4)) 20562306a36Sopenharmony_ci ret = extract_bytes(READ_ONCE(irq->mpidr), addr & 7, len); 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci vgic_put_irq(vcpu->kvm, irq); 20862306a36Sopenharmony_ci return ret; 20962306a36Sopenharmony_ci} 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_cistatic void vgic_mmio_write_irouter(struct kvm_vcpu *vcpu, 21262306a36Sopenharmony_ci gpa_t addr, unsigned int len, 21362306a36Sopenharmony_ci unsigned long val) 21462306a36Sopenharmony_ci{ 21562306a36Sopenharmony_ci int intid = VGIC_ADDR_TO_INTID(addr, 64); 21662306a36Sopenharmony_ci struct vgic_irq *irq; 21762306a36Sopenharmony_ci unsigned long flags; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci /* The upper word is WI for us since we don't implement Aff3. */ 22062306a36Sopenharmony_ci if (addr & 4) 22162306a36Sopenharmony_ci return; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci irq = vgic_get_irq(vcpu->kvm, NULL, intid); 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci if (!irq) 22662306a36Sopenharmony_ci return; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci raw_spin_lock_irqsave(&irq->irq_lock, flags); 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci /* We only care about and preserve Aff0, Aff1 and Aff2. */ 23162306a36Sopenharmony_ci irq->mpidr = val & GENMASK(23, 0); 23262306a36Sopenharmony_ci irq->target_vcpu = kvm_mpidr_to_vcpu(vcpu->kvm, irq->mpidr); 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&irq->irq_lock, flags); 23562306a36Sopenharmony_ci vgic_put_irq(vcpu->kvm, irq); 23662306a36Sopenharmony_ci} 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_cibool vgic_lpis_enabled(struct kvm_vcpu *vcpu) 23962306a36Sopenharmony_ci{ 24062306a36Sopenharmony_ci struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci return atomic_read(&vgic_cpu->ctlr) == GICR_CTLR_ENABLE_LPIS; 24362306a36Sopenharmony_ci} 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_cistatic unsigned long vgic_mmio_read_v3r_ctlr(struct kvm_vcpu *vcpu, 24662306a36Sopenharmony_ci gpa_t addr, unsigned int len) 24762306a36Sopenharmony_ci{ 24862306a36Sopenharmony_ci struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu; 24962306a36Sopenharmony_ci unsigned long val; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci val = atomic_read(&vgic_cpu->ctlr); 25262306a36Sopenharmony_ci if (vgic_get_implementation_rev(vcpu) >= KVM_VGIC_IMP_REV_3) 25362306a36Sopenharmony_ci val |= GICR_CTLR_IR | GICR_CTLR_CES; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci return val; 25662306a36Sopenharmony_ci} 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_cistatic void vgic_mmio_write_v3r_ctlr(struct kvm_vcpu *vcpu, 25962306a36Sopenharmony_ci gpa_t addr, unsigned int len, 26062306a36Sopenharmony_ci unsigned long val) 26162306a36Sopenharmony_ci{ 26262306a36Sopenharmony_ci struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu; 26362306a36Sopenharmony_ci u32 ctlr; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci if (!vgic_has_its(vcpu->kvm)) 26662306a36Sopenharmony_ci return; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci if (!(val & GICR_CTLR_ENABLE_LPIS)) { 26962306a36Sopenharmony_ci /* 27062306a36Sopenharmony_ci * Don't disable if RWP is set, as there already an 27162306a36Sopenharmony_ci * ongoing disable. Funky guest... 27262306a36Sopenharmony_ci */ 27362306a36Sopenharmony_ci ctlr = atomic_cmpxchg_acquire(&vgic_cpu->ctlr, 27462306a36Sopenharmony_ci GICR_CTLR_ENABLE_LPIS, 27562306a36Sopenharmony_ci GICR_CTLR_RWP); 27662306a36Sopenharmony_ci if (ctlr != GICR_CTLR_ENABLE_LPIS) 27762306a36Sopenharmony_ci return; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci vgic_flush_pending_lpis(vcpu); 28062306a36Sopenharmony_ci vgic_its_invalidate_cache(vcpu->kvm); 28162306a36Sopenharmony_ci atomic_set_release(&vgic_cpu->ctlr, 0); 28262306a36Sopenharmony_ci } else { 28362306a36Sopenharmony_ci ctlr = atomic_cmpxchg_acquire(&vgic_cpu->ctlr, 0, 28462306a36Sopenharmony_ci GICR_CTLR_ENABLE_LPIS); 28562306a36Sopenharmony_ci if (ctlr != 0) 28662306a36Sopenharmony_ci return; 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci vgic_enable_lpis(vcpu); 28962306a36Sopenharmony_ci } 29062306a36Sopenharmony_ci} 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_cistatic bool vgic_mmio_vcpu_rdist_is_last(struct kvm_vcpu *vcpu) 29362306a36Sopenharmony_ci{ 29462306a36Sopenharmony_ci struct vgic_dist *vgic = &vcpu->kvm->arch.vgic; 29562306a36Sopenharmony_ci struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu; 29662306a36Sopenharmony_ci struct vgic_redist_region *iter, *rdreg = vgic_cpu->rdreg; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci if (!rdreg) 29962306a36Sopenharmony_ci return false; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci if (vgic_cpu->rdreg_index < rdreg->free_index - 1) { 30262306a36Sopenharmony_ci return false; 30362306a36Sopenharmony_ci } else if (rdreg->count && vgic_cpu->rdreg_index == (rdreg->count - 1)) { 30462306a36Sopenharmony_ci struct list_head *rd_regions = &vgic->rd_regions; 30562306a36Sopenharmony_ci gpa_t end = rdreg->base + rdreg->count * KVM_VGIC_V3_REDIST_SIZE; 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci /* 30862306a36Sopenharmony_ci * the rdist is the last one of the redist region, 30962306a36Sopenharmony_ci * check whether there is no other contiguous rdist region 31062306a36Sopenharmony_ci */ 31162306a36Sopenharmony_ci list_for_each_entry(iter, rd_regions, list) { 31262306a36Sopenharmony_ci if (iter->base == end && iter->free_index > 0) 31362306a36Sopenharmony_ci return false; 31462306a36Sopenharmony_ci } 31562306a36Sopenharmony_ci } 31662306a36Sopenharmony_ci return true; 31762306a36Sopenharmony_ci} 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_cistatic unsigned long vgic_mmio_read_v3r_typer(struct kvm_vcpu *vcpu, 32062306a36Sopenharmony_ci gpa_t addr, unsigned int len) 32162306a36Sopenharmony_ci{ 32262306a36Sopenharmony_ci unsigned long mpidr = kvm_vcpu_get_mpidr_aff(vcpu); 32362306a36Sopenharmony_ci int target_vcpu_id = vcpu->vcpu_id; 32462306a36Sopenharmony_ci u64 value; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci value = (u64)(mpidr & GENMASK(23, 0)) << 32; 32762306a36Sopenharmony_ci value |= ((target_vcpu_id & 0xffff) << 8); 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci if (vgic_has_its(vcpu->kvm)) 33062306a36Sopenharmony_ci value |= GICR_TYPER_PLPIS; 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci if (vgic_mmio_vcpu_rdist_is_last(vcpu)) 33362306a36Sopenharmony_ci value |= GICR_TYPER_LAST; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci return extract_bytes(value, addr & 7, len); 33662306a36Sopenharmony_ci} 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_cistatic unsigned long vgic_mmio_read_v3r_iidr(struct kvm_vcpu *vcpu, 33962306a36Sopenharmony_ci gpa_t addr, unsigned int len) 34062306a36Sopenharmony_ci{ 34162306a36Sopenharmony_ci return (PRODUCT_ID_KVM << 24) | (IMPLEMENTER_ARM << 0); 34262306a36Sopenharmony_ci} 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_cistatic unsigned long vgic_mmio_read_v3_idregs(struct kvm_vcpu *vcpu, 34562306a36Sopenharmony_ci gpa_t addr, unsigned int len) 34662306a36Sopenharmony_ci{ 34762306a36Sopenharmony_ci switch (addr & 0xffff) { 34862306a36Sopenharmony_ci case GICD_PIDR2: 34962306a36Sopenharmony_ci /* report a GICv3 compliant implementation */ 35062306a36Sopenharmony_ci return 0x3b; 35162306a36Sopenharmony_ci } 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci return 0; 35462306a36Sopenharmony_ci} 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_cistatic int vgic_v3_uaccess_write_pending(struct kvm_vcpu *vcpu, 35762306a36Sopenharmony_ci gpa_t addr, unsigned int len, 35862306a36Sopenharmony_ci unsigned long val) 35962306a36Sopenharmony_ci{ 36062306a36Sopenharmony_ci u32 intid = VGIC_ADDR_TO_INTID(addr, 1); 36162306a36Sopenharmony_ci int i; 36262306a36Sopenharmony_ci unsigned long flags; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci for (i = 0; i < len * 8; i++) { 36562306a36Sopenharmony_ci struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i); 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci raw_spin_lock_irqsave(&irq->irq_lock, flags); 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci /* 37062306a36Sopenharmony_ci * pending_latch is set irrespective of irq type 37162306a36Sopenharmony_ci * (level or edge) to avoid dependency that VM should 37262306a36Sopenharmony_ci * restore irq config before pending info. 37362306a36Sopenharmony_ci */ 37462306a36Sopenharmony_ci irq->pending_latch = test_bit(i, &val); 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci if (irq->hw && vgic_irq_is_sgi(irq->intid)) { 37762306a36Sopenharmony_ci irq_set_irqchip_state(irq->host_irq, 37862306a36Sopenharmony_ci IRQCHIP_STATE_PENDING, 37962306a36Sopenharmony_ci irq->pending_latch); 38062306a36Sopenharmony_ci irq->pending_latch = false; 38162306a36Sopenharmony_ci } 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci if (irq->pending_latch) 38462306a36Sopenharmony_ci vgic_queue_irq_unlock(vcpu->kvm, irq, flags); 38562306a36Sopenharmony_ci else 38662306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&irq->irq_lock, flags); 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci vgic_put_irq(vcpu->kvm, irq); 38962306a36Sopenharmony_ci } 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci return 0; 39262306a36Sopenharmony_ci} 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci/* We want to avoid outer shareable. */ 39562306a36Sopenharmony_ciu64 vgic_sanitise_shareability(u64 field) 39662306a36Sopenharmony_ci{ 39762306a36Sopenharmony_ci switch (field) { 39862306a36Sopenharmony_ci case GIC_BASER_OuterShareable: 39962306a36Sopenharmony_ci return GIC_BASER_InnerShareable; 40062306a36Sopenharmony_ci default: 40162306a36Sopenharmony_ci return field; 40262306a36Sopenharmony_ci } 40362306a36Sopenharmony_ci} 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci/* Avoid any inner non-cacheable mapping. */ 40662306a36Sopenharmony_ciu64 vgic_sanitise_inner_cacheability(u64 field) 40762306a36Sopenharmony_ci{ 40862306a36Sopenharmony_ci switch (field) { 40962306a36Sopenharmony_ci case GIC_BASER_CACHE_nCnB: 41062306a36Sopenharmony_ci case GIC_BASER_CACHE_nC: 41162306a36Sopenharmony_ci return GIC_BASER_CACHE_RaWb; 41262306a36Sopenharmony_ci default: 41362306a36Sopenharmony_ci return field; 41462306a36Sopenharmony_ci } 41562306a36Sopenharmony_ci} 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci/* Non-cacheable or same-as-inner are OK. */ 41862306a36Sopenharmony_ciu64 vgic_sanitise_outer_cacheability(u64 field) 41962306a36Sopenharmony_ci{ 42062306a36Sopenharmony_ci switch (field) { 42162306a36Sopenharmony_ci case GIC_BASER_CACHE_SameAsInner: 42262306a36Sopenharmony_ci case GIC_BASER_CACHE_nC: 42362306a36Sopenharmony_ci return field; 42462306a36Sopenharmony_ci default: 42562306a36Sopenharmony_ci return GIC_BASER_CACHE_SameAsInner; 42662306a36Sopenharmony_ci } 42762306a36Sopenharmony_ci} 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ciu64 vgic_sanitise_field(u64 reg, u64 field_mask, int field_shift, 43062306a36Sopenharmony_ci u64 (*sanitise_fn)(u64)) 43162306a36Sopenharmony_ci{ 43262306a36Sopenharmony_ci u64 field = (reg & field_mask) >> field_shift; 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci field = sanitise_fn(field) << field_shift; 43562306a36Sopenharmony_ci return (reg & ~field_mask) | field; 43662306a36Sopenharmony_ci} 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci#define PROPBASER_RES0_MASK \ 43962306a36Sopenharmony_ci (GENMASK_ULL(63, 59) | GENMASK_ULL(55, 52) | GENMASK_ULL(6, 5)) 44062306a36Sopenharmony_ci#define PENDBASER_RES0_MASK \ 44162306a36Sopenharmony_ci (BIT_ULL(63) | GENMASK_ULL(61, 59) | GENMASK_ULL(55, 52) | \ 44262306a36Sopenharmony_ci GENMASK_ULL(15, 12) | GENMASK_ULL(6, 0)) 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_cistatic u64 vgic_sanitise_pendbaser(u64 reg) 44562306a36Sopenharmony_ci{ 44662306a36Sopenharmony_ci reg = vgic_sanitise_field(reg, GICR_PENDBASER_SHAREABILITY_MASK, 44762306a36Sopenharmony_ci GICR_PENDBASER_SHAREABILITY_SHIFT, 44862306a36Sopenharmony_ci vgic_sanitise_shareability); 44962306a36Sopenharmony_ci reg = vgic_sanitise_field(reg, GICR_PENDBASER_INNER_CACHEABILITY_MASK, 45062306a36Sopenharmony_ci GICR_PENDBASER_INNER_CACHEABILITY_SHIFT, 45162306a36Sopenharmony_ci vgic_sanitise_inner_cacheability); 45262306a36Sopenharmony_ci reg = vgic_sanitise_field(reg, GICR_PENDBASER_OUTER_CACHEABILITY_MASK, 45362306a36Sopenharmony_ci GICR_PENDBASER_OUTER_CACHEABILITY_SHIFT, 45462306a36Sopenharmony_ci vgic_sanitise_outer_cacheability); 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci reg &= ~PENDBASER_RES0_MASK; 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci return reg; 45962306a36Sopenharmony_ci} 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_cistatic u64 vgic_sanitise_propbaser(u64 reg) 46262306a36Sopenharmony_ci{ 46362306a36Sopenharmony_ci reg = vgic_sanitise_field(reg, GICR_PROPBASER_SHAREABILITY_MASK, 46462306a36Sopenharmony_ci GICR_PROPBASER_SHAREABILITY_SHIFT, 46562306a36Sopenharmony_ci vgic_sanitise_shareability); 46662306a36Sopenharmony_ci reg = vgic_sanitise_field(reg, GICR_PROPBASER_INNER_CACHEABILITY_MASK, 46762306a36Sopenharmony_ci GICR_PROPBASER_INNER_CACHEABILITY_SHIFT, 46862306a36Sopenharmony_ci vgic_sanitise_inner_cacheability); 46962306a36Sopenharmony_ci reg = vgic_sanitise_field(reg, GICR_PROPBASER_OUTER_CACHEABILITY_MASK, 47062306a36Sopenharmony_ci GICR_PROPBASER_OUTER_CACHEABILITY_SHIFT, 47162306a36Sopenharmony_ci vgic_sanitise_outer_cacheability); 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci reg &= ~PROPBASER_RES0_MASK; 47462306a36Sopenharmony_ci return reg; 47562306a36Sopenharmony_ci} 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_cistatic unsigned long vgic_mmio_read_propbase(struct kvm_vcpu *vcpu, 47862306a36Sopenharmony_ci gpa_t addr, unsigned int len) 47962306a36Sopenharmony_ci{ 48062306a36Sopenharmony_ci struct vgic_dist *dist = &vcpu->kvm->arch.vgic; 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci return extract_bytes(dist->propbaser, addr & 7, len); 48362306a36Sopenharmony_ci} 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_cistatic void vgic_mmio_write_propbase(struct kvm_vcpu *vcpu, 48662306a36Sopenharmony_ci gpa_t addr, unsigned int len, 48762306a36Sopenharmony_ci unsigned long val) 48862306a36Sopenharmony_ci{ 48962306a36Sopenharmony_ci struct vgic_dist *dist = &vcpu->kvm->arch.vgic; 49062306a36Sopenharmony_ci u64 old_propbaser, propbaser; 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci /* Storing a value with LPIs already enabled is undefined */ 49362306a36Sopenharmony_ci if (vgic_lpis_enabled(vcpu)) 49462306a36Sopenharmony_ci return; 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci do { 49762306a36Sopenharmony_ci old_propbaser = READ_ONCE(dist->propbaser); 49862306a36Sopenharmony_ci propbaser = old_propbaser; 49962306a36Sopenharmony_ci propbaser = update_64bit_reg(propbaser, addr & 4, len, val); 50062306a36Sopenharmony_ci propbaser = vgic_sanitise_propbaser(propbaser); 50162306a36Sopenharmony_ci } while (cmpxchg64(&dist->propbaser, old_propbaser, 50262306a36Sopenharmony_ci propbaser) != old_propbaser); 50362306a36Sopenharmony_ci} 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_cistatic unsigned long vgic_mmio_read_pendbase(struct kvm_vcpu *vcpu, 50662306a36Sopenharmony_ci gpa_t addr, unsigned int len) 50762306a36Sopenharmony_ci{ 50862306a36Sopenharmony_ci struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu; 50962306a36Sopenharmony_ci u64 value = vgic_cpu->pendbaser; 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci value &= ~GICR_PENDBASER_PTZ; 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci return extract_bytes(value, addr & 7, len); 51462306a36Sopenharmony_ci} 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_cistatic void vgic_mmio_write_pendbase(struct kvm_vcpu *vcpu, 51762306a36Sopenharmony_ci gpa_t addr, unsigned int len, 51862306a36Sopenharmony_ci unsigned long val) 51962306a36Sopenharmony_ci{ 52062306a36Sopenharmony_ci struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu; 52162306a36Sopenharmony_ci u64 old_pendbaser, pendbaser; 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci /* Storing a value with LPIs already enabled is undefined */ 52462306a36Sopenharmony_ci if (vgic_lpis_enabled(vcpu)) 52562306a36Sopenharmony_ci return; 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci do { 52862306a36Sopenharmony_ci old_pendbaser = READ_ONCE(vgic_cpu->pendbaser); 52962306a36Sopenharmony_ci pendbaser = old_pendbaser; 53062306a36Sopenharmony_ci pendbaser = update_64bit_reg(pendbaser, addr & 4, len, val); 53162306a36Sopenharmony_ci pendbaser = vgic_sanitise_pendbaser(pendbaser); 53262306a36Sopenharmony_ci } while (cmpxchg64(&vgic_cpu->pendbaser, old_pendbaser, 53362306a36Sopenharmony_ci pendbaser) != old_pendbaser); 53462306a36Sopenharmony_ci} 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_cistatic unsigned long vgic_mmio_read_sync(struct kvm_vcpu *vcpu, 53762306a36Sopenharmony_ci gpa_t addr, unsigned int len) 53862306a36Sopenharmony_ci{ 53962306a36Sopenharmony_ci return !!atomic_read(&vcpu->arch.vgic_cpu.syncr_busy); 54062306a36Sopenharmony_ci} 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_cistatic void vgic_set_rdist_busy(struct kvm_vcpu *vcpu, bool busy) 54362306a36Sopenharmony_ci{ 54462306a36Sopenharmony_ci if (busy) { 54562306a36Sopenharmony_ci atomic_inc(&vcpu->arch.vgic_cpu.syncr_busy); 54662306a36Sopenharmony_ci smp_mb__after_atomic(); 54762306a36Sopenharmony_ci } else { 54862306a36Sopenharmony_ci smp_mb__before_atomic(); 54962306a36Sopenharmony_ci atomic_dec(&vcpu->arch.vgic_cpu.syncr_busy); 55062306a36Sopenharmony_ci } 55162306a36Sopenharmony_ci} 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_cistatic void vgic_mmio_write_invlpi(struct kvm_vcpu *vcpu, 55462306a36Sopenharmony_ci gpa_t addr, unsigned int len, 55562306a36Sopenharmony_ci unsigned long val) 55662306a36Sopenharmony_ci{ 55762306a36Sopenharmony_ci struct vgic_irq *irq; 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci /* 56062306a36Sopenharmony_ci * If the guest wrote only to the upper 32bit part of the 56162306a36Sopenharmony_ci * register, drop the write on the floor, as it is only for 56262306a36Sopenharmony_ci * vPEs (which we don't support for obvious reasons). 56362306a36Sopenharmony_ci * 56462306a36Sopenharmony_ci * Also discard the access if LPIs are not enabled. 56562306a36Sopenharmony_ci */ 56662306a36Sopenharmony_ci if ((addr & 4) || !vgic_lpis_enabled(vcpu)) 56762306a36Sopenharmony_ci return; 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci vgic_set_rdist_busy(vcpu, true); 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci irq = vgic_get_irq(vcpu->kvm, NULL, lower_32_bits(val)); 57262306a36Sopenharmony_ci if (irq) { 57362306a36Sopenharmony_ci vgic_its_inv_lpi(vcpu->kvm, irq); 57462306a36Sopenharmony_ci vgic_put_irq(vcpu->kvm, irq); 57562306a36Sopenharmony_ci } 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci vgic_set_rdist_busy(vcpu, false); 57862306a36Sopenharmony_ci} 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_cistatic void vgic_mmio_write_invall(struct kvm_vcpu *vcpu, 58162306a36Sopenharmony_ci gpa_t addr, unsigned int len, 58262306a36Sopenharmony_ci unsigned long val) 58362306a36Sopenharmony_ci{ 58462306a36Sopenharmony_ci /* See vgic_mmio_write_invlpi() for the early return rationale */ 58562306a36Sopenharmony_ci if ((addr & 4) || !vgic_lpis_enabled(vcpu)) 58662306a36Sopenharmony_ci return; 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci vgic_set_rdist_busy(vcpu, true); 58962306a36Sopenharmony_ci vgic_its_invall(vcpu); 59062306a36Sopenharmony_ci vgic_set_rdist_busy(vcpu, false); 59162306a36Sopenharmony_ci} 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci/* 59462306a36Sopenharmony_ci * The GICv3 per-IRQ registers are split to control PPIs and SGIs in the 59562306a36Sopenharmony_ci * redistributors, while SPIs are covered by registers in the distributor 59662306a36Sopenharmony_ci * block. Trying to set private IRQs in this block gets ignored. 59762306a36Sopenharmony_ci * We take some special care here to fix the calculation of the register 59862306a36Sopenharmony_ci * offset. 59962306a36Sopenharmony_ci */ 60062306a36Sopenharmony_ci#define REGISTER_DESC_WITH_BITS_PER_IRQ_SHARED(off, rd, wr, ur, uw, bpi, acc) \ 60162306a36Sopenharmony_ci { \ 60262306a36Sopenharmony_ci .reg_offset = off, \ 60362306a36Sopenharmony_ci .bits_per_irq = bpi, \ 60462306a36Sopenharmony_ci .len = (bpi * VGIC_NR_PRIVATE_IRQS) / 8, \ 60562306a36Sopenharmony_ci .access_flags = acc, \ 60662306a36Sopenharmony_ci .read = vgic_mmio_read_raz, \ 60762306a36Sopenharmony_ci .write = vgic_mmio_write_wi, \ 60862306a36Sopenharmony_ci }, { \ 60962306a36Sopenharmony_ci .reg_offset = off + (bpi * VGIC_NR_PRIVATE_IRQS) / 8, \ 61062306a36Sopenharmony_ci .bits_per_irq = bpi, \ 61162306a36Sopenharmony_ci .len = (bpi * (1024 - VGIC_NR_PRIVATE_IRQS)) / 8, \ 61262306a36Sopenharmony_ci .access_flags = acc, \ 61362306a36Sopenharmony_ci .read = rd, \ 61462306a36Sopenharmony_ci .write = wr, \ 61562306a36Sopenharmony_ci .uaccess_read = ur, \ 61662306a36Sopenharmony_ci .uaccess_write = uw, \ 61762306a36Sopenharmony_ci } 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_cistatic const struct vgic_register_region vgic_v3_dist_registers[] = { 62062306a36Sopenharmony_ci REGISTER_DESC_WITH_LENGTH_UACCESS(GICD_CTLR, 62162306a36Sopenharmony_ci vgic_mmio_read_v3_misc, vgic_mmio_write_v3_misc, 62262306a36Sopenharmony_ci NULL, vgic_mmio_uaccess_write_v3_misc, 62362306a36Sopenharmony_ci 16, VGIC_ACCESS_32bit), 62462306a36Sopenharmony_ci REGISTER_DESC_WITH_LENGTH(GICD_STATUSR, 62562306a36Sopenharmony_ci vgic_mmio_read_rao, vgic_mmio_write_wi, 4, 62662306a36Sopenharmony_ci VGIC_ACCESS_32bit), 62762306a36Sopenharmony_ci REGISTER_DESC_WITH_BITS_PER_IRQ_SHARED(GICD_IGROUPR, 62862306a36Sopenharmony_ci vgic_mmio_read_group, vgic_mmio_write_group, NULL, NULL, 1, 62962306a36Sopenharmony_ci VGIC_ACCESS_32bit), 63062306a36Sopenharmony_ci REGISTER_DESC_WITH_BITS_PER_IRQ_SHARED(GICD_ISENABLER, 63162306a36Sopenharmony_ci vgic_mmio_read_enable, vgic_mmio_write_senable, 63262306a36Sopenharmony_ci NULL, vgic_uaccess_write_senable, 1, 63362306a36Sopenharmony_ci VGIC_ACCESS_32bit), 63462306a36Sopenharmony_ci REGISTER_DESC_WITH_BITS_PER_IRQ_SHARED(GICD_ICENABLER, 63562306a36Sopenharmony_ci vgic_mmio_read_enable, vgic_mmio_write_cenable, 63662306a36Sopenharmony_ci NULL, vgic_uaccess_write_cenable, 1, 63762306a36Sopenharmony_ci VGIC_ACCESS_32bit), 63862306a36Sopenharmony_ci REGISTER_DESC_WITH_BITS_PER_IRQ_SHARED(GICD_ISPENDR, 63962306a36Sopenharmony_ci vgic_mmio_read_pending, vgic_mmio_write_spending, 64062306a36Sopenharmony_ci vgic_uaccess_read_pending, vgic_v3_uaccess_write_pending, 1, 64162306a36Sopenharmony_ci VGIC_ACCESS_32bit), 64262306a36Sopenharmony_ci REGISTER_DESC_WITH_BITS_PER_IRQ_SHARED(GICD_ICPENDR, 64362306a36Sopenharmony_ci vgic_mmio_read_pending, vgic_mmio_write_cpending, 64462306a36Sopenharmony_ci vgic_mmio_read_raz, vgic_mmio_uaccess_write_wi, 1, 64562306a36Sopenharmony_ci VGIC_ACCESS_32bit), 64662306a36Sopenharmony_ci REGISTER_DESC_WITH_BITS_PER_IRQ_SHARED(GICD_ISACTIVER, 64762306a36Sopenharmony_ci vgic_mmio_read_active, vgic_mmio_write_sactive, 64862306a36Sopenharmony_ci vgic_uaccess_read_active, vgic_mmio_uaccess_write_sactive, 1, 64962306a36Sopenharmony_ci VGIC_ACCESS_32bit), 65062306a36Sopenharmony_ci REGISTER_DESC_WITH_BITS_PER_IRQ_SHARED(GICD_ICACTIVER, 65162306a36Sopenharmony_ci vgic_mmio_read_active, vgic_mmio_write_cactive, 65262306a36Sopenharmony_ci vgic_uaccess_read_active, vgic_mmio_uaccess_write_cactive, 65362306a36Sopenharmony_ci 1, VGIC_ACCESS_32bit), 65462306a36Sopenharmony_ci REGISTER_DESC_WITH_BITS_PER_IRQ_SHARED(GICD_IPRIORITYR, 65562306a36Sopenharmony_ci vgic_mmio_read_priority, vgic_mmio_write_priority, NULL, NULL, 65662306a36Sopenharmony_ci 8, VGIC_ACCESS_32bit | VGIC_ACCESS_8bit), 65762306a36Sopenharmony_ci REGISTER_DESC_WITH_BITS_PER_IRQ_SHARED(GICD_ITARGETSR, 65862306a36Sopenharmony_ci vgic_mmio_read_raz, vgic_mmio_write_wi, NULL, NULL, 8, 65962306a36Sopenharmony_ci VGIC_ACCESS_32bit | VGIC_ACCESS_8bit), 66062306a36Sopenharmony_ci REGISTER_DESC_WITH_BITS_PER_IRQ_SHARED(GICD_ICFGR, 66162306a36Sopenharmony_ci vgic_mmio_read_config, vgic_mmio_write_config, NULL, NULL, 2, 66262306a36Sopenharmony_ci VGIC_ACCESS_32bit), 66362306a36Sopenharmony_ci REGISTER_DESC_WITH_BITS_PER_IRQ_SHARED(GICD_IGRPMODR, 66462306a36Sopenharmony_ci vgic_mmio_read_raz, vgic_mmio_write_wi, NULL, NULL, 1, 66562306a36Sopenharmony_ci VGIC_ACCESS_32bit), 66662306a36Sopenharmony_ci REGISTER_DESC_WITH_BITS_PER_IRQ_SHARED(GICD_IROUTER, 66762306a36Sopenharmony_ci vgic_mmio_read_irouter, vgic_mmio_write_irouter, NULL, NULL, 64, 66862306a36Sopenharmony_ci VGIC_ACCESS_64bit | VGIC_ACCESS_32bit), 66962306a36Sopenharmony_ci REGISTER_DESC_WITH_LENGTH(GICD_IDREGS, 67062306a36Sopenharmony_ci vgic_mmio_read_v3_idregs, vgic_mmio_write_wi, 48, 67162306a36Sopenharmony_ci VGIC_ACCESS_32bit), 67262306a36Sopenharmony_ci}; 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_cistatic const struct vgic_register_region vgic_v3_rd_registers[] = { 67562306a36Sopenharmony_ci /* RD_base registers */ 67662306a36Sopenharmony_ci REGISTER_DESC_WITH_LENGTH(GICR_CTLR, 67762306a36Sopenharmony_ci vgic_mmio_read_v3r_ctlr, vgic_mmio_write_v3r_ctlr, 4, 67862306a36Sopenharmony_ci VGIC_ACCESS_32bit), 67962306a36Sopenharmony_ci REGISTER_DESC_WITH_LENGTH(GICR_STATUSR, 68062306a36Sopenharmony_ci vgic_mmio_read_raz, vgic_mmio_write_wi, 4, 68162306a36Sopenharmony_ci VGIC_ACCESS_32bit), 68262306a36Sopenharmony_ci REGISTER_DESC_WITH_LENGTH(GICR_IIDR, 68362306a36Sopenharmony_ci vgic_mmio_read_v3r_iidr, vgic_mmio_write_wi, 4, 68462306a36Sopenharmony_ci VGIC_ACCESS_32bit), 68562306a36Sopenharmony_ci REGISTER_DESC_WITH_LENGTH_UACCESS(GICR_TYPER, 68662306a36Sopenharmony_ci vgic_mmio_read_v3r_typer, vgic_mmio_write_wi, 68762306a36Sopenharmony_ci NULL, vgic_mmio_uaccess_write_wi, 8, 68862306a36Sopenharmony_ci VGIC_ACCESS_64bit | VGIC_ACCESS_32bit), 68962306a36Sopenharmony_ci REGISTER_DESC_WITH_LENGTH(GICR_WAKER, 69062306a36Sopenharmony_ci vgic_mmio_read_raz, vgic_mmio_write_wi, 4, 69162306a36Sopenharmony_ci VGIC_ACCESS_32bit), 69262306a36Sopenharmony_ci REGISTER_DESC_WITH_LENGTH(GICR_PROPBASER, 69362306a36Sopenharmony_ci vgic_mmio_read_propbase, vgic_mmio_write_propbase, 8, 69462306a36Sopenharmony_ci VGIC_ACCESS_64bit | VGIC_ACCESS_32bit), 69562306a36Sopenharmony_ci REGISTER_DESC_WITH_LENGTH(GICR_PENDBASER, 69662306a36Sopenharmony_ci vgic_mmio_read_pendbase, vgic_mmio_write_pendbase, 8, 69762306a36Sopenharmony_ci VGIC_ACCESS_64bit | VGIC_ACCESS_32bit), 69862306a36Sopenharmony_ci REGISTER_DESC_WITH_LENGTH(GICR_INVLPIR, 69962306a36Sopenharmony_ci vgic_mmio_read_raz, vgic_mmio_write_invlpi, 8, 70062306a36Sopenharmony_ci VGIC_ACCESS_64bit | VGIC_ACCESS_32bit), 70162306a36Sopenharmony_ci REGISTER_DESC_WITH_LENGTH(GICR_INVALLR, 70262306a36Sopenharmony_ci vgic_mmio_read_raz, vgic_mmio_write_invall, 8, 70362306a36Sopenharmony_ci VGIC_ACCESS_64bit | VGIC_ACCESS_32bit), 70462306a36Sopenharmony_ci REGISTER_DESC_WITH_LENGTH(GICR_SYNCR, 70562306a36Sopenharmony_ci vgic_mmio_read_sync, vgic_mmio_write_wi, 4, 70662306a36Sopenharmony_ci VGIC_ACCESS_32bit), 70762306a36Sopenharmony_ci REGISTER_DESC_WITH_LENGTH(GICR_IDREGS, 70862306a36Sopenharmony_ci vgic_mmio_read_v3_idregs, vgic_mmio_write_wi, 48, 70962306a36Sopenharmony_ci VGIC_ACCESS_32bit), 71062306a36Sopenharmony_ci /* SGI_base registers */ 71162306a36Sopenharmony_ci REGISTER_DESC_WITH_LENGTH(SZ_64K + GICR_IGROUPR0, 71262306a36Sopenharmony_ci vgic_mmio_read_group, vgic_mmio_write_group, 4, 71362306a36Sopenharmony_ci VGIC_ACCESS_32bit), 71462306a36Sopenharmony_ci REGISTER_DESC_WITH_LENGTH_UACCESS(SZ_64K + GICR_ISENABLER0, 71562306a36Sopenharmony_ci vgic_mmio_read_enable, vgic_mmio_write_senable, 71662306a36Sopenharmony_ci NULL, vgic_uaccess_write_senable, 4, 71762306a36Sopenharmony_ci VGIC_ACCESS_32bit), 71862306a36Sopenharmony_ci REGISTER_DESC_WITH_LENGTH_UACCESS(SZ_64K + GICR_ICENABLER0, 71962306a36Sopenharmony_ci vgic_mmio_read_enable, vgic_mmio_write_cenable, 72062306a36Sopenharmony_ci NULL, vgic_uaccess_write_cenable, 4, 72162306a36Sopenharmony_ci VGIC_ACCESS_32bit), 72262306a36Sopenharmony_ci REGISTER_DESC_WITH_LENGTH_UACCESS(SZ_64K + GICR_ISPENDR0, 72362306a36Sopenharmony_ci vgic_mmio_read_pending, vgic_mmio_write_spending, 72462306a36Sopenharmony_ci vgic_uaccess_read_pending, vgic_v3_uaccess_write_pending, 4, 72562306a36Sopenharmony_ci VGIC_ACCESS_32bit), 72662306a36Sopenharmony_ci REGISTER_DESC_WITH_LENGTH_UACCESS(SZ_64K + GICR_ICPENDR0, 72762306a36Sopenharmony_ci vgic_mmio_read_pending, vgic_mmio_write_cpending, 72862306a36Sopenharmony_ci vgic_mmio_read_raz, vgic_mmio_uaccess_write_wi, 4, 72962306a36Sopenharmony_ci VGIC_ACCESS_32bit), 73062306a36Sopenharmony_ci REGISTER_DESC_WITH_LENGTH_UACCESS(SZ_64K + GICR_ISACTIVER0, 73162306a36Sopenharmony_ci vgic_mmio_read_active, vgic_mmio_write_sactive, 73262306a36Sopenharmony_ci vgic_uaccess_read_active, vgic_mmio_uaccess_write_sactive, 4, 73362306a36Sopenharmony_ci VGIC_ACCESS_32bit), 73462306a36Sopenharmony_ci REGISTER_DESC_WITH_LENGTH_UACCESS(SZ_64K + GICR_ICACTIVER0, 73562306a36Sopenharmony_ci vgic_mmio_read_active, vgic_mmio_write_cactive, 73662306a36Sopenharmony_ci vgic_uaccess_read_active, vgic_mmio_uaccess_write_cactive, 4, 73762306a36Sopenharmony_ci VGIC_ACCESS_32bit), 73862306a36Sopenharmony_ci REGISTER_DESC_WITH_LENGTH(SZ_64K + GICR_IPRIORITYR0, 73962306a36Sopenharmony_ci vgic_mmio_read_priority, vgic_mmio_write_priority, 32, 74062306a36Sopenharmony_ci VGIC_ACCESS_32bit | VGIC_ACCESS_8bit), 74162306a36Sopenharmony_ci REGISTER_DESC_WITH_LENGTH(SZ_64K + GICR_ICFGR0, 74262306a36Sopenharmony_ci vgic_mmio_read_config, vgic_mmio_write_config, 8, 74362306a36Sopenharmony_ci VGIC_ACCESS_32bit), 74462306a36Sopenharmony_ci REGISTER_DESC_WITH_LENGTH(SZ_64K + GICR_IGRPMODR0, 74562306a36Sopenharmony_ci vgic_mmio_read_raz, vgic_mmio_write_wi, 4, 74662306a36Sopenharmony_ci VGIC_ACCESS_32bit), 74762306a36Sopenharmony_ci REGISTER_DESC_WITH_LENGTH(SZ_64K + GICR_NSACR, 74862306a36Sopenharmony_ci vgic_mmio_read_raz, vgic_mmio_write_wi, 4, 74962306a36Sopenharmony_ci VGIC_ACCESS_32bit), 75062306a36Sopenharmony_ci}; 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ciunsigned int vgic_v3_init_dist_iodev(struct vgic_io_device *dev) 75362306a36Sopenharmony_ci{ 75462306a36Sopenharmony_ci dev->regions = vgic_v3_dist_registers; 75562306a36Sopenharmony_ci dev->nr_regions = ARRAY_SIZE(vgic_v3_dist_registers); 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci kvm_iodevice_init(&dev->dev, &kvm_io_gic_ops); 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci return SZ_64K; 76062306a36Sopenharmony_ci} 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci/** 76362306a36Sopenharmony_ci * vgic_register_redist_iodev - register a single redist iodev 76462306a36Sopenharmony_ci * @vcpu: The VCPU to which the redistributor belongs 76562306a36Sopenharmony_ci * 76662306a36Sopenharmony_ci * Register a KVM iodev for this VCPU's redistributor using the address 76762306a36Sopenharmony_ci * provided. 76862306a36Sopenharmony_ci * 76962306a36Sopenharmony_ci * Return 0 on success, -ERRNO otherwise. 77062306a36Sopenharmony_ci */ 77162306a36Sopenharmony_ciint vgic_register_redist_iodev(struct kvm_vcpu *vcpu) 77262306a36Sopenharmony_ci{ 77362306a36Sopenharmony_ci struct kvm *kvm = vcpu->kvm; 77462306a36Sopenharmony_ci struct vgic_dist *vgic = &kvm->arch.vgic; 77562306a36Sopenharmony_ci struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu; 77662306a36Sopenharmony_ci struct vgic_io_device *rd_dev = &vcpu->arch.vgic_cpu.rd_iodev; 77762306a36Sopenharmony_ci struct vgic_redist_region *rdreg; 77862306a36Sopenharmony_ci gpa_t rd_base; 77962306a36Sopenharmony_ci int ret = 0; 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci lockdep_assert_held(&kvm->slots_lock); 78262306a36Sopenharmony_ci mutex_lock(&kvm->arch.config_lock); 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci if (!IS_VGIC_ADDR_UNDEF(vgic_cpu->rd_iodev.base_addr)) 78562306a36Sopenharmony_ci goto out_unlock; 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_ci /* 78862306a36Sopenharmony_ci * We may be creating VCPUs before having set the base address for the 78962306a36Sopenharmony_ci * redistributor region, in which case we will come back to this 79062306a36Sopenharmony_ci * function for all VCPUs when the base address is set. Just return 79162306a36Sopenharmony_ci * without doing any work for now. 79262306a36Sopenharmony_ci */ 79362306a36Sopenharmony_ci rdreg = vgic_v3_rdist_free_slot(&vgic->rd_regions); 79462306a36Sopenharmony_ci if (!rdreg) 79562306a36Sopenharmony_ci goto out_unlock; 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci if (!vgic_v3_check_base(kvm)) { 79862306a36Sopenharmony_ci ret = -EINVAL; 79962306a36Sopenharmony_ci goto out_unlock; 80062306a36Sopenharmony_ci } 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci vgic_cpu->rdreg = rdreg; 80362306a36Sopenharmony_ci vgic_cpu->rdreg_index = rdreg->free_index; 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci rd_base = rdreg->base + rdreg->free_index * KVM_VGIC_V3_REDIST_SIZE; 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_ci kvm_iodevice_init(&rd_dev->dev, &kvm_io_gic_ops); 80862306a36Sopenharmony_ci rd_dev->base_addr = rd_base; 80962306a36Sopenharmony_ci rd_dev->iodev_type = IODEV_REDIST; 81062306a36Sopenharmony_ci rd_dev->regions = vgic_v3_rd_registers; 81162306a36Sopenharmony_ci rd_dev->nr_regions = ARRAY_SIZE(vgic_v3_rd_registers); 81262306a36Sopenharmony_ci rd_dev->redist_vcpu = vcpu; 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_ci mutex_unlock(&kvm->arch.config_lock); 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci ret = kvm_io_bus_register_dev(kvm, KVM_MMIO_BUS, rd_base, 81762306a36Sopenharmony_ci 2 * SZ_64K, &rd_dev->dev); 81862306a36Sopenharmony_ci if (ret) 81962306a36Sopenharmony_ci return ret; 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_ci /* Protected by slots_lock */ 82262306a36Sopenharmony_ci rdreg->free_index++; 82362306a36Sopenharmony_ci return 0; 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_ciout_unlock: 82662306a36Sopenharmony_ci mutex_unlock(&kvm->arch.config_lock); 82762306a36Sopenharmony_ci return ret; 82862306a36Sopenharmony_ci} 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_civoid vgic_unregister_redist_iodev(struct kvm_vcpu *vcpu) 83162306a36Sopenharmony_ci{ 83262306a36Sopenharmony_ci struct vgic_io_device *rd_dev = &vcpu->arch.vgic_cpu.rd_iodev; 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci kvm_io_bus_unregister_dev(vcpu->kvm, KVM_MMIO_BUS, &rd_dev->dev); 83562306a36Sopenharmony_ci} 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_cistatic int vgic_register_all_redist_iodevs(struct kvm *kvm) 83862306a36Sopenharmony_ci{ 83962306a36Sopenharmony_ci struct kvm_vcpu *vcpu; 84062306a36Sopenharmony_ci unsigned long c; 84162306a36Sopenharmony_ci int ret = 0; 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_ci kvm_for_each_vcpu(c, vcpu, kvm) { 84462306a36Sopenharmony_ci ret = vgic_register_redist_iodev(vcpu); 84562306a36Sopenharmony_ci if (ret) 84662306a36Sopenharmony_ci break; 84762306a36Sopenharmony_ci } 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ci if (ret) { 85062306a36Sopenharmony_ci /* The current c failed, so iterate over the previous ones. */ 85162306a36Sopenharmony_ci int i; 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ci for (i = 0; i < c; i++) { 85462306a36Sopenharmony_ci vcpu = kvm_get_vcpu(kvm, i); 85562306a36Sopenharmony_ci vgic_unregister_redist_iodev(vcpu); 85662306a36Sopenharmony_ci } 85762306a36Sopenharmony_ci } 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_ci return ret; 86062306a36Sopenharmony_ci} 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_ci/** 86362306a36Sopenharmony_ci * vgic_v3_alloc_redist_region - Allocate a new redistributor region 86462306a36Sopenharmony_ci * 86562306a36Sopenharmony_ci * Performs various checks before inserting the rdist region in the list. 86662306a36Sopenharmony_ci * Those tests depend on whether the size of the rdist region is known 86762306a36Sopenharmony_ci * (ie. count != 0). The list is sorted by rdist region index. 86862306a36Sopenharmony_ci * 86962306a36Sopenharmony_ci * @kvm: kvm handle 87062306a36Sopenharmony_ci * @index: redist region index 87162306a36Sopenharmony_ci * @base: base of the new rdist region 87262306a36Sopenharmony_ci * @count: number of redistributors the region is made of (0 in the old style 87362306a36Sopenharmony_ci * single region, whose size is induced from the number of vcpus) 87462306a36Sopenharmony_ci * 87562306a36Sopenharmony_ci * Return 0 on success, < 0 otherwise 87662306a36Sopenharmony_ci */ 87762306a36Sopenharmony_cistatic int vgic_v3_alloc_redist_region(struct kvm *kvm, uint32_t index, 87862306a36Sopenharmony_ci gpa_t base, uint32_t count) 87962306a36Sopenharmony_ci{ 88062306a36Sopenharmony_ci struct vgic_dist *d = &kvm->arch.vgic; 88162306a36Sopenharmony_ci struct vgic_redist_region *rdreg; 88262306a36Sopenharmony_ci struct list_head *rd_regions = &d->rd_regions; 88362306a36Sopenharmony_ci int nr_vcpus = atomic_read(&kvm->online_vcpus); 88462306a36Sopenharmony_ci size_t size = count ? count * KVM_VGIC_V3_REDIST_SIZE 88562306a36Sopenharmony_ci : nr_vcpus * KVM_VGIC_V3_REDIST_SIZE; 88662306a36Sopenharmony_ci int ret; 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_ci /* cross the end of memory ? */ 88962306a36Sopenharmony_ci if (base + size < base) 89062306a36Sopenharmony_ci return -EINVAL; 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_ci if (list_empty(rd_regions)) { 89362306a36Sopenharmony_ci if (index != 0) 89462306a36Sopenharmony_ci return -EINVAL; 89562306a36Sopenharmony_ci } else { 89662306a36Sopenharmony_ci rdreg = list_last_entry(rd_regions, 89762306a36Sopenharmony_ci struct vgic_redist_region, list); 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_ci /* Don't mix single region and discrete redist regions */ 90062306a36Sopenharmony_ci if (!count && rdreg->count) 90162306a36Sopenharmony_ci return -EINVAL; 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_ci if (!count) 90462306a36Sopenharmony_ci return -EEXIST; 90562306a36Sopenharmony_ci 90662306a36Sopenharmony_ci if (index != rdreg->index + 1) 90762306a36Sopenharmony_ci return -EINVAL; 90862306a36Sopenharmony_ci } 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ci /* 91162306a36Sopenharmony_ci * For legacy single-region redistributor regions (!count), 91262306a36Sopenharmony_ci * check that the redistributor region does not overlap with the 91362306a36Sopenharmony_ci * distributor's address space. 91462306a36Sopenharmony_ci */ 91562306a36Sopenharmony_ci if (!count && !IS_VGIC_ADDR_UNDEF(d->vgic_dist_base) && 91662306a36Sopenharmony_ci vgic_dist_overlap(kvm, base, size)) 91762306a36Sopenharmony_ci return -EINVAL; 91862306a36Sopenharmony_ci 91962306a36Sopenharmony_ci /* collision with any other rdist region? */ 92062306a36Sopenharmony_ci if (vgic_v3_rdist_overlap(kvm, base, size)) 92162306a36Sopenharmony_ci return -EINVAL; 92262306a36Sopenharmony_ci 92362306a36Sopenharmony_ci rdreg = kzalloc(sizeof(*rdreg), GFP_KERNEL_ACCOUNT); 92462306a36Sopenharmony_ci if (!rdreg) 92562306a36Sopenharmony_ci return -ENOMEM; 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_ci rdreg->base = VGIC_ADDR_UNDEF; 92862306a36Sopenharmony_ci 92962306a36Sopenharmony_ci ret = vgic_check_iorange(kvm, rdreg->base, base, SZ_64K, size); 93062306a36Sopenharmony_ci if (ret) 93162306a36Sopenharmony_ci goto free; 93262306a36Sopenharmony_ci 93362306a36Sopenharmony_ci rdreg->base = base; 93462306a36Sopenharmony_ci rdreg->count = count; 93562306a36Sopenharmony_ci rdreg->free_index = 0; 93662306a36Sopenharmony_ci rdreg->index = index; 93762306a36Sopenharmony_ci 93862306a36Sopenharmony_ci list_add_tail(&rdreg->list, rd_regions); 93962306a36Sopenharmony_ci return 0; 94062306a36Sopenharmony_cifree: 94162306a36Sopenharmony_ci kfree(rdreg); 94262306a36Sopenharmony_ci return ret; 94362306a36Sopenharmony_ci} 94462306a36Sopenharmony_ci 94562306a36Sopenharmony_civoid vgic_v3_free_redist_region(struct vgic_redist_region *rdreg) 94662306a36Sopenharmony_ci{ 94762306a36Sopenharmony_ci list_del(&rdreg->list); 94862306a36Sopenharmony_ci kfree(rdreg); 94962306a36Sopenharmony_ci} 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_ciint vgic_v3_set_redist_base(struct kvm *kvm, u32 index, u64 addr, u32 count) 95262306a36Sopenharmony_ci{ 95362306a36Sopenharmony_ci int ret; 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_ci mutex_lock(&kvm->arch.config_lock); 95662306a36Sopenharmony_ci ret = vgic_v3_alloc_redist_region(kvm, index, addr, count); 95762306a36Sopenharmony_ci mutex_unlock(&kvm->arch.config_lock); 95862306a36Sopenharmony_ci if (ret) 95962306a36Sopenharmony_ci return ret; 96062306a36Sopenharmony_ci 96162306a36Sopenharmony_ci /* 96262306a36Sopenharmony_ci * Register iodevs for each existing VCPU. Adding more VCPUs 96362306a36Sopenharmony_ci * afterwards will register the iodevs when needed. 96462306a36Sopenharmony_ci */ 96562306a36Sopenharmony_ci ret = vgic_register_all_redist_iodevs(kvm); 96662306a36Sopenharmony_ci if (ret) { 96762306a36Sopenharmony_ci struct vgic_redist_region *rdreg; 96862306a36Sopenharmony_ci 96962306a36Sopenharmony_ci mutex_lock(&kvm->arch.config_lock); 97062306a36Sopenharmony_ci rdreg = vgic_v3_rdist_region_from_index(kvm, index); 97162306a36Sopenharmony_ci vgic_v3_free_redist_region(rdreg); 97262306a36Sopenharmony_ci mutex_unlock(&kvm->arch.config_lock); 97362306a36Sopenharmony_ci return ret; 97462306a36Sopenharmony_ci } 97562306a36Sopenharmony_ci 97662306a36Sopenharmony_ci return 0; 97762306a36Sopenharmony_ci} 97862306a36Sopenharmony_ci 97962306a36Sopenharmony_ciint vgic_v3_has_attr_regs(struct kvm_device *dev, struct kvm_device_attr *attr) 98062306a36Sopenharmony_ci{ 98162306a36Sopenharmony_ci const struct vgic_register_region *region; 98262306a36Sopenharmony_ci struct vgic_io_device iodev; 98362306a36Sopenharmony_ci struct vgic_reg_attr reg_attr; 98462306a36Sopenharmony_ci struct kvm_vcpu *vcpu; 98562306a36Sopenharmony_ci gpa_t addr; 98662306a36Sopenharmony_ci int ret; 98762306a36Sopenharmony_ci 98862306a36Sopenharmony_ci ret = vgic_v3_parse_attr(dev, attr, ®_attr); 98962306a36Sopenharmony_ci if (ret) 99062306a36Sopenharmony_ci return ret; 99162306a36Sopenharmony_ci 99262306a36Sopenharmony_ci vcpu = reg_attr.vcpu; 99362306a36Sopenharmony_ci addr = reg_attr.addr; 99462306a36Sopenharmony_ci 99562306a36Sopenharmony_ci switch (attr->group) { 99662306a36Sopenharmony_ci case KVM_DEV_ARM_VGIC_GRP_DIST_REGS: 99762306a36Sopenharmony_ci iodev.regions = vgic_v3_dist_registers; 99862306a36Sopenharmony_ci iodev.nr_regions = ARRAY_SIZE(vgic_v3_dist_registers); 99962306a36Sopenharmony_ci iodev.base_addr = 0; 100062306a36Sopenharmony_ci break; 100162306a36Sopenharmony_ci case KVM_DEV_ARM_VGIC_GRP_REDIST_REGS:{ 100262306a36Sopenharmony_ci iodev.regions = vgic_v3_rd_registers; 100362306a36Sopenharmony_ci iodev.nr_regions = ARRAY_SIZE(vgic_v3_rd_registers); 100462306a36Sopenharmony_ci iodev.base_addr = 0; 100562306a36Sopenharmony_ci break; 100662306a36Sopenharmony_ci } 100762306a36Sopenharmony_ci case KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS: 100862306a36Sopenharmony_ci return vgic_v3_has_cpu_sysregs_attr(vcpu, attr); 100962306a36Sopenharmony_ci default: 101062306a36Sopenharmony_ci return -ENXIO; 101162306a36Sopenharmony_ci } 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_ci /* We only support aligned 32-bit accesses. */ 101462306a36Sopenharmony_ci if (addr & 3) 101562306a36Sopenharmony_ci return -ENXIO; 101662306a36Sopenharmony_ci 101762306a36Sopenharmony_ci region = vgic_get_mmio_region(vcpu, &iodev, addr, sizeof(u32)); 101862306a36Sopenharmony_ci if (!region) 101962306a36Sopenharmony_ci return -ENXIO; 102062306a36Sopenharmony_ci 102162306a36Sopenharmony_ci return 0; 102262306a36Sopenharmony_ci} 102362306a36Sopenharmony_ci/* 102462306a36Sopenharmony_ci * Compare a given affinity (level 1-3 and a level 0 mask, from the SGI 102562306a36Sopenharmony_ci * generation register ICC_SGI1R_EL1) with a given VCPU. 102662306a36Sopenharmony_ci * If the VCPU's MPIDR matches, return the level0 affinity, otherwise 102762306a36Sopenharmony_ci * return -1. 102862306a36Sopenharmony_ci */ 102962306a36Sopenharmony_cistatic int match_mpidr(u64 sgi_aff, u16 sgi_cpu_mask, struct kvm_vcpu *vcpu) 103062306a36Sopenharmony_ci{ 103162306a36Sopenharmony_ci unsigned long affinity; 103262306a36Sopenharmony_ci int level0; 103362306a36Sopenharmony_ci 103462306a36Sopenharmony_ci /* 103562306a36Sopenharmony_ci * Split the current VCPU's MPIDR into affinity level 0 and the 103662306a36Sopenharmony_ci * rest as this is what we have to compare against. 103762306a36Sopenharmony_ci */ 103862306a36Sopenharmony_ci affinity = kvm_vcpu_get_mpidr_aff(vcpu); 103962306a36Sopenharmony_ci level0 = MPIDR_AFFINITY_LEVEL(affinity, 0); 104062306a36Sopenharmony_ci affinity &= ~MPIDR_LEVEL_MASK; 104162306a36Sopenharmony_ci 104262306a36Sopenharmony_ci /* bail out if the upper three levels don't match */ 104362306a36Sopenharmony_ci if (sgi_aff != affinity) 104462306a36Sopenharmony_ci return -1; 104562306a36Sopenharmony_ci 104662306a36Sopenharmony_ci /* Is this VCPU's bit set in the mask ? */ 104762306a36Sopenharmony_ci if (!(sgi_cpu_mask & BIT(level0))) 104862306a36Sopenharmony_ci return -1; 104962306a36Sopenharmony_ci 105062306a36Sopenharmony_ci return level0; 105162306a36Sopenharmony_ci} 105262306a36Sopenharmony_ci 105362306a36Sopenharmony_ci/* 105462306a36Sopenharmony_ci * The ICC_SGI* registers encode the affinity differently from the MPIDR, 105562306a36Sopenharmony_ci * so provide a wrapper to use the existing defines to isolate a certain 105662306a36Sopenharmony_ci * affinity level. 105762306a36Sopenharmony_ci */ 105862306a36Sopenharmony_ci#define SGI_AFFINITY_LEVEL(reg, level) \ 105962306a36Sopenharmony_ci ((((reg) & ICC_SGI1R_AFFINITY_## level ##_MASK) \ 106062306a36Sopenharmony_ci >> ICC_SGI1R_AFFINITY_## level ##_SHIFT) << MPIDR_LEVEL_SHIFT(level)) 106162306a36Sopenharmony_ci 106262306a36Sopenharmony_ci/** 106362306a36Sopenharmony_ci * vgic_v3_dispatch_sgi - handle SGI requests from VCPUs 106462306a36Sopenharmony_ci * @vcpu: The VCPU requesting a SGI 106562306a36Sopenharmony_ci * @reg: The value written into ICC_{ASGI1,SGI0,SGI1}R by that VCPU 106662306a36Sopenharmony_ci * @allow_group1: Does the sysreg access allow generation of G1 SGIs 106762306a36Sopenharmony_ci * 106862306a36Sopenharmony_ci * With GICv3 (and ARE=1) CPUs trigger SGIs by writing to a system register. 106962306a36Sopenharmony_ci * This will trap in sys_regs.c and call this function. 107062306a36Sopenharmony_ci * This ICC_SGI1R_EL1 register contains the upper three affinity levels of the 107162306a36Sopenharmony_ci * target processors as well as a bitmask of 16 Aff0 CPUs. 107262306a36Sopenharmony_ci * If the interrupt routing mode bit is not set, we iterate over all VCPUs to 107362306a36Sopenharmony_ci * check for matching ones. If this bit is set, we signal all, but not the 107462306a36Sopenharmony_ci * calling VCPU. 107562306a36Sopenharmony_ci */ 107662306a36Sopenharmony_civoid vgic_v3_dispatch_sgi(struct kvm_vcpu *vcpu, u64 reg, bool allow_group1) 107762306a36Sopenharmony_ci{ 107862306a36Sopenharmony_ci struct kvm *kvm = vcpu->kvm; 107962306a36Sopenharmony_ci struct kvm_vcpu *c_vcpu; 108062306a36Sopenharmony_ci u16 target_cpus; 108162306a36Sopenharmony_ci u64 mpidr; 108262306a36Sopenharmony_ci int sgi; 108362306a36Sopenharmony_ci int vcpu_id = vcpu->vcpu_id; 108462306a36Sopenharmony_ci bool broadcast; 108562306a36Sopenharmony_ci unsigned long c, flags; 108662306a36Sopenharmony_ci 108762306a36Sopenharmony_ci sgi = (reg & ICC_SGI1R_SGI_ID_MASK) >> ICC_SGI1R_SGI_ID_SHIFT; 108862306a36Sopenharmony_ci broadcast = reg & BIT_ULL(ICC_SGI1R_IRQ_ROUTING_MODE_BIT); 108962306a36Sopenharmony_ci target_cpus = (reg & ICC_SGI1R_TARGET_LIST_MASK) >> ICC_SGI1R_TARGET_LIST_SHIFT; 109062306a36Sopenharmony_ci mpidr = SGI_AFFINITY_LEVEL(reg, 3); 109162306a36Sopenharmony_ci mpidr |= SGI_AFFINITY_LEVEL(reg, 2); 109262306a36Sopenharmony_ci mpidr |= SGI_AFFINITY_LEVEL(reg, 1); 109362306a36Sopenharmony_ci 109462306a36Sopenharmony_ci /* 109562306a36Sopenharmony_ci * We iterate over all VCPUs to find the MPIDRs matching the request. 109662306a36Sopenharmony_ci * If we have handled one CPU, we clear its bit to detect early 109762306a36Sopenharmony_ci * if we are already finished. This avoids iterating through all 109862306a36Sopenharmony_ci * VCPUs when most of the times we just signal a single VCPU. 109962306a36Sopenharmony_ci */ 110062306a36Sopenharmony_ci kvm_for_each_vcpu(c, c_vcpu, kvm) { 110162306a36Sopenharmony_ci struct vgic_irq *irq; 110262306a36Sopenharmony_ci 110362306a36Sopenharmony_ci /* Exit early if we have dealt with all requested CPUs */ 110462306a36Sopenharmony_ci if (!broadcast && target_cpus == 0) 110562306a36Sopenharmony_ci break; 110662306a36Sopenharmony_ci 110762306a36Sopenharmony_ci /* Don't signal the calling VCPU */ 110862306a36Sopenharmony_ci if (broadcast && c == vcpu_id) 110962306a36Sopenharmony_ci continue; 111062306a36Sopenharmony_ci 111162306a36Sopenharmony_ci if (!broadcast) { 111262306a36Sopenharmony_ci int level0; 111362306a36Sopenharmony_ci 111462306a36Sopenharmony_ci level0 = match_mpidr(mpidr, target_cpus, c_vcpu); 111562306a36Sopenharmony_ci if (level0 == -1) 111662306a36Sopenharmony_ci continue; 111762306a36Sopenharmony_ci 111862306a36Sopenharmony_ci /* remove this matching VCPU from the mask */ 111962306a36Sopenharmony_ci target_cpus &= ~BIT(level0); 112062306a36Sopenharmony_ci } 112162306a36Sopenharmony_ci 112262306a36Sopenharmony_ci irq = vgic_get_irq(vcpu->kvm, c_vcpu, sgi); 112362306a36Sopenharmony_ci 112462306a36Sopenharmony_ci raw_spin_lock_irqsave(&irq->irq_lock, flags); 112562306a36Sopenharmony_ci 112662306a36Sopenharmony_ci /* 112762306a36Sopenharmony_ci * An access targeting Group0 SGIs can only generate 112862306a36Sopenharmony_ci * those, while an access targeting Group1 SGIs can 112962306a36Sopenharmony_ci * generate interrupts of either group. 113062306a36Sopenharmony_ci */ 113162306a36Sopenharmony_ci if (!irq->group || allow_group1) { 113262306a36Sopenharmony_ci if (!irq->hw) { 113362306a36Sopenharmony_ci irq->pending_latch = true; 113462306a36Sopenharmony_ci vgic_queue_irq_unlock(vcpu->kvm, irq, flags); 113562306a36Sopenharmony_ci } else { 113662306a36Sopenharmony_ci /* HW SGI? Ask the GIC to inject it */ 113762306a36Sopenharmony_ci int err; 113862306a36Sopenharmony_ci err = irq_set_irqchip_state(irq->host_irq, 113962306a36Sopenharmony_ci IRQCHIP_STATE_PENDING, 114062306a36Sopenharmony_ci true); 114162306a36Sopenharmony_ci WARN_RATELIMIT(err, "IRQ %d", irq->host_irq); 114262306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&irq->irq_lock, flags); 114362306a36Sopenharmony_ci } 114462306a36Sopenharmony_ci } else { 114562306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&irq->irq_lock, flags); 114662306a36Sopenharmony_ci } 114762306a36Sopenharmony_ci 114862306a36Sopenharmony_ci vgic_put_irq(vcpu->kvm, irq); 114962306a36Sopenharmony_ci } 115062306a36Sopenharmony_ci} 115162306a36Sopenharmony_ci 115262306a36Sopenharmony_ciint vgic_v3_dist_uaccess(struct kvm_vcpu *vcpu, bool is_write, 115362306a36Sopenharmony_ci int offset, u32 *val) 115462306a36Sopenharmony_ci{ 115562306a36Sopenharmony_ci struct vgic_io_device dev = { 115662306a36Sopenharmony_ci .regions = vgic_v3_dist_registers, 115762306a36Sopenharmony_ci .nr_regions = ARRAY_SIZE(vgic_v3_dist_registers), 115862306a36Sopenharmony_ci }; 115962306a36Sopenharmony_ci 116062306a36Sopenharmony_ci return vgic_uaccess(vcpu, &dev, is_write, offset, val); 116162306a36Sopenharmony_ci} 116262306a36Sopenharmony_ci 116362306a36Sopenharmony_ciint vgic_v3_redist_uaccess(struct kvm_vcpu *vcpu, bool is_write, 116462306a36Sopenharmony_ci int offset, u32 *val) 116562306a36Sopenharmony_ci{ 116662306a36Sopenharmony_ci struct vgic_io_device rd_dev = { 116762306a36Sopenharmony_ci .regions = vgic_v3_rd_registers, 116862306a36Sopenharmony_ci .nr_regions = ARRAY_SIZE(vgic_v3_rd_registers), 116962306a36Sopenharmony_ci }; 117062306a36Sopenharmony_ci 117162306a36Sopenharmony_ci return vgic_uaccess(vcpu, &rd_dev, is_write, offset, val); 117262306a36Sopenharmony_ci} 117362306a36Sopenharmony_ci 117462306a36Sopenharmony_ciint vgic_v3_line_level_info_uaccess(struct kvm_vcpu *vcpu, bool is_write, 117562306a36Sopenharmony_ci u32 intid, u32 *val) 117662306a36Sopenharmony_ci{ 117762306a36Sopenharmony_ci if (intid % 32) 117862306a36Sopenharmony_ci return -EINVAL; 117962306a36Sopenharmony_ci 118062306a36Sopenharmony_ci if (is_write) 118162306a36Sopenharmony_ci vgic_write_irq_line_level_info(vcpu, intid, *val); 118262306a36Sopenharmony_ci else 118362306a36Sopenharmony_ci *val = vgic_read_irq_line_level_info(vcpu, intid); 118462306a36Sopenharmony_ci 118562306a36Sopenharmony_ci return 0; 118662306a36Sopenharmony_ci} 1187