18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2012,2013 - ARM Ltd 48c2ecf20Sopenharmony_ci * Author: Marc Zyngier <marc.zyngier@arm.com> 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Derived from arch/arm/kvm/emulate.c: 78c2ecf20Sopenharmony_ci * Copyright (C) 2012 - Virtual Open Systems and Columbia University 88c2ecf20Sopenharmony_ci * Author: Christoffer Dall <c.dall@virtualopensystems.com> 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/mm.h> 128c2ecf20Sopenharmony_ci#include <linux/kvm_host.h> 138c2ecf20Sopenharmony_ci#include <asm/kvm_emulate.h> 148c2ecf20Sopenharmony_ci#include <asm/ptrace.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#define VCPU_NR_MODES 6 178c2ecf20Sopenharmony_ci#define REG_OFFSET(_reg) \ 188c2ecf20Sopenharmony_ci (offsetof(struct user_pt_regs, _reg) / sizeof(unsigned long)) 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#define USR_REG_OFFSET(R) REG_OFFSET(compat_usr(R)) 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_cistatic const unsigned long vcpu_reg_offsets[VCPU_NR_MODES][16] = { 238c2ecf20Sopenharmony_ci /* USR Registers */ 248c2ecf20Sopenharmony_ci { 258c2ecf20Sopenharmony_ci USR_REG_OFFSET(0), USR_REG_OFFSET(1), USR_REG_OFFSET(2), 268c2ecf20Sopenharmony_ci USR_REG_OFFSET(3), USR_REG_OFFSET(4), USR_REG_OFFSET(5), 278c2ecf20Sopenharmony_ci USR_REG_OFFSET(6), USR_REG_OFFSET(7), USR_REG_OFFSET(8), 288c2ecf20Sopenharmony_ci USR_REG_OFFSET(9), USR_REG_OFFSET(10), USR_REG_OFFSET(11), 298c2ecf20Sopenharmony_ci USR_REG_OFFSET(12), USR_REG_OFFSET(13), USR_REG_OFFSET(14), 308c2ecf20Sopenharmony_ci REG_OFFSET(pc) 318c2ecf20Sopenharmony_ci }, 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci /* FIQ Registers */ 348c2ecf20Sopenharmony_ci { 358c2ecf20Sopenharmony_ci USR_REG_OFFSET(0), USR_REG_OFFSET(1), USR_REG_OFFSET(2), 368c2ecf20Sopenharmony_ci USR_REG_OFFSET(3), USR_REG_OFFSET(4), USR_REG_OFFSET(5), 378c2ecf20Sopenharmony_ci USR_REG_OFFSET(6), USR_REG_OFFSET(7), 388c2ecf20Sopenharmony_ci REG_OFFSET(compat_r8_fiq), /* r8 */ 398c2ecf20Sopenharmony_ci REG_OFFSET(compat_r9_fiq), /* r9 */ 408c2ecf20Sopenharmony_ci REG_OFFSET(compat_r10_fiq), /* r10 */ 418c2ecf20Sopenharmony_ci REG_OFFSET(compat_r11_fiq), /* r11 */ 428c2ecf20Sopenharmony_ci REG_OFFSET(compat_r12_fiq), /* r12 */ 438c2ecf20Sopenharmony_ci REG_OFFSET(compat_sp_fiq), /* r13 */ 448c2ecf20Sopenharmony_ci REG_OFFSET(compat_lr_fiq), /* r14 */ 458c2ecf20Sopenharmony_ci REG_OFFSET(pc) 468c2ecf20Sopenharmony_ci }, 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci /* IRQ Registers */ 498c2ecf20Sopenharmony_ci { 508c2ecf20Sopenharmony_ci USR_REG_OFFSET(0), USR_REG_OFFSET(1), USR_REG_OFFSET(2), 518c2ecf20Sopenharmony_ci USR_REG_OFFSET(3), USR_REG_OFFSET(4), USR_REG_OFFSET(5), 528c2ecf20Sopenharmony_ci USR_REG_OFFSET(6), USR_REG_OFFSET(7), USR_REG_OFFSET(8), 538c2ecf20Sopenharmony_ci USR_REG_OFFSET(9), USR_REG_OFFSET(10), USR_REG_OFFSET(11), 548c2ecf20Sopenharmony_ci USR_REG_OFFSET(12), 558c2ecf20Sopenharmony_ci REG_OFFSET(compat_sp_irq), /* r13 */ 568c2ecf20Sopenharmony_ci REG_OFFSET(compat_lr_irq), /* r14 */ 578c2ecf20Sopenharmony_ci REG_OFFSET(pc) 588c2ecf20Sopenharmony_ci }, 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci /* SVC Registers */ 618c2ecf20Sopenharmony_ci { 628c2ecf20Sopenharmony_ci USR_REG_OFFSET(0), USR_REG_OFFSET(1), USR_REG_OFFSET(2), 638c2ecf20Sopenharmony_ci USR_REG_OFFSET(3), USR_REG_OFFSET(4), USR_REG_OFFSET(5), 648c2ecf20Sopenharmony_ci USR_REG_OFFSET(6), USR_REG_OFFSET(7), USR_REG_OFFSET(8), 658c2ecf20Sopenharmony_ci USR_REG_OFFSET(9), USR_REG_OFFSET(10), USR_REG_OFFSET(11), 668c2ecf20Sopenharmony_ci USR_REG_OFFSET(12), 678c2ecf20Sopenharmony_ci REG_OFFSET(compat_sp_svc), /* r13 */ 688c2ecf20Sopenharmony_ci REG_OFFSET(compat_lr_svc), /* r14 */ 698c2ecf20Sopenharmony_ci REG_OFFSET(pc) 708c2ecf20Sopenharmony_ci }, 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci /* ABT Registers */ 738c2ecf20Sopenharmony_ci { 748c2ecf20Sopenharmony_ci USR_REG_OFFSET(0), USR_REG_OFFSET(1), USR_REG_OFFSET(2), 758c2ecf20Sopenharmony_ci USR_REG_OFFSET(3), USR_REG_OFFSET(4), USR_REG_OFFSET(5), 768c2ecf20Sopenharmony_ci USR_REG_OFFSET(6), USR_REG_OFFSET(7), USR_REG_OFFSET(8), 778c2ecf20Sopenharmony_ci USR_REG_OFFSET(9), USR_REG_OFFSET(10), USR_REG_OFFSET(11), 788c2ecf20Sopenharmony_ci USR_REG_OFFSET(12), 798c2ecf20Sopenharmony_ci REG_OFFSET(compat_sp_abt), /* r13 */ 808c2ecf20Sopenharmony_ci REG_OFFSET(compat_lr_abt), /* r14 */ 818c2ecf20Sopenharmony_ci REG_OFFSET(pc) 828c2ecf20Sopenharmony_ci }, 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci /* UND Registers */ 858c2ecf20Sopenharmony_ci { 868c2ecf20Sopenharmony_ci USR_REG_OFFSET(0), USR_REG_OFFSET(1), USR_REG_OFFSET(2), 878c2ecf20Sopenharmony_ci USR_REG_OFFSET(3), USR_REG_OFFSET(4), USR_REG_OFFSET(5), 888c2ecf20Sopenharmony_ci USR_REG_OFFSET(6), USR_REG_OFFSET(7), USR_REG_OFFSET(8), 898c2ecf20Sopenharmony_ci USR_REG_OFFSET(9), USR_REG_OFFSET(10), USR_REG_OFFSET(11), 908c2ecf20Sopenharmony_ci USR_REG_OFFSET(12), 918c2ecf20Sopenharmony_ci REG_OFFSET(compat_sp_und), /* r13 */ 928c2ecf20Sopenharmony_ci REG_OFFSET(compat_lr_und), /* r14 */ 938c2ecf20Sopenharmony_ci REG_OFFSET(pc) 948c2ecf20Sopenharmony_ci }, 958c2ecf20Sopenharmony_ci}; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci/* 988c2ecf20Sopenharmony_ci * Return a pointer to the register number valid in the current mode of 998c2ecf20Sopenharmony_ci * the virtual CPU. 1008c2ecf20Sopenharmony_ci */ 1018c2ecf20Sopenharmony_ciunsigned long *vcpu_reg32(const struct kvm_vcpu *vcpu, u8 reg_num) 1028c2ecf20Sopenharmony_ci{ 1038c2ecf20Sopenharmony_ci unsigned long *reg_array = (unsigned long *)&vcpu->arch.ctxt.regs; 1048c2ecf20Sopenharmony_ci unsigned long mode = *vcpu_cpsr(vcpu) & PSR_AA32_MODE_MASK; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci switch (mode) { 1078c2ecf20Sopenharmony_ci case PSR_AA32_MODE_USR ... PSR_AA32_MODE_SVC: 1088c2ecf20Sopenharmony_ci mode &= ~PSR_MODE32_BIT; /* 0 ... 3 */ 1098c2ecf20Sopenharmony_ci break; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci case PSR_AA32_MODE_ABT: 1128c2ecf20Sopenharmony_ci mode = 4; 1138c2ecf20Sopenharmony_ci break; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci case PSR_AA32_MODE_UND: 1168c2ecf20Sopenharmony_ci mode = 5; 1178c2ecf20Sopenharmony_ci break; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci case PSR_AA32_MODE_SYS: 1208c2ecf20Sopenharmony_ci mode = 0; /* SYS maps to USR */ 1218c2ecf20Sopenharmony_ci break; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci default: 1248c2ecf20Sopenharmony_ci BUG(); 1258c2ecf20Sopenharmony_ci } 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci return reg_array + vcpu_reg_offsets[mode][reg_num]; 1288c2ecf20Sopenharmony_ci} 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci/* 1318c2ecf20Sopenharmony_ci * Return the SPSR for the current mode of the virtual CPU. 1328c2ecf20Sopenharmony_ci */ 1338c2ecf20Sopenharmony_cistatic int vcpu_spsr32_mode(const struct kvm_vcpu *vcpu) 1348c2ecf20Sopenharmony_ci{ 1358c2ecf20Sopenharmony_ci unsigned long mode = *vcpu_cpsr(vcpu) & PSR_AA32_MODE_MASK; 1368c2ecf20Sopenharmony_ci switch (mode) { 1378c2ecf20Sopenharmony_ci case PSR_AA32_MODE_SVC: return KVM_SPSR_SVC; 1388c2ecf20Sopenharmony_ci case PSR_AA32_MODE_ABT: return KVM_SPSR_ABT; 1398c2ecf20Sopenharmony_ci case PSR_AA32_MODE_UND: return KVM_SPSR_UND; 1408c2ecf20Sopenharmony_ci case PSR_AA32_MODE_IRQ: return KVM_SPSR_IRQ; 1418c2ecf20Sopenharmony_ci case PSR_AA32_MODE_FIQ: return KVM_SPSR_FIQ; 1428c2ecf20Sopenharmony_ci default: BUG(); 1438c2ecf20Sopenharmony_ci } 1448c2ecf20Sopenharmony_ci} 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ciunsigned long vcpu_read_spsr32(const struct kvm_vcpu *vcpu) 1478c2ecf20Sopenharmony_ci{ 1488c2ecf20Sopenharmony_ci int spsr_idx = vcpu_spsr32_mode(vcpu); 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci if (!vcpu->arch.sysregs_loaded_on_cpu) { 1518c2ecf20Sopenharmony_ci switch (spsr_idx) { 1528c2ecf20Sopenharmony_ci case KVM_SPSR_SVC: 1538c2ecf20Sopenharmony_ci return __vcpu_sys_reg(vcpu, SPSR_EL1); 1548c2ecf20Sopenharmony_ci case KVM_SPSR_ABT: 1558c2ecf20Sopenharmony_ci return vcpu->arch.ctxt.spsr_abt; 1568c2ecf20Sopenharmony_ci case KVM_SPSR_UND: 1578c2ecf20Sopenharmony_ci return vcpu->arch.ctxt.spsr_und; 1588c2ecf20Sopenharmony_ci case KVM_SPSR_IRQ: 1598c2ecf20Sopenharmony_ci return vcpu->arch.ctxt.spsr_irq; 1608c2ecf20Sopenharmony_ci case KVM_SPSR_FIQ: 1618c2ecf20Sopenharmony_ci return vcpu->arch.ctxt.spsr_fiq; 1628c2ecf20Sopenharmony_ci } 1638c2ecf20Sopenharmony_ci } 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci switch (spsr_idx) { 1668c2ecf20Sopenharmony_ci case KVM_SPSR_SVC: 1678c2ecf20Sopenharmony_ci return read_sysreg_el1(SYS_SPSR); 1688c2ecf20Sopenharmony_ci case KVM_SPSR_ABT: 1698c2ecf20Sopenharmony_ci return read_sysreg(spsr_abt); 1708c2ecf20Sopenharmony_ci case KVM_SPSR_UND: 1718c2ecf20Sopenharmony_ci return read_sysreg(spsr_und); 1728c2ecf20Sopenharmony_ci case KVM_SPSR_IRQ: 1738c2ecf20Sopenharmony_ci return read_sysreg(spsr_irq); 1748c2ecf20Sopenharmony_ci case KVM_SPSR_FIQ: 1758c2ecf20Sopenharmony_ci return read_sysreg(spsr_fiq); 1768c2ecf20Sopenharmony_ci default: 1778c2ecf20Sopenharmony_ci BUG(); 1788c2ecf20Sopenharmony_ci } 1798c2ecf20Sopenharmony_ci} 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_civoid vcpu_write_spsr32(struct kvm_vcpu *vcpu, unsigned long v) 1828c2ecf20Sopenharmony_ci{ 1838c2ecf20Sopenharmony_ci int spsr_idx = vcpu_spsr32_mode(vcpu); 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci if (!vcpu->arch.sysregs_loaded_on_cpu) { 1868c2ecf20Sopenharmony_ci switch (spsr_idx) { 1878c2ecf20Sopenharmony_ci case KVM_SPSR_SVC: 1888c2ecf20Sopenharmony_ci __vcpu_sys_reg(vcpu, SPSR_EL1) = v; 1898c2ecf20Sopenharmony_ci break; 1908c2ecf20Sopenharmony_ci case KVM_SPSR_ABT: 1918c2ecf20Sopenharmony_ci vcpu->arch.ctxt.spsr_abt = v; 1928c2ecf20Sopenharmony_ci break; 1938c2ecf20Sopenharmony_ci case KVM_SPSR_UND: 1948c2ecf20Sopenharmony_ci vcpu->arch.ctxt.spsr_und = v; 1958c2ecf20Sopenharmony_ci break; 1968c2ecf20Sopenharmony_ci case KVM_SPSR_IRQ: 1978c2ecf20Sopenharmony_ci vcpu->arch.ctxt.spsr_irq = v; 1988c2ecf20Sopenharmony_ci break; 1998c2ecf20Sopenharmony_ci case KVM_SPSR_FIQ: 2008c2ecf20Sopenharmony_ci vcpu->arch.ctxt.spsr_fiq = v; 2018c2ecf20Sopenharmony_ci break; 2028c2ecf20Sopenharmony_ci } 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci return; 2058c2ecf20Sopenharmony_ci } 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci switch (spsr_idx) { 2088c2ecf20Sopenharmony_ci case KVM_SPSR_SVC: 2098c2ecf20Sopenharmony_ci write_sysreg_el1(v, SYS_SPSR); 2108c2ecf20Sopenharmony_ci break; 2118c2ecf20Sopenharmony_ci case KVM_SPSR_ABT: 2128c2ecf20Sopenharmony_ci write_sysreg(v, spsr_abt); 2138c2ecf20Sopenharmony_ci break; 2148c2ecf20Sopenharmony_ci case KVM_SPSR_UND: 2158c2ecf20Sopenharmony_ci write_sysreg(v, spsr_und); 2168c2ecf20Sopenharmony_ci break; 2178c2ecf20Sopenharmony_ci case KVM_SPSR_IRQ: 2188c2ecf20Sopenharmony_ci write_sysreg(v, spsr_irq); 2198c2ecf20Sopenharmony_ci break; 2208c2ecf20Sopenharmony_ci case KVM_SPSR_FIQ: 2218c2ecf20Sopenharmony_ci write_sysreg(v, spsr_fiq); 2228c2ecf20Sopenharmony_ci break; 2238c2ecf20Sopenharmony_ci } 2248c2ecf20Sopenharmony_ci} 225