18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public 38c2ecf20Sopenharmony_ci * License. See the file "COPYING" in the main directory of this archive 48c2ecf20Sopenharmony_ci * for more details. 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * KVM/MIPS: Instruction/Exception emulation 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Copyright (C) 2012 MIPS Technologies, Inc. All rights reserved. 98c2ecf20Sopenharmony_ci * Authors: Sanjay Lal <sanjayl@kymasys.com> 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/errno.h> 138c2ecf20Sopenharmony_ci#include <linux/err.h> 148c2ecf20Sopenharmony_ci#include <linux/ktime.h> 158c2ecf20Sopenharmony_ci#include <linux/kvm_host.h> 168c2ecf20Sopenharmony_ci#include <linux/vmalloc.h> 178c2ecf20Sopenharmony_ci#include <linux/fs.h> 188c2ecf20Sopenharmony_ci#include <linux/memblock.h> 198c2ecf20Sopenharmony_ci#include <linux/random.h> 208c2ecf20Sopenharmony_ci#include <asm/page.h> 218c2ecf20Sopenharmony_ci#include <asm/cacheflush.h> 228c2ecf20Sopenharmony_ci#include <asm/cacheops.h> 238c2ecf20Sopenharmony_ci#include <asm/cpu-info.h> 248c2ecf20Sopenharmony_ci#include <asm/mmu_context.h> 258c2ecf20Sopenharmony_ci#include <asm/tlbflush.h> 268c2ecf20Sopenharmony_ci#include <asm/inst.h> 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#undef CONFIG_MIPS_MT 298c2ecf20Sopenharmony_ci#include <asm/r4kcache.h> 308c2ecf20Sopenharmony_ci#define CONFIG_MIPS_MT 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci#include "interrupt.h" 338c2ecf20Sopenharmony_ci#include "commpage.h" 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci#include "trace.h" 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci/* 388c2ecf20Sopenharmony_ci * Compute the return address and do emulate branch simulation, if required. 398c2ecf20Sopenharmony_ci * This function should be called only in branch delay slot active. 408c2ecf20Sopenharmony_ci */ 418c2ecf20Sopenharmony_cistatic int kvm_compute_return_epc(struct kvm_vcpu *vcpu, unsigned long instpc, 428c2ecf20Sopenharmony_ci unsigned long *out) 438c2ecf20Sopenharmony_ci{ 448c2ecf20Sopenharmony_ci unsigned int dspcontrol; 458c2ecf20Sopenharmony_ci union mips_instruction insn; 468c2ecf20Sopenharmony_ci struct kvm_vcpu_arch *arch = &vcpu->arch; 478c2ecf20Sopenharmony_ci long epc = instpc; 488c2ecf20Sopenharmony_ci long nextpc; 498c2ecf20Sopenharmony_ci int err; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci if (epc & 3) { 528c2ecf20Sopenharmony_ci kvm_err("%s: unaligned epc\n", __func__); 538c2ecf20Sopenharmony_ci return -EINVAL; 548c2ecf20Sopenharmony_ci } 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci /* Read the instruction */ 578c2ecf20Sopenharmony_ci err = kvm_get_badinstrp((u32 *)epc, vcpu, &insn.word); 588c2ecf20Sopenharmony_ci if (err) 598c2ecf20Sopenharmony_ci return err; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci switch (insn.i_format.opcode) { 628c2ecf20Sopenharmony_ci /* jr and jalr are in r_format format. */ 638c2ecf20Sopenharmony_ci case spec_op: 648c2ecf20Sopenharmony_ci switch (insn.r_format.func) { 658c2ecf20Sopenharmony_ci case jalr_op: 668c2ecf20Sopenharmony_ci arch->gprs[insn.r_format.rd] = epc + 8; 678c2ecf20Sopenharmony_ci fallthrough; 688c2ecf20Sopenharmony_ci case jr_op: 698c2ecf20Sopenharmony_ci nextpc = arch->gprs[insn.r_format.rs]; 708c2ecf20Sopenharmony_ci break; 718c2ecf20Sopenharmony_ci default: 728c2ecf20Sopenharmony_ci return -EINVAL; 738c2ecf20Sopenharmony_ci } 748c2ecf20Sopenharmony_ci break; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci /* 778c2ecf20Sopenharmony_ci * This group contains: 788c2ecf20Sopenharmony_ci * bltz_op, bgez_op, bltzl_op, bgezl_op, 798c2ecf20Sopenharmony_ci * bltzal_op, bgezal_op, bltzall_op, bgezall_op. 808c2ecf20Sopenharmony_ci */ 818c2ecf20Sopenharmony_ci case bcond_op: 828c2ecf20Sopenharmony_ci switch (insn.i_format.rt) { 838c2ecf20Sopenharmony_ci case bltz_op: 848c2ecf20Sopenharmony_ci case bltzl_op: 858c2ecf20Sopenharmony_ci if ((long)arch->gprs[insn.i_format.rs] < 0) 868c2ecf20Sopenharmony_ci epc = epc + 4 + (insn.i_format.simmediate << 2); 878c2ecf20Sopenharmony_ci else 888c2ecf20Sopenharmony_ci epc += 8; 898c2ecf20Sopenharmony_ci nextpc = epc; 908c2ecf20Sopenharmony_ci break; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci case bgez_op: 938c2ecf20Sopenharmony_ci case bgezl_op: 948c2ecf20Sopenharmony_ci if ((long)arch->gprs[insn.i_format.rs] >= 0) 958c2ecf20Sopenharmony_ci epc = epc + 4 + (insn.i_format.simmediate << 2); 968c2ecf20Sopenharmony_ci else 978c2ecf20Sopenharmony_ci epc += 8; 988c2ecf20Sopenharmony_ci nextpc = epc; 998c2ecf20Sopenharmony_ci break; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci case bltzal_op: 1028c2ecf20Sopenharmony_ci case bltzall_op: 1038c2ecf20Sopenharmony_ci arch->gprs[31] = epc + 8; 1048c2ecf20Sopenharmony_ci if ((long)arch->gprs[insn.i_format.rs] < 0) 1058c2ecf20Sopenharmony_ci epc = epc + 4 + (insn.i_format.simmediate << 2); 1068c2ecf20Sopenharmony_ci else 1078c2ecf20Sopenharmony_ci epc += 8; 1088c2ecf20Sopenharmony_ci nextpc = epc; 1098c2ecf20Sopenharmony_ci break; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci case bgezal_op: 1128c2ecf20Sopenharmony_ci case bgezall_op: 1138c2ecf20Sopenharmony_ci arch->gprs[31] = epc + 8; 1148c2ecf20Sopenharmony_ci if ((long)arch->gprs[insn.i_format.rs] >= 0) 1158c2ecf20Sopenharmony_ci epc = epc + 4 + (insn.i_format.simmediate << 2); 1168c2ecf20Sopenharmony_ci else 1178c2ecf20Sopenharmony_ci epc += 8; 1188c2ecf20Sopenharmony_ci nextpc = epc; 1198c2ecf20Sopenharmony_ci break; 1208c2ecf20Sopenharmony_ci case bposge32_op: 1218c2ecf20Sopenharmony_ci if (!cpu_has_dsp) { 1228c2ecf20Sopenharmony_ci kvm_err("%s: DSP branch but not DSP ASE\n", 1238c2ecf20Sopenharmony_ci __func__); 1248c2ecf20Sopenharmony_ci return -EINVAL; 1258c2ecf20Sopenharmony_ci } 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci dspcontrol = rddsp(0x01); 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci if (dspcontrol >= 32) 1308c2ecf20Sopenharmony_ci epc = epc + 4 + (insn.i_format.simmediate << 2); 1318c2ecf20Sopenharmony_ci else 1328c2ecf20Sopenharmony_ci epc += 8; 1338c2ecf20Sopenharmony_ci nextpc = epc; 1348c2ecf20Sopenharmony_ci break; 1358c2ecf20Sopenharmony_ci default: 1368c2ecf20Sopenharmony_ci return -EINVAL; 1378c2ecf20Sopenharmony_ci } 1388c2ecf20Sopenharmony_ci break; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci /* These are unconditional and in j_format. */ 1418c2ecf20Sopenharmony_ci case jal_op: 1428c2ecf20Sopenharmony_ci arch->gprs[31] = instpc + 8; 1438c2ecf20Sopenharmony_ci fallthrough; 1448c2ecf20Sopenharmony_ci case j_op: 1458c2ecf20Sopenharmony_ci epc += 4; 1468c2ecf20Sopenharmony_ci epc >>= 28; 1478c2ecf20Sopenharmony_ci epc <<= 28; 1488c2ecf20Sopenharmony_ci epc |= (insn.j_format.target << 2); 1498c2ecf20Sopenharmony_ci nextpc = epc; 1508c2ecf20Sopenharmony_ci break; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci /* These are conditional and in i_format. */ 1538c2ecf20Sopenharmony_ci case beq_op: 1548c2ecf20Sopenharmony_ci case beql_op: 1558c2ecf20Sopenharmony_ci if (arch->gprs[insn.i_format.rs] == 1568c2ecf20Sopenharmony_ci arch->gprs[insn.i_format.rt]) 1578c2ecf20Sopenharmony_ci epc = epc + 4 + (insn.i_format.simmediate << 2); 1588c2ecf20Sopenharmony_ci else 1598c2ecf20Sopenharmony_ci epc += 8; 1608c2ecf20Sopenharmony_ci nextpc = epc; 1618c2ecf20Sopenharmony_ci break; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci case bne_op: 1648c2ecf20Sopenharmony_ci case bnel_op: 1658c2ecf20Sopenharmony_ci if (arch->gprs[insn.i_format.rs] != 1668c2ecf20Sopenharmony_ci arch->gprs[insn.i_format.rt]) 1678c2ecf20Sopenharmony_ci epc = epc + 4 + (insn.i_format.simmediate << 2); 1688c2ecf20Sopenharmony_ci else 1698c2ecf20Sopenharmony_ci epc += 8; 1708c2ecf20Sopenharmony_ci nextpc = epc; 1718c2ecf20Sopenharmony_ci break; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci case blez_op: /* POP06 */ 1748c2ecf20Sopenharmony_ci#ifndef CONFIG_CPU_MIPSR6 1758c2ecf20Sopenharmony_ci case blezl_op: /* removed in R6 */ 1768c2ecf20Sopenharmony_ci#endif 1778c2ecf20Sopenharmony_ci if (insn.i_format.rt != 0) 1788c2ecf20Sopenharmony_ci goto compact_branch; 1798c2ecf20Sopenharmony_ci if ((long)arch->gprs[insn.i_format.rs] <= 0) 1808c2ecf20Sopenharmony_ci epc = epc + 4 + (insn.i_format.simmediate << 2); 1818c2ecf20Sopenharmony_ci else 1828c2ecf20Sopenharmony_ci epc += 8; 1838c2ecf20Sopenharmony_ci nextpc = epc; 1848c2ecf20Sopenharmony_ci break; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci case bgtz_op: /* POP07 */ 1878c2ecf20Sopenharmony_ci#ifndef CONFIG_CPU_MIPSR6 1888c2ecf20Sopenharmony_ci case bgtzl_op: /* removed in R6 */ 1898c2ecf20Sopenharmony_ci#endif 1908c2ecf20Sopenharmony_ci if (insn.i_format.rt != 0) 1918c2ecf20Sopenharmony_ci goto compact_branch; 1928c2ecf20Sopenharmony_ci if ((long)arch->gprs[insn.i_format.rs] > 0) 1938c2ecf20Sopenharmony_ci epc = epc + 4 + (insn.i_format.simmediate << 2); 1948c2ecf20Sopenharmony_ci else 1958c2ecf20Sopenharmony_ci epc += 8; 1968c2ecf20Sopenharmony_ci nextpc = epc; 1978c2ecf20Sopenharmony_ci break; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci /* And now the FPA/cp1 branch instructions. */ 2008c2ecf20Sopenharmony_ci case cop1_op: 2018c2ecf20Sopenharmony_ci kvm_err("%s: unsupported cop1_op\n", __func__); 2028c2ecf20Sopenharmony_ci return -EINVAL; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci#ifdef CONFIG_CPU_MIPSR6 2058c2ecf20Sopenharmony_ci /* R6 added the following compact branches with forbidden slots */ 2068c2ecf20Sopenharmony_ci case blezl_op: /* POP26 */ 2078c2ecf20Sopenharmony_ci case bgtzl_op: /* POP27 */ 2088c2ecf20Sopenharmony_ci /* only rt == 0 isn't compact branch */ 2098c2ecf20Sopenharmony_ci if (insn.i_format.rt != 0) 2108c2ecf20Sopenharmony_ci goto compact_branch; 2118c2ecf20Sopenharmony_ci return -EINVAL; 2128c2ecf20Sopenharmony_ci case pop10_op: 2138c2ecf20Sopenharmony_ci case pop30_op: 2148c2ecf20Sopenharmony_ci /* only rs == rt == 0 is reserved, rest are compact branches */ 2158c2ecf20Sopenharmony_ci if (insn.i_format.rs != 0 || insn.i_format.rt != 0) 2168c2ecf20Sopenharmony_ci goto compact_branch; 2178c2ecf20Sopenharmony_ci return -EINVAL; 2188c2ecf20Sopenharmony_ci case pop66_op: 2198c2ecf20Sopenharmony_ci case pop76_op: 2208c2ecf20Sopenharmony_ci /* only rs == 0 isn't compact branch */ 2218c2ecf20Sopenharmony_ci if (insn.i_format.rs != 0) 2228c2ecf20Sopenharmony_ci goto compact_branch; 2238c2ecf20Sopenharmony_ci return -EINVAL; 2248c2ecf20Sopenharmony_cicompact_branch: 2258c2ecf20Sopenharmony_ci /* 2268c2ecf20Sopenharmony_ci * If we've hit an exception on the forbidden slot, then 2278c2ecf20Sopenharmony_ci * the branch must not have been taken. 2288c2ecf20Sopenharmony_ci */ 2298c2ecf20Sopenharmony_ci epc += 8; 2308c2ecf20Sopenharmony_ci nextpc = epc; 2318c2ecf20Sopenharmony_ci break; 2328c2ecf20Sopenharmony_ci#else 2338c2ecf20Sopenharmony_cicompact_branch: 2348c2ecf20Sopenharmony_ci /* Fall through - Compact branches not supported before R6 */ 2358c2ecf20Sopenharmony_ci#endif 2368c2ecf20Sopenharmony_ci default: 2378c2ecf20Sopenharmony_ci return -EINVAL; 2388c2ecf20Sopenharmony_ci } 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci *out = nextpc; 2418c2ecf20Sopenharmony_ci return 0; 2428c2ecf20Sopenharmony_ci} 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_cienum emulation_result update_pc(struct kvm_vcpu *vcpu, u32 cause) 2458c2ecf20Sopenharmony_ci{ 2468c2ecf20Sopenharmony_ci int err; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci if (cause & CAUSEF_BD) { 2498c2ecf20Sopenharmony_ci err = kvm_compute_return_epc(vcpu, vcpu->arch.pc, 2508c2ecf20Sopenharmony_ci &vcpu->arch.pc); 2518c2ecf20Sopenharmony_ci if (err) 2528c2ecf20Sopenharmony_ci return EMULATE_FAIL; 2538c2ecf20Sopenharmony_ci } else { 2548c2ecf20Sopenharmony_ci vcpu->arch.pc += 4; 2558c2ecf20Sopenharmony_ci } 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci kvm_debug("update_pc(): New PC: %#lx\n", vcpu->arch.pc); 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci return EMULATE_DONE; 2608c2ecf20Sopenharmony_ci} 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci/** 2638c2ecf20Sopenharmony_ci * kvm_get_badinstr() - Get bad instruction encoding. 2648c2ecf20Sopenharmony_ci * @opc: Guest pointer to faulting instruction. 2658c2ecf20Sopenharmony_ci * @vcpu: KVM VCPU information. 2668c2ecf20Sopenharmony_ci * 2678c2ecf20Sopenharmony_ci * Gets the instruction encoding of the faulting instruction, using the saved 2688c2ecf20Sopenharmony_ci * BadInstr register value if it exists, otherwise falling back to reading guest 2698c2ecf20Sopenharmony_ci * memory at @opc. 2708c2ecf20Sopenharmony_ci * 2718c2ecf20Sopenharmony_ci * Returns: The instruction encoding of the faulting instruction. 2728c2ecf20Sopenharmony_ci */ 2738c2ecf20Sopenharmony_ciint kvm_get_badinstr(u32 *opc, struct kvm_vcpu *vcpu, u32 *out) 2748c2ecf20Sopenharmony_ci{ 2758c2ecf20Sopenharmony_ci if (cpu_has_badinstr) { 2768c2ecf20Sopenharmony_ci *out = vcpu->arch.host_cp0_badinstr; 2778c2ecf20Sopenharmony_ci return 0; 2788c2ecf20Sopenharmony_ci } else { 2798c2ecf20Sopenharmony_ci return kvm_get_inst(opc, vcpu, out); 2808c2ecf20Sopenharmony_ci } 2818c2ecf20Sopenharmony_ci} 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci/** 2848c2ecf20Sopenharmony_ci * kvm_get_badinstrp() - Get bad prior instruction encoding. 2858c2ecf20Sopenharmony_ci * @opc: Guest pointer to prior faulting instruction. 2868c2ecf20Sopenharmony_ci * @vcpu: KVM VCPU information. 2878c2ecf20Sopenharmony_ci * 2888c2ecf20Sopenharmony_ci * Gets the instruction encoding of the prior faulting instruction (the branch 2898c2ecf20Sopenharmony_ci * containing the delay slot which faulted), using the saved BadInstrP register 2908c2ecf20Sopenharmony_ci * value if it exists, otherwise falling back to reading guest memory at @opc. 2918c2ecf20Sopenharmony_ci * 2928c2ecf20Sopenharmony_ci * Returns: The instruction encoding of the prior faulting instruction. 2938c2ecf20Sopenharmony_ci */ 2948c2ecf20Sopenharmony_ciint kvm_get_badinstrp(u32 *opc, struct kvm_vcpu *vcpu, u32 *out) 2958c2ecf20Sopenharmony_ci{ 2968c2ecf20Sopenharmony_ci if (cpu_has_badinstrp) { 2978c2ecf20Sopenharmony_ci *out = vcpu->arch.host_cp0_badinstrp; 2988c2ecf20Sopenharmony_ci return 0; 2998c2ecf20Sopenharmony_ci } else { 3008c2ecf20Sopenharmony_ci return kvm_get_inst(opc, vcpu, out); 3018c2ecf20Sopenharmony_ci } 3028c2ecf20Sopenharmony_ci} 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci/** 3058c2ecf20Sopenharmony_ci * kvm_mips_count_disabled() - Find whether the CP0_Count timer is disabled. 3068c2ecf20Sopenharmony_ci * @vcpu: Virtual CPU. 3078c2ecf20Sopenharmony_ci * 3088c2ecf20Sopenharmony_ci * Returns: 1 if the CP0_Count timer is disabled by either the guest 3098c2ecf20Sopenharmony_ci * CP0_Cause.DC bit or the count_ctl.DC bit. 3108c2ecf20Sopenharmony_ci * 0 otherwise (in which case CP0_Count timer is running). 3118c2ecf20Sopenharmony_ci */ 3128c2ecf20Sopenharmony_ciint kvm_mips_count_disabled(struct kvm_vcpu *vcpu) 3138c2ecf20Sopenharmony_ci{ 3148c2ecf20Sopenharmony_ci struct mips_coproc *cop0 = vcpu->arch.cop0; 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci return (vcpu->arch.count_ctl & KVM_REG_MIPS_COUNT_CTL_DC) || 3178c2ecf20Sopenharmony_ci (kvm_read_c0_guest_cause(cop0) & CAUSEF_DC); 3188c2ecf20Sopenharmony_ci} 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci/** 3218c2ecf20Sopenharmony_ci * kvm_mips_ktime_to_count() - Scale ktime_t to a 32-bit count. 3228c2ecf20Sopenharmony_ci * 3238c2ecf20Sopenharmony_ci * Caches the dynamic nanosecond bias in vcpu->arch.count_dyn_bias. 3248c2ecf20Sopenharmony_ci * 3258c2ecf20Sopenharmony_ci * Assumes !kvm_mips_count_disabled(@vcpu) (guest CP0_Count timer is running). 3268c2ecf20Sopenharmony_ci */ 3278c2ecf20Sopenharmony_cistatic u32 kvm_mips_ktime_to_count(struct kvm_vcpu *vcpu, ktime_t now) 3288c2ecf20Sopenharmony_ci{ 3298c2ecf20Sopenharmony_ci s64 now_ns, periods; 3308c2ecf20Sopenharmony_ci u64 delta; 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci now_ns = ktime_to_ns(now); 3338c2ecf20Sopenharmony_ci delta = now_ns + vcpu->arch.count_dyn_bias; 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci if (delta >= vcpu->arch.count_period) { 3368c2ecf20Sopenharmony_ci /* If delta is out of safe range the bias needs adjusting */ 3378c2ecf20Sopenharmony_ci periods = div64_s64(now_ns, vcpu->arch.count_period); 3388c2ecf20Sopenharmony_ci vcpu->arch.count_dyn_bias = -periods * vcpu->arch.count_period; 3398c2ecf20Sopenharmony_ci /* Recalculate delta with new bias */ 3408c2ecf20Sopenharmony_ci delta = now_ns + vcpu->arch.count_dyn_bias; 3418c2ecf20Sopenharmony_ci } 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci /* 3448c2ecf20Sopenharmony_ci * We've ensured that: 3458c2ecf20Sopenharmony_ci * delta < count_period 3468c2ecf20Sopenharmony_ci * 3478c2ecf20Sopenharmony_ci * Therefore the intermediate delta*count_hz will never overflow since 3488c2ecf20Sopenharmony_ci * at the boundary condition: 3498c2ecf20Sopenharmony_ci * delta = count_period 3508c2ecf20Sopenharmony_ci * delta = NSEC_PER_SEC * 2^32 / count_hz 3518c2ecf20Sopenharmony_ci * delta * count_hz = NSEC_PER_SEC * 2^32 3528c2ecf20Sopenharmony_ci */ 3538c2ecf20Sopenharmony_ci return div_u64(delta * vcpu->arch.count_hz, NSEC_PER_SEC); 3548c2ecf20Sopenharmony_ci} 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci/** 3578c2ecf20Sopenharmony_ci * kvm_mips_count_time() - Get effective current time. 3588c2ecf20Sopenharmony_ci * @vcpu: Virtual CPU. 3598c2ecf20Sopenharmony_ci * 3608c2ecf20Sopenharmony_ci * Get effective monotonic ktime. This is usually a straightforward ktime_get(), 3618c2ecf20Sopenharmony_ci * except when the master disable bit is set in count_ctl, in which case it is 3628c2ecf20Sopenharmony_ci * count_resume, i.e. the time that the count was disabled. 3638c2ecf20Sopenharmony_ci * 3648c2ecf20Sopenharmony_ci * Returns: Effective monotonic ktime for CP0_Count. 3658c2ecf20Sopenharmony_ci */ 3668c2ecf20Sopenharmony_cistatic inline ktime_t kvm_mips_count_time(struct kvm_vcpu *vcpu) 3678c2ecf20Sopenharmony_ci{ 3688c2ecf20Sopenharmony_ci if (unlikely(vcpu->arch.count_ctl & KVM_REG_MIPS_COUNT_CTL_DC)) 3698c2ecf20Sopenharmony_ci return vcpu->arch.count_resume; 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci return ktime_get(); 3728c2ecf20Sopenharmony_ci} 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci/** 3758c2ecf20Sopenharmony_ci * kvm_mips_read_count_running() - Read the current count value as if running. 3768c2ecf20Sopenharmony_ci * @vcpu: Virtual CPU. 3778c2ecf20Sopenharmony_ci * @now: Kernel time to read CP0_Count at. 3788c2ecf20Sopenharmony_ci * 3798c2ecf20Sopenharmony_ci * Returns the current guest CP0_Count register at time @now and handles if the 3808c2ecf20Sopenharmony_ci * timer interrupt is pending and hasn't been handled yet. 3818c2ecf20Sopenharmony_ci * 3828c2ecf20Sopenharmony_ci * Returns: The current value of the guest CP0_Count register. 3838c2ecf20Sopenharmony_ci */ 3848c2ecf20Sopenharmony_cistatic u32 kvm_mips_read_count_running(struct kvm_vcpu *vcpu, ktime_t now) 3858c2ecf20Sopenharmony_ci{ 3868c2ecf20Sopenharmony_ci struct mips_coproc *cop0 = vcpu->arch.cop0; 3878c2ecf20Sopenharmony_ci ktime_t expires, threshold; 3888c2ecf20Sopenharmony_ci u32 count, compare; 3898c2ecf20Sopenharmony_ci int running; 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci /* Calculate the biased and scaled guest CP0_Count */ 3928c2ecf20Sopenharmony_ci count = vcpu->arch.count_bias + kvm_mips_ktime_to_count(vcpu, now); 3938c2ecf20Sopenharmony_ci compare = kvm_read_c0_guest_compare(cop0); 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci /* 3968c2ecf20Sopenharmony_ci * Find whether CP0_Count has reached the closest timer interrupt. If 3978c2ecf20Sopenharmony_ci * not, we shouldn't inject it. 3988c2ecf20Sopenharmony_ci */ 3998c2ecf20Sopenharmony_ci if ((s32)(count - compare) < 0) 4008c2ecf20Sopenharmony_ci return count; 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci /* 4038c2ecf20Sopenharmony_ci * The CP0_Count we're going to return has already reached the closest 4048c2ecf20Sopenharmony_ci * timer interrupt. Quickly check if it really is a new interrupt by 4058c2ecf20Sopenharmony_ci * looking at whether the interval until the hrtimer expiry time is 4068c2ecf20Sopenharmony_ci * less than 1/4 of the timer period. 4078c2ecf20Sopenharmony_ci */ 4088c2ecf20Sopenharmony_ci expires = hrtimer_get_expires(&vcpu->arch.comparecount_timer); 4098c2ecf20Sopenharmony_ci threshold = ktime_add_ns(now, vcpu->arch.count_period / 4); 4108c2ecf20Sopenharmony_ci if (ktime_before(expires, threshold)) { 4118c2ecf20Sopenharmony_ci /* 4128c2ecf20Sopenharmony_ci * Cancel it while we handle it so there's no chance of 4138c2ecf20Sopenharmony_ci * interference with the timeout handler. 4148c2ecf20Sopenharmony_ci */ 4158c2ecf20Sopenharmony_ci running = hrtimer_cancel(&vcpu->arch.comparecount_timer); 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci /* Nothing should be waiting on the timeout */ 4188c2ecf20Sopenharmony_ci kvm_mips_callbacks->queue_timer_int(vcpu); 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci /* 4218c2ecf20Sopenharmony_ci * Restart the timer if it was running based on the expiry time 4228c2ecf20Sopenharmony_ci * we read, so that we don't push it back 2 periods. 4238c2ecf20Sopenharmony_ci */ 4248c2ecf20Sopenharmony_ci if (running) { 4258c2ecf20Sopenharmony_ci expires = ktime_add_ns(expires, 4268c2ecf20Sopenharmony_ci vcpu->arch.count_period); 4278c2ecf20Sopenharmony_ci hrtimer_start(&vcpu->arch.comparecount_timer, expires, 4288c2ecf20Sopenharmony_ci HRTIMER_MODE_ABS); 4298c2ecf20Sopenharmony_ci } 4308c2ecf20Sopenharmony_ci } 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci return count; 4338c2ecf20Sopenharmony_ci} 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci/** 4368c2ecf20Sopenharmony_ci * kvm_mips_read_count() - Read the current count value. 4378c2ecf20Sopenharmony_ci * @vcpu: Virtual CPU. 4388c2ecf20Sopenharmony_ci * 4398c2ecf20Sopenharmony_ci * Read the current guest CP0_Count value, taking into account whether the timer 4408c2ecf20Sopenharmony_ci * is stopped. 4418c2ecf20Sopenharmony_ci * 4428c2ecf20Sopenharmony_ci * Returns: The current guest CP0_Count value. 4438c2ecf20Sopenharmony_ci */ 4448c2ecf20Sopenharmony_ciu32 kvm_mips_read_count(struct kvm_vcpu *vcpu) 4458c2ecf20Sopenharmony_ci{ 4468c2ecf20Sopenharmony_ci struct mips_coproc *cop0 = vcpu->arch.cop0; 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci /* If count disabled just read static copy of count */ 4498c2ecf20Sopenharmony_ci if (kvm_mips_count_disabled(vcpu)) 4508c2ecf20Sopenharmony_ci return kvm_read_c0_guest_count(cop0); 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci return kvm_mips_read_count_running(vcpu, ktime_get()); 4538c2ecf20Sopenharmony_ci} 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci/** 4568c2ecf20Sopenharmony_ci * kvm_mips_freeze_hrtimer() - Safely stop the hrtimer. 4578c2ecf20Sopenharmony_ci * @vcpu: Virtual CPU. 4588c2ecf20Sopenharmony_ci * @count: Output pointer for CP0_Count value at point of freeze. 4598c2ecf20Sopenharmony_ci * 4608c2ecf20Sopenharmony_ci * Freeze the hrtimer safely and return both the ktime and the CP0_Count value 4618c2ecf20Sopenharmony_ci * at the point it was frozen. It is guaranteed that any pending interrupts at 4628c2ecf20Sopenharmony_ci * the point it was frozen are handled, and none after that point. 4638c2ecf20Sopenharmony_ci * 4648c2ecf20Sopenharmony_ci * This is useful where the time/CP0_Count is needed in the calculation of the 4658c2ecf20Sopenharmony_ci * new parameters. 4668c2ecf20Sopenharmony_ci * 4678c2ecf20Sopenharmony_ci * Assumes !kvm_mips_count_disabled(@vcpu) (guest CP0_Count timer is running). 4688c2ecf20Sopenharmony_ci * 4698c2ecf20Sopenharmony_ci * Returns: The ktime at the point of freeze. 4708c2ecf20Sopenharmony_ci */ 4718c2ecf20Sopenharmony_ciktime_t kvm_mips_freeze_hrtimer(struct kvm_vcpu *vcpu, u32 *count) 4728c2ecf20Sopenharmony_ci{ 4738c2ecf20Sopenharmony_ci ktime_t now; 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci /* stop hrtimer before finding time */ 4768c2ecf20Sopenharmony_ci hrtimer_cancel(&vcpu->arch.comparecount_timer); 4778c2ecf20Sopenharmony_ci now = ktime_get(); 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci /* find count at this point and handle pending hrtimer */ 4808c2ecf20Sopenharmony_ci *count = kvm_mips_read_count_running(vcpu, now); 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci return now; 4838c2ecf20Sopenharmony_ci} 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci/** 4868c2ecf20Sopenharmony_ci * kvm_mips_resume_hrtimer() - Resume hrtimer, updating expiry. 4878c2ecf20Sopenharmony_ci * @vcpu: Virtual CPU. 4888c2ecf20Sopenharmony_ci * @now: ktime at point of resume. 4898c2ecf20Sopenharmony_ci * @count: CP0_Count at point of resume. 4908c2ecf20Sopenharmony_ci * 4918c2ecf20Sopenharmony_ci * Resumes the timer and updates the timer expiry based on @now and @count. 4928c2ecf20Sopenharmony_ci * This can be used in conjunction with kvm_mips_freeze_timer() when timer 4938c2ecf20Sopenharmony_ci * parameters need to be changed. 4948c2ecf20Sopenharmony_ci * 4958c2ecf20Sopenharmony_ci * It is guaranteed that a timer interrupt immediately after resume will be 4968c2ecf20Sopenharmony_ci * handled, but not if CP_Compare is exactly at @count. That case is already 4978c2ecf20Sopenharmony_ci * handled by kvm_mips_freeze_timer(). 4988c2ecf20Sopenharmony_ci * 4998c2ecf20Sopenharmony_ci * Assumes !kvm_mips_count_disabled(@vcpu) (guest CP0_Count timer is running). 5008c2ecf20Sopenharmony_ci */ 5018c2ecf20Sopenharmony_cistatic void kvm_mips_resume_hrtimer(struct kvm_vcpu *vcpu, 5028c2ecf20Sopenharmony_ci ktime_t now, u32 count) 5038c2ecf20Sopenharmony_ci{ 5048c2ecf20Sopenharmony_ci struct mips_coproc *cop0 = vcpu->arch.cop0; 5058c2ecf20Sopenharmony_ci u32 compare; 5068c2ecf20Sopenharmony_ci u64 delta; 5078c2ecf20Sopenharmony_ci ktime_t expire; 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci /* Calculate timeout (wrap 0 to 2^32) */ 5108c2ecf20Sopenharmony_ci compare = kvm_read_c0_guest_compare(cop0); 5118c2ecf20Sopenharmony_ci delta = (u64)(u32)(compare - count - 1) + 1; 5128c2ecf20Sopenharmony_ci delta = div_u64(delta * NSEC_PER_SEC, vcpu->arch.count_hz); 5138c2ecf20Sopenharmony_ci expire = ktime_add_ns(now, delta); 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci /* Update hrtimer to use new timeout */ 5168c2ecf20Sopenharmony_ci hrtimer_cancel(&vcpu->arch.comparecount_timer); 5178c2ecf20Sopenharmony_ci hrtimer_start(&vcpu->arch.comparecount_timer, expire, HRTIMER_MODE_ABS); 5188c2ecf20Sopenharmony_ci} 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci/** 5218c2ecf20Sopenharmony_ci * kvm_mips_restore_hrtimer() - Restore hrtimer after a gap, updating expiry. 5228c2ecf20Sopenharmony_ci * @vcpu: Virtual CPU. 5238c2ecf20Sopenharmony_ci * @before: Time before Count was saved, lower bound of drift calculation. 5248c2ecf20Sopenharmony_ci * @count: CP0_Count at point of restore. 5258c2ecf20Sopenharmony_ci * @min_drift: Minimum amount of drift permitted before correction. 5268c2ecf20Sopenharmony_ci * Must be <= 0. 5278c2ecf20Sopenharmony_ci * 5288c2ecf20Sopenharmony_ci * Restores the timer from a particular @count, accounting for drift. This can 5298c2ecf20Sopenharmony_ci * be used in conjunction with kvm_mips_freeze_timer() when a hardware timer is 5308c2ecf20Sopenharmony_ci * to be used for a period of time, but the exact ktime corresponding to the 5318c2ecf20Sopenharmony_ci * final Count that must be restored is not known. 5328c2ecf20Sopenharmony_ci * 5338c2ecf20Sopenharmony_ci * It is gauranteed that a timer interrupt immediately after restore will be 5348c2ecf20Sopenharmony_ci * handled, but not if CP0_Compare is exactly at @count. That case should 5358c2ecf20Sopenharmony_ci * already be handled when the hardware timer state is saved. 5368c2ecf20Sopenharmony_ci * 5378c2ecf20Sopenharmony_ci * Assumes !kvm_mips_count_disabled(@vcpu) (guest CP0_Count timer is not 5388c2ecf20Sopenharmony_ci * stopped). 5398c2ecf20Sopenharmony_ci * 5408c2ecf20Sopenharmony_ci * Returns: Amount of correction to count_bias due to drift. 5418c2ecf20Sopenharmony_ci */ 5428c2ecf20Sopenharmony_ciint kvm_mips_restore_hrtimer(struct kvm_vcpu *vcpu, ktime_t before, 5438c2ecf20Sopenharmony_ci u32 count, int min_drift) 5448c2ecf20Sopenharmony_ci{ 5458c2ecf20Sopenharmony_ci ktime_t now, count_time; 5468c2ecf20Sopenharmony_ci u32 now_count, before_count; 5478c2ecf20Sopenharmony_ci u64 delta; 5488c2ecf20Sopenharmony_ci int drift, ret = 0; 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci /* Calculate expected count at before */ 5518c2ecf20Sopenharmony_ci before_count = vcpu->arch.count_bias + 5528c2ecf20Sopenharmony_ci kvm_mips_ktime_to_count(vcpu, before); 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci /* 5558c2ecf20Sopenharmony_ci * Detect significantly negative drift, where count is lower than 5568c2ecf20Sopenharmony_ci * expected. Some negative drift is expected when hardware counter is 5578c2ecf20Sopenharmony_ci * set after kvm_mips_freeze_timer(), and it is harmless to allow the 5588c2ecf20Sopenharmony_ci * time to jump forwards a little, within reason. If the drift is too 5598c2ecf20Sopenharmony_ci * significant, adjust the bias to avoid a big Guest.CP0_Count jump. 5608c2ecf20Sopenharmony_ci */ 5618c2ecf20Sopenharmony_ci drift = count - before_count; 5628c2ecf20Sopenharmony_ci if (drift < min_drift) { 5638c2ecf20Sopenharmony_ci count_time = before; 5648c2ecf20Sopenharmony_ci vcpu->arch.count_bias += drift; 5658c2ecf20Sopenharmony_ci ret = drift; 5668c2ecf20Sopenharmony_ci goto resume; 5678c2ecf20Sopenharmony_ci } 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci /* Calculate expected count right now */ 5708c2ecf20Sopenharmony_ci now = ktime_get(); 5718c2ecf20Sopenharmony_ci now_count = vcpu->arch.count_bias + kvm_mips_ktime_to_count(vcpu, now); 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci /* 5748c2ecf20Sopenharmony_ci * Detect positive drift, where count is higher than expected, and 5758c2ecf20Sopenharmony_ci * adjust the bias to avoid guest time going backwards. 5768c2ecf20Sopenharmony_ci */ 5778c2ecf20Sopenharmony_ci drift = count - now_count; 5788c2ecf20Sopenharmony_ci if (drift > 0) { 5798c2ecf20Sopenharmony_ci count_time = now; 5808c2ecf20Sopenharmony_ci vcpu->arch.count_bias += drift; 5818c2ecf20Sopenharmony_ci ret = drift; 5828c2ecf20Sopenharmony_ci goto resume; 5838c2ecf20Sopenharmony_ci } 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci /* Subtract nanosecond delta to find ktime when count was read */ 5868c2ecf20Sopenharmony_ci delta = (u64)(u32)(now_count - count); 5878c2ecf20Sopenharmony_ci delta = div_u64(delta * NSEC_PER_SEC, vcpu->arch.count_hz); 5888c2ecf20Sopenharmony_ci count_time = ktime_sub_ns(now, delta); 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ciresume: 5918c2ecf20Sopenharmony_ci /* Resume using the calculated ktime */ 5928c2ecf20Sopenharmony_ci kvm_mips_resume_hrtimer(vcpu, count_time, count); 5938c2ecf20Sopenharmony_ci return ret; 5948c2ecf20Sopenharmony_ci} 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ci/** 5978c2ecf20Sopenharmony_ci * kvm_mips_write_count() - Modify the count and update timer. 5988c2ecf20Sopenharmony_ci * @vcpu: Virtual CPU. 5998c2ecf20Sopenharmony_ci * @count: Guest CP0_Count value to set. 6008c2ecf20Sopenharmony_ci * 6018c2ecf20Sopenharmony_ci * Sets the CP0_Count value and updates the timer accordingly. 6028c2ecf20Sopenharmony_ci */ 6038c2ecf20Sopenharmony_civoid kvm_mips_write_count(struct kvm_vcpu *vcpu, u32 count) 6048c2ecf20Sopenharmony_ci{ 6058c2ecf20Sopenharmony_ci struct mips_coproc *cop0 = vcpu->arch.cop0; 6068c2ecf20Sopenharmony_ci ktime_t now; 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci /* Calculate bias */ 6098c2ecf20Sopenharmony_ci now = kvm_mips_count_time(vcpu); 6108c2ecf20Sopenharmony_ci vcpu->arch.count_bias = count - kvm_mips_ktime_to_count(vcpu, now); 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci if (kvm_mips_count_disabled(vcpu)) 6138c2ecf20Sopenharmony_ci /* The timer's disabled, adjust the static count */ 6148c2ecf20Sopenharmony_ci kvm_write_c0_guest_count(cop0, count); 6158c2ecf20Sopenharmony_ci else 6168c2ecf20Sopenharmony_ci /* Update timeout */ 6178c2ecf20Sopenharmony_ci kvm_mips_resume_hrtimer(vcpu, now, count); 6188c2ecf20Sopenharmony_ci} 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci/** 6218c2ecf20Sopenharmony_ci * kvm_mips_init_count() - Initialise timer. 6228c2ecf20Sopenharmony_ci * @vcpu: Virtual CPU. 6238c2ecf20Sopenharmony_ci * @count_hz: Frequency of timer. 6248c2ecf20Sopenharmony_ci * 6258c2ecf20Sopenharmony_ci * Initialise the timer to the specified frequency, zero it, and set it going if 6268c2ecf20Sopenharmony_ci * it's enabled. 6278c2ecf20Sopenharmony_ci */ 6288c2ecf20Sopenharmony_civoid kvm_mips_init_count(struct kvm_vcpu *vcpu, unsigned long count_hz) 6298c2ecf20Sopenharmony_ci{ 6308c2ecf20Sopenharmony_ci vcpu->arch.count_hz = count_hz; 6318c2ecf20Sopenharmony_ci vcpu->arch.count_period = div_u64((u64)NSEC_PER_SEC << 32, count_hz); 6328c2ecf20Sopenharmony_ci vcpu->arch.count_dyn_bias = 0; 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci /* Starting at 0 */ 6358c2ecf20Sopenharmony_ci kvm_mips_write_count(vcpu, 0); 6368c2ecf20Sopenharmony_ci} 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci/** 6398c2ecf20Sopenharmony_ci * kvm_mips_set_count_hz() - Update the frequency of the timer. 6408c2ecf20Sopenharmony_ci * @vcpu: Virtual CPU. 6418c2ecf20Sopenharmony_ci * @count_hz: Frequency of CP0_Count timer in Hz. 6428c2ecf20Sopenharmony_ci * 6438c2ecf20Sopenharmony_ci * Change the frequency of the CP0_Count timer. This is done atomically so that 6448c2ecf20Sopenharmony_ci * CP0_Count is continuous and no timer interrupt is lost. 6458c2ecf20Sopenharmony_ci * 6468c2ecf20Sopenharmony_ci * Returns: -EINVAL if @count_hz is out of range. 6478c2ecf20Sopenharmony_ci * 0 on success. 6488c2ecf20Sopenharmony_ci */ 6498c2ecf20Sopenharmony_ciint kvm_mips_set_count_hz(struct kvm_vcpu *vcpu, s64 count_hz) 6508c2ecf20Sopenharmony_ci{ 6518c2ecf20Sopenharmony_ci struct mips_coproc *cop0 = vcpu->arch.cop0; 6528c2ecf20Sopenharmony_ci int dc; 6538c2ecf20Sopenharmony_ci ktime_t now; 6548c2ecf20Sopenharmony_ci u32 count; 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci /* ensure the frequency is in a sensible range... */ 6578c2ecf20Sopenharmony_ci if (count_hz <= 0 || count_hz > NSEC_PER_SEC) 6588c2ecf20Sopenharmony_ci return -EINVAL; 6598c2ecf20Sopenharmony_ci /* ... and has actually changed */ 6608c2ecf20Sopenharmony_ci if (vcpu->arch.count_hz == count_hz) 6618c2ecf20Sopenharmony_ci return 0; 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_ci /* Safely freeze timer so we can keep it continuous */ 6648c2ecf20Sopenharmony_ci dc = kvm_mips_count_disabled(vcpu); 6658c2ecf20Sopenharmony_ci if (dc) { 6668c2ecf20Sopenharmony_ci now = kvm_mips_count_time(vcpu); 6678c2ecf20Sopenharmony_ci count = kvm_read_c0_guest_count(cop0); 6688c2ecf20Sopenharmony_ci } else { 6698c2ecf20Sopenharmony_ci now = kvm_mips_freeze_hrtimer(vcpu, &count); 6708c2ecf20Sopenharmony_ci } 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci /* Update the frequency */ 6738c2ecf20Sopenharmony_ci vcpu->arch.count_hz = count_hz; 6748c2ecf20Sopenharmony_ci vcpu->arch.count_period = div_u64((u64)NSEC_PER_SEC << 32, count_hz); 6758c2ecf20Sopenharmony_ci vcpu->arch.count_dyn_bias = 0; 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci /* Calculate adjusted bias so dynamic count is unchanged */ 6788c2ecf20Sopenharmony_ci vcpu->arch.count_bias = count - kvm_mips_ktime_to_count(vcpu, now); 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci /* Update and resume hrtimer */ 6818c2ecf20Sopenharmony_ci if (!dc) 6828c2ecf20Sopenharmony_ci kvm_mips_resume_hrtimer(vcpu, now, count); 6838c2ecf20Sopenharmony_ci return 0; 6848c2ecf20Sopenharmony_ci} 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_ci/** 6878c2ecf20Sopenharmony_ci * kvm_mips_write_compare() - Modify compare and update timer. 6888c2ecf20Sopenharmony_ci * @vcpu: Virtual CPU. 6898c2ecf20Sopenharmony_ci * @compare: New CP0_Compare value. 6908c2ecf20Sopenharmony_ci * @ack: Whether to acknowledge timer interrupt. 6918c2ecf20Sopenharmony_ci * 6928c2ecf20Sopenharmony_ci * Update CP0_Compare to a new value and update the timeout. 6938c2ecf20Sopenharmony_ci * If @ack, atomically acknowledge any pending timer interrupt, otherwise ensure 6948c2ecf20Sopenharmony_ci * any pending timer interrupt is preserved. 6958c2ecf20Sopenharmony_ci */ 6968c2ecf20Sopenharmony_civoid kvm_mips_write_compare(struct kvm_vcpu *vcpu, u32 compare, bool ack) 6978c2ecf20Sopenharmony_ci{ 6988c2ecf20Sopenharmony_ci struct mips_coproc *cop0 = vcpu->arch.cop0; 6998c2ecf20Sopenharmony_ci int dc; 7008c2ecf20Sopenharmony_ci u32 old_compare = kvm_read_c0_guest_compare(cop0); 7018c2ecf20Sopenharmony_ci s32 delta = compare - old_compare; 7028c2ecf20Sopenharmony_ci u32 cause; 7038c2ecf20Sopenharmony_ci ktime_t now = ktime_set(0, 0); /* silence bogus GCC warning */ 7048c2ecf20Sopenharmony_ci u32 count; 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_ci /* if unchanged, must just be an ack */ 7078c2ecf20Sopenharmony_ci if (old_compare == compare) { 7088c2ecf20Sopenharmony_ci if (!ack) 7098c2ecf20Sopenharmony_ci return; 7108c2ecf20Sopenharmony_ci kvm_mips_callbacks->dequeue_timer_int(vcpu); 7118c2ecf20Sopenharmony_ci kvm_write_c0_guest_compare(cop0, compare); 7128c2ecf20Sopenharmony_ci return; 7138c2ecf20Sopenharmony_ci } 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_ci /* 7168c2ecf20Sopenharmony_ci * If guest CP0_Compare moves forward, CP0_GTOffset should be adjusted 7178c2ecf20Sopenharmony_ci * too to prevent guest CP0_Count hitting guest CP0_Compare. 7188c2ecf20Sopenharmony_ci * 7198c2ecf20Sopenharmony_ci * The new GTOffset corresponds to the new value of CP0_Compare, and is 7208c2ecf20Sopenharmony_ci * set prior to it being written into the guest context. We disable 7218c2ecf20Sopenharmony_ci * preemption until the new value is written to prevent restore of a 7228c2ecf20Sopenharmony_ci * GTOffset corresponding to the old CP0_Compare value. 7238c2ecf20Sopenharmony_ci */ 7248c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_KVM_MIPS_VZ) && delta > 0) { 7258c2ecf20Sopenharmony_ci preempt_disable(); 7268c2ecf20Sopenharmony_ci write_c0_gtoffset(compare - read_c0_count()); 7278c2ecf20Sopenharmony_ci back_to_back_c0_hazard(); 7288c2ecf20Sopenharmony_ci } 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci /* freeze_hrtimer() takes care of timer interrupts <= count */ 7318c2ecf20Sopenharmony_ci dc = kvm_mips_count_disabled(vcpu); 7328c2ecf20Sopenharmony_ci if (!dc) 7338c2ecf20Sopenharmony_ci now = kvm_mips_freeze_hrtimer(vcpu, &count); 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_ci if (ack) 7368c2ecf20Sopenharmony_ci kvm_mips_callbacks->dequeue_timer_int(vcpu); 7378c2ecf20Sopenharmony_ci else if (IS_ENABLED(CONFIG_KVM_MIPS_VZ)) 7388c2ecf20Sopenharmony_ci /* 7398c2ecf20Sopenharmony_ci * With VZ, writing CP0_Compare acks (clears) CP0_Cause.TI, so 7408c2ecf20Sopenharmony_ci * preserve guest CP0_Cause.TI if we don't want to ack it. 7418c2ecf20Sopenharmony_ci */ 7428c2ecf20Sopenharmony_ci cause = kvm_read_c0_guest_cause(cop0); 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci kvm_write_c0_guest_compare(cop0, compare); 7458c2ecf20Sopenharmony_ci 7468c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_KVM_MIPS_VZ)) { 7478c2ecf20Sopenharmony_ci if (delta > 0) 7488c2ecf20Sopenharmony_ci preempt_enable(); 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_ci back_to_back_c0_hazard(); 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_ci if (!ack && cause & CAUSEF_TI) 7538c2ecf20Sopenharmony_ci kvm_write_c0_guest_cause(cop0, cause); 7548c2ecf20Sopenharmony_ci } 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_ci /* resume_hrtimer() takes care of timer interrupts > count */ 7578c2ecf20Sopenharmony_ci if (!dc) 7588c2ecf20Sopenharmony_ci kvm_mips_resume_hrtimer(vcpu, now, count); 7598c2ecf20Sopenharmony_ci 7608c2ecf20Sopenharmony_ci /* 7618c2ecf20Sopenharmony_ci * If guest CP0_Compare is moving backward, we delay CP0_GTOffset change 7628c2ecf20Sopenharmony_ci * until after the new CP0_Compare is written, otherwise new guest 7638c2ecf20Sopenharmony_ci * CP0_Count could hit new guest CP0_Compare. 7648c2ecf20Sopenharmony_ci */ 7658c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_KVM_MIPS_VZ) && delta <= 0) 7668c2ecf20Sopenharmony_ci write_c0_gtoffset(compare - read_c0_count()); 7678c2ecf20Sopenharmony_ci} 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_ci/** 7708c2ecf20Sopenharmony_ci * kvm_mips_count_disable() - Disable count. 7718c2ecf20Sopenharmony_ci * @vcpu: Virtual CPU. 7728c2ecf20Sopenharmony_ci * 7738c2ecf20Sopenharmony_ci * Disable the CP0_Count timer. A timer interrupt on or before the final stop 7748c2ecf20Sopenharmony_ci * time will be handled but not after. 7758c2ecf20Sopenharmony_ci * 7768c2ecf20Sopenharmony_ci * Assumes CP0_Count was previously enabled but now Guest.CP0_Cause.DC or 7778c2ecf20Sopenharmony_ci * count_ctl.DC has been set (count disabled). 7788c2ecf20Sopenharmony_ci * 7798c2ecf20Sopenharmony_ci * Returns: The time that the timer was stopped. 7808c2ecf20Sopenharmony_ci */ 7818c2ecf20Sopenharmony_cistatic ktime_t kvm_mips_count_disable(struct kvm_vcpu *vcpu) 7828c2ecf20Sopenharmony_ci{ 7838c2ecf20Sopenharmony_ci struct mips_coproc *cop0 = vcpu->arch.cop0; 7848c2ecf20Sopenharmony_ci u32 count; 7858c2ecf20Sopenharmony_ci ktime_t now; 7868c2ecf20Sopenharmony_ci 7878c2ecf20Sopenharmony_ci /* Stop hrtimer */ 7888c2ecf20Sopenharmony_ci hrtimer_cancel(&vcpu->arch.comparecount_timer); 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_ci /* Set the static count from the dynamic count, handling pending TI */ 7918c2ecf20Sopenharmony_ci now = ktime_get(); 7928c2ecf20Sopenharmony_ci count = kvm_mips_read_count_running(vcpu, now); 7938c2ecf20Sopenharmony_ci kvm_write_c0_guest_count(cop0, count); 7948c2ecf20Sopenharmony_ci 7958c2ecf20Sopenharmony_ci return now; 7968c2ecf20Sopenharmony_ci} 7978c2ecf20Sopenharmony_ci 7988c2ecf20Sopenharmony_ci/** 7998c2ecf20Sopenharmony_ci * kvm_mips_count_disable_cause() - Disable count using CP0_Cause.DC. 8008c2ecf20Sopenharmony_ci * @vcpu: Virtual CPU. 8018c2ecf20Sopenharmony_ci * 8028c2ecf20Sopenharmony_ci * Disable the CP0_Count timer and set CP0_Cause.DC. A timer interrupt on or 8038c2ecf20Sopenharmony_ci * before the final stop time will be handled if the timer isn't disabled by 8048c2ecf20Sopenharmony_ci * count_ctl.DC, but not after. 8058c2ecf20Sopenharmony_ci * 8068c2ecf20Sopenharmony_ci * Assumes CP0_Cause.DC is clear (count enabled). 8078c2ecf20Sopenharmony_ci */ 8088c2ecf20Sopenharmony_civoid kvm_mips_count_disable_cause(struct kvm_vcpu *vcpu) 8098c2ecf20Sopenharmony_ci{ 8108c2ecf20Sopenharmony_ci struct mips_coproc *cop0 = vcpu->arch.cop0; 8118c2ecf20Sopenharmony_ci 8128c2ecf20Sopenharmony_ci kvm_set_c0_guest_cause(cop0, CAUSEF_DC); 8138c2ecf20Sopenharmony_ci if (!(vcpu->arch.count_ctl & KVM_REG_MIPS_COUNT_CTL_DC)) 8148c2ecf20Sopenharmony_ci kvm_mips_count_disable(vcpu); 8158c2ecf20Sopenharmony_ci} 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_ci/** 8188c2ecf20Sopenharmony_ci * kvm_mips_count_enable_cause() - Enable count using CP0_Cause.DC. 8198c2ecf20Sopenharmony_ci * @vcpu: Virtual CPU. 8208c2ecf20Sopenharmony_ci * 8218c2ecf20Sopenharmony_ci * Enable the CP0_Count timer and clear CP0_Cause.DC. A timer interrupt after 8228c2ecf20Sopenharmony_ci * the start time will be handled if the timer isn't disabled by count_ctl.DC, 8238c2ecf20Sopenharmony_ci * potentially before even returning, so the caller should be careful with 8248c2ecf20Sopenharmony_ci * ordering of CP0_Cause modifications so as not to lose it. 8258c2ecf20Sopenharmony_ci * 8268c2ecf20Sopenharmony_ci * Assumes CP0_Cause.DC is set (count disabled). 8278c2ecf20Sopenharmony_ci */ 8288c2ecf20Sopenharmony_civoid kvm_mips_count_enable_cause(struct kvm_vcpu *vcpu) 8298c2ecf20Sopenharmony_ci{ 8308c2ecf20Sopenharmony_ci struct mips_coproc *cop0 = vcpu->arch.cop0; 8318c2ecf20Sopenharmony_ci u32 count; 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_ci kvm_clear_c0_guest_cause(cop0, CAUSEF_DC); 8348c2ecf20Sopenharmony_ci 8358c2ecf20Sopenharmony_ci /* 8368c2ecf20Sopenharmony_ci * Set the dynamic count to match the static count. 8378c2ecf20Sopenharmony_ci * This starts the hrtimer if count_ctl.DC allows it. 8388c2ecf20Sopenharmony_ci * Otherwise it conveniently updates the biases. 8398c2ecf20Sopenharmony_ci */ 8408c2ecf20Sopenharmony_ci count = kvm_read_c0_guest_count(cop0); 8418c2ecf20Sopenharmony_ci kvm_mips_write_count(vcpu, count); 8428c2ecf20Sopenharmony_ci} 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_ci/** 8458c2ecf20Sopenharmony_ci * kvm_mips_set_count_ctl() - Update the count control KVM register. 8468c2ecf20Sopenharmony_ci * @vcpu: Virtual CPU. 8478c2ecf20Sopenharmony_ci * @count_ctl: Count control register new value. 8488c2ecf20Sopenharmony_ci * 8498c2ecf20Sopenharmony_ci * Set the count control KVM register. The timer is updated accordingly. 8508c2ecf20Sopenharmony_ci * 8518c2ecf20Sopenharmony_ci * Returns: -EINVAL if reserved bits are set. 8528c2ecf20Sopenharmony_ci * 0 on success. 8538c2ecf20Sopenharmony_ci */ 8548c2ecf20Sopenharmony_ciint kvm_mips_set_count_ctl(struct kvm_vcpu *vcpu, s64 count_ctl) 8558c2ecf20Sopenharmony_ci{ 8568c2ecf20Sopenharmony_ci struct mips_coproc *cop0 = vcpu->arch.cop0; 8578c2ecf20Sopenharmony_ci s64 changed = count_ctl ^ vcpu->arch.count_ctl; 8588c2ecf20Sopenharmony_ci s64 delta; 8598c2ecf20Sopenharmony_ci ktime_t expire, now; 8608c2ecf20Sopenharmony_ci u32 count, compare; 8618c2ecf20Sopenharmony_ci 8628c2ecf20Sopenharmony_ci /* Only allow defined bits to be changed */ 8638c2ecf20Sopenharmony_ci if (changed & ~(s64)(KVM_REG_MIPS_COUNT_CTL_DC)) 8648c2ecf20Sopenharmony_ci return -EINVAL; 8658c2ecf20Sopenharmony_ci 8668c2ecf20Sopenharmony_ci /* Apply new value */ 8678c2ecf20Sopenharmony_ci vcpu->arch.count_ctl = count_ctl; 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_ci /* Master CP0_Count disable */ 8708c2ecf20Sopenharmony_ci if (changed & KVM_REG_MIPS_COUNT_CTL_DC) { 8718c2ecf20Sopenharmony_ci /* Is CP0_Cause.DC already disabling CP0_Count? */ 8728c2ecf20Sopenharmony_ci if (kvm_read_c0_guest_cause(cop0) & CAUSEF_DC) { 8738c2ecf20Sopenharmony_ci if (count_ctl & KVM_REG_MIPS_COUNT_CTL_DC) 8748c2ecf20Sopenharmony_ci /* Just record the current time */ 8758c2ecf20Sopenharmony_ci vcpu->arch.count_resume = ktime_get(); 8768c2ecf20Sopenharmony_ci } else if (count_ctl & KVM_REG_MIPS_COUNT_CTL_DC) { 8778c2ecf20Sopenharmony_ci /* disable timer and record current time */ 8788c2ecf20Sopenharmony_ci vcpu->arch.count_resume = kvm_mips_count_disable(vcpu); 8798c2ecf20Sopenharmony_ci } else { 8808c2ecf20Sopenharmony_ci /* 8818c2ecf20Sopenharmony_ci * Calculate timeout relative to static count at resume 8828c2ecf20Sopenharmony_ci * time (wrap 0 to 2^32). 8838c2ecf20Sopenharmony_ci */ 8848c2ecf20Sopenharmony_ci count = kvm_read_c0_guest_count(cop0); 8858c2ecf20Sopenharmony_ci compare = kvm_read_c0_guest_compare(cop0); 8868c2ecf20Sopenharmony_ci delta = (u64)(u32)(compare - count - 1) + 1; 8878c2ecf20Sopenharmony_ci delta = div_u64(delta * NSEC_PER_SEC, 8888c2ecf20Sopenharmony_ci vcpu->arch.count_hz); 8898c2ecf20Sopenharmony_ci expire = ktime_add_ns(vcpu->arch.count_resume, delta); 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_ci /* Handle pending interrupt */ 8928c2ecf20Sopenharmony_ci now = ktime_get(); 8938c2ecf20Sopenharmony_ci if (ktime_compare(now, expire) >= 0) 8948c2ecf20Sopenharmony_ci /* Nothing should be waiting on the timeout */ 8958c2ecf20Sopenharmony_ci kvm_mips_callbacks->queue_timer_int(vcpu); 8968c2ecf20Sopenharmony_ci 8978c2ecf20Sopenharmony_ci /* Resume hrtimer without changing bias */ 8988c2ecf20Sopenharmony_ci count = kvm_mips_read_count_running(vcpu, now); 8998c2ecf20Sopenharmony_ci kvm_mips_resume_hrtimer(vcpu, now, count); 9008c2ecf20Sopenharmony_ci } 9018c2ecf20Sopenharmony_ci } 9028c2ecf20Sopenharmony_ci 9038c2ecf20Sopenharmony_ci return 0; 9048c2ecf20Sopenharmony_ci} 9058c2ecf20Sopenharmony_ci 9068c2ecf20Sopenharmony_ci/** 9078c2ecf20Sopenharmony_ci * kvm_mips_set_count_resume() - Update the count resume KVM register. 9088c2ecf20Sopenharmony_ci * @vcpu: Virtual CPU. 9098c2ecf20Sopenharmony_ci * @count_resume: Count resume register new value. 9108c2ecf20Sopenharmony_ci * 9118c2ecf20Sopenharmony_ci * Set the count resume KVM register. 9128c2ecf20Sopenharmony_ci * 9138c2ecf20Sopenharmony_ci * Returns: -EINVAL if out of valid range (0..now). 9148c2ecf20Sopenharmony_ci * 0 on success. 9158c2ecf20Sopenharmony_ci */ 9168c2ecf20Sopenharmony_ciint kvm_mips_set_count_resume(struct kvm_vcpu *vcpu, s64 count_resume) 9178c2ecf20Sopenharmony_ci{ 9188c2ecf20Sopenharmony_ci /* 9198c2ecf20Sopenharmony_ci * It doesn't make sense for the resume time to be in the future, as it 9208c2ecf20Sopenharmony_ci * would be possible for the next interrupt to be more than a full 9218c2ecf20Sopenharmony_ci * period in the future. 9228c2ecf20Sopenharmony_ci */ 9238c2ecf20Sopenharmony_ci if (count_resume < 0 || count_resume > ktime_to_ns(ktime_get())) 9248c2ecf20Sopenharmony_ci return -EINVAL; 9258c2ecf20Sopenharmony_ci 9268c2ecf20Sopenharmony_ci vcpu->arch.count_resume = ns_to_ktime(count_resume); 9278c2ecf20Sopenharmony_ci return 0; 9288c2ecf20Sopenharmony_ci} 9298c2ecf20Sopenharmony_ci 9308c2ecf20Sopenharmony_ci/** 9318c2ecf20Sopenharmony_ci * kvm_mips_count_timeout() - Push timer forward on timeout. 9328c2ecf20Sopenharmony_ci * @vcpu: Virtual CPU. 9338c2ecf20Sopenharmony_ci * 9348c2ecf20Sopenharmony_ci * Handle an hrtimer event by push the hrtimer forward a period. 9358c2ecf20Sopenharmony_ci * 9368c2ecf20Sopenharmony_ci * Returns: The hrtimer_restart value to return to the hrtimer subsystem. 9378c2ecf20Sopenharmony_ci */ 9388c2ecf20Sopenharmony_cienum hrtimer_restart kvm_mips_count_timeout(struct kvm_vcpu *vcpu) 9398c2ecf20Sopenharmony_ci{ 9408c2ecf20Sopenharmony_ci /* Add the Count period to the current expiry time */ 9418c2ecf20Sopenharmony_ci hrtimer_add_expires_ns(&vcpu->arch.comparecount_timer, 9428c2ecf20Sopenharmony_ci vcpu->arch.count_period); 9438c2ecf20Sopenharmony_ci return HRTIMER_RESTART; 9448c2ecf20Sopenharmony_ci} 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_cienum emulation_result kvm_mips_emul_eret(struct kvm_vcpu *vcpu) 9478c2ecf20Sopenharmony_ci{ 9488c2ecf20Sopenharmony_ci struct mips_coproc *cop0 = vcpu->arch.cop0; 9498c2ecf20Sopenharmony_ci enum emulation_result er = EMULATE_DONE; 9508c2ecf20Sopenharmony_ci 9518c2ecf20Sopenharmony_ci if (kvm_read_c0_guest_status(cop0) & ST0_ERL) { 9528c2ecf20Sopenharmony_ci kvm_clear_c0_guest_status(cop0, ST0_ERL); 9538c2ecf20Sopenharmony_ci vcpu->arch.pc = kvm_read_c0_guest_errorepc(cop0); 9548c2ecf20Sopenharmony_ci } else if (kvm_read_c0_guest_status(cop0) & ST0_EXL) { 9558c2ecf20Sopenharmony_ci kvm_debug("[%#lx] ERET to %#lx\n", vcpu->arch.pc, 9568c2ecf20Sopenharmony_ci kvm_read_c0_guest_epc(cop0)); 9578c2ecf20Sopenharmony_ci kvm_clear_c0_guest_status(cop0, ST0_EXL); 9588c2ecf20Sopenharmony_ci vcpu->arch.pc = kvm_read_c0_guest_epc(cop0); 9598c2ecf20Sopenharmony_ci 9608c2ecf20Sopenharmony_ci } else { 9618c2ecf20Sopenharmony_ci kvm_err("[%#lx] ERET when MIPS_SR_EXL|MIPS_SR_ERL == 0\n", 9628c2ecf20Sopenharmony_ci vcpu->arch.pc); 9638c2ecf20Sopenharmony_ci er = EMULATE_FAIL; 9648c2ecf20Sopenharmony_ci } 9658c2ecf20Sopenharmony_ci 9668c2ecf20Sopenharmony_ci return er; 9678c2ecf20Sopenharmony_ci} 9688c2ecf20Sopenharmony_ci 9698c2ecf20Sopenharmony_cienum emulation_result kvm_mips_emul_wait(struct kvm_vcpu *vcpu) 9708c2ecf20Sopenharmony_ci{ 9718c2ecf20Sopenharmony_ci kvm_debug("[%#lx] !!!WAIT!!! (%#lx)\n", vcpu->arch.pc, 9728c2ecf20Sopenharmony_ci vcpu->arch.pending_exceptions); 9738c2ecf20Sopenharmony_ci 9748c2ecf20Sopenharmony_ci ++vcpu->stat.wait_exits; 9758c2ecf20Sopenharmony_ci trace_kvm_exit(vcpu, KVM_TRACE_EXIT_WAIT); 9768c2ecf20Sopenharmony_ci if (!vcpu->arch.pending_exceptions) { 9778c2ecf20Sopenharmony_ci kvm_vz_lose_htimer(vcpu); 9788c2ecf20Sopenharmony_ci vcpu->arch.wait = 1; 9798c2ecf20Sopenharmony_ci kvm_vcpu_block(vcpu); 9808c2ecf20Sopenharmony_ci 9818c2ecf20Sopenharmony_ci /* 9828c2ecf20Sopenharmony_ci * We we are runnable, then definitely go off to user space to 9838c2ecf20Sopenharmony_ci * check if any I/O interrupts are pending. 9848c2ecf20Sopenharmony_ci */ 9858c2ecf20Sopenharmony_ci if (kvm_check_request(KVM_REQ_UNHALT, vcpu)) { 9868c2ecf20Sopenharmony_ci kvm_clear_request(KVM_REQ_UNHALT, vcpu); 9878c2ecf20Sopenharmony_ci vcpu->run->exit_reason = KVM_EXIT_IRQ_WINDOW_OPEN; 9888c2ecf20Sopenharmony_ci } 9898c2ecf20Sopenharmony_ci } 9908c2ecf20Sopenharmony_ci 9918c2ecf20Sopenharmony_ci return EMULATE_DONE; 9928c2ecf20Sopenharmony_ci} 9938c2ecf20Sopenharmony_ci 9948c2ecf20Sopenharmony_cistatic void kvm_mips_change_entryhi(struct kvm_vcpu *vcpu, 9958c2ecf20Sopenharmony_ci unsigned long entryhi) 9968c2ecf20Sopenharmony_ci{ 9978c2ecf20Sopenharmony_ci struct mips_coproc *cop0 = vcpu->arch.cop0; 9988c2ecf20Sopenharmony_ci struct mm_struct *kern_mm = &vcpu->arch.guest_kernel_mm; 9998c2ecf20Sopenharmony_ci int cpu, i; 10008c2ecf20Sopenharmony_ci u32 nasid = entryhi & KVM_ENTRYHI_ASID; 10018c2ecf20Sopenharmony_ci 10028c2ecf20Sopenharmony_ci if (((kvm_read_c0_guest_entryhi(cop0) & KVM_ENTRYHI_ASID) != nasid)) { 10038c2ecf20Sopenharmony_ci trace_kvm_asid_change(vcpu, kvm_read_c0_guest_entryhi(cop0) & 10048c2ecf20Sopenharmony_ci KVM_ENTRYHI_ASID, nasid); 10058c2ecf20Sopenharmony_ci 10068c2ecf20Sopenharmony_ci /* 10078c2ecf20Sopenharmony_ci * Flush entries from the GVA page tables. 10088c2ecf20Sopenharmony_ci * Guest user page table will get flushed lazily on re-entry to 10098c2ecf20Sopenharmony_ci * guest user if the guest ASID actually changes. 10108c2ecf20Sopenharmony_ci */ 10118c2ecf20Sopenharmony_ci kvm_mips_flush_gva_pt(kern_mm->pgd, KMF_KERN); 10128c2ecf20Sopenharmony_ci 10138c2ecf20Sopenharmony_ci /* 10148c2ecf20Sopenharmony_ci * Regenerate/invalidate kernel MMU context. 10158c2ecf20Sopenharmony_ci * The user MMU context will be regenerated lazily on re-entry 10168c2ecf20Sopenharmony_ci * to guest user if the guest ASID actually changes. 10178c2ecf20Sopenharmony_ci */ 10188c2ecf20Sopenharmony_ci preempt_disable(); 10198c2ecf20Sopenharmony_ci cpu = smp_processor_id(); 10208c2ecf20Sopenharmony_ci get_new_mmu_context(kern_mm); 10218c2ecf20Sopenharmony_ci for_each_possible_cpu(i) 10228c2ecf20Sopenharmony_ci if (i != cpu) 10238c2ecf20Sopenharmony_ci set_cpu_context(i, kern_mm, 0); 10248c2ecf20Sopenharmony_ci preempt_enable(); 10258c2ecf20Sopenharmony_ci } 10268c2ecf20Sopenharmony_ci kvm_write_c0_guest_entryhi(cop0, entryhi); 10278c2ecf20Sopenharmony_ci} 10288c2ecf20Sopenharmony_ci 10298c2ecf20Sopenharmony_cienum emulation_result kvm_mips_emul_tlbr(struct kvm_vcpu *vcpu) 10308c2ecf20Sopenharmony_ci{ 10318c2ecf20Sopenharmony_ci struct mips_coproc *cop0 = vcpu->arch.cop0; 10328c2ecf20Sopenharmony_ci struct kvm_mips_tlb *tlb; 10338c2ecf20Sopenharmony_ci unsigned long pc = vcpu->arch.pc; 10348c2ecf20Sopenharmony_ci int index; 10358c2ecf20Sopenharmony_ci 10368c2ecf20Sopenharmony_ci index = kvm_read_c0_guest_index(cop0); 10378c2ecf20Sopenharmony_ci if (index < 0 || index >= KVM_MIPS_GUEST_TLB_SIZE) { 10388c2ecf20Sopenharmony_ci /* UNDEFINED */ 10398c2ecf20Sopenharmony_ci kvm_debug("[%#lx] TLBR Index %#x out of range\n", pc, index); 10408c2ecf20Sopenharmony_ci index &= KVM_MIPS_GUEST_TLB_SIZE - 1; 10418c2ecf20Sopenharmony_ci } 10428c2ecf20Sopenharmony_ci 10438c2ecf20Sopenharmony_ci tlb = &vcpu->arch.guest_tlb[index]; 10448c2ecf20Sopenharmony_ci kvm_write_c0_guest_pagemask(cop0, tlb->tlb_mask); 10458c2ecf20Sopenharmony_ci kvm_write_c0_guest_entrylo0(cop0, tlb->tlb_lo[0]); 10468c2ecf20Sopenharmony_ci kvm_write_c0_guest_entrylo1(cop0, tlb->tlb_lo[1]); 10478c2ecf20Sopenharmony_ci kvm_mips_change_entryhi(vcpu, tlb->tlb_hi); 10488c2ecf20Sopenharmony_ci 10498c2ecf20Sopenharmony_ci return EMULATE_DONE; 10508c2ecf20Sopenharmony_ci} 10518c2ecf20Sopenharmony_ci 10528c2ecf20Sopenharmony_ci/** 10538c2ecf20Sopenharmony_ci * kvm_mips_invalidate_guest_tlb() - Indicates a change in guest MMU map. 10548c2ecf20Sopenharmony_ci * @vcpu: VCPU with changed mappings. 10558c2ecf20Sopenharmony_ci * @tlb: TLB entry being removed. 10568c2ecf20Sopenharmony_ci * 10578c2ecf20Sopenharmony_ci * This is called to indicate a single change in guest MMU mappings, so that we 10588c2ecf20Sopenharmony_ci * can arrange TLB flushes on this and other CPUs. 10598c2ecf20Sopenharmony_ci */ 10608c2ecf20Sopenharmony_cistatic void kvm_mips_invalidate_guest_tlb(struct kvm_vcpu *vcpu, 10618c2ecf20Sopenharmony_ci struct kvm_mips_tlb *tlb) 10628c2ecf20Sopenharmony_ci{ 10638c2ecf20Sopenharmony_ci struct mm_struct *kern_mm = &vcpu->arch.guest_kernel_mm; 10648c2ecf20Sopenharmony_ci struct mm_struct *user_mm = &vcpu->arch.guest_user_mm; 10658c2ecf20Sopenharmony_ci int cpu, i; 10668c2ecf20Sopenharmony_ci bool user; 10678c2ecf20Sopenharmony_ci 10688c2ecf20Sopenharmony_ci /* No need to flush for entries which are already invalid */ 10698c2ecf20Sopenharmony_ci if (!((tlb->tlb_lo[0] | tlb->tlb_lo[1]) & ENTRYLO_V)) 10708c2ecf20Sopenharmony_ci return; 10718c2ecf20Sopenharmony_ci /* Don't touch host kernel page tables or TLB mappings */ 10728c2ecf20Sopenharmony_ci if ((unsigned long)tlb->tlb_hi > 0x7fffffff) 10738c2ecf20Sopenharmony_ci return; 10748c2ecf20Sopenharmony_ci /* User address space doesn't need flushing for KSeg2/3 changes */ 10758c2ecf20Sopenharmony_ci user = tlb->tlb_hi < KVM_GUEST_KSEG0; 10768c2ecf20Sopenharmony_ci 10778c2ecf20Sopenharmony_ci preempt_disable(); 10788c2ecf20Sopenharmony_ci 10798c2ecf20Sopenharmony_ci /* Invalidate page table entries */ 10808c2ecf20Sopenharmony_ci kvm_trap_emul_invalidate_gva(vcpu, tlb->tlb_hi & VPN2_MASK, user); 10818c2ecf20Sopenharmony_ci 10828c2ecf20Sopenharmony_ci /* 10838c2ecf20Sopenharmony_ci * Probe the shadow host TLB for the entry being overwritten, if one 10848c2ecf20Sopenharmony_ci * matches, invalidate it 10858c2ecf20Sopenharmony_ci */ 10868c2ecf20Sopenharmony_ci kvm_mips_host_tlb_inv(vcpu, tlb->tlb_hi, user, true); 10878c2ecf20Sopenharmony_ci 10888c2ecf20Sopenharmony_ci /* Invalidate the whole ASID on other CPUs */ 10898c2ecf20Sopenharmony_ci cpu = smp_processor_id(); 10908c2ecf20Sopenharmony_ci for_each_possible_cpu(i) { 10918c2ecf20Sopenharmony_ci if (i == cpu) 10928c2ecf20Sopenharmony_ci continue; 10938c2ecf20Sopenharmony_ci if (user) 10948c2ecf20Sopenharmony_ci set_cpu_context(i, user_mm, 0); 10958c2ecf20Sopenharmony_ci set_cpu_context(i, kern_mm, 0); 10968c2ecf20Sopenharmony_ci } 10978c2ecf20Sopenharmony_ci 10988c2ecf20Sopenharmony_ci preempt_enable(); 10998c2ecf20Sopenharmony_ci} 11008c2ecf20Sopenharmony_ci 11018c2ecf20Sopenharmony_ci/* Write Guest TLB Entry @ Index */ 11028c2ecf20Sopenharmony_cienum emulation_result kvm_mips_emul_tlbwi(struct kvm_vcpu *vcpu) 11038c2ecf20Sopenharmony_ci{ 11048c2ecf20Sopenharmony_ci struct mips_coproc *cop0 = vcpu->arch.cop0; 11058c2ecf20Sopenharmony_ci int index = kvm_read_c0_guest_index(cop0); 11068c2ecf20Sopenharmony_ci struct kvm_mips_tlb *tlb = NULL; 11078c2ecf20Sopenharmony_ci unsigned long pc = vcpu->arch.pc; 11088c2ecf20Sopenharmony_ci 11098c2ecf20Sopenharmony_ci if (index < 0 || index >= KVM_MIPS_GUEST_TLB_SIZE) { 11108c2ecf20Sopenharmony_ci kvm_debug("%s: illegal index: %d\n", __func__, index); 11118c2ecf20Sopenharmony_ci kvm_debug("[%#lx] COP0_TLBWI [%d] (entryhi: %#lx, entrylo0: %#lx entrylo1: %#lx, mask: %#lx)\n", 11128c2ecf20Sopenharmony_ci pc, index, kvm_read_c0_guest_entryhi(cop0), 11138c2ecf20Sopenharmony_ci kvm_read_c0_guest_entrylo0(cop0), 11148c2ecf20Sopenharmony_ci kvm_read_c0_guest_entrylo1(cop0), 11158c2ecf20Sopenharmony_ci kvm_read_c0_guest_pagemask(cop0)); 11168c2ecf20Sopenharmony_ci index = (index & ~0x80000000) % KVM_MIPS_GUEST_TLB_SIZE; 11178c2ecf20Sopenharmony_ci } 11188c2ecf20Sopenharmony_ci 11198c2ecf20Sopenharmony_ci tlb = &vcpu->arch.guest_tlb[index]; 11208c2ecf20Sopenharmony_ci 11218c2ecf20Sopenharmony_ci kvm_mips_invalidate_guest_tlb(vcpu, tlb); 11228c2ecf20Sopenharmony_ci 11238c2ecf20Sopenharmony_ci tlb->tlb_mask = kvm_read_c0_guest_pagemask(cop0); 11248c2ecf20Sopenharmony_ci tlb->tlb_hi = kvm_read_c0_guest_entryhi(cop0); 11258c2ecf20Sopenharmony_ci tlb->tlb_lo[0] = kvm_read_c0_guest_entrylo0(cop0); 11268c2ecf20Sopenharmony_ci tlb->tlb_lo[1] = kvm_read_c0_guest_entrylo1(cop0); 11278c2ecf20Sopenharmony_ci 11288c2ecf20Sopenharmony_ci kvm_debug("[%#lx] COP0_TLBWI [%d] (entryhi: %#lx, entrylo0: %#lx entrylo1: %#lx, mask: %#lx)\n", 11298c2ecf20Sopenharmony_ci pc, index, kvm_read_c0_guest_entryhi(cop0), 11308c2ecf20Sopenharmony_ci kvm_read_c0_guest_entrylo0(cop0), 11318c2ecf20Sopenharmony_ci kvm_read_c0_guest_entrylo1(cop0), 11328c2ecf20Sopenharmony_ci kvm_read_c0_guest_pagemask(cop0)); 11338c2ecf20Sopenharmony_ci 11348c2ecf20Sopenharmony_ci return EMULATE_DONE; 11358c2ecf20Sopenharmony_ci} 11368c2ecf20Sopenharmony_ci 11378c2ecf20Sopenharmony_ci/* Write Guest TLB Entry @ Random Index */ 11388c2ecf20Sopenharmony_cienum emulation_result kvm_mips_emul_tlbwr(struct kvm_vcpu *vcpu) 11398c2ecf20Sopenharmony_ci{ 11408c2ecf20Sopenharmony_ci struct mips_coproc *cop0 = vcpu->arch.cop0; 11418c2ecf20Sopenharmony_ci struct kvm_mips_tlb *tlb = NULL; 11428c2ecf20Sopenharmony_ci unsigned long pc = vcpu->arch.pc; 11438c2ecf20Sopenharmony_ci int index; 11448c2ecf20Sopenharmony_ci 11458c2ecf20Sopenharmony_ci index = prandom_u32_max(KVM_MIPS_GUEST_TLB_SIZE); 11468c2ecf20Sopenharmony_ci tlb = &vcpu->arch.guest_tlb[index]; 11478c2ecf20Sopenharmony_ci 11488c2ecf20Sopenharmony_ci kvm_mips_invalidate_guest_tlb(vcpu, tlb); 11498c2ecf20Sopenharmony_ci 11508c2ecf20Sopenharmony_ci tlb->tlb_mask = kvm_read_c0_guest_pagemask(cop0); 11518c2ecf20Sopenharmony_ci tlb->tlb_hi = kvm_read_c0_guest_entryhi(cop0); 11528c2ecf20Sopenharmony_ci tlb->tlb_lo[0] = kvm_read_c0_guest_entrylo0(cop0); 11538c2ecf20Sopenharmony_ci tlb->tlb_lo[1] = kvm_read_c0_guest_entrylo1(cop0); 11548c2ecf20Sopenharmony_ci 11558c2ecf20Sopenharmony_ci kvm_debug("[%#lx] COP0_TLBWR[%d] (entryhi: %#lx, entrylo0: %#lx entrylo1: %#lx)\n", 11568c2ecf20Sopenharmony_ci pc, index, kvm_read_c0_guest_entryhi(cop0), 11578c2ecf20Sopenharmony_ci kvm_read_c0_guest_entrylo0(cop0), 11588c2ecf20Sopenharmony_ci kvm_read_c0_guest_entrylo1(cop0)); 11598c2ecf20Sopenharmony_ci 11608c2ecf20Sopenharmony_ci return EMULATE_DONE; 11618c2ecf20Sopenharmony_ci} 11628c2ecf20Sopenharmony_ci 11638c2ecf20Sopenharmony_cienum emulation_result kvm_mips_emul_tlbp(struct kvm_vcpu *vcpu) 11648c2ecf20Sopenharmony_ci{ 11658c2ecf20Sopenharmony_ci struct mips_coproc *cop0 = vcpu->arch.cop0; 11668c2ecf20Sopenharmony_ci long entryhi = kvm_read_c0_guest_entryhi(cop0); 11678c2ecf20Sopenharmony_ci unsigned long pc = vcpu->arch.pc; 11688c2ecf20Sopenharmony_ci int index = -1; 11698c2ecf20Sopenharmony_ci 11708c2ecf20Sopenharmony_ci index = kvm_mips_guest_tlb_lookup(vcpu, entryhi); 11718c2ecf20Sopenharmony_ci 11728c2ecf20Sopenharmony_ci kvm_write_c0_guest_index(cop0, index); 11738c2ecf20Sopenharmony_ci 11748c2ecf20Sopenharmony_ci kvm_debug("[%#lx] COP0_TLBP (entryhi: %#lx), index: %d\n", pc, entryhi, 11758c2ecf20Sopenharmony_ci index); 11768c2ecf20Sopenharmony_ci 11778c2ecf20Sopenharmony_ci return EMULATE_DONE; 11788c2ecf20Sopenharmony_ci} 11798c2ecf20Sopenharmony_ci 11808c2ecf20Sopenharmony_ci/** 11818c2ecf20Sopenharmony_ci * kvm_mips_config1_wrmask() - Find mask of writable bits in guest Config1 11828c2ecf20Sopenharmony_ci * @vcpu: Virtual CPU. 11838c2ecf20Sopenharmony_ci * 11848c2ecf20Sopenharmony_ci * Finds the mask of bits which are writable in the guest's Config1 CP0 11858c2ecf20Sopenharmony_ci * register, by userland (currently read-only to the guest). 11868c2ecf20Sopenharmony_ci */ 11878c2ecf20Sopenharmony_ciunsigned int kvm_mips_config1_wrmask(struct kvm_vcpu *vcpu) 11888c2ecf20Sopenharmony_ci{ 11898c2ecf20Sopenharmony_ci unsigned int mask = 0; 11908c2ecf20Sopenharmony_ci 11918c2ecf20Sopenharmony_ci /* Permit FPU to be present if FPU is supported */ 11928c2ecf20Sopenharmony_ci if (kvm_mips_guest_can_have_fpu(&vcpu->arch)) 11938c2ecf20Sopenharmony_ci mask |= MIPS_CONF1_FP; 11948c2ecf20Sopenharmony_ci 11958c2ecf20Sopenharmony_ci return mask; 11968c2ecf20Sopenharmony_ci} 11978c2ecf20Sopenharmony_ci 11988c2ecf20Sopenharmony_ci/** 11998c2ecf20Sopenharmony_ci * kvm_mips_config3_wrmask() - Find mask of writable bits in guest Config3 12008c2ecf20Sopenharmony_ci * @vcpu: Virtual CPU. 12018c2ecf20Sopenharmony_ci * 12028c2ecf20Sopenharmony_ci * Finds the mask of bits which are writable in the guest's Config3 CP0 12038c2ecf20Sopenharmony_ci * register, by userland (currently read-only to the guest). 12048c2ecf20Sopenharmony_ci */ 12058c2ecf20Sopenharmony_ciunsigned int kvm_mips_config3_wrmask(struct kvm_vcpu *vcpu) 12068c2ecf20Sopenharmony_ci{ 12078c2ecf20Sopenharmony_ci /* Config4 and ULRI are optional */ 12088c2ecf20Sopenharmony_ci unsigned int mask = MIPS_CONF_M | MIPS_CONF3_ULRI; 12098c2ecf20Sopenharmony_ci 12108c2ecf20Sopenharmony_ci /* Permit MSA to be present if MSA is supported */ 12118c2ecf20Sopenharmony_ci if (kvm_mips_guest_can_have_msa(&vcpu->arch)) 12128c2ecf20Sopenharmony_ci mask |= MIPS_CONF3_MSA; 12138c2ecf20Sopenharmony_ci 12148c2ecf20Sopenharmony_ci return mask; 12158c2ecf20Sopenharmony_ci} 12168c2ecf20Sopenharmony_ci 12178c2ecf20Sopenharmony_ci/** 12188c2ecf20Sopenharmony_ci * kvm_mips_config4_wrmask() - Find mask of writable bits in guest Config4 12198c2ecf20Sopenharmony_ci * @vcpu: Virtual CPU. 12208c2ecf20Sopenharmony_ci * 12218c2ecf20Sopenharmony_ci * Finds the mask of bits which are writable in the guest's Config4 CP0 12228c2ecf20Sopenharmony_ci * register, by userland (currently read-only to the guest). 12238c2ecf20Sopenharmony_ci */ 12248c2ecf20Sopenharmony_ciunsigned int kvm_mips_config4_wrmask(struct kvm_vcpu *vcpu) 12258c2ecf20Sopenharmony_ci{ 12268c2ecf20Sopenharmony_ci /* Config5 is optional */ 12278c2ecf20Sopenharmony_ci unsigned int mask = MIPS_CONF_M; 12288c2ecf20Sopenharmony_ci 12298c2ecf20Sopenharmony_ci /* KScrExist */ 12308c2ecf20Sopenharmony_ci mask |= 0xfc << MIPS_CONF4_KSCREXIST_SHIFT; 12318c2ecf20Sopenharmony_ci 12328c2ecf20Sopenharmony_ci return mask; 12338c2ecf20Sopenharmony_ci} 12348c2ecf20Sopenharmony_ci 12358c2ecf20Sopenharmony_ci/** 12368c2ecf20Sopenharmony_ci * kvm_mips_config5_wrmask() - Find mask of writable bits in guest Config5 12378c2ecf20Sopenharmony_ci * @vcpu: Virtual CPU. 12388c2ecf20Sopenharmony_ci * 12398c2ecf20Sopenharmony_ci * Finds the mask of bits which are writable in the guest's Config5 CP0 12408c2ecf20Sopenharmony_ci * register, by the guest itself. 12418c2ecf20Sopenharmony_ci */ 12428c2ecf20Sopenharmony_ciunsigned int kvm_mips_config5_wrmask(struct kvm_vcpu *vcpu) 12438c2ecf20Sopenharmony_ci{ 12448c2ecf20Sopenharmony_ci unsigned int mask = 0; 12458c2ecf20Sopenharmony_ci 12468c2ecf20Sopenharmony_ci /* Permit MSAEn changes if MSA supported and enabled */ 12478c2ecf20Sopenharmony_ci if (kvm_mips_guest_has_msa(&vcpu->arch)) 12488c2ecf20Sopenharmony_ci mask |= MIPS_CONF5_MSAEN; 12498c2ecf20Sopenharmony_ci 12508c2ecf20Sopenharmony_ci /* 12518c2ecf20Sopenharmony_ci * Permit guest FPU mode changes if FPU is enabled and the relevant 12528c2ecf20Sopenharmony_ci * feature exists according to FIR register. 12538c2ecf20Sopenharmony_ci */ 12548c2ecf20Sopenharmony_ci if (kvm_mips_guest_has_fpu(&vcpu->arch)) { 12558c2ecf20Sopenharmony_ci if (cpu_has_fre) 12568c2ecf20Sopenharmony_ci mask |= MIPS_CONF5_FRE; 12578c2ecf20Sopenharmony_ci /* We don't support UFR or UFE */ 12588c2ecf20Sopenharmony_ci } 12598c2ecf20Sopenharmony_ci 12608c2ecf20Sopenharmony_ci return mask; 12618c2ecf20Sopenharmony_ci} 12628c2ecf20Sopenharmony_ci 12638c2ecf20Sopenharmony_cienum emulation_result kvm_mips_emulate_CP0(union mips_instruction inst, 12648c2ecf20Sopenharmony_ci u32 *opc, u32 cause, 12658c2ecf20Sopenharmony_ci struct kvm_vcpu *vcpu) 12668c2ecf20Sopenharmony_ci{ 12678c2ecf20Sopenharmony_ci struct mips_coproc *cop0 = vcpu->arch.cop0; 12688c2ecf20Sopenharmony_ci enum emulation_result er = EMULATE_DONE; 12698c2ecf20Sopenharmony_ci u32 rt, rd, sel; 12708c2ecf20Sopenharmony_ci unsigned long curr_pc; 12718c2ecf20Sopenharmony_ci 12728c2ecf20Sopenharmony_ci /* 12738c2ecf20Sopenharmony_ci * Update PC and hold onto current PC in case there is 12748c2ecf20Sopenharmony_ci * an error and we want to rollback the PC 12758c2ecf20Sopenharmony_ci */ 12768c2ecf20Sopenharmony_ci curr_pc = vcpu->arch.pc; 12778c2ecf20Sopenharmony_ci er = update_pc(vcpu, cause); 12788c2ecf20Sopenharmony_ci if (er == EMULATE_FAIL) 12798c2ecf20Sopenharmony_ci return er; 12808c2ecf20Sopenharmony_ci 12818c2ecf20Sopenharmony_ci if (inst.co_format.co) { 12828c2ecf20Sopenharmony_ci switch (inst.co_format.func) { 12838c2ecf20Sopenharmony_ci case tlbr_op: /* Read indexed TLB entry */ 12848c2ecf20Sopenharmony_ci er = kvm_mips_emul_tlbr(vcpu); 12858c2ecf20Sopenharmony_ci break; 12868c2ecf20Sopenharmony_ci case tlbwi_op: /* Write indexed */ 12878c2ecf20Sopenharmony_ci er = kvm_mips_emul_tlbwi(vcpu); 12888c2ecf20Sopenharmony_ci break; 12898c2ecf20Sopenharmony_ci case tlbwr_op: /* Write random */ 12908c2ecf20Sopenharmony_ci er = kvm_mips_emul_tlbwr(vcpu); 12918c2ecf20Sopenharmony_ci break; 12928c2ecf20Sopenharmony_ci case tlbp_op: /* TLB Probe */ 12938c2ecf20Sopenharmony_ci er = kvm_mips_emul_tlbp(vcpu); 12948c2ecf20Sopenharmony_ci break; 12958c2ecf20Sopenharmony_ci case rfe_op: 12968c2ecf20Sopenharmony_ci kvm_err("!!!COP0_RFE!!!\n"); 12978c2ecf20Sopenharmony_ci break; 12988c2ecf20Sopenharmony_ci case eret_op: 12998c2ecf20Sopenharmony_ci er = kvm_mips_emul_eret(vcpu); 13008c2ecf20Sopenharmony_ci goto dont_update_pc; 13018c2ecf20Sopenharmony_ci case wait_op: 13028c2ecf20Sopenharmony_ci er = kvm_mips_emul_wait(vcpu); 13038c2ecf20Sopenharmony_ci break; 13048c2ecf20Sopenharmony_ci case hypcall_op: 13058c2ecf20Sopenharmony_ci er = kvm_mips_emul_hypcall(vcpu, inst); 13068c2ecf20Sopenharmony_ci break; 13078c2ecf20Sopenharmony_ci } 13088c2ecf20Sopenharmony_ci } else { 13098c2ecf20Sopenharmony_ci rt = inst.c0r_format.rt; 13108c2ecf20Sopenharmony_ci rd = inst.c0r_format.rd; 13118c2ecf20Sopenharmony_ci sel = inst.c0r_format.sel; 13128c2ecf20Sopenharmony_ci 13138c2ecf20Sopenharmony_ci switch (inst.c0r_format.rs) { 13148c2ecf20Sopenharmony_ci case mfc_op: 13158c2ecf20Sopenharmony_ci#ifdef CONFIG_KVM_MIPS_DEBUG_COP0_COUNTERS 13168c2ecf20Sopenharmony_ci cop0->stat[rd][sel]++; 13178c2ecf20Sopenharmony_ci#endif 13188c2ecf20Sopenharmony_ci /* Get reg */ 13198c2ecf20Sopenharmony_ci if ((rd == MIPS_CP0_COUNT) && (sel == 0)) { 13208c2ecf20Sopenharmony_ci vcpu->arch.gprs[rt] = 13218c2ecf20Sopenharmony_ci (s32)kvm_mips_read_count(vcpu); 13228c2ecf20Sopenharmony_ci } else if ((rd == MIPS_CP0_ERRCTL) && (sel == 0)) { 13238c2ecf20Sopenharmony_ci vcpu->arch.gprs[rt] = 0x0; 13248c2ecf20Sopenharmony_ci#ifdef CONFIG_KVM_MIPS_DYN_TRANS 13258c2ecf20Sopenharmony_ci kvm_mips_trans_mfc0(inst, opc, vcpu); 13268c2ecf20Sopenharmony_ci#endif 13278c2ecf20Sopenharmony_ci } else { 13288c2ecf20Sopenharmony_ci vcpu->arch.gprs[rt] = (s32)cop0->reg[rd][sel]; 13298c2ecf20Sopenharmony_ci 13308c2ecf20Sopenharmony_ci#ifdef CONFIG_KVM_MIPS_DYN_TRANS 13318c2ecf20Sopenharmony_ci kvm_mips_trans_mfc0(inst, opc, vcpu); 13328c2ecf20Sopenharmony_ci#endif 13338c2ecf20Sopenharmony_ci } 13348c2ecf20Sopenharmony_ci 13358c2ecf20Sopenharmony_ci trace_kvm_hwr(vcpu, KVM_TRACE_MFC0, 13368c2ecf20Sopenharmony_ci KVM_TRACE_COP0(rd, sel), 13378c2ecf20Sopenharmony_ci vcpu->arch.gprs[rt]); 13388c2ecf20Sopenharmony_ci break; 13398c2ecf20Sopenharmony_ci 13408c2ecf20Sopenharmony_ci case dmfc_op: 13418c2ecf20Sopenharmony_ci vcpu->arch.gprs[rt] = cop0->reg[rd][sel]; 13428c2ecf20Sopenharmony_ci 13438c2ecf20Sopenharmony_ci trace_kvm_hwr(vcpu, KVM_TRACE_DMFC0, 13448c2ecf20Sopenharmony_ci KVM_TRACE_COP0(rd, sel), 13458c2ecf20Sopenharmony_ci vcpu->arch.gprs[rt]); 13468c2ecf20Sopenharmony_ci break; 13478c2ecf20Sopenharmony_ci 13488c2ecf20Sopenharmony_ci case mtc_op: 13498c2ecf20Sopenharmony_ci#ifdef CONFIG_KVM_MIPS_DEBUG_COP0_COUNTERS 13508c2ecf20Sopenharmony_ci cop0->stat[rd][sel]++; 13518c2ecf20Sopenharmony_ci#endif 13528c2ecf20Sopenharmony_ci trace_kvm_hwr(vcpu, KVM_TRACE_MTC0, 13538c2ecf20Sopenharmony_ci KVM_TRACE_COP0(rd, sel), 13548c2ecf20Sopenharmony_ci vcpu->arch.gprs[rt]); 13558c2ecf20Sopenharmony_ci 13568c2ecf20Sopenharmony_ci if ((rd == MIPS_CP0_TLB_INDEX) 13578c2ecf20Sopenharmony_ci && (vcpu->arch.gprs[rt] >= 13588c2ecf20Sopenharmony_ci KVM_MIPS_GUEST_TLB_SIZE)) { 13598c2ecf20Sopenharmony_ci kvm_err("Invalid TLB Index: %ld", 13608c2ecf20Sopenharmony_ci vcpu->arch.gprs[rt]); 13618c2ecf20Sopenharmony_ci er = EMULATE_FAIL; 13628c2ecf20Sopenharmony_ci break; 13638c2ecf20Sopenharmony_ci } 13648c2ecf20Sopenharmony_ci if ((rd == MIPS_CP0_PRID) && (sel == 1)) { 13658c2ecf20Sopenharmony_ci /* 13668c2ecf20Sopenharmony_ci * Preserve core number, and keep the exception 13678c2ecf20Sopenharmony_ci * base in guest KSeg0. 13688c2ecf20Sopenharmony_ci */ 13698c2ecf20Sopenharmony_ci kvm_change_c0_guest_ebase(cop0, 0x1ffff000, 13708c2ecf20Sopenharmony_ci vcpu->arch.gprs[rt]); 13718c2ecf20Sopenharmony_ci } else if (rd == MIPS_CP0_TLB_HI && sel == 0) { 13728c2ecf20Sopenharmony_ci kvm_mips_change_entryhi(vcpu, 13738c2ecf20Sopenharmony_ci vcpu->arch.gprs[rt]); 13748c2ecf20Sopenharmony_ci } 13758c2ecf20Sopenharmony_ci /* Are we writing to COUNT */ 13768c2ecf20Sopenharmony_ci else if ((rd == MIPS_CP0_COUNT) && (sel == 0)) { 13778c2ecf20Sopenharmony_ci kvm_mips_write_count(vcpu, vcpu->arch.gprs[rt]); 13788c2ecf20Sopenharmony_ci goto done; 13798c2ecf20Sopenharmony_ci } else if ((rd == MIPS_CP0_COMPARE) && (sel == 0)) { 13808c2ecf20Sopenharmony_ci /* If we are writing to COMPARE */ 13818c2ecf20Sopenharmony_ci /* Clear pending timer interrupt, if any */ 13828c2ecf20Sopenharmony_ci kvm_mips_write_compare(vcpu, 13838c2ecf20Sopenharmony_ci vcpu->arch.gprs[rt], 13848c2ecf20Sopenharmony_ci true); 13858c2ecf20Sopenharmony_ci } else if ((rd == MIPS_CP0_STATUS) && (sel == 0)) { 13868c2ecf20Sopenharmony_ci unsigned int old_val, val, change; 13878c2ecf20Sopenharmony_ci 13888c2ecf20Sopenharmony_ci old_val = kvm_read_c0_guest_status(cop0); 13898c2ecf20Sopenharmony_ci val = vcpu->arch.gprs[rt]; 13908c2ecf20Sopenharmony_ci change = val ^ old_val; 13918c2ecf20Sopenharmony_ci 13928c2ecf20Sopenharmony_ci /* Make sure that the NMI bit is never set */ 13938c2ecf20Sopenharmony_ci val &= ~ST0_NMI; 13948c2ecf20Sopenharmony_ci 13958c2ecf20Sopenharmony_ci /* 13968c2ecf20Sopenharmony_ci * Don't allow CU1 or FR to be set unless FPU 13978c2ecf20Sopenharmony_ci * capability enabled and exists in guest 13988c2ecf20Sopenharmony_ci * configuration. 13998c2ecf20Sopenharmony_ci */ 14008c2ecf20Sopenharmony_ci if (!kvm_mips_guest_has_fpu(&vcpu->arch)) 14018c2ecf20Sopenharmony_ci val &= ~(ST0_CU1 | ST0_FR); 14028c2ecf20Sopenharmony_ci 14038c2ecf20Sopenharmony_ci /* 14048c2ecf20Sopenharmony_ci * Also don't allow FR to be set if host doesn't 14058c2ecf20Sopenharmony_ci * support it. 14068c2ecf20Sopenharmony_ci */ 14078c2ecf20Sopenharmony_ci if (!(current_cpu_data.fpu_id & MIPS_FPIR_F64)) 14088c2ecf20Sopenharmony_ci val &= ~ST0_FR; 14098c2ecf20Sopenharmony_ci 14108c2ecf20Sopenharmony_ci 14118c2ecf20Sopenharmony_ci /* Handle changes in FPU mode */ 14128c2ecf20Sopenharmony_ci preempt_disable(); 14138c2ecf20Sopenharmony_ci 14148c2ecf20Sopenharmony_ci /* 14158c2ecf20Sopenharmony_ci * FPU and Vector register state is made 14168c2ecf20Sopenharmony_ci * UNPREDICTABLE by a change of FR, so don't 14178c2ecf20Sopenharmony_ci * even bother saving it. 14188c2ecf20Sopenharmony_ci */ 14198c2ecf20Sopenharmony_ci if (change & ST0_FR) 14208c2ecf20Sopenharmony_ci kvm_drop_fpu(vcpu); 14218c2ecf20Sopenharmony_ci 14228c2ecf20Sopenharmony_ci /* 14238c2ecf20Sopenharmony_ci * If MSA state is already live, it is undefined 14248c2ecf20Sopenharmony_ci * how it interacts with FR=0 FPU state, and we 14258c2ecf20Sopenharmony_ci * don't want to hit reserved instruction 14268c2ecf20Sopenharmony_ci * exceptions trying to save the MSA state later 14278c2ecf20Sopenharmony_ci * when CU=1 && FR=1, so play it safe and save 14288c2ecf20Sopenharmony_ci * it first. 14298c2ecf20Sopenharmony_ci */ 14308c2ecf20Sopenharmony_ci if (change & ST0_CU1 && !(val & ST0_FR) && 14318c2ecf20Sopenharmony_ci vcpu->arch.aux_inuse & KVM_MIPS_AUX_MSA) 14328c2ecf20Sopenharmony_ci kvm_lose_fpu(vcpu); 14338c2ecf20Sopenharmony_ci 14348c2ecf20Sopenharmony_ci /* 14358c2ecf20Sopenharmony_ci * Propagate CU1 (FPU enable) changes 14368c2ecf20Sopenharmony_ci * immediately if the FPU context is already 14378c2ecf20Sopenharmony_ci * loaded. When disabling we leave the context 14388c2ecf20Sopenharmony_ci * loaded so it can be quickly enabled again in 14398c2ecf20Sopenharmony_ci * the near future. 14408c2ecf20Sopenharmony_ci */ 14418c2ecf20Sopenharmony_ci if (change & ST0_CU1 && 14428c2ecf20Sopenharmony_ci vcpu->arch.aux_inuse & KVM_MIPS_AUX_FPU) 14438c2ecf20Sopenharmony_ci change_c0_status(ST0_CU1, val); 14448c2ecf20Sopenharmony_ci 14458c2ecf20Sopenharmony_ci preempt_enable(); 14468c2ecf20Sopenharmony_ci 14478c2ecf20Sopenharmony_ci kvm_write_c0_guest_status(cop0, val); 14488c2ecf20Sopenharmony_ci 14498c2ecf20Sopenharmony_ci#ifdef CONFIG_KVM_MIPS_DYN_TRANS 14508c2ecf20Sopenharmony_ci /* 14518c2ecf20Sopenharmony_ci * If FPU present, we need CU1/FR bits to take 14528c2ecf20Sopenharmony_ci * effect fairly soon. 14538c2ecf20Sopenharmony_ci */ 14548c2ecf20Sopenharmony_ci if (!kvm_mips_guest_has_fpu(&vcpu->arch)) 14558c2ecf20Sopenharmony_ci kvm_mips_trans_mtc0(inst, opc, vcpu); 14568c2ecf20Sopenharmony_ci#endif 14578c2ecf20Sopenharmony_ci } else if ((rd == MIPS_CP0_CONFIG) && (sel == 5)) { 14588c2ecf20Sopenharmony_ci unsigned int old_val, val, change, wrmask; 14598c2ecf20Sopenharmony_ci 14608c2ecf20Sopenharmony_ci old_val = kvm_read_c0_guest_config5(cop0); 14618c2ecf20Sopenharmony_ci val = vcpu->arch.gprs[rt]; 14628c2ecf20Sopenharmony_ci 14638c2ecf20Sopenharmony_ci /* Only a few bits are writable in Config5 */ 14648c2ecf20Sopenharmony_ci wrmask = kvm_mips_config5_wrmask(vcpu); 14658c2ecf20Sopenharmony_ci change = (val ^ old_val) & wrmask; 14668c2ecf20Sopenharmony_ci val = old_val ^ change; 14678c2ecf20Sopenharmony_ci 14688c2ecf20Sopenharmony_ci 14698c2ecf20Sopenharmony_ci /* Handle changes in FPU/MSA modes */ 14708c2ecf20Sopenharmony_ci preempt_disable(); 14718c2ecf20Sopenharmony_ci 14728c2ecf20Sopenharmony_ci /* 14738c2ecf20Sopenharmony_ci * Propagate FRE changes immediately if the FPU 14748c2ecf20Sopenharmony_ci * context is already loaded. 14758c2ecf20Sopenharmony_ci */ 14768c2ecf20Sopenharmony_ci if (change & MIPS_CONF5_FRE && 14778c2ecf20Sopenharmony_ci vcpu->arch.aux_inuse & KVM_MIPS_AUX_FPU) 14788c2ecf20Sopenharmony_ci change_c0_config5(MIPS_CONF5_FRE, val); 14798c2ecf20Sopenharmony_ci 14808c2ecf20Sopenharmony_ci /* 14818c2ecf20Sopenharmony_ci * Propagate MSAEn changes immediately if the 14828c2ecf20Sopenharmony_ci * MSA context is already loaded. When disabling 14838c2ecf20Sopenharmony_ci * we leave the context loaded so it can be 14848c2ecf20Sopenharmony_ci * quickly enabled again in the near future. 14858c2ecf20Sopenharmony_ci */ 14868c2ecf20Sopenharmony_ci if (change & MIPS_CONF5_MSAEN && 14878c2ecf20Sopenharmony_ci vcpu->arch.aux_inuse & KVM_MIPS_AUX_MSA) 14888c2ecf20Sopenharmony_ci change_c0_config5(MIPS_CONF5_MSAEN, 14898c2ecf20Sopenharmony_ci val); 14908c2ecf20Sopenharmony_ci 14918c2ecf20Sopenharmony_ci preempt_enable(); 14928c2ecf20Sopenharmony_ci 14938c2ecf20Sopenharmony_ci kvm_write_c0_guest_config5(cop0, val); 14948c2ecf20Sopenharmony_ci } else if ((rd == MIPS_CP0_CAUSE) && (sel == 0)) { 14958c2ecf20Sopenharmony_ci u32 old_cause, new_cause; 14968c2ecf20Sopenharmony_ci 14978c2ecf20Sopenharmony_ci old_cause = kvm_read_c0_guest_cause(cop0); 14988c2ecf20Sopenharmony_ci new_cause = vcpu->arch.gprs[rt]; 14998c2ecf20Sopenharmony_ci /* Update R/W bits */ 15008c2ecf20Sopenharmony_ci kvm_change_c0_guest_cause(cop0, 0x08800300, 15018c2ecf20Sopenharmony_ci new_cause); 15028c2ecf20Sopenharmony_ci /* DC bit enabling/disabling timer? */ 15038c2ecf20Sopenharmony_ci if ((old_cause ^ new_cause) & CAUSEF_DC) { 15048c2ecf20Sopenharmony_ci if (new_cause & CAUSEF_DC) 15058c2ecf20Sopenharmony_ci kvm_mips_count_disable_cause(vcpu); 15068c2ecf20Sopenharmony_ci else 15078c2ecf20Sopenharmony_ci kvm_mips_count_enable_cause(vcpu); 15088c2ecf20Sopenharmony_ci } 15098c2ecf20Sopenharmony_ci } else if ((rd == MIPS_CP0_HWRENA) && (sel == 0)) { 15108c2ecf20Sopenharmony_ci u32 mask = MIPS_HWRENA_CPUNUM | 15118c2ecf20Sopenharmony_ci MIPS_HWRENA_SYNCISTEP | 15128c2ecf20Sopenharmony_ci MIPS_HWRENA_CC | 15138c2ecf20Sopenharmony_ci MIPS_HWRENA_CCRES; 15148c2ecf20Sopenharmony_ci 15158c2ecf20Sopenharmony_ci if (kvm_read_c0_guest_config3(cop0) & 15168c2ecf20Sopenharmony_ci MIPS_CONF3_ULRI) 15178c2ecf20Sopenharmony_ci mask |= MIPS_HWRENA_ULR; 15188c2ecf20Sopenharmony_ci cop0->reg[rd][sel] = vcpu->arch.gprs[rt] & mask; 15198c2ecf20Sopenharmony_ci } else { 15208c2ecf20Sopenharmony_ci cop0->reg[rd][sel] = vcpu->arch.gprs[rt]; 15218c2ecf20Sopenharmony_ci#ifdef CONFIG_KVM_MIPS_DYN_TRANS 15228c2ecf20Sopenharmony_ci kvm_mips_trans_mtc0(inst, opc, vcpu); 15238c2ecf20Sopenharmony_ci#endif 15248c2ecf20Sopenharmony_ci } 15258c2ecf20Sopenharmony_ci break; 15268c2ecf20Sopenharmony_ci 15278c2ecf20Sopenharmony_ci case dmtc_op: 15288c2ecf20Sopenharmony_ci kvm_err("!!!!!!![%#lx]dmtc_op: rt: %d, rd: %d, sel: %d!!!!!!\n", 15298c2ecf20Sopenharmony_ci vcpu->arch.pc, rt, rd, sel); 15308c2ecf20Sopenharmony_ci trace_kvm_hwr(vcpu, KVM_TRACE_DMTC0, 15318c2ecf20Sopenharmony_ci KVM_TRACE_COP0(rd, sel), 15328c2ecf20Sopenharmony_ci vcpu->arch.gprs[rt]); 15338c2ecf20Sopenharmony_ci er = EMULATE_FAIL; 15348c2ecf20Sopenharmony_ci break; 15358c2ecf20Sopenharmony_ci 15368c2ecf20Sopenharmony_ci case mfmc0_op: 15378c2ecf20Sopenharmony_ci#ifdef KVM_MIPS_DEBUG_COP0_COUNTERS 15388c2ecf20Sopenharmony_ci cop0->stat[MIPS_CP0_STATUS][0]++; 15398c2ecf20Sopenharmony_ci#endif 15408c2ecf20Sopenharmony_ci if (rt != 0) 15418c2ecf20Sopenharmony_ci vcpu->arch.gprs[rt] = 15428c2ecf20Sopenharmony_ci kvm_read_c0_guest_status(cop0); 15438c2ecf20Sopenharmony_ci /* EI */ 15448c2ecf20Sopenharmony_ci if (inst.mfmc0_format.sc) { 15458c2ecf20Sopenharmony_ci kvm_debug("[%#lx] mfmc0_op: EI\n", 15468c2ecf20Sopenharmony_ci vcpu->arch.pc); 15478c2ecf20Sopenharmony_ci kvm_set_c0_guest_status(cop0, ST0_IE); 15488c2ecf20Sopenharmony_ci } else { 15498c2ecf20Sopenharmony_ci kvm_debug("[%#lx] mfmc0_op: DI\n", 15508c2ecf20Sopenharmony_ci vcpu->arch.pc); 15518c2ecf20Sopenharmony_ci kvm_clear_c0_guest_status(cop0, ST0_IE); 15528c2ecf20Sopenharmony_ci } 15538c2ecf20Sopenharmony_ci 15548c2ecf20Sopenharmony_ci break; 15558c2ecf20Sopenharmony_ci 15568c2ecf20Sopenharmony_ci case wrpgpr_op: 15578c2ecf20Sopenharmony_ci { 15588c2ecf20Sopenharmony_ci u32 css = cop0->reg[MIPS_CP0_STATUS][2] & 0xf; 15598c2ecf20Sopenharmony_ci u32 pss = 15608c2ecf20Sopenharmony_ci (cop0->reg[MIPS_CP0_STATUS][2] >> 6) & 0xf; 15618c2ecf20Sopenharmony_ci /* 15628c2ecf20Sopenharmony_ci * We don't support any shadow register sets, so 15638c2ecf20Sopenharmony_ci * SRSCtl[PSS] == SRSCtl[CSS] = 0 15648c2ecf20Sopenharmony_ci */ 15658c2ecf20Sopenharmony_ci if (css || pss) { 15668c2ecf20Sopenharmony_ci er = EMULATE_FAIL; 15678c2ecf20Sopenharmony_ci break; 15688c2ecf20Sopenharmony_ci } 15698c2ecf20Sopenharmony_ci kvm_debug("WRPGPR[%d][%d] = %#lx\n", pss, rd, 15708c2ecf20Sopenharmony_ci vcpu->arch.gprs[rt]); 15718c2ecf20Sopenharmony_ci vcpu->arch.gprs[rd] = vcpu->arch.gprs[rt]; 15728c2ecf20Sopenharmony_ci } 15738c2ecf20Sopenharmony_ci break; 15748c2ecf20Sopenharmony_ci default: 15758c2ecf20Sopenharmony_ci kvm_err("[%#lx]MachEmulateCP0: unsupported COP0, copz: 0x%x\n", 15768c2ecf20Sopenharmony_ci vcpu->arch.pc, inst.c0r_format.rs); 15778c2ecf20Sopenharmony_ci er = EMULATE_FAIL; 15788c2ecf20Sopenharmony_ci break; 15798c2ecf20Sopenharmony_ci } 15808c2ecf20Sopenharmony_ci } 15818c2ecf20Sopenharmony_ci 15828c2ecf20Sopenharmony_cidone: 15838c2ecf20Sopenharmony_ci /* Rollback PC only if emulation was unsuccessful */ 15848c2ecf20Sopenharmony_ci if (er == EMULATE_FAIL) 15858c2ecf20Sopenharmony_ci vcpu->arch.pc = curr_pc; 15868c2ecf20Sopenharmony_ci 15878c2ecf20Sopenharmony_cidont_update_pc: 15888c2ecf20Sopenharmony_ci /* 15898c2ecf20Sopenharmony_ci * This is for special instructions whose emulation 15908c2ecf20Sopenharmony_ci * updates the PC, so do not overwrite the PC under 15918c2ecf20Sopenharmony_ci * any circumstances 15928c2ecf20Sopenharmony_ci */ 15938c2ecf20Sopenharmony_ci 15948c2ecf20Sopenharmony_ci return er; 15958c2ecf20Sopenharmony_ci} 15968c2ecf20Sopenharmony_ci 15978c2ecf20Sopenharmony_cienum emulation_result kvm_mips_emulate_store(union mips_instruction inst, 15988c2ecf20Sopenharmony_ci u32 cause, 15998c2ecf20Sopenharmony_ci struct kvm_vcpu *vcpu) 16008c2ecf20Sopenharmony_ci{ 16018c2ecf20Sopenharmony_ci int r; 16028c2ecf20Sopenharmony_ci enum emulation_result er; 16038c2ecf20Sopenharmony_ci u32 rt; 16048c2ecf20Sopenharmony_ci struct kvm_run *run = vcpu->run; 16058c2ecf20Sopenharmony_ci void *data = run->mmio.data; 16068c2ecf20Sopenharmony_ci unsigned int imme; 16078c2ecf20Sopenharmony_ci unsigned long curr_pc; 16088c2ecf20Sopenharmony_ci 16098c2ecf20Sopenharmony_ci /* 16108c2ecf20Sopenharmony_ci * Update PC and hold onto current PC in case there is 16118c2ecf20Sopenharmony_ci * an error and we want to rollback the PC 16128c2ecf20Sopenharmony_ci */ 16138c2ecf20Sopenharmony_ci curr_pc = vcpu->arch.pc; 16148c2ecf20Sopenharmony_ci er = update_pc(vcpu, cause); 16158c2ecf20Sopenharmony_ci if (er == EMULATE_FAIL) 16168c2ecf20Sopenharmony_ci return er; 16178c2ecf20Sopenharmony_ci 16188c2ecf20Sopenharmony_ci rt = inst.i_format.rt; 16198c2ecf20Sopenharmony_ci 16208c2ecf20Sopenharmony_ci run->mmio.phys_addr = kvm_mips_callbacks->gva_to_gpa( 16218c2ecf20Sopenharmony_ci vcpu->arch.host_cp0_badvaddr); 16228c2ecf20Sopenharmony_ci if (run->mmio.phys_addr == KVM_INVALID_ADDR) 16238c2ecf20Sopenharmony_ci goto out_fail; 16248c2ecf20Sopenharmony_ci 16258c2ecf20Sopenharmony_ci switch (inst.i_format.opcode) { 16268c2ecf20Sopenharmony_ci#if defined(CONFIG_64BIT) && defined(CONFIG_KVM_MIPS_VZ) 16278c2ecf20Sopenharmony_ci case sd_op: 16288c2ecf20Sopenharmony_ci run->mmio.len = 8; 16298c2ecf20Sopenharmony_ci *(u64 *)data = vcpu->arch.gprs[rt]; 16308c2ecf20Sopenharmony_ci 16318c2ecf20Sopenharmony_ci kvm_debug("[%#lx] OP_SD: eaddr: %#lx, gpr: %#lx, data: %#llx\n", 16328c2ecf20Sopenharmony_ci vcpu->arch.pc, vcpu->arch.host_cp0_badvaddr, 16338c2ecf20Sopenharmony_ci vcpu->arch.gprs[rt], *(u64 *)data); 16348c2ecf20Sopenharmony_ci break; 16358c2ecf20Sopenharmony_ci#endif 16368c2ecf20Sopenharmony_ci 16378c2ecf20Sopenharmony_ci case sw_op: 16388c2ecf20Sopenharmony_ci run->mmio.len = 4; 16398c2ecf20Sopenharmony_ci *(u32 *)data = vcpu->arch.gprs[rt]; 16408c2ecf20Sopenharmony_ci 16418c2ecf20Sopenharmony_ci kvm_debug("[%#lx] OP_SW: eaddr: %#lx, gpr: %#lx, data: %#x\n", 16428c2ecf20Sopenharmony_ci vcpu->arch.pc, vcpu->arch.host_cp0_badvaddr, 16438c2ecf20Sopenharmony_ci vcpu->arch.gprs[rt], *(u32 *)data); 16448c2ecf20Sopenharmony_ci break; 16458c2ecf20Sopenharmony_ci 16468c2ecf20Sopenharmony_ci case sh_op: 16478c2ecf20Sopenharmony_ci run->mmio.len = 2; 16488c2ecf20Sopenharmony_ci *(u16 *)data = vcpu->arch.gprs[rt]; 16498c2ecf20Sopenharmony_ci 16508c2ecf20Sopenharmony_ci kvm_debug("[%#lx] OP_SH: eaddr: %#lx, gpr: %#lx, data: %#x\n", 16518c2ecf20Sopenharmony_ci vcpu->arch.pc, vcpu->arch.host_cp0_badvaddr, 16528c2ecf20Sopenharmony_ci vcpu->arch.gprs[rt], *(u16 *)data); 16538c2ecf20Sopenharmony_ci break; 16548c2ecf20Sopenharmony_ci 16558c2ecf20Sopenharmony_ci case sb_op: 16568c2ecf20Sopenharmony_ci run->mmio.len = 1; 16578c2ecf20Sopenharmony_ci *(u8 *)data = vcpu->arch.gprs[rt]; 16588c2ecf20Sopenharmony_ci 16598c2ecf20Sopenharmony_ci kvm_debug("[%#lx] OP_SB: eaddr: %#lx, gpr: %#lx, data: %#x\n", 16608c2ecf20Sopenharmony_ci vcpu->arch.pc, vcpu->arch.host_cp0_badvaddr, 16618c2ecf20Sopenharmony_ci vcpu->arch.gprs[rt], *(u8 *)data); 16628c2ecf20Sopenharmony_ci break; 16638c2ecf20Sopenharmony_ci 16648c2ecf20Sopenharmony_ci case swl_op: 16658c2ecf20Sopenharmony_ci run->mmio.phys_addr = kvm_mips_callbacks->gva_to_gpa( 16668c2ecf20Sopenharmony_ci vcpu->arch.host_cp0_badvaddr) & (~0x3); 16678c2ecf20Sopenharmony_ci run->mmio.len = 4; 16688c2ecf20Sopenharmony_ci imme = vcpu->arch.host_cp0_badvaddr & 0x3; 16698c2ecf20Sopenharmony_ci switch (imme) { 16708c2ecf20Sopenharmony_ci case 0: 16718c2ecf20Sopenharmony_ci *(u32 *)data = ((*(u32 *)data) & 0xffffff00) | 16728c2ecf20Sopenharmony_ci (vcpu->arch.gprs[rt] >> 24); 16738c2ecf20Sopenharmony_ci break; 16748c2ecf20Sopenharmony_ci case 1: 16758c2ecf20Sopenharmony_ci *(u32 *)data = ((*(u32 *)data) & 0xffff0000) | 16768c2ecf20Sopenharmony_ci (vcpu->arch.gprs[rt] >> 16); 16778c2ecf20Sopenharmony_ci break; 16788c2ecf20Sopenharmony_ci case 2: 16798c2ecf20Sopenharmony_ci *(u32 *)data = ((*(u32 *)data) & 0xff000000) | 16808c2ecf20Sopenharmony_ci (vcpu->arch.gprs[rt] >> 8); 16818c2ecf20Sopenharmony_ci break; 16828c2ecf20Sopenharmony_ci case 3: 16838c2ecf20Sopenharmony_ci *(u32 *)data = vcpu->arch.gprs[rt]; 16848c2ecf20Sopenharmony_ci break; 16858c2ecf20Sopenharmony_ci default: 16868c2ecf20Sopenharmony_ci break; 16878c2ecf20Sopenharmony_ci } 16888c2ecf20Sopenharmony_ci 16898c2ecf20Sopenharmony_ci kvm_debug("[%#lx] OP_SWL: eaddr: %#lx, gpr: %#lx, data: %#x\n", 16908c2ecf20Sopenharmony_ci vcpu->arch.pc, vcpu->arch.host_cp0_badvaddr, 16918c2ecf20Sopenharmony_ci vcpu->arch.gprs[rt], *(u32 *)data); 16928c2ecf20Sopenharmony_ci break; 16938c2ecf20Sopenharmony_ci 16948c2ecf20Sopenharmony_ci case swr_op: 16958c2ecf20Sopenharmony_ci run->mmio.phys_addr = kvm_mips_callbacks->gva_to_gpa( 16968c2ecf20Sopenharmony_ci vcpu->arch.host_cp0_badvaddr) & (~0x3); 16978c2ecf20Sopenharmony_ci run->mmio.len = 4; 16988c2ecf20Sopenharmony_ci imme = vcpu->arch.host_cp0_badvaddr & 0x3; 16998c2ecf20Sopenharmony_ci switch (imme) { 17008c2ecf20Sopenharmony_ci case 0: 17018c2ecf20Sopenharmony_ci *(u32 *)data = vcpu->arch.gprs[rt]; 17028c2ecf20Sopenharmony_ci break; 17038c2ecf20Sopenharmony_ci case 1: 17048c2ecf20Sopenharmony_ci *(u32 *)data = ((*(u32 *)data) & 0xff) | 17058c2ecf20Sopenharmony_ci (vcpu->arch.gprs[rt] << 8); 17068c2ecf20Sopenharmony_ci break; 17078c2ecf20Sopenharmony_ci case 2: 17088c2ecf20Sopenharmony_ci *(u32 *)data = ((*(u32 *)data) & 0xffff) | 17098c2ecf20Sopenharmony_ci (vcpu->arch.gprs[rt] << 16); 17108c2ecf20Sopenharmony_ci break; 17118c2ecf20Sopenharmony_ci case 3: 17128c2ecf20Sopenharmony_ci *(u32 *)data = ((*(u32 *)data) & 0xffffff) | 17138c2ecf20Sopenharmony_ci (vcpu->arch.gprs[rt] << 24); 17148c2ecf20Sopenharmony_ci break; 17158c2ecf20Sopenharmony_ci default: 17168c2ecf20Sopenharmony_ci break; 17178c2ecf20Sopenharmony_ci } 17188c2ecf20Sopenharmony_ci 17198c2ecf20Sopenharmony_ci kvm_debug("[%#lx] OP_SWR: eaddr: %#lx, gpr: %#lx, data: %#x\n", 17208c2ecf20Sopenharmony_ci vcpu->arch.pc, vcpu->arch.host_cp0_badvaddr, 17218c2ecf20Sopenharmony_ci vcpu->arch.gprs[rt], *(u32 *)data); 17228c2ecf20Sopenharmony_ci break; 17238c2ecf20Sopenharmony_ci 17248c2ecf20Sopenharmony_ci#if defined(CONFIG_64BIT) && defined(CONFIG_KVM_MIPS_VZ) 17258c2ecf20Sopenharmony_ci case sdl_op: 17268c2ecf20Sopenharmony_ci run->mmio.phys_addr = kvm_mips_callbacks->gva_to_gpa( 17278c2ecf20Sopenharmony_ci vcpu->arch.host_cp0_badvaddr) & (~0x7); 17288c2ecf20Sopenharmony_ci 17298c2ecf20Sopenharmony_ci run->mmio.len = 8; 17308c2ecf20Sopenharmony_ci imme = vcpu->arch.host_cp0_badvaddr & 0x7; 17318c2ecf20Sopenharmony_ci switch (imme) { 17328c2ecf20Sopenharmony_ci case 0: 17338c2ecf20Sopenharmony_ci *(u64 *)data = ((*(u64 *)data) & 0xffffffffffffff00) | 17348c2ecf20Sopenharmony_ci ((vcpu->arch.gprs[rt] >> 56) & 0xff); 17358c2ecf20Sopenharmony_ci break; 17368c2ecf20Sopenharmony_ci case 1: 17378c2ecf20Sopenharmony_ci *(u64 *)data = ((*(u64 *)data) & 0xffffffffffff0000) | 17388c2ecf20Sopenharmony_ci ((vcpu->arch.gprs[rt] >> 48) & 0xffff); 17398c2ecf20Sopenharmony_ci break; 17408c2ecf20Sopenharmony_ci case 2: 17418c2ecf20Sopenharmony_ci *(u64 *)data = ((*(u64 *)data) & 0xffffffffff000000) | 17428c2ecf20Sopenharmony_ci ((vcpu->arch.gprs[rt] >> 40) & 0xffffff); 17438c2ecf20Sopenharmony_ci break; 17448c2ecf20Sopenharmony_ci case 3: 17458c2ecf20Sopenharmony_ci *(u64 *)data = ((*(u64 *)data) & 0xffffffff00000000) | 17468c2ecf20Sopenharmony_ci ((vcpu->arch.gprs[rt] >> 32) & 0xffffffff); 17478c2ecf20Sopenharmony_ci break; 17488c2ecf20Sopenharmony_ci case 4: 17498c2ecf20Sopenharmony_ci *(u64 *)data = ((*(u64 *)data) & 0xffffff0000000000) | 17508c2ecf20Sopenharmony_ci ((vcpu->arch.gprs[rt] >> 24) & 0xffffffffff); 17518c2ecf20Sopenharmony_ci break; 17528c2ecf20Sopenharmony_ci case 5: 17538c2ecf20Sopenharmony_ci *(u64 *)data = ((*(u64 *)data) & 0xffff000000000000) | 17548c2ecf20Sopenharmony_ci ((vcpu->arch.gprs[rt] >> 16) & 0xffffffffffff); 17558c2ecf20Sopenharmony_ci break; 17568c2ecf20Sopenharmony_ci case 6: 17578c2ecf20Sopenharmony_ci *(u64 *)data = ((*(u64 *)data) & 0xff00000000000000) | 17588c2ecf20Sopenharmony_ci ((vcpu->arch.gprs[rt] >> 8) & 0xffffffffffffff); 17598c2ecf20Sopenharmony_ci break; 17608c2ecf20Sopenharmony_ci case 7: 17618c2ecf20Sopenharmony_ci *(u64 *)data = vcpu->arch.gprs[rt]; 17628c2ecf20Sopenharmony_ci break; 17638c2ecf20Sopenharmony_ci default: 17648c2ecf20Sopenharmony_ci break; 17658c2ecf20Sopenharmony_ci } 17668c2ecf20Sopenharmony_ci 17678c2ecf20Sopenharmony_ci kvm_debug("[%#lx] OP_SDL: eaddr: %#lx, gpr: %#lx, data: %llx\n", 17688c2ecf20Sopenharmony_ci vcpu->arch.pc, vcpu->arch.host_cp0_badvaddr, 17698c2ecf20Sopenharmony_ci vcpu->arch.gprs[rt], *(u64 *)data); 17708c2ecf20Sopenharmony_ci break; 17718c2ecf20Sopenharmony_ci 17728c2ecf20Sopenharmony_ci case sdr_op: 17738c2ecf20Sopenharmony_ci run->mmio.phys_addr = kvm_mips_callbacks->gva_to_gpa( 17748c2ecf20Sopenharmony_ci vcpu->arch.host_cp0_badvaddr) & (~0x7); 17758c2ecf20Sopenharmony_ci 17768c2ecf20Sopenharmony_ci run->mmio.len = 8; 17778c2ecf20Sopenharmony_ci imme = vcpu->arch.host_cp0_badvaddr & 0x7; 17788c2ecf20Sopenharmony_ci switch (imme) { 17798c2ecf20Sopenharmony_ci case 0: 17808c2ecf20Sopenharmony_ci *(u64 *)data = vcpu->arch.gprs[rt]; 17818c2ecf20Sopenharmony_ci break; 17828c2ecf20Sopenharmony_ci case 1: 17838c2ecf20Sopenharmony_ci *(u64 *)data = ((*(u64 *)data) & 0xff) | 17848c2ecf20Sopenharmony_ci (vcpu->arch.gprs[rt] << 8); 17858c2ecf20Sopenharmony_ci break; 17868c2ecf20Sopenharmony_ci case 2: 17878c2ecf20Sopenharmony_ci *(u64 *)data = ((*(u64 *)data) & 0xffff) | 17888c2ecf20Sopenharmony_ci (vcpu->arch.gprs[rt] << 16); 17898c2ecf20Sopenharmony_ci break; 17908c2ecf20Sopenharmony_ci case 3: 17918c2ecf20Sopenharmony_ci *(u64 *)data = ((*(u64 *)data) & 0xffffff) | 17928c2ecf20Sopenharmony_ci (vcpu->arch.gprs[rt] << 24); 17938c2ecf20Sopenharmony_ci break; 17948c2ecf20Sopenharmony_ci case 4: 17958c2ecf20Sopenharmony_ci *(u64 *)data = ((*(u64 *)data) & 0xffffffff) | 17968c2ecf20Sopenharmony_ci (vcpu->arch.gprs[rt] << 32); 17978c2ecf20Sopenharmony_ci break; 17988c2ecf20Sopenharmony_ci case 5: 17998c2ecf20Sopenharmony_ci *(u64 *)data = ((*(u64 *)data) & 0xffffffffff) | 18008c2ecf20Sopenharmony_ci (vcpu->arch.gprs[rt] << 40); 18018c2ecf20Sopenharmony_ci break; 18028c2ecf20Sopenharmony_ci case 6: 18038c2ecf20Sopenharmony_ci *(u64 *)data = ((*(u64 *)data) & 0xffffffffffff) | 18048c2ecf20Sopenharmony_ci (vcpu->arch.gprs[rt] << 48); 18058c2ecf20Sopenharmony_ci break; 18068c2ecf20Sopenharmony_ci case 7: 18078c2ecf20Sopenharmony_ci *(u64 *)data = ((*(u64 *)data) & 0xffffffffffffff) | 18088c2ecf20Sopenharmony_ci (vcpu->arch.gprs[rt] << 56); 18098c2ecf20Sopenharmony_ci break; 18108c2ecf20Sopenharmony_ci default: 18118c2ecf20Sopenharmony_ci break; 18128c2ecf20Sopenharmony_ci } 18138c2ecf20Sopenharmony_ci 18148c2ecf20Sopenharmony_ci kvm_debug("[%#lx] OP_SDR: eaddr: %#lx, gpr: %#lx, data: %llx\n", 18158c2ecf20Sopenharmony_ci vcpu->arch.pc, vcpu->arch.host_cp0_badvaddr, 18168c2ecf20Sopenharmony_ci vcpu->arch.gprs[rt], *(u64 *)data); 18178c2ecf20Sopenharmony_ci break; 18188c2ecf20Sopenharmony_ci#endif 18198c2ecf20Sopenharmony_ci 18208c2ecf20Sopenharmony_ci#ifdef CONFIG_CPU_LOONGSON64 18218c2ecf20Sopenharmony_ci case sdc2_op: 18228c2ecf20Sopenharmony_ci rt = inst.loongson3_lsdc2_format.rt; 18238c2ecf20Sopenharmony_ci switch (inst.loongson3_lsdc2_format.opcode1) { 18248c2ecf20Sopenharmony_ci /* 18258c2ecf20Sopenharmony_ci * Loongson-3 overridden sdc2 instructions. 18268c2ecf20Sopenharmony_ci * opcode1 instruction 18278c2ecf20Sopenharmony_ci * 0x0 gssbx: store 1 bytes from GPR 18288c2ecf20Sopenharmony_ci * 0x1 gsshx: store 2 bytes from GPR 18298c2ecf20Sopenharmony_ci * 0x2 gsswx: store 4 bytes from GPR 18308c2ecf20Sopenharmony_ci * 0x3 gssdx: store 8 bytes from GPR 18318c2ecf20Sopenharmony_ci */ 18328c2ecf20Sopenharmony_ci case 0x0: 18338c2ecf20Sopenharmony_ci run->mmio.len = 1; 18348c2ecf20Sopenharmony_ci *(u8 *)data = vcpu->arch.gprs[rt]; 18358c2ecf20Sopenharmony_ci 18368c2ecf20Sopenharmony_ci kvm_debug("[%#lx] OP_GSSBX: eaddr: %#lx, gpr: %#lx, data: %#x\n", 18378c2ecf20Sopenharmony_ci vcpu->arch.pc, vcpu->arch.host_cp0_badvaddr, 18388c2ecf20Sopenharmony_ci vcpu->arch.gprs[rt], *(u8 *)data); 18398c2ecf20Sopenharmony_ci break; 18408c2ecf20Sopenharmony_ci case 0x1: 18418c2ecf20Sopenharmony_ci run->mmio.len = 2; 18428c2ecf20Sopenharmony_ci *(u16 *)data = vcpu->arch.gprs[rt]; 18438c2ecf20Sopenharmony_ci 18448c2ecf20Sopenharmony_ci kvm_debug("[%#lx] OP_GSSSHX: eaddr: %#lx, gpr: %#lx, data: %#x\n", 18458c2ecf20Sopenharmony_ci vcpu->arch.pc, vcpu->arch.host_cp0_badvaddr, 18468c2ecf20Sopenharmony_ci vcpu->arch.gprs[rt], *(u16 *)data); 18478c2ecf20Sopenharmony_ci break; 18488c2ecf20Sopenharmony_ci case 0x2: 18498c2ecf20Sopenharmony_ci run->mmio.len = 4; 18508c2ecf20Sopenharmony_ci *(u32 *)data = vcpu->arch.gprs[rt]; 18518c2ecf20Sopenharmony_ci 18528c2ecf20Sopenharmony_ci kvm_debug("[%#lx] OP_GSSWX: eaddr: %#lx, gpr: %#lx, data: %#x\n", 18538c2ecf20Sopenharmony_ci vcpu->arch.pc, vcpu->arch.host_cp0_badvaddr, 18548c2ecf20Sopenharmony_ci vcpu->arch.gprs[rt], *(u32 *)data); 18558c2ecf20Sopenharmony_ci break; 18568c2ecf20Sopenharmony_ci case 0x3: 18578c2ecf20Sopenharmony_ci run->mmio.len = 8; 18588c2ecf20Sopenharmony_ci *(u64 *)data = vcpu->arch.gprs[rt]; 18598c2ecf20Sopenharmony_ci 18608c2ecf20Sopenharmony_ci kvm_debug("[%#lx] OP_GSSDX: eaddr: %#lx, gpr: %#lx, data: %#llx\n", 18618c2ecf20Sopenharmony_ci vcpu->arch.pc, vcpu->arch.host_cp0_badvaddr, 18628c2ecf20Sopenharmony_ci vcpu->arch.gprs[rt], *(u64 *)data); 18638c2ecf20Sopenharmony_ci break; 18648c2ecf20Sopenharmony_ci default: 18658c2ecf20Sopenharmony_ci kvm_err("Godson Extended GS-Store not yet supported (inst=0x%08x)\n", 18668c2ecf20Sopenharmony_ci inst.word); 18678c2ecf20Sopenharmony_ci break; 18688c2ecf20Sopenharmony_ci } 18698c2ecf20Sopenharmony_ci break; 18708c2ecf20Sopenharmony_ci#endif 18718c2ecf20Sopenharmony_ci default: 18728c2ecf20Sopenharmony_ci kvm_err("Store not yet supported (inst=0x%08x)\n", 18738c2ecf20Sopenharmony_ci inst.word); 18748c2ecf20Sopenharmony_ci goto out_fail; 18758c2ecf20Sopenharmony_ci } 18768c2ecf20Sopenharmony_ci 18778c2ecf20Sopenharmony_ci vcpu->mmio_needed = 1; 18788c2ecf20Sopenharmony_ci run->mmio.is_write = 1; 18798c2ecf20Sopenharmony_ci vcpu->mmio_is_write = 1; 18808c2ecf20Sopenharmony_ci 18818c2ecf20Sopenharmony_ci r = kvm_io_bus_write(vcpu, KVM_MMIO_BUS, 18828c2ecf20Sopenharmony_ci run->mmio.phys_addr, run->mmio.len, data); 18838c2ecf20Sopenharmony_ci 18848c2ecf20Sopenharmony_ci if (!r) { 18858c2ecf20Sopenharmony_ci vcpu->mmio_needed = 0; 18868c2ecf20Sopenharmony_ci return EMULATE_DONE; 18878c2ecf20Sopenharmony_ci } 18888c2ecf20Sopenharmony_ci 18898c2ecf20Sopenharmony_ci return EMULATE_DO_MMIO; 18908c2ecf20Sopenharmony_ci 18918c2ecf20Sopenharmony_ciout_fail: 18928c2ecf20Sopenharmony_ci /* Rollback PC if emulation was unsuccessful */ 18938c2ecf20Sopenharmony_ci vcpu->arch.pc = curr_pc; 18948c2ecf20Sopenharmony_ci return EMULATE_FAIL; 18958c2ecf20Sopenharmony_ci} 18968c2ecf20Sopenharmony_ci 18978c2ecf20Sopenharmony_cienum emulation_result kvm_mips_emulate_load(union mips_instruction inst, 18988c2ecf20Sopenharmony_ci u32 cause, struct kvm_vcpu *vcpu) 18998c2ecf20Sopenharmony_ci{ 19008c2ecf20Sopenharmony_ci struct kvm_run *run = vcpu->run; 19018c2ecf20Sopenharmony_ci int r; 19028c2ecf20Sopenharmony_ci enum emulation_result er; 19038c2ecf20Sopenharmony_ci unsigned long curr_pc; 19048c2ecf20Sopenharmony_ci u32 op, rt; 19058c2ecf20Sopenharmony_ci unsigned int imme; 19068c2ecf20Sopenharmony_ci 19078c2ecf20Sopenharmony_ci rt = inst.i_format.rt; 19088c2ecf20Sopenharmony_ci op = inst.i_format.opcode; 19098c2ecf20Sopenharmony_ci 19108c2ecf20Sopenharmony_ci /* 19118c2ecf20Sopenharmony_ci * Find the resume PC now while we have safe and easy access to the 19128c2ecf20Sopenharmony_ci * prior branch instruction, and save it for 19138c2ecf20Sopenharmony_ci * kvm_mips_complete_mmio_load() to restore later. 19148c2ecf20Sopenharmony_ci */ 19158c2ecf20Sopenharmony_ci curr_pc = vcpu->arch.pc; 19168c2ecf20Sopenharmony_ci er = update_pc(vcpu, cause); 19178c2ecf20Sopenharmony_ci if (er == EMULATE_FAIL) 19188c2ecf20Sopenharmony_ci return er; 19198c2ecf20Sopenharmony_ci vcpu->arch.io_pc = vcpu->arch.pc; 19208c2ecf20Sopenharmony_ci vcpu->arch.pc = curr_pc; 19218c2ecf20Sopenharmony_ci 19228c2ecf20Sopenharmony_ci vcpu->arch.io_gpr = rt; 19238c2ecf20Sopenharmony_ci 19248c2ecf20Sopenharmony_ci run->mmio.phys_addr = kvm_mips_callbacks->gva_to_gpa( 19258c2ecf20Sopenharmony_ci vcpu->arch.host_cp0_badvaddr); 19268c2ecf20Sopenharmony_ci if (run->mmio.phys_addr == KVM_INVALID_ADDR) 19278c2ecf20Sopenharmony_ci return EMULATE_FAIL; 19288c2ecf20Sopenharmony_ci 19298c2ecf20Sopenharmony_ci vcpu->mmio_needed = 2; /* signed */ 19308c2ecf20Sopenharmony_ci switch (op) { 19318c2ecf20Sopenharmony_ci#if defined(CONFIG_64BIT) && defined(CONFIG_KVM_MIPS_VZ) 19328c2ecf20Sopenharmony_ci case ld_op: 19338c2ecf20Sopenharmony_ci run->mmio.len = 8; 19348c2ecf20Sopenharmony_ci break; 19358c2ecf20Sopenharmony_ci 19368c2ecf20Sopenharmony_ci case lwu_op: 19378c2ecf20Sopenharmony_ci vcpu->mmio_needed = 1; /* unsigned */ 19388c2ecf20Sopenharmony_ci fallthrough; 19398c2ecf20Sopenharmony_ci#endif 19408c2ecf20Sopenharmony_ci case lw_op: 19418c2ecf20Sopenharmony_ci run->mmio.len = 4; 19428c2ecf20Sopenharmony_ci break; 19438c2ecf20Sopenharmony_ci 19448c2ecf20Sopenharmony_ci case lhu_op: 19458c2ecf20Sopenharmony_ci vcpu->mmio_needed = 1; /* unsigned */ 19468c2ecf20Sopenharmony_ci fallthrough; 19478c2ecf20Sopenharmony_ci case lh_op: 19488c2ecf20Sopenharmony_ci run->mmio.len = 2; 19498c2ecf20Sopenharmony_ci break; 19508c2ecf20Sopenharmony_ci 19518c2ecf20Sopenharmony_ci case lbu_op: 19528c2ecf20Sopenharmony_ci vcpu->mmio_needed = 1; /* unsigned */ 19538c2ecf20Sopenharmony_ci fallthrough; 19548c2ecf20Sopenharmony_ci case lb_op: 19558c2ecf20Sopenharmony_ci run->mmio.len = 1; 19568c2ecf20Sopenharmony_ci break; 19578c2ecf20Sopenharmony_ci 19588c2ecf20Sopenharmony_ci case lwl_op: 19598c2ecf20Sopenharmony_ci run->mmio.phys_addr = kvm_mips_callbacks->gva_to_gpa( 19608c2ecf20Sopenharmony_ci vcpu->arch.host_cp0_badvaddr) & (~0x3); 19618c2ecf20Sopenharmony_ci 19628c2ecf20Sopenharmony_ci run->mmio.len = 4; 19638c2ecf20Sopenharmony_ci imme = vcpu->arch.host_cp0_badvaddr & 0x3; 19648c2ecf20Sopenharmony_ci switch (imme) { 19658c2ecf20Sopenharmony_ci case 0: 19668c2ecf20Sopenharmony_ci vcpu->mmio_needed = 3; /* 1 byte */ 19678c2ecf20Sopenharmony_ci break; 19688c2ecf20Sopenharmony_ci case 1: 19698c2ecf20Sopenharmony_ci vcpu->mmio_needed = 4; /* 2 bytes */ 19708c2ecf20Sopenharmony_ci break; 19718c2ecf20Sopenharmony_ci case 2: 19728c2ecf20Sopenharmony_ci vcpu->mmio_needed = 5; /* 3 bytes */ 19738c2ecf20Sopenharmony_ci break; 19748c2ecf20Sopenharmony_ci case 3: 19758c2ecf20Sopenharmony_ci vcpu->mmio_needed = 6; /* 4 bytes */ 19768c2ecf20Sopenharmony_ci break; 19778c2ecf20Sopenharmony_ci default: 19788c2ecf20Sopenharmony_ci break; 19798c2ecf20Sopenharmony_ci } 19808c2ecf20Sopenharmony_ci break; 19818c2ecf20Sopenharmony_ci 19828c2ecf20Sopenharmony_ci case lwr_op: 19838c2ecf20Sopenharmony_ci run->mmio.phys_addr = kvm_mips_callbacks->gva_to_gpa( 19848c2ecf20Sopenharmony_ci vcpu->arch.host_cp0_badvaddr) & (~0x3); 19858c2ecf20Sopenharmony_ci 19868c2ecf20Sopenharmony_ci run->mmio.len = 4; 19878c2ecf20Sopenharmony_ci imme = vcpu->arch.host_cp0_badvaddr & 0x3; 19888c2ecf20Sopenharmony_ci switch (imme) { 19898c2ecf20Sopenharmony_ci case 0: 19908c2ecf20Sopenharmony_ci vcpu->mmio_needed = 7; /* 4 bytes */ 19918c2ecf20Sopenharmony_ci break; 19928c2ecf20Sopenharmony_ci case 1: 19938c2ecf20Sopenharmony_ci vcpu->mmio_needed = 8; /* 3 bytes */ 19948c2ecf20Sopenharmony_ci break; 19958c2ecf20Sopenharmony_ci case 2: 19968c2ecf20Sopenharmony_ci vcpu->mmio_needed = 9; /* 2 bytes */ 19978c2ecf20Sopenharmony_ci break; 19988c2ecf20Sopenharmony_ci case 3: 19998c2ecf20Sopenharmony_ci vcpu->mmio_needed = 10; /* 1 byte */ 20008c2ecf20Sopenharmony_ci break; 20018c2ecf20Sopenharmony_ci default: 20028c2ecf20Sopenharmony_ci break; 20038c2ecf20Sopenharmony_ci } 20048c2ecf20Sopenharmony_ci break; 20058c2ecf20Sopenharmony_ci 20068c2ecf20Sopenharmony_ci#if defined(CONFIG_64BIT) && defined(CONFIG_KVM_MIPS_VZ) 20078c2ecf20Sopenharmony_ci case ldl_op: 20088c2ecf20Sopenharmony_ci run->mmio.phys_addr = kvm_mips_callbacks->gva_to_gpa( 20098c2ecf20Sopenharmony_ci vcpu->arch.host_cp0_badvaddr) & (~0x7); 20108c2ecf20Sopenharmony_ci 20118c2ecf20Sopenharmony_ci run->mmio.len = 8; 20128c2ecf20Sopenharmony_ci imme = vcpu->arch.host_cp0_badvaddr & 0x7; 20138c2ecf20Sopenharmony_ci switch (imme) { 20148c2ecf20Sopenharmony_ci case 0: 20158c2ecf20Sopenharmony_ci vcpu->mmio_needed = 11; /* 1 byte */ 20168c2ecf20Sopenharmony_ci break; 20178c2ecf20Sopenharmony_ci case 1: 20188c2ecf20Sopenharmony_ci vcpu->mmio_needed = 12; /* 2 bytes */ 20198c2ecf20Sopenharmony_ci break; 20208c2ecf20Sopenharmony_ci case 2: 20218c2ecf20Sopenharmony_ci vcpu->mmio_needed = 13; /* 3 bytes */ 20228c2ecf20Sopenharmony_ci break; 20238c2ecf20Sopenharmony_ci case 3: 20248c2ecf20Sopenharmony_ci vcpu->mmio_needed = 14; /* 4 bytes */ 20258c2ecf20Sopenharmony_ci break; 20268c2ecf20Sopenharmony_ci case 4: 20278c2ecf20Sopenharmony_ci vcpu->mmio_needed = 15; /* 5 bytes */ 20288c2ecf20Sopenharmony_ci break; 20298c2ecf20Sopenharmony_ci case 5: 20308c2ecf20Sopenharmony_ci vcpu->mmio_needed = 16; /* 6 bytes */ 20318c2ecf20Sopenharmony_ci break; 20328c2ecf20Sopenharmony_ci case 6: 20338c2ecf20Sopenharmony_ci vcpu->mmio_needed = 17; /* 7 bytes */ 20348c2ecf20Sopenharmony_ci break; 20358c2ecf20Sopenharmony_ci case 7: 20368c2ecf20Sopenharmony_ci vcpu->mmio_needed = 18; /* 8 bytes */ 20378c2ecf20Sopenharmony_ci break; 20388c2ecf20Sopenharmony_ci default: 20398c2ecf20Sopenharmony_ci break; 20408c2ecf20Sopenharmony_ci } 20418c2ecf20Sopenharmony_ci break; 20428c2ecf20Sopenharmony_ci 20438c2ecf20Sopenharmony_ci case ldr_op: 20448c2ecf20Sopenharmony_ci run->mmio.phys_addr = kvm_mips_callbacks->gva_to_gpa( 20458c2ecf20Sopenharmony_ci vcpu->arch.host_cp0_badvaddr) & (~0x7); 20468c2ecf20Sopenharmony_ci 20478c2ecf20Sopenharmony_ci run->mmio.len = 8; 20488c2ecf20Sopenharmony_ci imme = vcpu->arch.host_cp0_badvaddr & 0x7; 20498c2ecf20Sopenharmony_ci switch (imme) { 20508c2ecf20Sopenharmony_ci case 0: 20518c2ecf20Sopenharmony_ci vcpu->mmio_needed = 19; /* 8 bytes */ 20528c2ecf20Sopenharmony_ci break; 20538c2ecf20Sopenharmony_ci case 1: 20548c2ecf20Sopenharmony_ci vcpu->mmio_needed = 20; /* 7 bytes */ 20558c2ecf20Sopenharmony_ci break; 20568c2ecf20Sopenharmony_ci case 2: 20578c2ecf20Sopenharmony_ci vcpu->mmio_needed = 21; /* 6 bytes */ 20588c2ecf20Sopenharmony_ci break; 20598c2ecf20Sopenharmony_ci case 3: 20608c2ecf20Sopenharmony_ci vcpu->mmio_needed = 22; /* 5 bytes */ 20618c2ecf20Sopenharmony_ci break; 20628c2ecf20Sopenharmony_ci case 4: 20638c2ecf20Sopenharmony_ci vcpu->mmio_needed = 23; /* 4 bytes */ 20648c2ecf20Sopenharmony_ci break; 20658c2ecf20Sopenharmony_ci case 5: 20668c2ecf20Sopenharmony_ci vcpu->mmio_needed = 24; /* 3 bytes */ 20678c2ecf20Sopenharmony_ci break; 20688c2ecf20Sopenharmony_ci case 6: 20698c2ecf20Sopenharmony_ci vcpu->mmio_needed = 25; /* 2 bytes */ 20708c2ecf20Sopenharmony_ci break; 20718c2ecf20Sopenharmony_ci case 7: 20728c2ecf20Sopenharmony_ci vcpu->mmio_needed = 26; /* 1 byte */ 20738c2ecf20Sopenharmony_ci break; 20748c2ecf20Sopenharmony_ci default: 20758c2ecf20Sopenharmony_ci break; 20768c2ecf20Sopenharmony_ci } 20778c2ecf20Sopenharmony_ci break; 20788c2ecf20Sopenharmony_ci#endif 20798c2ecf20Sopenharmony_ci 20808c2ecf20Sopenharmony_ci#ifdef CONFIG_CPU_LOONGSON64 20818c2ecf20Sopenharmony_ci case ldc2_op: 20828c2ecf20Sopenharmony_ci rt = inst.loongson3_lsdc2_format.rt; 20838c2ecf20Sopenharmony_ci switch (inst.loongson3_lsdc2_format.opcode1) { 20848c2ecf20Sopenharmony_ci /* 20858c2ecf20Sopenharmony_ci * Loongson-3 overridden ldc2 instructions. 20868c2ecf20Sopenharmony_ci * opcode1 instruction 20878c2ecf20Sopenharmony_ci * 0x0 gslbx: store 1 bytes from GPR 20888c2ecf20Sopenharmony_ci * 0x1 gslhx: store 2 bytes from GPR 20898c2ecf20Sopenharmony_ci * 0x2 gslwx: store 4 bytes from GPR 20908c2ecf20Sopenharmony_ci * 0x3 gsldx: store 8 bytes from GPR 20918c2ecf20Sopenharmony_ci */ 20928c2ecf20Sopenharmony_ci case 0x0: 20938c2ecf20Sopenharmony_ci run->mmio.len = 1; 20948c2ecf20Sopenharmony_ci vcpu->mmio_needed = 27; /* signed */ 20958c2ecf20Sopenharmony_ci break; 20968c2ecf20Sopenharmony_ci case 0x1: 20978c2ecf20Sopenharmony_ci run->mmio.len = 2; 20988c2ecf20Sopenharmony_ci vcpu->mmio_needed = 28; /* signed */ 20998c2ecf20Sopenharmony_ci break; 21008c2ecf20Sopenharmony_ci case 0x2: 21018c2ecf20Sopenharmony_ci run->mmio.len = 4; 21028c2ecf20Sopenharmony_ci vcpu->mmio_needed = 29; /* signed */ 21038c2ecf20Sopenharmony_ci break; 21048c2ecf20Sopenharmony_ci case 0x3: 21058c2ecf20Sopenharmony_ci run->mmio.len = 8; 21068c2ecf20Sopenharmony_ci vcpu->mmio_needed = 30; /* signed */ 21078c2ecf20Sopenharmony_ci break; 21088c2ecf20Sopenharmony_ci default: 21098c2ecf20Sopenharmony_ci kvm_err("Godson Extended GS-Load for float not yet supported (inst=0x%08x)\n", 21108c2ecf20Sopenharmony_ci inst.word); 21118c2ecf20Sopenharmony_ci break; 21128c2ecf20Sopenharmony_ci } 21138c2ecf20Sopenharmony_ci break; 21148c2ecf20Sopenharmony_ci#endif 21158c2ecf20Sopenharmony_ci 21168c2ecf20Sopenharmony_ci default: 21178c2ecf20Sopenharmony_ci kvm_err("Load not yet supported (inst=0x%08x)\n", 21188c2ecf20Sopenharmony_ci inst.word); 21198c2ecf20Sopenharmony_ci vcpu->mmio_needed = 0; 21208c2ecf20Sopenharmony_ci return EMULATE_FAIL; 21218c2ecf20Sopenharmony_ci } 21228c2ecf20Sopenharmony_ci 21238c2ecf20Sopenharmony_ci run->mmio.is_write = 0; 21248c2ecf20Sopenharmony_ci vcpu->mmio_is_write = 0; 21258c2ecf20Sopenharmony_ci 21268c2ecf20Sopenharmony_ci r = kvm_io_bus_read(vcpu, KVM_MMIO_BUS, 21278c2ecf20Sopenharmony_ci run->mmio.phys_addr, run->mmio.len, run->mmio.data); 21288c2ecf20Sopenharmony_ci 21298c2ecf20Sopenharmony_ci if (!r) { 21308c2ecf20Sopenharmony_ci kvm_mips_complete_mmio_load(vcpu); 21318c2ecf20Sopenharmony_ci vcpu->mmio_needed = 0; 21328c2ecf20Sopenharmony_ci return EMULATE_DONE; 21338c2ecf20Sopenharmony_ci } 21348c2ecf20Sopenharmony_ci 21358c2ecf20Sopenharmony_ci return EMULATE_DO_MMIO; 21368c2ecf20Sopenharmony_ci} 21378c2ecf20Sopenharmony_ci 21388c2ecf20Sopenharmony_ci#ifndef CONFIG_KVM_MIPS_VZ 21398c2ecf20Sopenharmony_cistatic enum emulation_result kvm_mips_guest_cache_op(int (*fn)(unsigned long), 21408c2ecf20Sopenharmony_ci unsigned long curr_pc, 21418c2ecf20Sopenharmony_ci unsigned long addr, 21428c2ecf20Sopenharmony_ci struct kvm_vcpu *vcpu, 21438c2ecf20Sopenharmony_ci u32 cause) 21448c2ecf20Sopenharmony_ci{ 21458c2ecf20Sopenharmony_ci int err; 21468c2ecf20Sopenharmony_ci 21478c2ecf20Sopenharmony_ci for (;;) { 21488c2ecf20Sopenharmony_ci /* Carefully attempt the cache operation */ 21498c2ecf20Sopenharmony_ci kvm_trap_emul_gva_lockless_begin(vcpu); 21508c2ecf20Sopenharmony_ci err = fn(addr); 21518c2ecf20Sopenharmony_ci kvm_trap_emul_gva_lockless_end(vcpu); 21528c2ecf20Sopenharmony_ci 21538c2ecf20Sopenharmony_ci if (likely(!err)) 21548c2ecf20Sopenharmony_ci return EMULATE_DONE; 21558c2ecf20Sopenharmony_ci 21568c2ecf20Sopenharmony_ci /* 21578c2ecf20Sopenharmony_ci * Try to handle the fault and retry, maybe we just raced with a 21588c2ecf20Sopenharmony_ci * GVA invalidation. 21598c2ecf20Sopenharmony_ci */ 21608c2ecf20Sopenharmony_ci switch (kvm_trap_emul_gva_fault(vcpu, addr, false)) { 21618c2ecf20Sopenharmony_ci case KVM_MIPS_GVA: 21628c2ecf20Sopenharmony_ci case KVM_MIPS_GPA: 21638c2ecf20Sopenharmony_ci /* bad virtual or physical address */ 21648c2ecf20Sopenharmony_ci return EMULATE_FAIL; 21658c2ecf20Sopenharmony_ci case KVM_MIPS_TLB: 21668c2ecf20Sopenharmony_ci /* no matching guest TLB */ 21678c2ecf20Sopenharmony_ci vcpu->arch.host_cp0_badvaddr = addr; 21688c2ecf20Sopenharmony_ci vcpu->arch.pc = curr_pc; 21698c2ecf20Sopenharmony_ci kvm_mips_emulate_tlbmiss_ld(cause, NULL, vcpu); 21708c2ecf20Sopenharmony_ci return EMULATE_EXCEPT; 21718c2ecf20Sopenharmony_ci case KVM_MIPS_TLBINV: 21728c2ecf20Sopenharmony_ci /* invalid matching guest TLB */ 21738c2ecf20Sopenharmony_ci vcpu->arch.host_cp0_badvaddr = addr; 21748c2ecf20Sopenharmony_ci vcpu->arch.pc = curr_pc; 21758c2ecf20Sopenharmony_ci kvm_mips_emulate_tlbinv_ld(cause, NULL, vcpu); 21768c2ecf20Sopenharmony_ci return EMULATE_EXCEPT; 21778c2ecf20Sopenharmony_ci default: 21788c2ecf20Sopenharmony_ci break; 21798c2ecf20Sopenharmony_ci } 21808c2ecf20Sopenharmony_ci } 21818c2ecf20Sopenharmony_ci} 21828c2ecf20Sopenharmony_ci 21838c2ecf20Sopenharmony_cienum emulation_result kvm_mips_emulate_cache(union mips_instruction inst, 21848c2ecf20Sopenharmony_ci u32 *opc, u32 cause, 21858c2ecf20Sopenharmony_ci struct kvm_vcpu *vcpu) 21868c2ecf20Sopenharmony_ci{ 21878c2ecf20Sopenharmony_ci enum emulation_result er = EMULATE_DONE; 21888c2ecf20Sopenharmony_ci u32 cache, op_inst, op, base; 21898c2ecf20Sopenharmony_ci s16 offset; 21908c2ecf20Sopenharmony_ci struct kvm_vcpu_arch *arch = &vcpu->arch; 21918c2ecf20Sopenharmony_ci unsigned long va; 21928c2ecf20Sopenharmony_ci unsigned long curr_pc; 21938c2ecf20Sopenharmony_ci 21948c2ecf20Sopenharmony_ci /* 21958c2ecf20Sopenharmony_ci * Update PC and hold onto current PC in case there is 21968c2ecf20Sopenharmony_ci * an error and we want to rollback the PC 21978c2ecf20Sopenharmony_ci */ 21988c2ecf20Sopenharmony_ci curr_pc = vcpu->arch.pc; 21998c2ecf20Sopenharmony_ci er = update_pc(vcpu, cause); 22008c2ecf20Sopenharmony_ci if (er == EMULATE_FAIL) 22018c2ecf20Sopenharmony_ci return er; 22028c2ecf20Sopenharmony_ci 22038c2ecf20Sopenharmony_ci base = inst.i_format.rs; 22048c2ecf20Sopenharmony_ci op_inst = inst.i_format.rt; 22058c2ecf20Sopenharmony_ci if (cpu_has_mips_r6) 22068c2ecf20Sopenharmony_ci offset = inst.spec3_format.simmediate; 22078c2ecf20Sopenharmony_ci else 22088c2ecf20Sopenharmony_ci offset = inst.i_format.simmediate; 22098c2ecf20Sopenharmony_ci cache = op_inst & CacheOp_Cache; 22108c2ecf20Sopenharmony_ci op = op_inst & CacheOp_Op; 22118c2ecf20Sopenharmony_ci 22128c2ecf20Sopenharmony_ci va = arch->gprs[base] + offset; 22138c2ecf20Sopenharmony_ci 22148c2ecf20Sopenharmony_ci kvm_debug("CACHE (cache: %#x, op: %#x, base[%d]: %#lx, offset: %#x\n", 22158c2ecf20Sopenharmony_ci cache, op, base, arch->gprs[base], offset); 22168c2ecf20Sopenharmony_ci 22178c2ecf20Sopenharmony_ci /* 22188c2ecf20Sopenharmony_ci * Treat INDEX_INV as a nop, basically issued by Linux on startup to 22198c2ecf20Sopenharmony_ci * invalidate the caches entirely by stepping through all the 22208c2ecf20Sopenharmony_ci * ways/indexes 22218c2ecf20Sopenharmony_ci */ 22228c2ecf20Sopenharmony_ci if (op == Index_Writeback_Inv) { 22238c2ecf20Sopenharmony_ci kvm_debug("@ %#lx/%#lx CACHE (cache: %#x, op: %#x, base[%d]: %#lx, offset: %#x\n", 22248c2ecf20Sopenharmony_ci vcpu->arch.pc, vcpu->arch.gprs[31], cache, op, base, 22258c2ecf20Sopenharmony_ci arch->gprs[base], offset); 22268c2ecf20Sopenharmony_ci 22278c2ecf20Sopenharmony_ci if (cache == Cache_D) { 22288c2ecf20Sopenharmony_ci#ifdef CONFIG_CPU_R4K_CACHE_TLB 22298c2ecf20Sopenharmony_ci r4k_blast_dcache(); 22308c2ecf20Sopenharmony_ci#else 22318c2ecf20Sopenharmony_ci switch (boot_cpu_type()) { 22328c2ecf20Sopenharmony_ci case CPU_CAVIUM_OCTEON3: 22338c2ecf20Sopenharmony_ci /* locally flush icache */ 22348c2ecf20Sopenharmony_ci local_flush_icache_range(0, 0); 22358c2ecf20Sopenharmony_ci break; 22368c2ecf20Sopenharmony_ci default: 22378c2ecf20Sopenharmony_ci __flush_cache_all(); 22388c2ecf20Sopenharmony_ci break; 22398c2ecf20Sopenharmony_ci } 22408c2ecf20Sopenharmony_ci#endif 22418c2ecf20Sopenharmony_ci } else if (cache == Cache_I) { 22428c2ecf20Sopenharmony_ci#ifdef CONFIG_CPU_R4K_CACHE_TLB 22438c2ecf20Sopenharmony_ci r4k_blast_icache(); 22448c2ecf20Sopenharmony_ci#else 22458c2ecf20Sopenharmony_ci switch (boot_cpu_type()) { 22468c2ecf20Sopenharmony_ci case CPU_CAVIUM_OCTEON3: 22478c2ecf20Sopenharmony_ci /* locally flush icache */ 22488c2ecf20Sopenharmony_ci local_flush_icache_range(0, 0); 22498c2ecf20Sopenharmony_ci break; 22508c2ecf20Sopenharmony_ci default: 22518c2ecf20Sopenharmony_ci flush_icache_all(); 22528c2ecf20Sopenharmony_ci break; 22538c2ecf20Sopenharmony_ci } 22548c2ecf20Sopenharmony_ci#endif 22558c2ecf20Sopenharmony_ci } else { 22568c2ecf20Sopenharmony_ci kvm_err("%s: unsupported CACHE INDEX operation\n", 22578c2ecf20Sopenharmony_ci __func__); 22588c2ecf20Sopenharmony_ci return EMULATE_FAIL; 22598c2ecf20Sopenharmony_ci } 22608c2ecf20Sopenharmony_ci 22618c2ecf20Sopenharmony_ci#ifdef CONFIG_KVM_MIPS_DYN_TRANS 22628c2ecf20Sopenharmony_ci kvm_mips_trans_cache_index(inst, opc, vcpu); 22638c2ecf20Sopenharmony_ci#endif 22648c2ecf20Sopenharmony_ci goto done; 22658c2ecf20Sopenharmony_ci } 22668c2ecf20Sopenharmony_ci 22678c2ecf20Sopenharmony_ci /* XXXKYMA: Only a subset of cache ops are supported, used by Linux */ 22688c2ecf20Sopenharmony_ci if (op_inst == Hit_Writeback_Inv_D || op_inst == Hit_Invalidate_D) { 22698c2ecf20Sopenharmony_ci /* 22708c2ecf20Sopenharmony_ci * Perform the dcache part of icache synchronisation on the 22718c2ecf20Sopenharmony_ci * guest's behalf. 22728c2ecf20Sopenharmony_ci */ 22738c2ecf20Sopenharmony_ci er = kvm_mips_guest_cache_op(protected_writeback_dcache_line, 22748c2ecf20Sopenharmony_ci curr_pc, va, vcpu, cause); 22758c2ecf20Sopenharmony_ci if (er != EMULATE_DONE) 22768c2ecf20Sopenharmony_ci goto done; 22778c2ecf20Sopenharmony_ci#ifdef CONFIG_KVM_MIPS_DYN_TRANS 22788c2ecf20Sopenharmony_ci /* 22798c2ecf20Sopenharmony_ci * Replace the CACHE instruction, with a SYNCI, not the same, 22808c2ecf20Sopenharmony_ci * but avoids a trap 22818c2ecf20Sopenharmony_ci */ 22828c2ecf20Sopenharmony_ci kvm_mips_trans_cache_va(inst, opc, vcpu); 22838c2ecf20Sopenharmony_ci#endif 22848c2ecf20Sopenharmony_ci } else if (op_inst == Hit_Invalidate_I) { 22858c2ecf20Sopenharmony_ci /* Perform the icache synchronisation on the guest's behalf */ 22868c2ecf20Sopenharmony_ci er = kvm_mips_guest_cache_op(protected_writeback_dcache_line, 22878c2ecf20Sopenharmony_ci curr_pc, va, vcpu, cause); 22888c2ecf20Sopenharmony_ci if (er != EMULATE_DONE) 22898c2ecf20Sopenharmony_ci goto done; 22908c2ecf20Sopenharmony_ci er = kvm_mips_guest_cache_op(protected_flush_icache_line, 22918c2ecf20Sopenharmony_ci curr_pc, va, vcpu, cause); 22928c2ecf20Sopenharmony_ci if (er != EMULATE_DONE) 22938c2ecf20Sopenharmony_ci goto done; 22948c2ecf20Sopenharmony_ci 22958c2ecf20Sopenharmony_ci#ifdef CONFIG_KVM_MIPS_DYN_TRANS 22968c2ecf20Sopenharmony_ci /* Replace the CACHE instruction, with a SYNCI */ 22978c2ecf20Sopenharmony_ci kvm_mips_trans_cache_va(inst, opc, vcpu); 22988c2ecf20Sopenharmony_ci#endif 22998c2ecf20Sopenharmony_ci } else { 23008c2ecf20Sopenharmony_ci kvm_err("NO-OP CACHE (cache: %#x, op: %#x, base[%d]: %#lx, offset: %#x\n", 23018c2ecf20Sopenharmony_ci cache, op, base, arch->gprs[base], offset); 23028c2ecf20Sopenharmony_ci er = EMULATE_FAIL; 23038c2ecf20Sopenharmony_ci } 23048c2ecf20Sopenharmony_ci 23058c2ecf20Sopenharmony_cidone: 23068c2ecf20Sopenharmony_ci /* Rollback PC only if emulation was unsuccessful */ 23078c2ecf20Sopenharmony_ci if (er == EMULATE_FAIL) 23088c2ecf20Sopenharmony_ci vcpu->arch.pc = curr_pc; 23098c2ecf20Sopenharmony_ci /* Guest exception needs guest to resume */ 23108c2ecf20Sopenharmony_ci if (er == EMULATE_EXCEPT) 23118c2ecf20Sopenharmony_ci er = EMULATE_DONE; 23128c2ecf20Sopenharmony_ci 23138c2ecf20Sopenharmony_ci return er; 23148c2ecf20Sopenharmony_ci} 23158c2ecf20Sopenharmony_ci 23168c2ecf20Sopenharmony_cienum emulation_result kvm_mips_emulate_inst(u32 cause, u32 *opc, 23178c2ecf20Sopenharmony_ci struct kvm_vcpu *vcpu) 23188c2ecf20Sopenharmony_ci{ 23198c2ecf20Sopenharmony_ci union mips_instruction inst; 23208c2ecf20Sopenharmony_ci enum emulation_result er = EMULATE_DONE; 23218c2ecf20Sopenharmony_ci int err; 23228c2ecf20Sopenharmony_ci 23238c2ecf20Sopenharmony_ci /* Fetch the instruction. */ 23248c2ecf20Sopenharmony_ci if (cause & CAUSEF_BD) 23258c2ecf20Sopenharmony_ci opc += 1; 23268c2ecf20Sopenharmony_ci err = kvm_get_badinstr(opc, vcpu, &inst.word); 23278c2ecf20Sopenharmony_ci if (err) 23288c2ecf20Sopenharmony_ci return EMULATE_FAIL; 23298c2ecf20Sopenharmony_ci 23308c2ecf20Sopenharmony_ci switch (inst.r_format.opcode) { 23318c2ecf20Sopenharmony_ci case cop0_op: 23328c2ecf20Sopenharmony_ci er = kvm_mips_emulate_CP0(inst, opc, cause, vcpu); 23338c2ecf20Sopenharmony_ci break; 23348c2ecf20Sopenharmony_ci 23358c2ecf20Sopenharmony_ci#ifndef CONFIG_CPU_MIPSR6 23368c2ecf20Sopenharmony_ci case cache_op: 23378c2ecf20Sopenharmony_ci ++vcpu->stat.cache_exits; 23388c2ecf20Sopenharmony_ci trace_kvm_exit(vcpu, KVM_TRACE_EXIT_CACHE); 23398c2ecf20Sopenharmony_ci er = kvm_mips_emulate_cache(inst, opc, cause, vcpu); 23408c2ecf20Sopenharmony_ci break; 23418c2ecf20Sopenharmony_ci#else 23428c2ecf20Sopenharmony_ci case spec3_op: 23438c2ecf20Sopenharmony_ci switch (inst.spec3_format.func) { 23448c2ecf20Sopenharmony_ci case cache6_op: 23458c2ecf20Sopenharmony_ci ++vcpu->stat.cache_exits; 23468c2ecf20Sopenharmony_ci trace_kvm_exit(vcpu, KVM_TRACE_EXIT_CACHE); 23478c2ecf20Sopenharmony_ci er = kvm_mips_emulate_cache(inst, opc, cause, 23488c2ecf20Sopenharmony_ci vcpu); 23498c2ecf20Sopenharmony_ci break; 23508c2ecf20Sopenharmony_ci default: 23518c2ecf20Sopenharmony_ci goto unknown; 23528c2ecf20Sopenharmony_ci } 23538c2ecf20Sopenharmony_ci break; 23548c2ecf20Sopenharmony_ciunknown: 23558c2ecf20Sopenharmony_ci#endif 23568c2ecf20Sopenharmony_ci 23578c2ecf20Sopenharmony_ci default: 23588c2ecf20Sopenharmony_ci kvm_err("Instruction emulation not supported (%p/%#x)\n", opc, 23598c2ecf20Sopenharmony_ci inst.word); 23608c2ecf20Sopenharmony_ci kvm_arch_vcpu_dump_regs(vcpu); 23618c2ecf20Sopenharmony_ci er = EMULATE_FAIL; 23628c2ecf20Sopenharmony_ci break; 23638c2ecf20Sopenharmony_ci } 23648c2ecf20Sopenharmony_ci 23658c2ecf20Sopenharmony_ci return er; 23668c2ecf20Sopenharmony_ci} 23678c2ecf20Sopenharmony_ci#endif /* CONFIG_KVM_MIPS_VZ */ 23688c2ecf20Sopenharmony_ci 23698c2ecf20Sopenharmony_ci/** 23708c2ecf20Sopenharmony_ci * kvm_mips_guest_exception_base() - Find guest exception vector base address. 23718c2ecf20Sopenharmony_ci * 23728c2ecf20Sopenharmony_ci * Returns: The base address of the current guest exception vector, taking 23738c2ecf20Sopenharmony_ci * both Guest.CP0_Status.BEV and Guest.CP0_EBase into account. 23748c2ecf20Sopenharmony_ci */ 23758c2ecf20Sopenharmony_cilong kvm_mips_guest_exception_base(struct kvm_vcpu *vcpu) 23768c2ecf20Sopenharmony_ci{ 23778c2ecf20Sopenharmony_ci struct mips_coproc *cop0 = vcpu->arch.cop0; 23788c2ecf20Sopenharmony_ci 23798c2ecf20Sopenharmony_ci if (kvm_read_c0_guest_status(cop0) & ST0_BEV) 23808c2ecf20Sopenharmony_ci return KVM_GUEST_CKSEG1ADDR(0x1fc00200); 23818c2ecf20Sopenharmony_ci else 23828c2ecf20Sopenharmony_ci return kvm_read_c0_guest_ebase(cop0) & MIPS_EBASE_BASE; 23838c2ecf20Sopenharmony_ci} 23848c2ecf20Sopenharmony_ci 23858c2ecf20Sopenharmony_cienum emulation_result kvm_mips_emulate_syscall(u32 cause, 23868c2ecf20Sopenharmony_ci u32 *opc, 23878c2ecf20Sopenharmony_ci struct kvm_vcpu *vcpu) 23888c2ecf20Sopenharmony_ci{ 23898c2ecf20Sopenharmony_ci struct mips_coproc *cop0 = vcpu->arch.cop0; 23908c2ecf20Sopenharmony_ci struct kvm_vcpu_arch *arch = &vcpu->arch; 23918c2ecf20Sopenharmony_ci enum emulation_result er = EMULATE_DONE; 23928c2ecf20Sopenharmony_ci 23938c2ecf20Sopenharmony_ci if ((kvm_read_c0_guest_status(cop0) & ST0_EXL) == 0) { 23948c2ecf20Sopenharmony_ci /* save old pc */ 23958c2ecf20Sopenharmony_ci kvm_write_c0_guest_epc(cop0, arch->pc); 23968c2ecf20Sopenharmony_ci kvm_set_c0_guest_status(cop0, ST0_EXL); 23978c2ecf20Sopenharmony_ci 23988c2ecf20Sopenharmony_ci if (cause & CAUSEF_BD) 23998c2ecf20Sopenharmony_ci kvm_set_c0_guest_cause(cop0, CAUSEF_BD); 24008c2ecf20Sopenharmony_ci else 24018c2ecf20Sopenharmony_ci kvm_clear_c0_guest_cause(cop0, CAUSEF_BD); 24028c2ecf20Sopenharmony_ci 24038c2ecf20Sopenharmony_ci kvm_debug("Delivering SYSCALL @ pc %#lx\n", arch->pc); 24048c2ecf20Sopenharmony_ci 24058c2ecf20Sopenharmony_ci kvm_change_c0_guest_cause(cop0, (0xff), 24068c2ecf20Sopenharmony_ci (EXCCODE_SYS << CAUSEB_EXCCODE)); 24078c2ecf20Sopenharmony_ci 24088c2ecf20Sopenharmony_ci /* Set PC to the exception entry point */ 24098c2ecf20Sopenharmony_ci arch->pc = kvm_mips_guest_exception_base(vcpu) + 0x180; 24108c2ecf20Sopenharmony_ci 24118c2ecf20Sopenharmony_ci } else { 24128c2ecf20Sopenharmony_ci kvm_err("Trying to deliver SYSCALL when EXL is already set\n"); 24138c2ecf20Sopenharmony_ci er = EMULATE_FAIL; 24148c2ecf20Sopenharmony_ci } 24158c2ecf20Sopenharmony_ci 24168c2ecf20Sopenharmony_ci return er; 24178c2ecf20Sopenharmony_ci} 24188c2ecf20Sopenharmony_ci 24198c2ecf20Sopenharmony_cienum emulation_result kvm_mips_emulate_tlbmiss_ld(u32 cause, 24208c2ecf20Sopenharmony_ci u32 *opc, 24218c2ecf20Sopenharmony_ci struct kvm_vcpu *vcpu) 24228c2ecf20Sopenharmony_ci{ 24238c2ecf20Sopenharmony_ci struct mips_coproc *cop0 = vcpu->arch.cop0; 24248c2ecf20Sopenharmony_ci struct kvm_vcpu_arch *arch = &vcpu->arch; 24258c2ecf20Sopenharmony_ci unsigned long entryhi = (vcpu->arch. host_cp0_badvaddr & VPN2_MASK) | 24268c2ecf20Sopenharmony_ci (kvm_read_c0_guest_entryhi(cop0) & KVM_ENTRYHI_ASID); 24278c2ecf20Sopenharmony_ci 24288c2ecf20Sopenharmony_ci if ((kvm_read_c0_guest_status(cop0) & ST0_EXL) == 0) { 24298c2ecf20Sopenharmony_ci /* save old pc */ 24308c2ecf20Sopenharmony_ci kvm_write_c0_guest_epc(cop0, arch->pc); 24318c2ecf20Sopenharmony_ci kvm_set_c0_guest_status(cop0, ST0_EXL); 24328c2ecf20Sopenharmony_ci 24338c2ecf20Sopenharmony_ci if (cause & CAUSEF_BD) 24348c2ecf20Sopenharmony_ci kvm_set_c0_guest_cause(cop0, CAUSEF_BD); 24358c2ecf20Sopenharmony_ci else 24368c2ecf20Sopenharmony_ci kvm_clear_c0_guest_cause(cop0, CAUSEF_BD); 24378c2ecf20Sopenharmony_ci 24388c2ecf20Sopenharmony_ci kvm_debug("[EXL == 0] delivering TLB MISS @ pc %#lx\n", 24398c2ecf20Sopenharmony_ci arch->pc); 24408c2ecf20Sopenharmony_ci 24418c2ecf20Sopenharmony_ci /* set pc to the exception entry point */ 24428c2ecf20Sopenharmony_ci arch->pc = kvm_mips_guest_exception_base(vcpu) + 0x0; 24438c2ecf20Sopenharmony_ci 24448c2ecf20Sopenharmony_ci } else { 24458c2ecf20Sopenharmony_ci kvm_debug("[EXL == 1] delivering TLB MISS @ pc %#lx\n", 24468c2ecf20Sopenharmony_ci arch->pc); 24478c2ecf20Sopenharmony_ci 24488c2ecf20Sopenharmony_ci arch->pc = kvm_mips_guest_exception_base(vcpu) + 0x180; 24498c2ecf20Sopenharmony_ci } 24508c2ecf20Sopenharmony_ci 24518c2ecf20Sopenharmony_ci kvm_change_c0_guest_cause(cop0, (0xff), 24528c2ecf20Sopenharmony_ci (EXCCODE_TLBL << CAUSEB_EXCCODE)); 24538c2ecf20Sopenharmony_ci 24548c2ecf20Sopenharmony_ci /* setup badvaddr, context and entryhi registers for the guest */ 24558c2ecf20Sopenharmony_ci kvm_write_c0_guest_badvaddr(cop0, vcpu->arch.host_cp0_badvaddr); 24568c2ecf20Sopenharmony_ci /* XXXKYMA: is the context register used by linux??? */ 24578c2ecf20Sopenharmony_ci kvm_write_c0_guest_entryhi(cop0, entryhi); 24588c2ecf20Sopenharmony_ci 24598c2ecf20Sopenharmony_ci return EMULATE_DONE; 24608c2ecf20Sopenharmony_ci} 24618c2ecf20Sopenharmony_ci 24628c2ecf20Sopenharmony_cienum emulation_result kvm_mips_emulate_tlbinv_ld(u32 cause, 24638c2ecf20Sopenharmony_ci u32 *opc, 24648c2ecf20Sopenharmony_ci struct kvm_vcpu *vcpu) 24658c2ecf20Sopenharmony_ci{ 24668c2ecf20Sopenharmony_ci struct mips_coproc *cop0 = vcpu->arch.cop0; 24678c2ecf20Sopenharmony_ci struct kvm_vcpu_arch *arch = &vcpu->arch; 24688c2ecf20Sopenharmony_ci unsigned long entryhi = 24698c2ecf20Sopenharmony_ci (vcpu->arch.host_cp0_badvaddr & VPN2_MASK) | 24708c2ecf20Sopenharmony_ci (kvm_read_c0_guest_entryhi(cop0) & KVM_ENTRYHI_ASID); 24718c2ecf20Sopenharmony_ci 24728c2ecf20Sopenharmony_ci if ((kvm_read_c0_guest_status(cop0) & ST0_EXL) == 0) { 24738c2ecf20Sopenharmony_ci /* save old pc */ 24748c2ecf20Sopenharmony_ci kvm_write_c0_guest_epc(cop0, arch->pc); 24758c2ecf20Sopenharmony_ci kvm_set_c0_guest_status(cop0, ST0_EXL); 24768c2ecf20Sopenharmony_ci 24778c2ecf20Sopenharmony_ci if (cause & CAUSEF_BD) 24788c2ecf20Sopenharmony_ci kvm_set_c0_guest_cause(cop0, CAUSEF_BD); 24798c2ecf20Sopenharmony_ci else 24808c2ecf20Sopenharmony_ci kvm_clear_c0_guest_cause(cop0, CAUSEF_BD); 24818c2ecf20Sopenharmony_ci 24828c2ecf20Sopenharmony_ci kvm_debug("[EXL == 0] delivering TLB INV @ pc %#lx\n", 24838c2ecf20Sopenharmony_ci arch->pc); 24848c2ecf20Sopenharmony_ci } else { 24858c2ecf20Sopenharmony_ci kvm_debug("[EXL == 1] delivering TLB MISS @ pc %#lx\n", 24868c2ecf20Sopenharmony_ci arch->pc); 24878c2ecf20Sopenharmony_ci } 24888c2ecf20Sopenharmony_ci 24898c2ecf20Sopenharmony_ci /* set pc to the exception entry point */ 24908c2ecf20Sopenharmony_ci arch->pc = kvm_mips_guest_exception_base(vcpu) + 0x180; 24918c2ecf20Sopenharmony_ci 24928c2ecf20Sopenharmony_ci kvm_change_c0_guest_cause(cop0, (0xff), 24938c2ecf20Sopenharmony_ci (EXCCODE_TLBL << CAUSEB_EXCCODE)); 24948c2ecf20Sopenharmony_ci 24958c2ecf20Sopenharmony_ci /* setup badvaddr, context and entryhi registers for the guest */ 24968c2ecf20Sopenharmony_ci kvm_write_c0_guest_badvaddr(cop0, vcpu->arch.host_cp0_badvaddr); 24978c2ecf20Sopenharmony_ci /* XXXKYMA: is the context register used by linux??? */ 24988c2ecf20Sopenharmony_ci kvm_write_c0_guest_entryhi(cop0, entryhi); 24998c2ecf20Sopenharmony_ci 25008c2ecf20Sopenharmony_ci return EMULATE_DONE; 25018c2ecf20Sopenharmony_ci} 25028c2ecf20Sopenharmony_ci 25038c2ecf20Sopenharmony_cienum emulation_result kvm_mips_emulate_tlbmiss_st(u32 cause, 25048c2ecf20Sopenharmony_ci u32 *opc, 25058c2ecf20Sopenharmony_ci struct kvm_vcpu *vcpu) 25068c2ecf20Sopenharmony_ci{ 25078c2ecf20Sopenharmony_ci struct mips_coproc *cop0 = vcpu->arch.cop0; 25088c2ecf20Sopenharmony_ci struct kvm_vcpu_arch *arch = &vcpu->arch; 25098c2ecf20Sopenharmony_ci unsigned long entryhi = (vcpu->arch.host_cp0_badvaddr & VPN2_MASK) | 25108c2ecf20Sopenharmony_ci (kvm_read_c0_guest_entryhi(cop0) & KVM_ENTRYHI_ASID); 25118c2ecf20Sopenharmony_ci 25128c2ecf20Sopenharmony_ci if ((kvm_read_c0_guest_status(cop0) & ST0_EXL) == 0) { 25138c2ecf20Sopenharmony_ci /* save old pc */ 25148c2ecf20Sopenharmony_ci kvm_write_c0_guest_epc(cop0, arch->pc); 25158c2ecf20Sopenharmony_ci kvm_set_c0_guest_status(cop0, ST0_EXL); 25168c2ecf20Sopenharmony_ci 25178c2ecf20Sopenharmony_ci if (cause & CAUSEF_BD) 25188c2ecf20Sopenharmony_ci kvm_set_c0_guest_cause(cop0, CAUSEF_BD); 25198c2ecf20Sopenharmony_ci else 25208c2ecf20Sopenharmony_ci kvm_clear_c0_guest_cause(cop0, CAUSEF_BD); 25218c2ecf20Sopenharmony_ci 25228c2ecf20Sopenharmony_ci kvm_debug("[EXL == 0] Delivering TLB MISS @ pc %#lx\n", 25238c2ecf20Sopenharmony_ci arch->pc); 25248c2ecf20Sopenharmony_ci 25258c2ecf20Sopenharmony_ci /* Set PC to the exception entry point */ 25268c2ecf20Sopenharmony_ci arch->pc = kvm_mips_guest_exception_base(vcpu) + 0x0; 25278c2ecf20Sopenharmony_ci } else { 25288c2ecf20Sopenharmony_ci kvm_debug("[EXL == 1] Delivering TLB MISS @ pc %#lx\n", 25298c2ecf20Sopenharmony_ci arch->pc); 25308c2ecf20Sopenharmony_ci arch->pc = kvm_mips_guest_exception_base(vcpu) + 0x180; 25318c2ecf20Sopenharmony_ci } 25328c2ecf20Sopenharmony_ci 25338c2ecf20Sopenharmony_ci kvm_change_c0_guest_cause(cop0, (0xff), 25348c2ecf20Sopenharmony_ci (EXCCODE_TLBS << CAUSEB_EXCCODE)); 25358c2ecf20Sopenharmony_ci 25368c2ecf20Sopenharmony_ci /* setup badvaddr, context and entryhi registers for the guest */ 25378c2ecf20Sopenharmony_ci kvm_write_c0_guest_badvaddr(cop0, vcpu->arch.host_cp0_badvaddr); 25388c2ecf20Sopenharmony_ci /* XXXKYMA: is the context register used by linux??? */ 25398c2ecf20Sopenharmony_ci kvm_write_c0_guest_entryhi(cop0, entryhi); 25408c2ecf20Sopenharmony_ci 25418c2ecf20Sopenharmony_ci return EMULATE_DONE; 25428c2ecf20Sopenharmony_ci} 25438c2ecf20Sopenharmony_ci 25448c2ecf20Sopenharmony_cienum emulation_result kvm_mips_emulate_tlbinv_st(u32 cause, 25458c2ecf20Sopenharmony_ci u32 *opc, 25468c2ecf20Sopenharmony_ci struct kvm_vcpu *vcpu) 25478c2ecf20Sopenharmony_ci{ 25488c2ecf20Sopenharmony_ci struct mips_coproc *cop0 = vcpu->arch.cop0; 25498c2ecf20Sopenharmony_ci struct kvm_vcpu_arch *arch = &vcpu->arch; 25508c2ecf20Sopenharmony_ci unsigned long entryhi = (vcpu->arch.host_cp0_badvaddr & VPN2_MASK) | 25518c2ecf20Sopenharmony_ci (kvm_read_c0_guest_entryhi(cop0) & KVM_ENTRYHI_ASID); 25528c2ecf20Sopenharmony_ci 25538c2ecf20Sopenharmony_ci if ((kvm_read_c0_guest_status(cop0) & ST0_EXL) == 0) { 25548c2ecf20Sopenharmony_ci /* save old pc */ 25558c2ecf20Sopenharmony_ci kvm_write_c0_guest_epc(cop0, arch->pc); 25568c2ecf20Sopenharmony_ci kvm_set_c0_guest_status(cop0, ST0_EXL); 25578c2ecf20Sopenharmony_ci 25588c2ecf20Sopenharmony_ci if (cause & CAUSEF_BD) 25598c2ecf20Sopenharmony_ci kvm_set_c0_guest_cause(cop0, CAUSEF_BD); 25608c2ecf20Sopenharmony_ci else 25618c2ecf20Sopenharmony_ci kvm_clear_c0_guest_cause(cop0, CAUSEF_BD); 25628c2ecf20Sopenharmony_ci 25638c2ecf20Sopenharmony_ci kvm_debug("[EXL == 0] Delivering TLB MISS @ pc %#lx\n", 25648c2ecf20Sopenharmony_ci arch->pc); 25658c2ecf20Sopenharmony_ci } else { 25668c2ecf20Sopenharmony_ci kvm_debug("[EXL == 1] Delivering TLB MISS @ pc %#lx\n", 25678c2ecf20Sopenharmony_ci arch->pc); 25688c2ecf20Sopenharmony_ci } 25698c2ecf20Sopenharmony_ci 25708c2ecf20Sopenharmony_ci /* Set PC to the exception entry point */ 25718c2ecf20Sopenharmony_ci arch->pc = kvm_mips_guest_exception_base(vcpu) + 0x180; 25728c2ecf20Sopenharmony_ci 25738c2ecf20Sopenharmony_ci kvm_change_c0_guest_cause(cop0, (0xff), 25748c2ecf20Sopenharmony_ci (EXCCODE_TLBS << CAUSEB_EXCCODE)); 25758c2ecf20Sopenharmony_ci 25768c2ecf20Sopenharmony_ci /* setup badvaddr, context and entryhi registers for the guest */ 25778c2ecf20Sopenharmony_ci kvm_write_c0_guest_badvaddr(cop0, vcpu->arch.host_cp0_badvaddr); 25788c2ecf20Sopenharmony_ci /* XXXKYMA: is the context register used by linux??? */ 25798c2ecf20Sopenharmony_ci kvm_write_c0_guest_entryhi(cop0, entryhi); 25808c2ecf20Sopenharmony_ci 25818c2ecf20Sopenharmony_ci return EMULATE_DONE; 25828c2ecf20Sopenharmony_ci} 25838c2ecf20Sopenharmony_ci 25848c2ecf20Sopenharmony_cienum emulation_result kvm_mips_emulate_tlbmod(u32 cause, 25858c2ecf20Sopenharmony_ci u32 *opc, 25868c2ecf20Sopenharmony_ci struct kvm_vcpu *vcpu) 25878c2ecf20Sopenharmony_ci{ 25888c2ecf20Sopenharmony_ci struct mips_coproc *cop0 = vcpu->arch.cop0; 25898c2ecf20Sopenharmony_ci unsigned long entryhi = (vcpu->arch.host_cp0_badvaddr & VPN2_MASK) | 25908c2ecf20Sopenharmony_ci (kvm_read_c0_guest_entryhi(cop0) & KVM_ENTRYHI_ASID); 25918c2ecf20Sopenharmony_ci struct kvm_vcpu_arch *arch = &vcpu->arch; 25928c2ecf20Sopenharmony_ci 25938c2ecf20Sopenharmony_ci if ((kvm_read_c0_guest_status(cop0) & ST0_EXL) == 0) { 25948c2ecf20Sopenharmony_ci /* save old pc */ 25958c2ecf20Sopenharmony_ci kvm_write_c0_guest_epc(cop0, arch->pc); 25968c2ecf20Sopenharmony_ci kvm_set_c0_guest_status(cop0, ST0_EXL); 25978c2ecf20Sopenharmony_ci 25988c2ecf20Sopenharmony_ci if (cause & CAUSEF_BD) 25998c2ecf20Sopenharmony_ci kvm_set_c0_guest_cause(cop0, CAUSEF_BD); 26008c2ecf20Sopenharmony_ci else 26018c2ecf20Sopenharmony_ci kvm_clear_c0_guest_cause(cop0, CAUSEF_BD); 26028c2ecf20Sopenharmony_ci 26038c2ecf20Sopenharmony_ci kvm_debug("[EXL == 0] Delivering TLB MOD @ pc %#lx\n", 26048c2ecf20Sopenharmony_ci arch->pc); 26058c2ecf20Sopenharmony_ci } else { 26068c2ecf20Sopenharmony_ci kvm_debug("[EXL == 1] Delivering TLB MOD @ pc %#lx\n", 26078c2ecf20Sopenharmony_ci arch->pc); 26088c2ecf20Sopenharmony_ci } 26098c2ecf20Sopenharmony_ci 26108c2ecf20Sopenharmony_ci arch->pc = kvm_mips_guest_exception_base(vcpu) + 0x180; 26118c2ecf20Sopenharmony_ci 26128c2ecf20Sopenharmony_ci kvm_change_c0_guest_cause(cop0, (0xff), 26138c2ecf20Sopenharmony_ci (EXCCODE_MOD << CAUSEB_EXCCODE)); 26148c2ecf20Sopenharmony_ci 26158c2ecf20Sopenharmony_ci /* setup badvaddr, context and entryhi registers for the guest */ 26168c2ecf20Sopenharmony_ci kvm_write_c0_guest_badvaddr(cop0, vcpu->arch.host_cp0_badvaddr); 26178c2ecf20Sopenharmony_ci /* XXXKYMA: is the context register used by linux??? */ 26188c2ecf20Sopenharmony_ci kvm_write_c0_guest_entryhi(cop0, entryhi); 26198c2ecf20Sopenharmony_ci 26208c2ecf20Sopenharmony_ci return EMULATE_DONE; 26218c2ecf20Sopenharmony_ci} 26228c2ecf20Sopenharmony_ci 26238c2ecf20Sopenharmony_cienum emulation_result kvm_mips_emulate_fpu_exc(u32 cause, 26248c2ecf20Sopenharmony_ci u32 *opc, 26258c2ecf20Sopenharmony_ci struct kvm_vcpu *vcpu) 26268c2ecf20Sopenharmony_ci{ 26278c2ecf20Sopenharmony_ci struct mips_coproc *cop0 = vcpu->arch.cop0; 26288c2ecf20Sopenharmony_ci struct kvm_vcpu_arch *arch = &vcpu->arch; 26298c2ecf20Sopenharmony_ci 26308c2ecf20Sopenharmony_ci if ((kvm_read_c0_guest_status(cop0) & ST0_EXL) == 0) { 26318c2ecf20Sopenharmony_ci /* save old pc */ 26328c2ecf20Sopenharmony_ci kvm_write_c0_guest_epc(cop0, arch->pc); 26338c2ecf20Sopenharmony_ci kvm_set_c0_guest_status(cop0, ST0_EXL); 26348c2ecf20Sopenharmony_ci 26358c2ecf20Sopenharmony_ci if (cause & CAUSEF_BD) 26368c2ecf20Sopenharmony_ci kvm_set_c0_guest_cause(cop0, CAUSEF_BD); 26378c2ecf20Sopenharmony_ci else 26388c2ecf20Sopenharmony_ci kvm_clear_c0_guest_cause(cop0, CAUSEF_BD); 26398c2ecf20Sopenharmony_ci 26408c2ecf20Sopenharmony_ci } 26418c2ecf20Sopenharmony_ci 26428c2ecf20Sopenharmony_ci arch->pc = kvm_mips_guest_exception_base(vcpu) + 0x180; 26438c2ecf20Sopenharmony_ci 26448c2ecf20Sopenharmony_ci kvm_change_c0_guest_cause(cop0, (0xff), 26458c2ecf20Sopenharmony_ci (EXCCODE_CPU << CAUSEB_EXCCODE)); 26468c2ecf20Sopenharmony_ci kvm_change_c0_guest_cause(cop0, (CAUSEF_CE), (0x1 << CAUSEB_CE)); 26478c2ecf20Sopenharmony_ci 26488c2ecf20Sopenharmony_ci return EMULATE_DONE; 26498c2ecf20Sopenharmony_ci} 26508c2ecf20Sopenharmony_ci 26518c2ecf20Sopenharmony_cienum emulation_result kvm_mips_emulate_ri_exc(u32 cause, 26528c2ecf20Sopenharmony_ci u32 *opc, 26538c2ecf20Sopenharmony_ci struct kvm_vcpu *vcpu) 26548c2ecf20Sopenharmony_ci{ 26558c2ecf20Sopenharmony_ci struct mips_coproc *cop0 = vcpu->arch.cop0; 26568c2ecf20Sopenharmony_ci struct kvm_vcpu_arch *arch = &vcpu->arch; 26578c2ecf20Sopenharmony_ci enum emulation_result er = EMULATE_DONE; 26588c2ecf20Sopenharmony_ci 26598c2ecf20Sopenharmony_ci if ((kvm_read_c0_guest_status(cop0) & ST0_EXL) == 0) { 26608c2ecf20Sopenharmony_ci /* save old pc */ 26618c2ecf20Sopenharmony_ci kvm_write_c0_guest_epc(cop0, arch->pc); 26628c2ecf20Sopenharmony_ci kvm_set_c0_guest_status(cop0, ST0_EXL); 26638c2ecf20Sopenharmony_ci 26648c2ecf20Sopenharmony_ci if (cause & CAUSEF_BD) 26658c2ecf20Sopenharmony_ci kvm_set_c0_guest_cause(cop0, CAUSEF_BD); 26668c2ecf20Sopenharmony_ci else 26678c2ecf20Sopenharmony_ci kvm_clear_c0_guest_cause(cop0, CAUSEF_BD); 26688c2ecf20Sopenharmony_ci 26698c2ecf20Sopenharmony_ci kvm_debug("Delivering RI @ pc %#lx\n", arch->pc); 26708c2ecf20Sopenharmony_ci 26718c2ecf20Sopenharmony_ci kvm_change_c0_guest_cause(cop0, (0xff), 26728c2ecf20Sopenharmony_ci (EXCCODE_RI << CAUSEB_EXCCODE)); 26738c2ecf20Sopenharmony_ci 26748c2ecf20Sopenharmony_ci /* Set PC to the exception entry point */ 26758c2ecf20Sopenharmony_ci arch->pc = kvm_mips_guest_exception_base(vcpu) + 0x180; 26768c2ecf20Sopenharmony_ci 26778c2ecf20Sopenharmony_ci } else { 26788c2ecf20Sopenharmony_ci kvm_err("Trying to deliver RI when EXL is already set\n"); 26798c2ecf20Sopenharmony_ci er = EMULATE_FAIL; 26808c2ecf20Sopenharmony_ci } 26818c2ecf20Sopenharmony_ci 26828c2ecf20Sopenharmony_ci return er; 26838c2ecf20Sopenharmony_ci} 26848c2ecf20Sopenharmony_ci 26858c2ecf20Sopenharmony_cienum emulation_result kvm_mips_emulate_bp_exc(u32 cause, 26868c2ecf20Sopenharmony_ci u32 *opc, 26878c2ecf20Sopenharmony_ci struct kvm_vcpu *vcpu) 26888c2ecf20Sopenharmony_ci{ 26898c2ecf20Sopenharmony_ci struct mips_coproc *cop0 = vcpu->arch.cop0; 26908c2ecf20Sopenharmony_ci struct kvm_vcpu_arch *arch = &vcpu->arch; 26918c2ecf20Sopenharmony_ci enum emulation_result er = EMULATE_DONE; 26928c2ecf20Sopenharmony_ci 26938c2ecf20Sopenharmony_ci if ((kvm_read_c0_guest_status(cop0) & ST0_EXL) == 0) { 26948c2ecf20Sopenharmony_ci /* save old pc */ 26958c2ecf20Sopenharmony_ci kvm_write_c0_guest_epc(cop0, arch->pc); 26968c2ecf20Sopenharmony_ci kvm_set_c0_guest_status(cop0, ST0_EXL); 26978c2ecf20Sopenharmony_ci 26988c2ecf20Sopenharmony_ci if (cause & CAUSEF_BD) 26998c2ecf20Sopenharmony_ci kvm_set_c0_guest_cause(cop0, CAUSEF_BD); 27008c2ecf20Sopenharmony_ci else 27018c2ecf20Sopenharmony_ci kvm_clear_c0_guest_cause(cop0, CAUSEF_BD); 27028c2ecf20Sopenharmony_ci 27038c2ecf20Sopenharmony_ci kvm_debug("Delivering BP @ pc %#lx\n", arch->pc); 27048c2ecf20Sopenharmony_ci 27058c2ecf20Sopenharmony_ci kvm_change_c0_guest_cause(cop0, (0xff), 27068c2ecf20Sopenharmony_ci (EXCCODE_BP << CAUSEB_EXCCODE)); 27078c2ecf20Sopenharmony_ci 27088c2ecf20Sopenharmony_ci /* Set PC to the exception entry point */ 27098c2ecf20Sopenharmony_ci arch->pc = kvm_mips_guest_exception_base(vcpu) + 0x180; 27108c2ecf20Sopenharmony_ci 27118c2ecf20Sopenharmony_ci } else { 27128c2ecf20Sopenharmony_ci kvm_err("Trying to deliver BP when EXL is already set\n"); 27138c2ecf20Sopenharmony_ci er = EMULATE_FAIL; 27148c2ecf20Sopenharmony_ci } 27158c2ecf20Sopenharmony_ci 27168c2ecf20Sopenharmony_ci return er; 27178c2ecf20Sopenharmony_ci} 27188c2ecf20Sopenharmony_ci 27198c2ecf20Sopenharmony_cienum emulation_result kvm_mips_emulate_trap_exc(u32 cause, 27208c2ecf20Sopenharmony_ci u32 *opc, 27218c2ecf20Sopenharmony_ci struct kvm_vcpu *vcpu) 27228c2ecf20Sopenharmony_ci{ 27238c2ecf20Sopenharmony_ci struct mips_coproc *cop0 = vcpu->arch.cop0; 27248c2ecf20Sopenharmony_ci struct kvm_vcpu_arch *arch = &vcpu->arch; 27258c2ecf20Sopenharmony_ci enum emulation_result er = EMULATE_DONE; 27268c2ecf20Sopenharmony_ci 27278c2ecf20Sopenharmony_ci if ((kvm_read_c0_guest_status(cop0) & ST0_EXL) == 0) { 27288c2ecf20Sopenharmony_ci /* save old pc */ 27298c2ecf20Sopenharmony_ci kvm_write_c0_guest_epc(cop0, arch->pc); 27308c2ecf20Sopenharmony_ci kvm_set_c0_guest_status(cop0, ST0_EXL); 27318c2ecf20Sopenharmony_ci 27328c2ecf20Sopenharmony_ci if (cause & CAUSEF_BD) 27338c2ecf20Sopenharmony_ci kvm_set_c0_guest_cause(cop0, CAUSEF_BD); 27348c2ecf20Sopenharmony_ci else 27358c2ecf20Sopenharmony_ci kvm_clear_c0_guest_cause(cop0, CAUSEF_BD); 27368c2ecf20Sopenharmony_ci 27378c2ecf20Sopenharmony_ci kvm_debug("Delivering TRAP @ pc %#lx\n", arch->pc); 27388c2ecf20Sopenharmony_ci 27398c2ecf20Sopenharmony_ci kvm_change_c0_guest_cause(cop0, (0xff), 27408c2ecf20Sopenharmony_ci (EXCCODE_TR << CAUSEB_EXCCODE)); 27418c2ecf20Sopenharmony_ci 27428c2ecf20Sopenharmony_ci /* Set PC to the exception entry point */ 27438c2ecf20Sopenharmony_ci arch->pc = kvm_mips_guest_exception_base(vcpu) + 0x180; 27448c2ecf20Sopenharmony_ci 27458c2ecf20Sopenharmony_ci } else { 27468c2ecf20Sopenharmony_ci kvm_err("Trying to deliver TRAP when EXL is already set\n"); 27478c2ecf20Sopenharmony_ci er = EMULATE_FAIL; 27488c2ecf20Sopenharmony_ci } 27498c2ecf20Sopenharmony_ci 27508c2ecf20Sopenharmony_ci return er; 27518c2ecf20Sopenharmony_ci} 27528c2ecf20Sopenharmony_ci 27538c2ecf20Sopenharmony_cienum emulation_result kvm_mips_emulate_msafpe_exc(u32 cause, 27548c2ecf20Sopenharmony_ci u32 *opc, 27558c2ecf20Sopenharmony_ci struct kvm_vcpu *vcpu) 27568c2ecf20Sopenharmony_ci{ 27578c2ecf20Sopenharmony_ci struct mips_coproc *cop0 = vcpu->arch.cop0; 27588c2ecf20Sopenharmony_ci struct kvm_vcpu_arch *arch = &vcpu->arch; 27598c2ecf20Sopenharmony_ci enum emulation_result er = EMULATE_DONE; 27608c2ecf20Sopenharmony_ci 27618c2ecf20Sopenharmony_ci if ((kvm_read_c0_guest_status(cop0) & ST0_EXL) == 0) { 27628c2ecf20Sopenharmony_ci /* save old pc */ 27638c2ecf20Sopenharmony_ci kvm_write_c0_guest_epc(cop0, arch->pc); 27648c2ecf20Sopenharmony_ci kvm_set_c0_guest_status(cop0, ST0_EXL); 27658c2ecf20Sopenharmony_ci 27668c2ecf20Sopenharmony_ci if (cause & CAUSEF_BD) 27678c2ecf20Sopenharmony_ci kvm_set_c0_guest_cause(cop0, CAUSEF_BD); 27688c2ecf20Sopenharmony_ci else 27698c2ecf20Sopenharmony_ci kvm_clear_c0_guest_cause(cop0, CAUSEF_BD); 27708c2ecf20Sopenharmony_ci 27718c2ecf20Sopenharmony_ci kvm_debug("Delivering MSAFPE @ pc %#lx\n", arch->pc); 27728c2ecf20Sopenharmony_ci 27738c2ecf20Sopenharmony_ci kvm_change_c0_guest_cause(cop0, (0xff), 27748c2ecf20Sopenharmony_ci (EXCCODE_MSAFPE << CAUSEB_EXCCODE)); 27758c2ecf20Sopenharmony_ci 27768c2ecf20Sopenharmony_ci /* Set PC to the exception entry point */ 27778c2ecf20Sopenharmony_ci arch->pc = kvm_mips_guest_exception_base(vcpu) + 0x180; 27788c2ecf20Sopenharmony_ci 27798c2ecf20Sopenharmony_ci } else { 27808c2ecf20Sopenharmony_ci kvm_err("Trying to deliver MSAFPE when EXL is already set\n"); 27818c2ecf20Sopenharmony_ci er = EMULATE_FAIL; 27828c2ecf20Sopenharmony_ci } 27838c2ecf20Sopenharmony_ci 27848c2ecf20Sopenharmony_ci return er; 27858c2ecf20Sopenharmony_ci} 27868c2ecf20Sopenharmony_ci 27878c2ecf20Sopenharmony_cienum emulation_result kvm_mips_emulate_fpe_exc(u32 cause, 27888c2ecf20Sopenharmony_ci u32 *opc, 27898c2ecf20Sopenharmony_ci struct kvm_vcpu *vcpu) 27908c2ecf20Sopenharmony_ci{ 27918c2ecf20Sopenharmony_ci struct mips_coproc *cop0 = vcpu->arch.cop0; 27928c2ecf20Sopenharmony_ci struct kvm_vcpu_arch *arch = &vcpu->arch; 27938c2ecf20Sopenharmony_ci enum emulation_result er = EMULATE_DONE; 27948c2ecf20Sopenharmony_ci 27958c2ecf20Sopenharmony_ci if ((kvm_read_c0_guest_status(cop0) & ST0_EXL) == 0) { 27968c2ecf20Sopenharmony_ci /* save old pc */ 27978c2ecf20Sopenharmony_ci kvm_write_c0_guest_epc(cop0, arch->pc); 27988c2ecf20Sopenharmony_ci kvm_set_c0_guest_status(cop0, ST0_EXL); 27998c2ecf20Sopenharmony_ci 28008c2ecf20Sopenharmony_ci if (cause & CAUSEF_BD) 28018c2ecf20Sopenharmony_ci kvm_set_c0_guest_cause(cop0, CAUSEF_BD); 28028c2ecf20Sopenharmony_ci else 28038c2ecf20Sopenharmony_ci kvm_clear_c0_guest_cause(cop0, CAUSEF_BD); 28048c2ecf20Sopenharmony_ci 28058c2ecf20Sopenharmony_ci kvm_debug("Delivering FPE @ pc %#lx\n", arch->pc); 28068c2ecf20Sopenharmony_ci 28078c2ecf20Sopenharmony_ci kvm_change_c0_guest_cause(cop0, (0xff), 28088c2ecf20Sopenharmony_ci (EXCCODE_FPE << CAUSEB_EXCCODE)); 28098c2ecf20Sopenharmony_ci 28108c2ecf20Sopenharmony_ci /* Set PC to the exception entry point */ 28118c2ecf20Sopenharmony_ci arch->pc = kvm_mips_guest_exception_base(vcpu) + 0x180; 28128c2ecf20Sopenharmony_ci 28138c2ecf20Sopenharmony_ci } else { 28148c2ecf20Sopenharmony_ci kvm_err("Trying to deliver FPE when EXL is already set\n"); 28158c2ecf20Sopenharmony_ci er = EMULATE_FAIL; 28168c2ecf20Sopenharmony_ci } 28178c2ecf20Sopenharmony_ci 28188c2ecf20Sopenharmony_ci return er; 28198c2ecf20Sopenharmony_ci} 28208c2ecf20Sopenharmony_ci 28218c2ecf20Sopenharmony_cienum emulation_result kvm_mips_emulate_msadis_exc(u32 cause, 28228c2ecf20Sopenharmony_ci u32 *opc, 28238c2ecf20Sopenharmony_ci struct kvm_vcpu *vcpu) 28248c2ecf20Sopenharmony_ci{ 28258c2ecf20Sopenharmony_ci struct mips_coproc *cop0 = vcpu->arch.cop0; 28268c2ecf20Sopenharmony_ci struct kvm_vcpu_arch *arch = &vcpu->arch; 28278c2ecf20Sopenharmony_ci enum emulation_result er = EMULATE_DONE; 28288c2ecf20Sopenharmony_ci 28298c2ecf20Sopenharmony_ci if ((kvm_read_c0_guest_status(cop0) & ST0_EXL) == 0) { 28308c2ecf20Sopenharmony_ci /* save old pc */ 28318c2ecf20Sopenharmony_ci kvm_write_c0_guest_epc(cop0, arch->pc); 28328c2ecf20Sopenharmony_ci kvm_set_c0_guest_status(cop0, ST0_EXL); 28338c2ecf20Sopenharmony_ci 28348c2ecf20Sopenharmony_ci if (cause & CAUSEF_BD) 28358c2ecf20Sopenharmony_ci kvm_set_c0_guest_cause(cop0, CAUSEF_BD); 28368c2ecf20Sopenharmony_ci else 28378c2ecf20Sopenharmony_ci kvm_clear_c0_guest_cause(cop0, CAUSEF_BD); 28388c2ecf20Sopenharmony_ci 28398c2ecf20Sopenharmony_ci kvm_debug("Delivering MSADIS @ pc %#lx\n", arch->pc); 28408c2ecf20Sopenharmony_ci 28418c2ecf20Sopenharmony_ci kvm_change_c0_guest_cause(cop0, (0xff), 28428c2ecf20Sopenharmony_ci (EXCCODE_MSADIS << CAUSEB_EXCCODE)); 28438c2ecf20Sopenharmony_ci 28448c2ecf20Sopenharmony_ci /* Set PC to the exception entry point */ 28458c2ecf20Sopenharmony_ci arch->pc = kvm_mips_guest_exception_base(vcpu) + 0x180; 28468c2ecf20Sopenharmony_ci 28478c2ecf20Sopenharmony_ci } else { 28488c2ecf20Sopenharmony_ci kvm_err("Trying to deliver MSADIS when EXL is already set\n"); 28498c2ecf20Sopenharmony_ci er = EMULATE_FAIL; 28508c2ecf20Sopenharmony_ci } 28518c2ecf20Sopenharmony_ci 28528c2ecf20Sopenharmony_ci return er; 28538c2ecf20Sopenharmony_ci} 28548c2ecf20Sopenharmony_ci 28558c2ecf20Sopenharmony_cienum emulation_result kvm_mips_handle_ri(u32 cause, u32 *opc, 28568c2ecf20Sopenharmony_ci struct kvm_vcpu *vcpu) 28578c2ecf20Sopenharmony_ci{ 28588c2ecf20Sopenharmony_ci struct mips_coproc *cop0 = vcpu->arch.cop0; 28598c2ecf20Sopenharmony_ci struct kvm_vcpu_arch *arch = &vcpu->arch; 28608c2ecf20Sopenharmony_ci enum emulation_result er = EMULATE_DONE; 28618c2ecf20Sopenharmony_ci unsigned long curr_pc; 28628c2ecf20Sopenharmony_ci union mips_instruction inst; 28638c2ecf20Sopenharmony_ci int err; 28648c2ecf20Sopenharmony_ci 28658c2ecf20Sopenharmony_ci /* 28668c2ecf20Sopenharmony_ci * Update PC and hold onto current PC in case there is 28678c2ecf20Sopenharmony_ci * an error and we want to rollback the PC 28688c2ecf20Sopenharmony_ci */ 28698c2ecf20Sopenharmony_ci curr_pc = vcpu->arch.pc; 28708c2ecf20Sopenharmony_ci er = update_pc(vcpu, cause); 28718c2ecf20Sopenharmony_ci if (er == EMULATE_FAIL) 28728c2ecf20Sopenharmony_ci return er; 28738c2ecf20Sopenharmony_ci 28748c2ecf20Sopenharmony_ci /* Fetch the instruction. */ 28758c2ecf20Sopenharmony_ci if (cause & CAUSEF_BD) 28768c2ecf20Sopenharmony_ci opc += 1; 28778c2ecf20Sopenharmony_ci err = kvm_get_badinstr(opc, vcpu, &inst.word); 28788c2ecf20Sopenharmony_ci if (err) { 28798c2ecf20Sopenharmony_ci kvm_err("%s: Cannot get inst @ %p (%d)\n", __func__, opc, err); 28808c2ecf20Sopenharmony_ci return EMULATE_FAIL; 28818c2ecf20Sopenharmony_ci } 28828c2ecf20Sopenharmony_ci 28838c2ecf20Sopenharmony_ci if (inst.r_format.opcode == spec3_op && 28848c2ecf20Sopenharmony_ci inst.r_format.func == rdhwr_op && 28858c2ecf20Sopenharmony_ci inst.r_format.rs == 0 && 28868c2ecf20Sopenharmony_ci (inst.r_format.re >> 3) == 0) { 28878c2ecf20Sopenharmony_ci int usermode = !KVM_GUEST_KERNEL_MODE(vcpu); 28888c2ecf20Sopenharmony_ci int rd = inst.r_format.rd; 28898c2ecf20Sopenharmony_ci int rt = inst.r_format.rt; 28908c2ecf20Sopenharmony_ci int sel = inst.r_format.re & 0x7; 28918c2ecf20Sopenharmony_ci 28928c2ecf20Sopenharmony_ci /* If usermode, check RDHWR rd is allowed by guest HWREna */ 28938c2ecf20Sopenharmony_ci if (usermode && !(kvm_read_c0_guest_hwrena(cop0) & BIT(rd))) { 28948c2ecf20Sopenharmony_ci kvm_debug("RDHWR %#x disallowed by HWREna @ %p\n", 28958c2ecf20Sopenharmony_ci rd, opc); 28968c2ecf20Sopenharmony_ci goto emulate_ri; 28978c2ecf20Sopenharmony_ci } 28988c2ecf20Sopenharmony_ci switch (rd) { 28998c2ecf20Sopenharmony_ci case MIPS_HWR_CPUNUM: /* CPU number */ 29008c2ecf20Sopenharmony_ci arch->gprs[rt] = vcpu->vcpu_id; 29018c2ecf20Sopenharmony_ci break; 29028c2ecf20Sopenharmony_ci case MIPS_HWR_SYNCISTEP: /* SYNCI length */ 29038c2ecf20Sopenharmony_ci arch->gprs[rt] = min(current_cpu_data.dcache.linesz, 29048c2ecf20Sopenharmony_ci current_cpu_data.icache.linesz); 29058c2ecf20Sopenharmony_ci break; 29068c2ecf20Sopenharmony_ci case MIPS_HWR_CC: /* Read count register */ 29078c2ecf20Sopenharmony_ci arch->gprs[rt] = (s32)kvm_mips_read_count(vcpu); 29088c2ecf20Sopenharmony_ci break; 29098c2ecf20Sopenharmony_ci case MIPS_HWR_CCRES: /* Count register resolution */ 29108c2ecf20Sopenharmony_ci switch (current_cpu_data.cputype) { 29118c2ecf20Sopenharmony_ci case CPU_20KC: 29128c2ecf20Sopenharmony_ci case CPU_25KF: 29138c2ecf20Sopenharmony_ci arch->gprs[rt] = 1; 29148c2ecf20Sopenharmony_ci break; 29158c2ecf20Sopenharmony_ci default: 29168c2ecf20Sopenharmony_ci arch->gprs[rt] = 2; 29178c2ecf20Sopenharmony_ci } 29188c2ecf20Sopenharmony_ci break; 29198c2ecf20Sopenharmony_ci case MIPS_HWR_ULR: /* Read UserLocal register */ 29208c2ecf20Sopenharmony_ci arch->gprs[rt] = kvm_read_c0_guest_userlocal(cop0); 29218c2ecf20Sopenharmony_ci break; 29228c2ecf20Sopenharmony_ci 29238c2ecf20Sopenharmony_ci default: 29248c2ecf20Sopenharmony_ci kvm_debug("RDHWR %#x not supported @ %p\n", rd, opc); 29258c2ecf20Sopenharmony_ci goto emulate_ri; 29268c2ecf20Sopenharmony_ci } 29278c2ecf20Sopenharmony_ci 29288c2ecf20Sopenharmony_ci trace_kvm_hwr(vcpu, KVM_TRACE_RDHWR, KVM_TRACE_HWR(rd, sel), 29298c2ecf20Sopenharmony_ci vcpu->arch.gprs[rt]); 29308c2ecf20Sopenharmony_ci } else { 29318c2ecf20Sopenharmony_ci kvm_debug("Emulate RI not supported @ %p: %#x\n", 29328c2ecf20Sopenharmony_ci opc, inst.word); 29338c2ecf20Sopenharmony_ci goto emulate_ri; 29348c2ecf20Sopenharmony_ci } 29358c2ecf20Sopenharmony_ci 29368c2ecf20Sopenharmony_ci return EMULATE_DONE; 29378c2ecf20Sopenharmony_ci 29388c2ecf20Sopenharmony_ciemulate_ri: 29398c2ecf20Sopenharmony_ci /* 29408c2ecf20Sopenharmony_ci * Rollback PC (if in branch delay slot then the PC already points to 29418c2ecf20Sopenharmony_ci * branch target), and pass the RI exception to the guest OS. 29428c2ecf20Sopenharmony_ci */ 29438c2ecf20Sopenharmony_ci vcpu->arch.pc = curr_pc; 29448c2ecf20Sopenharmony_ci return kvm_mips_emulate_ri_exc(cause, opc, vcpu); 29458c2ecf20Sopenharmony_ci} 29468c2ecf20Sopenharmony_ci 29478c2ecf20Sopenharmony_cienum emulation_result kvm_mips_complete_mmio_load(struct kvm_vcpu *vcpu) 29488c2ecf20Sopenharmony_ci{ 29498c2ecf20Sopenharmony_ci struct kvm_run *run = vcpu->run; 29508c2ecf20Sopenharmony_ci unsigned long *gpr = &vcpu->arch.gprs[vcpu->arch.io_gpr]; 29518c2ecf20Sopenharmony_ci enum emulation_result er = EMULATE_DONE; 29528c2ecf20Sopenharmony_ci 29538c2ecf20Sopenharmony_ci if (run->mmio.len > sizeof(*gpr)) { 29548c2ecf20Sopenharmony_ci kvm_err("Bad MMIO length: %d", run->mmio.len); 29558c2ecf20Sopenharmony_ci er = EMULATE_FAIL; 29568c2ecf20Sopenharmony_ci goto done; 29578c2ecf20Sopenharmony_ci } 29588c2ecf20Sopenharmony_ci 29598c2ecf20Sopenharmony_ci /* Restore saved resume PC */ 29608c2ecf20Sopenharmony_ci vcpu->arch.pc = vcpu->arch.io_pc; 29618c2ecf20Sopenharmony_ci 29628c2ecf20Sopenharmony_ci switch (run->mmio.len) { 29638c2ecf20Sopenharmony_ci case 8: 29648c2ecf20Sopenharmony_ci switch (vcpu->mmio_needed) { 29658c2ecf20Sopenharmony_ci case 11: 29668c2ecf20Sopenharmony_ci *gpr = (vcpu->arch.gprs[vcpu->arch.io_gpr] & 0xffffffffffffff) | 29678c2ecf20Sopenharmony_ci (((*(s64 *)run->mmio.data) & 0xff) << 56); 29688c2ecf20Sopenharmony_ci break; 29698c2ecf20Sopenharmony_ci case 12: 29708c2ecf20Sopenharmony_ci *gpr = (vcpu->arch.gprs[vcpu->arch.io_gpr] & 0xffffffffffff) | 29718c2ecf20Sopenharmony_ci (((*(s64 *)run->mmio.data) & 0xffff) << 48); 29728c2ecf20Sopenharmony_ci break; 29738c2ecf20Sopenharmony_ci case 13: 29748c2ecf20Sopenharmony_ci *gpr = (vcpu->arch.gprs[vcpu->arch.io_gpr] & 0xffffffffff) | 29758c2ecf20Sopenharmony_ci (((*(s64 *)run->mmio.data) & 0xffffff) << 40); 29768c2ecf20Sopenharmony_ci break; 29778c2ecf20Sopenharmony_ci case 14: 29788c2ecf20Sopenharmony_ci *gpr = (vcpu->arch.gprs[vcpu->arch.io_gpr] & 0xffffffff) | 29798c2ecf20Sopenharmony_ci (((*(s64 *)run->mmio.data) & 0xffffffff) << 32); 29808c2ecf20Sopenharmony_ci break; 29818c2ecf20Sopenharmony_ci case 15: 29828c2ecf20Sopenharmony_ci *gpr = (vcpu->arch.gprs[vcpu->arch.io_gpr] & 0xffffff) | 29838c2ecf20Sopenharmony_ci (((*(s64 *)run->mmio.data) & 0xffffffffff) << 24); 29848c2ecf20Sopenharmony_ci break; 29858c2ecf20Sopenharmony_ci case 16: 29868c2ecf20Sopenharmony_ci *gpr = (vcpu->arch.gprs[vcpu->arch.io_gpr] & 0xffff) | 29878c2ecf20Sopenharmony_ci (((*(s64 *)run->mmio.data) & 0xffffffffffff) << 16); 29888c2ecf20Sopenharmony_ci break; 29898c2ecf20Sopenharmony_ci case 17: 29908c2ecf20Sopenharmony_ci *gpr = (vcpu->arch.gprs[vcpu->arch.io_gpr] & 0xff) | 29918c2ecf20Sopenharmony_ci (((*(s64 *)run->mmio.data) & 0xffffffffffffff) << 8); 29928c2ecf20Sopenharmony_ci break; 29938c2ecf20Sopenharmony_ci case 18: 29948c2ecf20Sopenharmony_ci case 19: 29958c2ecf20Sopenharmony_ci *gpr = *(s64 *)run->mmio.data; 29968c2ecf20Sopenharmony_ci break; 29978c2ecf20Sopenharmony_ci case 20: 29988c2ecf20Sopenharmony_ci *gpr = (vcpu->arch.gprs[vcpu->arch.io_gpr] & 0xff00000000000000) | 29998c2ecf20Sopenharmony_ci ((((*(s64 *)run->mmio.data)) >> 8) & 0xffffffffffffff); 30008c2ecf20Sopenharmony_ci break; 30018c2ecf20Sopenharmony_ci case 21: 30028c2ecf20Sopenharmony_ci *gpr = (vcpu->arch.gprs[vcpu->arch.io_gpr] & 0xffff000000000000) | 30038c2ecf20Sopenharmony_ci ((((*(s64 *)run->mmio.data)) >> 16) & 0xffffffffffff); 30048c2ecf20Sopenharmony_ci break; 30058c2ecf20Sopenharmony_ci case 22: 30068c2ecf20Sopenharmony_ci *gpr = (vcpu->arch.gprs[vcpu->arch.io_gpr] & 0xffffff0000000000) | 30078c2ecf20Sopenharmony_ci ((((*(s64 *)run->mmio.data)) >> 24) & 0xffffffffff); 30088c2ecf20Sopenharmony_ci break; 30098c2ecf20Sopenharmony_ci case 23: 30108c2ecf20Sopenharmony_ci *gpr = (vcpu->arch.gprs[vcpu->arch.io_gpr] & 0xffffffff00000000) | 30118c2ecf20Sopenharmony_ci ((((*(s64 *)run->mmio.data)) >> 32) & 0xffffffff); 30128c2ecf20Sopenharmony_ci break; 30138c2ecf20Sopenharmony_ci case 24: 30148c2ecf20Sopenharmony_ci *gpr = (vcpu->arch.gprs[vcpu->arch.io_gpr] & 0xffffffffff000000) | 30158c2ecf20Sopenharmony_ci ((((*(s64 *)run->mmio.data)) >> 40) & 0xffffff); 30168c2ecf20Sopenharmony_ci break; 30178c2ecf20Sopenharmony_ci case 25: 30188c2ecf20Sopenharmony_ci *gpr = (vcpu->arch.gprs[vcpu->arch.io_gpr] & 0xffffffffffff0000) | 30198c2ecf20Sopenharmony_ci ((((*(s64 *)run->mmio.data)) >> 48) & 0xffff); 30208c2ecf20Sopenharmony_ci break; 30218c2ecf20Sopenharmony_ci case 26: 30228c2ecf20Sopenharmony_ci *gpr = (vcpu->arch.gprs[vcpu->arch.io_gpr] & 0xffffffffffffff00) | 30238c2ecf20Sopenharmony_ci ((((*(s64 *)run->mmio.data)) >> 56) & 0xff); 30248c2ecf20Sopenharmony_ci break; 30258c2ecf20Sopenharmony_ci default: 30268c2ecf20Sopenharmony_ci *gpr = *(s64 *)run->mmio.data; 30278c2ecf20Sopenharmony_ci } 30288c2ecf20Sopenharmony_ci break; 30298c2ecf20Sopenharmony_ci 30308c2ecf20Sopenharmony_ci case 4: 30318c2ecf20Sopenharmony_ci switch (vcpu->mmio_needed) { 30328c2ecf20Sopenharmony_ci case 1: 30338c2ecf20Sopenharmony_ci *gpr = *(u32 *)run->mmio.data; 30348c2ecf20Sopenharmony_ci break; 30358c2ecf20Sopenharmony_ci case 2: 30368c2ecf20Sopenharmony_ci *gpr = *(s32 *)run->mmio.data; 30378c2ecf20Sopenharmony_ci break; 30388c2ecf20Sopenharmony_ci case 3: 30398c2ecf20Sopenharmony_ci *gpr = (vcpu->arch.gprs[vcpu->arch.io_gpr] & 0xffffff) | 30408c2ecf20Sopenharmony_ci (((*(s32 *)run->mmio.data) & 0xff) << 24); 30418c2ecf20Sopenharmony_ci break; 30428c2ecf20Sopenharmony_ci case 4: 30438c2ecf20Sopenharmony_ci *gpr = (vcpu->arch.gprs[vcpu->arch.io_gpr] & 0xffff) | 30448c2ecf20Sopenharmony_ci (((*(s32 *)run->mmio.data) & 0xffff) << 16); 30458c2ecf20Sopenharmony_ci break; 30468c2ecf20Sopenharmony_ci case 5: 30478c2ecf20Sopenharmony_ci *gpr = (vcpu->arch.gprs[vcpu->arch.io_gpr] & 0xff) | 30488c2ecf20Sopenharmony_ci (((*(s32 *)run->mmio.data) & 0xffffff) << 8); 30498c2ecf20Sopenharmony_ci break; 30508c2ecf20Sopenharmony_ci case 6: 30518c2ecf20Sopenharmony_ci case 7: 30528c2ecf20Sopenharmony_ci *gpr = *(s32 *)run->mmio.data; 30538c2ecf20Sopenharmony_ci break; 30548c2ecf20Sopenharmony_ci case 8: 30558c2ecf20Sopenharmony_ci *gpr = (vcpu->arch.gprs[vcpu->arch.io_gpr] & 0xff000000) | 30568c2ecf20Sopenharmony_ci ((((*(s32 *)run->mmio.data)) >> 8) & 0xffffff); 30578c2ecf20Sopenharmony_ci break; 30588c2ecf20Sopenharmony_ci case 9: 30598c2ecf20Sopenharmony_ci *gpr = (vcpu->arch.gprs[vcpu->arch.io_gpr] & 0xffff0000) | 30608c2ecf20Sopenharmony_ci ((((*(s32 *)run->mmio.data)) >> 16) & 0xffff); 30618c2ecf20Sopenharmony_ci break; 30628c2ecf20Sopenharmony_ci case 10: 30638c2ecf20Sopenharmony_ci *gpr = (vcpu->arch.gprs[vcpu->arch.io_gpr] & 0xffffff00) | 30648c2ecf20Sopenharmony_ci ((((*(s32 *)run->mmio.data)) >> 24) & 0xff); 30658c2ecf20Sopenharmony_ci break; 30668c2ecf20Sopenharmony_ci default: 30678c2ecf20Sopenharmony_ci *gpr = *(s32 *)run->mmio.data; 30688c2ecf20Sopenharmony_ci } 30698c2ecf20Sopenharmony_ci break; 30708c2ecf20Sopenharmony_ci 30718c2ecf20Sopenharmony_ci case 2: 30728c2ecf20Sopenharmony_ci if (vcpu->mmio_needed == 1) 30738c2ecf20Sopenharmony_ci *gpr = *(u16 *)run->mmio.data; 30748c2ecf20Sopenharmony_ci else 30758c2ecf20Sopenharmony_ci *gpr = *(s16 *)run->mmio.data; 30768c2ecf20Sopenharmony_ci 30778c2ecf20Sopenharmony_ci break; 30788c2ecf20Sopenharmony_ci case 1: 30798c2ecf20Sopenharmony_ci if (vcpu->mmio_needed == 1) 30808c2ecf20Sopenharmony_ci *gpr = *(u8 *)run->mmio.data; 30818c2ecf20Sopenharmony_ci else 30828c2ecf20Sopenharmony_ci *gpr = *(s8 *)run->mmio.data; 30838c2ecf20Sopenharmony_ci break; 30848c2ecf20Sopenharmony_ci } 30858c2ecf20Sopenharmony_ci 30868c2ecf20Sopenharmony_cidone: 30878c2ecf20Sopenharmony_ci return er; 30888c2ecf20Sopenharmony_ci} 30898c2ecf20Sopenharmony_ci 30908c2ecf20Sopenharmony_cistatic enum emulation_result kvm_mips_emulate_exc(u32 cause, 30918c2ecf20Sopenharmony_ci u32 *opc, 30928c2ecf20Sopenharmony_ci struct kvm_vcpu *vcpu) 30938c2ecf20Sopenharmony_ci{ 30948c2ecf20Sopenharmony_ci u32 exccode = (cause >> CAUSEB_EXCCODE) & 0x1f; 30958c2ecf20Sopenharmony_ci struct mips_coproc *cop0 = vcpu->arch.cop0; 30968c2ecf20Sopenharmony_ci struct kvm_vcpu_arch *arch = &vcpu->arch; 30978c2ecf20Sopenharmony_ci enum emulation_result er = EMULATE_DONE; 30988c2ecf20Sopenharmony_ci 30998c2ecf20Sopenharmony_ci if ((kvm_read_c0_guest_status(cop0) & ST0_EXL) == 0) { 31008c2ecf20Sopenharmony_ci /* save old pc */ 31018c2ecf20Sopenharmony_ci kvm_write_c0_guest_epc(cop0, arch->pc); 31028c2ecf20Sopenharmony_ci kvm_set_c0_guest_status(cop0, ST0_EXL); 31038c2ecf20Sopenharmony_ci 31048c2ecf20Sopenharmony_ci if (cause & CAUSEF_BD) 31058c2ecf20Sopenharmony_ci kvm_set_c0_guest_cause(cop0, CAUSEF_BD); 31068c2ecf20Sopenharmony_ci else 31078c2ecf20Sopenharmony_ci kvm_clear_c0_guest_cause(cop0, CAUSEF_BD); 31088c2ecf20Sopenharmony_ci 31098c2ecf20Sopenharmony_ci kvm_change_c0_guest_cause(cop0, (0xff), 31108c2ecf20Sopenharmony_ci (exccode << CAUSEB_EXCCODE)); 31118c2ecf20Sopenharmony_ci 31128c2ecf20Sopenharmony_ci /* Set PC to the exception entry point */ 31138c2ecf20Sopenharmony_ci arch->pc = kvm_mips_guest_exception_base(vcpu) + 0x180; 31148c2ecf20Sopenharmony_ci kvm_write_c0_guest_badvaddr(cop0, vcpu->arch.host_cp0_badvaddr); 31158c2ecf20Sopenharmony_ci 31168c2ecf20Sopenharmony_ci kvm_debug("Delivering EXC %d @ pc %#lx, badVaddr: %#lx\n", 31178c2ecf20Sopenharmony_ci exccode, kvm_read_c0_guest_epc(cop0), 31188c2ecf20Sopenharmony_ci kvm_read_c0_guest_badvaddr(cop0)); 31198c2ecf20Sopenharmony_ci } else { 31208c2ecf20Sopenharmony_ci kvm_err("Trying to deliver EXC when EXL is already set\n"); 31218c2ecf20Sopenharmony_ci er = EMULATE_FAIL; 31228c2ecf20Sopenharmony_ci } 31238c2ecf20Sopenharmony_ci 31248c2ecf20Sopenharmony_ci return er; 31258c2ecf20Sopenharmony_ci} 31268c2ecf20Sopenharmony_ci 31278c2ecf20Sopenharmony_cienum emulation_result kvm_mips_check_privilege(u32 cause, 31288c2ecf20Sopenharmony_ci u32 *opc, 31298c2ecf20Sopenharmony_ci struct kvm_vcpu *vcpu) 31308c2ecf20Sopenharmony_ci{ 31318c2ecf20Sopenharmony_ci enum emulation_result er = EMULATE_DONE; 31328c2ecf20Sopenharmony_ci u32 exccode = (cause >> CAUSEB_EXCCODE) & 0x1f; 31338c2ecf20Sopenharmony_ci unsigned long badvaddr = vcpu->arch.host_cp0_badvaddr; 31348c2ecf20Sopenharmony_ci 31358c2ecf20Sopenharmony_ci int usermode = !KVM_GUEST_KERNEL_MODE(vcpu); 31368c2ecf20Sopenharmony_ci 31378c2ecf20Sopenharmony_ci if (usermode) { 31388c2ecf20Sopenharmony_ci switch (exccode) { 31398c2ecf20Sopenharmony_ci case EXCCODE_INT: 31408c2ecf20Sopenharmony_ci case EXCCODE_SYS: 31418c2ecf20Sopenharmony_ci case EXCCODE_BP: 31428c2ecf20Sopenharmony_ci case EXCCODE_RI: 31438c2ecf20Sopenharmony_ci case EXCCODE_TR: 31448c2ecf20Sopenharmony_ci case EXCCODE_MSAFPE: 31458c2ecf20Sopenharmony_ci case EXCCODE_FPE: 31468c2ecf20Sopenharmony_ci case EXCCODE_MSADIS: 31478c2ecf20Sopenharmony_ci break; 31488c2ecf20Sopenharmony_ci 31498c2ecf20Sopenharmony_ci case EXCCODE_CPU: 31508c2ecf20Sopenharmony_ci if (((cause & CAUSEF_CE) >> CAUSEB_CE) == 0) 31518c2ecf20Sopenharmony_ci er = EMULATE_PRIV_FAIL; 31528c2ecf20Sopenharmony_ci break; 31538c2ecf20Sopenharmony_ci 31548c2ecf20Sopenharmony_ci case EXCCODE_MOD: 31558c2ecf20Sopenharmony_ci break; 31568c2ecf20Sopenharmony_ci 31578c2ecf20Sopenharmony_ci case EXCCODE_TLBL: 31588c2ecf20Sopenharmony_ci /* 31598c2ecf20Sopenharmony_ci * We we are accessing Guest kernel space, then send an 31608c2ecf20Sopenharmony_ci * address error exception to the guest 31618c2ecf20Sopenharmony_ci */ 31628c2ecf20Sopenharmony_ci if (badvaddr >= (unsigned long) KVM_GUEST_KSEG0) { 31638c2ecf20Sopenharmony_ci kvm_debug("%s: LD MISS @ %#lx\n", __func__, 31648c2ecf20Sopenharmony_ci badvaddr); 31658c2ecf20Sopenharmony_ci cause &= ~0xff; 31668c2ecf20Sopenharmony_ci cause |= (EXCCODE_ADEL << CAUSEB_EXCCODE); 31678c2ecf20Sopenharmony_ci er = EMULATE_PRIV_FAIL; 31688c2ecf20Sopenharmony_ci } 31698c2ecf20Sopenharmony_ci break; 31708c2ecf20Sopenharmony_ci 31718c2ecf20Sopenharmony_ci case EXCCODE_TLBS: 31728c2ecf20Sopenharmony_ci /* 31738c2ecf20Sopenharmony_ci * We we are accessing Guest kernel space, then send an 31748c2ecf20Sopenharmony_ci * address error exception to the guest 31758c2ecf20Sopenharmony_ci */ 31768c2ecf20Sopenharmony_ci if (badvaddr >= (unsigned long) KVM_GUEST_KSEG0) { 31778c2ecf20Sopenharmony_ci kvm_debug("%s: ST MISS @ %#lx\n", __func__, 31788c2ecf20Sopenharmony_ci badvaddr); 31798c2ecf20Sopenharmony_ci cause &= ~0xff; 31808c2ecf20Sopenharmony_ci cause |= (EXCCODE_ADES << CAUSEB_EXCCODE); 31818c2ecf20Sopenharmony_ci er = EMULATE_PRIV_FAIL; 31828c2ecf20Sopenharmony_ci } 31838c2ecf20Sopenharmony_ci break; 31848c2ecf20Sopenharmony_ci 31858c2ecf20Sopenharmony_ci case EXCCODE_ADES: 31868c2ecf20Sopenharmony_ci kvm_debug("%s: address error ST @ %#lx\n", __func__, 31878c2ecf20Sopenharmony_ci badvaddr); 31888c2ecf20Sopenharmony_ci if ((badvaddr & PAGE_MASK) == KVM_GUEST_COMMPAGE_ADDR) { 31898c2ecf20Sopenharmony_ci cause &= ~0xff; 31908c2ecf20Sopenharmony_ci cause |= (EXCCODE_TLBS << CAUSEB_EXCCODE); 31918c2ecf20Sopenharmony_ci } 31928c2ecf20Sopenharmony_ci er = EMULATE_PRIV_FAIL; 31938c2ecf20Sopenharmony_ci break; 31948c2ecf20Sopenharmony_ci case EXCCODE_ADEL: 31958c2ecf20Sopenharmony_ci kvm_debug("%s: address error LD @ %#lx\n", __func__, 31968c2ecf20Sopenharmony_ci badvaddr); 31978c2ecf20Sopenharmony_ci if ((badvaddr & PAGE_MASK) == KVM_GUEST_COMMPAGE_ADDR) { 31988c2ecf20Sopenharmony_ci cause &= ~0xff; 31998c2ecf20Sopenharmony_ci cause |= (EXCCODE_TLBL << CAUSEB_EXCCODE); 32008c2ecf20Sopenharmony_ci } 32018c2ecf20Sopenharmony_ci er = EMULATE_PRIV_FAIL; 32028c2ecf20Sopenharmony_ci break; 32038c2ecf20Sopenharmony_ci default: 32048c2ecf20Sopenharmony_ci er = EMULATE_PRIV_FAIL; 32058c2ecf20Sopenharmony_ci break; 32068c2ecf20Sopenharmony_ci } 32078c2ecf20Sopenharmony_ci } 32088c2ecf20Sopenharmony_ci 32098c2ecf20Sopenharmony_ci if (er == EMULATE_PRIV_FAIL) 32108c2ecf20Sopenharmony_ci kvm_mips_emulate_exc(cause, opc, vcpu); 32118c2ecf20Sopenharmony_ci 32128c2ecf20Sopenharmony_ci return er; 32138c2ecf20Sopenharmony_ci} 32148c2ecf20Sopenharmony_ci 32158c2ecf20Sopenharmony_ci/* 32168c2ecf20Sopenharmony_ci * User Address (UA) fault, this could happen if 32178c2ecf20Sopenharmony_ci * (1) TLB entry not present/valid in both Guest and shadow host TLBs, in this 32188c2ecf20Sopenharmony_ci * case we pass on the fault to the guest kernel and let it handle it. 32198c2ecf20Sopenharmony_ci * (2) TLB entry is present in the Guest TLB but not in the shadow, in this 32208c2ecf20Sopenharmony_ci * case we inject the TLB from the Guest TLB into the shadow host TLB 32218c2ecf20Sopenharmony_ci */ 32228c2ecf20Sopenharmony_cienum emulation_result kvm_mips_handle_tlbmiss(u32 cause, 32238c2ecf20Sopenharmony_ci u32 *opc, 32248c2ecf20Sopenharmony_ci struct kvm_vcpu *vcpu, 32258c2ecf20Sopenharmony_ci bool write_fault) 32268c2ecf20Sopenharmony_ci{ 32278c2ecf20Sopenharmony_ci enum emulation_result er = EMULATE_DONE; 32288c2ecf20Sopenharmony_ci u32 exccode = (cause >> CAUSEB_EXCCODE) & 0x1f; 32298c2ecf20Sopenharmony_ci unsigned long va = vcpu->arch.host_cp0_badvaddr; 32308c2ecf20Sopenharmony_ci int index; 32318c2ecf20Sopenharmony_ci 32328c2ecf20Sopenharmony_ci kvm_debug("kvm_mips_handle_tlbmiss: badvaddr: %#lx\n", 32338c2ecf20Sopenharmony_ci vcpu->arch.host_cp0_badvaddr); 32348c2ecf20Sopenharmony_ci 32358c2ecf20Sopenharmony_ci /* 32368c2ecf20Sopenharmony_ci * KVM would not have got the exception if this entry was valid in the 32378c2ecf20Sopenharmony_ci * shadow host TLB. Check the Guest TLB, if the entry is not there then 32388c2ecf20Sopenharmony_ci * send the guest an exception. The guest exc handler should then inject 32398c2ecf20Sopenharmony_ci * an entry into the guest TLB. 32408c2ecf20Sopenharmony_ci */ 32418c2ecf20Sopenharmony_ci index = kvm_mips_guest_tlb_lookup(vcpu, 32428c2ecf20Sopenharmony_ci (va & VPN2_MASK) | 32438c2ecf20Sopenharmony_ci (kvm_read_c0_guest_entryhi(vcpu->arch.cop0) & 32448c2ecf20Sopenharmony_ci KVM_ENTRYHI_ASID)); 32458c2ecf20Sopenharmony_ci if (index < 0) { 32468c2ecf20Sopenharmony_ci if (exccode == EXCCODE_TLBL) { 32478c2ecf20Sopenharmony_ci er = kvm_mips_emulate_tlbmiss_ld(cause, opc, vcpu); 32488c2ecf20Sopenharmony_ci } else if (exccode == EXCCODE_TLBS) { 32498c2ecf20Sopenharmony_ci er = kvm_mips_emulate_tlbmiss_st(cause, opc, vcpu); 32508c2ecf20Sopenharmony_ci } else { 32518c2ecf20Sopenharmony_ci kvm_err("%s: invalid exc code: %d\n", __func__, 32528c2ecf20Sopenharmony_ci exccode); 32538c2ecf20Sopenharmony_ci er = EMULATE_FAIL; 32548c2ecf20Sopenharmony_ci } 32558c2ecf20Sopenharmony_ci } else { 32568c2ecf20Sopenharmony_ci struct kvm_mips_tlb *tlb = &vcpu->arch.guest_tlb[index]; 32578c2ecf20Sopenharmony_ci 32588c2ecf20Sopenharmony_ci /* 32598c2ecf20Sopenharmony_ci * Check if the entry is valid, if not then setup a TLB invalid 32608c2ecf20Sopenharmony_ci * exception to the guest 32618c2ecf20Sopenharmony_ci */ 32628c2ecf20Sopenharmony_ci if (!TLB_IS_VALID(*tlb, va)) { 32638c2ecf20Sopenharmony_ci if (exccode == EXCCODE_TLBL) { 32648c2ecf20Sopenharmony_ci er = kvm_mips_emulate_tlbinv_ld(cause, opc, 32658c2ecf20Sopenharmony_ci vcpu); 32668c2ecf20Sopenharmony_ci } else if (exccode == EXCCODE_TLBS) { 32678c2ecf20Sopenharmony_ci er = kvm_mips_emulate_tlbinv_st(cause, opc, 32688c2ecf20Sopenharmony_ci vcpu); 32698c2ecf20Sopenharmony_ci } else { 32708c2ecf20Sopenharmony_ci kvm_err("%s: invalid exc code: %d\n", __func__, 32718c2ecf20Sopenharmony_ci exccode); 32728c2ecf20Sopenharmony_ci er = EMULATE_FAIL; 32738c2ecf20Sopenharmony_ci } 32748c2ecf20Sopenharmony_ci } else { 32758c2ecf20Sopenharmony_ci kvm_debug("Injecting hi: %#lx, lo0: %#lx, lo1: %#lx into shadow host TLB\n", 32768c2ecf20Sopenharmony_ci tlb->tlb_hi, tlb->tlb_lo[0], tlb->tlb_lo[1]); 32778c2ecf20Sopenharmony_ci /* 32788c2ecf20Sopenharmony_ci * OK we have a Guest TLB entry, now inject it into the 32798c2ecf20Sopenharmony_ci * shadow host TLB 32808c2ecf20Sopenharmony_ci */ 32818c2ecf20Sopenharmony_ci if (kvm_mips_handle_mapped_seg_tlb_fault(vcpu, tlb, va, 32828c2ecf20Sopenharmony_ci write_fault)) { 32838c2ecf20Sopenharmony_ci kvm_err("%s: handling mapped seg tlb fault for %lx, index: %u, vcpu: %p, ASID: %#lx\n", 32848c2ecf20Sopenharmony_ci __func__, va, index, vcpu, 32858c2ecf20Sopenharmony_ci read_c0_entryhi()); 32868c2ecf20Sopenharmony_ci er = EMULATE_FAIL; 32878c2ecf20Sopenharmony_ci } 32888c2ecf20Sopenharmony_ci } 32898c2ecf20Sopenharmony_ci } 32908c2ecf20Sopenharmony_ci 32918c2ecf20Sopenharmony_ci return er; 32928c2ecf20Sopenharmony_ci} 3293