18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * VGICv2 MMIO handling functions
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci#include <linux/irqchip/arm-gic.h>
78c2ecf20Sopenharmony_ci#include <linux/kvm.h>
88c2ecf20Sopenharmony_ci#include <linux/kvm_host.h>
98c2ecf20Sopenharmony_ci#include <linux/nospec.h>
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <kvm/iodev.h>
128c2ecf20Sopenharmony_ci#include <kvm/arm_vgic.h>
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#include "vgic.h"
158c2ecf20Sopenharmony_ci#include "vgic-mmio.h"
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci/*
188c2ecf20Sopenharmony_ci * The Revision field in the IIDR have the following meanings:
198c2ecf20Sopenharmony_ci *
208c2ecf20Sopenharmony_ci * Revision 1: Report GICv2 interrupts as group 0 instead of group 1
218c2ecf20Sopenharmony_ci * Revision 2: Interrupt groups are guest-configurable and signaled using
228c2ecf20Sopenharmony_ci * 	       their configured groups.
238c2ecf20Sopenharmony_ci */
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_cistatic unsigned long vgic_mmio_read_v2_misc(struct kvm_vcpu *vcpu,
268c2ecf20Sopenharmony_ci					    gpa_t addr, unsigned int len)
278c2ecf20Sopenharmony_ci{
288c2ecf20Sopenharmony_ci	struct vgic_dist *vgic = &vcpu->kvm->arch.vgic;
298c2ecf20Sopenharmony_ci	u32 value;
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci	switch (addr & 0x0c) {
328c2ecf20Sopenharmony_ci	case GIC_DIST_CTRL:
338c2ecf20Sopenharmony_ci		value = vgic->enabled ? GICD_ENABLE : 0;
348c2ecf20Sopenharmony_ci		break;
358c2ecf20Sopenharmony_ci	case GIC_DIST_CTR:
368c2ecf20Sopenharmony_ci		value = vgic->nr_spis + VGIC_NR_PRIVATE_IRQS;
378c2ecf20Sopenharmony_ci		value = (value >> 5) - 1;
388c2ecf20Sopenharmony_ci		value |= (atomic_read(&vcpu->kvm->online_vcpus) - 1) << 5;
398c2ecf20Sopenharmony_ci		break;
408c2ecf20Sopenharmony_ci	case GIC_DIST_IIDR:
418c2ecf20Sopenharmony_ci		value = (PRODUCT_ID_KVM << GICD_IIDR_PRODUCT_ID_SHIFT) |
428c2ecf20Sopenharmony_ci			(vgic->implementation_rev << GICD_IIDR_REVISION_SHIFT) |
438c2ecf20Sopenharmony_ci			(IMPLEMENTER_ARM << GICD_IIDR_IMPLEMENTER_SHIFT);
448c2ecf20Sopenharmony_ci		break;
458c2ecf20Sopenharmony_ci	default:
468c2ecf20Sopenharmony_ci		return 0;
478c2ecf20Sopenharmony_ci	}
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci	return value;
508c2ecf20Sopenharmony_ci}
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_cistatic void vgic_mmio_write_v2_misc(struct kvm_vcpu *vcpu,
538c2ecf20Sopenharmony_ci				    gpa_t addr, unsigned int len,
548c2ecf20Sopenharmony_ci				    unsigned long val)
558c2ecf20Sopenharmony_ci{
568c2ecf20Sopenharmony_ci	struct vgic_dist *dist = &vcpu->kvm->arch.vgic;
578c2ecf20Sopenharmony_ci	bool was_enabled = dist->enabled;
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci	switch (addr & 0x0c) {
608c2ecf20Sopenharmony_ci	case GIC_DIST_CTRL:
618c2ecf20Sopenharmony_ci		dist->enabled = val & GICD_ENABLE;
628c2ecf20Sopenharmony_ci		if (!was_enabled && dist->enabled)
638c2ecf20Sopenharmony_ci			vgic_kick_vcpus(vcpu->kvm);
648c2ecf20Sopenharmony_ci		break;
658c2ecf20Sopenharmony_ci	case GIC_DIST_CTR:
668c2ecf20Sopenharmony_ci	case GIC_DIST_IIDR:
678c2ecf20Sopenharmony_ci		/* Nothing to do */
688c2ecf20Sopenharmony_ci		return;
698c2ecf20Sopenharmony_ci	}
708c2ecf20Sopenharmony_ci}
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_cistatic int vgic_mmio_uaccess_write_v2_misc(struct kvm_vcpu *vcpu,
738c2ecf20Sopenharmony_ci					   gpa_t addr, unsigned int len,
748c2ecf20Sopenharmony_ci					   unsigned long val)
758c2ecf20Sopenharmony_ci{
768c2ecf20Sopenharmony_ci	switch (addr & 0x0c) {
778c2ecf20Sopenharmony_ci	case GIC_DIST_IIDR:
788c2ecf20Sopenharmony_ci		if (val != vgic_mmio_read_v2_misc(vcpu, addr, len))
798c2ecf20Sopenharmony_ci			return -EINVAL;
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci		/*
828c2ecf20Sopenharmony_ci		 * If we observe a write to GICD_IIDR we know that userspace
838c2ecf20Sopenharmony_ci		 * has been updated and has had a chance to cope with older
848c2ecf20Sopenharmony_ci		 * kernels (VGICv2 IIDR.Revision == 0) incorrectly reporting
858c2ecf20Sopenharmony_ci		 * interrupts as group 1, and therefore we now allow groups to
868c2ecf20Sopenharmony_ci		 * be user writable.  Doing this by default would break
878c2ecf20Sopenharmony_ci		 * migration from old kernels to new kernels with legacy
888c2ecf20Sopenharmony_ci		 * userspace.
898c2ecf20Sopenharmony_ci		 */
908c2ecf20Sopenharmony_ci		vcpu->kvm->arch.vgic.v2_groups_user_writable = true;
918c2ecf20Sopenharmony_ci		return 0;
928c2ecf20Sopenharmony_ci	}
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	vgic_mmio_write_v2_misc(vcpu, addr, len, val);
958c2ecf20Sopenharmony_ci	return 0;
968c2ecf20Sopenharmony_ci}
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_cistatic int vgic_mmio_uaccess_write_v2_group(struct kvm_vcpu *vcpu,
998c2ecf20Sopenharmony_ci					    gpa_t addr, unsigned int len,
1008c2ecf20Sopenharmony_ci					    unsigned long val)
1018c2ecf20Sopenharmony_ci{
1028c2ecf20Sopenharmony_ci	if (vcpu->kvm->arch.vgic.v2_groups_user_writable)
1038c2ecf20Sopenharmony_ci		vgic_mmio_write_group(vcpu, addr, len, val);
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	return 0;
1068c2ecf20Sopenharmony_ci}
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_cistatic void vgic_mmio_write_sgir(struct kvm_vcpu *source_vcpu,
1098c2ecf20Sopenharmony_ci				 gpa_t addr, unsigned int len,
1108c2ecf20Sopenharmony_ci				 unsigned long val)
1118c2ecf20Sopenharmony_ci{
1128c2ecf20Sopenharmony_ci	int nr_vcpus = atomic_read(&source_vcpu->kvm->online_vcpus);
1138c2ecf20Sopenharmony_ci	int intid = val & 0xf;
1148c2ecf20Sopenharmony_ci	int targets = (val >> 16) & 0xff;
1158c2ecf20Sopenharmony_ci	int mode = (val >> 24) & 0x03;
1168c2ecf20Sopenharmony_ci	int c;
1178c2ecf20Sopenharmony_ci	struct kvm_vcpu *vcpu;
1188c2ecf20Sopenharmony_ci	unsigned long flags;
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	switch (mode) {
1218c2ecf20Sopenharmony_ci	case 0x0:		/* as specified by targets */
1228c2ecf20Sopenharmony_ci		break;
1238c2ecf20Sopenharmony_ci	case 0x1:
1248c2ecf20Sopenharmony_ci		targets = (1U << nr_vcpus) - 1;			/* all, ... */
1258c2ecf20Sopenharmony_ci		targets &= ~(1U << source_vcpu->vcpu_id);	/* but self */
1268c2ecf20Sopenharmony_ci		break;
1278c2ecf20Sopenharmony_ci	case 0x2:		/* this very vCPU only */
1288c2ecf20Sopenharmony_ci		targets = (1U << source_vcpu->vcpu_id);
1298c2ecf20Sopenharmony_ci		break;
1308c2ecf20Sopenharmony_ci	case 0x3:		/* reserved */
1318c2ecf20Sopenharmony_ci		return;
1328c2ecf20Sopenharmony_ci	}
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	kvm_for_each_vcpu(c, vcpu, source_vcpu->kvm) {
1358c2ecf20Sopenharmony_ci		struct vgic_irq *irq;
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci		if (!(targets & (1U << c)))
1388c2ecf20Sopenharmony_ci			continue;
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci		irq = vgic_get_irq(source_vcpu->kvm, vcpu, intid);
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci		raw_spin_lock_irqsave(&irq->irq_lock, flags);
1438c2ecf20Sopenharmony_ci		irq->pending_latch = true;
1448c2ecf20Sopenharmony_ci		irq->source |= 1U << source_vcpu->vcpu_id;
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci		vgic_queue_irq_unlock(source_vcpu->kvm, irq, flags);
1478c2ecf20Sopenharmony_ci		vgic_put_irq(source_vcpu->kvm, irq);
1488c2ecf20Sopenharmony_ci	}
1498c2ecf20Sopenharmony_ci}
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_cistatic unsigned long vgic_mmio_read_target(struct kvm_vcpu *vcpu,
1528c2ecf20Sopenharmony_ci					   gpa_t addr, unsigned int len)
1538c2ecf20Sopenharmony_ci{
1548c2ecf20Sopenharmony_ci	u32 intid = VGIC_ADDR_TO_INTID(addr, 8);
1558c2ecf20Sopenharmony_ci	int i;
1568c2ecf20Sopenharmony_ci	u64 val = 0;
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	for (i = 0; i < len; i++) {
1598c2ecf20Sopenharmony_ci		struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i);
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci		val |= (u64)irq->targets << (i * 8);
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci		vgic_put_irq(vcpu->kvm, irq);
1648c2ecf20Sopenharmony_ci	}
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	return val;
1678c2ecf20Sopenharmony_ci}
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_cistatic void vgic_mmio_write_target(struct kvm_vcpu *vcpu,
1708c2ecf20Sopenharmony_ci				   gpa_t addr, unsigned int len,
1718c2ecf20Sopenharmony_ci				   unsigned long val)
1728c2ecf20Sopenharmony_ci{
1738c2ecf20Sopenharmony_ci	u32 intid = VGIC_ADDR_TO_INTID(addr, 8);
1748c2ecf20Sopenharmony_ci	u8 cpu_mask = GENMASK(atomic_read(&vcpu->kvm->online_vcpus) - 1, 0);
1758c2ecf20Sopenharmony_ci	int i;
1768c2ecf20Sopenharmony_ci	unsigned long flags;
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	/* GICD_ITARGETSR[0-7] are read-only */
1798c2ecf20Sopenharmony_ci	if (intid < VGIC_NR_PRIVATE_IRQS)
1808c2ecf20Sopenharmony_ci		return;
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	for (i = 0; i < len; i++) {
1838c2ecf20Sopenharmony_ci		struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, NULL, intid + i);
1848c2ecf20Sopenharmony_ci		int target;
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci		raw_spin_lock_irqsave(&irq->irq_lock, flags);
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci		irq->targets = (val >> (i * 8)) & cpu_mask;
1898c2ecf20Sopenharmony_ci		target = irq->targets ? __ffs(irq->targets) : 0;
1908c2ecf20Sopenharmony_ci		irq->target_vcpu = kvm_get_vcpu(vcpu->kvm, target);
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci		raw_spin_unlock_irqrestore(&irq->irq_lock, flags);
1938c2ecf20Sopenharmony_ci		vgic_put_irq(vcpu->kvm, irq);
1948c2ecf20Sopenharmony_ci	}
1958c2ecf20Sopenharmony_ci}
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_cistatic unsigned long vgic_mmio_read_sgipend(struct kvm_vcpu *vcpu,
1988c2ecf20Sopenharmony_ci					    gpa_t addr, unsigned int len)
1998c2ecf20Sopenharmony_ci{
2008c2ecf20Sopenharmony_ci	u32 intid = addr & 0x0f;
2018c2ecf20Sopenharmony_ci	int i;
2028c2ecf20Sopenharmony_ci	u64 val = 0;
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	for (i = 0; i < len; i++) {
2058c2ecf20Sopenharmony_ci		struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i);
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci		val |= (u64)irq->source << (i * 8);
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci		vgic_put_irq(vcpu->kvm, irq);
2108c2ecf20Sopenharmony_ci	}
2118c2ecf20Sopenharmony_ci	return val;
2128c2ecf20Sopenharmony_ci}
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_cistatic void vgic_mmio_write_sgipendc(struct kvm_vcpu *vcpu,
2158c2ecf20Sopenharmony_ci				     gpa_t addr, unsigned int len,
2168c2ecf20Sopenharmony_ci				     unsigned long val)
2178c2ecf20Sopenharmony_ci{
2188c2ecf20Sopenharmony_ci	u32 intid = addr & 0x0f;
2198c2ecf20Sopenharmony_ci	int i;
2208c2ecf20Sopenharmony_ci	unsigned long flags;
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	for (i = 0; i < len; i++) {
2238c2ecf20Sopenharmony_ci		struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i);
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci		raw_spin_lock_irqsave(&irq->irq_lock, flags);
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci		irq->source &= ~((val >> (i * 8)) & 0xff);
2288c2ecf20Sopenharmony_ci		if (!irq->source)
2298c2ecf20Sopenharmony_ci			irq->pending_latch = false;
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci		raw_spin_unlock_irqrestore(&irq->irq_lock, flags);
2328c2ecf20Sopenharmony_ci		vgic_put_irq(vcpu->kvm, irq);
2338c2ecf20Sopenharmony_ci	}
2348c2ecf20Sopenharmony_ci}
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_cistatic void vgic_mmio_write_sgipends(struct kvm_vcpu *vcpu,
2378c2ecf20Sopenharmony_ci				     gpa_t addr, unsigned int len,
2388c2ecf20Sopenharmony_ci				     unsigned long val)
2398c2ecf20Sopenharmony_ci{
2408c2ecf20Sopenharmony_ci	u32 intid = addr & 0x0f;
2418c2ecf20Sopenharmony_ci	int i;
2428c2ecf20Sopenharmony_ci	unsigned long flags;
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	for (i = 0; i < len; i++) {
2458c2ecf20Sopenharmony_ci		struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i);
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci		raw_spin_lock_irqsave(&irq->irq_lock, flags);
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci		irq->source |= (val >> (i * 8)) & 0xff;
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci		if (irq->source) {
2528c2ecf20Sopenharmony_ci			irq->pending_latch = true;
2538c2ecf20Sopenharmony_ci			vgic_queue_irq_unlock(vcpu->kvm, irq, flags);
2548c2ecf20Sopenharmony_ci		} else {
2558c2ecf20Sopenharmony_ci			raw_spin_unlock_irqrestore(&irq->irq_lock, flags);
2568c2ecf20Sopenharmony_ci		}
2578c2ecf20Sopenharmony_ci		vgic_put_irq(vcpu->kvm, irq);
2588c2ecf20Sopenharmony_ci	}
2598c2ecf20Sopenharmony_ci}
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci#define GICC_ARCH_VERSION_V2	0x2
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci/* These are for userland accesses only, there is no guest-facing emulation. */
2648c2ecf20Sopenharmony_cistatic unsigned long vgic_mmio_read_vcpuif(struct kvm_vcpu *vcpu,
2658c2ecf20Sopenharmony_ci					   gpa_t addr, unsigned int len)
2668c2ecf20Sopenharmony_ci{
2678c2ecf20Sopenharmony_ci	struct vgic_vmcr vmcr;
2688c2ecf20Sopenharmony_ci	u32 val;
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci	vgic_get_vmcr(vcpu, &vmcr);
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci	switch (addr & 0xff) {
2738c2ecf20Sopenharmony_ci	case GIC_CPU_CTRL:
2748c2ecf20Sopenharmony_ci		val = vmcr.grpen0 << GIC_CPU_CTRL_EnableGrp0_SHIFT;
2758c2ecf20Sopenharmony_ci		val |= vmcr.grpen1 << GIC_CPU_CTRL_EnableGrp1_SHIFT;
2768c2ecf20Sopenharmony_ci		val |= vmcr.ackctl << GIC_CPU_CTRL_AckCtl_SHIFT;
2778c2ecf20Sopenharmony_ci		val |= vmcr.fiqen << GIC_CPU_CTRL_FIQEn_SHIFT;
2788c2ecf20Sopenharmony_ci		val |= vmcr.cbpr << GIC_CPU_CTRL_CBPR_SHIFT;
2798c2ecf20Sopenharmony_ci		val |= vmcr.eoim << GIC_CPU_CTRL_EOImodeNS_SHIFT;
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci		break;
2828c2ecf20Sopenharmony_ci	case GIC_CPU_PRIMASK:
2838c2ecf20Sopenharmony_ci		/*
2848c2ecf20Sopenharmony_ci		 * Our KVM_DEV_TYPE_ARM_VGIC_V2 device ABI exports the
2858c2ecf20Sopenharmony_ci		 * the PMR field as GICH_VMCR.VMPriMask rather than
2868c2ecf20Sopenharmony_ci		 * GICC_PMR.Priority, so we expose the upper five bits of
2878c2ecf20Sopenharmony_ci		 * priority mask to userspace using the lower bits in the
2888c2ecf20Sopenharmony_ci		 * unsigned long.
2898c2ecf20Sopenharmony_ci		 */
2908c2ecf20Sopenharmony_ci		val = (vmcr.pmr & GICV_PMR_PRIORITY_MASK) >>
2918c2ecf20Sopenharmony_ci			GICV_PMR_PRIORITY_SHIFT;
2928c2ecf20Sopenharmony_ci		break;
2938c2ecf20Sopenharmony_ci	case GIC_CPU_BINPOINT:
2948c2ecf20Sopenharmony_ci		val = vmcr.bpr;
2958c2ecf20Sopenharmony_ci		break;
2968c2ecf20Sopenharmony_ci	case GIC_CPU_ALIAS_BINPOINT:
2978c2ecf20Sopenharmony_ci		val = vmcr.abpr;
2988c2ecf20Sopenharmony_ci		break;
2998c2ecf20Sopenharmony_ci	case GIC_CPU_IDENT:
3008c2ecf20Sopenharmony_ci		val = ((PRODUCT_ID_KVM << 20) |
3018c2ecf20Sopenharmony_ci		       (GICC_ARCH_VERSION_V2 << 16) |
3028c2ecf20Sopenharmony_ci		       IMPLEMENTER_ARM);
3038c2ecf20Sopenharmony_ci		break;
3048c2ecf20Sopenharmony_ci	default:
3058c2ecf20Sopenharmony_ci		return 0;
3068c2ecf20Sopenharmony_ci	}
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci	return val;
3098c2ecf20Sopenharmony_ci}
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_cistatic void vgic_mmio_write_vcpuif(struct kvm_vcpu *vcpu,
3128c2ecf20Sopenharmony_ci				   gpa_t addr, unsigned int len,
3138c2ecf20Sopenharmony_ci				   unsigned long val)
3148c2ecf20Sopenharmony_ci{
3158c2ecf20Sopenharmony_ci	struct vgic_vmcr vmcr;
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci	vgic_get_vmcr(vcpu, &vmcr);
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci	switch (addr & 0xff) {
3208c2ecf20Sopenharmony_ci	case GIC_CPU_CTRL:
3218c2ecf20Sopenharmony_ci		vmcr.grpen0 = !!(val & GIC_CPU_CTRL_EnableGrp0);
3228c2ecf20Sopenharmony_ci		vmcr.grpen1 = !!(val & GIC_CPU_CTRL_EnableGrp1);
3238c2ecf20Sopenharmony_ci		vmcr.ackctl = !!(val & GIC_CPU_CTRL_AckCtl);
3248c2ecf20Sopenharmony_ci		vmcr.fiqen = !!(val & GIC_CPU_CTRL_FIQEn);
3258c2ecf20Sopenharmony_ci		vmcr.cbpr = !!(val & GIC_CPU_CTRL_CBPR);
3268c2ecf20Sopenharmony_ci		vmcr.eoim = !!(val & GIC_CPU_CTRL_EOImodeNS);
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci		break;
3298c2ecf20Sopenharmony_ci	case GIC_CPU_PRIMASK:
3308c2ecf20Sopenharmony_ci		/*
3318c2ecf20Sopenharmony_ci		 * Our KVM_DEV_TYPE_ARM_VGIC_V2 device ABI exports the
3328c2ecf20Sopenharmony_ci		 * the PMR field as GICH_VMCR.VMPriMask rather than
3338c2ecf20Sopenharmony_ci		 * GICC_PMR.Priority, so we expose the upper five bits of
3348c2ecf20Sopenharmony_ci		 * priority mask to userspace using the lower bits in the
3358c2ecf20Sopenharmony_ci		 * unsigned long.
3368c2ecf20Sopenharmony_ci		 */
3378c2ecf20Sopenharmony_ci		vmcr.pmr = (val << GICV_PMR_PRIORITY_SHIFT) &
3388c2ecf20Sopenharmony_ci			GICV_PMR_PRIORITY_MASK;
3398c2ecf20Sopenharmony_ci		break;
3408c2ecf20Sopenharmony_ci	case GIC_CPU_BINPOINT:
3418c2ecf20Sopenharmony_ci		vmcr.bpr = val;
3428c2ecf20Sopenharmony_ci		break;
3438c2ecf20Sopenharmony_ci	case GIC_CPU_ALIAS_BINPOINT:
3448c2ecf20Sopenharmony_ci		vmcr.abpr = val;
3458c2ecf20Sopenharmony_ci		break;
3468c2ecf20Sopenharmony_ci	}
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci	vgic_set_vmcr(vcpu, &vmcr);
3498c2ecf20Sopenharmony_ci}
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_cistatic unsigned long vgic_mmio_read_apr(struct kvm_vcpu *vcpu,
3528c2ecf20Sopenharmony_ci					gpa_t addr, unsigned int len)
3538c2ecf20Sopenharmony_ci{
3548c2ecf20Sopenharmony_ci	int n; /* which APRn is this */
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci	n = (addr >> 2) & 0x3;
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci	if (kvm_vgic_global_state.type == VGIC_V2) {
3598c2ecf20Sopenharmony_ci		/* GICv2 hardware systems support max. 32 groups */
3608c2ecf20Sopenharmony_ci		if (n != 0)
3618c2ecf20Sopenharmony_ci			return 0;
3628c2ecf20Sopenharmony_ci		return vcpu->arch.vgic_cpu.vgic_v2.vgic_apr;
3638c2ecf20Sopenharmony_ci	} else {
3648c2ecf20Sopenharmony_ci		struct vgic_v3_cpu_if *vgicv3 = &vcpu->arch.vgic_cpu.vgic_v3;
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci		if (n > vgic_v3_max_apr_idx(vcpu))
3678c2ecf20Sopenharmony_ci			return 0;
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ci		n = array_index_nospec(n, 4);
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci		/* GICv3 only uses ICH_AP1Rn for memory mapped (GICv2) guests */
3728c2ecf20Sopenharmony_ci		return vgicv3->vgic_ap1r[n];
3738c2ecf20Sopenharmony_ci	}
3748c2ecf20Sopenharmony_ci}
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_cistatic void vgic_mmio_write_apr(struct kvm_vcpu *vcpu,
3778c2ecf20Sopenharmony_ci				gpa_t addr, unsigned int len,
3788c2ecf20Sopenharmony_ci				unsigned long val)
3798c2ecf20Sopenharmony_ci{
3808c2ecf20Sopenharmony_ci	int n; /* which APRn is this */
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci	n = (addr >> 2) & 0x3;
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	if (kvm_vgic_global_state.type == VGIC_V2) {
3858c2ecf20Sopenharmony_ci		/* GICv2 hardware systems support max. 32 groups */
3868c2ecf20Sopenharmony_ci		if (n != 0)
3878c2ecf20Sopenharmony_ci			return;
3888c2ecf20Sopenharmony_ci		vcpu->arch.vgic_cpu.vgic_v2.vgic_apr = val;
3898c2ecf20Sopenharmony_ci	} else {
3908c2ecf20Sopenharmony_ci		struct vgic_v3_cpu_if *vgicv3 = &vcpu->arch.vgic_cpu.vgic_v3;
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci		if (n > vgic_v3_max_apr_idx(vcpu))
3938c2ecf20Sopenharmony_ci			return;
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci		n = array_index_nospec(n, 4);
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ci		/* GICv3 only uses ICH_AP1Rn for memory mapped (GICv2) guests */
3988c2ecf20Sopenharmony_ci		vgicv3->vgic_ap1r[n] = val;
3998c2ecf20Sopenharmony_ci	}
4008c2ecf20Sopenharmony_ci}
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_cistatic const struct vgic_register_region vgic_v2_dist_registers[] = {
4038c2ecf20Sopenharmony_ci	REGISTER_DESC_WITH_LENGTH_UACCESS(GIC_DIST_CTRL,
4048c2ecf20Sopenharmony_ci		vgic_mmio_read_v2_misc, vgic_mmio_write_v2_misc,
4058c2ecf20Sopenharmony_ci		NULL, vgic_mmio_uaccess_write_v2_misc,
4068c2ecf20Sopenharmony_ci		12, VGIC_ACCESS_32bit),
4078c2ecf20Sopenharmony_ci	REGISTER_DESC_WITH_BITS_PER_IRQ(GIC_DIST_IGROUP,
4088c2ecf20Sopenharmony_ci		vgic_mmio_read_group, vgic_mmio_write_group,
4098c2ecf20Sopenharmony_ci		NULL, vgic_mmio_uaccess_write_v2_group, 1,
4108c2ecf20Sopenharmony_ci		VGIC_ACCESS_32bit),
4118c2ecf20Sopenharmony_ci	REGISTER_DESC_WITH_BITS_PER_IRQ(GIC_DIST_ENABLE_SET,
4128c2ecf20Sopenharmony_ci		vgic_mmio_read_enable, vgic_mmio_write_senable,
4138c2ecf20Sopenharmony_ci		NULL, vgic_uaccess_write_senable, 1,
4148c2ecf20Sopenharmony_ci		VGIC_ACCESS_32bit),
4158c2ecf20Sopenharmony_ci	REGISTER_DESC_WITH_BITS_PER_IRQ(GIC_DIST_ENABLE_CLEAR,
4168c2ecf20Sopenharmony_ci		vgic_mmio_read_enable, vgic_mmio_write_cenable,
4178c2ecf20Sopenharmony_ci		NULL, vgic_uaccess_write_cenable, 1,
4188c2ecf20Sopenharmony_ci		VGIC_ACCESS_32bit),
4198c2ecf20Sopenharmony_ci	REGISTER_DESC_WITH_BITS_PER_IRQ(GIC_DIST_PENDING_SET,
4208c2ecf20Sopenharmony_ci		vgic_mmio_read_pending, vgic_mmio_write_spending,
4218c2ecf20Sopenharmony_ci		vgic_uaccess_read_pending, vgic_uaccess_write_spending, 1,
4228c2ecf20Sopenharmony_ci		VGIC_ACCESS_32bit),
4238c2ecf20Sopenharmony_ci	REGISTER_DESC_WITH_BITS_PER_IRQ(GIC_DIST_PENDING_CLEAR,
4248c2ecf20Sopenharmony_ci		vgic_mmio_read_pending, vgic_mmio_write_cpending,
4258c2ecf20Sopenharmony_ci		vgic_uaccess_read_pending, vgic_uaccess_write_cpending, 1,
4268c2ecf20Sopenharmony_ci		VGIC_ACCESS_32bit),
4278c2ecf20Sopenharmony_ci	REGISTER_DESC_WITH_BITS_PER_IRQ(GIC_DIST_ACTIVE_SET,
4288c2ecf20Sopenharmony_ci		vgic_mmio_read_active, vgic_mmio_write_sactive,
4298c2ecf20Sopenharmony_ci		vgic_uaccess_read_active, vgic_mmio_uaccess_write_sactive, 1,
4308c2ecf20Sopenharmony_ci		VGIC_ACCESS_32bit),
4318c2ecf20Sopenharmony_ci	REGISTER_DESC_WITH_BITS_PER_IRQ(GIC_DIST_ACTIVE_CLEAR,
4328c2ecf20Sopenharmony_ci		vgic_mmio_read_active, vgic_mmio_write_cactive,
4338c2ecf20Sopenharmony_ci		vgic_uaccess_read_active, vgic_mmio_uaccess_write_cactive, 1,
4348c2ecf20Sopenharmony_ci		VGIC_ACCESS_32bit),
4358c2ecf20Sopenharmony_ci	REGISTER_DESC_WITH_BITS_PER_IRQ(GIC_DIST_PRI,
4368c2ecf20Sopenharmony_ci		vgic_mmio_read_priority, vgic_mmio_write_priority, NULL, NULL,
4378c2ecf20Sopenharmony_ci		8, VGIC_ACCESS_32bit | VGIC_ACCESS_8bit),
4388c2ecf20Sopenharmony_ci	REGISTER_DESC_WITH_BITS_PER_IRQ(GIC_DIST_TARGET,
4398c2ecf20Sopenharmony_ci		vgic_mmio_read_target, vgic_mmio_write_target, NULL, NULL, 8,
4408c2ecf20Sopenharmony_ci		VGIC_ACCESS_32bit | VGIC_ACCESS_8bit),
4418c2ecf20Sopenharmony_ci	REGISTER_DESC_WITH_BITS_PER_IRQ(GIC_DIST_CONFIG,
4428c2ecf20Sopenharmony_ci		vgic_mmio_read_config, vgic_mmio_write_config, NULL, NULL, 2,
4438c2ecf20Sopenharmony_ci		VGIC_ACCESS_32bit),
4448c2ecf20Sopenharmony_ci	REGISTER_DESC_WITH_LENGTH(GIC_DIST_SOFTINT,
4458c2ecf20Sopenharmony_ci		vgic_mmio_read_raz, vgic_mmio_write_sgir, 4,
4468c2ecf20Sopenharmony_ci		VGIC_ACCESS_32bit),
4478c2ecf20Sopenharmony_ci	REGISTER_DESC_WITH_LENGTH(GIC_DIST_SGI_PENDING_CLEAR,
4488c2ecf20Sopenharmony_ci		vgic_mmio_read_sgipend, vgic_mmio_write_sgipendc, 16,
4498c2ecf20Sopenharmony_ci		VGIC_ACCESS_32bit | VGIC_ACCESS_8bit),
4508c2ecf20Sopenharmony_ci	REGISTER_DESC_WITH_LENGTH(GIC_DIST_SGI_PENDING_SET,
4518c2ecf20Sopenharmony_ci		vgic_mmio_read_sgipend, vgic_mmio_write_sgipends, 16,
4528c2ecf20Sopenharmony_ci		VGIC_ACCESS_32bit | VGIC_ACCESS_8bit),
4538c2ecf20Sopenharmony_ci};
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_cistatic const struct vgic_register_region vgic_v2_cpu_registers[] = {
4568c2ecf20Sopenharmony_ci	REGISTER_DESC_WITH_LENGTH(GIC_CPU_CTRL,
4578c2ecf20Sopenharmony_ci		vgic_mmio_read_vcpuif, vgic_mmio_write_vcpuif, 4,
4588c2ecf20Sopenharmony_ci		VGIC_ACCESS_32bit),
4598c2ecf20Sopenharmony_ci	REGISTER_DESC_WITH_LENGTH(GIC_CPU_PRIMASK,
4608c2ecf20Sopenharmony_ci		vgic_mmio_read_vcpuif, vgic_mmio_write_vcpuif, 4,
4618c2ecf20Sopenharmony_ci		VGIC_ACCESS_32bit),
4628c2ecf20Sopenharmony_ci	REGISTER_DESC_WITH_LENGTH(GIC_CPU_BINPOINT,
4638c2ecf20Sopenharmony_ci		vgic_mmio_read_vcpuif, vgic_mmio_write_vcpuif, 4,
4648c2ecf20Sopenharmony_ci		VGIC_ACCESS_32bit),
4658c2ecf20Sopenharmony_ci	REGISTER_DESC_WITH_LENGTH(GIC_CPU_ALIAS_BINPOINT,
4668c2ecf20Sopenharmony_ci		vgic_mmio_read_vcpuif, vgic_mmio_write_vcpuif, 4,
4678c2ecf20Sopenharmony_ci		VGIC_ACCESS_32bit),
4688c2ecf20Sopenharmony_ci	REGISTER_DESC_WITH_LENGTH(GIC_CPU_ACTIVEPRIO,
4698c2ecf20Sopenharmony_ci		vgic_mmio_read_apr, vgic_mmio_write_apr, 16,
4708c2ecf20Sopenharmony_ci		VGIC_ACCESS_32bit),
4718c2ecf20Sopenharmony_ci	REGISTER_DESC_WITH_LENGTH(GIC_CPU_IDENT,
4728c2ecf20Sopenharmony_ci		vgic_mmio_read_vcpuif, vgic_mmio_write_vcpuif, 4,
4738c2ecf20Sopenharmony_ci		VGIC_ACCESS_32bit),
4748c2ecf20Sopenharmony_ci};
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_ciunsigned int vgic_v2_init_dist_iodev(struct vgic_io_device *dev)
4778c2ecf20Sopenharmony_ci{
4788c2ecf20Sopenharmony_ci	dev->regions = vgic_v2_dist_registers;
4798c2ecf20Sopenharmony_ci	dev->nr_regions = ARRAY_SIZE(vgic_v2_dist_registers);
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_ci	kvm_iodevice_init(&dev->dev, &kvm_io_gic_ops);
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_ci	return SZ_4K;
4848c2ecf20Sopenharmony_ci}
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ciint vgic_v2_has_attr_regs(struct kvm_device *dev, struct kvm_device_attr *attr)
4878c2ecf20Sopenharmony_ci{
4888c2ecf20Sopenharmony_ci	const struct vgic_register_region *region;
4898c2ecf20Sopenharmony_ci	struct vgic_io_device iodev;
4908c2ecf20Sopenharmony_ci	struct vgic_reg_attr reg_attr;
4918c2ecf20Sopenharmony_ci	struct kvm_vcpu *vcpu;
4928c2ecf20Sopenharmony_ci	gpa_t addr;
4938c2ecf20Sopenharmony_ci	int ret;
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_ci	ret = vgic_v2_parse_attr(dev, attr, &reg_attr);
4968c2ecf20Sopenharmony_ci	if (ret)
4978c2ecf20Sopenharmony_ci		return ret;
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci	vcpu = reg_attr.vcpu;
5008c2ecf20Sopenharmony_ci	addr = reg_attr.addr;
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_ci	switch (attr->group) {
5038c2ecf20Sopenharmony_ci	case KVM_DEV_ARM_VGIC_GRP_DIST_REGS:
5048c2ecf20Sopenharmony_ci		iodev.regions = vgic_v2_dist_registers;
5058c2ecf20Sopenharmony_ci		iodev.nr_regions = ARRAY_SIZE(vgic_v2_dist_registers);
5068c2ecf20Sopenharmony_ci		iodev.base_addr = 0;
5078c2ecf20Sopenharmony_ci		break;
5088c2ecf20Sopenharmony_ci	case KVM_DEV_ARM_VGIC_GRP_CPU_REGS:
5098c2ecf20Sopenharmony_ci		iodev.regions = vgic_v2_cpu_registers;
5108c2ecf20Sopenharmony_ci		iodev.nr_regions = ARRAY_SIZE(vgic_v2_cpu_registers);
5118c2ecf20Sopenharmony_ci		iodev.base_addr = 0;
5128c2ecf20Sopenharmony_ci		break;
5138c2ecf20Sopenharmony_ci	default:
5148c2ecf20Sopenharmony_ci		return -ENXIO;
5158c2ecf20Sopenharmony_ci	}
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci	/* We only support aligned 32-bit accesses. */
5188c2ecf20Sopenharmony_ci	if (addr & 3)
5198c2ecf20Sopenharmony_ci		return -ENXIO;
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_ci	region = vgic_get_mmio_region(vcpu, &iodev, addr, sizeof(u32));
5228c2ecf20Sopenharmony_ci	if (!region)
5238c2ecf20Sopenharmony_ci		return -ENXIO;
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_ci	return 0;
5268c2ecf20Sopenharmony_ci}
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_ciint vgic_v2_cpuif_uaccess(struct kvm_vcpu *vcpu, bool is_write,
5298c2ecf20Sopenharmony_ci			  int offset, u32 *val)
5308c2ecf20Sopenharmony_ci{
5318c2ecf20Sopenharmony_ci	struct vgic_io_device dev = {
5328c2ecf20Sopenharmony_ci		.regions = vgic_v2_cpu_registers,
5338c2ecf20Sopenharmony_ci		.nr_regions = ARRAY_SIZE(vgic_v2_cpu_registers),
5348c2ecf20Sopenharmony_ci		.iodev_type = IODEV_CPUIF,
5358c2ecf20Sopenharmony_ci	};
5368c2ecf20Sopenharmony_ci
5378c2ecf20Sopenharmony_ci	return vgic_uaccess(vcpu, &dev, is_write, offset, val);
5388c2ecf20Sopenharmony_ci}
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_ciint vgic_v2_dist_uaccess(struct kvm_vcpu *vcpu, bool is_write,
5418c2ecf20Sopenharmony_ci			 int offset, u32 *val)
5428c2ecf20Sopenharmony_ci{
5438c2ecf20Sopenharmony_ci	struct vgic_io_device dev = {
5448c2ecf20Sopenharmony_ci		.regions = vgic_v2_dist_registers,
5458c2ecf20Sopenharmony_ci		.nr_regions = ARRAY_SIZE(vgic_v2_dist_registers),
5468c2ecf20Sopenharmony_ci		.iodev_type = IODEV_DIST,
5478c2ecf20Sopenharmony_ci	};
5488c2ecf20Sopenharmony_ci
5498c2ecf20Sopenharmony_ci	return vgic_uaccess(vcpu, &dev, is_write, offset, val);
5508c2ecf20Sopenharmony_ci}
551