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, &reg_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