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