#include "kvm/kvm-cpu.h" #include "kvm/term.h" #include static int debug_fd; void kvm_cpu__set_debug_fd(int fd) { debug_fd = fd; } int kvm_cpu__get_debug_fd(void) { return debug_fd; } void kvm_cpu__delete(struct kvm_cpu *vcpu) { free(vcpu); } static struct kvm_cpu *kvm_cpu__new(struct kvm *kvm) { struct kvm_cpu *vcpu; vcpu = calloc(1, sizeof(*vcpu)); if (!vcpu) return NULL; vcpu->kvm = kvm; return vcpu; } struct kvm_cpu *kvm_cpu__arch_init(struct kvm *kvm, unsigned long cpu_id) { struct kvm_cpu *vcpu; int mmap_size; int coalesced_offset; vcpu = kvm_cpu__new(kvm); if (!vcpu) return NULL; vcpu->cpu_id = cpu_id; vcpu->vcpu_fd = ioctl(vcpu->kvm->vm_fd, KVM_CREATE_VCPU, cpu_id); if (vcpu->vcpu_fd < 0) die_perror("KVM_CREATE_VCPU ioctl"); mmap_size = ioctl(vcpu->kvm->sys_fd, KVM_GET_VCPU_MMAP_SIZE, 0); if (mmap_size < 0) die_perror("KVM_GET_VCPU_MMAP_SIZE ioctl"); vcpu->kvm_run = mmap(NULL, mmap_size, PROT_RW, MAP_SHARED, vcpu->vcpu_fd, 0); if (vcpu->kvm_run == MAP_FAILED) die("unable to mmap vcpu fd"); vcpu->is_running = true; coalesced_offset = ioctl(kvm->sys_fd, KVM_CHECK_EXTENSION, KVM_CAP_COALESCED_MMIO); if (coalesced_offset) vcpu->ring = (void *)vcpu->kvm_run + (coalesced_offset * PAGE_SIZE); return vcpu; } static void kvm_cpu__setup_regs(struct kvm_cpu *vcpu) { uint32_t v; struct kvm_one_reg one_reg; memset(&vcpu->regs, 0, sizeof(vcpu->regs)); vcpu->regs.pc = vcpu->kvm->arch.entry_point; vcpu->regs.gpr[4] = vcpu->kvm->arch.argc; vcpu->regs.gpr[5] = vcpu->kvm->arch.argv; if (ioctl(vcpu->vcpu_fd, KVM_SET_REGS, &vcpu->regs) < 0) die_perror("KVM_SET_REGS failed"); one_reg.id = KVM_REG_MIPS | KVM_REG_SIZE_U32 | (0x10000 + 8 * 12 + 0); /* Status */ one_reg.addr = (unsigned long)(uint32_t *)&v; v = 6; if (ioctl(vcpu->vcpu_fd, KVM_SET_ONE_REG, &one_reg) < 0) die_perror("KVM_SET_ONE_REG failed"); } /** * kvm_cpu__reset_vcpu - reset virtual CPU to a known state */ void kvm_cpu__reset_vcpu(struct kvm_cpu *vcpu) { kvm_cpu__setup_regs(vcpu); } static bool kvm_cpu__hypercall_write_cons(struct kvm_cpu *vcpu) { int term = (int)vcpu->kvm_run->hypercall.args[0]; u64 addr = vcpu->kvm_run->hypercall.args[1]; int len = (int)vcpu->kvm_run->hypercall.args[2]; char *host_addr; if (term < 0 || term >= TERM_MAX_DEVS) { pr_warning("hypercall_write_cons term out of range <%d>", term); return false; } if ((addr & 0xffffffffc0000000ull) == 0xffffffff80000000ull) addr &= 0x1ffffffful; /* Convert KSEG{0,1} to physical. */ if ((addr & 0xc000000000000000ull) == 0x8000000000000000ull) addr &= 0x07ffffffffffffffull; /* Convert XKPHYS to pysical */ host_addr = guest_flat_to_host(vcpu->kvm, addr); if (!host_addr) { pr_warning("hypercall_write_cons unmapped physaddr %llx", (unsigned long long)addr); return false; } if ((len <= 0) || !host_ptr_in_ram(vcpu->kvm, host_addr + len)) { pr_warning("hypercall_write_cons len out of range <%d>", len); return false; } term_putc(host_addr, len, term); return true; } #define KVM_HC_MIPS_CONSOLE_OUTPUT 8 bool kvm_cpu__handle_exit(struct kvm_cpu *vcpu) { switch(vcpu->kvm_run->exit_reason) { case KVM_EXIT_HYPERCALL: if (vcpu->kvm_run->hypercall.nr == KVM_HC_MIPS_CONSOLE_OUTPUT) { return kvm_cpu__hypercall_write_cons(vcpu); } else { pr_warning("KVM_EXIT_HYPERCALL unrecognized call %llu", (unsigned long long)vcpu->kvm_run->hypercall.nr); return false; } case KVM_EXIT_EXCEPTION: case KVM_EXIT_INTERNAL_ERROR: return false; default: break; } return false; } void kvm_cpu__arch_nmi(struct kvm_cpu *cpu) { } void kvm_cpu__show_registers(struct kvm_cpu *vcpu) { struct kvm_regs regs; if (ioctl(vcpu->vcpu_fd, KVM_GET_REGS, ®s) < 0) die("KVM_GET_REGS failed"); dprintf(debug_fd, "\n Registers:\n"); dprintf(debug_fd, " ----------\n"); dprintf(debug_fd, "$0 : %016llx %016llx %016llx %016llx\n", (unsigned long long)regs.gpr[0], (unsigned long long)regs.gpr[1], (unsigned long long)regs.gpr[2], (unsigned long long)regs.gpr[3]); dprintf(debug_fd, "$4 : %016llx %016llx %016llx %016llx\n", (unsigned long long)regs.gpr[4], (unsigned long long)regs.gpr[5], (unsigned long long)regs.gpr[6], (unsigned long long)regs.gpr[7]); dprintf(debug_fd, "$8 : %016llx %016llx %016llx %016llx\n", (unsigned long long)regs.gpr[8], (unsigned long long)regs.gpr[9], (unsigned long long)regs.gpr[10], (unsigned long long)regs.gpr[11]); dprintf(debug_fd, "$12 : %016llx %016llx %016llx %016llx\n", (unsigned long long)regs.gpr[12], (unsigned long long)regs.gpr[13], (unsigned long long)regs.gpr[14], (unsigned long long)regs.gpr[15]); dprintf(debug_fd, "$16 : %016llx %016llx %016llx %016llx\n", (unsigned long long)regs.gpr[16], (unsigned long long)regs.gpr[17], (unsigned long long)regs.gpr[18], (unsigned long long)regs.gpr[19]); dprintf(debug_fd, "$20 : %016llx %016llx %016llx %016llx\n", (unsigned long long)regs.gpr[20], (unsigned long long)regs.gpr[21], (unsigned long long)regs.gpr[22], (unsigned long long)regs.gpr[23]); dprintf(debug_fd, "$24 : %016llx %016llx %016llx %016llx\n", (unsigned long long)regs.gpr[24], (unsigned long long)regs.gpr[25], (unsigned long long)regs.gpr[26], (unsigned long long)regs.gpr[27]); dprintf(debug_fd, "$28 : %016llx %016llx %016llx %016llx\n", (unsigned long long)regs.gpr[28], (unsigned long long)regs.gpr[29], (unsigned long long)regs.gpr[30], (unsigned long long)regs.gpr[31]); dprintf(debug_fd, "hi : %016llx\n", (unsigned long long)regs.hi); dprintf(debug_fd, "lo : %016llx\n", (unsigned long long)regs.lo); dprintf(debug_fd, "epc : %016llx\n", (unsigned long long)regs.pc); dprintf(debug_fd, "\n"); } void kvm_cpu__show_code(struct kvm_cpu *vcpu) { } void kvm_cpu__show_page_tables(struct kvm_cpu *vcpu) { }