162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public 362306a36Sopenharmony_ci * License. See the file "COPYING" in the main directory of this archive 462306a36Sopenharmony_ci * for more details. 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * KVM/MIPS: Instruction/Exception emulation 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Copyright (C) 2012 MIPS Technologies, Inc. All rights reserved. 962306a36Sopenharmony_ci * Authors: Sanjay Lal <sanjayl@kymasys.com> 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/errno.h> 1362306a36Sopenharmony_ci#include <linux/err.h> 1462306a36Sopenharmony_ci#include <linux/ktime.h> 1562306a36Sopenharmony_ci#include <linux/kvm_host.h> 1662306a36Sopenharmony_ci#include <linux/vmalloc.h> 1762306a36Sopenharmony_ci#include <linux/fs.h> 1862306a36Sopenharmony_ci#include <linux/memblock.h> 1962306a36Sopenharmony_ci#include <linux/random.h> 2062306a36Sopenharmony_ci#include <asm/page.h> 2162306a36Sopenharmony_ci#include <asm/cacheflush.h> 2262306a36Sopenharmony_ci#include <asm/cacheops.h> 2362306a36Sopenharmony_ci#include <asm/cpu-info.h> 2462306a36Sopenharmony_ci#include <asm/mmu_context.h> 2562306a36Sopenharmony_ci#include <asm/tlbflush.h> 2662306a36Sopenharmony_ci#include <asm/inst.h> 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#undef CONFIG_MIPS_MT 2962306a36Sopenharmony_ci#include <asm/r4kcache.h> 3062306a36Sopenharmony_ci#define CONFIG_MIPS_MT 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci#include "interrupt.h" 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci#include "trace.h" 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci/* 3762306a36Sopenharmony_ci * Compute the return address and do emulate branch simulation, if required. 3862306a36Sopenharmony_ci * This function should be called only in branch delay slot active. 3962306a36Sopenharmony_ci */ 4062306a36Sopenharmony_cistatic int kvm_compute_return_epc(struct kvm_vcpu *vcpu, unsigned long instpc, 4162306a36Sopenharmony_ci unsigned long *out) 4262306a36Sopenharmony_ci{ 4362306a36Sopenharmony_ci unsigned int dspcontrol; 4462306a36Sopenharmony_ci union mips_instruction insn; 4562306a36Sopenharmony_ci struct kvm_vcpu_arch *arch = &vcpu->arch; 4662306a36Sopenharmony_ci long epc = instpc; 4762306a36Sopenharmony_ci long nextpc; 4862306a36Sopenharmony_ci int err; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci if (epc & 3) { 5162306a36Sopenharmony_ci kvm_err("%s: unaligned epc\n", __func__); 5262306a36Sopenharmony_ci return -EINVAL; 5362306a36Sopenharmony_ci } 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci /* Read the instruction */ 5662306a36Sopenharmony_ci err = kvm_get_badinstrp((u32 *)epc, vcpu, &insn.word); 5762306a36Sopenharmony_ci if (err) 5862306a36Sopenharmony_ci return err; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci switch (insn.i_format.opcode) { 6162306a36Sopenharmony_ci /* jr and jalr are in r_format format. */ 6262306a36Sopenharmony_ci case spec_op: 6362306a36Sopenharmony_ci switch (insn.r_format.func) { 6462306a36Sopenharmony_ci case jalr_op: 6562306a36Sopenharmony_ci arch->gprs[insn.r_format.rd] = epc + 8; 6662306a36Sopenharmony_ci fallthrough; 6762306a36Sopenharmony_ci case jr_op: 6862306a36Sopenharmony_ci nextpc = arch->gprs[insn.r_format.rs]; 6962306a36Sopenharmony_ci break; 7062306a36Sopenharmony_ci default: 7162306a36Sopenharmony_ci return -EINVAL; 7262306a36Sopenharmony_ci } 7362306a36Sopenharmony_ci break; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci /* 7662306a36Sopenharmony_ci * This group contains: 7762306a36Sopenharmony_ci * bltz_op, bgez_op, bltzl_op, bgezl_op, 7862306a36Sopenharmony_ci * bltzal_op, bgezal_op, bltzall_op, bgezall_op. 7962306a36Sopenharmony_ci */ 8062306a36Sopenharmony_ci case bcond_op: 8162306a36Sopenharmony_ci switch (insn.i_format.rt) { 8262306a36Sopenharmony_ci case bltz_op: 8362306a36Sopenharmony_ci case bltzl_op: 8462306a36Sopenharmony_ci if ((long)arch->gprs[insn.i_format.rs] < 0) 8562306a36Sopenharmony_ci epc = epc + 4 + (insn.i_format.simmediate << 2); 8662306a36Sopenharmony_ci else 8762306a36Sopenharmony_ci epc += 8; 8862306a36Sopenharmony_ci nextpc = epc; 8962306a36Sopenharmony_ci break; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci case bgez_op: 9262306a36Sopenharmony_ci case bgezl_op: 9362306a36Sopenharmony_ci if ((long)arch->gprs[insn.i_format.rs] >= 0) 9462306a36Sopenharmony_ci epc = epc + 4 + (insn.i_format.simmediate << 2); 9562306a36Sopenharmony_ci else 9662306a36Sopenharmony_ci epc += 8; 9762306a36Sopenharmony_ci nextpc = epc; 9862306a36Sopenharmony_ci break; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci case bltzal_op: 10162306a36Sopenharmony_ci case bltzall_op: 10262306a36Sopenharmony_ci arch->gprs[31] = epc + 8; 10362306a36Sopenharmony_ci if ((long)arch->gprs[insn.i_format.rs] < 0) 10462306a36Sopenharmony_ci epc = epc + 4 + (insn.i_format.simmediate << 2); 10562306a36Sopenharmony_ci else 10662306a36Sopenharmony_ci epc += 8; 10762306a36Sopenharmony_ci nextpc = epc; 10862306a36Sopenharmony_ci break; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci case bgezal_op: 11162306a36Sopenharmony_ci case bgezall_op: 11262306a36Sopenharmony_ci arch->gprs[31] = epc + 8; 11362306a36Sopenharmony_ci if ((long)arch->gprs[insn.i_format.rs] >= 0) 11462306a36Sopenharmony_ci epc = epc + 4 + (insn.i_format.simmediate << 2); 11562306a36Sopenharmony_ci else 11662306a36Sopenharmony_ci epc += 8; 11762306a36Sopenharmony_ci nextpc = epc; 11862306a36Sopenharmony_ci break; 11962306a36Sopenharmony_ci case bposge32_op: 12062306a36Sopenharmony_ci if (!cpu_has_dsp) { 12162306a36Sopenharmony_ci kvm_err("%s: DSP branch but not DSP ASE\n", 12262306a36Sopenharmony_ci __func__); 12362306a36Sopenharmony_ci return -EINVAL; 12462306a36Sopenharmony_ci } 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci dspcontrol = rddsp(0x01); 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci if (dspcontrol >= 32) 12962306a36Sopenharmony_ci epc = epc + 4 + (insn.i_format.simmediate << 2); 13062306a36Sopenharmony_ci else 13162306a36Sopenharmony_ci epc += 8; 13262306a36Sopenharmony_ci nextpc = epc; 13362306a36Sopenharmony_ci break; 13462306a36Sopenharmony_ci default: 13562306a36Sopenharmony_ci return -EINVAL; 13662306a36Sopenharmony_ci } 13762306a36Sopenharmony_ci break; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci /* These are unconditional and in j_format. */ 14062306a36Sopenharmony_ci case jal_op: 14162306a36Sopenharmony_ci arch->gprs[31] = instpc + 8; 14262306a36Sopenharmony_ci fallthrough; 14362306a36Sopenharmony_ci case j_op: 14462306a36Sopenharmony_ci epc += 4; 14562306a36Sopenharmony_ci epc >>= 28; 14662306a36Sopenharmony_ci epc <<= 28; 14762306a36Sopenharmony_ci epc |= (insn.j_format.target << 2); 14862306a36Sopenharmony_ci nextpc = epc; 14962306a36Sopenharmony_ci break; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci /* These are conditional and in i_format. */ 15262306a36Sopenharmony_ci case beq_op: 15362306a36Sopenharmony_ci case beql_op: 15462306a36Sopenharmony_ci if (arch->gprs[insn.i_format.rs] == 15562306a36Sopenharmony_ci arch->gprs[insn.i_format.rt]) 15662306a36Sopenharmony_ci epc = epc + 4 + (insn.i_format.simmediate << 2); 15762306a36Sopenharmony_ci else 15862306a36Sopenharmony_ci epc += 8; 15962306a36Sopenharmony_ci nextpc = epc; 16062306a36Sopenharmony_ci break; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci case bne_op: 16362306a36Sopenharmony_ci case bnel_op: 16462306a36Sopenharmony_ci if (arch->gprs[insn.i_format.rs] != 16562306a36Sopenharmony_ci arch->gprs[insn.i_format.rt]) 16662306a36Sopenharmony_ci epc = epc + 4 + (insn.i_format.simmediate << 2); 16762306a36Sopenharmony_ci else 16862306a36Sopenharmony_ci epc += 8; 16962306a36Sopenharmony_ci nextpc = epc; 17062306a36Sopenharmony_ci break; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci case blez_op: /* POP06 */ 17362306a36Sopenharmony_ci#ifndef CONFIG_CPU_MIPSR6 17462306a36Sopenharmony_ci case blezl_op: /* removed in R6 */ 17562306a36Sopenharmony_ci#endif 17662306a36Sopenharmony_ci if (insn.i_format.rt != 0) 17762306a36Sopenharmony_ci goto compact_branch; 17862306a36Sopenharmony_ci if ((long)arch->gprs[insn.i_format.rs] <= 0) 17962306a36Sopenharmony_ci epc = epc + 4 + (insn.i_format.simmediate << 2); 18062306a36Sopenharmony_ci else 18162306a36Sopenharmony_ci epc += 8; 18262306a36Sopenharmony_ci nextpc = epc; 18362306a36Sopenharmony_ci break; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci case bgtz_op: /* POP07 */ 18662306a36Sopenharmony_ci#ifndef CONFIG_CPU_MIPSR6 18762306a36Sopenharmony_ci case bgtzl_op: /* removed in R6 */ 18862306a36Sopenharmony_ci#endif 18962306a36Sopenharmony_ci if (insn.i_format.rt != 0) 19062306a36Sopenharmony_ci goto compact_branch; 19162306a36Sopenharmony_ci if ((long)arch->gprs[insn.i_format.rs] > 0) 19262306a36Sopenharmony_ci epc = epc + 4 + (insn.i_format.simmediate << 2); 19362306a36Sopenharmony_ci else 19462306a36Sopenharmony_ci epc += 8; 19562306a36Sopenharmony_ci nextpc = epc; 19662306a36Sopenharmony_ci break; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci /* And now the FPA/cp1 branch instructions. */ 19962306a36Sopenharmony_ci case cop1_op: 20062306a36Sopenharmony_ci kvm_err("%s: unsupported cop1_op\n", __func__); 20162306a36Sopenharmony_ci return -EINVAL; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci#ifdef CONFIG_CPU_MIPSR6 20462306a36Sopenharmony_ci /* R6 added the following compact branches with forbidden slots */ 20562306a36Sopenharmony_ci case blezl_op: /* POP26 */ 20662306a36Sopenharmony_ci case bgtzl_op: /* POP27 */ 20762306a36Sopenharmony_ci /* only rt == 0 isn't compact branch */ 20862306a36Sopenharmony_ci if (insn.i_format.rt != 0) 20962306a36Sopenharmony_ci goto compact_branch; 21062306a36Sopenharmony_ci return -EINVAL; 21162306a36Sopenharmony_ci case pop10_op: 21262306a36Sopenharmony_ci case pop30_op: 21362306a36Sopenharmony_ci /* only rs == rt == 0 is reserved, rest are compact branches */ 21462306a36Sopenharmony_ci if (insn.i_format.rs != 0 || insn.i_format.rt != 0) 21562306a36Sopenharmony_ci goto compact_branch; 21662306a36Sopenharmony_ci return -EINVAL; 21762306a36Sopenharmony_ci case pop66_op: 21862306a36Sopenharmony_ci case pop76_op: 21962306a36Sopenharmony_ci /* only rs == 0 isn't compact branch */ 22062306a36Sopenharmony_ci if (insn.i_format.rs != 0) 22162306a36Sopenharmony_ci goto compact_branch; 22262306a36Sopenharmony_ci return -EINVAL; 22362306a36Sopenharmony_cicompact_branch: 22462306a36Sopenharmony_ci /* 22562306a36Sopenharmony_ci * If we've hit an exception on the forbidden slot, then 22662306a36Sopenharmony_ci * the branch must not have been taken. 22762306a36Sopenharmony_ci */ 22862306a36Sopenharmony_ci epc += 8; 22962306a36Sopenharmony_ci nextpc = epc; 23062306a36Sopenharmony_ci break; 23162306a36Sopenharmony_ci#else 23262306a36Sopenharmony_cicompact_branch: 23362306a36Sopenharmony_ci /* Fall through - Compact branches not supported before R6 */ 23462306a36Sopenharmony_ci#endif 23562306a36Sopenharmony_ci default: 23662306a36Sopenharmony_ci return -EINVAL; 23762306a36Sopenharmony_ci } 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci *out = nextpc; 24062306a36Sopenharmony_ci return 0; 24162306a36Sopenharmony_ci} 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_cienum emulation_result update_pc(struct kvm_vcpu *vcpu, u32 cause) 24462306a36Sopenharmony_ci{ 24562306a36Sopenharmony_ci int err; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci if (cause & CAUSEF_BD) { 24862306a36Sopenharmony_ci err = kvm_compute_return_epc(vcpu, vcpu->arch.pc, 24962306a36Sopenharmony_ci &vcpu->arch.pc); 25062306a36Sopenharmony_ci if (err) 25162306a36Sopenharmony_ci return EMULATE_FAIL; 25262306a36Sopenharmony_ci } else { 25362306a36Sopenharmony_ci vcpu->arch.pc += 4; 25462306a36Sopenharmony_ci } 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci kvm_debug("update_pc(): New PC: %#lx\n", vcpu->arch.pc); 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci return EMULATE_DONE; 25962306a36Sopenharmony_ci} 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci/** 26262306a36Sopenharmony_ci * kvm_get_badinstr() - Get bad instruction encoding. 26362306a36Sopenharmony_ci * @opc: Guest pointer to faulting instruction. 26462306a36Sopenharmony_ci * @vcpu: KVM VCPU information. 26562306a36Sopenharmony_ci * 26662306a36Sopenharmony_ci * Gets the instruction encoding of the faulting instruction, using the saved 26762306a36Sopenharmony_ci * BadInstr register value if it exists, otherwise falling back to reading guest 26862306a36Sopenharmony_ci * memory at @opc. 26962306a36Sopenharmony_ci * 27062306a36Sopenharmony_ci * Returns: The instruction encoding of the faulting instruction. 27162306a36Sopenharmony_ci */ 27262306a36Sopenharmony_ciint kvm_get_badinstr(u32 *opc, struct kvm_vcpu *vcpu, u32 *out) 27362306a36Sopenharmony_ci{ 27462306a36Sopenharmony_ci if (cpu_has_badinstr) { 27562306a36Sopenharmony_ci *out = vcpu->arch.host_cp0_badinstr; 27662306a36Sopenharmony_ci return 0; 27762306a36Sopenharmony_ci } else { 27862306a36Sopenharmony_ci WARN_ONCE(1, "CPU doesn't have BadInstr register\n"); 27962306a36Sopenharmony_ci return -EINVAL; 28062306a36Sopenharmony_ci } 28162306a36Sopenharmony_ci} 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci/** 28462306a36Sopenharmony_ci * kvm_get_badinstrp() - Get bad prior instruction encoding. 28562306a36Sopenharmony_ci * @opc: Guest pointer to prior faulting instruction. 28662306a36Sopenharmony_ci * @vcpu: KVM VCPU information. 28762306a36Sopenharmony_ci * 28862306a36Sopenharmony_ci * Gets the instruction encoding of the prior faulting instruction (the branch 28962306a36Sopenharmony_ci * containing the delay slot which faulted), using the saved BadInstrP register 29062306a36Sopenharmony_ci * value if it exists, otherwise falling back to reading guest memory at @opc. 29162306a36Sopenharmony_ci * 29262306a36Sopenharmony_ci * Returns: The instruction encoding of the prior faulting instruction. 29362306a36Sopenharmony_ci */ 29462306a36Sopenharmony_ciint kvm_get_badinstrp(u32 *opc, struct kvm_vcpu *vcpu, u32 *out) 29562306a36Sopenharmony_ci{ 29662306a36Sopenharmony_ci if (cpu_has_badinstrp) { 29762306a36Sopenharmony_ci *out = vcpu->arch.host_cp0_badinstrp; 29862306a36Sopenharmony_ci return 0; 29962306a36Sopenharmony_ci } else { 30062306a36Sopenharmony_ci WARN_ONCE(1, "CPU doesn't have BadInstrp register\n"); 30162306a36Sopenharmony_ci return -EINVAL; 30262306a36Sopenharmony_ci } 30362306a36Sopenharmony_ci} 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci/** 30662306a36Sopenharmony_ci * kvm_mips_count_disabled() - Find whether the CP0_Count timer is disabled. 30762306a36Sopenharmony_ci * @vcpu: Virtual CPU. 30862306a36Sopenharmony_ci * 30962306a36Sopenharmony_ci * Returns: 1 if the CP0_Count timer is disabled by either the guest 31062306a36Sopenharmony_ci * CP0_Cause.DC bit or the count_ctl.DC bit. 31162306a36Sopenharmony_ci * 0 otherwise (in which case CP0_Count timer is running). 31262306a36Sopenharmony_ci */ 31362306a36Sopenharmony_ciint kvm_mips_count_disabled(struct kvm_vcpu *vcpu) 31462306a36Sopenharmony_ci{ 31562306a36Sopenharmony_ci struct mips_coproc *cop0 = &vcpu->arch.cop0; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci return (vcpu->arch.count_ctl & KVM_REG_MIPS_COUNT_CTL_DC) || 31862306a36Sopenharmony_ci (kvm_read_c0_guest_cause(cop0) & CAUSEF_DC); 31962306a36Sopenharmony_ci} 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci/** 32262306a36Sopenharmony_ci * kvm_mips_ktime_to_count() - Scale ktime_t to a 32-bit count. 32362306a36Sopenharmony_ci * 32462306a36Sopenharmony_ci * Caches the dynamic nanosecond bias in vcpu->arch.count_dyn_bias. 32562306a36Sopenharmony_ci * 32662306a36Sopenharmony_ci * Assumes !kvm_mips_count_disabled(@vcpu) (guest CP0_Count timer is running). 32762306a36Sopenharmony_ci */ 32862306a36Sopenharmony_cistatic u32 kvm_mips_ktime_to_count(struct kvm_vcpu *vcpu, ktime_t now) 32962306a36Sopenharmony_ci{ 33062306a36Sopenharmony_ci s64 now_ns, periods; 33162306a36Sopenharmony_ci u64 delta; 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci now_ns = ktime_to_ns(now); 33462306a36Sopenharmony_ci delta = now_ns + vcpu->arch.count_dyn_bias; 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci if (delta >= vcpu->arch.count_period) { 33762306a36Sopenharmony_ci /* If delta is out of safe range the bias needs adjusting */ 33862306a36Sopenharmony_ci periods = div64_s64(now_ns, vcpu->arch.count_period); 33962306a36Sopenharmony_ci vcpu->arch.count_dyn_bias = -periods * vcpu->arch.count_period; 34062306a36Sopenharmony_ci /* Recalculate delta with new bias */ 34162306a36Sopenharmony_ci delta = now_ns + vcpu->arch.count_dyn_bias; 34262306a36Sopenharmony_ci } 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci /* 34562306a36Sopenharmony_ci * We've ensured that: 34662306a36Sopenharmony_ci * delta < count_period 34762306a36Sopenharmony_ci * 34862306a36Sopenharmony_ci * Therefore the intermediate delta*count_hz will never overflow since 34962306a36Sopenharmony_ci * at the boundary condition: 35062306a36Sopenharmony_ci * delta = count_period 35162306a36Sopenharmony_ci * delta = NSEC_PER_SEC * 2^32 / count_hz 35262306a36Sopenharmony_ci * delta * count_hz = NSEC_PER_SEC * 2^32 35362306a36Sopenharmony_ci */ 35462306a36Sopenharmony_ci return div_u64(delta * vcpu->arch.count_hz, NSEC_PER_SEC); 35562306a36Sopenharmony_ci} 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci/** 35862306a36Sopenharmony_ci * kvm_mips_count_time() - Get effective current time. 35962306a36Sopenharmony_ci * @vcpu: Virtual CPU. 36062306a36Sopenharmony_ci * 36162306a36Sopenharmony_ci * Get effective monotonic ktime. This is usually a straightforward ktime_get(), 36262306a36Sopenharmony_ci * except when the master disable bit is set in count_ctl, in which case it is 36362306a36Sopenharmony_ci * count_resume, i.e. the time that the count was disabled. 36462306a36Sopenharmony_ci * 36562306a36Sopenharmony_ci * Returns: Effective monotonic ktime for CP0_Count. 36662306a36Sopenharmony_ci */ 36762306a36Sopenharmony_cistatic inline ktime_t kvm_mips_count_time(struct kvm_vcpu *vcpu) 36862306a36Sopenharmony_ci{ 36962306a36Sopenharmony_ci if (unlikely(vcpu->arch.count_ctl & KVM_REG_MIPS_COUNT_CTL_DC)) 37062306a36Sopenharmony_ci return vcpu->arch.count_resume; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci return ktime_get(); 37362306a36Sopenharmony_ci} 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci/** 37662306a36Sopenharmony_ci * kvm_mips_read_count_running() - Read the current count value as if running. 37762306a36Sopenharmony_ci * @vcpu: Virtual CPU. 37862306a36Sopenharmony_ci * @now: Kernel time to read CP0_Count at. 37962306a36Sopenharmony_ci * 38062306a36Sopenharmony_ci * Returns the current guest CP0_Count register at time @now and handles if the 38162306a36Sopenharmony_ci * timer interrupt is pending and hasn't been handled yet. 38262306a36Sopenharmony_ci * 38362306a36Sopenharmony_ci * Returns: The current value of the guest CP0_Count register. 38462306a36Sopenharmony_ci */ 38562306a36Sopenharmony_cistatic u32 kvm_mips_read_count_running(struct kvm_vcpu *vcpu, ktime_t now) 38662306a36Sopenharmony_ci{ 38762306a36Sopenharmony_ci struct mips_coproc *cop0 = &vcpu->arch.cop0; 38862306a36Sopenharmony_ci ktime_t expires, threshold; 38962306a36Sopenharmony_ci u32 count, compare; 39062306a36Sopenharmony_ci int running; 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci /* Calculate the biased and scaled guest CP0_Count */ 39362306a36Sopenharmony_ci count = vcpu->arch.count_bias + kvm_mips_ktime_to_count(vcpu, now); 39462306a36Sopenharmony_ci compare = kvm_read_c0_guest_compare(cop0); 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci /* 39762306a36Sopenharmony_ci * Find whether CP0_Count has reached the closest timer interrupt. If 39862306a36Sopenharmony_ci * not, we shouldn't inject it. 39962306a36Sopenharmony_ci */ 40062306a36Sopenharmony_ci if ((s32)(count - compare) < 0) 40162306a36Sopenharmony_ci return count; 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci /* 40462306a36Sopenharmony_ci * The CP0_Count we're going to return has already reached the closest 40562306a36Sopenharmony_ci * timer interrupt. Quickly check if it really is a new interrupt by 40662306a36Sopenharmony_ci * looking at whether the interval until the hrtimer expiry time is 40762306a36Sopenharmony_ci * less than 1/4 of the timer period. 40862306a36Sopenharmony_ci */ 40962306a36Sopenharmony_ci expires = hrtimer_get_expires(&vcpu->arch.comparecount_timer); 41062306a36Sopenharmony_ci threshold = ktime_add_ns(now, vcpu->arch.count_period / 4); 41162306a36Sopenharmony_ci if (ktime_before(expires, threshold)) { 41262306a36Sopenharmony_ci /* 41362306a36Sopenharmony_ci * Cancel it while we handle it so there's no chance of 41462306a36Sopenharmony_ci * interference with the timeout handler. 41562306a36Sopenharmony_ci */ 41662306a36Sopenharmony_ci running = hrtimer_cancel(&vcpu->arch.comparecount_timer); 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci /* Nothing should be waiting on the timeout */ 41962306a36Sopenharmony_ci kvm_mips_callbacks->queue_timer_int(vcpu); 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci /* 42262306a36Sopenharmony_ci * Restart the timer if it was running based on the expiry time 42362306a36Sopenharmony_ci * we read, so that we don't push it back 2 periods. 42462306a36Sopenharmony_ci */ 42562306a36Sopenharmony_ci if (running) { 42662306a36Sopenharmony_ci expires = ktime_add_ns(expires, 42762306a36Sopenharmony_ci vcpu->arch.count_period); 42862306a36Sopenharmony_ci hrtimer_start(&vcpu->arch.comparecount_timer, expires, 42962306a36Sopenharmony_ci HRTIMER_MODE_ABS); 43062306a36Sopenharmony_ci } 43162306a36Sopenharmony_ci } 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci return count; 43462306a36Sopenharmony_ci} 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci/** 43762306a36Sopenharmony_ci * kvm_mips_read_count() - Read the current count value. 43862306a36Sopenharmony_ci * @vcpu: Virtual CPU. 43962306a36Sopenharmony_ci * 44062306a36Sopenharmony_ci * Read the current guest CP0_Count value, taking into account whether the timer 44162306a36Sopenharmony_ci * is stopped. 44262306a36Sopenharmony_ci * 44362306a36Sopenharmony_ci * Returns: The current guest CP0_Count value. 44462306a36Sopenharmony_ci */ 44562306a36Sopenharmony_ciu32 kvm_mips_read_count(struct kvm_vcpu *vcpu) 44662306a36Sopenharmony_ci{ 44762306a36Sopenharmony_ci struct mips_coproc *cop0 = &vcpu->arch.cop0; 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci /* If count disabled just read static copy of count */ 45062306a36Sopenharmony_ci if (kvm_mips_count_disabled(vcpu)) 45162306a36Sopenharmony_ci return kvm_read_c0_guest_count(cop0); 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci return kvm_mips_read_count_running(vcpu, ktime_get()); 45462306a36Sopenharmony_ci} 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci/** 45762306a36Sopenharmony_ci * kvm_mips_freeze_hrtimer() - Safely stop the hrtimer. 45862306a36Sopenharmony_ci * @vcpu: Virtual CPU. 45962306a36Sopenharmony_ci * @count: Output pointer for CP0_Count value at point of freeze. 46062306a36Sopenharmony_ci * 46162306a36Sopenharmony_ci * Freeze the hrtimer safely and return both the ktime and the CP0_Count value 46262306a36Sopenharmony_ci * at the point it was frozen. It is guaranteed that any pending interrupts at 46362306a36Sopenharmony_ci * the point it was frozen are handled, and none after that point. 46462306a36Sopenharmony_ci * 46562306a36Sopenharmony_ci * This is useful where the time/CP0_Count is needed in the calculation of the 46662306a36Sopenharmony_ci * new parameters. 46762306a36Sopenharmony_ci * 46862306a36Sopenharmony_ci * Assumes !kvm_mips_count_disabled(@vcpu) (guest CP0_Count timer is running). 46962306a36Sopenharmony_ci * 47062306a36Sopenharmony_ci * Returns: The ktime at the point of freeze. 47162306a36Sopenharmony_ci */ 47262306a36Sopenharmony_ciktime_t kvm_mips_freeze_hrtimer(struct kvm_vcpu *vcpu, u32 *count) 47362306a36Sopenharmony_ci{ 47462306a36Sopenharmony_ci ktime_t now; 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci /* stop hrtimer before finding time */ 47762306a36Sopenharmony_ci hrtimer_cancel(&vcpu->arch.comparecount_timer); 47862306a36Sopenharmony_ci now = ktime_get(); 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci /* find count at this point and handle pending hrtimer */ 48162306a36Sopenharmony_ci *count = kvm_mips_read_count_running(vcpu, now); 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci return now; 48462306a36Sopenharmony_ci} 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci/** 48762306a36Sopenharmony_ci * kvm_mips_resume_hrtimer() - Resume hrtimer, updating expiry. 48862306a36Sopenharmony_ci * @vcpu: Virtual CPU. 48962306a36Sopenharmony_ci * @now: ktime at point of resume. 49062306a36Sopenharmony_ci * @count: CP0_Count at point of resume. 49162306a36Sopenharmony_ci * 49262306a36Sopenharmony_ci * Resumes the timer and updates the timer expiry based on @now and @count. 49362306a36Sopenharmony_ci * This can be used in conjunction with kvm_mips_freeze_timer() when timer 49462306a36Sopenharmony_ci * parameters need to be changed. 49562306a36Sopenharmony_ci * 49662306a36Sopenharmony_ci * It is guaranteed that a timer interrupt immediately after resume will be 49762306a36Sopenharmony_ci * handled, but not if CP_Compare is exactly at @count. That case is already 49862306a36Sopenharmony_ci * handled by kvm_mips_freeze_timer(). 49962306a36Sopenharmony_ci * 50062306a36Sopenharmony_ci * Assumes !kvm_mips_count_disabled(@vcpu) (guest CP0_Count timer is running). 50162306a36Sopenharmony_ci */ 50262306a36Sopenharmony_cistatic void kvm_mips_resume_hrtimer(struct kvm_vcpu *vcpu, 50362306a36Sopenharmony_ci ktime_t now, u32 count) 50462306a36Sopenharmony_ci{ 50562306a36Sopenharmony_ci struct mips_coproc *cop0 = &vcpu->arch.cop0; 50662306a36Sopenharmony_ci u32 compare; 50762306a36Sopenharmony_ci u64 delta; 50862306a36Sopenharmony_ci ktime_t expire; 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci /* Calculate timeout (wrap 0 to 2^32) */ 51162306a36Sopenharmony_ci compare = kvm_read_c0_guest_compare(cop0); 51262306a36Sopenharmony_ci delta = (u64)(u32)(compare - count - 1) + 1; 51362306a36Sopenharmony_ci delta = div_u64(delta * NSEC_PER_SEC, vcpu->arch.count_hz); 51462306a36Sopenharmony_ci expire = ktime_add_ns(now, delta); 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci /* Update hrtimer to use new timeout */ 51762306a36Sopenharmony_ci hrtimer_cancel(&vcpu->arch.comparecount_timer); 51862306a36Sopenharmony_ci hrtimer_start(&vcpu->arch.comparecount_timer, expire, HRTIMER_MODE_ABS); 51962306a36Sopenharmony_ci} 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci/** 52262306a36Sopenharmony_ci * kvm_mips_restore_hrtimer() - Restore hrtimer after a gap, updating expiry. 52362306a36Sopenharmony_ci * @vcpu: Virtual CPU. 52462306a36Sopenharmony_ci * @before: Time before Count was saved, lower bound of drift calculation. 52562306a36Sopenharmony_ci * @count: CP0_Count at point of restore. 52662306a36Sopenharmony_ci * @min_drift: Minimum amount of drift permitted before correction. 52762306a36Sopenharmony_ci * Must be <= 0. 52862306a36Sopenharmony_ci * 52962306a36Sopenharmony_ci * Restores the timer from a particular @count, accounting for drift. This can 53062306a36Sopenharmony_ci * be used in conjunction with kvm_mips_freeze_timer() when a hardware timer is 53162306a36Sopenharmony_ci * to be used for a period of time, but the exact ktime corresponding to the 53262306a36Sopenharmony_ci * final Count that must be restored is not known. 53362306a36Sopenharmony_ci * 53462306a36Sopenharmony_ci * It is gauranteed that a timer interrupt immediately after restore will be 53562306a36Sopenharmony_ci * handled, but not if CP0_Compare is exactly at @count. That case should 53662306a36Sopenharmony_ci * already be handled when the hardware timer state is saved. 53762306a36Sopenharmony_ci * 53862306a36Sopenharmony_ci * Assumes !kvm_mips_count_disabled(@vcpu) (guest CP0_Count timer is not 53962306a36Sopenharmony_ci * stopped). 54062306a36Sopenharmony_ci * 54162306a36Sopenharmony_ci * Returns: Amount of correction to count_bias due to drift. 54262306a36Sopenharmony_ci */ 54362306a36Sopenharmony_ciint kvm_mips_restore_hrtimer(struct kvm_vcpu *vcpu, ktime_t before, 54462306a36Sopenharmony_ci u32 count, int min_drift) 54562306a36Sopenharmony_ci{ 54662306a36Sopenharmony_ci ktime_t now, count_time; 54762306a36Sopenharmony_ci u32 now_count, before_count; 54862306a36Sopenharmony_ci u64 delta; 54962306a36Sopenharmony_ci int drift, ret = 0; 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci /* Calculate expected count at before */ 55262306a36Sopenharmony_ci before_count = vcpu->arch.count_bias + 55362306a36Sopenharmony_ci kvm_mips_ktime_to_count(vcpu, before); 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci /* 55662306a36Sopenharmony_ci * Detect significantly negative drift, where count is lower than 55762306a36Sopenharmony_ci * expected. Some negative drift is expected when hardware counter is 55862306a36Sopenharmony_ci * set after kvm_mips_freeze_timer(), and it is harmless to allow the 55962306a36Sopenharmony_ci * time to jump forwards a little, within reason. If the drift is too 56062306a36Sopenharmony_ci * significant, adjust the bias to avoid a big Guest.CP0_Count jump. 56162306a36Sopenharmony_ci */ 56262306a36Sopenharmony_ci drift = count - before_count; 56362306a36Sopenharmony_ci if (drift < min_drift) { 56462306a36Sopenharmony_ci count_time = before; 56562306a36Sopenharmony_ci vcpu->arch.count_bias += drift; 56662306a36Sopenharmony_ci ret = drift; 56762306a36Sopenharmony_ci goto resume; 56862306a36Sopenharmony_ci } 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci /* Calculate expected count right now */ 57162306a36Sopenharmony_ci now = ktime_get(); 57262306a36Sopenharmony_ci now_count = vcpu->arch.count_bias + kvm_mips_ktime_to_count(vcpu, now); 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci /* 57562306a36Sopenharmony_ci * Detect positive drift, where count is higher than expected, and 57662306a36Sopenharmony_ci * adjust the bias to avoid guest time going backwards. 57762306a36Sopenharmony_ci */ 57862306a36Sopenharmony_ci drift = count - now_count; 57962306a36Sopenharmony_ci if (drift > 0) { 58062306a36Sopenharmony_ci count_time = now; 58162306a36Sopenharmony_ci vcpu->arch.count_bias += drift; 58262306a36Sopenharmony_ci ret = drift; 58362306a36Sopenharmony_ci goto resume; 58462306a36Sopenharmony_ci } 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci /* Subtract nanosecond delta to find ktime when count was read */ 58762306a36Sopenharmony_ci delta = (u64)(u32)(now_count - count); 58862306a36Sopenharmony_ci delta = div_u64(delta * NSEC_PER_SEC, vcpu->arch.count_hz); 58962306a36Sopenharmony_ci count_time = ktime_sub_ns(now, delta); 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ciresume: 59262306a36Sopenharmony_ci /* Resume using the calculated ktime */ 59362306a36Sopenharmony_ci kvm_mips_resume_hrtimer(vcpu, count_time, count); 59462306a36Sopenharmony_ci return ret; 59562306a36Sopenharmony_ci} 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci/** 59862306a36Sopenharmony_ci * kvm_mips_write_count() - Modify the count and update timer. 59962306a36Sopenharmony_ci * @vcpu: Virtual CPU. 60062306a36Sopenharmony_ci * @count: Guest CP0_Count value to set. 60162306a36Sopenharmony_ci * 60262306a36Sopenharmony_ci * Sets the CP0_Count value and updates the timer accordingly. 60362306a36Sopenharmony_ci */ 60462306a36Sopenharmony_civoid kvm_mips_write_count(struct kvm_vcpu *vcpu, u32 count) 60562306a36Sopenharmony_ci{ 60662306a36Sopenharmony_ci struct mips_coproc *cop0 = &vcpu->arch.cop0; 60762306a36Sopenharmony_ci ktime_t now; 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci /* Calculate bias */ 61062306a36Sopenharmony_ci now = kvm_mips_count_time(vcpu); 61162306a36Sopenharmony_ci vcpu->arch.count_bias = count - kvm_mips_ktime_to_count(vcpu, now); 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci if (kvm_mips_count_disabled(vcpu)) 61462306a36Sopenharmony_ci /* The timer's disabled, adjust the static count */ 61562306a36Sopenharmony_ci kvm_write_c0_guest_count(cop0, count); 61662306a36Sopenharmony_ci else 61762306a36Sopenharmony_ci /* Update timeout */ 61862306a36Sopenharmony_ci kvm_mips_resume_hrtimer(vcpu, now, count); 61962306a36Sopenharmony_ci} 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci/** 62262306a36Sopenharmony_ci * kvm_mips_init_count() - Initialise timer. 62362306a36Sopenharmony_ci * @vcpu: Virtual CPU. 62462306a36Sopenharmony_ci * @count_hz: Frequency of timer. 62562306a36Sopenharmony_ci * 62662306a36Sopenharmony_ci * Initialise the timer to the specified frequency, zero it, and set it going if 62762306a36Sopenharmony_ci * it's enabled. 62862306a36Sopenharmony_ci */ 62962306a36Sopenharmony_civoid kvm_mips_init_count(struct kvm_vcpu *vcpu, unsigned long count_hz) 63062306a36Sopenharmony_ci{ 63162306a36Sopenharmony_ci vcpu->arch.count_hz = count_hz; 63262306a36Sopenharmony_ci vcpu->arch.count_period = div_u64((u64)NSEC_PER_SEC << 32, count_hz); 63362306a36Sopenharmony_ci vcpu->arch.count_dyn_bias = 0; 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci /* Starting at 0 */ 63662306a36Sopenharmony_ci kvm_mips_write_count(vcpu, 0); 63762306a36Sopenharmony_ci} 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci/** 64062306a36Sopenharmony_ci * kvm_mips_set_count_hz() - Update the frequency of the timer. 64162306a36Sopenharmony_ci * @vcpu: Virtual CPU. 64262306a36Sopenharmony_ci * @count_hz: Frequency of CP0_Count timer in Hz. 64362306a36Sopenharmony_ci * 64462306a36Sopenharmony_ci * Change the frequency of the CP0_Count timer. This is done atomically so that 64562306a36Sopenharmony_ci * CP0_Count is continuous and no timer interrupt is lost. 64662306a36Sopenharmony_ci * 64762306a36Sopenharmony_ci * Returns: -EINVAL if @count_hz is out of range. 64862306a36Sopenharmony_ci * 0 on success. 64962306a36Sopenharmony_ci */ 65062306a36Sopenharmony_ciint kvm_mips_set_count_hz(struct kvm_vcpu *vcpu, s64 count_hz) 65162306a36Sopenharmony_ci{ 65262306a36Sopenharmony_ci struct mips_coproc *cop0 = &vcpu->arch.cop0; 65362306a36Sopenharmony_ci int dc; 65462306a36Sopenharmony_ci ktime_t now; 65562306a36Sopenharmony_ci u32 count; 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci /* ensure the frequency is in a sensible range... */ 65862306a36Sopenharmony_ci if (count_hz <= 0 || count_hz > NSEC_PER_SEC) 65962306a36Sopenharmony_ci return -EINVAL; 66062306a36Sopenharmony_ci /* ... and has actually changed */ 66162306a36Sopenharmony_ci if (vcpu->arch.count_hz == count_hz) 66262306a36Sopenharmony_ci return 0; 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci /* Safely freeze timer so we can keep it continuous */ 66562306a36Sopenharmony_ci dc = kvm_mips_count_disabled(vcpu); 66662306a36Sopenharmony_ci if (dc) { 66762306a36Sopenharmony_ci now = kvm_mips_count_time(vcpu); 66862306a36Sopenharmony_ci count = kvm_read_c0_guest_count(cop0); 66962306a36Sopenharmony_ci } else { 67062306a36Sopenharmony_ci now = kvm_mips_freeze_hrtimer(vcpu, &count); 67162306a36Sopenharmony_ci } 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci /* Update the frequency */ 67462306a36Sopenharmony_ci vcpu->arch.count_hz = count_hz; 67562306a36Sopenharmony_ci vcpu->arch.count_period = div_u64((u64)NSEC_PER_SEC << 32, count_hz); 67662306a36Sopenharmony_ci vcpu->arch.count_dyn_bias = 0; 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci /* Calculate adjusted bias so dynamic count is unchanged */ 67962306a36Sopenharmony_ci vcpu->arch.count_bias = count - kvm_mips_ktime_to_count(vcpu, now); 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci /* Update and resume hrtimer */ 68262306a36Sopenharmony_ci if (!dc) 68362306a36Sopenharmony_ci kvm_mips_resume_hrtimer(vcpu, now, count); 68462306a36Sopenharmony_ci return 0; 68562306a36Sopenharmony_ci} 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci/** 68862306a36Sopenharmony_ci * kvm_mips_write_compare() - Modify compare and update timer. 68962306a36Sopenharmony_ci * @vcpu: Virtual CPU. 69062306a36Sopenharmony_ci * @compare: New CP0_Compare value. 69162306a36Sopenharmony_ci * @ack: Whether to acknowledge timer interrupt. 69262306a36Sopenharmony_ci * 69362306a36Sopenharmony_ci * Update CP0_Compare to a new value and update the timeout. 69462306a36Sopenharmony_ci * If @ack, atomically acknowledge any pending timer interrupt, otherwise ensure 69562306a36Sopenharmony_ci * any pending timer interrupt is preserved. 69662306a36Sopenharmony_ci */ 69762306a36Sopenharmony_civoid kvm_mips_write_compare(struct kvm_vcpu *vcpu, u32 compare, bool ack) 69862306a36Sopenharmony_ci{ 69962306a36Sopenharmony_ci struct mips_coproc *cop0 = &vcpu->arch.cop0; 70062306a36Sopenharmony_ci int dc; 70162306a36Sopenharmony_ci u32 old_compare = kvm_read_c0_guest_compare(cop0); 70262306a36Sopenharmony_ci s32 delta = compare - old_compare; 70362306a36Sopenharmony_ci u32 cause; 70462306a36Sopenharmony_ci ktime_t now = ktime_set(0, 0); /* silence bogus GCC warning */ 70562306a36Sopenharmony_ci u32 count; 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci /* if unchanged, must just be an ack */ 70862306a36Sopenharmony_ci if (old_compare == compare) { 70962306a36Sopenharmony_ci if (!ack) 71062306a36Sopenharmony_ci return; 71162306a36Sopenharmony_ci kvm_mips_callbacks->dequeue_timer_int(vcpu); 71262306a36Sopenharmony_ci kvm_write_c0_guest_compare(cop0, compare); 71362306a36Sopenharmony_ci return; 71462306a36Sopenharmony_ci } 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci /* 71762306a36Sopenharmony_ci * If guest CP0_Compare moves forward, CP0_GTOffset should be adjusted 71862306a36Sopenharmony_ci * too to prevent guest CP0_Count hitting guest CP0_Compare. 71962306a36Sopenharmony_ci * 72062306a36Sopenharmony_ci * The new GTOffset corresponds to the new value of CP0_Compare, and is 72162306a36Sopenharmony_ci * set prior to it being written into the guest context. We disable 72262306a36Sopenharmony_ci * preemption until the new value is written to prevent restore of a 72362306a36Sopenharmony_ci * GTOffset corresponding to the old CP0_Compare value. 72462306a36Sopenharmony_ci */ 72562306a36Sopenharmony_ci if (delta > 0) { 72662306a36Sopenharmony_ci preempt_disable(); 72762306a36Sopenharmony_ci write_c0_gtoffset(compare - read_c0_count()); 72862306a36Sopenharmony_ci back_to_back_c0_hazard(); 72962306a36Sopenharmony_ci } 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci /* freeze_hrtimer() takes care of timer interrupts <= count */ 73262306a36Sopenharmony_ci dc = kvm_mips_count_disabled(vcpu); 73362306a36Sopenharmony_ci if (!dc) 73462306a36Sopenharmony_ci now = kvm_mips_freeze_hrtimer(vcpu, &count); 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci if (ack) 73762306a36Sopenharmony_ci kvm_mips_callbacks->dequeue_timer_int(vcpu); 73862306a36Sopenharmony_ci else 73962306a36Sopenharmony_ci /* 74062306a36Sopenharmony_ci * With VZ, writing CP0_Compare acks (clears) CP0_Cause.TI, so 74162306a36Sopenharmony_ci * preserve guest CP0_Cause.TI if we don't want to ack it. 74262306a36Sopenharmony_ci */ 74362306a36Sopenharmony_ci cause = kvm_read_c0_guest_cause(cop0); 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci kvm_write_c0_guest_compare(cop0, compare); 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci if (delta > 0) 74862306a36Sopenharmony_ci preempt_enable(); 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci back_to_back_c0_hazard(); 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci if (!ack && cause & CAUSEF_TI) 75362306a36Sopenharmony_ci kvm_write_c0_guest_cause(cop0, cause); 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci /* resume_hrtimer() takes care of timer interrupts > count */ 75662306a36Sopenharmony_ci if (!dc) 75762306a36Sopenharmony_ci kvm_mips_resume_hrtimer(vcpu, now, count); 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci /* 76062306a36Sopenharmony_ci * If guest CP0_Compare is moving backward, we delay CP0_GTOffset change 76162306a36Sopenharmony_ci * until after the new CP0_Compare is written, otherwise new guest 76262306a36Sopenharmony_ci * CP0_Count could hit new guest CP0_Compare. 76362306a36Sopenharmony_ci */ 76462306a36Sopenharmony_ci if (delta <= 0) 76562306a36Sopenharmony_ci write_c0_gtoffset(compare - read_c0_count()); 76662306a36Sopenharmony_ci} 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci/** 76962306a36Sopenharmony_ci * kvm_mips_count_disable() - Disable count. 77062306a36Sopenharmony_ci * @vcpu: Virtual CPU. 77162306a36Sopenharmony_ci * 77262306a36Sopenharmony_ci * Disable the CP0_Count timer. A timer interrupt on or before the final stop 77362306a36Sopenharmony_ci * time will be handled but not after. 77462306a36Sopenharmony_ci * 77562306a36Sopenharmony_ci * Assumes CP0_Count was previously enabled but now Guest.CP0_Cause.DC or 77662306a36Sopenharmony_ci * count_ctl.DC has been set (count disabled). 77762306a36Sopenharmony_ci * 77862306a36Sopenharmony_ci * Returns: The time that the timer was stopped. 77962306a36Sopenharmony_ci */ 78062306a36Sopenharmony_cistatic ktime_t kvm_mips_count_disable(struct kvm_vcpu *vcpu) 78162306a36Sopenharmony_ci{ 78262306a36Sopenharmony_ci struct mips_coproc *cop0 = &vcpu->arch.cop0; 78362306a36Sopenharmony_ci u32 count; 78462306a36Sopenharmony_ci ktime_t now; 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci /* Stop hrtimer */ 78762306a36Sopenharmony_ci hrtimer_cancel(&vcpu->arch.comparecount_timer); 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci /* Set the static count from the dynamic count, handling pending TI */ 79062306a36Sopenharmony_ci now = ktime_get(); 79162306a36Sopenharmony_ci count = kvm_mips_read_count_running(vcpu, now); 79262306a36Sopenharmony_ci kvm_write_c0_guest_count(cop0, count); 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci return now; 79562306a36Sopenharmony_ci} 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci/** 79862306a36Sopenharmony_ci * kvm_mips_count_disable_cause() - Disable count using CP0_Cause.DC. 79962306a36Sopenharmony_ci * @vcpu: Virtual CPU. 80062306a36Sopenharmony_ci * 80162306a36Sopenharmony_ci * Disable the CP0_Count timer and set CP0_Cause.DC. A timer interrupt on or 80262306a36Sopenharmony_ci * before the final stop time will be handled if the timer isn't disabled by 80362306a36Sopenharmony_ci * count_ctl.DC, but not after. 80462306a36Sopenharmony_ci * 80562306a36Sopenharmony_ci * Assumes CP0_Cause.DC is clear (count enabled). 80662306a36Sopenharmony_ci */ 80762306a36Sopenharmony_civoid kvm_mips_count_disable_cause(struct kvm_vcpu *vcpu) 80862306a36Sopenharmony_ci{ 80962306a36Sopenharmony_ci struct mips_coproc *cop0 = &vcpu->arch.cop0; 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci kvm_set_c0_guest_cause(cop0, CAUSEF_DC); 81262306a36Sopenharmony_ci if (!(vcpu->arch.count_ctl & KVM_REG_MIPS_COUNT_CTL_DC)) 81362306a36Sopenharmony_ci kvm_mips_count_disable(vcpu); 81462306a36Sopenharmony_ci} 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci/** 81762306a36Sopenharmony_ci * kvm_mips_count_enable_cause() - Enable count using CP0_Cause.DC. 81862306a36Sopenharmony_ci * @vcpu: Virtual CPU. 81962306a36Sopenharmony_ci * 82062306a36Sopenharmony_ci * Enable the CP0_Count timer and clear CP0_Cause.DC. A timer interrupt after 82162306a36Sopenharmony_ci * the start time will be handled if the timer isn't disabled by count_ctl.DC, 82262306a36Sopenharmony_ci * potentially before even returning, so the caller should be careful with 82362306a36Sopenharmony_ci * ordering of CP0_Cause modifications so as not to lose it. 82462306a36Sopenharmony_ci * 82562306a36Sopenharmony_ci * Assumes CP0_Cause.DC is set (count disabled). 82662306a36Sopenharmony_ci */ 82762306a36Sopenharmony_civoid kvm_mips_count_enable_cause(struct kvm_vcpu *vcpu) 82862306a36Sopenharmony_ci{ 82962306a36Sopenharmony_ci struct mips_coproc *cop0 = &vcpu->arch.cop0; 83062306a36Sopenharmony_ci u32 count; 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_ci kvm_clear_c0_guest_cause(cop0, CAUSEF_DC); 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci /* 83562306a36Sopenharmony_ci * Set the dynamic count to match the static count. 83662306a36Sopenharmony_ci * This starts the hrtimer if count_ctl.DC allows it. 83762306a36Sopenharmony_ci * Otherwise it conveniently updates the biases. 83862306a36Sopenharmony_ci */ 83962306a36Sopenharmony_ci count = kvm_read_c0_guest_count(cop0); 84062306a36Sopenharmony_ci kvm_mips_write_count(vcpu, count); 84162306a36Sopenharmony_ci} 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_ci/** 84462306a36Sopenharmony_ci * kvm_mips_set_count_ctl() - Update the count control KVM register. 84562306a36Sopenharmony_ci * @vcpu: Virtual CPU. 84662306a36Sopenharmony_ci * @count_ctl: Count control register new value. 84762306a36Sopenharmony_ci * 84862306a36Sopenharmony_ci * Set the count control KVM register. The timer is updated accordingly. 84962306a36Sopenharmony_ci * 85062306a36Sopenharmony_ci * Returns: -EINVAL if reserved bits are set. 85162306a36Sopenharmony_ci * 0 on success. 85262306a36Sopenharmony_ci */ 85362306a36Sopenharmony_ciint kvm_mips_set_count_ctl(struct kvm_vcpu *vcpu, s64 count_ctl) 85462306a36Sopenharmony_ci{ 85562306a36Sopenharmony_ci struct mips_coproc *cop0 = &vcpu->arch.cop0; 85662306a36Sopenharmony_ci s64 changed = count_ctl ^ vcpu->arch.count_ctl; 85762306a36Sopenharmony_ci s64 delta; 85862306a36Sopenharmony_ci ktime_t expire, now; 85962306a36Sopenharmony_ci u32 count, compare; 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_ci /* Only allow defined bits to be changed */ 86262306a36Sopenharmony_ci if (changed & ~(s64)(KVM_REG_MIPS_COUNT_CTL_DC)) 86362306a36Sopenharmony_ci return -EINVAL; 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci /* Apply new value */ 86662306a36Sopenharmony_ci vcpu->arch.count_ctl = count_ctl; 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_ci /* Master CP0_Count disable */ 86962306a36Sopenharmony_ci if (changed & KVM_REG_MIPS_COUNT_CTL_DC) { 87062306a36Sopenharmony_ci /* Is CP0_Cause.DC already disabling CP0_Count? */ 87162306a36Sopenharmony_ci if (kvm_read_c0_guest_cause(cop0) & CAUSEF_DC) { 87262306a36Sopenharmony_ci if (count_ctl & KVM_REG_MIPS_COUNT_CTL_DC) 87362306a36Sopenharmony_ci /* Just record the current time */ 87462306a36Sopenharmony_ci vcpu->arch.count_resume = ktime_get(); 87562306a36Sopenharmony_ci } else if (count_ctl & KVM_REG_MIPS_COUNT_CTL_DC) { 87662306a36Sopenharmony_ci /* disable timer and record current time */ 87762306a36Sopenharmony_ci vcpu->arch.count_resume = kvm_mips_count_disable(vcpu); 87862306a36Sopenharmony_ci } else { 87962306a36Sopenharmony_ci /* 88062306a36Sopenharmony_ci * Calculate timeout relative to static count at resume 88162306a36Sopenharmony_ci * time (wrap 0 to 2^32). 88262306a36Sopenharmony_ci */ 88362306a36Sopenharmony_ci count = kvm_read_c0_guest_count(cop0); 88462306a36Sopenharmony_ci compare = kvm_read_c0_guest_compare(cop0); 88562306a36Sopenharmony_ci delta = (u64)(u32)(compare - count - 1) + 1; 88662306a36Sopenharmony_ci delta = div_u64(delta * NSEC_PER_SEC, 88762306a36Sopenharmony_ci vcpu->arch.count_hz); 88862306a36Sopenharmony_ci expire = ktime_add_ns(vcpu->arch.count_resume, delta); 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_ci /* Handle pending interrupt */ 89162306a36Sopenharmony_ci now = ktime_get(); 89262306a36Sopenharmony_ci if (ktime_compare(now, expire) >= 0) 89362306a36Sopenharmony_ci /* Nothing should be waiting on the timeout */ 89462306a36Sopenharmony_ci kvm_mips_callbacks->queue_timer_int(vcpu); 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_ci /* Resume hrtimer without changing bias */ 89762306a36Sopenharmony_ci count = kvm_mips_read_count_running(vcpu, now); 89862306a36Sopenharmony_ci kvm_mips_resume_hrtimer(vcpu, now, count); 89962306a36Sopenharmony_ci } 90062306a36Sopenharmony_ci } 90162306a36Sopenharmony_ci 90262306a36Sopenharmony_ci return 0; 90362306a36Sopenharmony_ci} 90462306a36Sopenharmony_ci 90562306a36Sopenharmony_ci/** 90662306a36Sopenharmony_ci * kvm_mips_set_count_resume() - Update the count resume KVM register. 90762306a36Sopenharmony_ci * @vcpu: Virtual CPU. 90862306a36Sopenharmony_ci * @count_resume: Count resume register new value. 90962306a36Sopenharmony_ci * 91062306a36Sopenharmony_ci * Set the count resume KVM register. 91162306a36Sopenharmony_ci * 91262306a36Sopenharmony_ci * Returns: -EINVAL if out of valid range (0..now). 91362306a36Sopenharmony_ci * 0 on success. 91462306a36Sopenharmony_ci */ 91562306a36Sopenharmony_ciint kvm_mips_set_count_resume(struct kvm_vcpu *vcpu, s64 count_resume) 91662306a36Sopenharmony_ci{ 91762306a36Sopenharmony_ci /* 91862306a36Sopenharmony_ci * It doesn't make sense for the resume time to be in the future, as it 91962306a36Sopenharmony_ci * would be possible for the next interrupt to be more than a full 92062306a36Sopenharmony_ci * period in the future. 92162306a36Sopenharmony_ci */ 92262306a36Sopenharmony_ci if (count_resume < 0 || count_resume > ktime_to_ns(ktime_get())) 92362306a36Sopenharmony_ci return -EINVAL; 92462306a36Sopenharmony_ci 92562306a36Sopenharmony_ci vcpu->arch.count_resume = ns_to_ktime(count_resume); 92662306a36Sopenharmony_ci return 0; 92762306a36Sopenharmony_ci} 92862306a36Sopenharmony_ci 92962306a36Sopenharmony_ci/** 93062306a36Sopenharmony_ci * kvm_mips_count_timeout() - Push timer forward on timeout. 93162306a36Sopenharmony_ci * @vcpu: Virtual CPU. 93262306a36Sopenharmony_ci * 93362306a36Sopenharmony_ci * Handle an hrtimer event by push the hrtimer forward a period. 93462306a36Sopenharmony_ci * 93562306a36Sopenharmony_ci * Returns: The hrtimer_restart value to return to the hrtimer subsystem. 93662306a36Sopenharmony_ci */ 93762306a36Sopenharmony_cienum hrtimer_restart kvm_mips_count_timeout(struct kvm_vcpu *vcpu) 93862306a36Sopenharmony_ci{ 93962306a36Sopenharmony_ci /* Add the Count period to the current expiry time */ 94062306a36Sopenharmony_ci hrtimer_add_expires_ns(&vcpu->arch.comparecount_timer, 94162306a36Sopenharmony_ci vcpu->arch.count_period); 94262306a36Sopenharmony_ci return HRTIMER_RESTART; 94362306a36Sopenharmony_ci} 94462306a36Sopenharmony_ci 94562306a36Sopenharmony_cienum emulation_result kvm_mips_emul_wait(struct kvm_vcpu *vcpu) 94662306a36Sopenharmony_ci{ 94762306a36Sopenharmony_ci kvm_debug("[%#lx] !!!WAIT!!! (%#lx)\n", vcpu->arch.pc, 94862306a36Sopenharmony_ci vcpu->arch.pending_exceptions); 94962306a36Sopenharmony_ci 95062306a36Sopenharmony_ci ++vcpu->stat.wait_exits; 95162306a36Sopenharmony_ci trace_kvm_exit(vcpu, KVM_TRACE_EXIT_WAIT); 95262306a36Sopenharmony_ci if (!vcpu->arch.pending_exceptions) { 95362306a36Sopenharmony_ci kvm_vz_lose_htimer(vcpu); 95462306a36Sopenharmony_ci vcpu->arch.wait = 1; 95562306a36Sopenharmony_ci kvm_vcpu_halt(vcpu); 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_ci /* 95862306a36Sopenharmony_ci * We are runnable, then definitely go off to user space to 95962306a36Sopenharmony_ci * check if any I/O interrupts are pending. 96062306a36Sopenharmony_ci */ 96162306a36Sopenharmony_ci if (kvm_arch_vcpu_runnable(vcpu)) 96262306a36Sopenharmony_ci vcpu->run->exit_reason = KVM_EXIT_IRQ_WINDOW_OPEN; 96362306a36Sopenharmony_ci } 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_ci return EMULATE_DONE; 96662306a36Sopenharmony_ci} 96762306a36Sopenharmony_ci 96862306a36Sopenharmony_cienum emulation_result kvm_mips_emulate_store(union mips_instruction inst, 96962306a36Sopenharmony_ci u32 cause, 97062306a36Sopenharmony_ci struct kvm_vcpu *vcpu) 97162306a36Sopenharmony_ci{ 97262306a36Sopenharmony_ci int r; 97362306a36Sopenharmony_ci enum emulation_result er; 97462306a36Sopenharmony_ci u32 rt; 97562306a36Sopenharmony_ci struct kvm_run *run = vcpu->run; 97662306a36Sopenharmony_ci void *data = run->mmio.data; 97762306a36Sopenharmony_ci unsigned int imme; 97862306a36Sopenharmony_ci unsigned long curr_pc; 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_ci /* 98162306a36Sopenharmony_ci * Update PC and hold onto current PC in case there is 98262306a36Sopenharmony_ci * an error and we want to rollback the PC 98362306a36Sopenharmony_ci */ 98462306a36Sopenharmony_ci curr_pc = vcpu->arch.pc; 98562306a36Sopenharmony_ci er = update_pc(vcpu, cause); 98662306a36Sopenharmony_ci if (er == EMULATE_FAIL) 98762306a36Sopenharmony_ci return er; 98862306a36Sopenharmony_ci 98962306a36Sopenharmony_ci rt = inst.i_format.rt; 99062306a36Sopenharmony_ci 99162306a36Sopenharmony_ci run->mmio.phys_addr = kvm_mips_callbacks->gva_to_gpa( 99262306a36Sopenharmony_ci vcpu->arch.host_cp0_badvaddr); 99362306a36Sopenharmony_ci if (run->mmio.phys_addr == KVM_INVALID_ADDR) 99462306a36Sopenharmony_ci goto out_fail; 99562306a36Sopenharmony_ci 99662306a36Sopenharmony_ci switch (inst.i_format.opcode) { 99762306a36Sopenharmony_ci#if defined(CONFIG_64BIT) 99862306a36Sopenharmony_ci case sd_op: 99962306a36Sopenharmony_ci run->mmio.len = 8; 100062306a36Sopenharmony_ci *(u64 *)data = vcpu->arch.gprs[rt]; 100162306a36Sopenharmony_ci 100262306a36Sopenharmony_ci kvm_debug("[%#lx] OP_SD: eaddr: %#lx, gpr: %#lx, data: %#llx\n", 100362306a36Sopenharmony_ci vcpu->arch.pc, vcpu->arch.host_cp0_badvaddr, 100462306a36Sopenharmony_ci vcpu->arch.gprs[rt], *(u64 *)data); 100562306a36Sopenharmony_ci break; 100662306a36Sopenharmony_ci#endif 100762306a36Sopenharmony_ci 100862306a36Sopenharmony_ci case sw_op: 100962306a36Sopenharmony_ci run->mmio.len = 4; 101062306a36Sopenharmony_ci *(u32 *)data = vcpu->arch.gprs[rt]; 101162306a36Sopenharmony_ci 101262306a36Sopenharmony_ci kvm_debug("[%#lx] OP_SW: eaddr: %#lx, gpr: %#lx, data: %#x\n", 101362306a36Sopenharmony_ci vcpu->arch.pc, vcpu->arch.host_cp0_badvaddr, 101462306a36Sopenharmony_ci vcpu->arch.gprs[rt], *(u32 *)data); 101562306a36Sopenharmony_ci break; 101662306a36Sopenharmony_ci 101762306a36Sopenharmony_ci case sh_op: 101862306a36Sopenharmony_ci run->mmio.len = 2; 101962306a36Sopenharmony_ci *(u16 *)data = vcpu->arch.gprs[rt]; 102062306a36Sopenharmony_ci 102162306a36Sopenharmony_ci kvm_debug("[%#lx] OP_SH: eaddr: %#lx, gpr: %#lx, data: %#x\n", 102262306a36Sopenharmony_ci vcpu->arch.pc, vcpu->arch.host_cp0_badvaddr, 102362306a36Sopenharmony_ci vcpu->arch.gprs[rt], *(u16 *)data); 102462306a36Sopenharmony_ci break; 102562306a36Sopenharmony_ci 102662306a36Sopenharmony_ci case sb_op: 102762306a36Sopenharmony_ci run->mmio.len = 1; 102862306a36Sopenharmony_ci *(u8 *)data = vcpu->arch.gprs[rt]; 102962306a36Sopenharmony_ci 103062306a36Sopenharmony_ci kvm_debug("[%#lx] OP_SB: eaddr: %#lx, gpr: %#lx, data: %#x\n", 103162306a36Sopenharmony_ci vcpu->arch.pc, vcpu->arch.host_cp0_badvaddr, 103262306a36Sopenharmony_ci vcpu->arch.gprs[rt], *(u8 *)data); 103362306a36Sopenharmony_ci break; 103462306a36Sopenharmony_ci 103562306a36Sopenharmony_ci case swl_op: 103662306a36Sopenharmony_ci run->mmio.phys_addr = kvm_mips_callbacks->gva_to_gpa( 103762306a36Sopenharmony_ci vcpu->arch.host_cp0_badvaddr) & (~0x3); 103862306a36Sopenharmony_ci run->mmio.len = 4; 103962306a36Sopenharmony_ci imme = vcpu->arch.host_cp0_badvaddr & 0x3; 104062306a36Sopenharmony_ci switch (imme) { 104162306a36Sopenharmony_ci case 0: 104262306a36Sopenharmony_ci *(u32 *)data = ((*(u32 *)data) & 0xffffff00) | 104362306a36Sopenharmony_ci (vcpu->arch.gprs[rt] >> 24); 104462306a36Sopenharmony_ci break; 104562306a36Sopenharmony_ci case 1: 104662306a36Sopenharmony_ci *(u32 *)data = ((*(u32 *)data) & 0xffff0000) | 104762306a36Sopenharmony_ci (vcpu->arch.gprs[rt] >> 16); 104862306a36Sopenharmony_ci break; 104962306a36Sopenharmony_ci case 2: 105062306a36Sopenharmony_ci *(u32 *)data = ((*(u32 *)data) & 0xff000000) | 105162306a36Sopenharmony_ci (vcpu->arch.gprs[rt] >> 8); 105262306a36Sopenharmony_ci break; 105362306a36Sopenharmony_ci case 3: 105462306a36Sopenharmony_ci *(u32 *)data = vcpu->arch.gprs[rt]; 105562306a36Sopenharmony_ci break; 105662306a36Sopenharmony_ci default: 105762306a36Sopenharmony_ci break; 105862306a36Sopenharmony_ci } 105962306a36Sopenharmony_ci 106062306a36Sopenharmony_ci kvm_debug("[%#lx] OP_SWL: eaddr: %#lx, gpr: %#lx, data: %#x\n", 106162306a36Sopenharmony_ci vcpu->arch.pc, vcpu->arch.host_cp0_badvaddr, 106262306a36Sopenharmony_ci vcpu->arch.gprs[rt], *(u32 *)data); 106362306a36Sopenharmony_ci break; 106462306a36Sopenharmony_ci 106562306a36Sopenharmony_ci case swr_op: 106662306a36Sopenharmony_ci run->mmio.phys_addr = kvm_mips_callbacks->gva_to_gpa( 106762306a36Sopenharmony_ci vcpu->arch.host_cp0_badvaddr) & (~0x3); 106862306a36Sopenharmony_ci run->mmio.len = 4; 106962306a36Sopenharmony_ci imme = vcpu->arch.host_cp0_badvaddr & 0x3; 107062306a36Sopenharmony_ci switch (imme) { 107162306a36Sopenharmony_ci case 0: 107262306a36Sopenharmony_ci *(u32 *)data = vcpu->arch.gprs[rt]; 107362306a36Sopenharmony_ci break; 107462306a36Sopenharmony_ci case 1: 107562306a36Sopenharmony_ci *(u32 *)data = ((*(u32 *)data) & 0xff) | 107662306a36Sopenharmony_ci (vcpu->arch.gprs[rt] << 8); 107762306a36Sopenharmony_ci break; 107862306a36Sopenharmony_ci case 2: 107962306a36Sopenharmony_ci *(u32 *)data = ((*(u32 *)data) & 0xffff) | 108062306a36Sopenharmony_ci (vcpu->arch.gprs[rt] << 16); 108162306a36Sopenharmony_ci break; 108262306a36Sopenharmony_ci case 3: 108362306a36Sopenharmony_ci *(u32 *)data = ((*(u32 *)data) & 0xffffff) | 108462306a36Sopenharmony_ci (vcpu->arch.gprs[rt] << 24); 108562306a36Sopenharmony_ci break; 108662306a36Sopenharmony_ci default: 108762306a36Sopenharmony_ci break; 108862306a36Sopenharmony_ci } 108962306a36Sopenharmony_ci 109062306a36Sopenharmony_ci kvm_debug("[%#lx] OP_SWR: eaddr: %#lx, gpr: %#lx, data: %#x\n", 109162306a36Sopenharmony_ci vcpu->arch.pc, vcpu->arch.host_cp0_badvaddr, 109262306a36Sopenharmony_ci vcpu->arch.gprs[rt], *(u32 *)data); 109362306a36Sopenharmony_ci break; 109462306a36Sopenharmony_ci 109562306a36Sopenharmony_ci#if defined(CONFIG_64BIT) 109662306a36Sopenharmony_ci case sdl_op: 109762306a36Sopenharmony_ci run->mmio.phys_addr = kvm_mips_callbacks->gva_to_gpa( 109862306a36Sopenharmony_ci vcpu->arch.host_cp0_badvaddr) & (~0x7); 109962306a36Sopenharmony_ci 110062306a36Sopenharmony_ci run->mmio.len = 8; 110162306a36Sopenharmony_ci imme = vcpu->arch.host_cp0_badvaddr & 0x7; 110262306a36Sopenharmony_ci switch (imme) { 110362306a36Sopenharmony_ci case 0: 110462306a36Sopenharmony_ci *(u64 *)data = ((*(u64 *)data) & 0xffffffffffffff00) | 110562306a36Sopenharmony_ci ((vcpu->arch.gprs[rt] >> 56) & 0xff); 110662306a36Sopenharmony_ci break; 110762306a36Sopenharmony_ci case 1: 110862306a36Sopenharmony_ci *(u64 *)data = ((*(u64 *)data) & 0xffffffffffff0000) | 110962306a36Sopenharmony_ci ((vcpu->arch.gprs[rt] >> 48) & 0xffff); 111062306a36Sopenharmony_ci break; 111162306a36Sopenharmony_ci case 2: 111262306a36Sopenharmony_ci *(u64 *)data = ((*(u64 *)data) & 0xffffffffff000000) | 111362306a36Sopenharmony_ci ((vcpu->arch.gprs[rt] >> 40) & 0xffffff); 111462306a36Sopenharmony_ci break; 111562306a36Sopenharmony_ci case 3: 111662306a36Sopenharmony_ci *(u64 *)data = ((*(u64 *)data) & 0xffffffff00000000) | 111762306a36Sopenharmony_ci ((vcpu->arch.gprs[rt] >> 32) & 0xffffffff); 111862306a36Sopenharmony_ci break; 111962306a36Sopenharmony_ci case 4: 112062306a36Sopenharmony_ci *(u64 *)data = ((*(u64 *)data) & 0xffffff0000000000) | 112162306a36Sopenharmony_ci ((vcpu->arch.gprs[rt] >> 24) & 0xffffffffff); 112262306a36Sopenharmony_ci break; 112362306a36Sopenharmony_ci case 5: 112462306a36Sopenharmony_ci *(u64 *)data = ((*(u64 *)data) & 0xffff000000000000) | 112562306a36Sopenharmony_ci ((vcpu->arch.gprs[rt] >> 16) & 0xffffffffffff); 112662306a36Sopenharmony_ci break; 112762306a36Sopenharmony_ci case 6: 112862306a36Sopenharmony_ci *(u64 *)data = ((*(u64 *)data) & 0xff00000000000000) | 112962306a36Sopenharmony_ci ((vcpu->arch.gprs[rt] >> 8) & 0xffffffffffffff); 113062306a36Sopenharmony_ci break; 113162306a36Sopenharmony_ci case 7: 113262306a36Sopenharmony_ci *(u64 *)data = vcpu->arch.gprs[rt]; 113362306a36Sopenharmony_ci break; 113462306a36Sopenharmony_ci default: 113562306a36Sopenharmony_ci break; 113662306a36Sopenharmony_ci } 113762306a36Sopenharmony_ci 113862306a36Sopenharmony_ci kvm_debug("[%#lx] OP_SDL: eaddr: %#lx, gpr: %#lx, data: %llx\n", 113962306a36Sopenharmony_ci vcpu->arch.pc, vcpu->arch.host_cp0_badvaddr, 114062306a36Sopenharmony_ci vcpu->arch.gprs[rt], *(u64 *)data); 114162306a36Sopenharmony_ci break; 114262306a36Sopenharmony_ci 114362306a36Sopenharmony_ci case sdr_op: 114462306a36Sopenharmony_ci run->mmio.phys_addr = kvm_mips_callbacks->gva_to_gpa( 114562306a36Sopenharmony_ci vcpu->arch.host_cp0_badvaddr) & (~0x7); 114662306a36Sopenharmony_ci 114762306a36Sopenharmony_ci run->mmio.len = 8; 114862306a36Sopenharmony_ci imme = vcpu->arch.host_cp0_badvaddr & 0x7; 114962306a36Sopenharmony_ci switch (imme) { 115062306a36Sopenharmony_ci case 0: 115162306a36Sopenharmony_ci *(u64 *)data = vcpu->arch.gprs[rt]; 115262306a36Sopenharmony_ci break; 115362306a36Sopenharmony_ci case 1: 115462306a36Sopenharmony_ci *(u64 *)data = ((*(u64 *)data) & 0xff) | 115562306a36Sopenharmony_ci (vcpu->arch.gprs[rt] << 8); 115662306a36Sopenharmony_ci break; 115762306a36Sopenharmony_ci case 2: 115862306a36Sopenharmony_ci *(u64 *)data = ((*(u64 *)data) & 0xffff) | 115962306a36Sopenharmony_ci (vcpu->arch.gprs[rt] << 16); 116062306a36Sopenharmony_ci break; 116162306a36Sopenharmony_ci case 3: 116262306a36Sopenharmony_ci *(u64 *)data = ((*(u64 *)data) & 0xffffff) | 116362306a36Sopenharmony_ci (vcpu->arch.gprs[rt] << 24); 116462306a36Sopenharmony_ci break; 116562306a36Sopenharmony_ci case 4: 116662306a36Sopenharmony_ci *(u64 *)data = ((*(u64 *)data) & 0xffffffff) | 116762306a36Sopenharmony_ci (vcpu->arch.gprs[rt] << 32); 116862306a36Sopenharmony_ci break; 116962306a36Sopenharmony_ci case 5: 117062306a36Sopenharmony_ci *(u64 *)data = ((*(u64 *)data) & 0xffffffffff) | 117162306a36Sopenharmony_ci (vcpu->arch.gprs[rt] << 40); 117262306a36Sopenharmony_ci break; 117362306a36Sopenharmony_ci case 6: 117462306a36Sopenharmony_ci *(u64 *)data = ((*(u64 *)data) & 0xffffffffffff) | 117562306a36Sopenharmony_ci (vcpu->arch.gprs[rt] << 48); 117662306a36Sopenharmony_ci break; 117762306a36Sopenharmony_ci case 7: 117862306a36Sopenharmony_ci *(u64 *)data = ((*(u64 *)data) & 0xffffffffffffff) | 117962306a36Sopenharmony_ci (vcpu->arch.gprs[rt] << 56); 118062306a36Sopenharmony_ci break; 118162306a36Sopenharmony_ci default: 118262306a36Sopenharmony_ci break; 118362306a36Sopenharmony_ci } 118462306a36Sopenharmony_ci 118562306a36Sopenharmony_ci kvm_debug("[%#lx] OP_SDR: eaddr: %#lx, gpr: %#lx, data: %llx\n", 118662306a36Sopenharmony_ci vcpu->arch.pc, vcpu->arch.host_cp0_badvaddr, 118762306a36Sopenharmony_ci vcpu->arch.gprs[rt], *(u64 *)data); 118862306a36Sopenharmony_ci break; 118962306a36Sopenharmony_ci#endif 119062306a36Sopenharmony_ci 119162306a36Sopenharmony_ci#ifdef CONFIG_CPU_LOONGSON64 119262306a36Sopenharmony_ci case sdc2_op: 119362306a36Sopenharmony_ci rt = inst.loongson3_lsdc2_format.rt; 119462306a36Sopenharmony_ci switch (inst.loongson3_lsdc2_format.opcode1) { 119562306a36Sopenharmony_ci /* 119662306a36Sopenharmony_ci * Loongson-3 overridden sdc2 instructions. 119762306a36Sopenharmony_ci * opcode1 instruction 119862306a36Sopenharmony_ci * 0x0 gssbx: store 1 bytes from GPR 119962306a36Sopenharmony_ci * 0x1 gsshx: store 2 bytes from GPR 120062306a36Sopenharmony_ci * 0x2 gsswx: store 4 bytes from GPR 120162306a36Sopenharmony_ci * 0x3 gssdx: store 8 bytes from GPR 120262306a36Sopenharmony_ci */ 120362306a36Sopenharmony_ci case 0x0: 120462306a36Sopenharmony_ci run->mmio.len = 1; 120562306a36Sopenharmony_ci *(u8 *)data = vcpu->arch.gprs[rt]; 120662306a36Sopenharmony_ci 120762306a36Sopenharmony_ci kvm_debug("[%#lx] OP_GSSBX: eaddr: %#lx, gpr: %#lx, data: %#x\n", 120862306a36Sopenharmony_ci vcpu->arch.pc, vcpu->arch.host_cp0_badvaddr, 120962306a36Sopenharmony_ci vcpu->arch.gprs[rt], *(u8 *)data); 121062306a36Sopenharmony_ci break; 121162306a36Sopenharmony_ci case 0x1: 121262306a36Sopenharmony_ci run->mmio.len = 2; 121362306a36Sopenharmony_ci *(u16 *)data = vcpu->arch.gprs[rt]; 121462306a36Sopenharmony_ci 121562306a36Sopenharmony_ci kvm_debug("[%#lx] OP_GSSSHX: eaddr: %#lx, gpr: %#lx, data: %#x\n", 121662306a36Sopenharmony_ci vcpu->arch.pc, vcpu->arch.host_cp0_badvaddr, 121762306a36Sopenharmony_ci vcpu->arch.gprs[rt], *(u16 *)data); 121862306a36Sopenharmony_ci break; 121962306a36Sopenharmony_ci case 0x2: 122062306a36Sopenharmony_ci run->mmio.len = 4; 122162306a36Sopenharmony_ci *(u32 *)data = vcpu->arch.gprs[rt]; 122262306a36Sopenharmony_ci 122362306a36Sopenharmony_ci kvm_debug("[%#lx] OP_GSSWX: eaddr: %#lx, gpr: %#lx, data: %#x\n", 122462306a36Sopenharmony_ci vcpu->arch.pc, vcpu->arch.host_cp0_badvaddr, 122562306a36Sopenharmony_ci vcpu->arch.gprs[rt], *(u32 *)data); 122662306a36Sopenharmony_ci break; 122762306a36Sopenharmony_ci case 0x3: 122862306a36Sopenharmony_ci run->mmio.len = 8; 122962306a36Sopenharmony_ci *(u64 *)data = vcpu->arch.gprs[rt]; 123062306a36Sopenharmony_ci 123162306a36Sopenharmony_ci kvm_debug("[%#lx] OP_GSSDX: eaddr: %#lx, gpr: %#lx, data: %#llx\n", 123262306a36Sopenharmony_ci vcpu->arch.pc, vcpu->arch.host_cp0_badvaddr, 123362306a36Sopenharmony_ci vcpu->arch.gprs[rt], *(u64 *)data); 123462306a36Sopenharmony_ci break; 123562306a36Sopenharmony_ci default: 123662306a36Sopenharmony_ci kvm_err("Godson Extended GS-Store not yet supported (inst=0x%08x)\n", 123762306a36Sopenharmony_ci inst.word); 123862306a36Sopenharmony_ci break; 123962306a36Sopenharmony_ci } 124062306a36Sopenharmony_ci break; 124162306a36Sopenharmony_ci#endif 124262306a36Sopenharmony_ci default: 124362306a36Sopenharmony_ci kvm_err("Store not yet supported (inst=0x%08x)\n", 124462306a36Sopenharmony_ci inst.word); 124562306a36Sopenharmony_ci goto out_fail; 124662306a36Sopenharmony_ci } 124762306a36Sopenharmony_ci 124862306a36Sopenharmony_ci vcpu->mmio_needed = 1; 124962306a36Sopenharmony_ci run->mmio.is_write = 1; 125062306a36Sopenharmony_ci vcpu->mmio_is_write = 1; 125162306a36Sopenharmony_ci 125262306a36Sopenharmony_ci r = kvm_io_bus_write(vcpu, KVM_MMIO_BUS, 125362306a36Sopenharmony_ci run->mmio.phys_addr, run->mmio.len, data); 125462306a36Sopenharmony_ci 125562306a36Sopenharmony_ci if (!r) { 125662306a36Sopenharmony_ci vcpu->mmio_needed = 0; 125762306a36Sopenharmony_ci return EMULATE_DONE; 125862306a36Sopenharmony_ci } 125962306a36Sopenharmony_ci 126062306a36Sopenharmony_ci return EMULATE_DO_MMIO; 126162306a36Sopenharmony_ci 126262306a36Sopenharmony_ciout_fail: 126362306a36Sopenharmony_ci /* Rollback PC if emulation was unsuccessful */ 126462306a36Sopenharmony_ci vcpu->arch.pc = curr_pc; 126562306a36Sopenharmony_ci return EMULATE_FAIL; 126662306a36Sopenharmony_ci} 126762306a36Sopenharmony_ci 126862306a36Sopenharmony_cienum emulation_result kvm_mips_emulate_load(union mips_instruction inst, 126962306a36Sopenharmony_ci u32 cause, struct kvm_vcpu *vcpu) 127062306a36Sopenharmony_ci{ 127162306a36Sopenharmony_ci struct kvm_run *run = vcpu->run; 127262306a36Sopenharmony_ci int r; 127362306a36Sopenharmony_ci enum emulation_result er; 127462306a36Sopenharmony_ci unsigned long curr_pc; 127562306a36Sopenharmony_ci u32 op, rt; 127662306a36Sopenharmony_ci unsigned int imme; 127762306a36Sopenharmony_ci 127862306a36Sopenharmony_ci rt = inst.i_format.rt; 127962306a36Sopenharmony_ci op = inst.i_format.opcode; 128062306a36Sopenharmony_ci 128162306a36Sopenharmony_ci /* 128262306a36Sopenharmony_ci * Find the resume PC now while we have safe and easy access to the 128362306a36Sopenharmony_ci * prior branch instruction, and save it for 128462306a36Sopenharmony_ci * kvm_mips_complete_mmio_load() to restore later. 128562306a36Sopenharmony_ci */ 128662306a36Sopenharmony_ci curr_pc = vcpu->arch.pc; 128762306a36Sopenharmony_ci er = update_pc(vcpu, cause); 128862306a36Sopenharmony_ci if (er == EMULATE_FAIL) 128962306a36Sopenharmony_ci return er; 129062306a36Sopenharmony_ci vcpu->arch.io_pc = vcpu->arch.pc; 129162306a36Sopenharmony_ci vcpu->arch.pc = curr_pc; 129262306a36Sopenharmony_ci 129362306a36Sopenharmony_ci vcpu->arch.io_gpr = rt; 129462306a36Sopenharmony_ci 129562306a36Sopenharmony_ci run->mmio.phys_addr = kvm_mips_callbacks->gva_to_gpa( 129662306a36Sopenharmony_ci vcpu->arch.host_cp0_badvaddr); 129762306a36Sopenharmony_ci if (run->mmio.phys_addr == KVM_INVALID_ADDR) 129862306a36Sopenharmony_ci return EMULATE_FAIL; 129962306a36Sopenharmony_ci 130062306a36Sopenharmony_ci vcpu->mmio_needed = 2; /* signed */ 130162306a36Sopenharmony_ci switch (op) { 130262306a36Sopenharmony_ci#if defined(CONFIG_64BIT) 130362306a36Sopenharmony_ci case ld_op: 130462306a36Sopenharmony_ci run->mmio.len = 8; 130562306a36Sopenharmony_ci break; 130662306a36Sopenharmony_ci 130762306a36Sopenharmony_ci case lwu_op: 130862306a36Sopenharmony_ci vcpu->mmio_needed = 1; /* unsigned */ 130962306a36Sopenharmony_ci fallthrough; 131062306a36Sopenharmony_ci#endif 131162306a36Sopenharmony_ci case lw_op: 131262306a36Sopenharmony_ci run->mmio.len = 4; 131362306a36Sopenharmony_ci break; 131462306a36Sopenharmony_ci 131562306a36Sopenharmony_ci case lhu_op: 131662306a36Sopenharmony_ci vcpu->mmio_needed = 1; /* unsigned */ 131762306a36Sopenharmony_ci fallthrough; 131862306a36Sopenharmony_ci case lh_op: 131962306a36Sopenharmony_ci run->mmio.len = 2; 132062306a36Sopenharmony_ci break; 132162306a36Sopenharmony_ci 132262306a36Sopenharmony_ci case lbu_op: 132362306a36Sopenharmony_ci vcpu->mmio_needed = 1; /* unsigned */ 132462306a36Sopenharmony_ci fallthrough; 132562306a36Sopenharmony_ci case lb_op: 132662306a36Sopenharmony_ci run->mmio.len = 1; 132762306a36Sopenharmony_ci break; 132862306a36Sopenharmony_ci 132962306a36Sopenharmony_ci case lwl_op: 133062306a36Sopenharmony_ci run->mmio.phys_addr = kvm_mips_callbacks->gva_to_gpa( 133162306a36Sopenharmony_ci vcpu->arch.host_cp0_badvaddr) & (~0x3); 133262306a36Sopenharmony_ci 133362306a36Sopenharmony_ci run->mmio.len = 4; 133462306a36Sopenharmony_ci imme = vcpu->arch.host_cp0_badvaddr & 0x3; 133562306a36Sopenharmony_ci switch (imme) { 133662306a36Sopenharmony_ci case 0: 133762306a36Sopenharmony_ci vcpu->mmio_needed = 3; /* 1 byte */ 133862306a36Sopenharmony_ci break; 133962306a36Sopenharmony_ci case 1: 134062306a36Sopenharmony_ci vcpu->mmio_needed = 4; /* 2 bytes */ 134162306a36Sopenharmony_ci break; 134262306a36Sopenharmony_ci case 2: 134362306a36Sopenharmony_ci vcpu->mmio_needed = 5; /* 3 bytes */ 134462306a36Sopenharmony_ci break; 134562306a36Sopenharmony_ci case 3: 134662306a36Sopenharmony_ci vcpu->mmio_needed = 6; /* 4 bytes */ 134762306a36Sopenharmony_ci break; 134862306a36Sopenharmony_ci default: 134962306a36Sopenharmony_ci break; 135062306a36Sopenharmony_ci } 135162306a36Sopenharmony_ci break; 135262306a36Sopenharmony_ci 135362306a36Sopenharmony_ci case lwr_op: 135462306a36Sopenharmony_ci run->mmio.phys_addr = kvm_mips_callbacks->gva_to_gpa( 135562306a36Sopenharmony_ci vcpu->arch.host_cp0_badvaddr) & (~0x3); 135662306a36Sopenharmony_ci 135762306a36Sopenharmony_ci run->mmio.len = 4; 135862306a36Sopenharmony_ci imme = vcpu->arch.host_cp0_badvaddr & 0x3; 135962306a36Sopenharmony_ci switch (imme) { 136062306a36Sopenharmony_ci case 0: 136162306a36Sopenharmony_ci vcpu->mmio_needed = 7; /* 4 bytes */ 136262306a36Sopenharmony_ci break; 136362306a36Sopenharmony_ci case 1: 136462306a36Sopenharmony_ci vcpu->mmio_needed = 8; /* 3 bytes */ 136562306a36Sopenharmony_ci break; 136662306a36Sopenharmony_ci case 2: 136762306a36Sopenharmony_ci vcpu->mmio_needed = 9; /* 2 bytes */ 136862306a36Sopenharmony_ci break; 136962306a36Sopenharmony_ci case 3: 137062306a36Sopenharmony_ci vcpu->mmio_needed = 10; /* 1 byte */ 137162306a36Sopenharmony_ci break; 137262306a36Sopenharmony_ci default: 137362306a36Sopenharmony_ci break; 137462306a36Sopenharmony_ci } 137562306a36Sopenharmony_ci break; 137662306a36Sopenharmony_ci 137762306a36Sopenharmony_ci#if defined(CONFIG_64BIT) 137862306a36Sopenharmony_ci case ldl_op: 137962306a36Sopenharmony_ci run->mmio.phys_addr = kvm_mips_callbacks->gva_to_gpa( 138062306a36Sopenharmony_ci vcpu->arch.host_cp0_badvaddr) & (~0x7); 138162306a36Sopenharmony_ci 138262306a36Sopenharmony_ci run->mmio.len = 8; 138362306a36Sopenharmony_ci imme = vcpu->arch.host_cp0_badvaddr & 0x7; 138462306a36Sopenharmony_ci switch (imme) { 138562306a36Sopenharmony_ci case 0: 138662306a36Sopenharmony_ci vcpu->mmio_needed = 11; /* 1 byte */ 138762306a36Sopenharmony_ci break; 138862306a36Sopenharmony_ci case 1: 138962306a36Sopenharmony_ci vcpu->mmio_needed = 12; /* 2 bytes */ 139062306a36Sopenharmony_ci break; 139162306a36Sopenharmony_ci case 2: 139262306a36Sopenharmony_ci vcpu->mmio_needed = 13; /* 3 bytes */ 139362306a36Sopenharmony_ci break; 139462306a36Sopenharmony_ci case 3: 139562306a36Sopenharmony_ci vcpu->mmio_needed = 14; /* 4 bytes */ 139662306a36Sopenharmony_ci break; 139762306a36Sopenharmony_ci case 4: 139862306a36Sopenharmony_ci vcpu->mmio_needed = 15; /* 5 bytes */ 139962306a36Sopenharmony_ci break; 140062306a36Sopenharmony_ci case 5: 140162306a36Sopenharmony_ci vcpu->mmio_needed = 16; /* 6 bytes */ 140262306a36Sopenharmony_ci break; 140362306a36Sopenharmony_ci case 6: 140462306a36Sopenharmony_ci vcpu->mmio_needed = 17; /* 7 bytes */ 140562306a36Sopenharmony_ci break; 140662306a36Sopenharmony_ci case 7: 140762306a36Sopenharmony_ci vcpu->mmio_needed = 18; /* 8 bytes */ 140862306a36Sopenharmony_ci break; 140962306a36Sopenharmony_ci default: 141062306a36Sopenharmony_ci break; 141162306a36Sopenharmony_ci } 141262306a36Sopenharmony_ci break; 141362306a36Sopenharmony_ci 141462306a36Sopenharmony_ci case ldr_op: 141562306a36Sopenharmony_ci run->mmio.phys_addr = kvm_mips_callbacks->gva_to_gpa( 141662306a36Sopenharmony_ci vcpu->arch.host_cp0_badvaddr) & (~0x7); 141762306a36Sopenharmony_ci 141862306a36Sopenharmony_ci run->mmio.len = 8; 141962306a36Sopenharmony_ci imme = vcpu->arch.host_cp0_badvaddr & 0x7; 142062306a36Sopenharmony_ci switch (imme) { 142162306a36Sopenharmony_ci case 0: 142262306a36Sopenharmony_ci vcpu->mmio_needed = 19; /* 8 bytes */ 142362306a36Sopenharmony_ci break; 142462306a36Sopenharmony_ci case 1: 142562306a36Sopenharmony_ci vcpu->mmio_needed = 20; /* 7 bytes */ 142662306a36Sopenharmony_ci break; 142762306a36Sopenharmony_ci case 2: 142862306a36Sopenharmony_ci vcpu->mmio_needed = 21; /* 6 bytes */ 142962306a36Sopenharmony_ci break; 143062306a36Sopenharmony_ci case 3: 143162306a36Sopenharmony_ci vcpu->mmio_needed = 22; /* 5 bytes */ 143262306a36Sopenharmony_ci break; 143362306a36Sopenharmony_ci case 4: 143462306a36Sopenharmony_ci vcpu->mmio_needed = 23; /* 4 bytes */ 143562306a36Sopenharmony_ci break; 143662306a36Sopenharmony_ci case 5: 143762306a36Sopenharmony_ci vcpu->mmio_needed = 24; /* 3 bytes */ 143862306a36Sopenharmony_ci break; 143962306a36Sopenharmony_ci case 6: 144062306a36Sopenharmony_ci vcpu->mmio_needed = 25; /* 2 bytes */ 144162306a36Sopenharmony_ci break; 144262306a36Sopenharmony_ci case 7: 144362306a36Sopenharmony_ci vcpu->mmio_needed = 26; /* 1 byte */ 144462306a36Sopenharmony_ci break; 144562306a36Sopenharmony_ci default: 144662306a36Sopenharmony_ci break; 144762306a36Sopenharmony_ci } 144862306a36Sopenharmony_ci break; 144962306a36Sopenharmony_ci#endif 145062306a36Sopenharmony_ci 145162306a36Sopenharmony_ci#ifdef CONFIG_CPU_LOONGSON64 145262306a36Sopenharmony_ci case ldc2_op: 145362306a36Sopenharmony_ci rt = inst.loongson3_lsdc2_format.rt; 145462306a36Sopenharmony_ci switch (inst.loongson3_lsdc2_format.opcode1) { 145562306a36Sopenharmony_ci /* 145662306a36Sopenharmony_ci * Loongson-3 overridden ldc2 instructions. 145762306a36Sopenharmony_ci * opcode1 instruction 145862306a36Sopenharmony_ci * 0x0 gslbx: store 1 bytes from GPR 145962306a36Sopenharmony_ci * 0x1 gslhx: store 2 bytes from GPR 146062306a36Sopenharmony_ci * 0x2 gslwx: store 4 bytes from GPR 146162306a36Sopenharmony_ci * 0x3 gsldx: store 8 bytes from GPR 146262306a36Sopenharmony_ci */ 146362306a36Sopenharmony_ci case 0x0: 146462306a36Sopenharmony_ci run->mmio.len = 1; 146562306a36Sopenharmony_ci vcpu->mmio_needed = 27; /* signed */ 146662306a36Sopenharmony_ci break; 146762306a36Sopenharmony_ci case 0x1: 146862306a36Sopenharmony_ci run->mmio.len = 2; 146962306a36Sopenharmony_ci vcpu->mmio_needed = 28; /* signed */ 147062306a36Sopenharmony_ci break; 147162306a36Sopenharmony_ci case 0x2: 147262306a36Sopenharmony_ci run->mmio.len = 4; 147362306a36Sopenharmony_ci vcpu->mmio_needed = 29; /* signed */ 147462306a36Sopenharmony_ci break; 147562306a36Sopenharmony_ci case 0x3: 147662306a36Sopenharmony_ci run->mmio.len = 8; 147762306a36Sopenharmony_ci vcpu->mmio_needed = 30; /* signed */ 147862306a36Sopenharmony_ci break; 147962306a36Sopenharmony_ci default: 148062306a36Sopenharmony_ci kvm_err("Godson Extended GS-Load for float not yet supported (inst=0x%08x)\n", 148162306a36Sopenharmony_ci inst.word); 148262306a36Sopenharmony_ci break; 148362306a36Sopenharmony_ci } 148462306a36Sopenharmony_ci break; 148562306a36Sopenharmony_ci#endif 148662306a36Sopenharmony_ci 148762306a36Sopenharmony_ci default: 148862306a36Sopenharmony_ci kvm_err("Load not yet supported (inst=0x%08x)\n", 148962306a36Sopenharmony_ci inst.word); 149062306a36Sopenharmony_ci vcpu->mmio_needed = 0; 149162306a36Sopenharmony_ci return EMULATE_FAIL; 149262306a36Sopenharmony_ci } 149362306a36Sopenharmony_ci 149462306a36Sopenharmony_ci run->mmio.is_write = 0; 149562306a36Sopenharmony_ci vcpu->mmio_is_write = 0; 149662306a36Sopenharmony_ci 149762306a36Sopenharmony_ci r = kvm_io_bus_read(vcpu, KVM_MMIO_BUS, 149862306a36Sopenharmony_ci run->mmio.phys_addr, run->mmio.len, run->mmio.data); 149962306a36Sopenharmony_ci 150062306a36Sopenharmony_ci if (!r) { 150162306a36Sopenharmony_ci kvm_mips_complete_mmio_load(vcpu); 150262306a36Sopenharmony_ci vcpu->mmio_needed = 0; 150362306a36Sopenharmony_ci return EMULATE_DONE; 150462306a36Sopenharmony_ci } 150562306a36Sopenharmony_ci 150662306a36Sopenharmony_ci return EMULATE_DO_MMIO; 150762306a36Sopenharmony_ci} 150862306a36Sopenharmony_ci 150962306a36Sopenharmony_cienum emulation_result kvm_mips_complete_mmio_load(struct kvm_vcpu *vcpu) 151062306a36Sopenharmony_ci{ 151162306a36Sopenharmony_ci struct kvm_run *run = vcpu->run; 151262306a36Sopenharmony_ci unsigned long *gpr = &vcpu->arch.gprs[vcpu->arch.io_gpr]; 151362306a36Sopenharmony_ci enum emulation_result er = EMULATE_DONE; 151462306a36Sopenharmony_ci 151562306a36Sopenharmony_ci if (run->mmio.len > sizeof(*gpr)) { 151662306a36Sopenharmony_ci kvm_err("Bad MMIO length: %d", run->mmio.len); 151762306a36Sopenharmony_ci er = EMULATE_FAIL; 151862306a36Sopenharmony_ci goto done; 151962306a36Sopenharmony_ci } 152062306a36Sopenharmony_ci 152162306a36Sopenharmony_ci /* Restore saved resume PC */ 152262306a36Sopenharmony_ci vcpu->arch.pc = vcpu->arch.io_pc; 152362306a36Sopenharmony_ci 152462306a36Sopenharmony_ci switch (run->mmio.len) { 152562306a36Sopenharmony_ci case 8: 152662306a36Sopenharmony_ci switch (vcpu->mmio_needed) { 152762306a36Sopenharmony_ci case 11: 152862306a36Sopenharmony_ci *gpr = (vcpu->arch.gprs[vcpu->arch.io_gpr] & 0xffffffffffffff) | 152962306a36Sopenharmony_ci (((*(s64 *)run->mmio.data) & 0xff) << 56); 153062306a36Sopenharmony_ci break; 153162306a36Sopenharmony_ci case 12: 153262306a36Sopenharmony_ci *gpr = (vcpu->arch.gprs[vcpu->arch.io_gpr] & 0xffffffffffff) | 153362306a36Sopenharmony_ci (((*(s64 *)run->mmio.data) & 0xffff) << 48); 153462306a36Sopenharmony_ci break; 153562306a36Sopenharmony_ci case 13: 153662306a36Sopenharmony_ci *gpr = (vcpu->arch.gprs[vcpu->arch.io_gpr] & 0xffffffffff) | 153762306a36Sopenharmony_ci (((*(s64 *)run->mmio.data) & 0xffffff) << 40); 153862306a36Sopenharmony_ci break; 153962306a36Sopenharmony_ci case 14: 154062306a36Sopenharmony_ci *gpr = (vcpu->arch.gprs[vcpu->arch.io_gpr] & 0xffffffff) | 154162306a36Sopenharmony_ci (((*(s64 *)run->mmio.data) & 0xffffffff) << 32); 154262306a36Sopenharmony_ci break; 154362306a36Sopenharmony_ci case 15: 154462306a36Sopenharmony_ci *gpr = (vcpu->arch.gprs[vcpu->arch.io_gpr] & 0xffffff) | 154562306a36Sopenharmony_ci (((*(s64 *)run->mmio.data) & 0xffffffffff) << 24); 154662306a36Sopenharmony_ci break; 154762306a36Sopenharmony_ci case 16: 154862306a36Sopenharmony_ci *gpr = (vcpu->arch.gprs[vcpu->arch.io_gpr] & 0xffff) | 154962306a36Sopenharmony_ci (((*(s64 *)run->mmio.data) & 0xffffffffffff) << 16); 155062306a36Sopenharmony_ci break; 155162306a36Sopenharmony_ci case 17: 155262306a36Sopenharmony_ci *gpr = (vcpu->arch.gprs[vcpu->arch.io_gpr] & 0xff) | 155362306a36Sopenharmony_ci (((*(s64 *)run->mmio.data) & 0xffffffffffffff) << 8); 155462306a36Sopenharmony_ci break; 155562306a36Sopenharmony_ci case 18: 155662306a36Sopenharmony_ci case 19: 155762306a36Sopenharmony_ci *gpr = *(s64 *)run->mmio.data; 155862306a36Sopenharmony_ci break; 155962306a36Sopenharmony_ci case 20: 156062306a36Sopenharmony_ci *gpr = (vcpu->arch.gprs[vcpu->arch.io_gpr] & 0xff00000000000000) | 156162306a36Sopenharmony_ci ((((*(s64 *)run->mmio.data)) >> 8) & 0xffffffffffffff); 156262306a36Sopenharmony_ci break; 156362306a36Sopenharmony_ci case 21: 156462306a36Sopenharmony_ci *gpr = (vcpu->arch.gprs[vcpu->arch.io_gpr] & 0xffff000000000000) | 156562306a36Sopenharmony_ci ((((*(s64 *)run->mmio.data)) >> 16) & 0xffffffffffff); 156662306a36Sopenharmony_ci break; 156762306a36Sopenharmony_ci case 22: 156862306a36Sopenharmony_ci *gpr = (vcpu->arch.gprs[vcpu->arch.io_gpr] & 0xffffff0000000000) | 156962306a36Sopenharmony_ci ((((*(s64 *)run->mmio.data)) >> 24) & 0xffffffffff); 157062306a36Sopenharmony_ci break; 157162306a36Sopenharmony_ci case 23: 157262306a36Sopenharmony_ci *gpr = (vcpu->arch.gprs[vcpu->arch.io_gpr] & 0xffffffff00000000) | 157362306a36Sopenharmony_ci ((((*(s64 *)run->mmio.data)) >> 32) & 0xffffffff); 157462306a36Sopenharmony_ci break; 157562306a36Sopenharmony_ci case 24: 157662306a36Sopenharmony_ci *gpr = (vcpu->arch.gprs[vcpu->arch.io_gpr] & 0xffffffffff000000) | 157762306a36Sopenharmony_ci ((((*(s64 *)run->mmio.data)) >> 40) & 0xffffff); 157862306a36Sopenharmony_ci break; 157962306a36Sopenharmony_ci case 25: 158062306a36Sopenharmony_ci *gpr = (vcpu->arch.gprs[vcpu->arch.io_gpr] & 0xffffffffffff0000) | 158162306a36Sopenharmony_ci ((((*(s64 *)run->mmio.data)) >> 48) & 0xffff); 158262306a36Sopenharmony_ci break; 158362306a36Sopenharmony_ci case 26: 158462306a36Sopenharmony_ci *gpr = (vcpu->arch.gprs[vcpu->arch.io_gpr] & 0xffffffffffffff00) | 158562306a36Sopenharmony_ci ((((*(s64 *)run->mmio.data)) >> 56) & 0xff); 158662306a36Sopenharmony_ci break; 158762306a36Sopenharmony_ci default: 158862306a36Sopenharmony_ci *gpr = *(s64 *)run->mmio.data; 158962306a36Sopenharmony_ci } 159062306a36Sopenharmony_ci break; 159162306a36Sopenharmony_ci 159262306a36Sopenharmony_ci case 4: 159362306a36Sopenharmony_ci switch (vcpu->mmio_needed) { 159462306a36Sopenharmony_ci case 1: 159562306a36Sopenharmony_ci *gpr = *(u32 *)run->mmio.data; 159662306a36Sopenharmony_ci break; 159762306a36Sopenharmony_ci case 2: 159862306a36Sopenharmony_ci *gpr = *(s32 *)run->mmio.data; 159962306a36Sopenharmony_ci break; 160062306a36Sopenharmony_ci case 3: 160162306a36Sopenharmony_ci *gpr = (vcpu->arch.gprs[vcpu->arch.io_gpr] & 0xffffff) | 160262306a36Sopenharmony_ci (((*(s32 *)run->mmio.data) & 0xff) << 24); 160362306a36Sopenharmony_ci break; 160462306a36Sopenharmony_ci case 4: 160562306a36Sopenharmony_ci *gpr = (vcpu->arch.gprs[vcpu->arch.io_gpr] & 0xffff) | 160662306a36Sopenharmony_ci (((*(s32 *)run->mmio.data) & 0xffff) << 16); 160762306a36Sopenharmony_ci break; 160862306a36Sopenharmony_ci case 5: 160962306a36Sopenharmony_ci *gpr = (vcpu->arch.gprs[vcpu->arch.io_gpr] & 0xff) | 161062306a36Sopenharmony_ci (((*(s32 *)run->mmio.data) & 0xffffff) << 8); 161162306a36Sopenharmony_ci break; 161262306a36Sopenharmony_ci case 6: 161362306a36Sopenharmony_ci case 7: 161462306a36Sopenharmony_ci *gpr = *(s32 *)run->mmio.data; 161562306a36Sopenharmony_ci break; 161662306a36Sopenharmony_ci case 8: 161762306a36Sopenharmony_ci *gpr = (vcpu->arch.gprs[vcpu->arch.io_gpr] & 0xff000000) | 161862306a36Sopenharmony_ci ((((*(s32 *)run->mmio.data)) >> 8) & 0xffffff); 161962306a36Sopenharmony_ci break; 162062306a36Sopenharmony_ci case 9: 162162306a36Sopenharmony_ci *gpr = (vcpu->arch.gprs[vcpu->arch.io_gpr] & 0xffff0000) | 162262306a36Sopenharmony_ci ((((*(s32 *)run->mmio.data)) >> 16) & 0xffff); 162362306a36Sopenharmony_ci break; 162462306a36Sopenharmony_ci case 10: 162562306a36Sopenharmony_ci *gpr = (vcpu->arch.gprs[vcpu->arch.io_gpr] & 0xffffff00) | 162662306a36Sopenharmony_ci ((((*(s32 *)run->mmio.data)) >> 24) & 0xff); 162762306a36Sopenharmony_ci break; 162862306a36Sopenharmony_ci default: 162962306a36Sopenharmony_ci *gpr = *(s32 *)run->mmio.data; 163062306a36Sopenharmony_ci } 163162306a36Sopenharmony_ci break; 163262306a36Sopenharmony_ci 163362306a36Sopenharmony_ci case 2: 163462306a36Sopenharmony_ci if (vcpu->mmio_needed == 1) 163562306a36Sopenharmony_ci *gpr = *(u16 *)run->mmio.data; 163662306a36Sopenharmony_ci else 163762306a36Sopenharmony_ci *gpr = *(s16 *)run->mmio.data; 163862306a36Sopenharmony_ci 163962306a36Sopenharmony_ci break; 164062306a36Sopenharmony_ci case 1: 164162306a36Sopenharmony_ci if (vcpu->mmio_needed == 1) 164262306a36Sopenharmony_ci *gpr = *(u8 *)run->mmio.data; 164362306a36Sopenharmony_ci else 164462306a36Sopenharmony_ci *gpr = *(s8 *)run->mmio.data; 164562306a36Sopenharmony_ci break; 164662306a36Sopenharmony_ci } 164762306a36Sopenharmony_ci 164862306a36Sopenharmony_cidone: 164962306a36Sopenharmony_ci return er; 165062306a36Sopenharmony_ci} 1651