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: Deliver/Emulate exceptions to the guest kernel 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/kvm_host.h> 158c2ecf20Sopenharmony_ci#include <linux/log2.h> 168c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 178c2ecf20Sopenharmony_ci#include <linux/vmalloc.h> 188c2ecf20Sopenharmony_ci#include <asm/mmu_context.h> 198c2ecf20Sopenharmony_ci#include <asm/pgalloc.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#include "interrupt.h" 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_cistatic gpa_t kvm_trap_emul_gva_to_gpa_cb(gva_t gva) 248c2ecf20Sopenharmony_ci{ 258c2ecf20Sopenharmony_ci gpa_t gpa; 268c2ecf20Sopenharmony_ci gva_t kseg = KSEGX(gva); 278c2ecf20Sopenharmony_ci gva_t gkseg = KVM_GUEST_KSEGX(gva); 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci if ((kseg == CKSEG0) || (kseg == CKSEG1)) 308c2ecf20Sopenharmony_ci gpa = CPHYSADDR(gva); 318c2ecf20Sopenharmony_ci else if (gkseg == KVM_GUEST_KSEG0) 328c2ecf20Sopenharmony_ci gpa = KVM_GUEST_CPHYSADDR(gva); 338c2ecf20Sopenharmony_ci else { 348c2ecf20Sopenharmony_ci kvm_err("%s: cannot find GPA for GVA: %#lx\n", __func__, gva); 358c2ecf20Sopenharmony_ci kvm_mips_dump_host_tlbs(); 368c2ecf20Sopenharmony_ci gpa = KVM_INVALID_ADDR; 378c2ecf20Sopenharmony_ci } 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci kvm_debug("%s: gva %#lx, gpa: %#llx\n", __func__, gva, gpa); 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci return gpa; 428c2ecf20Sopenharmony_ci} 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_cistatic int kvm_trap_emul_no_handler(struct kvm_vcpu *vcpu) 458c2ecf20Sopenharmony_ci{ 468c2ecf20Sopenharmony_ci u32 __user *opc = (u32 __user *) vcpu->arch.pc; 478c2ecf20Sopenharmony_ci u32 cause = vcpu->arch.host_cp0_cause; 488c2ecf20Sopenharmony_ci u32 exccode = (cause & CAUSEF_EXCCODE) >> CAUSEB_EXCCODE; 498c2ecf20Sopenharmony_ci unsigned long badvaddr = vcpu->arch.host_cp0_badvaddr; 508c2ecf20Sopenharmony_ci u32 inst = 0; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci /* 538c2ecf20Sopenharmony_ci * Fetch the instruction. 548c2ecf20Sopenharmony_ci */ 558c2ecf20Sopenharmony_ci if (cause & CAUSEF_BD) 568c2ecf20Sopenharmony_ci opc += 1; 578c2ecf20Sopenharmony_ci kvm_get_badinstr(opc, vcpu, &inst); 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci kvm_err("Exception Code: %d not handled @ PC: %p, inst: 0x%08x BadVaddr: %#lx Status: %#x\n", 608c2ecf20Sopenharmony_ci exccode, opc, inst, badvaddr, 618c2ecf20Sopenharmony_ci kvm_read_c0_guest_status(vcpu->arch.cop0)); 628c2ecf20Sopenharmony_ci kvm_arch_vcpu_dump_regs(vcpu); 638c2ecf20Sopenharmony_ci vcpu->run->exit_reason = KVM_EXIT_INTERNAL_ERROR; 648c2ecf20Sopenharmony_ci return RESUME_HOST; 658c2ecf20Sopenharmony_ci} 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_cistatic int kvm_trap_emul_handle_cop_unusable(struct kvm_vcpu *vcpu) 688c2ecf20Sopenharmony_ci{ 698c2ecf20Sopenharmony_ci struct mips_coproc *cop0 = vcpu->arch.cop0; 708c2ecf20Sopenharmony_ci u32 __user *opc = (u32 __user *) vcpu->arch.pc; 718c2ecf20Sopenharmony_ci u32 cause = vcpu->arch.host_cp0_cause; 728c2ecf20Sopenharmony_ci enum emulation_result er = EMULATE_DONE; 738c2ecf20Sopenharmony_ci int ret = RESUME_GUEST; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci if (((cause & CAUSEF_CE) >> CAUSEB_CE) == 1) { 768c2ecf20Sopenharmony_ci /* FPU Unusable */ 778c2ecf20Sopenharmony_ci if (!kvm_mips_guest_has_fpu(&vcpu->arch) || 788c2ecf20Sopenharmony_ci (kvm_read_c0_guest_status(cop0) & ST0_CU1) == 0) { 798c2ecf20Sopenharmony_ci /* 808c2ecf20Sopenharmony_ci * Unusable/no FPU in guest: 818c2ecf20Sopenharmony_ci * deliver guest COP1 Unusable Exception 828c2ecf20Sopenharmony_ci */ 838c2ecf20Sopenharmony_ci er = kvm_mips_emulate_fpu_exc(cause, opc, vcpu); 848c2ecf20Sopenharmony_ci } else { 858c2ecf20Sopenharmony_ci /* Restore FPU state */ 868c2ecf20Sopenharmony_ci kvm_own_fpu(vcpu); 878c2ecf20Sopenharmony_ci er = EMULATE_DONE; 888c2ecf20Sopenharmony_ci } 898c2ecf20Sopenharmony_ci } else { 908c2ecf20Sopenharmony_ci er = kvm_mips_emulate_inst(cause, opc, vcpu); 918c2ecf20Sopenharmony_ci } 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci switch (er) { 948c2ecf20Sopenharmony_ci case EMULATE_DONE: 958c2ecf20Sopenharmony_ci ret = RESUME_GUEST; 968c2ecf20Sopenharmony_ci break; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci case EMULATE_FAIL: 998c2ecf20Sopenharmony_ci vcpu->run->exit_reason = KVM_EXIT_INTERNAL_ERROR; 1008c2ecf20Sopenharmony_ci ret = RESUME_HOST; 1018c2ecf20Sopenharmony_ci break; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci case EMULATE_WAIT: 1048c2ecf20Sopenharmony_ci vcpu->run->exit_reason = KVM_EXIT_INTR; 1058c2ecf20Sopenharmony_ci ret = RESUME_HOST; 1068c2ecf20Sopenharmony_ci break; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci case EMULATE_HYPERCALL: 1098c2ecf20Sopenharmony_ci ret = kvm_mips_handle_hypcall(vcpu); 1108c2ecf20Sopenharmony_ci break; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci default: 1138c2ecf20Sopenharmony_ci BUG(); 1148c2ecf20Sopenharmony_ci } 1158c2ecf20Sopenharmony_ci return ret; 1168c2ecf20Sopenharmony_ci} 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_cistatic int kvm_mips_bad_load(u32 cause, u32 *opc, struct kvm_vcpu *vcpu) 1198c2ecf20Sopenharmony_ci{ 1208c2ecf20Sopenharmony_ci enum emulation_result er; 1218c2ecf20Sopenharmony_ci union mips_instruction inst; 1228c2ecf20Sopenharmony_ci int err; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci /* A code fetch fault doesn't count as an MMIO */ 1258c2ecf20Sopenharmony_ci if (kvm_is_ifetch_fault(&vcpu->arch)) { 1268c2ecf20Sopenharmony_ci vcpu->run->exit_reason = KVM_EXIT_INTERNAL_ERROR; 1278c2ecf20Sopenharmony_ci return RESUME_HOST; 1288c2ecf20Sopenharmony_ci } 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci /* Fetch the instruction. */ 1318c2ecf20Sopenharmony_ci if (cause & CAUSEF_BD) 1328c2ecf20Sopenharmony_ci opc += 1; 1338c2ecf20Sopenharmony_ci err = kvm_get_badinstr(opc, vcpu, &inst.word); 1348c2ecf20Sopenharmony_ci if (err) { 1358c2ecf20Sopenharmony_ci vcpu->run->exit_reason = KVM_EXIT_INTERNAL_ERROR; 1368c2ecf20Sopenharmony_ci return RESUME_HOST; 1378c2ecf20Sopenharmony_ci } 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci /* Emulate the load */ 1408c2ecf20Sopenharmony_ci er = kvm_mips_emulate_load(inst, cause, vcpu); 1418c2ecf20Sopenharmony_ci if (er == EMULATE_FAIL) { 1428c2ecf20Sopenharmony_ci kvm_err("Emulate load from MMIO space failed\n"); 1438c2ecf20Sopenharmony_ci vcpu->run->exit_reason = KVM_EXIT_INTERNAL_ERROR; 1448c2ecf20Sopenharmony_ci } else { 1458c2ecf20Sopenharmony_ci vcpu->run->exit_reason = KVM_EXIT_MMIO; 1468c2ecf20Sopenharmony_ci } 1478c2ecf20Sopenharmony_ci return RESUME_HOST; 1488c2ecf20Sopenharmony_ci} 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_cistatic int kvm_mips_bad_store(u32 cause, u32 *opc, struct kvm_vcpu *vcpu) 1518c2ecf20Sopenharmony_ci{ 1528c2ecf20Sopenharmony_ci enum emulation_result er; 1538c2ecf20Sopenharmony_ci union mips_instruction inst; 1548c2ecf20Sopenharmony_ci int err; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci /* Fetch the instruction. */ 1578c2ecf20Sopenharmony_ci if (cause & CAUSEF_BD) 1588c2ecf20Sopenharmony_ci opc += 1; 1598c2ecf20Sopenharmony_ci err = kvm_get_badinstr(opc, vcpu, &inst.word); 1608c2ecf20Sopenharmony_ci if (err) { 1618c2ecf20Sopenharmony_ci vcpu->run->exit_reason = KVM_EXIT_INTERNAL_ERROR; 1628c2ecf20Sopenharmony_ci return RESUME_HOST; 1638c2ecf20Sopenharmony_ci } 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci /* Emulate the store */ 1668c2ecf20Sopenharmony_ci er = kvm_mips_emulate_store(inst, cause, vcpu); 1678c2ecf20Sopenharmony_ci if (er == EMULATE_FAIL) { 1688c2ecf20Sopenharmony_ci kvm_err("Emulate store to MMIO space failed\n"); 1698c2ecf20Sopenharmony_ci vcpu->run->exit_reason = KVM_EXIT_INTERNAL_ERROR; 1708c2ecf20Sopenharmony_ci } else { 1718c2ecf20Sopenharmony_ci vcpu->run->exit_reason = KVM_EXIT_MMIO; 1728c2ecf20Sopenharmony_ci } 1738c2ecf20Sopenharmony_ci return RESUME_HOST; 1748c2ecf20Sopenharmony_ci} 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_cistatic int kvm_mips_bad_access(u32 cause, u32 *opc, 1778c2ecf20Sopenharmony_ci struct kvm_vcpu *vcpu, bool store) 1788c2ecf20Sopenharmony_ci{ 1798c2ecf20Sopenharmony_ci if (store) 1808c2ecf20Sopenharmony_ci return kvm_mips_bad_store(cause, opc, vcpu); 1818c2ecf20Sopenharmony_ci else 1828c2ecf20Sopenharmony_ci return kvm_mips_bad_load(cause, opc, vcpu); 1838c2ecf20Sopenharmony_ci} 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_cistatic int kvm_trap_emul_handle_tlb_mod(struct kvm_vcpu *vcpu) 1868c2ecf20Sopenharmony_ci{ 1878c2ecf20Sopenharmony_ci struct mips_coproc *cop0 = vcpu->arch.cop0; 1888c2ecf20Sopenharmony_ci u32 __user *opc = (u32 __user *) vcpu->arch.pc; 1898c2ecf20Sopenharmony_ci unsigned long badvaddr = vcpu->arch.host_cp0_badvaddr; 1908c2ecf20Sopenharmony_ci u32 cause = vcpu->arch.host_cp0_cause; 1918c2ecf20Sopenharmony_ci struct kvm_mips_tlb *tlb; 1928c2ecf20Sopenharmony_ci unsigned long entryhi; 1938c2ecf20Sopenharmony_ci int index; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci if (KVM_GUEST_KSEGX(badvaddr) < KVM_GUEST_KSEG0 1968c2ecf20Sopenharmony_ci || KVM_GUEST_KSEGX(badvaddr) == KVM_GUEST_KSEG23) { 1978c2ecf20Sopenharmony_ci /* 1988c2ecf20Sopenharmony_ci * First find the mapping in the guest TLB. If the failure to 1998c2ecf20Sopenharmony_ci * write was due to the guest TLB, it should be up to the guest 2008c2ecf20Sopenharmony_ci * to handle it. 2018c2ecf20Sopenharmony_ci */ 2028c2ecf20Sopenharmony_ci entryhi = (badvaddr & VPN2_MASK) | 2038c2ecf20Sopenharmony_ci (kvm_read_c0_guest_entryhi(cop0) & KVM_ENTRYHI_ASID); 2048c2ecf20Sopenharmony_ci index = kvm_mips_guest_tlb_lookup(vcpu, entryhi); 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci /* 2078c2ecf20Sopenharmony_ci * These should never happen. 2088c2ecf20Sopenharmony_ci * They would indicate stale host TLB entries. 2098c2ecf20Sopenharmony_ci */ 2108c2ecf20Sopenharmony_ci if (unlikely(index < 0)) { 2118c2ecf20Sopenharmony_ci vcpu->run->exit_reason = KVM_EXIT_INTERNAL_ERROR; 2128c2ecf20Sopenharmony_ci return RESUME_HOST; 2138c2ecf20Sopenharmony_ci } 2148c2ecf20Sopenharmony_ci tlb = vcpu->arch.guest_tlb + index; 2158c2ecf20Sopenharmony_ci if (unlikely(!TLB_IS_VALID(*tlb, badvaddr))) { 2168c2ecf20Sopenharmony_ci vcpu->run->exit_reason = KVM_EXIT_INTERNAL_ERROR; 2178c2ecf20Sopenharmony_ci return RESUME_HOST; 2188c2ecf20Sopenharmony_ci } 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci /* 2218c2ecf20Sopenharmony_ci * Guest entry not dirty? That would explain the TLB modified 2228c2ecf20Sopenharmony_ci * exception. Relay that on to the guest so it can handle it. 2238c2ecf20Sopenharmony_ci */ 2248c2ecf20Sopenharmony_ci if (!TLB_IS_DIRTY(*tlb, badvaddr)) { 2258c2ecf20Sopenharmony_ci kvm_mips_emulate_tlbmod(cause, opc, vcpu); 2268c2ecf20Sopenharmony_ci return RESUME_GUEST; 2278c2ecf20Sopenharmony_ci } 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci if (kvm_mips_handle_mapped_seg_tlb_fault(vcpu, tlb, badvaddr, 2308c2ecf20Sopenharmony_ci true)) 2318c2ecf20Sopenharmony_ci /* Not writable, needs handling as MMIO */ 2328c2ecf20Sopenharmony_ci return kvm_mips_bad_store(cause, opc, vcpu); 2338c2ecf20Sopenharmony_ci return RESUME_GUEST; 2348c2ecf20Sopenharmony_ci } else if (KVM_GUEST_KSEGX(badvaddr) == KVM_GUEST_KSEG0) { 2358c2ecf20Sopenharmony_ci if (kvm_mips_handle_kseg0_tlb_fault(badvaddr, vcpu, true) < 0) 2368c2ecf20Sopenharmony_ci /* Not writable, needs handling as MMIO */ 2378c2ecf20Sopenharmony_ci return kvm_mips_bad_store(cause, opc, vcpu); 2388c2ecf20Sopenharmony_ci return RESUME_GUEST; 2398c2ecf20Sopenharmony_ci } else { 2408c2ecf20Sopenharmony_ci /* host kernel addresses are all handled as MMIO */ 2418c2ecf20Sopenharmony_ci return kvm_mips_bad_store(cause, opc, vcpu); 2428c2ecf20Sopenharmony_ci } 2438c2ecf20Sopenharmony_ci} 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_cistatic int kvm_trap_emul_handle_tlb_miss(struct kvm_vcpu *vcpu, bool store) 2468c2ecf20Sopenharmony_ci{ 2478c2ecf20Sopenharmony_ci struct kvm_run *run = vcpu->run; 2488c2ecf20Sopenharmony_ci u32 __user *opc = (u32 __user *) vcpu->arch.pc; 2498c2ecf20Sopenharmony_ci unsigned long badvaddr = vcpu->arch.host_cp0_badvaddr; 2508c2ecf20Sopenharmony_ci u32 cause = vcpu->arch.host_cp0_cause; 2518c2ecf20Sopenharmony_ci enum emulation_result er = EMULATE_DONE; 2528c2ecf20Sopenharmony_ci int ret = RESUME_GUEST; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci if (((badvaddr & PAGE_MASK) == KVM_GUEST_COMMPAGE_ADDR) 2558c2ecf20Sopenharmony_ci && KVM_GUEST_KERNEL_MODE(vcpu)) { 2568c2ecf20Sopenharmony_ci if (kvm_mips_handle_commpage_tlb_fault(badvaddr, vcpu) < 0) { 2578c2ecf20Sopenharmony_ci run->exit_reason = KVM_EXIT_INTERNAL_ERROR; 2588c2ecf20Sopenharmony_ci ret = RESUME_HOST; 2598c2ecf20Sopenharmony_ci } 2608c2ecf20Sopenharmony_ci } else if (KVM_GUEST_KSEGX(badvaddr) < KVM_GUEST_KSEG0 2618c2ecf20Sopenharmony_ci || KVM_GUEST_KSEGX(badvaddr) == KVM_GUEST_KSEG23) { 2628c2ecf20Sopenharmony_ci kvm_debug("USER ADDR TLB %s fault: cause %#x, PC: %p, BadVaddr: %#lx\n", 2638c2ecf20Sopenharmony_ci store ? "ST" : "LD", cause, opc, badvaddr); 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci /* 2668c2ecf20Sopenharmony_ci * User Address (UA) fault, this could happen if 2678c2ecf20Sopenharmony_ci * (1) TLB entry not present/valid in both Guest and shadow host 2688c2ecf20Sopenharmony_ci * TLBs, in this case we pass on the fault to the guest 2698c2ecf20Sopenharmony_ci * kernel and let it handle it. 2708c2ecf20Sopenharmony_ci * (2) TLB entry is present in the Guest TLB but not in the 2718c2ecf20Sopenharmony_ci * shadow, in this case we inject the TLB from the Guest TLB 2728c2ecf20Sopenharmony_ci * into the shadow host TLB 2738c2ecf20Sopenharmony_ci */ 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci er = kvm_mips_handle_tlbmiss(cause, opc, vcpu, store); 2768c2ecf20Sopenharmony_ci if (er == EMULATE_DONE) 2778c2ecf20Sopenharmony_ci ret = RESUME_GUEST; 2788c2ecf20Sopenharmony_ci else { 2798c2ecf20Sopenharmony_ci run->exit_reason = KVM_EXIT_INTERNAL_ERROR; 2808c2ecf20Sopenharmony_ci ret = RESUME_HOST; 2818c2ecf20Sopenharmony_ci } 2828c2ecf20Sopenharmony_ci } else if (KVM_GUEST_KSEGX(badvaddr) == KVM_GUEST_KSEG0) { 2838c2ecf20Sopenharmony_ci /* 2848c2ecf20Sopenharmony_ci * All KSEG0 faults are handled by KVM, as the guest kernel does 2858c2ecf20Sopenharmony_ci * not expect to ever get them 2868c2ecf20Sopenharmony_ci */ 2878c2ecf20Sopenharmony_ci if (kvm_mips_handle_kseg0_tlb_fault(badvaddr, vcpu, store) < 0) 2888c2ecf20Sopenharmony_ci ret = kvm_mips_bad_access(cause, opc, vcpu, store); 2898c2ecf20Sopenharmony_ci } else if (KVM_GUEST_KERNEL_MODE(vcpu) 2908c2ecf20Sopenharmony_ci && (KSEGX(badvaddr) == CKSEG0 || KSEGX(badvaddr) == CKSEG1)) { 2918c2ecf20Sopenharmony_ci /* 2928c2ecf20Sopenharmony_ci * With EVA we may get a TLB exception instead of an address 2938c2ecf20Sopenharmony_ci * error when the guest performs MMIO to KSeg1 addresses. 2948c2ecf20Sopenharmony_ci */ 2958c2ecf20Sopenharmony_ci ret = kvm_mips_bad_access(cause, opc, vcpu, store); 2968c2ecf20Sopenharmony_ci } else { 2978c2ecf20Sopenharmony_ci kvm_err("Illegal TLB %s fault address , cause %#x, PC: %p, BadVaddr: %#lx\n", 2988c2ecf20Sopenharmony_ci store ? "ST" : "LD", cause, opc, badvaddr); 2998c2ecf20Sopenharmony_ci kvm_mips_dump_host_tlbs(); 3008c2ecf20Sopenharmony_ci kvm_arch_vcpu_dump_regs(vcpu); 3018c2ecf20Sopenharmony_ci run->exit_reason = KVM_EXIT_INTERNAL_ERROR; 3028c2ecf20Sopenharmony_ci ret = RESUME_HOST; 3038c2ecf20Sopenharmony_ci } 3048c2ecf20Sopenharmony_ci return ret; 3058c2ecf20Sopenharmony_ci} 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_cistatic int kvm_trap_emul_handle_tlb_st_miss(struct kvm_vcpu *vcpu) 3088c2ecf20Sopenharmony_ci{ 3098c2ecf20Sopenharmony_ci return kvm_trap_emul_handle_tlb_miss(vcpu, true); 3108c2ecf20Sopenharmony_ci} 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_cistatic int kvm_trap_emul_handle_tlb_ld_miss(struct kvm_vcpu *vcpu) 3138c2ecf20Sopenharmony_ci{ 3148c2ecf20Sopenharmony_ci return kvm_trap_emul_handle_tlb_miss(vcpu, false); 3158c2ecf20Sopenharmony_ci} 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_cistatic int kvm_trap_emul_handle_addr_err_st(struct kvm_vcpu *vcpu) 3188c2ecf20Sopenharmony_ci{ 3198c2ecf20Sopenharmony_ci u32 __user *opc = (u32 __user *) vcpu->arch.pc; 3208c2ecf20Sopenharmony_ci unsigned long badvaddr = vcpu->arch.host_cp0_badvaddr; 3218c2ecf20Sopenharmony_ci u32 cause = vcpu->arch.host_cp0_cause; 3228c2ecf20Sopenharmony_ci int ret = RESUME_GUEST; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci if (KVM_GUEST_KERNEL_MODE(vcpu) 3258c2ecf20Sopenharmony_ci && (KSEGX(badvaddr) == CKSEG0 || KSEGX(badvaddr) == CKSEG1)) { 3268c2ecf20Sopenharmony_ci ret = kvm_mips_bad_store(cause, opc, vcpu); 3278c2ecf20Sopenharmony_ci } else { 3288c2ecf20Sopenharmony_ci kvm_err("Address Error (STORE): cause %#x, PC: %p, BadVaddr: %#lx\n", 3298c2ecf20Sopenharmony_ci cause, opc, badvaddr); 3308c2ecf20Sopenharmony_ci vcpu->run->exit_reason = KVM_EXIT_INTERNAL_ERROR; 3318c2ecf20Sopenharmony_ci ret = RESUME_HOST; 3328c2ecf20Sopenharmony_ci } 3338c2ecf20Sopenharmony_ci return ret; 3348c2ecf20Sopenharmony_ci} 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_cistatic int kvm_trap_emul_handle_addr_err_ld(struct kvm_vcpu *vcpu) 3378c2ecf20Sopenharmony_ci{ 3388c2ecf20Sopenharmony_ci u32 __user *opc = (u32 __user *) vcpu->arch.pc; 3398c2ecf20Sopenharmony_ci unsigned long badvaddr = vcpu->arch.host_cp0_badvaddr; 3408c2ecf20Sopenharmony_ci u32 cause = vcpu->arch.host_cp0_cause; 3418c2ecf20Sopenharmony_ci int ret = RESUME_GUEST; 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci if (KSEGX(badvaddr) == CKSEG0 || KSEGX(badvaddr) == CKSEG1) { 3448c2ecf20Sopenharmony_ci ret = kvm_mips_bad_load(cause, opc, vcpu); 3458c2ecf20Sopenharmony_ci } else { 3468c2ecf20Sopenharmony_ci kvm_err("Address Error (LOAD): cause %#x, PC: %p, BadVaddr: %#lx\n", 3478c2ecf20Sopenharmony_ci cause, opc, badvaddr); 3488c2ecf20Sopenharmony_ci vcpu->run->exit_reason = KVM_EXIT_INTERNAL_ERROR; 3498c2ecf20Sopenharmony_ci ret = RESUME_HOST; 3508c2ecf20Sopenharmony_ci } 3518c2ecf20Sopenharmony_ci return ret; 3528c2ecf20Sopenharmony_ci} 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_cistatic int kvm_trap_emul_handle_syscall(struct kvm_vcpu *vcpu) 3558c2ecf20Sopenharmony_ci{ 3568c2ecf20Sopenharmony_ci u32 __user *opc = (u32 __user *) vcpu->arch.pc; 3578c2ecf20Sopenharmony_ci u32 cause = vcpu->arch.host_cp0_cause; 3588c2ecf20Sopenharmony_ci enum emulation_result er = EMULATE_DONE; 3598c2ecf20Sopenharmony_ci int ret = RESUME_GUEST; 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci er = kvm_mips_emulate_syscall(cause, opc, vcpu); 3628c2ecf20Sopenharmony_ci if (er == EMULATE_DONE) 3638c2ecf20Sopenharmony_ci ret = RESUME_GUEST; 3648c2ecf20Sopenharmony_ci else { 3658c2ecf20Sopenharmony_ci vcpu->run->exit_reason = KVM_EXIT_INTERNAL_ERROR; 3668c2ecf20Sopenharmony_ci ret = RESUME_HOST; 3678c2ecf20Sopenharmony_ci } 3688c2ecf20Sopenharmony_ci return ret; 3698c2ecf20Sopenharmony_ci} 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_cistatic int kvm_trap_emul_handle_res_inst(struct kvm_vcpu *vcpu) 3728c2ecf20Sopenharmony_ci{ 3738c2ecf20Sopenharmony_ci u32 __user *opc = (u32 __user *) vcpu->arch.pc; 3748c2ecf20Sopenharmony_ci u32 cause = vcpu->arch.host_cp0_cause; 3758c2ecf20Sopenharmony_ci enum emulation_result er = EMULATE_DONE; 3768c2ecf20Sopenharmony_ci int ret = RESUME_GUEST; 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci er = kvm_mips_handle_ri(cause, opc, vcpu); 3798c2ecf20Sopenharmony_ci if (er == EMULATE_DONE) 3808c2ecf20Sopenharmony_ci ret = RESUME_GUEST; 3818c2ecf20Sopenharmony_ci else { 3828c2ecf20Sopenharmony_ci vcpu->run->exit_reason = KVM_EXIT_INTERNAL_ERROR; 3838c2ecf20Sopenharmony_ci ret = RESUME_HOST; 3848c2ecf20Sopenharmony_ci } 3858c2ecf20Sopenharmony_ci return ret; 3868c2ecf20Sopenharmony_ci} 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_cistatic int kvm_trap_emul_handle_break(struct kvm_vcpu *vcpu) 3898c2ecf20Sopenharmony_ci{ 3908c2ecf20Sopenharmony_ci u32 __user *opc = (u32 __user *) vcpu->arch.pc; 3918c2ecf20Sopenharmony_ci u32 cause = vcpu->arch.host_cp0_cause; 3928c2ecf20Sopenharmony_ci enum emulation_result er = EMULATE_DONE; 3938c2ecf20Sopenharmony_ci int ret = RESUME_GUEST; 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci er = kvm_mips_emulate_bp_exc(cause, opc, vcpu); 3968c2ecf20Sopenharmony_ci if (er == EMULATE_DONE) 3978c2ecf20Sopenharmony_ci ret = RESUME_GUEST; 3988c2ecf20Sopenharmony_ci else { 3998c2ecf20Sopenharmony_ci vcpu->run->exit_reason = KVM_EXIT_INTERNAL_ERROR; 4008c2ecf20Sopenharmony_ci ret = RESUME_HOST; 4018c2ecf20Sopenharmony_ci } 4028c2ecf20Sopenharmony_ci return ret; 4038c2ecf20Sopenharmony_ci} 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_cistatic int kvm_trap_emul_handle_trap(struct kvm_vcpu *vcpu) 4068c2ecf20Sopenharmony_ci{ 4078c2ecf20Sopenharmony_ci u32 __user *opc = (u32 __user *)vcpu->arch.pc; 4088c2ecf20Sopenharmony_ci u32 cause = vcpu->arch.host_cp0_cause; 4098c2ecf20Sopenharmony_ci enum emulation_result er = EMULATE_DONE; 4108c2ecf20Sopenharmony_ci int ret = RESUME_GUEST; 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci er = kvm_mips_emulate_trap_exc(cause, opc, vcpu); 4138c2ecf20Sopenharmony_ci if (er == EMULATE_DONE) { 4148c2ecf20Sopenharmony_ci ret = RESUME_GUEST; 4158c2ecf20Sopenharmony_ci } else { 4168c2ecf20Sopenharmony_ci vcpu->run->exit_reason = KVM_EXIT_INTERNAL_ERROR; 4178c2ecf20Sopenharmony_ci ret = RESUME_HOST; 4188c2ecf20Sopenharmony_ci } 4198c2ecf20Sopenharmony_ci return ret; 4208c2ecf20Sopenharmony_ci} 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_cistatic int kvm_trap_emul_handle_msa_fpe(struct kvm_vcpu *vcpu) 4238c2ecf20Sopenharmony_ci{ 4248c2ecf20Sopenharmony_ci u32 __user *opc = (u32 __user *)vcpu->arch.pc; 4258c2ecf20Sopenharmony_ci u32 cause = vcpu->arch.host_cp0_cause; 4268c2ecf20Sopenharmony_ci enum emulation_result er = EMULATE_DONE; 4278c2ecf20Sopenharmony_ci int ret = RESUME_GUEST; 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci er = kvm_mips_emulate_msafpe_exc(cause, opc, vcpu); 4308c2ecf20Sopenharmony_ci if (er == EMULATE_DONE) { 4318c2ecf20Sopenharmony_ci ret = RESUME_GUEST; 4328c2ecf20Sopenharmony_ci } else { 4338c2ecf20Sopenharmony_ci vcpu->run->exit_reason = KVM_EXIT_INTERNAL_ERROR; 4348c2ecf20Sopenharmony_ci ret = RESUME_HOST; 4358c2ecf20Sopenharmony_ci } 4368c2ecf20Sopenharmony_ci return ret; 4378c2ecf20Sopenharmony_ci} 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_cistatic int kvm_trap_emul_handle_fpe(struct kvm_vcpu *vcpu) 4408c2ecf20Sopenharmony_ci{ 4418c2ecf20Sopenharmony_ci u32 __user *opc = (u32 __user *)vcpu->arch.pc; 4428c2ecf20Sopenharmony_ci u32 cause = vcpu->arch.host_cp0_cause; 4438c2ecf20Sopenharmony_ci enum emulation_result er = EMULATE_DONE; 4448c2ecf20Sopenharmony_ci int ret = RESUME_GUEST; 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci er = kvm_mips_emulate_fpe_exc(cause, opc, vcpu); 4478c2ecf20Sopenharmony_ci if (er == EMULATE_DONE) { 4488c2ecf20Sopenharmony_ci ret = RESUME_GUEST; 4498c2ecf20Sopenharmony_ci } else { 4508c2ecf20Sopenharmony_ci vcpu->run->exit_reason = KVM_EXIT_INTERNAL_ERROR; 4518c2ecf20Sopenharmony_ci ret = RESUME_HOST; 4528c2ecf20Sopenharmony_ci } 4538c2ecf20Sopenharmony_ci return ret; 4548c2ecf20Sopenharmony_ci} 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci/** 4578c2ecf20Sopenharmony_ci * kvm_trap_emul_handle_msa_disabled() - Guest used MSA while disabled in root. 4588c2ecf20Sopenharmony_ci * @vcpu: Virtual CPU context. 4598c2ecf20Sopenharmony_ci * 4608c2ecf20Sopenharmony_ci * Handle when the guest attempts to use MSA when it is disabled. 4618c2ecf20Sopenharmony_ci */ 4628c2ecf20Sopenharmony_cistatic int kvm_trap_emul_handle_msa_disabled(struct kvm_vcpu *vcpu) 4638c2ecf20Sopenharmony_ci{ 4648c2ecf20Sopenharmony_ci struct mips_coproc *cop0 = vcpu->arch.cop0; 4658c2ecf20Sopenharmony_ci u32 __user *opc = (u32 __user *) vcpu->arch.pc; 4668c2ecf20Sopenharmony_ci u32 cause = vcpu->arch.host_cp0_cause; 4678c2ecf20Sopenharmony_ci enum emulation_result er = EMULATE_DONE; 4688c2ecf20Sopenharmony_ci int ret = RESUME_GUEST; 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci if (!kvm_mips_guest_has_msa(&vcpu->arch) || 4718c2ecf20Sopenharmony_ci (kvm_read_c0_guest_status(cop0) & (ST0_CU1 | ST0_FR)) == ST0_CU1) { 4728c2ecf20Sopenharmony_ci /* 4738c2ecf20Sopenharmony_ci * No MSA in guest, or FPU enabled and not in FR=1 mode, 4748c2ecf20Sopenharmony_ci * guest reserved instruction exception 4758c2ecf20Sopenharmony_ci */ 4768c2ecf20Sopenharmony_ci er = kvm_mips_emulate_ri_exc(cause, opc, vcpu); 4778c2ecf20Sopenharmony_ci } else if (!(kvm_read_c0_guest_config5(cop0) & MIPS_CONF5_MSAEN)) { 4788c2ecf20Sopenharmony_ci /* MSA disabled by guest, guest MSA disabled exception */ 4798c2ecf20Sopenharmony_ci er = kvm_mips_emulate_msadis_exc(cause, opc, vcpu); 4808c2ecf20Sopenharmony_ci } else { 4818c2ecf20Sopenharmony_ci /* Restore MSA/FPU state */ 4828c2ecf20Sopenharmony_ci kvm_own_msa(vcpu); 4838c2ecf20Sopenharmony_ci er = EMULATE_DONE; 4848c2ecf20Sopenharmony_ci } 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci switch (er) { 4878c2ecf20Sopenharmony_ci case EMULATE_DONE: 4888c2ecf20Sopenharmony_ci ret = RESUME_GUEST; 4898c2ecf20Sopenharmony_ci break; 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci case EMULATE_FAIL: 4928c2ecf20Sopenharmony_ci vcpu->run->exit_reason = KVM_EXIT_INTERNAL_ERROR; 4938c2ecf20Sopenharmony_ci ret = RESUME_HOST; 4948c2ecf20Sopenharmony_ci break; 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci default: 4978c2ecf20Sopenharmony_ci BUG(); 4988c2ecf20Sopenharmony_ci } 4998c2ecf20Sopenharmony_ci return ret; 5008c2ecf20Sopenharmony_ci} 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_cistatic int kvm_trap_emul_hardware_enable(void) 5038c2ecf20Sopenharmony_ci{ 5048c2ecf20Sopenharmony_ci return 0; 5058c2ecf20Sopenharmony_ci} 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_cistatic void kvm_trap_emul_hardware_disable(void) 5088c2ecf20Sopenharmony_ci{ 5098c2ecf20Sopenharmony_ci} 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_cistatic int kvm_trap_emul_check_extension(struct kvm *kvm, long ext) 5128c2ecf20Sopenharmony_ci{ 5138c2ecf20Sopenharmony_ci int r; 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci switch (ext) { 5168c2ecf20Sopenharmony_ci case KVM_CAP_MIPS_TE: 5178c2ecf20Sopenharmony_ci r = 1; 5188c2ecf20Sopenharmony_ci break; 5198c2ecf20Sopenharmony_ci case KVM_CAP_IOEVENTFD: 5208c2ecf20Sopenharmony_ci r = 1; 5218c2ecf20Sopenharmony_ci break; 5228c2ecf20Sopenharmony_ci default: 5238c2ecf20Sopenharmony_ci r = 0; 5248c2ecf20Sopenharmony_ci break; 5258c2ecf20Sopenharmony_ci } 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci return r; 5288c2ecf20Sopenharmony_ci} 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_cistatic int kvm_trap_emul_vcpu_init(struct kvm_vcpu *vcpu) 5318c2ecf20Sopenharmony_ci{ 5328c2ecf20Sopenharmony_ci struct mm_struct *kern_mm = &vcpu->arch.guest_kernel_mm; 5338c2ecf20Sopenharmony_ci struct mm_struct *user_mm = &vcpu->arch.guest_user_mm; 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci /* 5368c2ecf20Sopenharmony_ci * Allocate GVA -> HPA page tables. 5378c2ecf20Sopenharmony_ci * MIPS doesn't use the mm_struct pointer argument. 5388c2ecf20Sopenharmony_ci */ 5398c2ecf20Sopenharmony_ci kern_mm->pgd = pgd_alloc(kern_mm); 5408c2ecf20Sopenharmony_ci if (!kern_mm->pgd) 5418c2ecf20Sopenharmony_ci return -ENOMEM; 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci user_mm->pgd = pgd_alloc(user_mm); 5448c2ecf20Sopenharmony_ci if (!user_mm->pgd) { 5458c2ecf20Sopenharmony_ci pgd_free(kern_mm, kern_mm->pgd); 5468c2ecf20Sopenharmony_ci return -ENOMEM; 5478c2ecf20Sopenharmony_ci } 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci return 0; 5508c2ecf20Sopenharmony_ci} 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_cistatic void kvm_mips_emul_free_gva_pt(pgd_t *pgd) 5538c2ecf20Sopenharmony_ci{ 5548c2ecf20Sopenharmony_ci /* Don't free host kernel page tables copied from init_mm.pgd */ 5558c2ecf20Sopenharmony_ci const unsigned long end = 0x80000000; 5568c2ecf20Sopenharmony_ci unsigned long pgd_va, pud_va, pmd_va; 5578c2ecf20Sopenharmony_ci p4d_t *p4d; 5588c2ecf20Sopenharmony_ci pud_t *pud; 5598c2ecf20Sopenharmony_ci pmd_t *pmd; 5608c2ecf20Sopenharmony_ci pte_t *pte; 5618c2ecf20Sopenharmony_ci int i, j, k; 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci for (i = 0; i < USER_PTRS_PER_PGD; i++) { 5648c2ecf20Sopenharmony_ci if (pgd_none(pgd[i])) 5658c2ecf20Sopenharmony_ci continue; 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci pgd_va = (unsigned long)i << PGDIR_SHIFT; 5688c2ecf20Sopenharmony_ci if (pgd_va >= end) 5698c2ecf20Sopenharmony_ci break; 5708c2ecf20Sopenharmony_ci p4d = p4d_offset(pgd, 0); 5718c2ecf20Sopenharmony_ci pud = pud_offset(p4d + i, 0); 5728c2ecf20Sopenharmony_ci for (j = 0; j < PTRS_PER_PUD; j++) { 5738c2ecf20Sopenharmony_ci if (pud_none(pud[j])) 5748c2ecf20Sopenharmony_ci continue; 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci pud_va = pgd_va | ((unsigned long)j << PUD_SHIFT); 5778c2ecf20Sopenharmony_ci if (pud_va >= end) 5788c2ecf20Sopenharmony_ci break; 5798c2ecf20Sopenharmony_ci pmd = pmd_offset(pud + j, 0); 5808c2ecf20Sopenharmony_ci for (k = 0; k < PTRS_PER_PMD; k++) { 5818c2ecf20Sopenharmony_ci if (pmd_none(pmd[k])) 5828c2ecf20Sopenharmony_ci continue; 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci pmd_va = pud_va | (k << PMD_SHIFT); 5858c2ecf20Sopenharmony_ci if (pmd_va >= end) 5868c2ecf20Sopenharmony_ci break; 5878c2ecf20Sopenharmony_ci pte = pte_offset_kernel(pmd + k, 0); 5888c2ecf20Sopenharmony_ci pte_free_kernel(NULL, pte); 5898c2ecf20Sopenharmony_ci } 5908c2ecf20Sopenharmony_ci pmd_free(NULL, pmd); 5918c2ecf20Sopenharmony_ci } 5928c2ecf20Sopenharmony_ci pud_free(NULL, pud); 5938c2ecf20Sopenharmony_ci } 5948c2ecf20Sopenharmony_ci pgd_free(NULL, pgd); 5958c2ecf20Sopenharmony_ci} 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_cistatic void kvm_trap_emul_vcpu_uninit(struct kvm_vcpu *vcpu) 5988c2ecf20Sopenharmony_ci{ 5998c2ecf20Sopenharmony_ci kvm_mips_emul_free_gva_pt(vcpu->arch.guest_kernel_mm.pgd); 6008c2ecf20Sopenharmony_ci kvm_mips_emul_free_gva_pt(vcpu->arch.guest_user_mm.pgd); 6018c2ecf20Sopenharmony_ci} 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_cistatic int kvm_trap_emul_vcpu_setup(struct kvm_vcpu *vcpu) 6048c2ecf20Sopenharmony_ci{ 6058c2ecf20Sopenharmony_ci struct mips_coproc *cop0 = vcpu->arch.cop0; 6068c2ecf20Sopenharmony_ci u32 config, config1; 6078c2ecf20Sopenharmony_ci int vcpu_id = vcpu->vcpu_id; 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci /* Start off the timer at 100 MHz */ 6108c2ecf20Sopenharmony_ci kvm_mips_init_count(vcpu, 100*1000*1000); 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci /* 6138c2ecf20Sopenharmony_ci * Arch specific stuff, set up config registers properly so that the 6148c2ecf20Sopenharmony_ci * guest will come up as expected 6158c2ecf20Sopenharmony_ci */ 6168c2ecf20Sopenharmony_ci#ifndef CONFIG_CPU_MIPSR6 6178c2ecf20Sopenharmony_ci /* r2-r5, simulate a MIPS 24kc */ 6188c2ecf20Sopenharmony_ci kvm_write_c0_guest_prid(cop0, 0x00019300); 6198c2ecf20Sopenharmony_ci#else 6208c2ecf20Sopenharmony_ci /* r6+, simulate a generic QEMU machine */ 6218c2ecf20Sopenharmony_ci kvm_write_c0_guest_prid(cop0, 0x00010000); 6228c2ecf20Sopenharmony_ci#endif 6238c2ecf20Sopenharmony_ci /* 6248c2ecf20Sopenharmony_ci * Have config1, Cacheable, noncoherent, write-back, write allocate. 6258c2ecf20Sopenharmony_ci * Endianness, arch revision & virtually tagged icache should match 6268c2ecf20Sopenharmony_ci * host. 6278c2ecf20Sopenharmony_ci */ 6288c2ecf20Sopenharmony_ci config = read_c0_config() & MIPS_CONF_AR; 6298c2ecf20Sopenharmony_ci config |= MIPS_CONF_M | CONF_CM_CACHABLE_NONCOHERENT | MIPS_CONF_MT_TLB; 6308c2ecf20Sopenharmony_ci#ifdef CONFIG_CPU_BIG_ENDIAN 6318c2ecf20Sopenharmony_ci config |= CONF_BE; 6328c2ecf20Sopenharmony_ci#endif 6338c2ecf20Sopenharmony_ci if (cpu_has_vtag_icache) 6348c2ecf20Sopenharmony_ci config |= MIPS_CONF_VI; 6358c2ecf20Sopenharmony_ci kvm_write_c0_guest_config(cop0, config); 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_ci /* Read the cache characteristics from the host Config1 Register */ 6388c2ecf20Sopenharmony_ci config1 = (read_c0_config1() & ~0x7f); 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci /* DCache line size not correctly reported in Config1 on Octeon CPUs */ 6418c2ecf20Sopenharmony_ci if (cpu_dcache_line_size()) { 6428c2ecf20Sopenharmony_ci config1 &= ~MIPS_CONF1_DL; 6438c2ecf20Sopenharmony_ci config1 |= ((ilog2(cpu_dcache_line_size()) - 1) << 6448c2ecf20Sopenharmony_ci MIPS_CONF1_DL_SHF) & MIPS_CONF1_DL; 6458c2ecf20Sopenharmony_ci } 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci /* Set up MMU size */ 6488c2ecf20Sopenharmony_ci config1 &= ~(0x3f << 25); 6498c2ecf20Sopenharmony_ci config1 |= ((KVM_MIPS_GUEST_TLB_SIZE - 1) << 25); 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci /* We unset some bits that we aren't emulating */ 6528c2ecf20Sopenharmony_ci config1 &= ~(MIPS_CONF1_C2 | MIPS_CONF1_MD | MIPS_CONF1_PC | 6538c2ecf20Sopenharmony_ci MIPS_CONF1_WR | MIPS_CONF1_CA); 6548c2ecf20Sopenharmony_ci kvm_write_c0_guest_config1(cop0, config1); 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci /* Have config3, no tertiary/secondary caches implemented */ 6578c2ecf20Sopenharmony_ci kvm_write_c0_guest_config2(cop0, MIPS_CONF_M); 6588c2ecf20Sopenharmony_ci /* MIPS_CONF_M | (read_c0_config2() & 0xfff) */ 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci /* Have config4, UserLocal */ 6618c2ecf20Sopenharmony_ci kvm_write_c0_guest_config3(cop0, MIPS_CONF_M | MIPS_CONF3_ULRI); 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_ci /* Have config5 */ 6648c2ecf20Sopenharmony_ci kvm_write_c0_guest_config4(cop0, MIPS_CONF_M); 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_ci /* No config6 */ 6678c2ecf20Sopenharmony_ci kvm_write_c0_guest_config5(cop0, 0); 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ci /* Set Wait IE/IXMT Ignore in Config7, IAR, AR */ 6708c2ecf20Sopenharmony_ci kvm_write_c0_guest_config7(cop0, (MIPS_CONF7_WII) | (1 << 10)); 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci /* Status */ 6738c2ecf20Sopenharmony_ci kvm_write_c0_guest_status(cop0, ST0_BEV | ST0_ERL); 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_ci /* 6768c2ecf20Sopenharmony_ci * Setup IntCtl defaults, compatibility mode for timer interrupts (HW5) 6778c2ecf20Sopenharmony_ci */ 6788c2ecf20Sopenharmony_ci kvm_write_c0_guest_intctl(cop0, 0xFC000000); 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci /* Put in vcpu id as CPUNum into Ebase Reg to handle SMP Guests */ 6818c2ecf20Sopenharmony_ci kvm_write_c0_guest_ebase(cop0, KVM_GUEST_KSEG0 | 6828c2ecf20Sopenharmony_ci (vcpu_id & MIPS_EBASE_CPUNUM)); 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ci /* Put PC at guest reset vector */ 6858c2ecf20Sopenharmony_ci vcpu->arch.pc = KVM_GUEST_CKSEG1ADDR(0x1fc00000); 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_ci return 0; 6888c2ecf20Sopenharmony_ci} 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_cistatic void kvm_trap_emul_flush_shadow_all(struct kvm *kvm) 6918c2ecf20Sopenharmony_ci{ 6928c2ecf20Sopenharmony_ci /* Flush GVA page tables and invalidate GVA ASIDs on all VCPUs */ 6938c2ecf20Sopenharmony_ci kvm_flush_remote_tlbs(kvm); 6948c2ecf20Sopenharmony_ci} 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_cistatic void kvm_trap_emul_flush_shadow_memslot(struct kvm *kvm, 6978c2ecf20Sopenharmony_ci const struct kvm_memory_slot *slot) 6988c2ecf20Sopenharmony_ci{ 6998c2ecf20Sopenharmony_ci kvm_trap_emul_flush_shadow_all(kvm); 7008c2ecf20Sopenharmony_ci} 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_cistatic u64 kvm_trap_emul_get_one_regs[] = { 7038c2ecf20Sopenharmony_ci KVM_REG_MIPS_CP0_INDEX, 7048c2ecf20Sopenharmony_ci KVM_REG_MIPS_CP0_ENTRYLO0, 7058c2ecf20Sopenharmony_ci KVM_REG_MIPS_CP0_ENTRYLO1, 7068c2ecf20Sopenharmony_ci KVM_REG_MIPS_CP0_CONTEXT, 7078c2ecf20Sopenharmony_ci KVM_REG_MIPS_CP0_USERLOCAL, 7088c2ecf20Sopenharmony_ci KVM_REG_MIPS_CP0_PAGEMASK, 7098c2ecf20Sopenharmony_ci KVM_REG_MIPS_CP0_WIRED, 7108c2ecf20Sopenharmony_ci KVM_REG_MIPS_CP0_HWRENA, 7118c2ecf20Sopenharmony_ci KVM_REG_MIPS_CP0_BADVADDR, 7128c2ecf20Sopenharmony_ci KVM_REG_MIPS_CP0_COUNT, 7138c2ecf20Sopenharmony_ci KVM_REG_MIPS_CP0_ENTRYHI, 7148c2ecf20Sopenharmony_ci KVM_REG_MIPS_CP0_COMPARE, 7158c2ecf20Sopenharmony_ci KVM_REG_MIPS_CP0_STATUS, 7168c2ecf20Sopenharmony_ci KVM_REG_MIPS_CP0_INTCTL, 7178c2ecf20Sopenharmony_ci KVM_REG_MIPS_CP0_CAUSE, 7188c2ecf20Sopenharmony_ci KVM_REG_MIPS_CP0_EPC, 7198c2ecf20Sopenharmony_ci KVM_REG_MIPS_CP0_PRID, 7208c2ecf20Sopenharmony_ci KVM_REG_MIPS_CP0_EBASE, 7218c2ecf20Sopenharmony_ci KVM_REG_MIPS_CP0_CONFIG, 7228c2ecf20Sopenharmony_ci KVM_REG_MIPS_CP0_CONFIG1, 7238c2ecf20Sopenharmony_ci KVM_REG_MIPS_CP0_CONFIG2, 7248c2ecf20Sopenharmony_ci KVM_REG_MIPS_CP0_CONFIG3, 7258c2ecf20Sopenharmony_ci KVM_REG_MIPS_CP0_CONFIG4, 7268c2ecf20Sopenharmony_ci KVM_REG_MIPS_CP0_CONFIG5, 7278c2ecf20Sopenharmony_ci KVM_REG_MIPS_CP0_CONFIG7, 7288c2ecf20Sopenharmony_ci KVM_REG_MIPS_CP0_ERROREPC, 7298c2ecf20Sopenharmony_ci KVM_REG_MIPS_CP0_KSCRATCH1, 7308c2ecf20Sopenharmony_ci KVM_REG_MIPS_CP0_KSCRATCH2, 7318c2ecf20Sopenharmony_ci KVM_REG_MIPS_CP0_KSCRATCH3, 7328c2ecf20Sopenharmony_ci KVM_REG_MIPS_CP0_KSCRATCH4, 7338c2ecf20Sopenharmony_ci KVM_REG_MIPS_CP0_KSCRATCH5, 7348c2ecf20Sopenharmony_ci KVM_REG_MIPS_CP0_KSCRATCH6, 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_ci KVM_REG_MIPS_COUNT_CTL, 7378c2ecf20Sopenharmony_ci KVM_REG_MIPS_COUNT_RESUME, 7388c2ecf20Sopenharmony_ci KVM_REG_MIPS_COUNT_HZ, 7398c2ecf20Sopenharmony_ci}; 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_cistatic unsigned long kvm_trap_emul_num_regs(struct kvm_vcpu *vcpu) 7428c2ecf20Sopenharmony_ci{ 7438c2ecf20Sopenharmony_ci return ARRAY_SIZE(kvm_trap_emul_get_one_regs); 7448c2ecf20Sopenharmony_ci} 7458c2ecf20Sopenharmony_ci 7468c2ecf20Sopenharmony_cistatic int kvm_trap_emul_copy_reg_indices(struct kvm_vcpu *vcpu, 7478c2ecf20Sopenharmony_ci u64 __user *indices) 7488c2ecf20Sopenharmony_ci{ 7498c2ecf20Sopenharmony_ci if (copy_to_user(indices, kvm_trap_emul_get_one_regs, 7508c2ecf20Sopenharmony_ci sizeof(kvm_trap_emul_get_one_regs))) 7518c2ecf20Sopenharmony_ci return -EFAULT; 7528c2ecf20Sopenharmony_ci indices += ARRAY_SIZE(kvm_trap_emul_get_one_regs); 7538c2ecf20Sopenharmony_ci 7548c2ecf20Sopenharmony_ci return 0; 7558c2ecf20Sopenharmony_ci} 7568c2ecf20Sopenharmony_ci 7578c2ecf20Sopenharmony_cistatic int kvm_trap_emul_get_one_reg(struct kvm_vcpu *vcpu, 7588c2ecf20Sopenharmony_ci const struct kvm_one_reg *reg, 7598c2ecf20Sopenharmony_ci s64 *v) 7608c2ecf20Sopenharmony_ci{ 7618c2ecf20Sopenharmony_ci struct mips_coproc *cop0 = vcpu->arch.cop0; 7628c2ecf20Sopenharmony_ci 7638c2ecf20Sopenharmony_ci switch (reg->id) { 7648c2ecf20Sopenharmony_ci case KVM_REG_MIPS_CP0_INDEX: 7658c2ecf20Sopenharmony_ci *v = (long)kvm_read_c0_guest_index(cop0); 7668c2ecf20Sopenharmony_ci break; 7678c2ecf20Sopenharmony_ci case KVM_REG_MIPS_CP0_ENTRYLO0: 7688c2ecf20Sopenharmony_ci *v = kvm_read_c0_guest_entrylo0(cop0); 7698c2ecf20Sopenharmony_ci break; 7708c2ecf20Sopenharmony_ci case KVM_REG_MIPS_CP0_ENTRYLO1: 7718c2ecf20Sopenharmony_ci *v = kvm_read_c0_guest_entrylo1(cop0); 7728c2ecf20Sopenharmony_ci break; 7738c2ecf20Sopenharmony_ci case KVM_REG_MIPS_CP0_CONTEXT: 7748c2ecf20Sopenharmony_ci *v = (long)kvm_read_c0_guest_context(cop0); 7758c2ecf20Sopenharmony_ci break; 7768c2ecf20Sopenharmony_ci case KVM_REG_MIPS_CP0_USERLOCAL: 7778c2ecf20Sopenharmony_ci *v = (long)kvm_read_c0_guest_userlocal(cop0); 7788c2ecf20Sopenharmony_ci break; 7798c2ecf20Sopenharmony_ci case KVM_REG_MIPS_CP0_PAGEMASK: 7808c2ecf20Sopenharmony_ci *v = (long)kvm_read_c0_guest_pagemask(cop0); 7818c2ecf20Sopenharmony_ci break; 7828c2ecf20Sopenharmony_ci case KVM_REG_MIPS_CP0_WIRED: 7838c2ecf20Sopenharmony_ci *v = (long)kvm_read_c0_guest_wired(cop0); 7848c2ecf20Sopenharmony_ci break; 7858c2ecf20Sopenharmony_ci case KVM_REG_MIPS_CP0_HWRENA: 7868c2ecf20Sopenharmony_ci *v = (long)kvm_read_c0_guest_hwrena(cop0); 7878c2ecf20Sopenharmony_ci break; 7888c2ecf20Sopenharmony_ci case KVM_REG_MIPS_CP0_BADVADDR: 7898c2ecf20Sopenharmony_ci *v = (long)kvm_read_c0_guest_badvaddr(cop0); 7908c2ecf20Sopenharmony_ci break; 7918c2ecf20Sopenharmony_ci case KVM_REG_MIPS_CP0_ENTRYHI: 7928c2ecf20Sopenharmony_ci *v = (long)kvm_read_c0_guest_entryhi(cop0); 7938c2ecf20Sopenharmony_ci break; 7948c2ecf20Sopenharmony_ci case KVM_REG_MIPS_CP0_COMPARE: 7958c2ecf20Sopenharmony_ci *v = (long)kvm_read_c0_guest_compare(cop0); 7968c2ecf20Sopenharmony_ci break; 7978c2ecf20Sopenharmony_ci case KVM_REG_MIPS_CP0_STATUS: 7988c2ecf20Sopenharmony_ci *v = (long)kvm_read_c0_guest_status(cop0); 7998c2ecf20Sopenharmony_ci break; 8008c2ecf20Sopenharmony_ci case KVM_REG_MIPS_CP0_INTCTL: 8018c2ecf20Sopenharmony_ci *v = (long)kvm_read_c0_guest_intctl(cop0); 8028c2ecf20Sopenharmony_ci break; 8038c2ecf20Sopenharmony_ci case KVM_REG_MIPS_CP0_CAUSE: 8048c2ecf20Sopenharmony_ci *v = (long)kvm_read_c0_guest_cause(cop0); 8058c2ecf20Sopenharmony_ci break; 8068c2ecf20Sopenharmony_ci case KVM_REG_MIPS_CP0_EPC: 8078c2ecf20Sopenharmony_ci *v = (long)kvm_read_c0_guest_epc(cop0); 8088c2ecf20Sopenharmony_ci break; 8098c2ecf20Sopenharmony_ci case KVM_REG_MIPS_CP0_PRID: 8108c2ecf20Sopenharmony_ci *v = (long)kvm_read_c0_guest_prid(cop0); 8118c2ecf20Sopenharmony_ci break; 8128c2ecf20Sopenharmony_ci case KVM_REG_MIPS_CP0_EBASE: 8138c2ecf20Sopenharmony_ci *v = (long)kvm_read_c0_guest_ebase(cop0); 8148c2ecf20Sopenharmony_ci break; 8158c2ecf20Sopenharmony_ci case KVM_REG_MIPS_CP0_CONFIG: 8168c2ecf20Sopenharmony_ci *v = (long)kvm_read_c0_guest_config(cop0); 8178c2ecf20Sopenharmony_ci break; 8188c2ecf20Sopenharmony_ci case KVM_REG_MIPS_CP0_CONFIG1: 8198c2ecf20Sopenharmony_ci *v = (long)kvm_read_c0_guest_config1(cop0); 8208c2ecf20Sopenharmony_ci break; 8218c2ecf20Sopenharmony_ci case KVM_REG_MIPS_CP0_CONFIG2: 8228c2ecf20Sopenharmony_ci *v = (long)kvm_read_c0_guest_config2(cop0); 8238c2ecf20Sopenharmony_ci break; 8248c2ecf20Sopenharmony_ci case KVM_REG_MIPS_CP0_CONFIG3: 8258c2ecf20Sopenharmony_ci *v = (long)kvm_read_c0_guest_config3(cop0); 8268c2ecf20Sopenharmony_ci break; 8278c2ecf20Sopenharmony_ci case KVM_REG_MIPS_CP0_CONFIG4: 8288c2ecf20Sopenharmony_ci *v = (long)kvm_read_c0_guest_config4(cop0); 8298c2ecf20Sopenharmony_ci break; 8308c2ecf20Sopenharmony_ci case KVM_REG_MIPS_CP0_CONFIG5: 8318c2ecf20Sopenharmony_ci *v = (long)kvm_read_c0_guest_config5(cop0); 8328c2ecf20Sopenharmony_ci break; 8338c2ecf20Sopenharmony_ci case KVM_REG_MIPS_CP0_CONFIG7: 8348c2ecf20Sopenharmony_ci *v = (long)kvm_read_c0_guest_config7(cop0); 8358c2ecf20Sopenharmony_ci break; 8368c2ecf20Sopenharmony_ci case KVM_REG_MIPS_CP0_COUNT: 8378c2ecf20Sopenharmony_ci *v = kvm_mips_read_count(vcpu); 8388c2ecf20Sopenharmony_ci break; 8398c2ecf20Sopenharmony_ci case KVM_REG_MIPS_COUNT_CTL: 8408c2ecf20Sopenharmony_ci *v = vcpu->arch.count_ctl; 8418c2ecf20Sopenharmony_ci break; 8428c2ecf20Sopenharmony_ci case KVM_REG_MIPS_COUNT_RESUME: 8438c2ecf20Sopenharmony_ci *v = ktime_to_ns(vcpu->arch.count_resume); 8448c2ecf20Sopenharmony_ci break; 8458c2ecf20Sopenharmony_ci case KVM_REG_MIPS_COUNT_HZ: 8468c2ecf20Sopenharmony_ci *v = vcpu->arch.count_hz; 8478c2ecf20Sopenharmony_ci break; 8488c2ecf20Sopenharmony_ci case KVM_REG_MIPS_CP0_ERROREPC: 8498c2ecf20Sopenharmony_ci *v = (long)kvm_read_c0_guest_errorepc(cop0); 8508c2ecf20Sopenharmony_ci break; 8518c2ecf20Sopenharmony_ci case KVM_REG_MIPS_CP0_KSCRATCH1: 8528c2ecf20Sopenharmony_ci *v = (long)kvm_read_c0_guest_kscratch1(cop0); 8538c2ecf20Sopenharmony_ci break; 8548c2ecf20Sopenharmony_ci case KVM_REG_MIPS_CP0_KSCRATCH2: 8558c2ecf20Sopenharmony_ci *v = (long)kvm_read_c0_guest_kscratch2(cop0); 8568c2ecf20Sopenharmony_ci break; 8578c2ecf20Sopenharmony_ci case KVM_REG_MIPS_CP0_KSCRATCH3: 8588c2ecf20Sopenharmony_ci *v = (long)kvm_read_c0_guest_kscratch3(cop0); 8598c2ecf20Sopenharmony_ci break; 8608c2ecf20Sopenharmony_ci case KVM_REG_MIPS_CP0_KSCRATCH4: 8618c2ecf20Sopenharmony_ci *v = (long)kvm_read_c0_guest_kscratch4(cop0); 8628c2ecf20Sopenharmony_ci break; 8638c2ecf20Sopenharmony_ci case KVM_REG_MIPS_CP0_KSCRATCH5: 8648c2ecf20Sopenharmony_ci *v = (long)kvm_read_c0_guest_kscratch5(cop0); 8658c2ecf20Sopenharmony_ci break; 8668c2ecf20Sopenharmony_ci case KVM_REG_MIPS_CP0_KSCRATCH6: 8678c2ecf20Sopenharmony_ci *v = (long)kvm_read_c0_guest_kscratch6(cop0); 8688c2ecf20Sopenharmony_ci break; 8698c2ecf20Sopenharmony_ci default: 8708c2ecf20Sopenharmony_ci return -EINVAL; 8718c2ecf20Sopenharmony_ci } 8728c2ecf20Sopenharmony_ci return 0; 8738c2ecf20Sopenharmony_ci} 8748c2ecf20Sopenharmony_ci 8758c2ecf20Sopenharmony_cistatic int kvm_trap_emul_set_one_reg(struct kvm_vcpu *vcpu, 8768c2ecf20Sopenharmony_ci const struct kvm_one_reg *reg, 8778c2ecf20Sopenharmony_ci s64 v) 8788c2ecf20Sopenharmony_ci{ 8798c2ecf20Sopenharmony_ci struct mips_coproc *cop0 = vcpu->arch.cop0; 8808c2ecf20Sopenharmony_ci int ret = 0; 8818c2ecf20Sopenharmony_ci unsigned int cur, change; 8828c2ecf20Sopenharmony_ci 8838c2ecf20Sopenharmony_ci switch (reg->id) { 8848c2ecf20Sopenharmony_ci case KVM_REG_MIPS_CP0_INDEX: 8858c2ecf20Sopenharmony_ci kvm_write_c0_guest_index(cop0, v); 8868c2ecf20Sopenharmony_ci break; 8878c2ecf20Sopenharmony_ci case KVM_REG_MIPS_CP0_ENTRYLO0: 8888c2ecf20Sopenharmony_ci kvm_write_c0_guest_entrylo0(cop0, v); 8898c2ecf20Sopenharmony_ci break; 8908c2ecf20Sopenharmony_ci case KVM_REG_MIPS_CP0_ENTRYLO1: 8918c2ecf20Sopenharmony_ci kvm_write_c0_guest_entrylo1(cop0, v); 8928c2ecf20Sopenharmony_ci break; 8938c2ecf20Sopenharmony_ci case KVM_REG_MIPS_CP0_CONTEXT: 8948c2ecf20Sopenharmony_ci kvm_write_c0_guest_context(cop0, v); 8958c2ecf20Sopenharmony_ci break; 8968c2ecf20Sopenharmony_ci case KVM_REG_MIPS_CP0_USERLOCAL: 8978c2ecf20Sopenharmony_ci kvm_write_c0_guest_userlocal(cop0, v); 8988c2ecf20Sopenharmony_ci break; 8998c2ecf20Sopenharmony_ci case KVM_REG_MIPS_CP0_PAGEMASK: 9008c2ecf20Sopenharmony_ci kvm_write_c0_guest_pagemask(cop0, v); 9018c2ecf20Sopenharmony_ci break; 9028c2ecf20Sopenharmony_ci case KVM_REG_MIPS_CP0_WIRED: 9038c2ecf20Sopenharmony_ci kvm_write_c0_guest_wired(cop0, v); 9048c2ecf20Sopenharmony_ci break; 9058c2ecf20Sopenharmony_ci case KVM_REG_MIPS_CP0_HWRENA: 9068c2ecf20Sopenharmony_ci kvm_write_c0_guest_hwrena(cop0, v); 9078c2ecf20Sopenharmony_ci break; 9088c2ecf20Sopenharmony_ci case KVM_REG_MIPS_CP0_BADVADDR: 9098c2ecf20Sopenharmony_ci kvm_write_c0_guest_badvaddr(cop0, v); 9108c2ecf20Sopenharmony_ci break; 9118c2ecf20Sopenharmony_ci case KVM_REG_MIPS_CP0_ENTRYHI: 9128c2ecf20Sopenharmony_ci kvm_write_c0_guest_entryhi(cop0, v); 9138c2ecf20Sopenharmony_ci break; 9148c2ecf20Sopenharmony_ci case KVM_REG_MIPS_CP0_STATUS: 9158c2ecf20Sopenharmony_ci kvm_write_c0_guest_status(cop0, v); 9168c2ecf20Sopenharmony_ci break; 9178c2ecf20Sopenharmony_ci case KVM_REG_MIPS_CP0_INTCTL: 9188c2ecf20Sopenharmony_ci /* No VInt, so no VS, read-only for now */ 9198c2ecf20Sopenharmony_ci break; 9208c2ecf20Sopenharmony_ci case KVM_REG_MIPS_CP0_EPC: 9218c2ecf20Sopenharmony_ci kvm_write_c0_guest_epc(cop0, v); 9228c2ecf20Sopenharmony_ci break; 9238c2ecf20Sopenharmony_ci case KVM_REG_MIPS_CP0_PRID: 9248c2ecf20Sopenharmony_ci kvm_write_c0_guest_prid(cop0, v); 9258c2ecf20Sopenharmony_ci break; 9268c2ecf20Sopenharmony_ci case KVM_REG_MIPS_CP0_EBASE: 9278c2ecf20Sopenharmony_ci /* 9288c2ecf20Sopenharmony_ci * Allow core number to be written, but the exception base must 9298c2ecf20Sopenharmony_ci * remain in guest KSeg0. 9308c2ecf20Sopenharmony_ci */ 9318c2ecf20Sopenharmony_ci kvm_change_c0_guest_ebase(cop0, 0x1ffff000 | MIPS_EBASE_CPUNUM, 9328c2ecf20Sopenharmony_ci v); 9338c2ecf20Sopenharmony_ci break; 9348c2ecf20Sopenharmony_ci case KVM_REG_MIPS_CP0_COUNT: 9358c2ecf20Sopenharmony_ci kvm_mips_write_count(vcpu, v); 9368c2ecf20Sopenharmony_ci break; 9378c2ecf20Sopenharmony_ci case KVM_REG_MIPS_CP0_COMPARE: 9388c2ecf20Sopenharmony_ci kvm_mips_write_compare(vcpu, v, false); 9398c2ecf20Sopenharmony_ci break; 9408c2ecf20Sopenharmony_ci case KVM_REG_MIPS_CP0_CAUSE: 9418c2ecf20Sopenharmony_ci /* 9428c2ecf20Sopenharmony_ci * If the timer is stopped or started (DC bit) it must look 9438c2ecf20Sopenharmony_ci * atomic with changes to the interrupt pending bits (TI, IRQ5). 9448c2ecf20Sopenharmony_ci * A timer interrupt should not happen in between. 9458c2ecf20Sopenharmony_ci */ 9468c2ecf20Sopenharmony_ci if ((kvm_read_c0_guest_cause(cop0) ^ v) & CAUSEF_DC) { 9478c2ecf20Sopenharmony_ci if (v & CAUSEF_DC) { 9488c2ecf20Sopenharmony_ci /* disable timer first */ 9498c2ecf20Sopenharmony_ci kvm_mips_count_disable_cause(vcpu); 9508c2ecf20Sopenharmony_ci kvm_change_c0_guest_cause(cop0, (u32)~CAUSEF_DC, 9518c2ecf20Sopenharmony_ci v); 9528c2ecf20Sopenharmony_ci } else { 9538c2ecf20Sopenharmony_ci /* enable timer last */ 9548c2ecf20Sopenharmony_ci kvm_change_c0_guest_cause(cop0, (u32)~CAUSEF_DC, 9558c2ecf20Sopenharmony_ci v); 9568c2ecf20Sopenharmony_ci kvm_mips_count_enable_cause(vcpu); 9578c2ecf20Sopenharmony_ci } 9588c2ecf20Sopenharmony_ci } else { 9598c2ecf20Sopenharmony_ci kvm_write_c0_guest_cause(cop0, v); 9608c2ecf20Sopenharmony_ci } 9618c2ecf20Sopenharmony_ci break; 9628c2ecf20Sopenharmony_ci case KVM_REG_MIPS_CP0_CONFIG: 9638c2ecf20Sopenharmony_ci /* read-only for now */ 9648c2ecf20Sopenharmony_ci break; 9658c2ecf20Sopenharmony_ci case KVM_REG_MIPS_CP0_CONFIG1: 9668c2ecf20Sopenharmony_ci cur = kvm_read_c0_guest_config1(cop0); 9678c2ecf20Sopenharmony_ci change = (cur ^ v) & kvm_mips_config1_wrmask(vcpu); 9688c2ecf20Sopenharmony_ci if (change) { 9698c2ecf20Sopenharmony_ci v = cur ^ change; 9708c2ecf20Sopenharmony_ci kvm_write_c0_guest_config1(cop0, v); 9718c2ecf20Sopenharmony_ci } 9728c2ecf20Sopenharmony_ci break; 9738c2ecf20Sopenharmony_ci case KVM_REG_MIPS_CP0_CONFIG2: 9748c2ecf20Sopenharmony_ci /* read-only for now */ 9758c2ecf20Sopenharmony_ci break; 9768c2ecf20Sopenharmony_ci case KVM_REG_MIPS_CP0_CONFIG3: 9778c2ecf20Sopenharmony_ci cur = kvm_read_c0_guest_config3(cop0); 9788c2ecf20Sopenharmony_ci change = (cur ^ v) & kvm_mips_config3_wrmask(vcpu); 9798c2ecf20Sopenharmony_ci if (change) { 9808c2ecf20Sopenharmony_ci v = cur ^ change; 9818c2ecf20Sopenharmony_ci kvm_write_c0_guest_config3(cop0, v); 9828c2ecf20Sopenharmony_ci } 9838c2ecf20Sopenharmony_ci break; 9848c2ecf20Sopenharmony_ci case KVM_REG_MIPS_CP0_CONFIG4: 9858c2ecf20Sopenharmony_ci cur = kvm_read_c0_guest_config4(cop0); 9868c2ecf20Sopenharmony_ci change = (cur ^ v) & kvm_mips_config4_wrmask(vcpu); 9878c2ecf20Sopenharmony_ci if (change) { 9888c2ecf20Sopenharmony_ci v = cur ^ change; 9898c2ecf20Sopenharmony_ci kvm_write_c0_guest_config4(cop0, v); 9908c2ecf20Sopenharmony_ci } 9918c2ecf20Sopenharmony_ci break; 9928c2ecf20Sopenharmony_ci case KVM_REG_MIPS_CP0_CONFIG5: 9938c2ecf20Sopenharmony_ci cur = kvm_read_c0_guest_config5(cop0); 9948c2ecf20Sopenharmony_ci change = (cur ^ v) & kvm_mips_config5_wrmask(vcpu); 9958c2ecf20Sopenharmony_ci if (change) { 9968c2ecf20Sopenharmony_ci v = cur ^ change; 9978c2ecf20Sopenharmony_ci kvm_write_c0_guest_config5(cop0, v); 9988c2ecf20Sopenharmony_ci } 9998c2ecf20Sopenharmony_ci break; 10008c2ecf20Sopenharmony_ci case KVM_REG_MIPS_CP0_CONFIG7: 10018c2ecf20Sopenharmony_ci /* writes ignored */ 10028c2ecf20Sopenharmony_ci break; 10038c2ecf20Sopenharmony_ci case KVM_REG_MIPS_COUNT_CTL: 10048c2ecf20Sopenharmony_ci ret = kvm_mips_set_count_ctl(vcpu, v); 10058c2ecf20Sopenharmony_ci break; 10068c2ecf20Sopenharmony_ci case KVM_REG_MIPS_COUNT_RESUME: 10078c2ecf20Sopenharmony_ci ret = kvm_mips_set_count_resume(vcpu, v); 10088c2ecf20Sopenharmony_ci break; 10098c2ecf20Sopenharmony_ci case KVM_REG_MIPS_COUNT_HZ: 10108c2ecf20Sopenharmony_ci ret = kvm_mips_set_count_hz(vcpu, v); 10118c2ecf20Sopenharmony_ci break; 10128c2ecf20Sopenharmony_ci case KVM_REG_MIPS_CP0_ERROREPC: 10138c2ecf20Sopenharmony_ci kvm_write_c0_guest_errorepc(cop0, v); 10148c2ecf20Sopenharmony_ci break; 10158c2ecf20Sopenharmony_ci case KVM_REG_MIPS_CP0_KSCRATCH1: 10168c2ecf20Sopenharmony_ci kvm_write_c0_guest_kscratch1(cop0, v); 10178c2ecf20Sopenharmony_ci break; 10188c2ecf20Sopenharmony_ci case KVM_REG_MIPS_CP0_KSCRATCH2: 10198c2ecf20Sopenharmony_ci kvm_write_c0_guest_kscratch2(cop0, v); 10208c2ecf20Sopenharmony_ci break; 10218c2ecf20Sopenharmony_ci case KVM_REG_MIPS_CP0_KSCRATCH3: 10228c2ecf20Sopenharmony_ci kvm_write_c0_guest_kscratch3(cop0, v); 10238c2ecf20Sopenharmony_ci break; 10248c2ecf20Sopenharmony_ci case KVM_REG_MIPS_CP0_KSCRATCH4: 10258c2ecf20Sopenharmony_ci kvm_write_c0_guest_kscratch4(cop0, v); 10268c2ecf20Sopenharmony_ci break; 10278c2ecf20Sopenharmony_ci case KVM_REG_MIPS_CP0_KSCRATCH5: 10288c2ecf20Sopenharmony_ci kvm_write_c0_guest_kscratch5(cop0, v); 10298c2ecf20Sopenharmony_ci break; 10308c2ecf20Sopenharmony_ci case KVM_REG_MIPS_CP0_KSCRATCH6: 10318c2ecf20Sopenharmony_ci kvm_write_c0_guest_kscratch6(cop0, v); 10328c2ecf20Sopenharmony_ci break; 10338c2ecf20Sopenharmony_ci default: 10348c2ecf20Sopenharmony_ci return -EINVAL; 10358c2ecf20Sopenharmony_ci } 10368c2ecf20Sopenharmony_ci return ret; 10378c2ecf20Sopenharmony_ci} 10388c2ecf20Sopenharmony_ci 10398c2ecf20Sopenharmony_cistatic int kvm_trap_emul_vcpu_load(struct kvm_vcpu *vcpu, int cpu) 10408c2ecf20Sopenharmony_ci{ 10418c2ecf20Sopenharmony_ci struct mm_struct *kern_mm = &vcpu->arch.guest_kernel_mm; 10428c2ecf20Sopenharmony_ci struct mm_struct *user_mm = &vcpu->arch.guest_user_mm; 10438c2ecf20Sopenharmony_ci struct mm_struct *mm; 10448c2ecf20Sopenharmony_ci 10458c2ecf20Sopenharmony_ci /* 10468c2ecf20Sopenharmony_ci * Were we in guest context? If so, restore the appropriate ASID based 10478c2ecf20Sopenharmony_ci * on the mode of the Guest (Kernel/User). 10488c2ecf20Sopenharmony_ci */ 10498c2ecf20Sopenharmony_ci if (current->flags & PF_VCPU) { 10508c2ecf20Sopenharmony_ci mm = KVM_GUEST_KERNEL_MODE(vcpu) ? kern_mm : user_mm; 10518c2ecf20Sopenharmony_ci check_switch_mmu_context(mm); 10528c2ecf20Sopenharmony_ci kvm_mips_suspend_mm(cpu); 10538c2ecf20Sopenharmony_ci ehb(); 10548c2ecf20Sopenharmony_ci } 10558c2ecf20Sopenharmony_ci 10568c2ecf20Sopenharmony_ci return 0; 10578c2ecf20Sopenharmony_ci} 10588c2ecf20Sopenharmony_ci 10598c2ecf20Sopenharmony_cistatic int kvm_trap_emul_vcpu_put(struct kvm_vcpu *vcpu, int cpu) 10608c2ecf20Sopenharmony_ci{ 10618c2ecf20Sopenharmony_ci kvm_lose_fpu(vcpu); 10628c2ecf20Sopenharmony_ci 10638c2ecf20Sopenharmony_ci if (current->flags & PF_VCPU) { 10648c2ecf20Sopenharmony_ci /* Restore normal Linux process memory map */ 10658c2ecf20Sopenharmony_ci check_switch_mmu_context(current->mm); 10668c2ecf20Sopenharmony_ci kvm_mips_resume_mm(cpu); 10678c2ecf20Sopenharmony_ci ehb(); 10688c2ecf20Sopenharmony_ci } 10698c2ecf20Sopenharmony_ci 10708c2ecf20Sopenharmony_ci return 0; 10718c2ecf20Sopenharmony_ci} 10728c2ecf20Sopenharmony_ci 10738c2ecf20Sopenharmony_cistatic void kvm_trap_emul_check_requests(struct kvm_vcpu *vcpu, int cpu, 10748c2ecf20Sopenharmony_ci bool reload_asid) 10758c2ecf20Sopenharmony_ci{ 10768c2ecf20Sopenharmony_ci struct mm_struct *kern_mm = &vcpu->arch.guest_kernel_mm; 10778c2ecf20Sopenharmony_ci struct mm_struct *user_mm = &vcpu->arch.guest_user_mm; 10788c2ecf20Sopenharmony_ci struct mm_struct *mm; 10798c2ecf20Sopenharmony_ci int i; 10808c2ecf20Sopenharmony_ci 10818c2ecf20Sopenharmony_ci if (likely(!kvm_request_pending(vcpu))) 10828c2ecf20Sopenharmony_ci return; 10838c2ecf20Sopenharmony_ci 10848c2ecf20Sopenharmony_ci if (kvm_check_request(KVM_REQ_TLB_FLUSH, vcpu)) { 10858c2ecf20Sopenharmony_ci /* 10868c2ecf20Sopenharmony_ci * Both kernel & user GVA mappings must be invalidated. The 10878c2ecf20Sopenharmony_ci * caller is just about to check whether the ASID is stale 10888c2ecf20Sopenharmony_ci * anyway so no need to reload it here. 10898c2ecf20Sopenharmony_ci */ 10908c2ecf20Sopenharmony_ci kvm_mips_flush_gva_pt(kern_mm->pgd, KMF_GPA | KMF_KERN); 10918c2ecf20Sopenharmony_ci kvm_mips_flush_gva_pt(user_mm->pgd, KMF_GPA | KMF_USER); 10928c2ecf20Sopenharmony_ci for_each_possible_cpu(i) { 10938c2ecf20Sopenharmony_ci set_cpu_context(i, kern_mm, 0); 10948c2ecf20Sopenharmony_ci set_cpu_context(i, user_mm, 0); 10958c2ecf20Sopenharmony_ci } 10968c2ecf20Sopenharmony_ci 10978c2ecf20Sopenharmony_ci /* Generate new ASID for current mode */ 10988c2ecf20Sopenharmony_ci if (reload_asid) { 10998c2ecf20Sopenharmony_ci mm = KVM_GUEST_KERNEL_MODE(vcpu) ? kern_mm : user_mm; 11008c2ecf20Sopenharmony_ci get_new_mmu_context(mm); 11018c2ecf20Sopenharmony_ci htw_stop(); 11028c2ecf20Sopenharmony_ci write_c0_entryhi(cpu_asid(cpu, mm)); 11038c2ecf20Sopenharmony_ci TLBMISS_HANDLER_SETUP_PGD(mm->pgd); 11048c2ecf20Sopenharmony_ci htw_start(); 11058c2ecf20Sopenharmony_ci } 11068c2ecf20Sopenharmony_ci } 11078c2ecf20Sopenharmony_ci} 11088c2ecf20Sopenharmony_ci 11098c2ecf20Sopenharmony_ci/** 11108c2ecf20Sopenharmony_ci * kvm_trap_emul_gva_lockless_begin() - Begin lockless access to GVA space. 11118c2ecf20Sopenharmony_ci * @vcpu: VCPU pointer. 11128c2ecf20Sopenharmony_ci * 11138c2ecf20Sopenharmony_ci * Call before a GVA space access outside of guest mode, to ensure that 11148c2ecf20Sopenharmony_ci * asynchronous TLB flush requests are handled or delayed until completion of 11158c2ecf20Sopenharmony_ci * the GVA access (as indicated by a matching kvm_trap_emul_gva_lockless_end()). 11168c2ecf20Sopenharmony_ci * 11178c2ecf20Sopenharmony_ci * Should be called with IRQs already enabled. 11188c2ecf20Sopenharmony_ci */ 11198c2ecf20Sopenharmony_civoid kvm_trap_emul_gva_lockless_begin(struct kvm_vcpu *vcpu) 11208c2ecf20Sopenharmony_ci{ 11218c2ecf20Sopenharmony_ci /* We re-enable IRQs in kvm_trap_emul_gva_lockless_end() */ 11228c2ecf20Sopenharmony_ci WARN_ON_ONCE(irqs_disabled()); 11238c2ecf20Sopenharmony_ci 11248c2ecf20Sopenharmony_ci /* 11258c2ecf20Sopenharmony_ci * The caller is about to access the GVA space, so we set the mode to 11268c2ecf20Sopenharmony_ci * force TLB flush requests to send an IPI, and also disable IRQs to 11278c2ecf20Sopenharmony_ci * delay IPI handling until kvm_trap_emul_gva_lockless_end(). 11288c2ecf20Sopenharmony_ci */ 11298c2ecf20Sopenharmony_ci local_irq_disable(); 11308c2ecf20Sopenharmony_ci 11318c2ecf20Sopenharmony_ci /* 11328c2ecf20Sopenharmony_ci * Make sure the read of VCPU requests is not reordered ahead of the 11338c2ecf20Sopenharmony_ci * write to vcpu->mode, or we could miss a TLB flush request while 11348c2ecf20Sopenharmony_ci * the requester sees the VCPU as outside of guest mode and not needing 11358c2ecf20Sopenharmony_ci * an IPI. 11368c2ecf20Sopenharmony_ci */ 11378c2ecf20Sopenharmony_ci smp_store_mb(vcpu->mode, READING_SHADOW_PAGE_TABLES); 11388c2ecf20Sopenharmony_ci 11398c2ecf20Sopenharmony_ci /* 11408c2ecf20Sopenharmony_ci * If a TLB flush has been requested (potentially while 11418c2ecf20Sopenharmony_ci * OUTSIDE_GUEST_MODE and assumed immediately effective), perform it 11428c2ecf20Sopenharmony_ci * before accessing the GVA space, and be sure to reload the ASID if 11438c2ecf20Sopenharmony_ci * necessary as it'll be immediately used. 11448c2ecf20Sopenharmony_ci * 11458c2ecf20Sopenharmony_ci * TLB flush requests after this check will trigger an IPI due to the 11468c2ecf20Sopenharmony_ci * mode change above, which will be delayed due to IRQs disabled. 11478c2ecf20Sopenharmony_ci */ 11488c2ecf20Sopenharmony_ci kvm_trap_emul_check_requests(vcpu, smp_processor_id(), true); 11498c2ecf20Sopenharmony_ci} 11508c2ecf20Sopenharmony_ci 11518c2ecf20Sopenharmony_ci/** 11528c2ecf20Sopenharmony_ci * kvm_trap_emul_gva_lockless_end() - End lockless access to GVA space. 11538c2ecf20Sopenharmony_ci * @vcpu: VCPU pointer. 11548c2ecf20Sopenharmony_ci * 11558c2ecf20Sopenharmony_ci * Called after a GVA space access outside of guest mode. Should have a matching 11568c2ecf20Sopenharmony_ci * call to kvm_trap_emul_gva_lockless_begin(). 11578c2ecf20Sopenharmony_ci */ 11588c2ecf20Sopenharmony_civoid kvm_trap_emul_gva_lockless_end(struct kvm_vcpu *vcpu) 11598c2ecf20Sopenharmony_ci{ 11608c2ecf20Sopenharmony_ci /* 11618c2ecf20Sopenharmony_ci * Make sure the write to vcpu->mode is not reordered in front of GVA 11628c2ecf20Sopenharmony_ci * accesses, or a TLB flush requester may not think it necessary to send 11638c2ecf20Sopenharmony_ci * an IPI. 11648c2ecf20Sopenharmony_ci */ 11658c2ecf20Sopenharmony_ci smp_store_release(&vcpu->mode, OUTSIDE_GUEST_MODE); 11668c2ecf20Sopenharmony_ci 11678c2ecf20Sopenharmony_ci /* 11688c2ecf20Sopenharmony_ci * Now that the access to GVA space is complete, its safe for pending 11698c2ecf20Sopenharmony_ci * TLB flush request IPIs to be handled (which indicates completion). 11708c2ecf20Sopenharmony_ci */ 11718c2ecf20Sopenharmony_ci local_irq_enable(); 11728c2ecf20Sopenharmony_ci} 11738c2ecf20Sopenharmony_ci 11748c2ecf20Sopenharmony_cistatic void kvm_trap_emul_vcpu_reenter(struct kvm_vcpu *vcpu) 11758c2ecf20Sopenharmony_ci{ 11768c2ecf20Sopenharmony_ci struct mm_struct *kern_mm = &vcpu->arch.guest_kernel_mm; 11778c2ecf20Sopenharmony_ci struct mm_struct *user_mm = &vcpu->arch.guest_user_mm; 11788c2ecf20Sopenharmony_ci struct mm_struct *mm; 11798c2ecf20Sopenharmony_ci struct mips_coproc *cop0 = vcpu->arch.cop0; 11808c2ecf20Sopenharmony_ci int i, cpu = smp_processor_id(); 11818c2ecf20Sopenharmony_ci unsigned int gasid; 11828c2ecf20Sopenharmony_ci 11838c2ecf20Sopenharmony_ci /* 11848c2ecf20Sopenharmony_ci * No need to reload ASID, IRQs are disabled already so there's no rush, 11858c2ecf20Sopenharmony_ci * and we'll check if we need to regenerate below anyway before 11868c2ecf20Sopenharmony_ci * re-entering the guest. 11878c2ecf20Sopenharmony_ci */ 11888c2ecf20Sopenharmony_ci kvm_trap_emul_check_requests(vcpu, cpu, false); 11898c2ecf20Sopenharmony_ci 11908c2ecf20Sopenharmony_ci if (KVM_GUEST_KERNEL_MODE(vcpu)) { 11918c2ecf20Sopenharmony_ci mm = kern_mm; 11928c2ecf20Sopenharmony_ci } else { 11938c2ecf20Sopenharmony_ci mm = user_mm; 11948c2ecf20Sopenharmony_ci 11958c2ecf20Sopenharmony_ci /* 11968c2ecf20Sopenharmony_ci * Lazy host ASID regeneration / PT flush for guest user mode. 11978c2ecf20Sopenharmony_ci * If the guest ASID has changed since the last guest usermode 11988c2ecf20Sopenharmony_ci * execution, invalidate the stale TLB entries and flush GVA PT 11998c2ecf20Sopenharmony_ci * entries too. 12008c2ecf20Sopenharmony_ci */ 12018c2ecf20Sopenharmony_ci gasid = kvm_read_c0_guest_entryhi(cop0) & KVM_ENTRYHI_ASID; 12028c2ecf20Sopenharmony_ci if (gasid != vcpu->arch.last_user_gasid) { 12038c2ecf20Sopenharmony_ci kvm_mips_flush_gva_pt(user_mm->pgd, KMF_USER); 12048c2ecf20Sopenharmony_ci for_each_possible_cpu(i) 12058c2ecf20Sopenharmony_ci set_cpu_context(i, user_mm, 0); 12068c2ecf20Sopenharmony_ci vcpu->arch.last_user_gasid = gasid; 12078c2ecf20Sopenharmony_ci } 12088c2ecf20Sopenharmony_ci } 12098c2ecf20Sopenharmony_ci 12108c2ecf20Sopenharmony_ci /* 12118c2ecf20Sopenharmony_ci * Check if ASID is stale. This may happen due to a TLB flush request or 12128c2ecf20Sopenharmony_ci * a lazy user MM invalidation. 12138c2ecf20Sopenharmony_ci */ 12148c2ecf20Sopenharmony_ci check_mmu_context(mm); 12158c2ecf20Sopenharmony_ci} 12168c2ecf20Sopenharmony_ci 12178c2ecf20Sopenharmony_cistatic int kvm_trap_emul_vcpu_run(struct kvm_vcpu *vcpu) 12188c2ecf20Sopenharmony_ci{ 12198c2ecf20Sopenharmony_ci int cpu = smp_processor_id(); 12208c2ecf20Sopenharmony_ci int r; 12218c2ecf20Sopenharmony_ci 12228c2ecf20Sopenharmony_ci /* Check if we have any exceptions/interrupts pending */ 12238c2ecf20Sopenharmony_ci kvm_mips_deliver_interrupts(vcpu, 12248c2ecf20Sopenharmony_ci kvm_read_c0_guest_cause(vcpu->arch.cop0)); 12258c2ecf20Sopenharmony_ci 12268c2ecf20Sopenharmony_ci kvm_trap_emul_vcpu_reenter(vcpu); 12278c2ecf20Sopenharmony_ci 12288c2ecf20Sopenharmony_ci /* 12298c2ecf20Sopenharmony_ci * We use user accessors to access guest memory, but we don't want to 12308c2ecf20Sopenharmony_ci * invoke Linux page faulting. 12318c2ecf20Sopenharmony_ci */ 12328c2ecf20Sopenharmony_ci pagefault_disable(); 12338c2ecf20Sopenharmony_ci 12348c2ecf20Sopenharmony_ci /* Disable hardware page table walking while in guest */ 12358c2ecf20Sopenharmony_ci htw_stop(); 12368c2ecf20Sopenharmony_ci 12378c2ecf20Sopenharmony_ci /* 12388c2ecf20Sopenharmony_ci * While in guest context we're in the guest's address space, not the 12398c2ecf20Sopenharmony_ci * host process address space, so we need to be careful not to confuse 12408c2ecf20Sopenharmony_ci * e.g. cache management IPIs. 12418c2ecf20Sopenharmony_ci */ 12428c2ecf20Sopenharmony_ci kvm_mips_suspend_mm(cpu); 12438c2ecf20Sopenharmony_ci 12448c2ecf20Sopenharmony_ci r = vcpu->arch.vcpu_run(vcpu); 12458c2ecf20Sopenharmony_ci 12468c2ecf20Sopenharmony_ci /* We may have migrated while handling guest exits */ 12478c2ecf20Sopenharmony_ci cpu = smp_processor_id(); 12488c2ecf20Sopenharmony_ci 12498c2ecf20Sopenharmony_ci /* Restore normal Linux process memory map */ 12508c2ecf20Sopenharmony_ci check_switch_mmu_context(current->mm); 12518c2ecf20Sopenharmony_ci kvm_mips_resume_mm(cpu); 12528c2ecf20Sopenharmony_ci 12538c2ecf20Sopenharmony_ci htw_start(); 12548c2ecf20Sopenharmony_ci 12558c2ecf20Sopenharmony_ci pagefault_enable(); 12568c2ecf20Sopenharmony_ci 12578c2ecf20Sopenharmony_ci return r; 12588c2ecf20Sopenharmony_ci} 12598c2ecf20Sopenharmony_ci 12608c2ecf20Sopenharmony_cistatic struct kvm_mips_callbacks kvm_trap_emul_callbacks = { 12618c2ecf20Sopenharmony_ci /* exit handlers */ 12628c2ecf20Sopenharmony_ci .handle_cop_unusable = kvm_trap_emul_handle_cop_unusable, 12638c2ecf20Sopenharmony_ci .handle_tlb_mod = kvm_trap_emul_handle_tlb_mod, 12648c2ecf20Sopenharmony_ci .handle_tlb_st_miss = kvm_trap_emul_handle_tlb_st_miss, 12658c2ecf20Sopenharmony_ci .handle_tlb_ld_miss = kvm_trap_emul_handle_tlb_ld_miss, 12668c2ecf20Sopenharmony_ci .handle_addr_err_st = kvm_trap_emul_handle_addr_err_st, 12678c2ecf20Sopenharmony_ci .handle_addr_err_ld = kvm_trap_emul_handle_addr_err_ld, 12688c2ecf20Sopenharmony_ci .handle_syscall = kvm_trap_emul_handle_syscall, 12698c2ecf20Sopenharmony_ci .handle_res_inst = kvm_trap_emul_handle_res_inst, 12708c2ecf20Sopenharmony_ci .handle_break = kvm_trap_emul_handle_break, 12718c2ecf20Sopenharmony_ci .handle_trap = kvm_trap_emul_handle_trap, 12728c2ecf20Sopenharmony_ci .handle_msa_fpe = kvm_trap_emul_handle_msa_fpe, 12738c2ecf20Sopenharmony_ci .handle_fpe = kvm_trap_emul_handle_fpe, 12748c2ecf20Sopenharmony_ci .handle_msa_disabled = kvm_trap_emul_handle_msa_disabled, 12758c2ecf20Sopenharmony_ci .handle_guest_exit = kvm_trap_emul_no_handler, 12768c2ecf20Sopenharmony_ci 12778c2ecf20Sopenharmony_ci .hardware_enable = kvm_trap_emul_hardware_enable, 12788c2ecf20Sopenharmony_ci .hardware_disable = kvm_trap_emul_hardware_disable, 12798c2ecf20Sopenharmony_ci .check_extension = kvm_trap_emul_check_extension, 12808c2ecf20Sopenharmony_ci .vcpu_init = kvm_trap_emul_vcpu_init, 12818c2ecf20Sopenharmony_ci .vcpu_uninit = kvm_trap_emul_vcpu_uninit, 12828c2ecf20Sopenharmony_ci .vcpu_setup = kvm_trap_emul_vcpu_setup, 12838c2ecf20Sopenharmony_ci .flush_shadow_all = kvm_trap_emul_flush_shadow_all, 12848c2ecf20Sopenharmony_ci .flush_shadow_memslot = kvm_trap_emul_flush_shadow_memslot, 12858c2ecf20Sopenharmony_ci .gva_to_gpa = kvm_trap_emul_gva_to_gpa_cb, 12868c2ecf20Sopenharmony_ci .queue_timer_int = kvm_mips_queue_timer_int_cb, 12878c2ecf20Sopenharmony_ci .dequeue_timer_int = kvm_mips_dequeue_timer_int_cb, 12888c2ecf20Sopenharmony_ci .queue_io_int = kvm_mips_queue_io_int_cb, 12898c2ecf20Sopenharmony_ci .dequeue_io_int = kvm_mips_dequeue_io_int_cb, 12908c2ecf20Sopenharmony_ci .irq_deliver = kvm_mips_irq_deliver_cb, 12918c2ecf20Sopenharmony_ci .irq_clear = kvm_mips_irq_clear_cb, 12928c2ecf20Sopenharmony_ci .num_regs = kvm_trap_emul_num_regs, 12938c2ecf20Sopenharmony_ci .copy_reg_indices = kvm_trap_emul_copy_reg_indices, 12948c2ecf20Sopenharmony_ci .get_one_reg = kvm_trap_emul_get_one_reg, 12958c2ecf20Sopenharmony_ci .set_one_reg = kvm_trap_emul_set_one_reg, 12968c2ecf20Sopenharmony_ci .vcpu_load = kvm_trap_emul_vcpu_load, 12978c2ecf20Sopenharmony_ci .vcpu_put = kvm_trap_emul_vcpu_put, 12988c2ecf20Sopenharmony_ci .vcpu_run = kvm_trap_emul_vcpu_run, 12998c2ecf20Sopenharmony_ci .vcpu_reenter = kvm_trap_emul_vcpu_reenter, 13008c2ecf20Sopenharmony_ci}; 13018c2ecf20Sopenharmony_ci 13028c2ecf20Sopenharmony_ciint kvm_mips_emulation_init(struct kvm_mips_callbacks **install_callbacks) 13038c2ecf20Sopenharmony_ci{ 13048c2ecf20Sopenharmony_ci *install_callbacks = &kvm_trap_emul_callbacks; 13058c2ecf20Sopenharmony_ci return 0; 13068c2ecf20Sopenharmony_ci} 1307