From patchwork Thu Aug 9 16:33:07 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Peter Maydell X-Patchwork-Id: 10655 Return-Path: X-Original-To: patchwork@peony.canonical.com Delivered-To: patchwork@peony.canonical.com Received: from fiordland.canonical.com (fiordland.canonical.com [91.189.94.145]) by peony.canonical.com (Postfix) with ESMTP id 377D123E41 for ; Thu, 9 Aug 2012 16:33:19 +0000 (UTC) Received: from mail-yx0-f180.google.com (mail-yx0-f180.google.com [209.85.213.180]) by fiordland.canonical.com (Postfix) with ESMTP id E5766A18A13 for ; Thu, 9 Aug 2012 16:33:18 +0000 (UTC) Received: by yenq6 with SMTP id q6so640786yen.11 for ; Thu, 09 Aug 2012 09:33:18 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20120113; h=x-forwarded-to:x-forwarded-for:delivered-to:received-spf:from:to:cc :subject:date:message-id:x-mailer:in-reply-to:references :x-gm-message-state; bh=2N2lcZjhJS+BDyYpL8prtBw9SEcOtYDiIL3zwPx/HRg=; b=en8MngP+0ahBzMtT1RvQgOciNTd8kC6/0O/rNp9DrIyFGfC+VymxOch/Kkp1bQtKbD /XDQGS34MbAMLnYq2hg7cX2c2AI24nttjMEprEwbAm5bBEloiuc2Rfd1GVxbVUiOAyIY ZnAEIpBDNQcAipx936FryVExZ+FRLiRiahXWw66FaWyyUkstwsaNSngtvzbDJxWMk4OM Bfy+ueVUoZ3lJWqY9irfKieQyQJSiNprmhoWoALPuB8YWE7XwJmqv/mSiDynUJUFn9LL HDv4tj1Jz/H0v8m9wvKsQoNaXKnmwxeLly4PQUKKeiKb2M0gkQ2FxlTSx4WPRnsX8VnF oIIw== Received: by 10.50.95.230 with SMTP id dn6mr1766940igb.16.1344529998032; Thu, 09 Aug 2012 09:33:18 -0700 (PDT) X-Forwarded-To: linaro-patchwork@canonical.com X-Forwarded-For: patch@linaro.org linaro-patchwork@canonical.com Delivered-To: patches@linaro.org Received: by 10.50.184.200 with SMTP id ew8csp17468igc; Thu, 9 Aug 2012 09:33:16 -0700 (PDT) Received: by 10.14.223.9 with SMTP id u9mr28506734eep.10.1344529995694; Thu, 09 Aug 2012 09:33:15 -0700 (PDT) Received: from mnementh.archaic.org.uk (1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.d.1.0.0.b.8.0.1.0.0.2.ip6.arpa. [2001:8b0:1d0::1]) by mx.google.com with ESMTPS id e9si1011532eep.10.2012.08.09.09.33.14 (version=TLSv1/SSLv3 cipher=OTHER); Thu, 09 Aug 2012 09:33:15 -0700 (PDT) Received-SPF: neutral (google.com: 2001:8b0:1d0::1 is neither permitted nor denied by best guess record for domain of pm215@archaic.org.uk) client-ip=2001:8b0:1d0::1; Authentication-Results: mx.google.com; spf=neutral (google.com: 2001:8b0:1d0::1 is neither permitted nor denied by best guess record for domain of pm215@archaic.org.uk) smtp.mail=pm215@archaic.org.uk Received: from pm215 by mnementh.archaic.org.uk with local (Exim 4.72) (envelope-from ) id 1SzVfa-00063Y-Ny; Thu, 09 Aug 2012 17:33:10 +0100 From: Peter Maydell To: qemu-devel@nongnu.org Cc: patches@linaro.org, kvmarm@lists.cs.columbia.edu, kvm@vger.kernel.org Subject: [RFC 2/5] ARM: KVM: Add support for KVM on ARM architecture Date: Thu, 9 Aug 2012 17:33:07 +0100 Message-Id: <1344529990-23251-3-git-send-email-peter.maydell@linaro.org> X-Mailer: git-send-email 1.7.2.5 In-Reply-To: <1344529990-23251-1-git-send-email-peter.maydell@linaro.org> References: <1344529990-23251-1-git-send-email-peter.maydell@linaro.org> X-Gm-Message-State: ALoCoQmSafy3lcbS67MAKhlP/p7SU0Qu2SuXEtsQfdAXSjM3p3161xg5vu8YWy9hptEYtpYrMRHR From: Christoffer Dall Add basic support for KVM on ARM architecture. Signed-off-by: Christoffer Dall [Rusty: updates to use KVM_ARM_VCPU_INIT, KVM_GET/SET_MSRS] Signed-off-by: Rusty Russell [PMM: Minor tweaks and code cleanup] Signed-off-by: Peter Maydell --- hw/arm_pic.c | 28 +++++ target-arm/Makefile.objs | 1 + target-arm/cpu.h | 1 + target-arm/helper.c | 2 +- target-arm/kvm.c | 274 ++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 305 insertions(+), 1 deletion(-) create mode 100644 target-arm/kvm.c diff --git a/hw/arm_pic.c b/hw/arm_pic.c index ffb4d41..950e6c4 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) @@ -34,7 +35,34 @@ static void arm_pic_cpu_handler(void *opaque, int irq, int level) } } +#ifdef CONFIG_KVM +static void kvm_arm_pic_cpu_handler(void *opaque, int irq, int level) +{ + ARMCPU *cpu = opaque; + CPUARMState *env = &cpu->env; + int kvm_irq; + + switch (irq) { + case ARM_PIC_CPU_IRQ: + kvm_irq = KVM_ARM_IRQ_LINE; + break; + case ARM_PIC_CPU_FIQ: + kvm_irq = KVM_ARM_FIQ_LINE; + break; + default: + hw_error("kvm_arm_pic_cpu_handler: Bad interrupt line %d\n", irq); + } + kvm_irq |= (env->cpu_index << 1); + kvm_set_irq(kvm_state, kvm_irq, level ? 1 : 0); +} +#endif + qemu_irq *arm_pic_init_cpu(ARMCPU *cpu) { +#ifdef CONFIG_KVM + if (kvm_enabled()) { + return qemu_allocate_irqs(kvm_arm_pic_cpu_handler, cpu, 2); + } +#endif return qemu_allocate_irqs(arm_pic_cpu_handler, cpu, 2); } diff --git a/target-arm/Makefile.objs b/target-arm/Makefile.objs index f447c4f..f906d20 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 191895c..5194bad 100644 --- a/target-arm/cpu.h +++ b/target-arm/cpu.h @@ -236,6 +236,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 5727da2..632a366 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -1619,7 +1619,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 0000000..b9abee0 --- /dev/null +++ b/target-arm/kvm.c @@ -0,0 +1,274 @@ +/* + * 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 +#include +#include +#include + +#include + +#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) +{ + /* For ARM interrupt delivery is always asynchronous, + * whether we are using an in-kernel VGIC or not. + */ + kvm_async_interrupts_allowed = true; + return 0; +} + +int kvm_arch_init_vcpu(CPUARMState *env) +{ + struct kvm_vcpu_init init; + + init.target = KVM_ARM_TARGET_CORTEX_A15; + memset(init.features, 0, sizeof(init.features)); + return kvm_vcpu_ioctl(env, KVM_ARM_VCPU_INIT, &init); +} + +#define MSR32_INDEX_OF(coproc, crn, opc1, crm, opc2) \ + (((coproc)<<16) | ((opc1)<<11) | ((crn)<<7) | ((opc2)<<4) | (crm)) + +int kvm_arch_put_registers(CPUARMState *env, int level) +{ + struct kvm_regs regs; + int mode, bn; + struct cp15 { + struct kvm_msrs hdr; + struct kvm_msr_entry e[2]; + } cp15; + 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]; + + cp15.hdr.nmsrs = ARRAY_SIZE(cp15.e); + cp15.e[0].index = MSR32_INDEX_OF(15, 0, 0, 0, 0); /* MIDR */ + cp15.e[0].data = env->cp15.c0_cpuid; + cp15.e[1].index = MSR32_INDEX_OF(15, 1, 0, 0, 0); /* SCTLR */ + cp15.e[1].data = env->cp15.c1_sys; + + ret = kvm_vcpu_ioctl(env, KVM_SET_REGS, ®s); + if (ret == 0) { + ret = kvm_vcpu_ioctl(env, KVM_SET_MSRS, &cp15); + } + return ret; +} + +int kvm_arch_get_registers(CPUARMState *env) +{ + struct kvm_regs regs; + int mode, bn; + int32_t ret; + struct cp15 { + struct kvm_msrs hdr; + struct kvm_msr_entry e[6]; + } cp15; + + + 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]; + + /* TODO: investigate automatically getting all registers + * we know about via the ARMCPU cp_regs hashtable. + */ + cp15.hdr.nmsrs = ARRAY_SIZE(cp15.e); + cp15.e[0].index = MSR32_INDEX_OF(15, 0, 0, 0, 0); /* MIDR */ + cp15.e[1].index = MSR32_INDEX_OF(15, 1, 0, 0, 0); /* SCTLR */ + cp15.e[2].index = MSR32_INDEX_OF(15, 2, 0, 0, 0); /* TTBR0 */ + cp15.e[3].index = MSR32_INDEX_OF(15, 2, 0, 0, 1); /* TTBR1 */ + cp15.e[4].index = MSR32_INDEX_OF(15, 2, 0, 0, 2); /* TTBCR */ + cp15.e[5].index = MSR32_INDEX_OF(15, 3, 0, 0, 0); /* DACR */ + + ret = kvm_vcpu_ioctl(env, KVM_GET_MSRS, &cp15); + if (ret < 0) { + return ret; + } + + env->cp15.c1_sys = cp15.e[1].data; + env->cp15.c2_base0 = cp15.e[2].data; + env->cp15.c2_base1 = cp15.e[3].data; + + /* This is ugly, but necessary for GDB compatibility + * TODO: do this via an access function. + */ + env->cp15.c2_control = cp15.e[4].data; + env->cp15.c2_mask = ~(((uint32_t)0xffffffffu) >> cp15.e[4].data); + env->cp15.c2_base_mask = ~((uint32_t)0x3fffu >> cp15.e[4].data); + + env->cp15.c3 = cp15.e[5].data; + 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__); +}