Targets KVM support for Cortex A-15 processors. Contains no real functionality but all the framework components, make files, header files and some tracing functionality. Most functionality is in arch/arm/kvm/* or arch/arm/include/asm/kvm_*.h. Signed-off-by: Christoffer Dall <c.dall@xxxxxxxxxxxxxxxxxxxxxx> --- arch/arm/Kconfig | 2 arch/arm/Makefile | 1 arch/arm/include/asm/kvm.h | 66 ++++++ arch/arm/include/asm/kvm_asm.h | 28 +++ arch/arm/include/asm/kvm_emulate.h | 91 +++++++++ arch/arm/include/asm/kvm_host.h | 93 +++++++++ arch/arm/include/asm/kvm_para.h | 9 + arch/arm/include/asm/unified.h | 12 + arch/arm/kvm/Kconfig | 44 ++++ arch/arm/kvm/Makefile | 18 ++ arch/arm/kvm/arm.c | 272 ++++++++++++++++++++++++++ arch/arm/kvm/arm_emulate.c | 121 ++++++++++++ arch/arm/kvm/arm_exports.c | 16 ++ arch/arm/kvm/arm_guest.c | 148 ++++++++++++++ arch/arm/kvm/arm_init.S | 17 ++ arch/arm/kvm/arm_interrupts.S | 17 ++ arch/arm/kvm/arm_mmu.c | 15 + arch/arm/kvm/debug.c | 377 ++++++++++++++++++++++++++++++++++++ arch/arm/kvm/debug.h | 63 ++++++ arch/arm/kvm/trace.h | 52 +++++ arch/arm/mach-vexpress/Kconfig | 1 arch/arm/mm/Kconfig | 7 + 22 files changed, 1470 insertions(+), 0 deletions(-) create mode 100644 arch/arm/include/asm/kvm.h create mode 100644 arch/arm/include/asm/kvm_asm.h create mode 100644 arch/arm/include/asm/kvm_emulate.h create mode 100644 arch/arm/include/asm/kvm_host.h create mode 100644 arch/arm/include/asm/kvm_para.h create mode 100644 arch/arm/kvm/Kconfig create mode 100644 arch/arm/kvm/Makefile create mode 100644 arch/arm/kvm/arm.c create mode 100644 arch/arm/kvm/arm_emulate.c create mode 100644 arch/arm/kvm/arm_exports.c create mode 100644 arch/arm/kvm/arm_guest.c create mode 100644 arch/arm/kvm/arm_init.S create mode 100644 arch/arm/kvm/arm_interrupts.S create mode 100644 arch/arm/kvm/arm_mmu.c create mode 100644 arch/arm/kvm/debug.c create mode 100644 arch/arm/kvm/debug.h create mode 100644 arch/arm/kvm/trace.h diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index c7b01b0..3cc74c7 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -2049,3 +2049,5 @@ source "security/Kconfig" source "crypto/Kconfig" source "lib/Kconfig" + +source "arch/arm/kvm/Kconfig" diff --git a/arch/arm/Makefile b/arch/arm/Makefile index c7d321a..718876d 100644 --- a/arch/arm/Makefile +++ b/arch/arm/Makefile @@ -258,6 +258,7 @@ core-$(CONFIG_VFP) += arch/arm/vfp/ # If we have a machine-specific directory, then include it in the build. core-y += arch/arm/kernel/ arch/arm/mm/ arch/arm/common/ +core-y += arch/arm/kvm/ core-y += $(machdirs) $(platdirs) drivers-$(CONFIG_OPROFILE) += arch/arm/oprofile/ diff --git a/arch/arm/include/asm/kvm.h b/arch/arm/include/asm/kvm.h new file mode 100644 index 0000000..87dc33b --- /dev/null +++ b/arch/arm/include/asm/kvm.h @@ -0,0 +1,66 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef __ARM_KVM_H__ +#define __ARM_KVM_H__ + +#include <asm/types.h> + +/* + * Modes used for short-hand mode determinition in the world-switch code and + * in emulation code. + * + * Note: These indices do NOT correspond to the value of the CPSR mode bits! + */ +#define MODE_FIQ 0 +#define MODE_IRQ 1 +#define MODE_SVC 2 +#define MODE_ABT 3 +#define MODE_UND 4 +#define MODE_USR 5 +#define MODE_SYS 6 + +struct kvm_regs { + __u32 regs0_7[8]; /* Unbanked regs. (r0 - r7) */ + __u32 fiq_regs8_12[5]; /* Banked fiq regs. (r8 - r12) */ + __u32 usr_regs8_12[5]; /* Banked usr registers (r8 - r12) */ + __u32 reg13[6]; /* Banked r13, indexed by MODE_ */ + __u32 reg14[6]; /* Banked r13, indexed by MODE_ */ + __u32 reg15; + __u32 cpsr; + __u32 spsr[5]; /* Banked SPSR, indexed by MODE_ */ + struct { + __u32 c1_sys; + __u32 c2_base0; + __u32 c2_base1; + __u32 c3_dacr; + } cp15; + +}; + +struct kvm_sregs { +}; + +struct kvm_fpu { +}; + +struct kvm_guest_debug_arch { +}; + +struct kvm_debug_exit_arch { +}; + +#endif /* __ARM_KVM_H__ */ diff --git a/arch/arm/include/asm/kvm_asm.h b/arch/arm/include/asm/kvm_asm.h new file mode 100644 index 0000000..c3d4458 --- /dev/null +++ b/arch/arm/include/asm/kvm_asm.h @@ -0,0 +1,28 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef __ARM_KVM_ASM_H__ +#define __ARM_KVM_ASM_H__ + +#define ARM_EXCEPTION_RESET 0 +#define ARM_EXCEPTION_UNDEFINED 1 +#define ARM_EXCEPTION_SOFTWARE 2 +#define ARM_EXCEPTION_PREF_ABORT 3 +#define ARM_EXCEPTION_DATA_ABORT 4 +#define ARM_EXCEPTION_IRQ 5 +#define ARM_EXCEPTION_FIQ 6 + +#endif /* __ARM_KVM_ASM_H__ */ diff --git a/arch/arm/include/asm/kvm_emulate.h b/arch/arm/include/asm/kvm_emulate.h new file mode 100644 index 0000000..91d461a --- /dev/null +++ b/arch/arm/include/asm/kvm_emulate.h @@ -0,0 +1,91 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef __ARM_KVM_EMULATE_H__ +#define __ARM_KVM_EMULATE_H__ + +#include <linux/kvm_host.h> +#include <asm/kvm_asm.h> + +u32 *kvm_vcpu_reg(struct kvm_vcpu *vcpu, u8 reg_num, u32 mode); + +static inline unsigned char vcpu_mode(struct kvm_vcpu *vcpu) +{ + u8 modes_table[16] = { + MODE_USR, /* 0x0 */ + MODE_FIQ, /* 0x1 */ + MODE_IRQ, /* 0x2 */ + MODE_SVC, /* 0x3 */ + 0xf, 0xf, 0xf, + MODE_ABT, /* 0x7 */ + 0xf, 0xf, 0xf, + MODE_UND, /* 0xb */ + 0xf, 0xf, 0xf, + MODE_SYS}; /* 0xf */ + + BUG_ON(modes_table[vcpu->arch.regs.cpsr & 0xf] == 0xf); + return modes_table[vcpu->arch.regs.cpsr & 0xf]; +} + +/* + * Return the SPSR for the specified mode of the virtual CPU. + */ +static inline u32 *kvm_vcpu_spsr(struct kvm_vcpu *vcpu, u32 mode) +{ + switch (mode) { + case MODE_SVC: + return &vcpu->arch.regs.svc_regs[2]; + case MODE_ABT: + return &vcpu->arch.regs.svc_regs[2]; + case MODE_UND: + return &vcpu->arch.regs.svc_regs[2]; + case MODE_IRQ: + return &vcpu->arch.regs.svc_regs[2]; + case MODE_FIQ: + return &vcpu->arch.regs.fiq_regs[7]; + default: + BUG(); + } +} + +/* Get vcpu register for current mode */ +static inline u32 *vcpu_reg(struct kvm_vcpu *vcpu, unsigned long reg_num) +{ + return kvm_vcpu_reg(vcpu, reg_num, vcpu_mode(vcpu)); +} + +static inline u32 *vcpu_cpsr(struct kvm_vcpu *vcpu) +{ + return &vcpu->arch.regs.cpsr; +} + +/* Get vcpu SPSR for current mode */ +static inline u32 *vcpu_spsr(struct kvm_vcpu *vcpu) +{ + return kvm_vcpu_spsr(vcpu, vcpu_mode(vcpu)); +} + +static inline bool mode_has_spsr(struct kvm_vcpu *vcpu) +{ + return (vcpu_mode(vcpu) < MODE_USR); +} + +static inline bool vcpu_mode_priv(struct kvm_vcpu *vcpu) +{ + return ((vcpu_mode(vcpu)) == MODE_USR) ? 0 : 1; +} + +#endif /* __ARM_KVM_EMULATE_H__ */ diff --git a/arch/arm/include/asm/kvm_host.h b/arch/arm/include/asm/kvm_host.h new file mode 100644 index 0000000..b2fcd8a --- /dev/null +++ b/arch/arm/include/asm/kvm_host.h @@ -0,0 +1,93 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef __ARM_KVM_HOST_H__ +#define __ARM_KVM_HOST_H__ + +#define KVM_MAX_VCPUS 1 +#define KVM_MEMORY_SLOTS 32 +#define KVM_PRIVATE_MEM_SLOTS 4 +#define KVM_COALESCED_MMIO_PAGE_OFFSET 1 + +/* We don't currently support large pages. */ +#define KVM_HPAGE_GFN_SHIFT(x) 0 +#define KVM_NR_PAGE_SIZES 1 +#define KVM_PAGES_PER_HPAGE(x) (1UL<<31) + +struct kvm_vcpu; +u32 *kvm_vcpu_reg(struct kvm_vcpu *vcpu, u8 reg_num, u32 mode); + +struct kvm_arch { +}; + +#define EXCEPTION_NONE 0 +#define EXCEPTION_RESET 0x80 +#define EXCEPTION_UNDEFINED 0x40 +#define EXCEPTION_SOFTWARE 0x20 +#define EXCEPTION_PREFETCH 0x10 +#define EXCEPTION_DATA 0x08 +#define EXCEPTION_IMPRECISE 0x04 +#define EXCEPTION_IRQ 0x02 +#define EXCEPTION_FIQ 0x01 + +struct kvm_vcpu_regs { + u32 usr_regs[15]; /* R0_usr - R14_usr */ + u32 svc_regs[3]; /* SP_svc, LR_svc, SPSR_svc */ + u32 abt_regs[3]; /* SP_abt, LR_abt, SPSR_abt */ + u32 und_regs[3]; /* SP_und, LR_und, SPSR_und */ + u32 irq_regs[3]; /* SP_irq, LR_irq, SPSR_irq */ + u32 fiq_regs[8]; /* R8_fiq - R14_fiq, SPSR_fiq */ + u32 pc; /* The program counter (r15) */ + u32 cpsr; /* The guest CPSR */ +} __packed; + +struct kvm_vcpu_arch { + struct kvm_vcpu_regs regs; + + /* System control coprocessor (cp15) */ + struct { + u32 c1_SCTLR; /* System Control Register */ + u32 c1_ACTLR; /* Auxilliary Control Register */ + u32 c1_CPACR; /* Coprocessor Access Control */ + u64 c2_TTBR0; /* Translation Table Base Register 0 */ + u64 c2_TTBR1; /* Translation Table Base Register 1 */ + u32 c2_TTBCR; /* Translation Table Base Control R. */ + u32 c3_DACR; /* Domain Access Control Register */ + } cp15; + + u32 virt_irq; /* HCR exception mask */ + + /* Exception Information */ + u32 hsr; /* Hyp Syndrom Register */ + u32 hdfar; /* Hyp Data Fault Address Register */ + u32 hifar; /* Hyp Inst. Fault Address Register */ + u32 hpfar; /* Hyp IPA Fault Address Register */ + + /* IO related fields */ + u32 mmio_rd; + + /* Misc. fields */ + u32 wait_for_interrupts; +}; + +struct kvm_vm_stat { + u32 remote_tlb_flush; +}; + +struct kvm_vcpu_stat { +}; + +#endif /* __ARM_KVM_HOST_H__ */ diff --git a/arch/arm/include/asm/kvm_para.h b/arch/arm/include/asm/kvm_para.h new file mode 100644 index 0000000..7ce5f1c --- /dev/null +++ b/arch/arm/include/asm/kvm_para.h @@ -0,0 +1,9 @@ +#ifndef _ASM_X86_KVM_PARA_H +#define _ASM_X86_KVM_PARA_H + +static inline unsigned int kvm_arch_para_features(void) +{ + return 0; +} + +#endif /* _ASM_X86_KVM_PARA_H */ diff --git a/arch/arm/include/asm/unified.h b/arch/arm/include/asm/unified.h index bc63116..0d41bde 100644 --- a/arch/arm/include/asm/unified.h +++ b/arch/arm/include/asm/unified.h @@ -54,6 +54,18 @@ #endif /* CONFIG_THUMB2_KERNEL */ +#ifdef CONFIG_KVM_ARM_HOST +#ifdef __ASSEMBLY__ +.arch_extension sec +.arch_extension virt +#else +__asm__( +" .arch_extension sec\n" +" .arch_extension virt\n" +); +#endif +#endif + #ifndef CONFIG_ARM_ASM_UNIFIED /* diff --git a/arch/arm/kvm/Kconfig b/arch/arm/kvm/Kconfig new file mode 100644 index 0000000..ccabbb3 --- /dev/null +++ b/arch/arm/kvm/Kconfig @@ -0,0 +1,44 @@ +# +# KVM configuration +# + +source "virt/kvm/Kconfig" + +menuconfig VIRTUALIZATION + bool "Virtualization" + ---help--- + Say Y here to get to see options for using your Linux host to run + other operating systems inside virtual machines (guests). + This option alone does not add any kernel code. + + If you say N, all options in this submenu will be skipped and + disabled. + +if VIRTUALIZATION + +config KVM + tristate "Kernel-based Virtual Machine (KVM) support" + select PREEMPT_NOTIFIERS + select ANON_INODES + select KVM_ARM_HOST + select KVM_MMIO + ---help--- + Support hosting virtualized guest machines. You will also + need to select one or more of the processor modules below. + + This module provides access to the hardware capabilities through + a character device node named /dev/kvm. + + If unsure, say N. + +config KVM_ARM_HOST + bool "KVM host support for ARM cpus." + depends on KVM + depends on MMU + depends on CPU_V7 || ARM_VIRT_EXT + ---help--- + Provides host support for ARM processors. + +source drivers/virtio/Kconfig + +endif # VIRTUALIZATION diff --git a/arch/arm/kvm/Makefile b/arch/arm/kvm/Makefile new file mode 100644 index 0000000..017c65a --- /dev/null +++ b/arch/arm/kvm/Makefile @@ -0,0 +1,18 @@ +# +# Makefile for Kernel-based Virtual Machine module +# + +ccflags-y += -Ivirt/kvm -Iarch/arm/kvm +CFLAGS_arm.o := -I. +CFLAGS_arm_mmu.o := -I. + +EXTRA_CFLAGS += -Ivirt/kvm -Iarch/arm/kvm +AFLAGS_arm_interrupts.o := -I$(obj) + +obj-$(CONFIG_KVM_ARM_HOST) += arm_init.o arm_interrupts.o arm_exports.o + +kvm-arm-y += $(addprefix ../../../virt/kvm/, kvm_main.o coalesced_mmio.o) + +kvm-arm-y += arm.o arm_guest.o arm_mmu.o arm_emulate.o debug.o + +obj-$(CONFIG_KVM) += kvm-arm.o diff --git a/arch/arm/kvm/arm.c b/arch/arm/kvm/arm.c new file mode 100644 index 0000000..1e7a907 --- /dev/null +++ b/arch/arm/kvm/arm.c @@ -0,0 +1,272 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include <linux/errno.h> +#include <linux/err.h> +#include <linux/kvm_host.h> +#include <linux/module.h> +#include <linux/vmalloc.h> +#include <linux/fs.h> +#include <linux/mman.h> +#include <linux/sched.h> +#include <trace/events/kvm.h> + +#define CREATE_TRACE_POINTS +#include "trace.h" + +#include <asm/unified.h> +#include <asm/uaccess.h> +#include <asm/ptrace.h> +#include <asm/mman.h> + +#include "debug.h" + +int kvm_arch_hardware_enable(void *garbage) +{ + return 0; +} + +void kvm_arch_hardware_disable(void *garbage) +{ +} + +int kvm_arch_hardware_setup(void) +{ + return 0; +} + +void kvm_arch_hardware_unsetup(void) +{ +} + +void kvm_arch_check_processor_compat(void *rtn) +{ + *(int *)rtn = 0; +} + +void kvm_arch_sync_events(struct kvm *kvm) +{ +} + +int kvm_arch_init_vm(struct kvm *kvm) +{ + return 0; +} + +void kvm_arch_destroy_vm(struct kvm *kvm) +{ + int i; + + for (i = 0; i < KVM_MAX_VCPUS; ++i) { + if (kvm->vcpus[i]) { + kvm_arch_vcpu_free(kvm->vcpus[i]); + kvm->vcpus[i] = NULL; + } + } +} + +int kvm_dev_ioctl_check_extension(long ext) +{ + int r; + switch (ext) { + case KVM_CAP_USER_MEMORY: + case KVM_CAP_DESTROY_MEMORY_REGION_WORKS: + r = 1; + break; + case KVM_CAP_COALESCED_MMIO: + r = KVM_COALESCED_MMIO_PAGE_OFFSET; + break; + default: + r = 0; + break; + } + return r; +} + +long kvm_arch_dev_ioctl(struct file *filp, + unsigned int ioctl, unsigned long arg) +{ + int ret = 0; + + switch (ioctl) { + default: + ret = -EINVAL; + } + + if (ret < 0) + printk(KERN_ERR "error processing ARM ioct: %d", ret); + return ret; +} + +int kvm_arch_set_memory_region(struct kvm *kvm, + struct kvm_userspace_memory_region *mem, + struct kvm_memory_slot old, + int user_alloc) +{ + return 0; +} + +int kvm_arch_prepare_memory_region(struct kvm *kvm, + struct kvm_memory_slot *memslot, + struct kvm_memory_slot old, + struct kvm_userspace_memory_region *mem, + int user_alloc) +{ + return 0; +} + +void kvm_arch_commit_memory_region(struct kvm *kvm, + struct kvm_userspace_memory_region *mem, + struct kvm_memory_slot old, + int user_alloc) +{ +} + +void kvm_arch_flush_shadow(struct kvm *kvm) +{ +} + +struct kvm_vcpu *kvm_arch_vcpu_create(struct kvm *kvm, unsigned int id) +{ + int err; + struct kvm_vcpu *vcpu; + + vcpu = kmem_cache_zalloc(kvm_vcpu_cache, GFP_KERNEL); + if (!vcpu) { + err = -ENOMEM; + goto out; + } + + err = kvm_vcpu_init(vcpu, kvm, id); + if (err) + goto free_vcpu; + + latest_vcpu = vcpu; + return vcpu; +free_vcpu: + kmem_cache_free(kvm_vcpu_cache, vcpu); +out: + return ERR_PTR(err); +} + +void kvm_arch_vcpu_free(struct kvm_vcpu *vcpu) +{ + latest_vcpu = NULL; + KVMARM_NOT_IMPLEMENTED(); +} + +void kvm_arch_vcpu_destroy(struct kvm_vcpu *vcpu) +{ + kvm_arch_vcpu_free(vcpu); +} + +int kvm_cpu_has_pending_timer(struct kvm_vcpu *vcpu) +{ + return 0; +} + +int kvm_arch_vcpu_init(struct kvm_vcpu *vcpu) +{ + KVMARM_NOT_IMPLEMENTED(); + return 0; +} + +void kvm_arch_vcpu_uninit(struct kvm_vcpu *vcpu) +{ +} + +void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu) +{ +} + +void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu) +{ +} + +int kvm_arch_vcpu_ioctl_set_guest_debug(struct kvm_vcpu *vcpu, + struct kvm_guest_debug *dbg) +{ + return -EINVAL; +} + + +int kvm_arch_vcpu_ioctl_get_mpstate(struct kvm_vcpu *vcpu, + struct kvm_mp_state *mp_state) +{ + return -EINVAL; +} + +int kvm_arch_vcpu_ioctl_set_mpstate(struct kvm_vcpu *vcpu, + struct kvm_mp_state *mp_state) +{ + return -EINVAL; +} + +int kvm_arch_vcpu_runnable(struct kvm_vcpu *v) +{ + return 0; +} + +int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run) +{ + KVMARM_NOT_IMPLEMENTED(); + return -EINVAL; +} + +long kvm_arch_vcpu_ioctl(struct file *filp, + unsigned int ioctl, unsigned long arg) +{ + kvm_err(-EINVAL, "Unsupported ioctl (%d)", ioctl); + return -EINVAL; +} + +int kvm_vm_ioctl_get_dirty_log(struct kvm *kvm, struct kvm_dirty_log *log) +{ + return -EINVAL; +} + +long kvm_arch_vm_ioctl(struct file *filp, + unsigned int ioctl, unsigned long arg) +{ + printk(KERN_ERR "kvm_arch_vm_ioctl: Unsupported ioctl (%d)\n", ioctl); + return -EINVAL; +} + +int kvm_arch_init(void *opaque) +{ + return 0; +} + +void kvm_arch_exit(void) +{ +} + +static int arm_init(void) +{ + int rc = kvm_init(NULL, sizeof(struct kvm_vcpu), 0, THIS_MODULE); + if (rc == 0) + kvm_arm_debugfs_init(); + return rc; +} + +static void __exit arm_exit(void) +{ + kvm_exit(); + kvm_arm_debugfs_exit(); +} + +module_init(arm_init); +module_exit(arm_exit) diff --git a/arch/arm/kvm/arm_emulate.c b/arch/arm/kvm/arm_emulate.c new file mode 100644 index 0000000..6587dde --- /dev/null +++ b/arch/arm/kvm/arm_emulate.c @@ -0,0 +1,121 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include <asm/kvm_emulate.h> + +#define USR_REG_OFFSET(_reg) \ + offsetof(struct kvm_vcpu_arch, regs.usr_regs[_reg]) + +static unsigned long vcpu_reg_offsets[MODE_SYS + 1][16] = { + /* FIQ Registers */ + { + USR_REG_OFFSET(0), USR_REG_OFFSET(1), USR_REG_OFFSET(2), + USR_REG_OFFSET(3), USR_REG_OFFSET(4), USR_REG_OFFSET(5), + USR_REG_OFFSET(6), USR_REG_OFFSET(7), + offsetof(struct kvm_vcpu_arch, regs.fiq_regs[1]), /* r8 */ + offsetof(struct kvm_vcpu_arch, regs.fiq_regs[1]), /* r9 */ + offsetof(struct kvm_vcpu_arch, regs.fiq_regs[2]), /* r10 */ + offsetof(struct kvm_vcpu_arch, regs.fiq_regs[3]), /* r11 */ + offsetof(struct kvm_vcpu_arch, regs.fiq_regs[4]), /* r12 */ + offsetof(struct kvm_vcpu_arch, regs.fiq_regs[5]), /* r13 */ + offsetof(struct kvm_vcpu_arch, regs.fiq_regs[6]), /* r14 */ + offsetof(struct kvm_vcpu_arch, regs.pc) /* r15 */ + }, + + /* IRQ Registers */ + { + USR_REG_OFFSET(0), USR_REG_OFFSET(1), USR_REG_OFFSET(2), + USR_REG_OFFSET(3), USR_REG_OFFSET(4), USR_REG_OFFSET(5), + USR_REG_OFFSET(6), USR_REG_OFFSET(7), USR_REG_OFFSET(8), + USR_REG_OFFSET(9), USR_REG_OFFSET(10), USR_REG_OFFSET(11), + USR_REG_OFFSET(12), + offsetof(struct kvm_vcpu_arch, regs.irq_regs[0]), /* r13 */ + offsetof(struct kvm_vcpu_arch, regs.irq_regs[1]), /* r14 */ + offsetof(struct kvm_vcpu_arch, regs.pc) /* r15 */ + }, + + /* SVC Registers */ + { + USR_REG_OFFSET(0), USR_REG_OFFSET(1), USR_REG_OFFSET(2), + USR_REG_OFFSET(3), USR_REG_OFFSET(4), USR_REG_OFFSET(5), + USR_REG_OFFSET(6), USR_REG_OFFSET(7), USR_REG_OFFSET(8), + USR_REG_OFFSET(9), USR_REG_OFFSET(10), USR_REG_OFFSET(11), + USR_REG_OFFSET(12), + offsetof(struct kvm_vcpu_arch, regs.svc_regs[0]), /* r13 */ + offsetof(struct kvm_vcpu_arch, regs.svc_regs[1]), /* r14 */ + offsetof(struct kvm_vcpu_arch, regs.pc) /* r15 */ + }, + + /* ABT Registers */ + { + USR_REG_OFFSET(0), USR_REG_OFFSET(1), USR_REG_OFFSET(2), + USR_REG_OFFSET(3), USR_REG_OFFSET(4), USR_REG_OFFSET(5), + USR_REG_OFFSET(6), USR_REG_OFFSET(7), USR_REG_OFFSET(8), + USR_REG_OFFSET(9), USR_REG_OFFSET(10), USR_REG_OFFSET(11), + USR_REG_OFFSET(12), + offsetof(struct kvm_vcpu_arch, regs.abt_regs[0]), /* r13 */ + offsetof(struct kvm_vcpu_arch, regs.abt_regs[1]), /* r14 */ + offsetof(struct kvm_vcpu_arch, regs.pc) /* r15 */ + }, + + /* UND Registers */ + { + USR_REG_OFFSET(0), USR_REG_OFFSET(1), USR_REG_OFFSET(2), + USR_REG_OFFSET(3), USR_REG_OFFSET(4), USR_REG_OFFSET(5), + USR_REG_OFFSET(6), USR_REG_OFFSET(7), USR_REG_OFFSET(8), + USR_REG_OFFSET(9), USR_REG_OFFSET(10), USR_REG_OFFSET(11), + USR_REG_OFFSET(12), + offsetof(struct kvm_vcpu_arch, regs.und_regs[0]), /* r13 */ + offsetof(struct kvm_vcpu_arch, regs.und_regs[1]), /* r14 */ + offsetof(struct kvm_vcpu_arch, regs.pc) /* r15 */ + }, + + /* USR Registers */ + { + USR_REG_OFFSET(0), USR_REG_OFFSET(1), USR_REG_OFFSET(2), + USR_REG_OFFSET(3), USR_REG_OFFSET(4), USR_REG_OFFSET(5), + USR_REG_OFFSET(6), USR_REG_OFFSET(7), USR_REG_OFFSET(8), + USR_REG_OFFSET(9), USR_REG_OFFSET(10), USR_REG_OFFSET(11), + USR_REG_OFFSET(12), + offsetof(struct kvm_vcpu_arch, regs.usr_regs[13]), /* r13 */ + offsetof(struct kvm_vcpu_arch, regs.usr_regs[14]), /* r14 */ + offsetof(struct kvm_vcpu_arch, regs.pc) /* r15 */ + }, + + /* SYS Registers */ + { + USR_REG_OFFSET(0), USR_REG_OFFSET(1), USR_REG_OFFSET(2), + USR_REG_OFFSET(3), USR_REG_OFFSET(4), USR_REG_OFFSET(5), + USR_REG_OFFSET(6), USR_REG_OFFSET(7), USR_REG_OFFSET(8), + USR_REG_OFFSET(9), USR_REG_OFFSET(10), USR_REG_OFFSET(11), + USR_REG_OFFSET(12), + offsetof(struct kvm_vcpu_arch, regs.usr_regs[13]), /* r13 */ + offsetof(struct kvm_vcpu_arch, regs.usr_regs[14]), /* r14 */ + offsetof(struct kvm_vcpu_arch, regs.pc) /* r15 */ + }, +}; + +/* + * Return a pointer to the register number valid in the specified mode of + * the virtual CPU. + */ +u32 *kvm_vcpu_reg(struct kvm_vcpu *vcpu, u8 reg_num, u32 mode) +{ + BUG_ON(reg_num > 15); + BUG_ON(mode > MODE_SYS); + + return (u32 *)((void *)&vcpu->arch + vcpu_reg_offsets[mode][reg_num]); +} diff --git a/arch/arm/kvm/arm_exports.c b/arch/arm/kvm/arm_exports.c new file mode 100644 index 0000000..d8a7fd5 --- /dev/null +++ b/arch/arm/kvm/arm_exports.c @@ -0,0 +1,16 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <linux/module.h> diff --git a/arch/arm/kvm/arm_guest.c b/arch/arm/kvm/arm_guest.c new file mode 100644 index 0000000..94a5c54 --- /dev/null +++ b/arch/arm/kvm/arm_guest.c @@ -0,0 +1,148 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include <linux/errno.h> +#include <linux/err.h> +#include <linux/kvm_host.h> +#include <linux/module.h> +#include <linux/vmalloc.h> +#include <linux/fs.h> +#include <asm/uaccess.h> +#include <asm/kvm_asm.h> +#include <asm/kvm_emulate.h> + + +#define VM_STAT(x) (offsetof(struct kvm, stat.x), KVM_STAT_VM) +#define VCPU_STAT(x) (offsetof(struct kvm_vcpu, stat.x), KVM_STAT_VCPU) + +struct kvm_stats_debugfs_item debugfs_entries[] = { + { NULL } +}; + +int kvm_arch_vcpu_setup(struct kvm_vcpu *vcpu) +{ + return 0; +} + +int kvm_arch_vcpu_ioctl_get_regs(struct kvm_vcpu *vcpu, struct kvm_regs *regs) +{ + struct kvm_vcpu_regs *vcpu_regs = &vcpu->arch.regs; + + /* + * GPRs and PSRs + */ + memcpy(regs->regs0_7, &(vcpu_regs->usr_regs[0]), sizeof(u32) * 8); + memcpy(regs->usr_regs8_12, &(vcpu_regs->usr_regs[8]), sizeof(u32) * 5); + memcpy(regs->fiq_regs8_12, &(vcpu_regs->fiq_regs[0]), sizeof(u32) * 5); + regs->reg13[MODE_FIQ] = vcpu_regs->fiq_regs[5]; + regs->reg14[MODE_FIQ] = vcpu_regs->fiq_regs[6]; + regs->reg13[MODE_IRQ] = vcpu_regs->irq_regs[0]; + regs->reg14[MODE_IRQ] = vcpu_regs->irq_regs[1]; + regs->reg13[MODE_SVC] = vcpu_regs->svc_regs[0]; + regs->reg14[MODE_SVC] = vcpu_regs->svc_regs[1]; + regs->reg13[MODE_ABT] = vcpu_regs->abt_regs[0]; + regs->reg14[MODE_ABT] = vcpu_regs->abt_regs[1]; + regs->reg13[MODE_UND] = vcpu_regs->und_regs[0]; + regs->reg14[MODE_UND] = vcpu_regs->und_regs[1]; + regs->reg13[MODE_USR] = vcpu_regs->usr_regs[0]; + regs->reg14[MODE_USR] = vcpu_regs->usr_regs[1]; + + regs->spsr[MODE_FIQ] = vcpu_regs->fiq_regs[7]; + regs->spsr[MODE_IRQ] = vcpu_regs->irq_regs[2]; + regs->spsr[MODE_SVC] = vcpu_regs->svc_regs[2]; + regs->spsr[MODE_ABT] = vcpu_regs->abt_regs[2]; + regs->spsr[MODE_UND] = vcpu_regs->und_regs[2]; + + regs->reg15 = vcpu_regs->pc; + regs->cpsr = vcpu_regs->cpsr; + + + /* + * Co-processor registers. + */ + regs->cp15.c1_sys = vcpu->arch.cp15.c1_SCTLR; + regs->cp15.c2_base0 = vcpu->arch.cp15.c2_TTBR0; + regs->cp15.c2_base1 = vcpu->arch.cp15.c2_TTBR1; + regs->cp15.c3_dacr = vcpu->arch.cp15.c3_DACR; + + return 0; +} + +int kvm_arch_vcpu_ioctl_set_regs(struct kvm_vcpu *vcpu, struct kvm_regs *regs) +{ + struct kvm_vcpu_regs *vcpu_regs = &vcpu->arch.regs; + + memcpy(&(vcpu_regs->usr_regs[0]), regs->regs0_7, sizeof(u32) * 8); + memcpy(&(vcpu_regs->usr_regs[8]), regs->usr_regs8_12, sizeof(u32) * 5); + memcpy(&(vcpu_regs->fiq_regs[0]), regs->fiq_regs8_12, sizeof(u32) * 5); + + vcpu_regs->fiq_regs[5] = regs->reg13[MODE_FIQ]; + vcpu_regs->fiq_regs[6] = regs->reg14[MODE_FIQ]; + vcpu_regs->irq_regs[0] = regs->reg13[MODE_IRQ]; + vcpu_regs->irq_regs[1] = regs->reg14[MODE_IRQ]; + vcpu_regs->svc_regs[0] = regs->reg13[MODE_SVC]; + vcpu_regs->svc_regs[1] = regs->reg14[MODE_SVC]; + vcpu_regs->abt_regs[0] = regs->reg13[MODE_ABT]; + vcpu_regs->abt_regs[1] = regs->reg14[MODE_ABT]; + vcpu_regs->und_regs[0] = regs->reg13[MODE_UND]; + vcpu_regs->und_regs[1] = regs->reg14[MODE_UND]; + vcpu_regs->usr_regs[0] = regs->reg13[MODE_USR]; + vcpu_regs->usr_regs[1] = regs->reg14[MODE_USR]; + + vcpu_regs->fiq_regs[7] = regs->spsr[MODE_FIQ]; + vcpu_regs->irq_regs[2] = regs->spsr[MODE_IRQ]; + vcpu_regs->svc_regs[2] = regs->spsr[MODE_SVC]; + vcpu_regs->abt_regs[2] = regs->spsr[MODE_ABT]; + vcpu_regs->und_regs[2] = regs->spsr[MODE_UND]; + + /* + * Co-processor registers. + */ + vcpu->arch.cp15.c1_SCTLR = regs->cp15.c1_sys; + + vcpu_regs->pc = regs->reg15; + vcpu_regs->cpsr = regs->cpsr; + + return 0; +} + +int kvm_arch_vcpu_ioctl_get_sregs(struct kvm_vcpu *vcpu, + struct kvm_sregs *sregs) +{ + return -EINVAL; +} + +int kvm_arch_vcpu_ioctl_set_sregs(struct kvm_vcpu *vcpu, + struct kvm_sregs *sregs) +{ + return -EINVAL; +} + +int kvm_arch_vcpu_ioctl_get_fpu(struct kvm_vcpu *vcpu, struct kvm_fpu *fpu) +{ + return -EINVAL; +} + +int kvm_arch_vcpu_ioctl_set_fpu(struct kvm_vcpu *vcpu, struct kvm_fpu *fpu) +{ + return -EINVAL; +} + +int kvm_arch_vcpu_ioctl_translate(struct kvm_vcpu *vcpu, + struct kvm_translation *tr) +{ + return -EINVAL; +} diff --git a/arch/arm/kvm/arm_init.S b/arch/arm/kvm/arm_init.S new file mode 100644 index 0000000..073a494 --- /dev/null +++ b/arch/arm/kvm/arm_init.S @@ -0,0 +1,17 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ +#include <asm/asm-offsets.h> +#include <asm/kvm_asm.h> diff --git a/arch/arm/kvm/arm_interrupts.S b/arch/arm/kvm/arm_interrupts.S new file mode 100644 index 0000000..073a494 --- /dev/null +++ b/arch/arm/kvm/arm_interrupts.S @@ -0,0 +1,17 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ +#include <asm/asm-offsets.h> +#include <asm/kvm_asm.h> diff --git a/arch/arm/kvm/arm_mmu.c b/arch/arm/kvm/arm_mmu.c new file mode 100644 index 0000000..2cccd48 --- /dev/null +++ b/arch/arm/kvm/arm_mmu.c @@ -0,0 +1,15 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ diff --git a/arch/arm/kvm/debug.c b/arch/arm/kvm/debug.c new file mode 100644 index 0000000..c2b213a --- /dev/null +++ b/arch/arm/kvm/debug.c @@ -0,0 +1,377 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ +#include <linux/module.h> +#include <linux/types.h> +#include <linux/kvm_types.h> +#include <linux/kvm_host.h> +#include <linux/debugfs.h> +#include <linux/seq_file.h> +#include <asm/kvm_emulate.h> + +#include "debug.h" + +static struct dentry *vcpu_debugfs_file; +static struct dentry *ws_debugfs_file; + +/****************************************************************************** + * World-switch ring-buffer + */ + +#define WS_TRACE_ITEMS 10 +static u32 ws_trace_enter[WS_TRACE_ITEMS]; +static int ws_trace_enter_index; +static u32 ws_trace_exit[WS_TRACE_ITEMS]; +static int ws_trace_exit_index; +DEFINE_MUTEX(ws_trace_mutex); + +void debug_ws_enter(u32 guest_pc) +{ + mutex_lock(&ws_trace_mutex); + ws_trace_enter[ws_trace_enter_index++] = guest_pc; + if (ws_trace_enter_index >= WS_TRACE_ITEMS) + ws_trace_enter_index = 0; + mutex_unlock(&ws_trace_mutex); +} + +void debug_ws_exit(u32 guest_pc) +{ + mutex_lock(&ws_trace_mutex); + ws_trace_exit[ws_trace_exit_index++] = guest_pc; + if (ws_trace_exit_index >= WS_TRACE_ITEMS) + ws_trace_exit_index = 0; + mutex_unlock(&ws_trace_mutex); +} + +void print_ws_trace(void) +{ + int i; + mutex_lock(&ws_trace_mutex); + + if (ws_trace_enter_index != ws_trace_exit_index) { + kvm_msg("enter and exit WS trace count differ"); + mutex_unlock(&ws_trace_mutex); + return; + } + + /* Avoid potential endless loop */ + if (ws_trace_enter_index < 0 || + ws_trace_enter_index >= WS_TRACE_ITEMS) { + kvm_msg("ws_trace_enter_index out of bounds: %d", + ws_trace_enter_index); + mutex_unlock(&ws_trace_mutex); + return; + } + + for (i = ws_trace_enter_index - 1; i != ws_trace_enter_index; i--) { + if (i < 0) { + i = WS_TRACE_ITEMS; + continue; + } + + printk(KERN_ERR "Enter: %08x Exit: %08x\n", + ws_trace_enter[i], + ws_trace_exit[i]); + } + mutex_unlock(&ws_trace_mutex); +} + +/****************************************************************************** + * Dump total debug info, or write to debugfs entry + */ + +struct kvm_vcpu *latest_vcpu; + +void print_kvm_vcpu_info(int (*print_fn)(print_fn_args), struct seq_file *m) +{ + int i; + struct kvm_vcpu_regs *regs; + char *mode = NULL; + struct kvm_vcpu *vcpu = latest_vcpu; + + print_fn(m, "KVM/ARM runtime info\n"); + print_fn(m, "======================================================"); + print_fn(m, "\n\n"); + + if (vcpu == NULL) { + print_fn(m, "No registered VCPU\n"); + goto out; + } + + switch (vcpu_mode(vcpu)) { + case MODE_USR: + mode = "USR"; + break; + case MODE_FIQ: + mode = "FIQ"; + break; + case MODE_IRQ: + mode = "IRQ"; + break; + case MODE_SVC: + mode = "SVC"; + break; + case MODE_ABT: + mode = "ABT"; + break; + case MODE_UND: + mode = "UND"; + break; + case MODE_SYS: + mode = "SYS"; + break; + } + + vcpu_load(vcpu); + regs = &vcpu->arch.regs; + + print_fn(m, "Virtual CPU state:\n\n"); + print_fn(m, "PC is at: \t%08x\n", vcpu_reg(vcpu, 15)); + print_fn(m, "CPSR: \t%08x\n(Mode: %s) (IRQs: %s) (FIQs: %s) " + " (Vec: %s)\n", + regs->cpsr, mode, + (regs->cpsr & PSR_I_BIT) ? "off" : "on", + (regs->cpsr & PSR_F_BIT) ? "off" : "on", + (regs->cpsr & PSR_V_BIT) ? "high" : "low"); + + for (i = 0; i <= 12; i++) { + if ((i % 4) == 0) + print_fn(m, "\nregs[%u]: ", i); + + print_fn(m, "\t0x%08x", *kvm_vcpu_reg(vcpu, i, MODE_USR)); + } + + print_fn(m, "\n\n"); + print_fn(m, "Banked registers: \tr13\t\tr14\t\tspsr\n"); + print_fn(m, "-------------------\t--------\t--------\t--------\n"); + print_fn(m, " USR: \t%08x\t%08x\t////////\n", + *kvm_vcpu_reg(vcpu, 13, MODE_USR), + *kvm_vcpu_reg(vcpu, 14, MODE_USR)); + print_fn(m, " SVC: \t%08x\t%08x\t%08x\n", + *kvm_vcpu_reg(vcpu, 13, MODE_SVC), + *kvm_vcpu_reg(vcpu, 14, MODE_SVC), + *kvm_vcpu_spsr(vcpu, MODE_SVC)); + print_fn(m, " ABT: \t%08x\t%08x\t%08x\n", + *kvm_vcpu_reg(vcpu, 13, MODE_ABT), + *kvm_vcpu_reg(vcpu, 14, MODE_ABT), + *kvm_vcpu_spsr(vcpu, MODE_ABT)); + print_fn(m, " UND: \t%08x\t%08x\t%08x\n", + *kvm_vcpu_reg(vcpu, 13, MODE_UND), + *kvm_vcpu_reg(vcpu, 14, MODE_UND), + *kvm_vcpu_spsr(vcpu, MODE_UND)); + print_fn(m, " IRQ: \t%08x\t%08x\t%08x\n", + *kvm_vcpu_reg(vcpu, 13, MODE_IRQ), + *kvm_vcpu_reg(vcpu, 14, MODE_IRQ), + *kvm_vcpu_spsr(vcpu, MODE_IRQ)); + print_fn(m, " FIQ: \t%08x\t%08x\t%08x\n", + *kvm_vcpu_reg(vcpu, 13, MODE_FIQ), + *kvm_vcpu_reg(vcpu, 14, MODE_FIQ), + *kvm_vcpu_spsr(vcpu, MODE_FIQ)); + + print_fn(m, "\n"); + print_fn(m, "fiq regs:\t%08x\t%08x\t%08x\t%08x\n" + " \t%08x\n", + *kvm_vcpu_reg(vcpu, 8, MODE_FIQ), + *kvm_vcpu_reg(vcpu, 9, MODE_FIQ), + *kvm_vcpu_reg(vcpu, 10, MODE_FIQ), + *kvm_vcpu_reg(vcpu, 11, MODE_FIQ), + *kvm_vcpu_reg(vcpu, 12, MODE_FIQ)); + +out: + if (vcpu != NULL) + vcpu_put(vcpu); +} + +void print_kvm_ws_info(int (*print_fn)(print_fn_args), struct seq_file *m) +{ + int i; + + /* + * Print world-switch trace circular buffer + */ + print_fn(m, "World switch history:\n"); + print_fn(m, "---------------------\n"); + mutex_lock(&ws_trace_mutex); + + if (ws_trace_enter_index != ws_trace_exit_index || + ws_trace_enter_index < 0 || + ws_trace_enter_index >= WS_TRACE_ITEMS) { + mutex_unlock(&ws_trace_mutex); + return; + } + + for (i = ws_trace_enter_index - 1; i != ws_trace_enter_index; i--) { + if (i < 0) { + i = WS_TRACE_ITEMS; + continue; + } + + print_fn(m, "Enter: %08x Exit: %08x\n", + ws_trace_enter[i], ws_trace_exit[i]); + } + mutex_unlock(&ws_trace_mutex); +} + +static int __printk_relay(struct seq_file *m, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + vprintk(fmt, ap); + va_end(ap); + return 0; +} + +void kvm_dump_vcpu_state(void) +{ + print_kvm_vcpu_info(&__printk_relay, NULL); +} + +void kvm_arm_trace_init(void) +{ + +} + +/****************************************************************************** + * debugfs handling + */ + +static int vcpu_debugfs_show(struct seq_file *m, void *v) +{ + print_kvm_vcpu_info(&seq_printf, m); + return 0; +} + +static int ws_debugfs_show(struct seq_file *m, void *v) +{ + print_kvm_ws_info(&seq_printf, m); + return 0; +} + +static void *k_start(struct seq_file *m, loff_t *pos) +{ + return *pos < 1 ? (void *)1 : NULL; +} + +static void *k_next(struct seq_file *m, void *v, loff_t *pos) +{ + ++*pos; + return NULL; +} + +static void k_stop(struct seq_file *m, void *v) +{ +} + +static const struct seq_operations vcpu_debugfs_op = { + .start = k_start, + .next = k_next, + .stop = k_stop, + .show = vcpu_debugfs_show +}; + +static const struct seq_operations ws_debugfs_op = { + .start = k_start, + .next = k_next, + .stop = k_stop, + .show = ws_debugfs_show +}; + +static int vcpu_debugfs_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &vcpu_debugfs_op); +} + +static int ws_debugfs_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &ws_debugfs_op); +} + +static const struct file_operations vcpu_debugfs_fops = { + .owner = THIS_MODULE, + .open = vcpu_debugfs_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +static const struct file_operations ws_debugfs_fops = { + .owner = THIS_MODULE, + .open = ws_debugfs_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +/** + * kvm_arm_debugfs_init - create debugfs directory and files + * + * Create the debugfs entries for KVM/ARM + */ +void kvm_arm_debugfs_init(void) +{ + struct dentry *file; + + file = debugfs_create_file("vcpu", 0444, kvm_debugfs_dir, + NULL, &vcpu_debugfs_fops); + if (IS_ERR(file) || !file) { + kvm_err(PTR_ERR(file), + "cannot create debugfs KVM/ARM vcpu file\n"); + return; + } + vcpu_debugfs_file = file; + + file = debugfs_create_file("ws", 0444, kvm_debugfs_dir, + NULL, &ws_debugfs_fops); + if (IS_ERR(file) || !file) { + kvm_err(PTR_ERR(file), + "cannot create debugfs KVM/ARM ws file\n"); + } + ws_debugfs_file = file; +} + +void kvm_arm_debugfs_exit(void) +{ + if (vcpu_debugfs_file) + debugfs_remove(vcpu_debugfs_file); + if (ws_debugfs_file) + debugfs_remove(ws_debugfs_file); +} + + +/****************************************************************************** + * Printk-log-wrapping functionality + */ + +#define TMP_LOG_LEN 512 +static char __tmp_log_data[TMP_LOG_LEN]; +DEFINE_MUTEX(__tmp_log_lock); +void __kvm_print_msg(char *fmt, ...) +{ + va_list ap; + unsigned int size; + + mutex_lock(&__tmp_log_lock); + + va_start(ap, fmt); + size = vsnprintf(__tmp_log_data, TMP_LOG_LEN, fmt, ap); + va_end(ap); + + if (size >= TMP_LOG_LEN) + printk(KERN_ERR "Message exceeded log length!\n"); + else + printk(KERN_INFO "%s", __tmp_log_data); + + mutex_unlock(&__tmp_log_lock); +} diff --git a/arch/arm/kvm/debug.h b/arch/arm/kvm/debug.h new file mode 100644 index 0000000..4f5f381 --- /dev/null +++ b/arch/arm/kvm/debug.h @@ -0,0 +1,63 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * + * This file contains debugging and tracing functions and definitions + * for KVM/ARM. + */ +#ifndef __ARM_KVM_TRACE_H__ +#define __ARM_KVM_TRACE_H__ + +#include <linux/types.h> +#include <linux/kvm_types.h> +#include <linux/kvm_host.h> + +extern struct kvm_vcpu *latest_vcpu; + +void kvm_dump_vcpu_state(void); + +void debug_ws_enter(u32 guest_pc); +void debug_ws_exit(u32 guest_pc); + +#define print_fn_args struct seq_file *, const char *, ... +void print_kvm_debug_info(int (*print_fn)(print_fn_args), struct seq_file *m); + +void __kvm_print_msg(char *_fmt, ...); + +#define kvm_err(err, fmt, args...) do { \ + __kvm_print_msg(KERN_ERR "KVM error [%s:%d]: (%d) ", \ + __func__, __LINE__, err); \ + __kvm_print_msg(fmt "\n", ##args); \ +} while (0) + +#define __kvm_msg(fmt, args...) do { \ + __kvm_print_msg(KERN_ERR "KVM [%s:%d]: ", __func__, __LINE__); \ + __kvm_print_msg(fmt, ##args); \ +} while (0) + +#define kvm_msg(__fmt, __args...) __kvm_msg(__fmt "\n", ##__args) + + +#define KVMARM_NOT_IMPLEMENTED() \ +{ \ + printk(KERN_ERR "KVM not implemented [%s:%d] in %s\n", \ + __FILE__, __LINE__, __func__); \ +} + +void print_ws_trace(void); + +void kvm_arm_debugfs_init(void); +void kvm_arm_debugfs_exit(void); + +#endif /* __ARM_KVM_TRACE_H__ */ diff --git a/arch/arm/kvm/trace.h b/arch/arm/kvm/trace.h new file mode 100644 index 0000000..f8869c1 --- /dev/null +++ b/arch/arm/kvm/trace.h @@ -0,0 +1,52 @@ +#if !defined(_TRACE_KVM_H) || defined(TRACE_HEADER_MULTI_READ) +#define _TRACE_KVM_H + +#include <linux/tracepoint.h> + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM kvm + +/* + * Tracepoints for entry/exit to guest + */ +TRACE_EVENT(kvm_entry, + TP_PROTO(unsigned long vcpu_pc), + TP_ARGS(vcpu_pc), + + TP_STRUCT__entry( + __field( unsigned long, vcpu_pc ) + ), + + TP_fast_assign( + __entry->vcpu_pc = vcpu_pc; + ), + + TP_printk("PC: 0x%08lx", __entry->vcpu_pc) +); + +TRACE_EVENT(kvm_exit, + TP_PROTO(unsigned long vcpu_pc), + TP_ARGS(vcpu_pc), + + TP_STRUCT__entry( + __field( unsigned long, vcpu_pc ) + ), + + TP_fast_assign( + __entry->vcpu_pc = vcpu_pc; + ), + + TP_printk("PC: 0x%08lx", __entry->vcpu_pc) +); + + + +#endif /* _TRACE_KVM_H */ + +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH arch/arm/kvm +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE trace + +/* This part must be outside protection */ +#include <trace/define_trace.h> diff --git a/arch/arm/mach-vexpress/Kconfig b/arch/arm/mach-vexpress/Kconfig index e8c1111..34febe1 100644 --- a/arch/arm/mach-vexpress/Kconfig +++ b/arch/arm/mach-vexpress/Kconfig @@ -36,6 +36,7 @@ config ARCH_VEXPRESS_CA15X4 bool "Versatile Express Cortex-A15x4 tile" depends on VEXPRESS_EXTENDED_MEMORY_MAP select CPU_V7 + select ARM_VIRT_EXT select ARM_GIC select HAVE_ARCH_TIMERS diff --git a/arch/arm/mm/Kconfig b/arch/arm/mm/Kconfig index afb5231..ab7d8ea 100644 --- a/arch/arm/mm/Kconfig +++ b/arch/arm/mm/Kconfig @@ -636,6 +636,13 @@ config ARM_LPAE Say Y if you have an ARMv7 processor supporting the LPAE page table format and you would like to access memory beyond the 4GB limit. +config ARM_VIRT_EXT + bool "Support for ARM Virtualization Extensions" + depends on ARM_LPAE + help + Say Y if you have an ARMv7 processor supporting the ARM hardware + Virtualization extensions. + config ARCH_PHYS_ADDR_T_64BIT def_bool ARM_LPAE -- To unsubscribe from this list: send the line "unsubscribe kvm" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html