18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * (not much of an) Emulation layer for 32bit guests. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2012,2013 - ARM Ltd 68c2ecf20Sopenharmony_ci * Author: Marc Zyngier <marc.zyngier@arm.com> 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * based on arch/arm/kvm/emulate.c 98c2ecf20Sopenharmony_ci * Copyright (C) 2012 - Virtual Open Systems and Columbia University 108c2ecf20Sopenharmony_ci * Author: Christoffer Dall <c.dall@virtualopensystems.com> 118c2ecf20Sopenharmony_ci */ 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <linux/bits.h> 148c2ecf20Sopenharmony_ci#include <linux/kvm_host.h> 158c2ecf20Sopenharmony_ci#include <asm/kvm_emulate.h> 168c2ecf20Sopenharmony_ci#include <asm/kvm_hyp.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#define DFSR_FSC_EXTABT_LPAE 0x10 198c2ecf20Sopenharmony_ci#define DFSR_FSC_EXTABT_nLPAE 0x08 208c2ecf20Sopenharmony_ci#define DFSR_LPAE BIT(9) 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci/* 238c2ecf20Sopenharmony_ci * Table taken from ARMv8 ARM DDI0487B-B, table G1-10. 248c2ecf20Sopenharmony_ci */ 258c2ecf20Sopenharmony_cistatic const u8 return_offsets[8][2] = { 268c2ecf20Sopenharmony_ci [0] = { 0, 0 }, /* Reset, unused */ 278c2ecf20Sopenharmony_ci [1] = { 4, 2 }, /* Undefined */ 288c2ecf20Sopenharmony_ci [2] = { 0, 0 }, /* SVC, unused */ 298c2ecf20Sopenharmony_ci [3] = { 4, 4 }, /* Prefetch abort */ 308c2ecf20Sopenharmony_ci [4] = { 8, 8 }, /* Data abort */ 318c2ecf20Sopenharmony_ci [5] = { 0, 0 }, /* HVC, unused */ 328c2ecf20Sopenharmony_ci [6] = { 4, 4 }, /* IRQ, unused */ 338c2ecf20Sopenharmony_ci [7] = { 4, 4 }, /* FIQ, unused */ 348c2ecf20Sopenharmony_ci}; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cistatic bool pre_fault_synchronize(struct kvm_vcpu *vcpu) 378c2ecf20Sopenharmony_ci{ 388c2ecf20Sopenharmony_ci preempt_disable(); 398c2ecf20Sopenharmony_ci if (vcpu->arch.sysregs_loaded_on_cpu) { 408c2ecf20Sopenharmony_ci kvm_arch_vcpu_put(vcpu); 418c2ecf20Sopenharmony_ci return true; 428c2ecf20Sopenharmony_ci } 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci preempt_enable(); 458c2ecf20Sopenharmony_ci return false; 468c2ecf20Sopenharmony_ci} 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistatic void post_fault_synchronize(struct kvm_vcpu *vcpu, bool loaded) 498c2ecf20Sopenharmony_ci{ 508c2ecf20Sopenharmony_ci if (loaded) { 518c2ecf20Sopenharmony_ci kvm_arch_vcpu_load(vcpu, smp_processor_id()); 528c2ecf20Sopenharmony_ci preempt_enable(); 538c2ecf20Sopenharmony_ci } 548c2ecf20Sopenharmony_ci} 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci/* 578c2ecf20Sopenharmony_ci * When an exception is taken, most CPSR fields are left unchanged in the 588c2ecf20Sopenharmony_ci * handler. However, some are explicitly overridden (e.g. M[4:0]). 598c2ecf20Sopenharmony_ci * 608c2ecf20Sopenharmony_ci * The SPSR/SPSR_ELx layouts differ, and the below is intended to work with 618c2ecf20Sopenharmony_ci * either format. Note: SPSR.J bit doesn't exist in SPSR_ELx, but this bit was 628c2ecf20Sopenharmony_ci * obsoleted by the ARMv7 virtualization extensions and is RES0. 638c2ecf20Sopenharmony_ci * 648c2ecf20Sopenharmony_ci * For the SPSR layout seen from AArch32, see: 658c2ecf20Sopenharmony_ci * - ARM DDI 0406C.d, page B1-1148 668c2ecf20Sopenharmony_ci * - ARM DDI 0487E.a, page G8-6264 678c2ecf20Sopenharmony_ci * 688c2ecf20Sopenharmony_ci * For the SPSR_ELx layout for AArch32 seen from AArch64, see: 698c2ecf20Sopenharmony_ci * - ARM DDI 0487E.a, page C5-426 708c2ecf20Sopenharmony_ci * 718c2ecf20Sopenharmony_ci * Here we manipulate the fields in order of the AArch32 SPSR_ELx layout, from 728c2ecf20Sopenharmony_ci * MSB to LSB. 738c2ecf20Sopenharmony_ci */ 748c2ecf20Sopenharmony_cistatic unsigned long get_except32_cpsr(struct kvm_vcpu *vcpu, u32 mode) 758c2ecf20Sopenharmony_ci{ 768c2ecf20Sopenharmony_ci u32 sctlr = vcpu_cp15(vcpu, c1_SCTLR); 778c2ecf20Sopenharmony_ci unsigned long old, new; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci old = *vcpu_cpsr(vcpu); 808c2ecf20Sopenharmony_ci new = 0; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci new |= (old & PSR_AA32_N_BIT); 838c2ecf20Sopenharmony_ci new |= (old & PSR_AA32_Z_BIT); 848c2ecf20Sopenharmony_ci new |= (old & PSR_AA32_C_BIT); 858c2ecf20Sopenharmony_ci new |= (old & PSR_AA32_V_BIT); 868c2ecf20Sopenharmony_ci new |= (old & PSR_AA32_Q_BIT); 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci // CPSR.IT[7:0] are set to zero upon any exception 898c2ecf20Sopenharmony_ci // See ARM DDI 0487E.a, section G1.12.3 908c2ecf20Sopenharmony_ci // See ARM DDI 0406C.d, section B1.8.3 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci new |= (old & PSR_AA32_DIT_BIT); 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci // CPSR.SSBS is set to SCTLR.DSSBS upon any exception 958c2ecf20Sopenharmony_ci // See ARM DDI 0487E.a, page G8-6244 968c2ecf20Sopenharmony_ci if (sctlr & BIT(31)) 978c2ecf20Sopenharmony_ci new |= PSR_AA32_SSBS_BIT; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci // CPSR.PAN is unchanged unless SCTLR.SPAN == 0b0 1008c2ecf20Sopenharmony_ci // SCTLR.SPAN is RES1 when ARMv8.1-PAN is not implemented 1018c2ecf20Sopenharmony_ci // See ARM DDI 0487E.a, page G8-6246 1028c2ecf20Sopenharmony_ci new |= (old & PSR_AA32_PAN_BIT); 1038c2ecf20Sopenharmony_ci if (!(sctlr & BIT(23))) 1048c2ecf20Sopenharmony_ci new |= PSR_AA32_PAN_BIT; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci // SS does not exist in AArch32, so ignore 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci // CPSR.IL is set to zero upon any exception 1098c2ecf20Sopenharmony_ci // See ARM DDI 0487E.a, page G1-5527 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci new |= (old & PSR_AA32_GE_MASK); 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci // CPSR.IT[7:0] are set to zero upon any exception 1148c2ecf20Sopenharmony_ci // See prior comment above 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci // CPSR.E is set to SCTLR.EE upon any exception 1178c2ecf20Sopenharmony_ci // See ARM DDI 0487E.a, page G8-6245 1188c2ecf20Sopenharmony_ci // See ARM DDI 0406C.d, page B4-1701 1198c2ecf20Sopenharmony_ci if (sctlr & BIT(25)) 1208c2ecf20Sopenharmony_ci new |= PSR_AA32_E_BIT; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci // CPSR.A is unchanged upon an exception to Undefined, Supervisor 1238c2ecf20Sopenharmony_ci // CPSR.A is set upon an exception to other modes 1248c2ecf20Sopenharmony_ci // See ARM DDI 0487E.a, pages G1-5515 to G1-5516 1258c2ecf20Sopenharmony_ci // See ARM DDI 0406C.d, page B1-1182 1268c2ecf20Sopenharmony_ci new |= (old & PSR_AA32_A_BIT); 1278c2ecf20Sopenharmony_ci if (mode != PSR_AA32_MODE_UND && mode != PSR_AA32_MODE_SVC) 1288c2ecf20Sopenharmony_ci new |= PSR_AA32_A_BIT; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci // CPSR.I is set upon any exception 1318c2ecf20Sopenharmony_ci // See ARM DDI 0487E.a, pages G1-5515 to G1-5516 1328c2ecf20Sopenharmony_ci // See ARM DDI 0406C.d, page B1-1182 1338c2ecf20Sopenharmony_ci new |= PSR_AA32_I_BIT; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci // CPSR.F is set upon an exception to FIQ 1368c2ecf20Sopenharmony_ci // CPSR.F is unchanged upon an exception to other modes 1378c2ecf20Sopenharmony_ci // See ARM DDI 0487E.a, pages G1-5515 to G1-5516 1388c2ecf20Sopenharmony_ci // See ARM DDI 0406C.d, page B1-1182 1398c2ecf20Sopenharmony_ci new |= (old & PSR_AA32_F_BIT); 1408c2ecf20Sopenharmony_ci if (mode == PSR_AA32_MODE_FIQ) 1418c2ecf20Sopenharmony_ci new |= PSR_AA32_F_BIT; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci // CPSR.T is set to SCTLR.TE upon any exception 1448c2ecf20Sopenharmony_ci // See ARM DDI 0487E.a, page G8-5514 1458c2ecf20Sopenharmony_ci // See ARM DDI 0406C.d, page B1-1181 1468c2ecf20Sopenharmony_ci if (sctlr & BIT(30)) 1478c2ecf20Sopenharmony_ci new |= PSR_AA32_T_BIT; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci new |= mode; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci return new; 1528c2ecf20Sopenharmony_ci} 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_cistatic void prepare_fault32(struct kvm_vcpu *vcpu, u32 mode, u32 vect_offset) 1558c2ecf20Sopenharmony_ci{ 1568c2ecf20Sopenharmony_ci unsigned long spsr = *vcpu_cpsr(vcpu); 1578c2ecf20Sopenharmony_ci bool is_thumb = (spsr & PSR_AA32_T_BIT); 1588c2ecf20Sopenharmony_ci u32 return_offset = return_offsets[vect_offset >> 2][is_thumb]; 1598c2ecf20Sopenharmony_ci u32 sctlr = vcpu_cp15(vcpu, c1_SCTLR); 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci *vcpu_cpsr(vcpu) = get_except32_cpsr(vcpu, mode); 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci /* Note: These now point to the banked copies */ 1648c2ecf20Sopenharmony_ci vcpu_write_spsr(vcpu, host_spsr_to_spsr32(spsr)); 1658c2ecf20Sopenharmony_ci *vcpu_reg32(vcpu, 14) = *vcpu_pc(vcpu) + return_offset; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci /* Branch to exception vector */ 1688c2ecf20Sopenharmony_ci if (sctlr & (1 << 13)) 1698c2ecf20Sopenharmony_ci vect_offset += 0xffff0000; 1708c2ecf20Sopenharmony_ci else /* always have security exceptions */ 1718c2ecf20Sopenharmony_ci vect_offset += vcpu_cp15(vcpu, c12_VBAR); 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci *vcpu_pc(vcpu) = vect_offset; 1748c2ecf20Sopenharmony_ci} 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_civoid kvm_inject_undef32(struct kvm_vcpu *vcpu) 1778c2ecf20Sopenharmony_ci{ 1788c2ecf20Sopenharmony_ci bool loaded = pre_fault_synchronize(vcpu); 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci prepare_fault32(vcpu, PSR_AA32_MODE_UND, 4); 1818c2ecf20Sopenharmony_ci post_fault_synchronize(vcpu, loaded); 1828c2ecf20Sopenharmony_ci} 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci/* 1858c2ecf20Sopenharmony_ci * Modelled after TakeDataAbortException() and TakePrefetchAbortException 1868c2ecf20Sopenharmony_ci * pseudocode. 1878c2ecf20Sopenharmony_ci */ 1888c2ecf20Sopenharmony_cistatic void inject_abt32(struct kvm_vcpu *vcpu, bool is_pabt, 1898c2ecf20Sopenharmony_ci unsigned long addr) 1908c2ecf20Sopenharmony_ci{ 1918c2ecf20Sopenharmony_ci u32 vect_offset; 1928c2ecf20Sopenharmony_ci u32 *far, *fsr; 1938c2ecf20Sopenharmony_ci bool is_lpae; 1948c2ecf20Sopenharmony_ci bool loaded; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci loaded = pre_fault_synchronize(vcpu); 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci if (is_pabt) { 1998c2ecf20Sopenharmony_ci vect_offset = 12; 2008c2ecf20Sopenharmony_ci far = &vcpu_cp15(vcpu, c6_IFAR); 2018c2ecf20Sopenharmony_ci fsr = &vcpu_cp15(vcpu, c5_IFSR); 2028c2ecf20Sopenharmony_ci } else { /* !iabt */ 2038c2ecf20Sopenharmony_ci vect_offset = 16; 2048c2ecf20Sopenharmony_ci far = &vcpu_cp15(vcpu, c6_DFAR); 2058c2ecf20Sopenharmony_ci fsr = &vcpu_cp15(vcpu, c5_DFSR); 2068c2ecf20Sopenharmony_ci } 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci prepare_fault32(vcpu, PSR_AA32_MODE_ABT, vect_offset); 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci *far = addr; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci /* Give the guest an IMPLEMENTATION DEFINED exception */ 2138c2ecf20Sopenharmony_ci is_lpae = (vcpu_cp15(vcpu, c2_TTBCR) >> 31); 2148c2ecf20Sopenharmony_ci if (is_lpae) { 2158c2ecf20Sopenharmony_ci *fsr = DFSR_LPAE | DFSR_FSC_EXTABT_LPAE; 2168c2ecf20Sopenharmony_ci } else { 2178c2ecf20Sopenharmony_ci /* no need to shuffle FS[4] into DFSR[10] as its 0 */ 2188c2ecf20Sopenharmony_ci *fsr = DFSR_FSC_EXTABT_nLPAE; 2198c2ecf20Sopenharmony_ci } 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci post_fault_synchronize(vcpu, loaded); 2228c2ecf20Sopenharmony_ci} 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_civoid kvm_inject_dabt32(struct kvm_vcpu *vcpu, unsigned long addr) 2258c2ecf20Sopenharmony_ci{ 2268c2ecf20Sopenharmony_ci inject_abt32(vcpu, false, addr); 2278c2ecf20Sopenharmony_ci} 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_civoid kvm_inject_pabt32(struct kvm_vcpu *vcpu, unsigned long addr) 2308c2ecf20Sopenharmony_ci{ 2318c2ecf20Sopenharmony_ci inject_abt32(vcpu, true, addr); 2328c2ecf20Sopenharmony_ci} 233