[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [Qemu-devel] [RFC v2 2/6] ARM: KVM: Add support for KVM on ARM archi
From: |
Blue Swirl |
Subject: |
Re: [Qemu-devel] [RFC v2 2/6] ARM: KVM: Add support for KVM on ARM architecture |
Date: |
Sat, 13 Oct 2012 09:09:51 +0000 |
On Wed, Oct 10, 2012 at 3:07 PM, Peter Maydell <address@hidden> wrote:
> From: Christoffer Dall <address@hidden>
>
> Add basic support for KVM on ARM architecture.
>
> Signed-off-by: Christoffer Dall <address@hidden>
> [PMM: Minor tweaks and code cleanup, switch to ONE_REG]
> Signed-off-by: Peter Maydell <address@hidden>
> ---
> hw/arm_pic.c | 28 ++++
> target-arm/Makefile.objs | 1 +
> target-arm/cpu.h | 1 +
> target-arm/helper.c | 2 +-
> target-arm/kvm.c | 328
> ++++++++++++++++++++++++++++++++++++++++++++++
> 5 files changed, 359 insertions(+), 1 deletion(-)
> create mode 100644 target-arm/kvm.c
>
> diff --git a/hw/arm_pic.c b/hw/arm_pic.c
> index ffb4d41..72272dc 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 = KVM_ARM_IRQ_TYPE_CPU << KVM_ARM_IRQ_TYPE_SHIFT;
> +
> + switch (irq) {
> + case ARM_PIC_CPU_IRQ:
> + kvm_irq |= KVM_ARM_IRQ_CPU_IRQ;
> + break;
> + case ARM_PIC_CPU_FIQ:
> + kvm_irq |= KVM_ARM_IRQ_CPU_FIQ;
> + break;
> + default:
> + hw_error("kvm_arm_pic_cpu_handler: Bad interrupt line %d\n", irq);
> + }
> + kvm_irq |= env->cpu_index << KVM_ARM_IRQ_VCPU_SHIFT;
> + 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 b6f1a9e..d89b57c 100644
> --- a/target-arm/Makefile.objs
> +++ b/target-arm/Makefile.objs
> @@ -1,4 +1,5 @@
> 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 ff4de10..8c7e574 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 58340bd..f62f3da 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..fee60e1
> --- /dev/null
> +++ b/target-arm/kvm.c
> @@ -0,0 +1,328 @@
> +/*
> + * 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"
Is this used?
> +#include "hw/arm-misc.h"
> +
> +const KVMCapabilityInfo kvm_arch_required_capabilities[] = {
'static'. In fact, 'static' not used at all in this file, probably it
could be used a lot more.
> + 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);
> +}
> +
> +struct reg {
Reg or other CamelCase version and a typedef, please.
> + uint64_t id;
> + int offset;
> +};
> +
> +#define COREREG(KERNELNAME, QEMUFIELD) \
> + { \
> + KVM_REG_ARM | KVM_REG_SIZE_U32 | \
> + KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(KERNELNAME), \
> + offsetof(CPUARMState, QEMUFIELD) \
> + }
> +
> +#define CP15REG(CRN, CRM, OPC1, OPC2, QEMUFIELD) \
> + { \
> + KVM_REG_ARM | KVM_REG_SIZE_U32 | \
> + (15 << KVM_REG_ARM_COPROC_SHIFT) | \
> + ((CRN) << KVM_REG_ARM_32_CRN_SHIFT) | \
> + ((CRM) << KVM_REG_ARM_CRM_SHIFT) | \
> + ((OPC1) << KVM_REG_ARM_OPC1_SHIFT) | \
> + ((OPC2) << KVM_REG_ARM_32_OPC2_SHIFT), \
> + offsetof(CPUARMState, QEMUFIELD) \
> + }
> +
> +const struct reg regs[] = {
> + /* R0_usr .. R14_usr */
> + COREREG(usr_regs[0], regs[0]),
> + COREREG(usr_regs[1], regs[1]),
> + COREREG(usr_regs[2], regs[2]),
> + COREREG(usr_regs[3], regs[3]),
> + COREREG(usr_regs[4], regs[4]),
> + COREREG(usr_regs[5], regs[5]),
> + COREREG(usr_regs[6], regs[6]),
> + COREREG(usr_regs[7], regs[7]),
> + COREREG(usr_regs[8], usr_regs[0]),
> + COREREG(usr_regs[9], usr_regs[1]),
> + COREREG(usr_regs[10], usr_regs[2]),
> + COREREG(usr_regs[11], usr_regs[3]),
> + COREREG(usr_regs[12], usr_regs[4]),
> + COREREG(usr_regs[13], banked_r13[0]),
> + COREREG(usr_regs[14], banked_r14[0]),
> + /* R13, R14, SPSR for SVC, ABT, UND, IRQ banks */
> + COREREG(svc_regs[0], banked_r13[1]),
> + COREREG(svc_regs[1], banked_r14[1]),
> + COREREG(svc_regs[2], banked_spsr[1]),
> + COREREG(abt_regs[0], banked_r13[2]),
> + COREREG(abt_regs[1], banked_r14[2]),
> + COREREG(abt_regs[2], banked_spsr[2]),
> + COREREG(und_regs[0], banked_r13[3]),
> + COREREG(und_regs[1], banked_r14[3]),
> + COREREG(und_regs[2], banked_spsr[3]),
> + COREREG(irq_regs[0], banked_r13[4]),
> + COREREG(irq_regs[1], banked_r14[4]),
> + COREREG(irq_regs[2], banked_spsr[4]),
> + /* R8_fiq .. R14_fiq and SPSR_fiq */
> + COREREG(fiq_regs[0], fiq_regs[0]),
> + COREREG(fiq_regs[1], fiq_regs[1]),
> + COREREG(fiq_regs[2], fiq_regs[2]),
> + COREREG(fiq_regs[3], fiq_regs[3]),
> + COREREG(fiq_regs[4], fiq_regs[4]),
> + COREREG(fiq_regs[0], banked_r13[5]),
> + COREREG(fiq_regs[1], banked_r14[5]),
> + COREREG(fiq_regs[2], banked_spsr[5]),
> + /* R15 */
> + COREREG(pc, regs[15]),
> + /* A non-comprehensive set of cp15 registers.
> + * TODO: drive this from the cp_regs hashtable instead.
> + */
> + CP15REG(1, 0, 0, 0, cp15.c1_sys), /* SCTLR */
> + CP15REG(2, 0, 0, 2, cp15.c2_control), /* TTBCR */
> + CP15REG(3, 0, 0, 0, cp15.c3), /* DACR */
> +};
> +
> +int kvm_arch_put_registers(CPUARMState *env, int level)
> +{
> + struct kvm_one_reg r;
> + int mode, bn;
> + int ret, i;
> + uint32_t cpsr;
> + uint64_t ttbr;
> +
> + /* 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 */
> + for (i = 0; i < ARRAY_SIZE(regs); i++) {
> + r.id = regs[i].id;
> + r.addr = (uintptr_t)(env) + regs[i].offset;
> + ret = kvm_vcpu_ioctl(env, KVM_SET_ONE_REG, &r);
> + if (ret) {
> + return ret;
> + }
> + }
> +
> + /* Special cases which aren't a single CPUARMState field */
> + cpsr = cpsr_read(env);
> + r.id = KVM_REG_ARM | KVM_REG_SIZE_U32 |
> + KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(cpsr);
> + r.addr = (uintptr_t)(&cpsr);
> + ret = kvm_vcpu_ioctl(env, KVM_SET_ONE_REG, &r);
> + if (ret) {
> + return ret;
> + }
> +
> + /* TTBR0: cp15 crm=2 opc1=0 */
> + ttbr = ((uint64_t)env->cp15.c2_base0_hi << 32) | env->cp15.c2_base0;
> + r.id = KVM_REG_ARM | KVM_REG_SIZE_U64 | (15 << KVM_REG_ARM_COPROC_SHIFT)
> |
> + (2 << KVM_REG_ARM_CRM_SHIFT) | (0 << KVM_REG_ARM_OPC1_SHIFT);
> + r.addr = (uintptr_t)(&ttbr);
> + ret = kvm_vcpu_ioctl(env, KVM_SET_ONE_REG, &r);
> + if (ret) {
> + return ret;
> + }
> +
> + /* TTBR1: cp15 crm=2 opc1=1 */
> + ttbr = ((uint64_t)env->cp15.c2_base1_hi << 32) | env->cp15.c2_base1;
> + r.id = KVM_REG_ARM | KVM_REG_SIZE_U64 | (15 << KVM_REG_ARM_COPROC_SHIFT)
> |
> + (2 << KVM_REG_ARM_CRM_SHIFT) | (1 << KVM_REG_ARM_OPC1_SHIFT);
> + r.addr = (uintptr_t)(&ttbr);
> + ret = kvm_vcpu_ioctl(env, KVM_SET_ONE_REG, &r);
> +
> + return ret;
> +}
> +
> +int kvm_arch_get_registers(CPUARMState *env)
> +{
> + struct kvm_one_reg r;
> + int mode, bn;
> + int ret, i;
> + uint32_t cpsr;
> + uint64_t ttbr;
> +
> + for (i = 0; i < ARRAY_SIZE(regs); i++) {
> + r.id = regs[i].id;
> + r.addr = (uintptr_t)(env) + regs[i].offset;
> + ret = kvm_vcpu_ioctl(env, KVM_GET_ONE_REG, &r);
> + if (ret) {
> + return ret;
> + }
> + }
> +
> + /* Special cases which aren't a single CPUARMState field */
> + r.id = KVM_REG_ARM | KVM_REG_SIZE_U32 |
> + KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(cpsr);
> + r.addr = (uintptr_t)(&cpsr);
> + ret = kvm_vcpu_ioctl(env, KVM_GET_ONE_REG, &r);
> + if (ret) {
> + return ret;
> + }
> + cpsr_write(env, cpsr, 0xffffffff);
> +
> + /* TTBR0: cp15 crm=2 opc1=0 */
> + r.id = KVM_REG_ARM | KVM_REG_SIZE_U64 | (15 << KVM_REG_ARM_COPROC_SHIFT)
> |
> + (2 << KVM_REG_ARM_CRM_SHIFT) | (0 << KVM_REG_ARM_OPC1_SHIFT);
> + r.addr = (uintptr_t)(&ttbr);
> + ret = kvm_vcpu_ioctl(env, KVM_GET_ONE_REG, &r);
> + if (ret) {
> + return ret;
> + }
> + env->cp15.c2_base0_hi = ttbr >> 32;
> + env->cp15.c2_base0 = ttbr;
> +
> + /* TTBR1: cp15 crm=2 opc1=1 */
> + r.id = KVM_REG_ARM | KVM_REG_SIZE_U64 | (15 << KVM_REG_ARM_COPROC_SHIFT)
> |
> + (2 << KVM_REG_ARM_CRM_SHIFT) | (1 << KVM_REG_ARM_OPC1_SHIFT);
> + r.addr = (uintptr_t)(&ttbr);
> + ret = kvm_vcpu_ioctl(env, KVM_GET_ONE_REG, &r);
> + if (ret) {
> + return ret;
> + }
> + env->cp15.c2_base1_hi = ttbr >> 32;
> + env->cp15.c2_base1 = ttbr;
> +
> + /* 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];
> +
> + /* The main GET_ONE_REG loop above set c2_control, but we need to
> + * update some extra cached precomputed values too.
> + * When this is driven from the cp_regs hashtable then this ugliness
> + * can disappear because we'll use the access function which sets
> + * these values automatically.
> + */
> + env->cp15.c2_mask = ~(((uint32_t)0xffffffffu) >> env->cp15.c2_control);
> + env->cp15.c2_base_mask = ~((uint32_t)0x3fffu >> env->cp15.c2_control);
The casts don't look useful.
> +
> + 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__);
Please use qemu_log_mask(LOG_UNIMP, ...) instead.
> +}
> +
> +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__);
> +}
> --
> 1.7.9.5
>
>
- [Qemu-devel] [RFC v2 0/6] QEMU: Support KVM on ARM, Peter Maydell, 2012/10/10
- [Qemu-devel] [RFC v2 1/6] linux-headers: Add ARM KVM headers (not for upstream), Peter Maydell, 2012/10/10
- [Qemu-devel] [RFC v2 5/6] ARM KVM: save and load VFP registers from kernel, Peter Maydell, 2012/10/10
- [Qemu-devel] [RFC v2 4/6] hw/kvm/arm_gic: Implement support for KVM in-kernel ARM GIC, Peter Maydell, 2012/10/10
- [Qemu-devel] [RFC v2 3/6] hw/arm_gic: Add presave/postload hooks, Peter Maydell, 2012/10/10
- [Qemu-devel] [RFC v2 6/6] configure: Enable KVM on ARM, Peter Maydell, 2012/10/10
- [Qemu-devel] [RFC v2 2/6] ARM: KVM: Add support for KVM on ARM architecture, Peter Maydell, 2012/10/10