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/ktime.h> 98c2ecf20Sopenharmony_ci#include <linux/kvm_host.h> 108c2ecf20Sopenharmony_ci#include <linux/vmalloc.h> 118c2ecf20Sopenharmony_ci#include <linux/fs.h> 128c2ecf20Sopenharmony_ci#include <linux/random.h> 138c2ecf20Sopenharmony_ci#include <asm/page.h> 148c2ecf20Sopenharmony_ci#include <asm/cacheflush.h> 158c2ecf20Sopenharmony_ci#include <asm/cacheops.h> 168c2ecf20Sopenharmony_ci#include <asm/cpu-info.h> 178c2ecf20Sopenharmony_ci#include <asm/mmu_context.h> 188c2ecf20Sopenharmony_ci#include <asm/tlbflush.h> 198c2ecf20Sopenharmony_ci#include <asm/inst.h> 208c2ecf20Sopenharmony_ci#include "kvmcpu.h" 218c2ecf20Sopenharmony_ci#include "trace.h" 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ciint _kvm_emu_idle(struct kvm_vcpu *vcpu) 248c2ecf20Sopenharmony_ci{ 258c2ecf20Sopenharmony_ci ++vcpu->stat.idle_exits; 268c2ecf20Sopenharmony_ci trace_kvm_exit(vcpu, KVM_TRACE_EXIT_IDLE); 278c2ecf20Sopenharmony_ci if (!vcpu->arch.irq_pending) { 288c2ecf20Sopenharmony_ci kvm_save_timer(vcpu); 298c2ecf20Sopenharmony_ci kvm_vcpu_block(vcpu); 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci /* 328c2ecf20Sopenharmony_ci * We we are runnable, then definitely go off to user space to 338c2ecf20Sopenharmony_ci * check if any I/O interrupts are pending. 348c2ecf20Sopenharmony_ci */ 358c2ecf20Sopenharmony_ci if (kvm_check_request(KVM_REQ_UNHALT, vcpu)) { 368c2ecf20Sopenharmony_ci kvm_clear_request(KVM_REQ_UNHALT, vcpu); 378c2ecf20Sopenharmony_ci vcpu->run->exit_reason = KVM_EXIT_IRQ_WINDOW_OPEN; 388c2ecf20Sopenharmony_ci } 398c2ecf20Sopenharmony_ci } 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci if (kvm_check_request(KVM_REQ_EVENT, vcpu)) { 428c2ecf20Sopenharmony_ci vcpu->arch.pv.pv_unhalted = false; 438c2ecf20Sopenharmony_ci } 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci return EMULATE_DONE; 468c2ecf20Sopenharmony_ci} 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ciint _kvm_emu_mmio_write(struct kvm_vcpu *vcpu, larch_inst inst) 498c2ecf20Sopenharmony_ci{ 508c2ecf20Sopenharmony_ci struct kvm_run *run = vcpu->run; 518c2ecf20Sopenharmony_ci unsigned int rd, op8, opcode; 528c2ecf20Sopenharmony_ci unsigned long rd_val = 0; 538c2ecf20Sopenharmony_ci void *data = run->mmio.data; 548c2ecf20Sopenharmony_ci unsigned long curr_pc; 558c2ecf20Sopenharmony_ci int ret = 0; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci /* 588c2ecf20Sopenharmony_ci * Update PC and hold onto current PC in case there is 598c2ecf20Sopenharmony_ci * an error and we want to rollback the PC 608c2ecf20Sopenharmony_ci */ 618c2ecf20Sopenharmony_ci curr_pc = vcpu->arch.pc; 628c2ecf20Sopenharmony_ci update_pc(&vcpu->arch); 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci op8 = (inst.word >> 24) & 0xff; 658c2ecf20Sopenharmony_ci run->mmio.phys_addr = vcpu->arch.badv; 668c2ecf20Sopenharmony_ci if (run->mmio.phys_addr == KVM_INVALID_ADDR) 678c2ecf20Sopenharmony_ci goto out_fail; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci if (op8 < 0x28) { 708c2ecf20Sopenharmony_ci /* stptrw/d process */ 718c2ecf20Sopenharmony_ci rd = inst.reg2i14_format.rd; 728c2ecf20Sopenharmony_ci opcode = inst.reg2i14_format.opcode; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci switch (opcode) { 758c2ecf20Sopenharmony_ci case stptrd_op: 768c2ecf20Sopenharmony_ci run->mmio.len = 8; 778c2ecf20Sopenharmony_ci *(unsigned long *)data = vcpu->arch.gprs[rd]; 788c2ecf20Sopenharmony_ci break; 798c2ecf20Sopenharmony_ci case stptrw_op: 808c2ecf20Sopenharmony_ci run->mmio.len = 4; 818c2ecf20Sopenharmony_ci *(unsigned int *)data = vcpu->arch.gprs[rd]; 828c2ecf20Sopenharmony_ci break; 838c2ecf20Sopenharmony_ci default: 848c2ecf20Sopenharmony_ci break; 858c2ecf20Sopenharmony_ci } 868c2ecf20Sopenharmony_ci } else if (op8 < 0x30) { 878c2ecf20Sopenharmony_ci /* st.b/h/w/d process */ 888c2ecf20Sopenharmony_ci rd = inst.reg2i12_format.rd; 898c2ecf20Sopenharmony_ci opcode = inst.reg2i12_format.opcode; 908c2ecf20Sopenharmony_ci rd_val = vcpu->arch.gprs[rd]; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci switch (opcode) { 938c2ecf20Sopenharmony_ci case std_op: 948c2ecf20Sopenharmony_ci run->mmio.len = 8; 958c2ecf20Sopenharmony_ci *(unsigned long *)data = rd_val; 968c2ecf20Sopenharmony_ci break; 978c2ecf20Sopenharmony_ci case stw_op: 988c2ecf20Sopenharmony_ci run->mmio.len = 4; 998c2ecf20Sopenharmony_ci *(unsigned int *)data = rd_val; 1008c2ecf20Sopenharmony_ci break; 1018c2ecf20Sopenharmony_ci case sth_op: 1028c2ecf20Sopenharmony_ci run->mmio.len = 2; 1038c2ecf20Sopenharmony_ci *(unsigned short *)data = rd_val; 1048c2ecf20Sopenharmony_ci break; 1058c2ecf20Sopenharmony_ci case stb_op: 1068c2ecf20Sopenharmony_ci run->mmio.len = 1; 1078c2ecf20Sopenharmony_ci *(unsigned char *)data = rd_val; 1088c2ecf20Sopenharmony_ci break; 1098c2ecf20Sopenharmony_ci default: 1108c2ecf20Sopenharmony_ci kvm_err("Store not yet supporded (inst=0x%08x)\n", 1118c2ecf20Sopenharmony_ci inst.word); 1128c2ecf20Sopenharmony_ci kvm_arch_vcpu_dump_regs(vcpu); 1138c2ecf20Sopenharmony_ci goto out_fail; 1148c2ecf20Sopenharmony_ci } 1158c2ecf20Sopenharmony_ci } else if (op8 == 0x38) { 1168c2ecf20Sopenharmony_ci /* stxb/h/w/d process */ 1178c2ecf20Sopenharmony_ci rd = inst.reg3_format.rd; 1188c2ecf20Sopenharmony_ci opcode = inst.reg3_format.opcode; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci switch (opcode) { 1218c2ecf20Sopenharmony_ci case stxb_op: 1228c2ecf20Sopenharmony_ci run->mmio.len = 1; 1238c2ecf20Sopenharmony_ci *(unsigned char *)data = vcpu->arch.gprs[rd]; 1248c2ecf20Sopenharmony_ci break; 1258c2ecf20Sopenharmony_ci case stxh_op: 1268c2ecf20Sopenharmony_ci run->mmio.len = 2; 1278c2ecf20Sopenharmony_ci *(unsigned short *)data = vcpu->arch.gprs[rd]; 1288c2ecf20Sopenharmony_ci break; 1298c2ecf20Sopenharmony_ci case stxw_op: 1308c2ecf20Sopenharmony_ci run->mmio.len = 4; 1318c2ecf20Sopenharmony_ci *(unsigned int *)data = vcpu->arch.gprs[rd]; 1328c2ecf20Sopenharmony_ci break; 1338c2ecf20Sopenharmony_ci case stxd_op: 1348c2ecf20Sopenharmony_ci run->mmio.len = 8; 1358c2ecf20Sopenharmony_ci *(unsigned long *)data = vcpu->arch.gprs[rd]; 1368c2ecf20Sopenharmony_ci break; 1378c2ecf20Sopenharmony_ci default: 1388c2ecf20Sopenharmony_ci kvm_err("Store not yet supporded (inst=0x%08x)\n", 1398c2ecf20Sopenharmony_ci inst.word); 1408c2ecf20Sopenharmony_ci kvm_arch_vcpu_dump_regs(vcpu); 1418c2ecf20Sopenharmony_ci goto out_fail; 1428c2ecf20Sopenharmony_ci } 1438c2ecf20Sopenharmony_ci } else { 1448c2ecf20Sopenharmony_ci kvm_err("Store not yet supporded (inst=0x%08x)\n", 1458c2ecf20Sopenharmony_ci inst.word); 1468c2ecf20Sopenharmony_ci kvm_arch_vcpu_dump_regs(vcpu); 1478c2ecf20Sopenharmony_ci goto out_fail; 1488c2ecf20Sopenharmony_ci } 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci /* All MMIO emulate in kernel go through the common interface */ 1518c2ecf20Sopenharmony_ci ret = kvm_io_bus_write(vcpu, KVM_MMIO_BUS, run->mmio.phys_addr, 1528c2ecf20Sopenharmony_ci run->mmio.len, data); 1538c2ecf20Sopenharmony_ci if (!ret) { 1548c2ecf20Sopenharmony_ci vcpu->mmio_needed = 0; 1558c2ecf20Sopenharmony_ci return EMULATE_DONE; 1568c2ecf20Sopenharmony_ci } 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci run->mmio.is_write = 1; 1598c2ecf20Sopenharmony_ci vcpu->mmio_needed = 1; 1608c2ecf20Sopenharmony_ci vcpu->mmio_is_write = 1; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci return EMULATE_DO_MMIO; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ciout_fail: 1658c2ecf20Sopenharmony_ci /* Rollback PC if emulation was unsuccessful */ 1668c2ecf20Sopenharmony_ci vcpu->arch.pc = curr_pc; 1678c2ecf20Sopenharmony_ci return EMULATE_FAIL; 1688c2ecf20Sopenharmony_ci} 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ciint _kvm_emu_mmio_read(struct kvm_vcpu *vcpu, larch_inst inst) 1728c2ecf20Sopenharmony_ci{ 1738c2ecf20Sopenharmony_ci unsigned int op8, opcode, rd; 1748c2ecf20Sopenharmony_ci int ret = 0; 1758c2ecf20Sopenharmony_ci struct kvm_run *run = vcpu->run; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci run->mmio.phys_addr = vcpu->arch.badv; 1788c2ecf20Sopenharmony_ci if (run->mmio.phys_addr == KVM_INVALID_ADDR) 1798c2ecf20Sopenharmony_ci return EMULATE_FAIL; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci vcpu->mmio_needed = 2; /* signed */ 1828c2ecf20Sopenharmony_ci op8 = (inst.word >> 24) & 0xff; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci if (op8 < 0x28) { 1858c2ecf20Sopenharmony_ci /* ldptr.w/d process */ 1868c2ecf20Sopenharmony_ci rd = inst.reg2i14_format.rd; 1878c2ecf20Sopenharmony_ci opcode = inst.reg2i14_format.opcode; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci switch (opcode) { 1908c2ecf20Sopenharmony_ci case ldptrd_op: 1918c2ecf20Sopenharmony_ci run->mmio.len = 8; 1928c2ecf20Sopenharmony_ci break; 1938c2ecf20Sopenharmony_ci case ldptrw_op: 1948c2ecf20Sopenharmony_ci run->mmio.len = 4; 1958c2ecf20Sopenharmony_ci break; 1968c2ecf20Sopenharmony_ci default: 1978c2ecf20Sopenharmony_ci break; 1988c2ecf20Sopenharmony_ci } 1998c2ecf20Sopenharmony_ci } else if (op8 < 0x2f) { 2008c2ecf20Sopenharmony_ci /* ld.b/h/w/d, ld.bu/hu/wu process */ 2018c2ecf20Sopenharmony_ci rd = inst.reg2i12_format.rd; 2028c2ecf20Sopenharmony_ci opcode = inst.reg2i12_format.opcode; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci switch (opcode) { 2058c2ecf20Sopenharmony_ci case ldd_op: 2068c2ecf20Sopenharmony_ci run->mmio.len = 8; 2078c2ecf20Sopenharmony_ci break; 2088c2ecf20Sopenharmony_ci case ldwu_op: 2098c2ecf20Sopenharmony_ci vcpu->mmio_needed = 1; /* unsigned */ 2108c2ecf20Sopenharmony_ci run->mmio.len = 4; 2118c2ecf20Sopenharmony_ci break; 2128c2ecf20Sopenharmony_ci case ldw_op: 2138c2ecf20Sopenharmony_ci run->mmio.len = 4; 2148c2ecf20Sopenharmony_ci break; 2158c2ecf20Sopenharmony_ci case ldhu_op: 2168c2ecf20Sopenharmony_ci vcpu->mmio_needed = 1; /* unsigned */ 2178c2ecf20Sopenharmony_ci run->mmio.len = 2; 2188c2ecf20Sopenharmony_ci break; 2198c2ecf20Sopenharmony_ci case ldh_op: 2208c2ecf20Sopenharmony_ci run->mmio.len = 2; 2218c2ecf20Sopenharmony_ci break; 2228c2ecf20Sopenharmony_ci case ldbu_op: 2238c2ecf20Sopenharmony_ci vcpu->mmio_needed = 1; /* unsigned */ 2248c2ecf20Sopenharmony_ci run->mmio.len = 1; 2258c2ecf20Sopenharmony_ci break; 2268c2ecf20Sopenharmony_ci case ldb_op: 2278c2ecf20Sopenharmony_ci run->mmio.len = 1; 2288c2ecf20Sopenharmony_ci break; 2298c2ecf20Sopenharmony_ci default: 2308c2ecf20Sopenharmony_ci kvm_err("Load not yet supporded (inst=0x%08x)\n", 2318c2ecf20Sopenharmony_ci inst.word); 2328c2ecf20Sopenharmony_ci kvm_arch_vcpu_dump_regs(vcpu); 2338c2ecf20Sopenharmony_ci vcpu->mmio_needed = 0; 2348c2ecf20Sopenharmony_ci return EMULATE_FAIL; 2358c2ecf20Sopenharmony_ci } 2368c2ecf20Sopenharmony_ci } else if (op8 == 0x38) { 2378c2ecf20Sopenharmony_ci /* ldxb/h/w/d, ldxb/h/wu, ldgtb/h/w/d, ldleb/h/w/d process */ 2388c2ecf20Sopenharmony_ci rd = inst.reg3_format.rd; 2398c2ecf20Sopenharmony_ci opcode = inst.reg3_format.opcode; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci switch (opcode) { 2428c2ecf20Sopenharmony_ci case ldxb_op: 2438c2ecf20Sopenharmony_ci run->mmio.len = 1; 2448c2ecf20Sopenharmony_ci break; 2458c2ecf20Sopenharmony_ci case ldxbu_op: 2468c2ecf20Sopenharmony_ci run->mmio.len = 1; 2478c2ecf20Sopenharmony_ci vcpu->mmio_needed = 1; /* unsigned */ 2488c2ecf20Sopenharmony_ci break; 2498c2ecf20Sopenharmony_ci case ldxh_op: 2508c2ecf20Sopenharmony_ci run->mmio.len = 2; 2518c2ecf20Sopenharmony_ci break; 2528c2ecf20Sopenharmony_ci case ldxhu_op: 2538c2ecf20Sopenharmony_ci run->mmio.len = 2; 2548c2ecf20Sopenharmony_ci vcpu->mmio_needed = 1; /* unsigned */ 2558c2ecf20Sopenharmony_ci break; 2568c2ecf20Sopenharmony_ci case ldxw_op: 2578c2ecf20Sopenharmony_ci run->mmio.len = 4; 2588c2ecf20Sopenharmony_ci break; 2598c2ecf20Sopenharmony_ci case ldxwu_op: 2608c2ecf20Sopenharmony_ci run->mmio.len = 4; 2618c2ecf20Sopenharmony_ci vcpu->mmio_needed = 1; /* unsigned */ 2628c2ecf20Sopenharmony_ci break; 2638c2ecf20Sopenharmony_ci case ldxd_op: 2648c2ecf20Sopenharmony_ci run->mmio.len = 8; 2658c2ecf20Sopenharmony_ci break; 2668c2ecf20Sopenharmony_ci default: 2678c2ecf20Sopenharmony_ci kvm_err("Load not yet supporded (inst=0x%08x)\n", 2688c2ecf20Sopenharmony_ci inst.word); 2698c2ecf20Sopenharmony_ci kvm_arch_vcpu_dump_regs(vcpu); 2708c2ecf20Sopenharmony_ci vcpu->mmio_needed = 0; 2718c2ecf20Sopenharmony_ci return EMULATE_FAIL; 2728c2ecf20Sopenharmony_ci } 2738c2ecf20Sopenharmony_ci } else { 2748c2ecf20Sopenharmony_ci kvm_err("Load not yet supporded (inst=0x%08x) @ %lx\n", 2758c2ecf20Sopenharmony_ci inst.word, vcpu->arch.pc); 2768c2ecf20Sopenharmony_ci vcpu->mmio_needed = 0; 2778c2ecf20Sopenharmony_ci return EMULATE_FAIL; 2788c2ecf20Sopenharmony_ci } 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci /* Set for _kvm_complete_mmio_read use */ 2818c2ecf20Sopenharmony_ci vcpu->arch.io_gpr = rd; 2828c2ecf20Sopenharmony_ci ret = kvm_io_bus_read(vcpu, KVM_MMIO_BUS, run->mmio.phys_addr, 2838c2ecf20Sopenharmony_ci run->mmio.len, run->mmio.data); 2848c2ecf20Sopenharmony_ci run->mmio.is_write = 0; 2858c2ecf20Sopenharmony_ci vcpu->mmio_is_write = 0; 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci if (!ret) { 2888c2ecf20Sopenharmony_ci _kvm_complete_mmio_read(vcpu, run); 2898c2ecf20Sopenharmony_ci vcpu->mmio_needed = 0; 2908c2ecf20Sopenharmony_ci return EMULATE_DONE; 2918c2ecf20Sopenharmony_ci } 2928c2ecf20Sopenharmony_ci return EMULATE_DO_MMIO; 2938c2ecf20Sopenharmony_ci} 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ciint _kvm_complete_mmio_read(struct kvm_vcpu *vcpu, struct kvm_run *run) 2968c2ecf20Sopenharmony_ci{ 2978c2ecf20Sopenharmony_ci unsigned long *gpr = &vcpu->arch.gprs[vcpu->arch.io_gpr]; 2988c2ecf20Sopenharmony_ci enum emulation_result er = EMULATE_DONE; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci /* update with new PC */ 3018c2ecf20Sopenharmony_ci update_pc(&vcpu->arch); 3028c2ecf20Sopenharmony_ci switch (run->mmio.len) { 3038c2ecf20Sopenharmony_ci case 8: 3048c2ecf20Sopenharmony_ci *gpr = *(s64 *)run->mmio.data; 3058c2ecf20Sopenharmony_ci break; 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci case 4: 3088c2ecf20Sopenharmony_ci if (vcpu->mmio_needed == 2) { 3098c2ecf20Sopenharmony_ci *gpr = *(int *)run->mmio.data; 3108c2ecf20Sopenharmony_ci } else 3118c2ecf20Sopenharmony_ci *gpr = *(unsigned int *)run->mmio.data; 3128c2ecf20Sopenharmony_ci break; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci case 2: 3158c2ecf20Sopenharmony_ci if (vcpu->mmio_needed == 2) 3168c2ecf20Sopenharmony_ci *gpr = *(short *) run->mmio.data; 3178c2ecf20Sopenharmony_ci else 3188c2ecf20Sopenharmony_ci *gpr = *(unsigned short *)run->mmio.data; 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci break; 3218c2ecf20Sopenharmony_ci case 1: 3228c2ecf20Sopenharmony_ci if (vcpu->mmio_needed == 2) 3238c2ecf20Sopenharmony_ci *gpr = *(char *) run->mmio.data; 3248c2ecf20Sopenharmony_ci else 3258c2ecf20Sopenharmony_ci *gpr = *(unsigned char *) run->mmio.data; 3268c2ecf20Sopenharmony_ci break; 3278c2ecf20Sopenharmony_ci default: 3288c2ecf20Sopenharmony_ci kvm_err("Bad MMIO length: %d,addr is 0x%lx", 3298c2ecf20Sopenharmony_ci run->mmio.len, vcpu->arch.badv); 3308c2ecf20Sopenharmony_ci er = EMULATE_FAIL; 3318c2ecf20Sopenharmony_ci break; 3328c2ecf20Sopenharmony_ci } 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci return er; 3358c2ecf20Sopenharmony_ci} 336