xref: /linux/arch/arm64/kvm/hyp/vgic-v2-cpuif-proxy.c (revision d0034a7a4ac7fae708146ac0059b9c47a1543f0d)
1caab277bSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
28a43a2b3SChristoffer Dall /*
38a43a2b3SChristoffer Dall  * Copyright (C) 2012-2015 - ARM Ltd
48a43a2b3SChristoffer Dall  * Author: Marc Zyngier <marc.zyngier@arm.com>
58a43a2b3SChristoffer Dall  */
68a43a2b3SChristoffer Dall 
7cdb5e02eSMarc Zyngier #include <hyp/adjust_pc.h>
8cdb5e02eSMarc Zyngier 
98a43a2b3SChristoffer Dall #include <linux/compiler.h>
108a43a2b3SChristoffer Dall #include <linux/irqchip/arm-gic.h>
118a43a2b3SChristoffer Dall #include <linux/kvm_host.h>
12b220244dSJames Morse #include <linux/swab.h>
138a43a2b3SChristoffer Dall 
148a43a2b3SChristoffer Dall #include <asm/kvm_emulate.h>
158a43a2b3SChristoffer Dall #include <asm/kvm_hyp.h>
168a43a2b3SChristoffer Dall #include <asm/kvm_mmu.h>
178a43a2b3SChristoffer Dall 
__is_be(struct kvm_vcpu * vcpu)18c50cb043SDavid Brazdil static bool __is_be(struct kvm_vcpu *vcpu)
19b220244dSJames Morse {
20b220244dSJames Morse 	if (vcpu_mode_is_32bit(vcpu))
21fdec2a9eSDave Martin 		return !!(read_sysreg_el2(SYS_SPSR) & PSR_AA32_E_BIT);
22b220244dSJames Morse 
23b220244dSJames Morse 	return !!(read_sysreg(SCTLR_EL1) & SCTLR_ELx_EE);
24b220244dSJames Morse }
25b220244dSJames Morse 
268a43a2b3SChristoffer Dall /*
278a43a2b3SChristoffer Dall  * __vgic_v2_perform_cpuif_access -- perform a GICV access on behalf of the
288a43a2b3SChristoffer Dall  *				     guest.
298a43a2b3SChristoffer Dall  *
308a43a2b3SChristoffer Dall  * @vcpu: the offending vcpu
318a43a2b3SChristoffer Dall  *
328a43a2b3SChristoffer Dall  * Returns:
338a43a2b3SChristoffer Dall  *  1: GICV access successfully performed
348a43a2b3SChristoffer Dall  *  0: Not a GICV access
35bd7d95caSMark Rutland  * -1: Illegal GICV access successfully performed
368a43a2b3SChristoffer Dall  */
__vgic_v2_perform_cpuif_access(struct kvm_vcpu * vcpu)37c50cb043SDavid Brazdil int __vgic_v2_perform_cpuif_access(struct kvm_vcpu *vcpu)
388a43a2b3SChristoffer Dall {
398a43a2b3SChristoffer Dall 	struct kvm *kvm = kern_hyp_va(vcpu->kvm);
408a43a2b3SChristoffer Dall 	struct vgic_dist *vgic = &kvm->arch.vgic;
418a43a2b3SChristoffer Dall 	phys_addr_t fault_ipa;
428a43a2b3SChristoffer Dall 	void __iomem *addr;
438a43a2b3SChristoffer Dall 	int rd;
448a43a2b3SChristoffer Dall 
458a43a2b3SChristoffer Dall 	/* Build the full address */
468a43a2b3SChristoffer Dall 	fault_ipa  = kvm_vcpu_get_fault_ipa(vcpu);
478a43a2b3SChristoffer Dall 	fault_ipa |= kvm_vcpu_get_hfar(vcpu) & GENMASK(11, 0);
488a43a2b3SChristoffer Dall 
498a43a2b3SChristoffer Dall 	/* If not for GICV, move on */
508a43a2b3SChristoffer Dall 	if (fault_ipa <  vgic->vgic_cpu_base ||
518a43a2b3SChristoffer Dall 	    fault_ipa >= (vgic->vgic_cpu_base + KVM_VGIC_V2_CPU_SIZE))
528a43a2b3SChristoffer Dall 		return 0;
538a43a2b3SChristoffer Dall 
548a43a2b3SChristoffer Dall 	/* Reject anything but a 32bit access */
55bd7d95caSMark Rutland 	if (kvm_vcpu_dabt_get_as(vcpu) != sizeof(u32)) {
56bd7d95caSMark Rutland 		__kvm_skip_instr(vcpu);
578a43a2b3SChristoffer Dall 		return -1;
58bd7d95caSMark Rutland 	}
598a43a2b3SChristoffer Dall 
608a43a2b3SChristoffer Dall 	/* Not aligned? Don't bother */
61bd7d95caSMark Rutland 	if (fault_ipa & 3) {
62bd7d95caSMark Rutland 		__kvm_skip_instr(vcpu);
638a43a2b3SChristoffer Dall 		return -1;
64bd7d95caSMark Rutland 	}
658a43a2b3SChristoffer Dall 
668a43a2b3SChristoffer Dall 	rd = kvm_vcpu_dabt_get_rd(vcpu);
67*247bc166SDavid Brazdil 	addr  = kvm_vgic_global_state.vcpu_hyp_va;
688a43a2b3SChristoffer Dall 	addr += fault_ipa - vgic->vgic_cpu_base;
698a43a2b3SChristoffer Dall 
708a43a2b3SChristoffer Dall 	if (kvm_vcpu_dabt_iswrite(vcpu)) {
71b220244dSJames Morse 		u32 data = vcpu_get_reg(vcpu, rd);
72b220244dSJames Morse 		if (__is_be(vcpu)) {
73b220244dSJames Morse 			/* guest pre-swabbed data, undo this for writel() */
748c2d146eSJames Morse 			data = __kvm_swab32(data);
75b220244dSJames Morse 		}
768a43a2b3SChristoffer Dall 		writel_relaxed(data, addr);
778a43a2b3SChristoffer Dall 	} else {
788a43a2b3SChristoffer Dall 		u32 data = readl_relaxed(addr);
79b220244dSJames Morse 		if (__is_be(vcpu)) {
80b220244dSJames Morse 			/* guest expects swabbed data */
818c2d146eSJames Morse 			data = __kvm_swab32(data);
82b220244dSJames Morse 		}
83b220244dSJames Morse 		vcpu_set_reg(vcpu, rd, data);
848a43a2b3SChristoffer Dall 	}
858a43a2b3SChristoffer Dall 
86bd7d95caSMark Rutland 	__kvm_skip_instr(vcpu);
87bd7d95caSMark Rutland 
888a43a2b3SChristoffer Dall 	return 1;
898a43a2b3SChristoffer Dall }
90