diff options
author | Peter Maydell <peter.maydell@linaro.org> | 2012-07-17 17:54:30 +0100 |
---|---|---|
committer | Peter Maydell <peter.maydell@linaro.org> | 2012-07-25 13:35:45 +0100 |
commit | abf4939058d3dffe3b688d3b6d54614339ef63fe (patch) | |
tree | f0860920ac7d81a2105f39696de3ce4ee692fbaf | |
parent | f823a3059d47c4009681b4d424d0329d670baee9 (diff) | |
download | qemu-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-x | configure | 5 | ||||
-rw-r--r-- | hw/arm_pic.c | 4 | ||||
-rw-r--r-- | kvm.h | 2 | ||||
-rw-r--r-- | target-arm/Makefile.objs | 1 | ||||
-rw-r--r-- | target-arm/cpu.h | 1 | ||||
-rw-r--r-- | target-arm/helper.c | 2 | ||||
-rw-r--r-- | target-arm/kvm.c | 262 |
7 files changed, 275 insertions, 2 deletions
@@ -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) @@ -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, ®s); + 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, ®s); + + 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, ®s); + 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__); +} |