18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2012-2015 - ARM Ltd
48c2ecf20Sopenharmony_ci * Author: Marc Zyngier <marc.zyngier@arm.com>
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include <linux/compiler.h>
88c2ecf20Sopenharmony_ci#include <linux/irqchip/arm-gic.h>
98c2ecf20Sopenharmony_ci#include <linux/kvm_host.h>
108c2ecf20Sopenharmony_ci#include <linux/swab.h>
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include <asm/kvm_emulate.h>
138c2ecf20Sopenharmony_ci#include <asm/kvm_hyp.h>
148c2ecf20Sopenharmony_ci#include <asm/kvm_mmu.h>
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_cistatic bool __is_be(struct kvm_vcpu *vcpu)
178c2ecf20Sopenharmony_ci{
188c2ecf20Sopenharmony_ci	if (vcpu_mode_is_32bit(vcpu))
198c2ecf20Sopenharmony_ci		return !!(read_sysreg_el2(SYS_SPSR) & PSR_AA32_E_BIT);
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci	return !!(read_sysreg(SCTLR_EL1) & SCTLR_ELx_EE);
228c2ecf20Sopenharmony_ci}
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci/*
258c2ecf20Sopenharmony_ci * __vgic_v2_perform_cpuif_access -- perform a GICV access on behalf of the
268c2ecf20Sopenharmony_ci *				     guest.
278c2ecf20Sopenharmony_ci *
288c2ecf20Sopenharmony_ci * @vcpu: the offending vcpu
298c2ecf20Sopenharmony_ci *
308c2ecf20Sopenharmony_ci * Returns:
318c2ecf20Sopenharmony_ci *  1: GICV access successfully performed
328c2ecf20Sopenharmony_ci *  0: Not a GICV access
338c2ecf20Sopenharmony_ci * -1: Illegal GICV access successfully performed
348c2ecf20Sopenharmony_ci */
358c2ecf20Sopenharmony_ciint __vgic_v2_perform_cpuif_access(struct kvm_vcpu *vcpu)
368c2ecf20Sopenharmony_ci{
378c2ecf20Sopenharmony_ci	struct kvm *kvm = kern_hyp_va(vcpu->kvm);
388c2ecf20Sopenharmony_ci	struct vgic_dist *vgic = &kvm->arch.vgic;
398c2ecf20Sopenharmony_ci	phys_addr_t fault_ipa;
408c2ecf20Sopenharmony_ci	void __iomem *addr;
418c2ecf20Sopenharmony_ci	int rd;
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci	/* Build the full address */
448c2ecf20Sopenharmony_ci	fault_ipa  = kvm_vcpu_get_fault_ipa(vcpu);
458c2ecf20Sopenharmony_ci	fault_ipa |= kvm_vcpu_get_hfar(vcpu) & GENMASK(11, 0);
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci	/* If not for GICV, move on */
488c2ecf20Sopenharmony_ci	if (fault_ipa <  vgic->vgic_cpu_base ||
498c2ecf20Sopenharmony_ci	    fault_ipa >= (vgic->vgic_cpu_base + KVM_VGIC_V2_CPU_SIZE))
508c2ecf20Sopenharmony_ci		return 0;
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci	/* Reject anything but a 32bit access */
538c2ecf20Sopenharmony_ci	if (kvm_vcpu_dabt_get_as(vcpu) != sizeof(u32)) {
548c2ecf20Sopenharmony_ci		__kvm_skip_instr(vcpu);
558c2ecf20Sopenharmony_ci		return -1;
568c2ecf20Sopenharmony_ci	}
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci	/* Not aligned? Don't bother */
598c2ecf20Sopenharmony_ci	if (fault_ipa & 3) {
608c2ecf20Sopenharmony_ci		__kvm_skip_instr(vcpu);
618c2ecf20Sopenharmony_ci		return -1;
628c2ecf20Sopenharmony_ci	}
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	rd = kvm_vcpu_dabt_get_rd(vcpu);
658c2ecf20Sopenharmony_ci	addr  = hyp_symbol_addr(kvm_vgic_global_state)->vcpu_hyp_va;
668c2ecf20Sopenharmony_ci	addr += fault_ipa - vgic->vgic_cpu_base;
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci	if (kvm_vcpu_dabt_iswrite(vcpu)) {
698c2ecf20Sopenharmony_ci		u32 data = vcpu_get_reg(vcpu, rd);
708c2ecf20Sopenharmony_ci		if (__is_be(vcpu)) {
718c2ecf20Sopenharmony_ci			/* guest pre-swabbed data, undo this for writel() */
728c2ecf20Sopenharmony_ci			data = __kvm_swab32(data);
738c2ecf20Sopenharmony_ci		}
748c2ecf20Sopenharmony_ci		writel_relaxed(data, addr);
758c2ecf20Sopenharmony_ci	} else {
768c2ecf20Sopenharmony_ci		u32 data = readl_relaxed(addr);
778c2ecf20Sopenharmony_ci		if (__is_be(vcpu)) {
788c2ecf20Sopenharmony_ci			/* guest expects swabbed data */
798c2ecf20Sopenharmony_ci			data = __kvm_swab32(data);
808c2ecf20Sopenharmony_ci		}
818c2ecf20Sopenharmony_ci		vcpu_set_reg(vcpu, rd, data);
828c2ecf20Sopenharmony_ci	}
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	__kvm_skip_instr(vcpu);
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	return 1;
878c2ecf20Sopenharmony_ci}
88