summary refs log tree commit diff stats
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 d80ce2cd37..cbff562047 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 c4aa8810a3..4f8bc5e5bc 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 538d9f34c4..4676d50d87 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 0000000000..acbb1abd2b
--- /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 0000000000..0caa5b98c7
--- /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 0000000000..e536a881e9
--- /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__ */