mirror of
https://github.com/clearlinux/kvmtool.git
synced 2026-06-16 02:15:47 +00:00
7281a8db19
So far this was tested with host running KVM using MIPS-VZ (on Cavium Octeon3). A paravirtualized mips kernel was used for the guest. Signed-off-by: David Daney <david.daney@cavium.com> Signed-off-by: Andreas Herrmann <andreas.herrmann@caviumnetworks.com> Signed-off-by: Pekka Enberg <penberg@kernel.org>
220 lines
5.7 KiB
C
220 lines
5.7 KiB
C
#include "kvm/kvm-cpu.h"
|
|
#include "kvm/term.h"
|
|
|
|
#include <stdlib.h>
|
|
|
|
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)
|
|
{
|
|
}
|