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