summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.target3
-rwxr-xr-xconfigure6
-rw-r--r--target-ppc/helper.c5
-rw-r--r--target-ppc/kvm.c204
-rw-r--r--target-ppc/kvm_ppc.c110
-rw-r--r--target-ppc/kvm_ppc.h15
6 files changed, 343 insertions, 0 deletions
diff --git a/Makefile.target b/Makefile.target
index d80ce2cd3..cbff56204 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -659,6 +659,9 @@ ifdef FDT_LIBS
OBJS+= device_tree.o
LIBS+= $(FDT_LIBS)
endif
+ifdef CONFIG_KVM
+OBJS+= kvm_ppc.o
+endif
# virtio support
OBJS+= virtio.o virtio-blk.o virtio-balloon.o
endif
diff --git a/configure b/configure
index c4aa8810a..4f8bc5e5b 100755
--- a/configure
+++ b/configure
@@ -1491,6 +1491,7 @@ gdb_xml_files=""
# Make sure the target and host cpus are compatible
if test "$kvm" = "yes" -a ! \( "$target_cpu" = "$cpu" -o \
+ \( "$target_cpu" = "ppcemb" -a "$cpu" = "powerpc" \) -o \
\( "$target_cpu" = "x86_64" -a "$cpu" = "i386" \) -o \
\( "$target_cpu" = "i386" -a "$cpu" = "x86_64" \) \) ; then
kvm="no"
@@ -1585,6 +1586,11 @@ case "$target_cpu" in
echo "#define TARGET_ARCH \"ppcemb\"" >> $config_h
echo "#define TARGET_PPC 1" >> $config_h
echo "#define TARGET_PPCEMB 1" >> $config_h
+ if test "$kvm" = "yes" ; then
+ echo "CONFIG_KVM=yes" >> $config_mak
+ echo "KVM_CFLAGS=$kvm_cflags" >> $config_mak
+ echo "#define CONFIG_KVM 1" >> $config_h
+ fi
;;
ppc64)
echo "TARGET_ARCH=ppc64" >> $config_mak
diff --git a/target-ppc/helper.c b/target-ppc/helper.c
index 538d9f34c..4676d50d8 100644
--- a/target-ppc/helper.c
+++ b/target-ppc/helper.c
@@ -29,6 +29,7 @@
#include "exec-all.h"
#include "helper_regs.h"
#include "qemu-common.h"
+#include "kvm.h"
//#define DEBUG_MMU
//#define DEBUG_BATS
@@ -2920,6 +2921,10 @@ CPUPPCState *cpu_ppc_init (const char *cpu_model)
env->cpu_model_str = cpu_model;
cpu_ppc_register_internal(env, def);
cpu_ppc_reset(env);
+
+ if (kvm_enabled())
+ kvm_init_vcpu(env);
+
return env;
}
diff --git a/target-ppc/kvm.c b/target-ppc/kvm.c
new file mode 100644
index 000000000..acbb1abd2
--- /dev/null
+++ b/target-ppc/kvm.c
@@ -0,0 +1,204 @@
+/*
+ * PowerPC implementation of KVM hooks
+ *
+ * Copyright IBM Corp. 2007
+ *
+ * Authors:
+ * Jerone Young <jyoung5@us.ibm.com>
+ * Christian Ehrhardt <ehrhardt@linux.vnet.ibm.com>
+ * Hollis Blanchard <hollisb@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+
+#include <linux/kvm.h>
+
+#include "qemu-common.h"
+#include "qemu-timer.h"
+#include "sysemu.h"
+#include "kvm.h"
+#include "kvm_ppc.h"
+#include "cpu.h"
+#include "device_tree.h"
+
+//#define DEBUG_KVM
+
+#ifdef DEBUG_KVM
+#define dprintf(fmt, ...) \
+ do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0)
+#else
+#define dprintf(fmt, ...) \
+ do { } while (0)
+#endif
+
+int kvm_arch_init(KVMState *s, int smp_cpus)
+{
+ return 0;
+}
+
+int kvm_arch_init_vcpu(CPUState *cenv)
+{
+ return 0;
+}
+
+int kvm_arch_put_registers(CPUState *env)
+{
+ struct kvm_regs regs;
+ int ret;
+ int i;
+
+ ret = kvm_vcpu_ioctl(env, KVM_GET_REGS, &regs);
+ if (ret < 0)
+ return ret;
+
+ regs.ctr = env->ctr;
+ regs.lr = env->lr;
+ regs.xer = env->xer;
+ regs.msr = env->msr;
+ regs.pc = env->nip;
+
+ regs.srr0 = env->spr[SPR_SRR0];
+ regs.srr1 = env->spr[SPR_SRR1];
+
+ regs.sprg0 = env->spr[SPR_SPRG0];
+ regs.sprg1 = env->spr[SPR_SPRG1];
+ regs.sprg2 = env->spr[SPR_SPRG2];
+ regs.sprg3 = env->spr[SPR_SPRG3];
+ regs.sprg4 = env->spr[SPR_SPRG4];
+ regs.sprg5 = env->spr[SPR_SPRG5];
+ regs.sprg6 = env->spr[SPR_SPRG6];
+ regs.sprg7 = env->spr[SPR_SPRG7];
+
+ for (i = 0;i < 32; i++)
+ regs.gpr[i] = env->gpr[i];
+
+ ret = kvm_vcpu_ioctl(env, KVM_SET_REGS, &regs);
+ if (ret < 0)
+ return ret;
+
+ return ret;
+}
+
+int kvm_arch_get_registers(CPUState *env)
+{
+ struct kvm_regs regs;
+ uint32_t i, ret;
+
+ ret = kvm_vcpu_ioctl(env, KVM_GET_REGS, &regs);
+ if (ret < 0)
+ return ret;
+
+ env->ctr = regs.ctr;
+ env->lr = regs.lr;
+ env->xer = regs.xer;
+ env->msr = regs.msr;
+ env->nip = regs.pc;
+
+ env->spr[SPR_SRR0] = regs.srr0;
+ env->spr[SPR_SRR1] = regs.srr1;
+
+ env->spr[SPR_SPRG0] = regs.sprg0;
+ env->spr[SPR_SPRG1] = regs.sprg1;
+ env->spr[SPR_SPRG2] = regs.sprg2;
+ env->spr[SPR_SPRG3] = regs.sprg3;
+ env->spr[SPR_SPRG4] = regs.sprg4;
+ env->spr[SPR_SPRG5] = regs.sprg5;
+ env->spr[SPR_SPRG6] = regs.sprg6;
+ env->spr[SPR_SPRG7] = regs.sprg7;
+
+ for (i = 0;i < 32; i++)
+ env->gpr[i] = regs.gpr[i];
+
+ return 0;
+}
+
+int kvm_arch_pre_run(CPUState *env, struct kvm_run *run)
+{
+ int r;
+ unsigned irq;
+
+ /* PowerPC Qemu tracks the various core input pins (interrupt, critical
+ * interrupt, reset, etc) in PPC-specific env->irq_input_state. */
+ if (run->ready_for_interrupt_injection &&
+ (env->interrupt_request & CPU_INTERRUPT_HARD) &&
+ (env->irq_input_state & (1<<PPC40x_INPUT_INT)))
+ {
+ /* For now KVM disregards the 'irq' argument. However, in the
+ * future KVM could cache it in-kernel to avoid a heavyweight exit
+ * when reading the UIC.
+ */
+ irq = -1U;
+
+ dprintf("injected interrupt %d\n", irq);
+ r = kvm_vcpu_ioctl(env, KVM_INTERRUPT, &irq);
+ if (r < 0)
+ printf("cpu %d fail inject %x\n", env->cpu_index, irq);
+ }
+
+ /* We don't know if there are more interrupts pending after this. However,
+ * the guest will return to userspace in the course of handling this one
+ * anyways, so we will get a chance to deliver the rest. */
+ return 0;
+}
+
+int kvm_arch_post_run(CPUState *env, struct kvm_run *run)
+{
+ return 0;
+}
+
+static int kvmppc_handle_halt(CPUState *env)
+{
+ if (!(env->interrupt_request & CPU_INTERRUPT_HARD) && (msr_ee)) {
+ env->halted = 1;
+ env->exception_index = EXCP_HLT;
+ }
+
+ return 1;
+}
+
+/* map dcr access to existing qemu dcr emulation */
+static int kvmppc_handle_dcr_read(CPUState *env, uint32_t dcrn, uint32_t *data)
+{
+ if (ppc_dcr_read(env->dcr_env, dcrn, data) < 0)
+ fprintf(stderr, "Read to unhandled DCR (0x%x)\n", dcrn);
+
+ return 1;
+}
+
+static int kvmppc_handle_dcr_write(CPUState *env, uint32_t dcrn, uint32_t data)
+{
+ if (ppc_dcr_write(env->dcr_env, dcrn, data) < 0)
+ fprintf(stderr, "Write to unhandled DCR (0x%x)\n", dcrn);
+
+ return 1;
+}
+
+int kvm_arch_handle_exit(CPUState *env, struct kvm_run *run)
+{
+ int ret = 0;
+
+ switch (run->exit_reason) {
+ case KVM_EXIT_DCR:
+ if (run->dcr.is_write) {
+ dprintf("handle dcr write\n");
+ ret = kvmppc_handle_dcr_write(env, run->dcr.dcrn, run->dcr.data);
+ } else {
+ dprintf("handle dcr read\n");
+ ret = kvmppc_handle_dcr_read(env, run->dcr.dcrn, &run->dcr.data);
+ }
+ break;
+ case KVM_EXIT_HLT:
+ dprintf("handle halt\n");
+ ret = kvmppc_handle_halt(env);
+ break;
+ }
+
+ return ret;
+}
+
diff --git a/target-ppc/kvm_ppc.c b/target-ppc/kvm_ppc.c
new file mode 100644
index 000000000..0caa5b98c
--- /dev/null
+++ b/target-ppc/kvm_ppc.c
@@ -0,0 +1,110 @@
+/*
+ * PowerPC KVM support
+ *
+ * Copyright IBM Corp. 2008
+ *
+ * Authors:
+ * Hollis Blanchard <hollisb@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+
+#include "qemu-common.h"
+#include "qemu-timer.h"
+#include "kvm_ppc.h"
+#include "device_tree.h"
+
+#define PROC_DEVTREE_PATH "/proc/device-tree"
+
+static QEMUTimer *kvmppc_timer;
+static unsigned int kvmppc_timer_rate;
+
+#ifdef HAVE_FDT
+static int kvmppc_read_host_property(const char *node_path, const char *prop,
+ void *val, size_t len)
+{
+ char *path;
+ FILE *f;
+ int ret;
+ int pathlen;
+
+ pathlen = snprintf(NULL, 0, "%s/%s/%s", PROC_DEVTREE_PATH, node_path, prop)
+ + 1;
+ path = qemu_malloc(pathlen);
+ if (path == NULL) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ snprintf(path, pathlen, "%s/%s/%s", PROC_DEVTREE_PATH, node_path, prop);
+
+ f = fopen(path, "rb");
+ if (f == NULL) {
+ ret = errno;
+ goto free;
+ }
+
+ len = fread(val, len, 1, f);
+ if (len != 1) {
+ ret = ferror(f);
+ goto close;
+ }
+
+close:
+ fclose(f);
+free:
+ free(path);
+out:
+ return ret;
+}
+
+static int kvmppc_copy_host_cell(void *fdt, const char *node, const char *prop)
+{
+ uint32_t cell;
+ int ret;
+
+ ret = kvmppc_read_host_property(node, prop, &cell, sizeof(cell));
+ if (ret < 0) {
+ fprintf(stderr, "couldn't read host %s/%s\n", node, prop);
+ goto out;
+ }
+
+ ret = qemu_devtree_setprop_cell(fdt, node, prop, cell);
+ if (ret < 0) {
+ fprintf(stderr, "couldn't set guest %s/%s\n", node, prop);
+ goto out;
+ }
+
+out:
+ return ret;
+}
+
+void kvmppc_fdt_update(void *fdt)
+{
+ /* Copy data from the host device tree into the guest. Since the guest can
+ * directly access the timebase without host involvement, we must expose
+ * the correct frequencies. */
+ kvmppc_copy_host_cell(fdt, "/cpus/cpu@0", "clock-frequency");
+ kvmppc_copy_host_cell(fdt, "/cpus/cpu@0", "timebase-frequency");
+}
+#endif
+
+static void kvmppc_timer_hack(void *opaque)
+{
+ qemu_service_io();
+ qemu_mod_timer(kvmppc_timer, qemu_get_clock(vm_clock) + kvmppc_timer_rate);
+}
+
+void kvmppc_init(void)
+{
+ /* XXX The only reason KVM yields control back to qemu is device IO. Since
+ * an idle guest does no IO, qemu's device model will never get a chance to
+ * run. So, until Qemu gains IO threads, we create this timer to ensure
+ * that the device model gets a chance to run. */
+ kvmppc_timer_rate = ticks_per_sec / 10;
+ kvmppc_timer = qemu_new_timer(vm_clock, &kvmppc_timer_hack, NULL);
+ qemu_mod_timer(kvmppc_timer, qemu_get_clock(vm_clock) + kvmppc_timer_rate);
+}
+
diff --git a/target-ppc/kvm_ppc.h b/target-ppc/kvm_ppc.h
new file mode 100644
index 000000000..e536a881e
--- /dev/null
+++ b/target-ppc/kvm_ppc.h
@@ -0,0 +1,15 @@
+/*
+ * Copyright 2008 IBM Corporation.
+ * Authors: Hollis Blanchard <hollisb@us.ibm.com>
+ *
+ * This work is licensed under the GNU GPL license version 2 or later.
+ *
+ */
+
+#ifndef __KVM_PPC_H__
+#define __KVM_PPC_H__
+
+void kvmppc_init(void);
+void kvmppc_fdt_update(void *fdt);
+
+#endif /* __KVM_PPC_H__ */