aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Maydell <peter.maydell@linaro.org>2012-07-17 17:54:30 +0100
committerPeter Maydell <peter.maydell@linaro.org>2012-07-25 13:35:45 +0100
commitabf4939058d3dffe3b688d3b6d54614339ef63fe (patch)
treef0860920ac7d81a2105f39696de3ce4ee692fbaf
parentf823a3059d47c4009681b4d424d0329d670baee9 (diff)
downloadqemu-arm-abf4939058d3dffe3b688d3b6d54614339ef63fe.tar.gz
ARM: KVM: Add support for KVM on ARM architectures
This patch represents work in progress on adding support for KVM on ARM architectures. Note that it is highly experimental and is not enabled by default. TODO: this is Christoffer's patch, but with the configure script changes redone by me. Needs signed-off-by. Has also had a few subsequent fixes squashed in. Consider whether this can split apart into separate patches?
-rwxr-xr-xconfigure5
-rw-r--r--hw/arm_pic.c4
-rw-r--r--kvm.h2
-rw-r--r--target-arm/Makefile.objs1
-rw-r--r--target-arm/cpu.h1
-rw-r--r--target-arm/helper.c2
-rw-r--r--target-arm/kvm.c262
7 files changed, 275 insertions, 2 deletions
diff --git a/configure b/configure
index d469f62945..6f86ff1720 100755
--- a/configure
+++ b/configure
@@ -3514,6 +3514,9 @@ done
if test "$linux" = "yes" ; then
mkdir -p linux-headers
case "$cpu" in
+ arm)
+ symlink $source_path/linux-headers/asm-arm linux-headers/asm
+ ;;
i386|x86_64)
symlink "$source_path/linux-headers/asm-x86" linux-headers/asm
;;
@@ -3761,7 +3764,7 @@ case "$target_arch2" in
echo "CONFIG_NO_XEN=y" >> $config_target_mak
esac
case "$target_arch2" in
- i386|x86_64|ppcemb|ppc|ppc64|s390x)
+ i386|x86_64|ppcemb|ppc|ppc64|s390x|arm)
# Make sure the target and host cpus are compatible
if test "$kvm" = "yes" -a "$target_softmmu" = "yes" -a \
\( "$target_arch2" = "$cpu" -o \
diff --git a/hw/arm_pic.c b/hw/arm_pic.c
index ffb4d4171a..9f603752ba 100644
--- a/hw/arm_pic.c
+++ b/hw/arm_pic.c
@@ -9,6 +9,7 @@
#include "hw.h"
#include "arm-misc.h"
+#include "kvm.h"
/* Input 0 is IRQ and input 1 is FIQ. */
static void arm_pic_cpu_handler(void *opaque, int irq, int level)
@@ -32,6 +33,9 @@ static void arm_pic_cpu_handler(void *opaque, int irq, int level)
default:
hw_error("arm_pic_cpu_handler: Bad interrupt line %d\n", irq);
}
+
+ if (kvm_enabled())
+ kvm_arch_interrupt(env, irq, level);
}
qemu_irq *arm_pic_init_cpu(ARMCPU *cpu)
diff --git a/kvm.h b/kvm.h
index 2617dd5acd..a47acefad0 100644
--- a/kvm.h
+++ b/kvm.h
@@ -128,6 +128,8 @@ int kvm_arch_init_vcpu(CPUArchState *env);
void kvm_arch_reset_vcpu(CPUArchState *env);
+int kvm_arch_interrupt(CPUArchState *env, int irq, int level);
+
int kvm_arch_on_sigbus_vcpu(CPUArchState *env, int code, void *addr);
int kvm_arch_on_sigbus(int code, void *addr);
diff --git a/target-arm/Makefile.objs b/target-arm/Makefile.objs
index f447c4fdf2..f906d203ff 100644
--- a/target-arm/Makefile.objs
+++ b/target-arm/Makefile.objs
@@ -1,5 +1,6 @@
obj-y += arm-semi.o
obj-$(CONFIG_SOFTMMU) += machine.o
+obj-$(CONFIG_KVM) += kvm.o
obj-y += translate.o op_helper.o helper.o cpu.o
obj-y += neon_helper.o iwmmxt_helper.o
diff --git a/target-arm/cpu.h b/target-arm/cpu.h
index 3a4c21b0ad..2825700825 100644
--- a/target-arm/cpu.h
+++ b/target-arm/cpu.h
@@ -241,6 +241,7 @@ ARMCPU *cpu_arm_init(const char *cpu_model);
void arm_translate_init(void);
int cpu_arm_exec(CPUARMState *s);
void do_interrupt(CPUARMState *);
+int bank_number(CPUARMState *env, int mode);
void switch_mode(CPUARMState *, int);
uint32_t do_arm_semihosting(CPUARMState *env);
diff --git a/target-arm/helper.c b/target-arm/helper.c
index b4ebc8334c..aa93b9667a 100644
--- a/target-arm/helper.c
+++ b/target-arm/helper.c
@@ -1648,7 +1648,7 @@ uint32_t HELPER(get_r13_banked)(CPUARMState *env, uint32_t mode)
#else
/* Map CPU modes onto saved register banks. */
-static inline int bank_number(CPUARMState *env, int mode)
+int bank_number(CPUARMState *env, int mode)
{
switch (mode) {
case ARM_CPU_MODE_USR:
diff --git a/target-arm/kvm.c b/target-arm/kvm.c
new file mode 100644
index 0000000000..29bb51fc46
--- /dev/null
+++ b/target-arm/kvm.c
@@ -0,0 +1,262 @@
+/*
+ * ARM implementation of KVM hooks
+ *
+ * Copyright Christoffer Dall 2009-2010
+ *
+ * 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 <stdio.h>
+#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 "cpu.h"
+#include "device_tree.h"
+#include "hw/arm-misc.h"
+
+const KVMCapabilityInfo kvm_arch_required_capabilities[] = {
+ KVM_CAP_LAST_INFO
+};
+
+int kvm_arch_init(KVMState *s)
+{
+ return 0;
+}
+
+int kvm_arch_init_vcpu(CPUARMState *env)
+{
+ return 0;
+}
+
+int kvm_arch_put_registers(CPUARMState *env, int level)
+{
+ struct kvm_regs regs;
+ int mode, bn;
+ int ret;
+
+ ret = kvm_vcpu_ioctl(env, KVM_GET_REGS, &regs);
+ if (ret < 0)
+ return ret;
+
+ /* We make sure the banked regs are properly set */
+ mode = env->uncached_cpsr & CPSR_M;
+ bn = bank_number(env, mode);
+ if (mode == ARM_CPU_MODE_FIQ)
+ memcpy(env->fiq_regs, env->regs + 8, 5 * sizeof(uint32_t));
+ else
+ memcpy(env->usr_regs, env->regs + 8, 5 * sizeof(uint32_t));
+ env->banked_r13[bn] = env->regs[13];
+ env->banked_r14[bn] = env->regs[14];
+ env->banked_spsr[bn] = env->spsr;
+
+ /* Now we can safely copy stuff down to the kernel */
+ memcpy(regs.regs0_7, env->regs, sizeof(uint32_t) * 8);
+ memcpy(regs.usr_regs8_12, env->usr_regs, sizeof(uint32_t) * 5);
+ memcpy(regs.fiq_regs8_12, env->fiq_regs, sizeof(uint32_t) * 5);
+ regs.reg13[MODE_FIQ] = env->banked_r13[5];
+ regs.reg13[MODE_IRQ] = env->banked_r13[4];
+ regs.reg13[MODE_SVC] = env->banked_r13[1];
+ regs.reg13[MODE_ABT] = env->banked_r13[2];
+ regs.reg13[MODE_UND] = env->banked_r13[3];
+ regs.reg13[MODE_USR] = env->banked_r13[0];
+ regs.reg14[MODE_FIQ] = env->banked_r14[5];
+ regs.reg14[MODE_IRQ] = env->banked_r14[4];
+ regs.reg14[MODE_SVC] = env->banked_r14[1];
+ regs.reg14[MODE_ABT] = env->banked_r14[2];
+ regs.reg14[MODE_UND] = env->banked_r14[3];
+ regs.reg14[MODE_USR] = env->banked_r14[0];
+ regs.reg15 = env->regs[15];
+ regs.cpsr = cpsr_read(env);
+ regs.spsr[MODE_FIQ] = env->banked_spsr[5];
+ regs.spsr[MODE_IRQ] = env->banked_spsr[4];
+ regs.spsr[MODE_SVC] = env->banked_spsr[1];
+ regs.spsr[MODE_ABT] = env->banked_spsr[2];
+ regs.spsr[MODE_UND] = env->banked_spsr[3];
+
+ regs.cp15.c0_midr = env->cp15.c0_cpuid;
+ regs.cp15.c1_sys = env->cp15.c1_sys;
+
+ ret = kvm_vcpu_ioctl(env, KVM_SET_REGS, &regs);
+
+ return ret;
+}
+
+int kvm_arch_get_registers(CPUARMState *env)
+{
+ struct kvm_regs regs;
+ int mode, bn;
+ int32_t ret;
+
+ ret = kvm_vcpu_ioctl(env, KVM_GET_REGS, &regs);
+ if (ret < 0)
+ return ret;
+
+ /* First, let's transfer the banked state */
+ cpsr_write(env, regs.cpsr, 0xFFFFFFFF);
+ memcpy(env->regs, regs.regs0_7, sizeof(uint32_t) * 8);
+ memcpy(env->usr_regs, regs.usr_regs8_12, sizeof(uint32_t) * 5);
+ memcpy(env->fiq_regs, regs.fiq_regs8_12, sizeof(uint32_t) * 5);
+
+ env->banked_r13[5] = regs.reg13[MODE_FIQ];
+ env->banked_r13[4] = regs.reg13[MODE_IRQ];
+ env->banked_r13[1] = regs.reg13[MODE_SVC];
+ env->banked_r13[2] = regs.reg13[MODE_ABT];
+ env->banked_r13[3] = regs.reg13[MODE_UND];
+ env->banked_r13[0] = regs.reg13[MODE_USR];
+ env->banked_r14[5] = regs.reg14[MODE_FIQ];
+ env->banked_r14[4] = regs.reg14[MODE_IRQ];
+ env->banked_r14[1] = regs.reg14[MODE_SVC];
+ env->banked_r14[2] = regs.reg14[MODE_ABT];
+ env->banked_r14[3] = regs.reg14[MODE_UND];
+ env->banked_r14[0] = regs.reg14[MODE_USR];
+ env->regs[15] = regs.reg15;
+ env->banked_spsr[5] = regs.spsr[MODE_FIQ];
+ env->banked_spsr[4] = regs.spsr[MODE_IRQ];
+ env->banked_spsr[1] = regs.spsr[MODE_SVC];
+ env->banked_spsr[2] = regs.spsr[MODE_ABT];
+ env->banked_spsr[3] = regs.spsr[MODE_UND];
+
+ /* We make sure the current mode regs are properly set */
+ mode = env->uncached_cpsr & CPSR_M;
+ bn = bank_number(env, mode);
+ if (mode == ARM_CPU_MODE_FIQ)
+ memcpy(env->regs + 8, env->fiq_regs, 5 * sizeof(uint32_t));
+ else
+ memcpy(env->regs + 8, env->usr_regs, 5 * sizeof(uint32_t));
+ env->regs[13] = env->banked_r13[bn];
+ env->regs[14] = env->banked_r14[bn];
+ env->spsr = env->banked_spsr[bn];
+
+ //env->cp15.c0_cpuid = regs.cp15.c0_midr;
+ env->cp15.c1_sys = regs.cp15.c1_sys;
+ env->cp15.c2_base0 = regs.cp15.c2_base0;
+ env->cp15.c2_base1 = regs.cp15.c2_base1;
+
+ /* This is ugly, but necessary for GDB compatibility */
+ env->cp15.c2_control = regs.cp15.c2_control;
+ env->cp15.c2_mask = ~(((uint32_t)0xffffffffu) >> regs.cp15.c2_control);
+ env->cp15.c2_base_mask = ~((uint32_t)0x3fffu >> regs.cp15.c2_control);
+
+ env->cp15.c3 = regs.cp15.c3_dacr;
+
+ return 0;
+}
+
+#define KVM_ARM_EXCEPTION_IRQ 0x02
+#define KVM_ARM_EXCEPTION_FIQ 0x01
+int kvm_arch_interrupt(CPUARMState *env, int irq, int level)
+{
+ struct kvm_irq_level irq_level;
+ int vcpu_idx = env->cpu_index;
+ KVMState *s = kvm_state;
+ int ret;
+
+ if (level)
+ irq_level.level = 1;
+ else
+ irq_level.level = 0;
+
+ switch (irq) {
+ case ARM_PIC_CPU_IRQ:
+ irq_level.irq = KVM_ARM_IRQ_LINE | (vcpu_idx << 1);
+ break;
+ case ARM_PIC_CPU_FIQ:
+ irq_level.irq = KVM_ARM_FIQ_LINE | (vcpu_idx << 1);
+ break;
+ default:
+ fprintf(stderr, "unsupported ARM irq injection\n");
+ abort();
+ }
+
+ ret = kvm_vm_ioctl(s, KVM_IRQ_LINE, &irq_level);
+ if (ret) {
+ fprintf(stderr, "kvm_vm_ioctl(s, KVM_IRQ_LINE, &irq_level) failed\n");
+ abort();
+ }
+
+ return 0;
+}
+
+void kvm_arch_pre_run(CPUARMState *env, struct kvm_run *run)
+{
+}
+
+void kvm_arch_post_run(CPUARMState *env, struct kvm_run *run)
+{
+}
+
+int kvm_arch_handle_exit(CPUARMState *env, struct kvm_run *run)
+{
+ int ret = 0;
+
+ return ret;
+}
+
+void kvm_arch_reset_vcpu(CPUARMState *env)
+{
+}
+
+bool kvm_arch_stop_on_emulation_error(CPUARMState *env)
+{
+ return true;
+}
+
+int kvm_arch_process_async_events(CPUARMState *env)
+{
+ return 0;
+}
+
+int kvm_arch_on_sigbus_vcpu(CPUARMState *env, int code, void *addr)
+{
+ return 1;
+}
+
+int kvm_arch_on_sigbus(int code, void *addr)
+{
+ return 1;
+}
+
+void kvm_arch_update_guest_debug(CPUARMState *env, struct kvm_guest_debug *dbg)
+{
+ fprintf(stderr, "%s: not implemented\n", __func__);
+}
+
+int kvm_arch_insert_sw_breakpoint(CPUARMState *env, struct kvm_sw_breakpoint *bp)
+{
+ fprintf(stderr, "%s: not implemented\n", __func__);
+ return -EINVAL;
+}
+
+int kvm_arch_insert_hw_breakpoint(target_ulong addr,
+ target_ulong len, int type)
+{
+ fprintf(stderr, "%s: not implemented\n", __func__);
+ return -EINVAL;
+}
+
+int kvm_arch_remove_hw_breakpoint(target_ulong addr,
+ target_ulong len, int type)
+{
+ fprintf(stderr, "%s: not implemented\n", __func__);
+ return -EINVAL;
+}
+
+int kvm_arch_remove_sw_breakpoint(CPUARMState *env, struct kvm_sw_breakpoint *bp)
+{
+ fprintf(stderr, "%s: not implemented\n", __func__);
+ return -EINVAL;
+}
+
+void kvm_arch_remove_all_hw_breakpoints(void)
+{
+ fprintf(stderr, "%s: not implemented\n", __func__);
+}