162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2012-2015 - ARM Ltd
462306a36Sopenharmony_ci * Author: Marc Zyngier <marc.zyngier@arm.com>
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include <hyp/adjust_pc.h>
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/compiler.h>
1062306a36Sopenharmony_ci#include <linux/irqchip/arm-gic.h>
1162306a36Sopenharmony_ci#include <linux/kvm_host.h>
1262306a36Sopenharmony_ci#include <linux/swab.h>
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include <asm/kvm_emulate.h>
1562306a36Sopenharmony_ci#include <asm/kvm_hyp.h>
1662306a36Sopenharmony_ci#include <asm/kvm_mmu.h>
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_cistatic bool __is_be(struct kvm_vcpu *vcpu)
1962306a36Sopenharmony_ci{
2062306a36Sopenharmony_ci	if (vcpu_mode_is_32bit(vcpu))
2162306a36Sopenharmony_ci		return !!(read_sysreg_el2(SYS_SPSR) & PSR_AA32_E_BIT);
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci	return !!(read_sysreg(SCTLR_EL1) & SCTLR_ELx_EE);
2462306a36Sopenharmony_ci}
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci/*
2762306a36Sopenharmony_ci * __vgic_v2_perform_cpuif_access -- perform a GICV access on behalf of the
2862306a36Sopenharmony_ci *				     guest.
2962306a36Sopenharmony_ci *
3062306a36Sopenharmony_ci * @vcpu: the offending vcpu
3162306a36Sopenharmony_ci *
3262306a36Sopenharmony_ci * Returns:
3362306a36Sopenharmony_ci *  1: GICV access successfully performed
3462306a36Sopenharmony_ci *  0: Not a GICV access
3562306a36Sopenharmony_ci * -1: Illegal GICV access successfully performed
3662306a36Sopenharmony_ci */
3762306a36Sopenharmony_ciint __vgic_v2_perform_cpuif_access(struct kvm_vcpu *vcpu)
3862306a36Sopenharmony_ci{
3962306a36Sopenharmony_ci	struct kvm *kvm = kern_hyp_va(vcpu->kvm);
4062306a36Sopenharmony_ci	struct vgic_dist *vgic = &kvm->arch.vgic;
4162306a36Sopenharmony_ci	phys_addr_t fault_ipa;
4262306a36Sopenharmony_ci	void __iomem *addr;
4362306a36Sopenharmony_ci	int rd;
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci	/* Build the full address */
4662306a36Sopenharmony_ci	fault_ipa  = kvm_vcpu_get_fault_ipa(vcpu);
4762306a36Sopenharmony_ci	fault_ipa |= kvm_vcpu_get_hfar(vcpu) & GENMASK(11, 0);
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci	/* If not for GICV, move on */
5062306a36Sopenharmony_ci	if (fault_ipa <  vgic->vgic_cpu_base ||
5162306a36Sopenharmony_ci	    fault_ipa >= (vgic->vgic_cpu_base + KVM_VGIC_V2_CPU_SIZE))
5262306a36Sopenharmony_ci		return 0;
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	/* Reject anything but a 32bit access */
5562306a36Sopenharmony_ci	if (kvm_vcpu_dabt_get_as(vcpu) != sizeof(u32)) {
5662306a36Sopenharmony_ci		__kvm_skip_instr(vcpu);
5762306a36Sopenharmony_ci		return -1;
5862306a36Sopenharmony_ci	}
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	/* Not aligned? Don't bother */
6162306a36Sopenharmony_ci	if (fault_ipa & 3) {
6262306a36Sopenharmony_ci		__kvm_skip_instr(vcpu);
6362306a36Sopenharmony_ci		return -1;
6462306a36Sopenharmony_ci	}
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	rd = kvm_vcpu_dabt_get_rd(vcpu);
6762306a36Sopenharmony_ci	addr  = kvm_vgic_global_state.vcpu_hyp_va;
6862306a36Sopenharmony_ci	addr += fault_ipa - vgic->vgic_cpu_base;
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	if (kvm_vcpu_dabt_iswrite(vcpu)) {
7162306a36Sopenharmony_ci		u32 data = vcpu_get_reg(vcpu, rd);
7262306a36Sopenharmony_ci		if (__is_be(vcpu)) {
7362306a36Sopenharmony_ci			/* guest pre-swabbed data, undo this for writel() */
7462306a36Sopenharmony_ci			data = __kvm_swab32(data);
7562306a36Sopenharmony_ci		}
7662306a36Sopenharmony_ci		writel_relaxed(data, addr);
7762306a36Sopenharmony_ci	} else {
7862306a36Sopenharmony_ci		u32 data = readl_relaxed(addr);
7962306a36Sopenharmony_ci		if (__is_be(vcpu)) {
8062306a36Sopenharmony_ci			/* guest expects swabbed data */
8162306a36Sopenharmony_ci			data = __kvm_swab32(data);
8262306a36Sopenharmony_ci		}
8362306a36Sopenharmony_ci		vcpu_set_reg(vcpu, rd, data);
8462306a36Sopenharmony_ci	}
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	__kvm_skip_instr(vcpu);
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	return 1;
8962306a36Sopenharmony_ci}
90