162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * VGICv2 MMIO handling functions
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <linux/irqchip/arm-gic.h>
762306a36Sopenharmony_ci#include <linux/kvm.h>
862306a36Sopenharmony_ci#include <linux/kvm_host.h>
962306a36Sopenharmony_ci#include <linux/nospec.h>
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <kvm/iodev.h>
1262306a36Sopenharmony_ci#include <kvm/arm_vgic.h>
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include "vgic.h"
1562306a36Sopenharmony_ci#include "vgic-mmio.h"
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci/*
1862306a36Sopenharmony_ci * The Revision field in the IIDR have the following meanings:
1962306a36Sopenharmony_ci *
2062306a36Sopenharmony_ci * Revision 1: Report GICv2 interrupts as group 0 instead of group 1
2162306a36Sopenharmony_ci * Revision 2: Interrupt groups are guest-configurable and signaled using
2262306a36Sopenharmony_ci * 	       their configured groups.
2362306a36Sopenharmony_ci */
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_cistatic unsigned long vgic_mmio_read_v2_misc(struct kvm_vcpu *vcpu,
2662306a36Sopenharmony_ci					    gpa_t addr, unsigned int len)
2762306a36Sopenharmony_ci{
2862306a36Sopenharmony_ci	struct vgic_dist *vgic = &vcpu->kvm->arch.vgic;
2962306a36Sopenharmony_ci	u32 value;
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci	switch (addr & 0x0c) {
3262306a36Sopenharmony_ci	case GIC_DIST_CTRL:
3362306a36Sopenharmony_ci		value = vgic->enabled ? GICD_ENABLE : 0;
3462306a36Sopenharmony_ci		break;
3562306a36Sopenharmony_ci	case GIC_DIST_CTR:
3662306a36Sopenharmony_ci		value = vgic->nr_spis + VGIC_NR_PRIVATE_IRQS;
3762306a36Sopenharmony_ci		value = (value >> 5) - 1;
3862306a36Sopenharmony_ci		value |= (atomic_read(&vcpu->kvm->online_vcpus) - 1) << 5;
3962306a36Sopenharmony_ci		break;
4062306a36Sopenharmony_ci	case GIC_DIST_IIDR:
4162306a36Sopenharmony_ci		value = (PRODUCT_ID_KVM << GICD_IIDR_PRODUCT_ID_SHIFT) |
4262306a36Sopenharmony_ci			(vgic->implementation_rev << GICD_IIDR_REVISION_SHIFT) |
4362306a36Sopenharmony_ci			(IMPLEMENTER_ARM << GICD_IIDR_IMPLEMENTER_SHIFT);
4462306a36Sopenharmony_ci		break;
4562306a36Sopenharmony_ci	default:
4662306a36Sopenharmony_ci		return 0;
4762306a36Sopenharmony_ci	}
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci	return value;
5062306a36Sopenharmony_ci}
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_cistatic void vgic_mmio_write_v2_misc(struct kvm_vcpu *vcpu,
5362306a36Sopenharmony_ci				    gpa_t addr, unsigned int len,
5462306a36Sopenharmony_ci				    unsigned long val)
5562306a36Sopenharmony_ci{
5662306a36Sopenharmony_ci	struct vgic_dist *dist = &vcpu->kvm->arch.vgic;
5762306a36Sopenharmony_ci	bool was_enabled = dist->enabled;
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	switch (addr & 0x0c) {
6062306a36Sopenharmony_ci	case GIC_DIST_CTRL:
6162306a36Sopenharmony_ci		dist->enabled = val & GICD_ENABLE;
6262306a36Sopenharmony_ci		if (!was_enabled && dist->enabled)
6362306a36Sopenharmony_ci			vgic_kick_vcpus(vcpu->kvm);
6462306a36Sopenharmony_ci		break;
6562306a36Sopenharmony_ci	case GIC_DIST_CTR:
6662306a36Sopenharmony_ci	case GIC_DIST_IIDR:
6762306a36Sopenharmony_ci		/* Nothing to do */
6862306a36Sopenharmony_ci		return;
6962306a36Sopenharmony_ci	}
7062306a36Sopenharmony_ci}
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_cistatic int vgic_mmio_uaccess_write_v2_misc(struct kvm_vcpu *vcpu,
7362306a36Sopenharmony_ci					   gpa_t addr, unsigned int len,
7462306a36Sopenharmony_ci					   unsigned long val)
7562306a36Sopenharmony_ci{
7662306a36Sopenharmony_ci	struct vgic_dist *dist = &vcpu->kvm->arch.vgic;
7762306a36Sopenharmony_ci	u32 reg;
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	switch (addr & 0x0c) {
8062306a36Sopenharmony_ci	case GIC_DIST_IIDR:
8162306a36Sopenharmony_ci		reg = vgic_mmio_read_v2_misc(vcpu, addr, len);
8262306a36Sopenharmony_ci		if ((reg ^ val) & ~GICD_IIDR_REVISION_MASK)
8362306a36Sopenharmony_ci			return -EINVAL;
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci		/*
8662306a36Sopenharmony_ci		 * If we observe a write to GICD_IIDR we know that userspace
8762306a36Sopenharmony_ci		 * has been updated and has had a chance to cope with older
8862306a36Sopenharmony_ci		 * kernels (VGICv2 IIDR.Revision == 0) incorrectly reporting
8962306a36Sopenharmony_ci		 * interrupts as group 1, and therefore we now allow groups to
9062306a36Sopenharmony_ci		 * be user writable.  Doing this by default would break
9162306a36Sopenharmony_ci		 * migration from old kernels to new kernels with legacy
9262306a36Sopenharmony_ci		 * userspace.
9362306a36Sopenharmony_ci		 */
9462306a36Sopenharmony_ci		reg = FIELD_GET(GICD_IIDR_REVISION_MASK, reg);
9562306a36Sopenharmony_ci		switch (reg) {
9662306a36Sopenharmony_ci		case KVM_VGIC_IMP_REV_2:
9762306a36Sopenharmony_ci		case KVM_VGIC_IMP_REV_3:
9862306a36Sopenharmony_ci			vcpu->kvm->arch.vgic.v2_groups_user_writable = true;
9962306a36Sopenharmony_ci			dist->implementation_rev = reg;
10062306a36Sopenharmony_ci			return 0;
10162306a36Sopenharmony_ci		default:
10262306a36Sopenharmony_ci			return -EINVAL;
10362306a36Sopenharmony_ci		}
10462306a36Sopenharmony_ci	}
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	vgic_mmio_write_v2_misc(vcpu, addr, len, val);
10762306a36Sopenharmony_ci	return 0;
10862306a36Sopenharmony_ci}
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_cistatic int vgic_mmio_uaccess_write_v2_group(struct kvm_vcpu *vcpu,
11162306a36Sopenharmony_ci					    gpa_t addr, unsigned int len,
11262306a36Sopenharmony_ci					    unsigned long val)
11362306a36Sopenharmony_ci{
11462306a36Sopenharmony_ci	if (vcpu->kvm->arch.vgic.v2_groups_user_writable)
11562306a36Sopenharmony_ci		vgic_mmio_write_group(vcpu, addr, len, val);
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	return 0;
11862306a36Sopenharmony_ci}
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_cistatic void vgic_mmio_write_sgir(struct kvm_vcpu *source_vcpu,
12162306a36Sopenharmony_ci				 gpa_t addr, unsigned int len,
12262306a36Sopenharmony_ci				 unsigned long val)
12362306a36Sopenharmony_ci{
12462306a36Sopenharmony_ci	int nr_vcpus = atomic_read(&source_vcpu->kvm->online_vcpus);
12562306a36Sopenharmony_ci	int intid = val & 0xf;
12662306a36Sopenharmony_ci	int targets = (val >> 16) & 0xff;
12762306a36Sopenharmony_ci	int mode = (val >> 24) & 0x03;
12862306a36Sopenharmony_ci	struct kvm_vcpu *vcpu;
12962306a36Sopenharmony_ci	unsigned long flags, c;
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	switch (mode) {
13262306a36Sopenharmony_ci	case 0x0:		/* as specified by targets */
13362306a36Sopenharmony_ci		break;
13462306a36Sopenharmony_ci	case 0x1:
13562306a36Sopenharmony_ci		targets = (1U << nr_vcpus) - 1;			/* all, ... */
13662306a36Sopenharmony_ci		targets &= ~(1U << source_vcpu->vcpu_id);	/* but self */
13762306a36Sopenharmony_ci		break;
13862306a36Sopenharmony_ci	case 0x2:		/* this very vCPU only */
13962306a36Sopenharmony_ci		targets = (1U << source_vcpu->vcpu_id);
14062306a36Sopenharmony_ci		break;
14162306a36Sopenharmony_ci	case 0x3:		/* reserved */
14262306a36Sopenharmony_ci		return;
14362306a36Sopenharmony_ci	}
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	kvm_for_each_vcpu(c, vcpu, source_vcpu->kvm) {
14662306a36Sopenharmony_ci		struct vgic_irq *irq;
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci		if (!(targets & (1U << c)))
14962306a36Sopenharmony_ci			continue;
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci		irq = vgic_get_irq(source_vcpu->kvm, vcpu, intid);
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci		raw_spin_lock_irqsave(&irq->irq_lock, flags);
15462306a36Sopenharmony_ci		irq->pending_latch = true;
15562306a36Sopenharmony_ci		irq->source |= 1U << source_vcpu->vcpu_id;
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci		vgic_queue_irq_unlock(source_vcpu->kvm, irq, flags);
15862306a36Sopenharmony_ci		vgic_put_irq(source_vcpu->kvm, irq);
15962306a36Sopenharmony_ci	}
16062306a36Sopenharmony_ci}
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_cistatic unsigned long vgic_mmio_read_target(struct kvm_vcpu *vcpu,
16362306a36Sopenharmony_ci					   gpa_t addr, unsigned int len)
16462306a36Sopenharmony_ci{
16562306a36Sopenharmony_ci	u32 intid = VGIC_ADDR_TO_INTID(addr, 8);
16662306a36Sopenharmony_ci	int i;
16762306a36Sopenharmony_ci	u64 val = 0;
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	for (i = 0; i < len; i++) {
17062306a36Sopenharmony_ci		struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i);
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci		val |= (u64)irq->targets << (i * 8);
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci		vgic_put_irq(vcpu->kvm, irq);
17562306a36Sopenharmony_ci	}
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	return val;
17862306a36Sopenharmony_ci}
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_cistatic void vgic_mmio_write_target(struct kvm_vcpu *vcpu,
18162306a36Sopenharmony_ci				   gpa_t addr, unsigned int len,
18262306a36Sopenharmony_ci				   unsigned long val)
18362306a36Sopenharmony_ci{
18462306a36Sopenharmony_ci	u32 intid = VGIC_ADDR_TO_INTID(addr, 8);
18562306a36Sopenharmony_ci	u8 cpu_mask = GENMASK(atomic_read(&vcpu->kvm->online_vcpus) - 1, 0);
18662306a36Sopenharmony_ci	int i;
18762306a36Sopenharmony_ci	unsigned long flags;
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	/* GICD_ITARGETSR[0-7] are read-only */
19062306a36Sopenharmony_ci	if (intid < VGIC_NR_PRIVATE_IRQS)
19162306a36Sopenharmony_ci		return;
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	for (i = 0; i < len; i++) {
19462306a36Sopenharmony_ci		struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, NULL, intid + i);
19562306a36Sopenharmony_ci		int target;
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci		raw_spin_lock_irqsave(&irq->irq_lock, flags);
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci		irq->targets = (val >> (i * 8)) & cpu_mask;
20062306a36Sopenharmony_ci		target = irq->targets ? __ffs(irq->targets) : 0;
20162306a36Sopenharmony_ci		irq->target_vcpu = kvm_get_vcpu(vcpu->kvm, target);
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci		raw_spin_unlock_irqrestore(&irq->irq_lock, flags);
20462306a36Sopenharmony_ci		vgic_put_irq(vcpu->kvm, irq);
20562306a36Sopenharmony_ci	}
20662306a36Sopenharmony_ci}
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_cistatic unsigned long vgic_mmio_read_sgipend(struct kvm_vcpu *vcpu,
20962306a36Sopenharmony_ci					    gpa_t addr, unsigned int len)
21062306a36Sopenharmony_ci{
21162306a36Sopenharmony_ci	u32 intid = addr & 0x0f;
21262306a36Sopenharmony_ci	int i;
21362306a36Sopenharmony_ci	u64 val = 0;
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	for (i = 0; i < len; i++) {
21662306a36Sopenharmony_ci		struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i);
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci		val |= (u64)irq->source << (i * 8);
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci		vgic_put_irq(vcpu->kvm, irq);
22162306a36Sopenharmony_ci	}
22262306a36Sopenharmony_ci	return val;
22362306a36Sopenharmony_ci}
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_cistatic void vgic_mmio_write_sgipendc(struct kvm_vcpu *vcpu,
22662306a36Sopenharmony_ci				     gpa_t addr, unsigned int len,
22762306a36Sopenharmony_ci				     unsigned long val)
22862306a36Sopenharmony_ci{
22962306a36Sopenharmony_ci	u32 intid = addr & 0x0f;
23062306a36Sopenharmony_ci	int i;
23162306a36Sopenharmony_ci	unsigned long flags;
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	for (i = 0; i < len; i++) {
23462306a36Sopenharmony_ci		struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i);
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci		raw_spin_lock_irqsave(&irq->irq_lock, flags);
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci		irq->source &= ~((val >> (i * 8)) & 0xff);
23962306a36Sopenharmony_ci		if (!irq->source)
24062306a36Sopenharmony_ci			irq->pending_latch = false;
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci		raw_spin_unlock_irqrestore(&irq->irq_lock, flags);
24362306a36Sopenharmony_ci		vgic_put_irq(vcpu->kvm, irq);
24462306a36Sopenharmony_ci	}
24562306a36Sopenharmony_ci}
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_cistatic void vgic_mmio_write_sgipends(struct kvm_vcpu *vcpu,
24862306a36Sopenharmony_ci				     gpa_t addr, unsigned int len,
24962306a36Sopenharmony_ci				     unsigned long val)
25062306a36Sopenharmony_ci{
25162306a36Sopenharmony_ci	u32 intid = addr & 0x0f;
25262306a36Sopenharmony_ci	int i;
25362306a36Sopenharmony_ci	unsigned long flags;
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci	for (i = 0; i < len; i++) {
25662306a36Sopenharmony_ci		struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i);
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci		raw_spin_lock_irqsave(&irq->irq_lock, flags);
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci		irq->source |= (val >> (i * 8)) & 0xff;
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci		if (irq->source) {
26362306a36Sopenharmony_ci			irq->pending_latch = true;
26462306a36Sopenharmony_ci			vgic_queue_irq_unlock(vcpu->kvm, irq, flags);
26562306a36Sopenharmony_ci		} else {
26662306a36Sopenharmony_ci			raw_spin_unlock_irqrestore(&irq->irq_lock, flags);
26762306a36Sopenharmony_ci		}
26862306a36Sopenharmony_ci		vgic_put_irq(vcpu->kvm, irq);
26962306a36Sopenharmony_ci	}
27062306a36Sopenharmony_ci}
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci#define GICC_ARCH_VERSION_V2	0x2
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci/* These are for userland accesses only, there is no guest-facing emulation. */
27562306a36Sopenharmony_cistatic unsigned long vgic_mmio_read_vcpuif(struct kvm_vcpu *vcpu,
27662306a36Sopenharmony_ci					   gpa_t addr, unsigned int len)
27762306a36Sopenharmony_ci{
27862306a36Sopenharmony_ci	struct vgic_vmcr vmcr;
27962306a36Sopenharmony_ci	u32 val;
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	vgic_get_vmcr(vcpu, &vmcr);
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci	switch (addr & 0xff) {
28462306a36Sopenharmony_ci	case GIC_CPU_CTRL:
28562306a36Sopenharmony_ci		val = vmcr.grpen0 << GIC_CPU_CTRL_EnableGrp0_SHIFT;
28662306a36Sopenharmony_ci		val |= vmcr.grpen1 << GIC_CPU_CTRL_EnableGrp1_SHIFT;
28762306a36Sopenharmony_ci		val |= vmcr.ackctl << GIC_CPU_CTRL_AckCtl_SHIFT;
28862306a36Sopenharmony_ci		val |= vmcr.fiqen << GIC_CPU_CTRL_FIQEn_SHIFT;
28962306a36Sopenharmony_ci		val |= vmcr.cbpr << GIC_CPU_CTRL_CBPR_SHIFT;
29062306a36Sopenharmony_ci		val |= vmcr.eoim << GIC_CPU_CTRL_EOImodeNS_SHIFT;
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci		break;
29362306a36Sopenharmony_ci	case GIC_CPU_PRIMASK:
29462306a36Sopenharmony_ci		/*
29562306a36Sopenharmony_ci		 * Our KVM_DEV_TYPE_ARM_VGIC_V2 device ABI exports the
29662306a36Sopenharmony_ci		 * PMR field as GICH_VMCR.VMPriMask rather than
29762306a36Sopenharmony_ci		 * GICC_PMR.Priority, so we expose the upper five bits of
29862306a36Sopenharmony_ci		 * priority mask to userspace using the lower bits in the
29962306a36Sopenharmony_ci		 * unsigned long.
30062306a36Sopenharmony_ci		 */
30162306a36Sopenharmony_ci		val = (vmcr.pmr & GICV_PMR_PRIORITY_MASK) >>
30262306a36Sopenharmony_ci			GICV_PMR_PRIORITY_SHIFT;
30362306a36Sopenharmony_ci		break;
30462306a36Sopenharmony_ci	case GIC_CPU_BINPOINT:
30562306a36Sopenharmony_ci		val = vmcr.bpr;
30662306a36Sopenharmony_ci		break;
30762306a36Sopenharmony_ci	case GIC_CPU_ALIAS_BINPOINT:
30862306a36Sopenharmony_ci		val = vmcr.abpr;
30962306a36Sopenharmony_ci		break;
31062306a36Sopenharmony_ci	case GIC_CPU_IDENT:
31162306a36Sopenharmony_ci		val = ((PRODUCT_ID_KVM << 20) |
31262306a36Sopenharmony_ci		       (GICC_ARCH_VERSION_V2 << 16) |
31362306a36Sopenharmony_ci		       IMPLEMENTER_ARM);
31462306a36Sopenharmony_ci		break;
31562306a36Sopenharmony_ci	default:
31662306a36Sopenharmony_ci		return 0;
31762306a36Sopenharmony_ci	}
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci	return val;
32062306a36Sopenharmony_ci}
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_cistatic void vgic_mmio_write_vcpuif(struct kvm_vcpu *vcpu,
32362306a36Sopenharmony_ci				   gpa_t addr, unsigned int len,
32462306a36Sopenharmony_ci				   unsigned long val)
32562306a36Sopenharmony_ci{
32662306a36Sopenharmony_ci	struct vgic_vmcr vmcr;
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	vgic_get_vmcr(vcpu, &vmcr);
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	switch (addr & 0xff) {
33162306a36Sopenharmony_ci	case GIC_CPU_CTRL:
33262306a36Sopenharmony_ci		vmcr.grpen0 = !!(val & GIC_CPU_CTRL_EnableGrp0);
33362306a36Sopenharmony_ci		vmcr.grpen1 = !!(val & GIC_CPU_CTRL_EnableGrp1);
33462306a36Sopenharmony_ci		vmcr.ackctl = !!(val & GIC_CPU_CTRL_AckCtl);
33562306a36Sopenharmony_ci		vmcr.fiqen = !!(val & GIC_CPU_CTRL_FIQEn);
33662306a36Sopenharmony_ci		vmcr.cbpr = !!(val & GIC_CPU_CTRL_CBPR);
33762306a36Sopenharmony_ci		vmcr.eoim = !!(val & GIC_CPU_CTRL_EOImodeNS);
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci		break;
34062306a36Sopenharmony_ci	case GIC_CPU_PRIMASK:
34162306a36Sopenharmony_ci		/*
34262306a36Sopenharmony_ci		 * Our KVM_DEV_TYPE_ARM_VGIC_V2 device ABI exports the
34362306a36Sopenharmony_ci		 * PMR field as GICH_VMCR.VMPriMask rather than
34462306a36Sopenharmony_ci		 * GICC_PMR.Priority, so we expose the upper five bits of
34562306a36Sopenharmony_ci		 * priority mask to userspace using the lower bits in the
34662306a36Sopenharmony_ci		 * unsigned long.
34762306a36Sopenharmony_ci		 */
34862306a36Sopenharmony_ci		vmcr.pmr = (val << GICV_PMR_PRIORITY_SHIFT) &
34962306a36Sopenharmony_ci			GICV_PMR_PRIORITY_MASK;
35062306a36Sopenharmony_ci		break;
35162306a36Sopenharmony_ci	case GIC_CPU_BINPOINT:
35262306a36Sopenharmony_ci		vmcr.bpr = val;
35362306a36Sopenharmony_ci		break;
35462306a36Sopenharmony_ci	case GIC_CPU_ALIAS_BINPOINT:
35562306a36Sopenharmony_ci		vmcr.abpr = val;
35662306a36Sopenharmony_ci		break;
35762306a36Sopenharmony_ci	}
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	vgic_set_vmcr(vcpu, &vmcr);
36062306a36Sopenharmony_ci}
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_cistatic unsigned long vgic_mmio_read_apr(struct kvm_vcpu *vcpu,
36362306a36Sopenharmony_ci					gpa_t addr, unsigned int len)
36462306a36Sopenharmony_ci{
36562306a36Sopenharmony_ci	int n; /* which APRn is this */
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci	n = (addr >> 2) & 0x3;
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci	if (kvm_vgic_global_state.type == VGIC_V2) {
37062306a36Sopenharmony_ci		/* GICv2 hardware systems support max. 32 groups */
37162306a36Sopenharmony_ci		if (n != 0)
37262306a36Sopenharmony_ci			return 0;
37362306a36Sopenharmony_ci		return vcpu->arch.vgic_cpu.vgic_v2.vgic_apr;
37462306a36Sopenharmony_ci	} else {
37562306a36Sopenharmony_ci		struct vgic_v3_cpu_if *vgicv3 = &vcpu->arch.vgic_cpu.vgic_v3;
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci		if (n > vgic_v3_max_apr_idx(vcpu))
37862306a36Sopenharmony_ci			return 0;
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci		n = array_index_nospec(n, 4);
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci		/* GICv3 only uses ICH_AP1Rn for memory mapped (GICv2) guests */
38362306a36Sopenharmony_ci		return vgicv3->vgic_ap1r[n];
38462306a36Sopenharmony_ci	}
38562306a36Sopenharmony_ci}
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_cistatic void vgic_mmio_write_apr(struct kvm_vcpu *vcpu,
38862306a36Sopenharmony_ci				gpa_t addr, unsigned int len,
38962306a36Sopenharmony_ci				unsigned long val)
39062306a36Sopenharmony_ci{
39162306a36Sopenharmony_ci	int n; /* which APRn is this */
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	n = (addr >> 2) & 0x3;
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci	if (kvm_vgic_global_state.type == VGIC_V2) {
39662306a36Sopenharmony_ci		/* GICv2 hardware systems support max. 32 groups */
39762306a36Sopenharmony_ci		if (n != 0)
39862306a36Sopenharmony_ci			return;
39962306a36Sopenharmony_ci		vcpu->arch.vgic_cpu.vgic_v2.vgic_apr = val;
40062306a36Sopenharmony_ci	} else {
40162306a36Sopenharmony_ci		struct vgic_v3_cpu_if *vgicv3 = &vcpu->arch.vgic_cpu.vgic_v3;
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci		if (n > vgic_v3_max_apr_idx(vcpu))
40462306a36Sopenharmony_ci			return;
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci		n = array_index_nospec(n, 4);
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci		/* GICv3 only uses ICH_AP1Rn for memory mapped (GICv2) guests */
40962306a36Sopenharmony_ci		vgicv3->vgic_ap1r[n] = val;
41062306a36Sopenharmony_ci	}
41162306a36Sopenharmony_ci}
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_cistatic const struct vgic_register_region vgic_v2_dist_registers[] = {
41462306a36Sopenharmony_ci	REGISTER_DESC_WITH_LENGTH_UACCESS(GIC_DIST_CTRL,
41562306a36Sopenharmony_ci		vgic_mmio_read_v2_misc, vgic_mmio_write_v2_misc,
41662306a36Sopenharmony_ci		NULL, vgic_mmio_uaccess_write_v2_misc,
41762306a36Sopenharmony_ci		12, VGIC_ACCESS_32bit),
41862306a36Sopenharmony_ci	REGISTER_DESC_WITH_BITS_PER_IRQ(GIC_DIST_IGROUP,
41962306a36Sopenharmony_ci		vgic_mmio_read_group, vgic_mmio_write_group,
42062306a36Sopenharmony_ci		NULL, vgic_mmio_uaccess_write_v2_group, 1,
42162306a36Sopenharmony_ci		VGIC_ACCESS_32bit),
42262306a36Sopenharmony_ci	REGISTER_DESC_WITH_BITS_PER_IRQ(GIC_DIST_ENABLE_SET,
42362306a36Sopenharmony_ci		vgic_mmio_read_enable, vgic_mmio_write_senable,
42462306a36Sopenharmony_ci		NULL, vgic_uaccess_write_senable, 1,
42562306a36Sopenharmony_ci		VGIC_ACCESS_32bit),
42662306a36Sopenharmony_ci	REGISTER_DESC_WITH_BITS_PER_IRQ(GIC_DIST_ENABLE_CLEAR,
42762306a36Sopenharmony_ci		vgic_mmio_read_enable, vgic_mmio_write_cenable,
42862306a36Sopenharmony_ci		NULL, vgic_uaccess_write_cenable, 1,
42962306a36Sopenharmony_ci		VGIC_ACCESS_32bit),
43062306a36Sopenharmony_ci	REGISTER_DESC_WITH_BITS_PER_IRQ(GIC_DIST_PENDING_SET,
43162306a36Sopenharmony_ci		vgic_mmio_read_pending, vgic_mmio_write_spending,
43262306a36Sopenharmony_ci		vgic_uaccess_read_pending, vgic_uaccess_write_spending, 1,
43362306a36Sopenharmony_ci		VGIC_ACCESS_32bit),
43462306a36Sopenharmony_ci	REGISTER_DESC_WITH_BITS_PER_IRQ(GIC_DIST_PENDING_CLEAR,
43562306a36Sopenharmony_ci		vgic_mmio_read_pending, vgic_mmio_write_cpending,
43662306a36Sopenharmony_ci		vgic_uaccess_read_pending, vgic_uaccess_write_cpending, 1,
43762306a36Sopenharmony_ci		VGIC_ACCESS_32bit),
43862306a36Sopenharmony_ci	REGISTER_DESC_WITH_BITS_PER_IRQ(GIC_DIST_ACTIVE_SET,
43962306a36Sopenharmony_ci		vgic_mmio_read_active, vgic_mmio_write_sactive,
44062306a36Sopenharmony_ci		vgic_uaccess_read_active, vgic_mmio_uaccess_write_sactive, 1,
44162306a36Sopenharmony_ci		VGIC_ACCESS_32bit),
44262306a36Sopenharmony_ci	REGISTER_DESC_WITH_BITS_PER_IRQ(GIC_DIST_ACTIVE_CLEAR,
44362306a36Sopenharmony_ci		vgic_mmio_read_active, vgic_mmio_write_cactive,
44462306a36Sopenharmony_ci		vgic_uaccess_read_active, vgic_mmio_uaccess_write_cactive, 1,
44562306a36Sopenharmony_ci		VGIC_ACCESS_32bit),
44662306a36Sopenharmony_ci	REGISTER_DESC_WITH_BITS_PER_IRQ(GIC_DIST_PRI,
44762306a36Sopenharmony_ci		vgic_mmio_read_priority, vgic_mmio_write_priority, NULL, NULL,
44862306a36Sopenharmony_ci		8, VGIC_ACCESS_32bit | VGIC_ACCESS_8bit),
44962306a36Sopenharmony_ci	REGISTER_DESC_WITH_BITS_PER_IRQ(GIC_DIST_TARGET,
45062306a36Sopenharmony_ci		vgic_mmio_read_target, vgic_mmio_write_target, NULL, NULL, 8,
45162306a36Sopenharmony_ci		VGIC_ACCESS_32bit | VGIC_ACCESS_8bit),
45262306a36Sopenharmony_ci	REGISTER_DESC_WITH_BITS_PER_IRQ(GIC_DIST_CONFIG,
45362306a36Sopenharmony_ci		vgic_mmio_read_config, vgic_mmio_write_config, NULL, NULL, 2,
45462306a36Sopenharmony_ci		VGIC_ACCESS_32bit),
45562306a36Sopenharmony_ci	REGISTER_DESC_WITH_LENGTH(GIC_DIST_SOFTINT,
45662306a36Sopenharmony_ci		vgic_mmio_read_raz, vgic_mmio_write_sgir, 4,
45762306a36Sopenharmony_ci		VGIC_ACCESS_32bit),
45862306a36Sopenharmony_ci	REGISTER_DESC_WITH_LENGTH(GIC_DIST_SGI_PENDING_CLEAR,
45962306a36Sopenharmony_ci		vgic_mmio_read_sgipend, vgic_mmio_write_sgipendc, 16,
46062306a36Sopenharmony_ci		VGIC_ACCESS_32bit | VGIC_ACCESS_8bit),
46162306a36Sopenharmony_ci	REGISTER_DESC_WITH_LENGTH(GIC_DIST_SGI_PENDING_SET,
46262306a36Sopenharmony_ci		vgic_mmio_read_sgipend, vgic_mmio_write_sgipends, 16,
46362306a36Sopenharmony_ci		VGIC_ACCESS_32bit | VGIC_ACCESS_8bit),
46462306a36Sopenharmony_ci};
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_cistatic const struct vgic_register_region vgic_v2_cpu_registers[] = {
46762306a36Sopenharmony_ci	REGISTER_DESC_WITH_LENGTH(GIC_CPU_CTRL,
46862306a36Sopenharmony_ci		vgic_mmio_read_vcpuif, vgic_mmio_write_vcpuif, 4,
46962306a36Sopenharmony_ci		VGIC_ACCESS_32bit),
47062306a36Sopenharmony_ci	REGISTER_DESC_WITH_LENGTH(GIC_CPU_PRIMASK,
47162306a36Sopenharmony_ci		vgic_mmio_read_vcpuif, vgic_mmio_write_vcpuif, 4,
47262306a36Sopenharmony_ci		VGIC_ACCESS_32bit),
47362306a36Sopenharmony_ci	REGISTER_DESC_WITH_LENGTH(GIC_CPU_BINPOINT,
47462306a36Sopenharmony_ci		vgic_mmio_read_vcpuif, vgic_mmio_write_vcpuif, 4,
47562306a36Sopenharmony_ci		VGIC_ACCESS_32bit),
47662306a36Sopenharmony_ci	REGISTER_DESC_WITH_LENGTH(GIC_CPU_ALIAS_BINPOINT,
47762306a36Sopenharmony_ci		vgic_mmio_read_vcpuif, vgic_mmio_write_vcpuif, 4,
47862306a36Sopenharmony_ci		VGIC_ACCESS_32bit),
47962306a36Sopenharmony_ci	REGISTER_DESC_WITH_LENGTH(GIC_CPU_ACTIVEPRIO,
48062306a36Sopenharmony_ci		vgic_mmio_read_apr, vgic_mmio_write_apr, 16,
48162306a36Sopenharmony_ci		VGIC_ACCESS_32bit),
48262306a36Sopenharmony_ci	REGISTER_DESC_WITH_LENGTH(GIC_CPU_IDENT,
48362306a36Sopenharmony_ci		vgic_mmio_read_vcpuif, vgic_mmio_write_vcpuif, 4,
48462306a36Sopenharmony_ci		VGIC_ACCESS_32bit),
48562306a36Sopenharmony_ci};
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ciunsigned int vgic_v2_init_dist_iodev(struct vgic_io_device *dev)
48862306a36Sopenharmony_ci{
48962306a36Sopenharmony_ci	dev->regions = vgic_v2_dist_registers;
49062306a36Sopenharmony_ci	dev->nr_regions = ARRAY_SIZE(vgic_v2_dist_registers);
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci	kvm_iodevice_init(&dev->dev, &kvm_io_gic_ops);
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci	return SZ_4K;
49562306a36Sopenharmony_ci}
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ciint vgic_v2_has_attr_regs(struct kvm_device *dev, struct kvm_device_attr *attr)
49862306a36Sopenharmony_ci{
49962306a36Sopenharmony_ci	const struct vgic_register_region *region;
50062306a36Sopenharmony_ci	struct vgic_io_device iodev;
50162306a36Sopenharmony_ci	struct vgic_reg_attr reg_attr;
50262306a36Sopenharmony_ci	struct kvm_vcpu *vcpu;
50362306a36Sopenharmony_ci	gpa_t addr;
50462306a36Sopenharmony_ci	int ret;
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci	ret = vgic_v2_parse_attr(dev, attr, &reg_attr);
50762306a36Sopenharmony_ci	if (ret)
50862306a36Sopenharmony_ci		return ret;
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci	vcpu = reg_attr.vcpu;
51162306a36Sopenharmony_ci	addr = reg_attr.addr;
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci	switch (attr->group) {
51462306a36Sopenharmony_ci	case KVM_DEV_ARM_VGIC_GRP_DIST_REGS:
51562306a36Sopenharmony_ci		iodev.regions = vgic_v2_dist_registers;
51662306a36Sopenharmony_ci		iodev.nr_regions = ARRAY_SIZE(vgic_v2_dist_registers);
51762306a36Sopenharmony_ci		iodev.base_addr = 0;
51862306a36Sopenharmony_ci		break;
51962306a36Sopenharmony_ci	case KVM_DEV_ARM_VGIC_GRP_CPU_REGS:
52062306a36Sopenharmony_ci		iodev.regions = vgic_v2_cpu_registers;
52162306a36Sopenharmony_ci		iodev.nr_regions = ARRAY_SIZE(vgic_v2_cpu_registers);
52262306a36Sopenharmony_ci		iodev.base_addr = 0;
52362306a36Sopenharmony_ci		break;
52462306a36Sopenharmony_ci	default:
52562306a36Sopenharmony_ci		return -ENXIO;
52662306a36Sopenharmony_ci	}
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci	/* We only support aligned 32-bit accesses. */
52962306a36Sopenharmony_ci	if (addr & 3)
53062306a36Sopenharmony_ci		return -ENXIO;
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci	region = vgic_get_mmio_region(vcpu, &iodev, addr, sizeof(u32));
53362306a36Sopenharmony_ci	if (!region)
53462306a36Sopenharmony_ci		return -ENXIO;
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci	return 0;
53762306a36Sopenharmony_ci}
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ciint vgic_v2_cpuif_uaccess(struct kvm_vcpu *vcpu, bool is_write,
54062306a36Sopenharmony_ci			  int offset, u32 *val)
54162306a36Sopenharmony_ci{
54262306a36Sopenharmony_ci	struct vgic_io_device dev = {
54362306a36Sopenharmony_ci		.regions = vgic_v2_cpu_registers,
54462306a36Sopenharmony_ci		.nr_regions = ARRAY_SIZE(vgic_v2_cpu_registers),
54562306a36Sopenharmony_ci		.iodev_type = IODEV_CPUIF,
54662306a36Sopenharmony_ci	};
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci	return vgic_uaccess(vcpu, &dev, is_write, offset, val);
54962306a36Sopenharmony_ci}
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ciint vgic_v2_dist_uaccess(struct kvm_vcpu *vcpu, bool is_write,
55262306a36Sopenharmony_ci			 int offset, u32 *val)
55362306a36Sopenharmony_ci{
55462306a36Sopenharmony_ci	struct vgic_io_device dev = {
55562306a36Sopenharmony_ci		.regions = vgic_v2_dist_registers,
55662306a36Sopenharmony_ci		.nr_regions = ARRAY_SIZE(vgic_v2_dist_registers),
55762306a36Sopenharmony_ci		.iodev_type = IODEV_DIST,
55862306a36Sopenharmony_ci	};
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci	return vgic_uaccess(vcpu, &dev, is_write, offset, val);
56162306a36Sopenharmony_ci}
562