18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2020-2022 Loongson Technology Corporation Limited 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#include <linux/errno.h> 78c2ecf20Sopenharmony_ci#include <linux/err.h> 88c2ecf20Sopenharmony_ci#include <linux/module.h> 98c2ecf20Sopenharmony_ci#include <linux/preempt.h> 108c2ecf20Sopenharmony_ci#include <linux/vmalloc.h> 118c2ecf20Sopenharmony_ci#include <asm/cacheflush.h> 128c2ecf20Sopenharmony_ci#include <asm/cacheops.h> 138c2ecf20Sopenharmony_ci#include <asm/cmpxchg.h> 148c2ecf20Sopenharmony_ci#include <asm/fpu.h> 158c2ecf20Sopenharmony_ci#include <asm/inst.h> 168c2ecf20Sopenharmony_ci#include <asm/mmu_context.h> 178c2ecf20Sopenharmony_ci#include <asm/numa.h> 188c2ecf20Sopenharmony_ci#include <asm/cacheflush.h> 198c2ecf20Sopenharmony_ci#include <asm/time.h> 208c2ecf20Sopenharmony_ci#include <asm/tlb.h> 218c2ecf20Sopenharmony_ci#include <asm/watch.h> 228c2ecf20Sopenharmony_ci#include "kvmcpu.h" 238c2ecf20Sopenharmony_ci#include <linux/kvm_host.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#include "trace.h" 268c2ecf20Sopenharmony_ci#include "kvm_compat.h" 278c2ecf20Sopenharmony_ci#include "kvmcsr.h" 288c2ecf20Sopenharmony_ci#include "intc/ls3a_ext_irq.h" 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci/* 318c2ecf20Sopenharmony_ci * Loongarch KVM callback handling for not implemented guest exiting 328c2ecf20Sopenharmony_ci */ 338c2ecf20Sopenharmony_cistatic int _kvm_fault_ni(struct kvm_vcpu *vcpu) 348c2ecf20Sopenharmony_ci{ 358c2ecf20Sopenharmony_ci unsigned long estat, badv; 368c2ecf20Sopenharmony_ci unsigned int exccode, inst; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci /* 398c2ecf20Sopenharmony_ci * Fetch the instruction. 408c2ecf20Sopenharmony_ci */ 418c2ecf20Sopenharmony_ci badv = vcpu->arch.badv; 428c2ecf20Sopenharmony_ci estat = vcpu->arch.host_estat; 438c2ecf20Sopenharmony_ci exccode = (estat & KVM_ESTAT_EXC) >> KVM_ESTAT_EXC_SHIFT; 448c2ecf20Sopenharmony_ci inst = vcpu->arch.badi; 458c2ecf20Sopenharmony_ci kvm_err("Exccode: %d PC=%#lx inst=0x%08x BadVaddr=%#lx estat=%#llx\n", 468c2ecf20Sopenharmony_ci exccode, vcpu->arch.pc, inst, badv, kvm_read_gcsr_estat()); 478c2ecf20Sopenharmony_ci kvm_arch_vcpu_dump_regs(vcpu); 488c2ecf20Sopenharmony_ci vcpu->run->exit_reason = KVM_EXIT_INTERNAL_ERROR; 498c2ecf20Sopenharmony_ci return RESUME_HOST; 508c2ecf20Sopenharmony_ci} 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_cistatic int _kvm_handle_csr(struct kvm_vcpu *vcpu, larch_inst inst) 538c2ecf20Sopenharmony_ci{ 548c2ecf20Sopenharmony_ci enum emulation_result er = EMULATE_DONE; 558c2ecf20Sopenharmony_ci unsigned int rd, rj, csrid; 568c2ecf20Sopenharmony_ci unsigned long csr_mask; 578c2ecf20Sopenharmony_ci unsigned long val = 0; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci /* 608c2ecf20Sopenharmony_ci * CSR value mask imm 618c2ecf20Sopenharmony_ci * rj = 0 means csrrd 628c2ecf20Sopenharmony_ci * rj = 1 means csrwr 638c2ecf20Sopenharmony_ci * rj != 0,1 means csrxchg 648c2ecf20Sopenharmony_ci */ 658c2ecf20Sopenharmony_ci rd = inst.reg2csr_format.rd; 668c2ecf20Sopenharmony_ci rj = inst.reg2csr_format.rj; 678c2ecf20Sopenharmony_ci csrid = inst.reg2csr_format.csr; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci /* Process CSR ops */ 708c2ecf20Sopenharmony_ci if (rj == 0) { 718c2ecf20Sopenharmony_ci /* process csrrd */ 728c2ecf20Sopenharmony_ci val = _kvm_emu_read_csr(vcpu, csrid); 738c2ecf20Sopenharmony_ci if (er != EMULATE_FAIL) 748c2ecf20Sopenharmony_ci vcpu->arch.gprs[rd] = val; 758c2ecf20Sopenharmony_ci } else if (rj == 1) { 768c2ecf20Sopenharmony_ci /* process csrwr */ 778c2ecf20Sopenharmony_ci val = vcpu->arch.gprs[rd]; 788c2ecf20Sopenharmony_ci _kvm_emu_write_csr(vcpu, csrid, val); 798c2ecf20Sopenharmony_ci } else { 808c2ecf20Sopenharmony_ci /* process csrxchg */ 818c2ecf20Sopenharmony_ci val = vcpu->arch.gprs[rd]; 828c2ecf20Sopenharmony_ci csr_mask = vcpu->arch.gprs[rj]; 838c2ecf20Sopenharmony_ci _kvm_emu_xchg_csr(vcpu, csrid, csr_mask, val); 848c2ecf20Sopenharmony_ci } 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci return er; 878c2ecf20Sopenharmony_ci} 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_cistatic int _kvm_emu_cache(struct kvm_vcpu *vcpu, larch_inst inst) 908c2ecf20Sopenharmony_ci{ 918c2ecf20Sopenharmony_ci return EMULATE_DONE; 928c2ecf20Sopenharmony_ci} 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_cistatic int _kvm_trap_handle_gspr(struct kvm_vcpu *vcpu) 958c2ecf20Sopenharmony_ci{ 968c2ecf20Sopenharmony_ci enum emulation_result er = EMULATE_DONE; 978c2ecf20Sopenharmony_ci struct kvm_run *run = vcpu->run; 988c2ecf20Sopenharmony_ci larch_inst inst; 998c2ecf20Sopenharmony_ci unsigned long curr_pc; 1008c2ecf20Sopenharmony_ci int rd, rj; 1018c2ecf20Sopenharmony_ci unsigned int index; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci /* 1048c2ecf20Sopenharmony_ci * Fetch the instruction. 1058c2ecf20Sopenharmony_ci */ 1068c2ecf20Sopenharmony_ci inst.word = vcpu->arch.badi; 1078c2ecf20Sopenharmony_ci curr_pc = vcpu->arch.pc; 1088c2ecf20Sopenharmony_ci update_pc(&vcpu->arch); 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci er = EMULATE_FAIL; 1118c2ecf20Sopenharmony_ci switch (((inst.word >> 24) & 0xff)) { 1128c2ecf20Sopenharmony_ci case 0x0: 1138c2ecf20Sopenharmony_ci /* cpucfg GSPR */ 1148c2ecf20Sopenharmony_ci if (inst.reg2_format.opcode == 0x1B) { 1158c2ecf20Sopenharmony_ci rd = inst.reg2_format.rd; 1168c2ecf20Sopenharmony_ci rj = inst.reg2_format.rj; 1178c2ecf20Sopenharmony_ci ++vcpu->stat.cpucfg_exits; 1188c2ecf20Sopenharmony_ci index = vcpu->arch.gprs[rj]; 1198c2ecf20Sopenharmony_ci vcpu->arch.gprs[rd] = vcpu->kvm->arch.cpucfgs.cpucfg[index]; 1208c2ecf20Sopenharmony_ci if ((index == 2) || (vcpu->arch.gprs[rd] == 0)) 1218c2ecf20Sopenharmony_ci /* 1228c2ecf20Sopenharmony_ci * Fallback to get host cpucfg info, this is just for 1238c2ecf20Sopenharmony_ci * compatible with older qemu. 1248c2ecf20Sopenharmony_ci */ 1258c2ecf20Sopenharmony_ci vcpu->arch.gprs[rd] = read_cpucfg(index); 1268c2ecf20Sopenharmony_ci if (index == 2) 1278c2ecf20Sopenharmony_ci /* do not support nested virtualization */ 1288c2ecf20Sopenharmony_ci vcpu->arch.gprs[rd] &= ~CPUCFG2_LVZP; 1298c2ecf20Sopenharmony_ci er = EMULATE_DONE; 1308c2ecf20Sopenharmony_ci } 1318c2ecf20Sopenharmony_ci break; 1328c2ecf20Sopenharmony_ci case 0x4: 1338c2ecf20Sopenharmony_ci /* csr GSPR */ 1348c2ecf20Sopenharmony_ci er = _kvm_handle_csr(vcpu, inst); 1358c2ecf20Sopenharmony_ci break; 1368c2ecf20Sopenharmony_ci case 0x6: 1378c2ecf20Sopenharmony_ci /* iocsr,cacop,idle GSPR */ 1388c2ecf20Sopenharmony_ci switch (((inst.word >> 22) & 0x3ff)) { 1398c2ecf20Sopenharmony_ci case 0x18: 1408c2ecf20Sopenharmony_ci /* cache GSPR */ 1418c2ecf20Sopenharmony_ci er = _kvm_emu_cache(vcpu, inst); 1428c2ecf20Sopenharmony_ci trace_kvm_exit(vcpu, KVM_TRACE_EXIT_CACHE); 1438c2ecf20Sopenharmony_ci break; 1448c2ecf20Sopenharmony_ci case 0x19: 1458c2ecf20Sopenharmony_ci /* iocsr/idle GSPR */ 1468c2ecf20Sopenharmony_ci switch (((inst.word >> 15) & 0x1ffff)) { 1478c2ecf20Sopenharmony_ci case 0xc90: 1488c2ecf20Sopenharmony_ci /* iocsr GSPR */ 1498c2ecf20Sopenharmony_ci er = _kvm_emu_iocsr(inst, run, vcpu); 1508c2ecf20Sopenharmony_ci break; 1518c2ecf20Sopenharmony_ci case idle_op: 1528c2ecf20Sopenharmony_ci /* idle GSPR */ 1538c2ecf20Sopenharmony_ci er = _kvm_emu_idle(vcpu); 1548c2ecf20Sopenharmony_ci break; 1558c2ecf20Sopenharmony_ci default: 1568c2ecf20Sopenharmony_ci er = EMULATE_FAIL; 1578c2ecf20Sopenharmony_ci break; 1588c2ecf20Sopenharmony_ci } 1598c2ecf20Sopenharmony_ci break; 1608c2ecf20Sopenharmony_ci default: 1618c2ecf20Sopenharmony_ci er = EMULATE_FAIL; 1628c2ecf20Sopenharmony_ci break; 1638c2ecf20Sopenharmony_ci } 1648c2ecf20Sopenharmony_ci break; 1658c2ecf20Sopenharmony_ci default: 1668c2ecf20Sopenharmony_ci er = EMULATE_FAIL; 1678c2ecf20Sopenharmony_ci break; 1688c2ecf20Sopenharmony_ci } 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci /* Rollback PC only if emulation was unsuccessful */ 1718c2ecf20Sopenharmony_ci if (er == EMULATE_FAIL) { 1728c2ecf20Sopenharmony_ci kvm_err("[%#lx]%s: unsupported gspr instruction 0x%08x\n", 1738c2ecf20Sopenharmony_ci curr_pc, __func__, inst.word); 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci kvm_arch_vcpu_dump_regs(vcpu); 1768c2ecf20Sopenharmony_ci vcpu->arch.pc = curr_pc; 1778c2ecf20Sopenharmony_ci } 1788c2ecf20Sopenharmony_ci return er; 1798c2ecf20Sopenharmony_ci} 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_cistatic int _kvm_check_hypcall(struct kvm_vcpu *vcpu) 1828c2ecf20Sopenharmony_ci{ 1838c2ecf20Sopenharmony_ci enum emulation_result ret; 1848c2ecf20Sopenharmony_ci larch_inst inst; 1858c2ecf20Sopenharmony_ci unsigned long curr_pc; 1868c2ecf20Sopenharmony_ci unsigned int code; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci /* 1898c2ecf20Sopenharmony_ci * Update PC and hold onto current PC in case there is 1908c2ecf20Sopenharmony_ci * an error and we want to rollback the PC 1918c2ecf20Sopenharmony_ci */ 1928c2ecf20Sopenharmony_ci inst.word = vcpu->arch.badi; 1938c2ecf20Sopenharmony_ci code = inst.reg0i15_format.simmediate; 1948c2ecf20Sopenharmony_ci curr_pc = vcpu->arch.pc; 1958c2ecf20Sopenharmony_ci update_pc(&vcpu->arch); 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci ret = EMULATE_DONE; 1988c2ecf20Sopenharmony_ci switch (code) { 1998c2ecf20Sopenharmony_ci case KVM_HC_CODE_SERIVCE: 2008c2ecf20Sopenharmony_ci ret = EMULATE_PV_HYPERCALL; 2018c2ecf20Sopenharmony_ci break; 2028c2ecf20Sopenharmony_ci case KVM_HC_CODE_SWDBG: 2038c2ecf20Sopenharmony_ci /* 2048c2ecf20Sopenharmony_ci * Only SWDBG(SoftWare DeBug) could stop vm 2058c2ecf20Sopenharmony_ci * code other than 0 is ignored. 2068c2ecf20Sopenharmony_ci */ 2078c2ecf20Sopenharmony_ci ret = EMULATE_DEBUG; 2088c2ecf20Sopenharmony_ci break; 2098c2ecf20Sopenharmony_ci default: 2108c2ecf20Sopenharmony_ci kvm_info("[%#lx] HYPCALL %#03x unsupported\n", vcpu->arch.pc, code); 2118c2ecf20Sopenharmony_ci break; 2128c2ecf20Sopenharmony_ci } 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci if (ret == EMULATE_DEBUG) 2158c2ecf20Sopenharmony_ci vcpu->arch.pc = curr_pc; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci return ret; 2188c2ecf20Sopenharmony_ci} 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci/* Execute cpucfg instruction will tirggerGSPR, 2218c2ecf20Sopenharmony_ci * Also the access to unimplemented csrs 0x15 2228c2ecf20Sopenharmony_ci * 0x16, 0x50~0x53, 0x80, 0x81, 0x90~0x95, 0x98 2238c2ecf20Sopenharmony_ci * 0xc0~0xff, 0x100~0x109, 0x500~0x502, 2248c2ecf20Sopenharmony_ci * cacop_op, idle_op iocsr ops the same */ 2258c2ecf20Sopenharmony_cistatic int _kvm_handle_gspr(struct kvm_vcpu *vcpu) 2268c2ecf20Sopenharmony_ci{ 2278c2ecf20Sopenharmony_ci enum emulation_result er = EMULATE_DONE; 2288c2ecf20Sopenharmony_ci int ret = RESUME_GUEST; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci vcpu->arch.is_hypcall = 0; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci er = _kvm_trap_handle_gspr(vcpu); 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci if (er == EMULATE_DONE) { 2358c2ecf20Sopenharmony_ci ret = RESUME_GUEST; 2368c2ecf20Sopenharmony_ci } else if (er == EMULATE_DO_MMIO) { 2378c2ecf20Sopenharmony_ci vcpu->run->exit_reason = KVM_EXIT_MMIO; 2388c2ecf20Sopenharmony_ci ret = RESUME_HOST; 2398c2ecf20Sopenharmony_ci } else if (er == EMULATE_DO_IOCSR) { 2408c2ecf20Sopenharmony_ci vcpu->run->exit_reason = KVM_EXIT_LOONGARCH_IOCSR; 2418c2ecf20Sopenharmony_ci ret = RESUME_HOST; 2428c2ecf20Sopenharmony_ci } else { 2438c2ecf20Sopenharmony_ci kvm_err("%s internal error\n", __func__); 2448c2ecf20Sopenharmony_ci vcpu->run->exit_reason = KVM_EXIT_INTERNAL_ERROR; 2458c2ecf20Sopenharmony_ci ret = RESUME_HOST; 2468c2ecf20Sopenharmony_ci } 2478c2ecf20Sopenharmony_ci return ret; 2488c2ecf20Sopenharmony_ci} 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_cistatic int _kvm_handle_hypcall(struct kvm_vcpu *vcpu) 2518c2ecf20Sopenharmony_ci{ 2528c2ecf20Sopenharmony_ci enum emulation_result er = EMULATE_DONE; 2538c2ecf20Sopenharmony_ci int ret = RESUME_GUEST; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci vcpu->arch.is_hypcall = 0; 2568c2ecf20Sopenharmony_ci er = _kvm_check_hypcall(vcpu); 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci if (er == EMULATE_PV_HYPERCALL) 2598c2ecf20Sopenharmony_ci ret = _kvm_handle_pv_hcall(vcpu); 2608c2ecf20Sopenharmony_ci else if (er == EMULATE_DEBUG) { 2618c2ecf20Sopenharmony_ci vcpu->run->exit_reason = KVM_EXIT_DEBUG; 2628c2ecf20Sopenharmony_ci ret = RESUME_HOST; 2638c2ecf20Sopenharmony_ci } else 2648c2ecf20Sopenharmony_ci ret = RESUME_GUEST; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci return ret; 2678c2ecf20Sopenharmony_ci} 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_cistatic int _kvm_handle_gcm(struct kvm_vcpu *vcpu) 2708c2ecf20Sopenharmony_ci{ 2718c2ecf20Sopenharmony_ci int ret, subcode; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci vcpu->arch.is_hypcall = 0; 2748c2ecf20Sopenharmony_ci ret = RESUME_GUEST; 2758c2ecf20Sopenharmony_ci subcode = (vcpu->arch.host_estat & KVM_ESTAT_ESUBCODE) >> KVM_ESTAT_ESUBCODE_SHIFT; 2768c2ecf20Sopenharmony_ci if ((subcode != EXCSUBCODE_GCSC) && (subcode != EXCSUBCODE_GCHC)) { 2778c2ecf20Sopenharmony_ci kvm_err("%s internal error\n", __func__); 2788c2ecf20Sopenharmony_ci vcpu->run->exit_reason = KVM_EXIT_INTERNAL_ERROR; 2798c2ecf20Sopenharmony_ci ret = RESUME_HOST; 2808c2ecf20Sopenharmony_ci } 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci return ret; 2838c2ecf20Sopenharmony_ci} 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci/** 2868c2ecf20Sopenharmony_ci * _kvm_handle_fpu_disabled() - Guest used fpu however it is disabled at host 2878c2ecf20Sopenharmony_ci * @vcpu: Virtual CPU context. 2888c2ecf20Sopenharmony_ci * 2898c2ecf20Sopenharmony_ci * Handle when the guest attempts to use fpu which hasn't been allowed 2908c2ecf20Sopenharmony_ci * by the root context. 2918c2ecf20Sopenharmony_ci */ 2928c2ecf20Sopenharmony_cistatic int _kvm_handle_fpu_disabled(struct kvm_vcpu *vcpu) 2938c2ecf20Sopenharmony_ci{ 2948c2ecf20Sopenharmony_ci struct kvm_run *run = vcpu->run; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci /* 2978c2ecf20Sopenharmony_ci * If guest FPU not present, the FPU operation should have been 2988c2ecf20Sopenharmony_ci * treated as a reserved instruction! 2998c2ecf20Sopenharmony_ci * If FPU already in use, we shouldn't get this at all. 3008c2ecf20Sopenharmony_ci */ 3018c2ecf20Sopenharmony_ci if (WARN_ON(!_kvm_guest_has_fpu(&vcpu->arch) || 3028c2ecf20Sopenharmony_ci vcpu->arch.aux_inuse & KVM_LARCH_FPU)) { 3038c2ecf20Sopenharmony_ci kvm_err("%s internal error\n", __func__); 3048c2ecf20Sopenharmony_ci run->exit_reason = KVM_EXIT_INTERNAL_ERROR; 3058c2ecf20Sopenharmony_ci return RESUME_HOST; 3068c2ecf20Sopenharmony_ci } 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci kvm_own_fpu(vcpu); 3098c2ecf20Sopenharmony_ci return RESUME_GUEST; 3108c2ecf20Sopenharmony_ci} 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci/** 3138c2ecf20Sopenharmony_ci * _kvm_handle_lsx_disabled() - Guest used LSX while disabled in root. 3148c2ecf20Sopenharmony_ci * @vcpu: Virtual CPU context. 3158c2ecf20Sopenharmony_ci * 3168c2ecf20Sopenharmony_ci * Handle when the guest attempts to use LSX when it is disabled in the root 3178c2ecf20Sopenharmony_ci * context. 3188c2ecf20Sopenharmony_ci */ 3198c2ecf20Sopenharmony_cistatic int _kvm_handle_lsx_disabled(struct kvm_vcpu *vcpu) 3208c2ecf20Sopenharmony_ci{ 3218c2ecf20Sopenharmony_ci struct kvm_run *run = vcpu->run; 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci /* 3248c2ecf20Sopenharmony_ci * If LSX not present or not exposed to guest, the LSX operation 3258c2ecf20Sopenharmony_ci * should have been treated as a reserved instruction! 3268c2ecf20Sopenharmony_ci * If LSX already in use, we shouldn't get this at all. 3278c2ecf20Sopenharmony_ci */ 3288c2ecf20Sopenharmony_ci if (!_kvm_guest_has_lsx(&vcpu->arch) || 3298c2ecf20Sopenharmony_ci !(kvm_read_gcsr_euen() & KVM_EUEN_LSXEN) || 3308c2ecf20Sopenharmony_ci vcpu->arch.aux_inuse & KVM_LARCH_LSX) { 3318c2ecf20Sopenharmony_ci kvm_err("%s internal error, lsx %d guest euen %llx aux %x", 3328c2ecf20Sopenharmony_ci __func__, _kvm_guest_has_lsx(&vcpu->arch), 3338c2ecf20Sopenharmony_ci kvm_read_gcsr_euen(), vcpu->arch.aux_inuse); 3348c2ecf20Sopenharmony_ci run->exit_reason = KVM_EXIT_INTERNAL_ERROR; 3358c2ecf20Sopenharmony_ci return RESUME_HOST; 3368c2ecf20Sopenharmony_ci } 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci kvm_own_lsx(vcpu); 3398c2ecf20Sopenharmony_ci return RESUME_GUEST; 3408c2ecf20Sopenharmony_ci} 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_cibool _kvm_guest_has_lasx(struct kvm_vcpu *vcpu) 3438c2ecf20Sopenharmony_ci{ 3448c2ecf20Sopenharmony_ci return cpu_has_lasx && vcpu->arch.lsx_enabled && vcpu->kvm->arch.cpucfg_lasx; 3458c2ecf20Sopenharmony_ci} 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci/** 3488c2ecf20Sopenharmony_ci * _kvm_handle_lasx_disabled() - Guest used LASX while disabled in root. 3498c2ecf20Sopenharmony_ci * @vcpu: Virtual CPU context. 3508c2ecf20Sopenharmony_ci * 3518c2ecf20Sopenharmony_ci * Handle when the guest attempts to use LASX when it is disabled in the root 3528c2ecf20Sopenharmony_ci * context. 3538c2ecf20Sopenharmony_ci */ 3548c2ecf20Sopenharmony_cistatic int _kvm_handle_lasx_disabled(struct kvm_vcpu *vcpu) 3558c2ecf20Sopenharmony_ci{ 3568c2ecf20Sopenharmony_ci struct kvm_run *run = vcpu->run; 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci /* 3598c2ecf20Sopenharmony_ci * If LASX not present or not exposed to guest, the LASX operation 3608c2ecf20Sopenharmony_ci * should have been treated as a reserved instruction! 3618c2ecf20Sopenharmony_ci * If LASX already in use, we shouldn't get this at all. 3628c2ecf20Sopenharmony_ci */ 3638c2ecf20Sopenharmony_ci if (!_kvm_guest_has_lasx(vcpu) || 3648c2ecf20Sopenharmony_ci !(kvm_read_gcsr_euen() & KVM_EUEN_LSXEN) || 3658c2ecf20Sopenharmony_ci !(kvm_read_gcsr_euen() & KVM_EUEN_LASXEN) || 3668c2ecf20Sopenharmony_ci vcpu->arch.aux_inuse & KVM_LARCH_LASX) { 3678c2ecf20Sopenharmony_ci kvm_err("%s internal error, lasx %d guest euen %llx aux %x", 3688c2ecf20Sopenharmony_ci __func__, _kvm_guest_has_lasx(vcpu), 3698c2ecf20Sopenharmony_ci kvm_read_gcsr_euen(), vcpu->arch.aux_inuse); 3708c2ecf20Sopenharmony_ci run->exit_reason = KVM_EXIT_INTERNAL_ERROR; 3718c2ecf20Sopenharmony_ci return RESUME_HOST; 3728c2ecf20Sopenharmony_ci } 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci kvm_own_lasx(vcpu); 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci return RESUME_GUEST; 3778c2ecf20Sopenharmony_ci} 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci/** 3808c2ecf20Sopenharmony_ci * _kvm_handle_fpu_disabled() - Guest used lbt however it is disabled at host 3818c2ecf20Sopenharmony_ci * @vcpu: Virtual CPU context. 3828c2ecf20Sopenharmony_ci * 3838c2ecf20Sopenharmony_ci * Handle when the guest attempts to use lbt which hasn't been allowed 3848c2ecf20Sopenharmony_ci * by the root context. 3858c2ecf20Sopenharmony_ci */ 3868c2ecf20Sopenharmony_cistatic int _kvm_handle_lbt_disabled(struct kvm_vcpu *vcpu) 3878c2ecf20Sopenharmony_ci{ 3888c2ecf20Sopenharmony_ci struct kvm_run *run = vcpu->run; 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci /* 3918c2ecf20Sopenharmony_ci * If guest LBT not present, the LBT operation should have been 3928c2ecf20Sopenharmony_ci * treated as a reserved instruction! 3938c2ecf20Sopenharmony_ci * If LBT already in use, we shouldn't get this at all. 3948c2ecf20Sopenharmony_ci */ 3958c2ecf20Sopenharmony_ci if (vcpu->arch.aux_inuse & KVM_LARCH_LBT) { 3968c2ecf20Sopenharmony_ci kvm_err("%s internal error\n", __func__); 3978c2ecf20Sopenharmony_ci run->exit_reason = KVM_EXIT_INTERNAL_ERROR; 3988c2ecf20Sopenharmony_ci return RESUME_HOST; 3998c2ecf20Sopenharmony_ci } 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci kvm_own_lbt(vcpu); 4028c2ecf20Sopenharmony_ci return RESUME_GUEST; 4038c2ecf20Sopenharmony_ci} 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_cistatic int _kvm_handle_read_fault(struct kvm_vcpu *vcpu) 4068c2ecf20Sopenharmony_ci{ 4078c2ecf20Sopenharmony_ci struct kvm_run *run = vcpu->run; 4088c2ecf20Sopenharmony_ci ulong badv = vcpu->arch.badv; 4098c2ecf20Sopenharmony_ci larch_inst inst; 4108c2ecf20Sopenharmony_ci enum emulation_result er = EMULATE_DONE; 4118c2ecf20Sopenharmony_ci int ret = RESUME_GUEST; 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci if (kvm_handle_mm_fault(vcpu, badv, false)) { 4148c2ecf20Sopenharmony_ci /* A code fetch fault doesn't count as an MMIO */ 4158c2ecf20Sopenharmony_ci if (kvm_is_ifetch_fault(&vcpu->arch)) { 4168c2ecf20Sopenharmony_ci kvm_err("%s ifetch error addr:%lx\n", __func__, badv); 4178c2ecf20Sopenharmony_ci run->exit_reason = KVM_EXIT_INTERNAL_ERROR; 4188c2ecf20Sopenharmony_ci return RESUME_HOST; 4198c2ecf20Sopenharmony_ci } 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci /* Treat as MMIO */ 4228c2ecf20Sopenharmony_ci inst.word = vcpu->arch.badi; 4238c2ecf20Sopenharmony_ci er = _kvm_emu_mmio_read(vcpu, inst); 4248c2ecf20Sopenharmony_ci if (er == EMULATE_FAIL) { 4258c2ecf20Sopenharmony_ci kvm_err("Guest Emulate Load failed: PC: %#lx, BadVaddr: %#lx\n", 4268c2ecf20Sopenharmony_ci vcpu->arch.pc, badv); 4278c2ecf20Sopenharmony_ci run->exit_reason = KVM_EXIT_INTERNAL_ERROR; 4288c2ecf20Sopenharmony_ci } 4298c2ecf20Sopenharmony_ci } 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci if (er == EMULATE_DONE) { 4328c2ecf20Sopenharmony_ci ret = RESUME_GUEST; 4338c2ecf20Sopenharmony_ci } else if (er == EMULATE_DO_MMIO) { 4348c2ecf20Sopenharmony_ci run->exit_reason = KVM_EXIT_MMIO; 4358c2ecf20Sopenharmony_ci ret = RESUME_HOST; 4368c2ecf20Sopenharmony_ci } else { 4378c2ecf20Sopenharmony_ci run->exit_reason = KVM_EXIT_INTERNAL_ERROR; 4388c2ecf20Sopenharmony_ci ret = RESUME_HOST; 4398c2ecf20Sopenharmony_ci } 4408c2ecf20Sopenharmony_ci return ret; 4418c2ecf20Sopenharmony_ci} 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_cistatic int _kvm_handle_write_fault(struct kvm_vcpu *vcpu) 4448c2ecf20Sopenharmony_ci{ 4458c2ecf20Sopenharmony_ci struct kvm_run *run = vcpu->run; 4468c2ecf20Sopenharmony_ci ulong badv = vcpu->arch.badv; 4478c2ecf20Sopenharmony_ci larch_inst inst; 4488c2ecf20Sopenharmony_ci enum emulation_result er = EMULATE_DONE; 4498c2ecf20Sopenharmony_ci int ret = RESUME_GUEST; 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci if (kvm_handle_mm_fault(vcpu, badv, true)) { 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci /* Treat as MMIO */ 4548c2ecf20Sopenharmony_ci inst.word = vcpu->arch.badi; 4558c2ecf20Sopenharmony_ci er = _kvm_emu_mmio_write(vcpu, inst); 4568c2ecf20Sopenharmony_ci if (er == EMULATE_FAIL) { 4578c2ecf20Sopenharmony_ci kvm_err("Guest Emulate Store failed: PC: %#lx, BadVaddr: %#lx\n", 4588c2ecf20Sopenharmony_ci vcpu->arch.pc, badv); 4598c2ecf20Sopenharmony_ci run->exit_reason = KVM_EXIT_INTERNAL_ERROR; 4608c2ecf20Sopenharmony_ci } 4618c2ecf20Sopenharmony_ci } 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci if (er == EMULATE_DONE) { 4648c2ecf20Sopenharmony_ci ret = RESUME_GUEST; 4658c2ecf20Sopenharmony_ci } else if (er == EMULATE_DO_MMIO) { 4668c2ecf20Sopenharmony_ci run->exit_reason = KVM_EXIT_MMIO; 4678c2ecf20Sopenharmony_ci ret = RESUME_HOST; 4688c2ecf20Sopenharmony_ci } else { 4698c2ecf20Sopenharmony_ci run->exit_reason = KVM_EXIT_INTERNAL_ERROR; 4708c2ecf20Sopenharmony_ci ret = RESUME_HOST; 4718c2ecf20Sopenharmony_ci } 4728c2ecf20Sopenharmony_ci return ret; 4738c2ecf20Sopenharmony_ci} 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_cistatic int _kvm_handle_debug(struct kvm_vcpu *vcpu) 4768c2ecf20Sopenharmony_ci{ 4778c2ecf20Sopenharmony_ci uint32_t fwps, mwps; 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci fwps = kvm_csr_readq(KVM_CSR_FWPS); 4808c2ecf20Sopenharmony_ci mwps = kvm_csr_readq(KVM_CSR_MWPS); 4818c2ecf20Sopenharmony_ci if (fwps & 0xff) 4828c2ecf20Sopenharmony_ci kvm_csr_writeq(fwps, KVM_CSR_FWPS); 4838c2ecf20Sopenharmony_ci if (mwps & 0xff) 4848c2ecf20Sopenharmony_ci kvm_csr_writeq(mwps, KVM_CSR_MWPS); 4858c2ecf20Sopenharmony_ci vcpu->run->debug.arch.exception = EXCCODE_WATCH; 4868c2ecf20Sopenharmony_ci vcpu->run->debug.arch.fwps = fwps; 4878c2ecf20Sopenharmony_ci vcpu->run->debug.arch.mwps = mwps; 4888c2ecf20Sopenharmony_ci vcpu->run->exit_reason = KVM_EXIT_DEBUG; 4898c2ecf20Sopenharmony_ci return RESUME_HOST; 4908c2ecf20Sopenharmony_ci} 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_cistatic exit_handle_fn _kvm_fault_tables[EXCCODE_INT_START] = { 4938c2ecf20Sopenharmony_ci [EXCCODE_TLBL] = _kvm_handle_read_fault, 4948c2ecf20Sopenharmony_ci [EXCCODE_TLBS] = _kvm_handle_write_fault, 4958c2ecf20Sopenharmony_ci [EXCCODE_TLBI] = _kvm_handle_read_fault, 4968c2ecf20Sopenharmony_ci [EXCCODE_TLBM] = _kvm_handle_write_fault, 4978c2ecf20Sopenharmony_ci [EXCCODE_TLBNR] = _kvm_handle_read_fault, 4988c2ecf20Sopenharmony_ci [EXCCODE_TLBNX] = _kvm_handle_read_fault, 4998c2ecf20Sopenharmony_ci [EXCCODE_FPDIS] = _kvm_handle_fpu_disabled, 5008c2ecf20Sopenharmony_ci [EXCCODE_LSXDIS] = _kvm_handle_lsx_disabled, 5018c2ecf20Sopenharmony_ci [EXCCODE_LASXDIS] = _kvm_handle_lasx_disabled, 5028c2ecf20Sopenharmony_ci [EXCCODE_WATCH] = _kvm_handle_debug, 5038c2ecf20Sopenharmony_ci [EXCCODE_GSPR] = _kvm_handle_gspr, 5048c2ecf20Sopenharmony_ci [EXCCODE_HVC] = _kvm_handle_hypcall, 5058c2ecf20Sopenharmony_ci [EXCCODE_GCM] = _kvm_handle_gcm, 5068c2ecf20Sopenharmony_ci [EXCCODE_BTDIS] = _kvm_handle_lbt_disabled, 5078c2ecf20Sopenharmony_ci}; 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ciint _kvm_handle_fault(struct kvm_vcpu *vcpu, int fault) 5108c2ecf20Sopenharmony_ci{ 5118c2ecf20Sopenharmony_ci return _kvm_fault_tables[fault](vcpu); 5128c2ecf20Sopenharmony_ci} 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_civoid _kvm_init_fault(void) 5158c2ecf20Sopenharmony_ci{ 5168c2ecf20Sopenharmony_ci int i; 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci for (i = 0; i < EXCCODE_INT_START; i++) 5198c2ecf20Sopenharmony_ci if (!_kvm_fault_tables[i]) 5208c2ecf20Sopenharmony_ci _kvm_fault_tables[i] = _kvm_fault_ni; 5218c2ecf20Sopenharmony_ci} 522