// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2020-2022 Loongson Technology Corporation Limited */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kvmcpu.h" #include "trace.h" int _kvm_emu_idle(struct kvm_vcpu *vcpu) { ++vcpu->stat.idle_exits; trace_kvm_exit(vcpu, KVM_TRACE_EXIT_IDLE); if (!vcpu->arch.irq_pending) { kvm_save_timer(vcpu); kvm_vcpu_block(vcpu); /* * We we are runnable, then definitely go off to user space to * check if any I/O interrupts are pending. */ if (kvm_check_request(KVM_REQ_UNHALT, vcpu)) { kvm_clear_request(KVM_REQ_UNHALT, vcpu); vcpu->run->exit_reason = KVM_EXIT_IRQ_WINDOW_OPEN; } } if (kvm_check_request(KVM_REQ_EVENT, vcpu)) { vcpu->arch.pv.pv_unhalted = false; } return EMULATE_DONE; } int _kvm_emu_mmio_write(struct kvm_vcpu *vcpu, larch_inst inst) { struct kvm_run *run = vcpu->run; unsigned int rd, op8, opcode; unsigned long rd_val = 0; void *data = run->mmio.data; unsigned long curr_pc; int ret = 0; /* * Update PC and hold onto current PC in case there is * an error and we want to rollback the PC */ curr_pc = vcpu->arch.pc; update_pc(&vcpu->arch); op8 = (inst.word >> 24) & 0xff; run->mmio.phys_addr = vcpu->arch.badv; if (run->mmio.phys_addr == KVM_INVALID_ADDR) goto out_fail; if (op8 < 0x28) { /* stptrw/d process */ rd = inst.reg2i14_format.rd; opcode = inst.reg2i14_format.opcode; switch (opcode) { case stptrd_op: run->mmio.len = 8; *(unsigned long *)data = vcpu->arch.gprs[rd]; break; case stptrw_op: run->mmio.len = 4; *(unsigned int *)data = vcpu->arch.gprs[rd]; break; default: break; } } else if (op8 < 0x30) { /* st.b/h/w/d process */ rd = inst.reg2i12_format.rd; opcode = inst.reg2i12_format.opcode; rd_val = vcpu->arch.gprs[rd]; switch (opcode) { case std_op: run->mmio.len = 8; *(unsigned long *)data = rd_val; break; case stw_op: run->mmio.len = 4; *(unsigned int *)data = rd_val; break; case sth_op: run->mmio.len = 2; *(unsigned short *)data = rd_val; break; case stb_op: run->mmio.len = 1; *(unsigned char *)data = rd_val; break; default: kvm_err("Store not yet supporded (inst=0x%08x)\n", inst.word); kvm_arch_vcpu_dump_regs(vcpu); goto out_fail; } } else if (op8 == 0x38) { /* stxb/h/w/d process */ rd = inst.reg3_format.rd; opcode = inst.reg3_format.opcode; switch (opcode) { case stxb_op: run->mmio.len = 1; *(unsigned char *)data = vcpu->arch.gprs[rd]; break; case stxh_op: run->mmio.len = 2; *(unsigned short *)data = vcpu->arch.gprs[rd]; break; case stxw_op: run->mmio.len = 4; *(unsigned int *)data = vcpu->arch.gprs[rd]; break; case stxd_op: run->mmio.len = 8; *(unsigned long *)data = vcpu->arch.gprs[rd]; break; default: kvm_err("Store not yet supporded (inst=0x%08x)\n", inst.word); kvm_arch_vcpu_dump_regs(vcpu); goto out_fail; } } else { kvm_err("Store not yet supporded (inst=0x%08x)\n", inst.word); kvm_arch_vcpu_dump_regs(vcpu); goto out_fail; } /* All MMIO emulate in kernel go through the common interface */ ret = kvm_io_bus_write(vcpu, KVM_MMIO_BUS, run->mmio.phys_addr, run->mmio.len, data); if (!ret) { vcpu->mmio_needed = 0; return EMULATE_DONE; } run->mmio.is_write = 1; vcpu->mmio_needed = 1; vcpu->mmio_is_write = 1; return EMULATE_DO_MMIO; out_fail: /* Rollback PC if emulation was unsuccessful */ vcpu->arch.pc = curr_pc; return EMULATE_FAIL; } int _kvm_emu_mmio_read(struct kvm_vcpu *vcpu, larch_inst inst) { unsigned int op8, opcode, rd; int ret = 0; struct kvm_run *run = vcpu->run; run->mmio.phys_addr = vcpu->arch.badv; if (run->mmio.phys_addr == KVM_INVALID_ADDR) return EMULATE_FAIL; vcpu->mmio_needed = 2; /* signed */ op8 = (inst.word >> 24) & 0xff; if (op8 < 0x28) { /* ldptr.w/d process */ rd = inst.reg2i14_format.rd; opcode = inst.reg2i14_format.opcode; switch (opcode) { case ldptrd_op: run->mmio.len = 8; break; case ldptrw_op: run->mmio.len = 4; break; default: break; } } else if (op8 < 0x2f) { /* ld.b/h/w/d, ld.bu/hu/wu process */ rd = inst.reg2i12_format.rd; opcode = inst.reg2i12_format.opcode; switch (opcode) { case ldd_op: run->mmio.len = 8; break; case ldwu_op: vcpu->mmio_needed = 1; /* unsigned */ run->mmio.len = 4; break; case ldw_op: run->mmio.len = 4; break; case ldhu_op: vcpu->mmio_needed = 1; /* unsigned */ run->mmio.len = 2; break; case ldh_op: run->mmio.len = 2; break; case ldbu_op: vcpu->mmio_needed = 1; /* unsigned */ run->mmio.len = 1; break; case ldb_op: run->mmio.len = 1; break; default: kvm_err("Load not yet supporded (inst=0x%08x)\n", inst.word); kvm_arch_vcpu_dump_regs(vcpu); vcpu->mmio_needed = 0; return EMULATE_FAIL; } } else if (op8 == 0x38) { /* ldxb/h/w/d, ldxb/h/wu, ldgtb/h/w/d, ldleb/h/w/d process */ rd = inst.reg3_format.rd; opcode = inst.reg3_format.opcode; switch (opcode) { case ldxb_op: run->mmio.len = 1; break; case ldxbu_op: run->mmio.len = 1; vcpu->mmio_needed = 1; /* unsigned */ break; case ldxh_op: run->mmio.len = 2; break; case ldxhu_op: run->mmio.len = 2; vcpu->mmio_needed = 1; /* unsigned */ break; case ldxw_op: run->mmio.len = 4; break; case ldxwu_op: run->mmio.len = 4; vcpu->mmio_needed = 1; /* unsigned */ break; case ldxd_op: run->mmio.len = 8; break; default: kvm_err("Load not yet supporded (inst=0x%08x)\n", inst.word); kvm_arch_vcpu_dump_regs(vcpu); vcpu->mmio_needed = 0; return EMULATE_FAIL; } } else { kvm_err("Load not yet supporded (inst=0x%08x) @ %lx\n", inst.word, vcpu->arch.pc); vcpu->mmio_needed = 0; return EMULATE_FAIL; } /* Set for _kvm_complete_mmio_read use */ vcpu->arch.io_gpr = rd; ret = kvm_io_bus_read(vcpu, KVM_MMIO_BUS, run->mmio.phys_addr, run->mmio.len, run->mmio.data); run->mmio.is_write = 0; vcpu->mmio_is_write = 0; if (!ret) { _kvm_complete_mmio_read(vcpu, run); vcpu->mmio_needed = 0; return EMULATE_DONE; } return EMULATE_DO_MMIO; } int _kvm_complete_mmio_read(struct kvm_vcpu *vcpu, struct kvm_run *run) { unsigned long *gpr = &vcpu->arch.gprs[vcpu->arch.io_gpr]; enum emulation_result er = EMULATE_DONE; /* update with new PC */ update_pc(&vcpu->arch); switch (run->mmio.len) { case 8: *gpr = *(s64 *)run->mmio.data; break; case 4: if (vcpu->mmio_needed == 2) { *gpr = *(int *)run->mmio.data; } else *gpr = *(unsigned int *)run->mmio.data; break; case 2: if (vcpu->mmio_needed == 2) *gpr = *(short *) run->mmio.data; else *gpr = *(unsigned short *)run->mmio.data; break; case 1: if (vcpu->mmio_needed == 2) *gpr = *(char *) run->mmio.data; else *gpr = *(unsigned char *) run->mmio.data; break; default: kvm_err("Bad MMIO length: %d,addr is 0x%lx", run->mmio.len, vcpu->arch.badv); er = EMULATE_FAIL; break; } return er; }