]> Pileus Git - ~andy/linux/commitdiff
Merge branch 'kvm-arm-cleanup' from git://github.com/columbia/linux-kvm-arm.git
authorGleb Natapov <gleb@redhat.com>
Thu, 25 Apr 2013 15:23:48 +0000 (18:23 +0300)
committerGleb Natapov <gleb@redhat.com>
Thu, 25 Apr 2013 15:23:48 +0000 (18:23 +0300)
19 files changed:
arch/arm/include/asm/kvm_arm.h
arch/arm/include/asm/kvm_asm.h
arch/arm/include/asm/kvm_emulate.h
arch/arm/include/asm/kvm_host.h
arch/arm/include/asm/kvm_mmu.h
arch/arm/include/asm/kvm_vgic.h
arch/arm/include/uapi/asm/kvm.h
arch/arm/kernel/asm-offsets.c
arch/arm/kvm/Makefile
arch/arm/kvm/arm.c
arch/arm/kvm/coproc.c
arch/arm/kvm/coproc.h
arch/arm/kvm/emulate.c
arch/arm/kvm/guest.c
arch/arm/kvm/handle_exit.c [new file with mode: 0644]
arch/arm/kvm/interrupts.S
arch/arm/kvm/mmio.c
arch/arm/kvm/mmu.c
arch/arm/kvm/vgic.c

index 7c3d813e15dffe76720472252995c27a22950cd6..124623e5ef14ab2bde6b6ab954901fa4d4b8c3a7 100644 (file)
 
 #define HSR_HVC_IMM_MASK       ((1UL << 16) - 1)
 
+#define HSR_DABT_S1PTW         (1U << 7)
+#define HSR_DABT_CM            (1U << 8)
+#define HSR_DABT_EA            (1U << 9)
+
 #endif /* __ARM_KVM_ARM_H__ */
index e4956f4e23e11f94e1c9cc3526ab1e7034b80fc8..18d50322a9e2fd147c361717097dba4d1a2ba35d 100644 (file)
@@ -75,7 +75,7 @@ extern char __kvm_hyp_code_end[];
 extern void __kvm_tlb_flush_vmid(struct kvm *kvm);
 
 extern void __kvm_flush_vm_context(void);
-extern void __kvm_tlb_flush_vmid(struct kvm *kvm);
+extern void __kvm_tlb_flush_vmid_ipa(struct kvm *kvm, phys_addr_t ipa);
 
 extern int __kvm_vcpu_run(struct kvm_vcpu *vcpu);
 #endif
index fd611996bfb5c15f53ae4b11107fc7af32087432..82b4babead2c8b7bff97a2c9b0e3f3fc37ba63f7 100644 (file)
 #include <linux/kvm_host.h>
 #include <asm/kvm_asm.h>
 #include <asm/kvm_mmio.h>
+#include <asm/kvm_arm.h>
 
-u32 *vcpu_reg(struct kvm_vcpu *vcpu, u8 reg_num);
-u32 *vcpu_spsr(struct kvm_vcpu *vcpu);
+unsigned long *vcpu_reg(struct kvm_vcpu *vcpu, u8 reg_num);
+unsigned long *vcpu_spsr(struct kvm_vcpu *vcpu);
 
-int kvm_handle_wfi(struct kvm_vcpu *vcpu, struct kvm_run *run);
+bool kvm_condition_valid(struct kvm_vcpu *vcpu);
 void kvm_skip_instr(struct kvm_vcpu *vcpu, bool is_wide_instr);
 void kvm_inject_undefined(struct kvm_vcpu *vcpu);
 void kvm_inject_dabt(struct kvm_vcpu *vcpu, unsigned long addr);
@@ -37,14 +38,14 @@ static inline bool vcpu_mode_is_32bit(struct kvm_vcpu *vcpu)
        return 1;
 }
 
-static inline u32 *vcpu_pc(struct kvm_vcpu *vcpu)
+static inline unsigned long *vcpu_pc(struct kvm_vcpu *vcpu)
 {
-       return (u32 *)&vcpu->arch.regs.usr_regs.ARM_pc;
+       return &vcpu->arch.regs.usr_regs.ARM_pc;
 }
 
-static inline u32 *vcpu_cpsr(struct kvm_vcpu *vcpu)
+static inline unsigned long *vcpu_cpsr(struct kvm_vcpu *vcpu)
 {
-       return (u32 *)&vcpu->arch.regs.usr_regs.ARM_cpsr;
+       return &vcpu->arch.regs.usr_regs.ARM_cpsr;
 }
 
 static inline void vcpu_set_thumb(struct kvm_vcpu *vcpu)
@@ -69,4 +70,96 @@ static inline bool kvm_vcpu_reg_is_pc(struct kvm_vcpu *vcpu, int reg)
        return reg == 15;
 }
 
+static inline u32 kvm_vcpu_get_hsr(struct kvm_vcpu *vcpu)
+{
+       return vcpu->arch.fault.hsr;
+}
+
+static inline unsigned long kvm_vcpu_get_hfar(struct kvm_vcpu *vcpu)
+{
+       return vcpu->arch.fault.hxfar;
+}
+
+static inline phys_addr_t kvm_vcpu_get_fault_ipa(struct kvm_vcpu *vcpu)
+{
+       return ((phys_addr_t)vcpu->arch.fault.hpfar & HPFAR_MASK) << 8;
+}
+
+static inline unsigned long kvm_vcpu_get_hyp_pc(struct kvm_vcpu *vcpu)
+{
+       return vcpu->arch.fault.hyp_pc;
+}
+
+static inline bool kvm_vcpu_dabt_isvalid(struct kvm_vcpu *vcpu)
+{
+       return kvm_vcpu_get_hsr(vcpu) & HSR_ISV;
+}
+
+static inline bool kvm_vcpu_dabt_iswrite(struct kvm_vcpu *vcpu)
+{
+       return kvm_vcpu_get_hsr(vcpu) & HSR_WNR;
+}
+
+static inline bool kvm_vcpu_dabt_issext(struct kvm_vcpu *vcpu)
+{
+       return kvm_vcpu_get_hsr(vcpu) & HSR_SSE;
+}
+
+static inline int kvm_vcpu_dabt_get_rd(struct kvm_vcpu *vcpu)
+{
+       return (kvm_vcpu_get_hsr(vcpu) & HSR_SRT_MASK) >> HSR_SRT_SHIFT;
+}
+
+static inline bool kvm_vcpu_dabt_isextabt(struct kvm_vcpu *vcpu)
+{
+       return kvm_vcpu_get_hsr(vcpu) & HSR_DABT_EA;
+}
+
+static inline bool kvm_vcpu_dabt_iss1tw(struct kvm_vcpu *vcpu)
+{
+       return kvm_vcpu_get_hsr(vcpu) & HSR_DABT_S1PTW;
+}
+
+/* Get Access Size from a data abort */
+static inline int kvm_vcpu_dabt_get_as(struct kvm_vcpu *vcpu)
+{
+       switch ((kvm_vcpu_get_hsr(vcpu) >> 22) & 0x3) {
+       case 0:
+               return 1;
+       case 1:
+               return 2;
+       case 2:
+               return 4;
+       default:
+               kvm_err("Hardware is weird: SAS 0b11 is reserved\n");
+               return -EFAULT;
+       }
+}
+
+/* This one is not specific to Data Abort */
+static inline bool kvm_vcpu_trap_il_is32bit(struct kvm_vcpu *vcpu)
+{
+       return kvm_vcpu_get_hsr(vcpu) & HSR_IL;
+}
+
+static inline u8 kvm_vcpu_trap_get_class(struct kvm_vcpu *vcpu)
+{
+       return kvm_vcpu_get_hsr(vcpu) >> HSR_EC_SHIFT;
+}
+
+static inline bool kvm_vcpu_trap_is_iabt(struct kvm_vcpu *vcpu)
+{
+       return kvm_vcpu_trap_get_class(vcpu) == HSR_EC_IABT;
+}
+
+static inline u8 kvm_vcpu_trap_get_fault(struct kvm_vcpu *vcpu)
+{
+       return kvm_vcpu_get_hsr(vcpu) & HSR_FSC_TYPE;
+}
+
+static inline u32 kvm_vcpu_hvc_get_imm(struct kvm_vcpu *vcpu)
+{
+       return kvm_vcpu_get_hsr(vcpu) & HSR_HVC_IMM_MASK;
+}
+
 #endif /* __ARM_KVM_EMULATE_H__ */
index d1736a53b12d423560e402adc373d79e834f782f..0c4e643d939ea12be15ad3a63254e25ff6c4be76 100644 (file)
@@ -80,6 +80,15 @@ struct kvm_mmu_memory_cache {
        void *objects[KVM_NR_MEM_OBJS];
 };
 
+struct kvm_vcpu_fault_info {
+       u32 hsr;                /* Hyp Syndrome Register */
+       u32 hxfar;              /* Hyp Data/Inst. Fault Address Register */
+       u32 hpfar;              /* Hyp IPA Fault Address Register */
+       u32 hyp_pc;             /* PC when exception was taken from Hyp mode */
+};
+
+typedef struct vfp_hard_struct kvm_kernel_vfp_t;
+
 struct kvm_vcpu_arch {
        struct kvm_regs regs;
 
@@ -93,13 +102,11 @@ struct kvm_vcpu_arch {
        u32 midr;
 
        /* Exception Information */
-       u32 hsr;                /* Hyp Syndrome Register */
-       u32 hxfar;              /* Hyp Data/Inst Fault Address Register */
-       u32 hpfar;              /* Hyp IPA Fault Address Register */
+       struct kvm_vcpu_fault_info fault;
 
        /* Floating point registers (VFP and Advanced SIMD/NEON) */
-       struct vfp_hard_struct vfp_guest;
-       struct vfp_hard_struct *vfp_host;
+       kvm_kernel_vfp_t vfp_guest;
+       kvm_kernel_vfp_t *vfp_host;
 
        /* VGIC state */
        struct vgic_cpu vgic_cpu;
@@ -122,9 +129,6 @@ struct kvm_vcpu_arch {
        /* Interrupt related fields */
        u32 irq_lines;          /* IRQ and FIQ levels */
 
-       /* Hyp exception information */
-       u32 hyp_pc;             /* PC when exception was taken from Hyp mode */
-
        /* Cache some mmu pages needed inside spinlock regions */
        struct kvm_mmu_memory_cache mmu_page_cache;
 
@@ -181,4 +185,26 @@ struct kvm_one_reg;
 int kvm_arm_coproc_get_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *);
 int kvm_arm_coproc_set_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *);
 
+int handle_exit(struct kvm_vcpu *vcpu, struct kvm_run *run,
+               int exception_index);
+
+static inline void __cpu_init_hyp_mode(unsigned long long pgd_ptr,
+                                      unsigned long hyp_stack_ptr,
+                                      unsigned long vector_ptr)
+{
+       unsigned long pgd_low, pgd_high;
+
+       pgd_low = (pgd_ptr & ((1ULL << 32) - 1));
+       pgd_high = (pgd_ptr >> 32ULL);
+
+       /*
+        * Call initialization code, and switch to the full blown
+        * HYP code. The init code doesn't need to preserve these registers as
+        * r1-r3 and r12 are already callee save according to the AAPCS.
+        * Note that we slightly misuse the prototype by casing the pgd_low to
+        * a void *.
+        */
+       kvm_call_hyp((void *)pgd_low, pgd_high, hyp_stack_ptr, vector_ptr);
+}
+
 #endif /* __ARM_KVM_HOST_H__ */
index 421a20b34874384dcba6190d633adaef2afa8f4d..970f3b5fa109492ed2c46de8c9239bdfb1e5872c 100644 (file)
 #ifndef __ARM_KVM_MMU_H__
 #define __ARM_KVM_MMU_H__
 
+#include <asm/cacheflush.h>
+#include <asm/pgalloc.h>
+#include <asm/idmap.h>
+
+/*
+ * We directly use the kernel VA for the HYP, as we can directly share
+ * the mapping (HTTBR "covers" TTBR1).
+ */
+#define HYP_PAGE_OFFSET_MASK   (~0UL)
+#define HYP_PAGE_OFFSET                PAGE_OFFSET
+#define KERN_TO_HYP(kva)       (kva)
+
 int create_hyp_mappings(void *from, void *to);
 int create_hyp_io_mappings(void *from, void *to, phys_addr_t);
 void free_hyp_pmds(void);
@@ -36,6 +48,16 @@ phys_addr_t kvm_mmu_get_httbr(void);
 int kvm_mmu_init(void);
 void kvm_clear_hyp_idmap(void);
 
+static inline void kvm_set_pte(pte_t *pte, pte_t new_pte)
+{
+       pte_val(*pte) = new_pte;
+       /*
+        * flush_pmd_entry just takes a void pointer and cleans the necessary
+        * cache entries, so we can reuse the function for ptes.
+        */
+       flush_pmd_entry(pte);
+}
+
 static inline bool kvm_is_write_fault(unsigned long hsr)
 {
        unsigned long hsr_ec = hsr >> HSR_EC_SHIFT;
@@ -47,4 +69,49 @@ static inline bool kvm_is_write_fault(unsigned long hsr)
                return true;
 }
 
+static inline void kvm_clean_pgd(pgd_t *pgd)
+{
+       clean_dcache_area(pgd, PTRS_PER_S2_PGD * sizeof(pgd_t));
+}
+
+static inline void kvm_clean_pmd_entry(pmd_t *pmd)
+{
+       clean_pmd_entry(pmd);
+}
+
+static inline void kvm_clean_pte(pte_t *pte)
+{
+       clean_pte_table(pte);
+}
+
+static inline void kvm_set_s2pte_writable(pte_t *pte)
+{
+       pte_val(*pte) |= L_PTE_S2_RDWR;
+}
+
+struct kvm;
+
+static inline void coherent_icache_guest_page(struct kvm *kvm, gfn_t gfn)
+{
+       /*
+        * If we are going to insert an instruction page and the icache is
+        * either VIPT or PIPT, there is a potential problem where the host
+        * (or another VM) may have used the same page as this guest, and we
+        * read incorrect data from the icache.  If we're using a PIPT cache,
+        * we can invalidate just that page, but if we are using a VIPT cache
+        * we need to invalidate the entire icache - damn shame - as written
+        * in the ARM ARM (DDI 0406C.b - Page B3-1393).
+        *
+        * VIVT caches are tagged using both the ASID and the VMID and doesn't
+        * need any kind of flushing (DDI 0406C.b - Page B3-1392).
+        */
+       if (icache_is_pipt()) {
+               unsigned long hva = gfn_to_hva(kvm, gfn);
+               __cpuc_coherent_user_range(hva, hva + PAGE_SIZE);
+       } else if (!icache_is_vivt_asid_tagged()) {
+               /* any kind of VIPT cache */
+               __flush_icache_all();
+       }
+}
+
 #endif /* __ARM_KVM_MMU_H__ */
index ab97207d9cd3b76aa4aabd774dd399da88892997..343744e4809c17f6c93269c1070edfd43c7575ee 100644 (file)
@@ -21,7 +21,6 @@
 
 #include <linux/kernel.h>
 #include <linux/kvm.h>
-#include <linux/kvm_host.h>
 #include <linux/irqreturn.h>
 #include <linux/spinlock.h>
 #include <linux/types.h>
index 023bfeb367bf0066af20ddd4b82b684c1b8df80f..c1ee007523d78dd25b1dd21661af605da4aa7ef3 100644 (file)
 #define KVM_ARM_FIQ_spsr       fiq_regs[7]
 
 struct kvm_regs {
-       struct pt_regs usr_regs;/* R0_usr - R14_usr, PC, CPSR */
-       __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 */
+       struct pt_regs usr_regs;        /* R0_usr - R14_usr, PC, CPSR */
+       unsigned long svc_regs[3];      /* SP_svc, LR_svc, SPSR_svc */
+       unsigned long abt_regs[3];      /* SP_abt, LR_abt, SPSR_abt */
+       unsigned long und_regs[3];      /* SP_und, LR_und, SPSR_und */
+       unsigned long irq_regs[3];      /* SP_irq, LR_irq, SPSR_irq */
+       unsigned long fiq_regs[8];      /* R8_fiq - R14_fiq, SPSR_fiq */
 };
 
 /* Supported Processor Types */
index 923eec7105cff3cbb5f1eccd7e044a25e4cfdbf3..ee1ac39a58f0a50b96862a58e45875ed01c75407 100644 (file)
@@ -165,10 +165,10 @@ int main(void)
   DEFINE(VCPU_PC,              offsetof(struct kvm_vcpu, arch.regs.usr_regs.ARM_pc));
   DEFINE(VCPU_CPSR,            offsetof(struct kvm_vcpu, arch.regs.usr_regs.ARM_cpsr));
   DEFINE(VCPU_IRQ_LINES,       offsetof(struct kvm_vcpu, arch.irq_lines));
-  DEFINE(VCPU_HSR,             offsetof(struct kvm_vcpu, arch.hsr));
-  DEFINE(VCPU_HxFAR,           offsetof(struct kvm_vcpu, arch.hxfar));
-  DEFINE(VCPU_HPFAR,           offsetof(struct kvm_vcpu, arch.hpfar));
-  DEFINE(VCPU_HYP_PC,          offsetof(struct kvm_vcpu, arch.hyp_pc));
+  DEFINE(VCPU_HSR,             offsetof(struct kvm_vcpu, arch.fault.hsr));
+  DEFINE(VCPU_HxFAR,           offsetof(struct kvm_vcpu, arch.fault.hxfar));
+  DEFINE(VCPU_HPFAR,           offsetof(struct kvm_vcpu, arch.fault.hpfar));
+  DEFINE(VCPU_HYP_PC,          offsetof(struct kvm_vcpu, arch.fault.hyp_pc));
 #ifdef CONFIG_KVM_ARM_VGIC
   DEFINE(VCPU_VGIC_CPU,                offsetof(struct kvm_vcpu, arch.vgic_cpu));
   DEFINE(VGIC_CPU_HCR,         offsetof(struct vgic_cpu, vgic_hcr));
index fc96ce6f2357c683497f80344349449c74e3b077..8dc5e76cb789dcf9cf562e31595ddd840979c49e 100644 (file)
@@ -17,7 +17,7 @@ AFLAGS_interrupts.o := -Wa,-march=armv7-a$(plus_virt)
 kvm-arm-y = $(addprefix ../../../virt/kvm/, kvm_main.o coalesced_mmio.o)
 
 obj-y += kvm-arm.o init.o interrupts.o
-obj-y += arm.o guest.o mmu.o emulate.o reset.o
+obj-y += arm.o handle_exit.o guest.o mmu.o emulate.o reset.o
 obj-y += coproc.o coproc_a15.o mmio.o psci.o
 obj-$(CONFIG_KVM_ARM_VGIC) += vgic.o
 obj-$(CONFIG_KVM_ARM_TIMER) += arch_timer.o
index 678596f699f3a437bd51b1545ba0507484b1951c..1497e18a9e2ab2aaa8d296e026ad076883ade1e6 100644 (file)
 #define CREATE_TRACE_POINTS
 #include "trace.h"
 
-#include <asm/unified.h>
 #include <asm/uaccess.h>
 #include <asm/ptrace.h>
 #include <asm/mman.h>
-#include <asm/cputype.h>
 #include <asm/tlbflush.h>
 #include <asm/cacheflush.h>
 #include <asm/virt.h>
 #include <asm/kvm_emulate.h>
 #include <asm/kvm_coproc.h>
 #include <asm/kvm_psci.h>
-#include <asm/opcodes.h>
 
 #ifdef REQUIRES_VIRT
 __asm__(".arch_extension       virt");
 #endif
 
 static DEFINE_PER_CPU(unsigned long, kvm_arm_hyp_stack_page);
-static struct vfp_hard_struct __percpu *kvm_host_vfp_state;
+static kvm_kernel_vfp_t __percpu *kvm_host_vfp_state;
 static unsigned long hyp_default_vectors;
 
 /* Per-CPU variable containing the currently running vcpu. */
@@ -294,22 +291,6 @@ int kvm_cpu_has_pending_timer(struct kvm_vcpu *vcpu)
        return 0;
 }
 
-int __attribute_const__ kvm_target_cpu(void)
-{
-       unsigned long implementor = read_cpuid_implementor();
-       unsigned long part_number = read_cpuid_part_number();
-
-       if (implementor != ARM_CPU_IMP_ARM)
-               return -EINVAL;
-
-       switch (part_number) {
-       case ARM_CPU_PART_CORTEX_A15:
-               return KVM_ARM_TARGET_CORTEX_A15;
-       default:
-               return -EINVAL;
-       }
-}
-
 int kvm_arch_vcpu_init(struct kvm_vcpu *vcpu)
 {
        int ret;
@@ -472,163 +453,6 @@ static void update_vttbr(struct kvm *kvm)
        spin_unlock(&kvm_vmid_lock);
 }
 
-static int handle_svc_hyp(struct kvm_vcpu *vcpu, struct kvm_run *run)
-{
-       /* SVC called from Hyp mode should never get here */
-       kvm_debug("SVC called from Hyp mode shouldn't go here\n");
-       BUG();
-       return -EINVAL; /* Squash warning */
-}
-
-static int handle_hvc(struct kvm_vcpu *vcpu, struct kvm_run *run)
-{
-       trace_kvm_hvc(*vcpu_pc(vcpu), *vcpu_reg(vcpu, 0),
-                     vcpu->arch.hsr & HSR_HVC_IMM_MASK);
-
-       if (kvm_psci_call(vcpu))
-               return 1;
-
-       kvm_inject_undefined(vcpu);
-       return 1;
-}
-
-static int handle_smc(struct kvm_vcpu *vcpu, struct kvm_run *run)
-{
-       if (kvm_psci_call(vcpu))
-               return 1;
-
-       kvm_inject_undefined(vcpu);
-       return 1;
-}
-
-static int handle_pabt_hyp(struct kvm_vcpu *vcpu, struct kvm_run *run)
-{
-       /* The hypervisor should never cause aborts */
-       kvm_err("Prefetch Abort taken from Hyp mode at %#08x (HSR: %#08x)\n",
-               vcpu->arch.hxfar, vcpu->arch.hsr);
-       return -EFAULT;
-}
-
-static int handle_dabt_hyp(struct kvm_vcpu *vcpu, struct kvm_run *run)
-{
-       /* This is either an error in the ws. code or an external abort */
-       kvm_err("Data Abort taken from Hyp mode at %#08x (HSR: %#08x)\n",
-               vcpu->arch.hxfar, vcpu->arch.hsr);
-       return -EFAULT;
-}
-
-typedef int (*exit_handle_fn)(struct kvm_vcpu *, struct kvm_run *);
-static exit_handle_fn arm_exit_handlers[] = {
-       [HSR_EC_WFI]            = kvm_handle_wfi,
-       [HSR_EC_CP15_32]        = kvm_handle_cp15_32,
-       [HSR_EC_CP15_64]        = kvm_handle_cp15_64,
-       [HSR_EC_CP14_MR]        = kvm_handle_cp14_access,
-       [HSR_EC_CP14_LS]        = kvm_handle_cp14_load_store,
-       [HSR_EC_CP14_64]        = kvm_handle_cp14_access,
-       [HSR_EC_CP_0_13]        = kvm_handle_cp_0_13_access,
-       [HSR_EC_CP10_ID]        = kvm_handle_cp10_id,
-       [HSR_EC_SVC_HYP]        = handle_svc_hyp,
-       [HSR_EC_HVC]            = handle_hvc,
-       [HSR_EC_SMC]            = handle_smc,
-       [HSR_EC_IABT]           = kvm_handle_guest_abort,
-       [HSR_EC_IABT_HYP]       = handle_pabt_hyp,
-       [HSR_EC_DABT]           = kvm_handle_guest_abort,
-       [HSR_EC_DABT_HYP]       = handle_dabt_hyp,
-};
-
-/*
- * A conditional instruction is allowed to trap, even though it
- * wouldn't be executed.  So let's re-implement the hardware, in
- * software!
- */
-static bool kvm_condition_valid(struct kvm_vcpu *vcpu)
-{
-       unsigned long cpsr, cond, insn;
-
-       /*
-        * Exception Code 0 can only happen if we set HCR.TGE to 1, to
-        * catch undefined instructions, and then we won't get past
-        * the arm_exit_handlers test anyway.
-        */
-       BUG_ON(((vcpu->arch.hsr & HSR_EC) >> HSR_EC_SHIFT) == 0);
-
-       /* Top two bits non-zero?  Unconditional. */
-       if (vcpu->arch.hsr >> 30)
-               return true;
-
-       cpsr = *vcpu_cpsr(vcpu);
-
-       /* Is condition field valid? */
-       if ((vcpu->arch.hsr & HSR_CV) >> HSR_CV_SHIFT)
-               cond = (vcpu->arch.hsr & HSR_COND) >> HSR_COND_SHIFT;
-       else {
-               /* This can happen in Thumb mode: examine IT state. */
-               unsigned long it;
-
-               it = ((cpsr >> 8) & 0xFC) | ((cpsr >> 25) & 0x3);
-
-               /* it == 0 => unconditional. */
-               if (it == 0)
-                       return true;
-
-               /* The cond for this insn works out as the top 4 bits. */
-               cond = (it >> 4);
-       }
-
-       /* Shift makes it look like an ARM-mode instruction */
-       insn = cond << 28;
-       return arm_check_condition(insn, cpsr) != ARM_OPCODE_CONDTEST_FAIL;
-}
-
-/*
- * Return > 0 to return to guest, < 0 on error, 0 (and set exit_reason) on
- * proper exit to QEMU.
- */
-static int handle_exit(struct kvm_vcpu *vcpu, struct kvm_run *run,
-                      int exception_index)
-{
-       unsigned long hsr_ec;
-
-       switch (exception_index) {
-       case ARM_EXCEPTION_IRQ:
-               return 1;
-       case ARM_EXCEPTION_UNDEFINED:
-               kvm_err("Undefined exception in Hyp mode at: %#08x\n",
-                       vcpu->arch.hyp_pc);
-               BUG();
-               panic("KVM: Hypervisor undefined exception!\n");
-       case ARM_EXCEPTION_DATA_ABORT:
-       case ARM_EXCEPTION_PREF_ABORT:
-       case ARM_EXCEPTION_HVC:
-               hsr_ec = (vcpu->arch.hsr & HSR_EC) >> HSR_EC_SHIFT;
-
-               if (hsr_ec >= ARRAY_SIZE(arm_exit_handlers)
-                   || !arm_exit_handlers[hsr_ec]) {
-                       kvm_err("Unkown exception class: %#08lx, "
-                               "hsr: %#08x\n", hsr_ec,
-                               (unsigned int)vcpu->arch.hsr);
-                       BUG();
-               }
-
-               /*
-                * See ARM ARM B1.14.1: "Hyp traps on instructions
-                * that fail their condition code check"
-                */
-               if (!kvm_condition_valid(vcpu)) {
-                       bool is_wide = vcpu->arch.hsr & HSR_IL;
-                       kvm_skip_instr(vcpu, is_wide);
-                       return 1;
-               }
-
-               return arm_exit_handlers[hsr_ec](vcpu, run);
-       default:
-               kvm_pr_unimpl("Unsupported exception type: %d",
-                             exception_index);
-               run->exit_reason = KVM_EXIT_INTERNAL_ERROR;
-               return 0;
-       }
-}
-
 static int kvm_vcpu_first_run_init(struct kvm_vcpu *vcpu)
 {
        if (likely(vcpu->arch.has_run_once))
@@ -964,7 +788,6 @@ long kvm_arch_vm_ioctl(struct file *filp,
 static void cpu_init_hyp_mode(void *vector)
 {
        unsigned long long pgd_ptr;
-       unsigned long pgd_low, pgd_high;
        unsigned long hyp_stack_ptr;
        unsigned long stack_page;
        unsigned long vector_ptr;
@@ -973,20 +796,11 @@ static void cpu_init_hyp_mode(void *vector)
        __hyp_set_vectors((unsigned long)vector);
 
        pgd_ptr = (unsigned long long)kvm_mmu_get_httbr();
-       pgd_low = (pgd_ptr & ((1ULL << 32) - 1));
-       pgd_high = (pgd_ptr >> 32ULL);
        stack_page = __get_cpu_var(kvm_arm_hyp_stack_page);
        hyp_stack_ptr = stack_page + PAGE_SIZE;
        vector_ptr = (unsigned long)__kvm_hyp_vector;
 
-       /*
-        * Call initialization code, and switch to the full blown
-        * HYP code. The init code doesn't need to preserve these registers as
-        * r1-r3 and r12 are already callee save according to the AAPCS.
-        * Note that we slightly misuse the prototype by casing the pgd_low to
-        * a void *.
-        */
-       kvm_call_hyp((void *)pgd_low, pgd_high, hyp_stack_ptr, vector_ptr);
+       __cpu_init_hyp_mode(pgd_ptr, hyp_stack_ptr, vector_ptr);
 }
 
 /**
@@ -1069,7 +883,7 @@ static int init_hyp_mode(void)
        /*
         * Map the host VFP structures
         */
-       kvm_host_vfp_state = alloc_percpu(struct vfp_hard_struct);
+       kvm_host_vfp_state = alloc_percpu(kvm_kernel_vfp_t);
        if (!kvm_host_vfp_state) {
                err = -ENOMEM;
                kvm_err("Cannot allocate host VFP state\n");
@@ -1077,7 +891,7 @@ static int init_hyp_mode(void)
        }
 
        for_each_possible_cpu(cpu) {
-               struct vfp_hard_struct *vfp;
+               kvm_kernel_vfp_t *vfp;
 
                vfp = per_cpu_ptr(kvm_host_vfp_state, cpu);
                err = create_hyp_mappings(vfp, vfp + 1);
index 4ea9a982269c8f9c9c56808f92b464a025356533..94eee8befc83dec09d5a478397c82c65d719139f 100644 (file)
@@ -76,7 +76,7 @@ static bool access_dcsw(struct kvm_vcpu *vcpu,
                        const struct coproc_params *p,
                        const struct coproc_reg *r)
 {
-       u32 val;
+       unsigned long val;
        int cpu;
 
        cpu = get_cpu();
@@ -293,12 +293,12 @@ static int emulate_cp15(struct kvm_vcpu *vcpu,
 
                if (likely(r->access(vcpu, params, r))) {
                        /* Skip instruction, since it was emulated */
-                       kvm_skip_instr(vcpu, (vcpu->arch.hsr >> 25) & 1);
+                       kvm_skip_instr(vcpu, kvm_vcpu_trap_il_is32bit(vcpu));
                        return 1;
                }
                /* If access function fails, it should complain. */
        } else {
-               kvm_err("Unsupported guest CP15 access at: %08x\n",
+               kvm_err("Unsupported guest CP15 access at: %08lx\n",
                        *vcpu_pc(vcpu));
                print_cp_instr(params);
        }
@@ -315,14 +315,14 @@ int kvm_handle_cp15_64(struct kvm_vcpu *vcpu, struct kvm_run *run)
 {
        struct coproc_params params;
 
-       params.CRm = (vcpu->arch.hsr >> 1) & 0xf;
-       params.Rt1 = (vcpu->arch.hsr >> 5) & 0xf;
-       params.is_write = ((vcpu->arch.hsr & 1) == 0);
+       params.CRm = (kvm_vcpu_get_hsr(vcpu) >> 1) & 0xf;
+       params.Rt1 = (kvm_vcpu_get_hsr(vcpu) >> 5) & 0xf;
+       params.is_write = ((kvm_vcpu_get_hsr(vcpu) & 1) == 0);
        params.is_64bit = true;
 
-       params.Op1 = (vcpu->arch.hsr >> 16) & 0xf;
+       params.Op1 = (kvm_vcpu_get_hsr(vcpu) >> 16) & 0xf;
        params.Op2 = 0;
-       params.Rt2 = (vcpu->arch.hsr >> 10) & 0xf;
+       params.Rt2 = (kvm_vcpu_get_hsr(vcpu) >> 10) & 0xf;
        params.CRn = 0;
 
        return emulate_cp15(vcpu, &params);
@@ -347,14 +347,14 @@ int kvm_handle_cp15_32(struct kvm_vcpu *vcpu, struct kvm_run *run)
 {
        struct coproc_params params;
 
-       params.CRm = (vcpu->arch.hsr >> 1) & 0xf;
-       params.Rt1 = (vcpu->arch.hsr >> 5) & 0xf;
-       params.is_write = ((vcpu->arch.hsr & 1) == 0);
+       params.CRm = (kvm_vcpu_get_hsr(vcpu) >> 1) & 0xf;
+       params.Rt1 = (kvm_vcpu_get_hsr(vcpu) >> 5) & 0xf;
+       params.is_write = ((kvm_vcpu_get_hsr(vcpu) & 1) == 0);
        params.is_64bit = false;
 
-       params.CRn = (vcpu->arch.hsr >> 10) & 0xf;
-       params.Op1 = (vcpu->arch.hsr >> 14) & 0x7;
-       params.Op2 = (vcpu->arch.hsr >> 17) & 0x7;
+       params.CRn = (kvm_vcpu_get_hsr(vcpu) >> 10) & 0xf;
+       params.Op1 = (kvm_vcpu_get_hsr(vcpu) >> 14) & 0x7;
+       params.Op2 = (kvm_vcpu_get_hsr(vcpu) >> 17) & 0x7;
        params.Rt2 = 0;
 
        return emulate_cp15(vcpu, &params);
index 992adfafa2ff4eb31c8c0d6e9f22aa2bea681114..b7301d3e479921f4d8983a172c88ec6edababd81 100644 (file)
@@ -84,7 +84,7 @@ static inline bool read_zero(struct kvm_vcpu *vcpu,
 static inline bool write_to_read_only(struct kvm_vcpu *vcpu,
                                      const struct coproc_params *params)
 {
-       kvm_debug("CP15 write to read-only register at: %08x\n",
+       kvm_debug("CP15 write to read-only register at: %08lx\n",
                  *vcpu_pc(vcpu));
        print_cp_instr(params);
        return false;
@@ -93,7 +93,7 @@ static inline bool write_to_read_only(struct kvm_vcpu *vcpu,
 static inline bool read_from_write_only(struct kvm_vcpu *vcpu,
                                        const struct coproc_params *params)
 {
-       kvm_debug("CP15 read to write-only register at: %08x\n",
+       kvm_debug("CP15 read to write-only register at: %08lx\n",
                  *vcpu_pc(vcpu));
        print_cp_instr(params);
        return false;
index d61450ac6665ffa0ed5c94cd10ce9f0298434620..bdede9e7da516a43b5a3d681850727860f0534ab 100644 (file)
@@ -20,6 +20,7 @@
 #include <linux/kvm_host.h>
 #include <asm/kvm_arm.h>
 #include <asm/kvm_emulate.h>
+#include <asm/opcodes.h>
 #include <trace/events/kvm.h>
 
 #include "trace.h"
@@ -109,10 +110,10 @@ static const unsigned long vcpu_reg_offsets[VCPU_NR_MODES][15] = {
  * Return a pointer to the register number valid in the current mode of
  * the virtual CPU.
  */
-u32 *vcpu_reg(struct kvm_vcpu *vcpu, u8 reg_num)
+unsigned long *vcpu_reg(struct kvm_vcpu *vcpu, u8 reg_num)
 {
-       u32 *reg_array = (u32 *)&vcpu->arch.regs;
-       u32 mode = *vcpu_cpsr(vcpu) & MODE_MASK;
+       unsigned long *reg_array = (unsigned long *)&vcpu->arch.regs;
+       unsigned long mode = *vcpu_cpsr(vcpu) & MODE_MASK;
 
        switch (mode) {
        case USR_MODE...SVC_MODE:
@@ -141,9 +142,9 @@ u32 *vcpu_reg(struct kvm_vcpu *vcpu, u8 reg_num)
 /*
  * Return the SPSR for the current mode of the virtual CPU.
  */
-u32 *vcpu_spsr(struct kvm_vcpu *vcpu)
+unsigned long *vcpu_spsr(struct kvm_vcpu *vcpu)
 {
-       u32 mode = *vcpu_cpsr(vcpu) & MODE_MASK;
+       unsigned long mode = *vcpu_cpsr(vcpu) & MODE_MASK;
        switch (mode) {
        case SVC_MODE:
                return &vcpu->arch.regs.KVM_ARM_SVC_spsr;
@@ -160,20 +161,48 @@ u32 *vcpu_spsr(struct kvm_vcpu *vcpu)
        }
 }
 
-/**
- * kvm_handle_wfi - handle a wait-for-interrupts instruction executed by a guest
- * @vcpu:      the vcpu pointer
- * @run:       the kvm_run structure pointer
- *
- * Simply sets the wait_for_interrupts flag on the vcpu structure, which will
- * halt execution of world-switches and schedule other host processes until
- * there is an incoming IRQ or FIQ to the VM.
+/*
+ * A conditional instruction is allowed to trap, even though it
+ * wouldn't be executed.  So let's re-implement the hardware, in
+ * software!
  */
-int kvm_handle_wfi(struct kvm_vcpu *vcpu, struct kvm_run *run)
+bool kvm_condition_valid(struct kvm_vcpu *vcpu)
 {
-       trace_kvm_wfi(*vcpu_pc(vcpu));
-       kvm_vcpu_block(vcpu);
-       return 1;
+       unsigned long cpsr, cond, insn;
+
+       /*
+        * Exception Code 0 can only happen if we set HCR.TGE to 1, to
+        * catch undefined instructions, and then we won't get past
+        * the arm_exit_handlers test anyway.
+        */
+       BUG_ON(!kvm_vcpu_trap_get_class(vcpu));
+
+       /* Top two bits non-zero?  Unconditional. */
+       if (kvm_vcpu_get_hsr(vcpu) >> 30)
+               return true;
+
+       cpsr = *vcpu_cpsr(vcpu);
+
+       /* Is condition field valid? */
+       if ((kvm_vcpu_get_hsr(vcpu) & HSR_CV) >> HSR_CV_SHIFT)
+               cond = (kvm_vcpu_get_hsr(vcpu) & HSR_COND) >> HSR_COND_SHIFT;
+       else {
+               /* This can happen in Thumb mode: examine IT state. */
+               unsigned long it;
+
+               it = ((cpsr >> 8) & 0xFC) | ((cpsr >> 25) & 0x3);
+
+               /* it == 0 => unconditional. */
+               if (it == 0)
+                       return true;
+
+               /* The cond for this insn works out as the top 4 bits. */
+               cond = (it >> 4);
+       }
+
+       /* Shift makes it look like an ARM-mode instruction */
+       insn = cond << 28;
+       return arm_check_condition(insn, cpsr) != ARM_OPCODE_CONDTEST_FAIL;
 }
 
 /**
@@ -257,9 +286,9 @@ static u32 exc_vector_base(struct kvm_vcpu *vcpu)
  */
 void kvm_inject_undefined(struct kvm_vcpu *vcpu)
 {
-       u32 new_lr_value;
-       u32 new_spsr_value;
-       u32 cpsr = *vcpu_cpsr(vcpu);
+       unsigned long new_lr_value;
+       unsigned long new_spsr_value;
+       unsigned long cpsr = *vcpu_cpsr(vcpu);
        u32 sctlr = vcpu->arch.cp15[c1_SCTLR];
        bool is_thumb = (cpsr & PSR_T_BIT);
        u32 vect_offset = 4;
@@ -291,9 +320,9 @@ void kvm_inject_undefined(struct kvm_vcpu *vcpu)
  */
 static void inject_abt(struct kvm_vcpu *vcpu, bool is_pabt, unsigned long addr)
 {
-       u32 new_lr_value;
-       u32 new_spsr_value;
-       u32 cpsr = *vcpu_cpsr(vcpu);
+       unsigned long new_lr_value;
+       unsigned long new_spsr_value;
+       unsigned long cpsr = *vcpu_cpsr(vcpu);
        u32 sctlr = vcpu->arch.cp15[c1_SCTLR];
        bool is_thumb = (cpsr & PSR_T_BIT);
        u32 vect_offset;
index 2339d9609d368867daa5335473b77f031a8f78c3..152d03612181d16d5fef5e1e84da8d2c178fbf58 100644 (file)
@@ -22,6 +22,7 @@
 #include <linux/module.h>
 #include <linux/vmalloc.h>
 #include <linux/fs.h>
+#include <asm/cputype.h>
 #include <asm/uaccess.h>
 #include <asm/kvm.h>
 #include <asm/kvm_asm.h>
@@ -180,6 +181,22 @@ int kvm_arch_vcpu_ioctl_set_sregs(struct kvm_vcpu *vcpu,
        return -EINVAL;
 }
 
+int __attribute_const__ kvm_target_cpu(void)
+{
+       unsigned long implementor = read_cpuid_implementor();
+       unsigned long part_number = read_cpuid_part_number();
+
+       if (implementor != ARM_CPU_IMP_ARM)
+               return -EINVAL;
+
+       switch (part_number) {
+       case ARM_CPU_PART_CORTEX_A15:
+               return KVM_ARM_TARGET_CORTEX_A15;
+       default:
+               return -EINVAL;
+       }
+}
+
 int kvm_vcpu_set_target(struct kvm_vcpu *vcpu,
                        const struct kvm_vcpu_init *init)
 {
diff --git a/arch/arm/kvm/handle_exit.c b/arch/arm/kvm/handle_exit.c
new file mode 100644 (file)
index 0000000..26ad173
--- /dev/null
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2012 - Virtual Open Systems and Columbia University
+ * Author: Christoffer Dall <c.dall@virtualopensystems.com>
+ *
+ * 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/kvm.h>
+#include <linux/kvm_host.h>
+#include <asm/kvm_emulate.h>
+#include <asm/kvm_coproc.h>
+#include <asm/kvm_mmu.h>
+#include <asm/kvm_psci.h>
+#include <trace/events/kvm.h>
+
+#include "trace.h"
+
+#include "trace.h"
+
+typedef int (*exit_handle_fn)(struct kvm_vcpu *, struct kvm_run *);
+
+static int handle_svc_hyp(struct kvm_vcpu *vcpu, struct kvm_run *run)
+{
+       /* SVC called from Hyp mode should never get here */
+       kvm_debug("SVC called from Hyp mode shouldn't go here\n");
+       BUG();
+       return -EINVAL; /* Squash warning */
+}
+
+static int handle_hvc(struct kvm_vcpu *vcpu, struct kvm_run *run)
+{
+       trace_kvm_hvc(*vcpu_pc(vcpu), *vcpu_reg(vcpu, 0),
+                     kvm_vcpu_hvc_get_imm(vcpu));
+
+       if (kvm_psci_call(vcpu))
+               return 1;
+
+       kvm_inject_undefined(vcpu);
+       return 1;
+}
+
+static int handle_smc(struct kvm_vcpu *vcpu, struct kvm_run *run)
+{
+       if (kvm_psci_call(vcpu))
+               return 1;
+
+       kvm_inject_undefined(vcpu);
+       return 1;
+}
+
+static int handle_pabt_hyp(struct kvm_vcpu *vcpu, struct kvm_run *run)
+{
+       /* The hypervisor should never cause aborts */
+       kvm_err("Prefetch Abort taken from Hyp mode at %#08lx (HSR: %#08x)\n",
+               kvm_vcpu_get_hfar(vcpu), kvm_vcpu_get_hsr(vcpu));
+       return -EFAULT;
+}
+
+static int handle_dabt_hyp(struct kvm_vcpu *vcpu, struct kvm_run *run)
+{
+       /* This is either an error in the ws. code or an external abort */
+       kvm_err("Data Abort taken from Hyp mode at %#08lx (HSR: %#08x)\n",
+               kvm_vcpu_get_hfar(vcpu), kvm_vcpu_get_hsr(vcpu));
+       return -EFAULT;
+}
+
+/**
+ * kvm_handle_wfi - handle a wait-for-interrupts instruction executed by a guest
+ * @vcpu:      the vcpu pointer
+ * @run:       the kvm_run structure pointer
+ *
+ * Simply sets the wait_for_interrupts flag on the vcpu structure, which will
+ * halt execution of world-switches and schedule other host processes until
+ * there is an incoming IRQ or FIQ to the VM.
+ */
+static int kvm_handle_wfi(struct kvm_vcpu *vcpu, struct kvm_run *run)
+{
+       trace_kvm_wfi(*vcpu_pc(vcpu));
+       kvm_vcpu_block(vcpu);
+       return 1;
+}
+
+static exit_handle_fn arm_exit_handlers[] = {
+       [HSR_EC_WFI]            = kvm_handle_wfi,
+       [HSR_EC_CP15_32]        = kvm_handle_cp15_32,
+       [HSR_EC_CP15_64]        = kvm_handle_cp15_64,
+       [HSR_EC_CP14_MR]        = kvm_handle_cp14_access,
+       [HSR_EC_CP14_LS]        = kvm_handle_cp14_load_store,
+       [HSR_EC_CP14_64]        = kvm_handle_cp14_access,
+       [HSR_EC_CP_0_13]        = kvm_handle_cp_0_13_access,
+       [HSR_EC_CP10_ID]        = kvm_handle_cp10_id,
+       [HSR_EC_SVC_HYP]        = handle_svc_hyp,
+       [HSR_EC_HVC]            = handle_hvc,
+       [HSR_EC_SMC]            = handle_smc,
+       [HSR_EC_IABT]           = kvm_handle_guest_abort,
+       [HSR_EC_IABT_HYP]       = handle_pabt_hyp,
+       [HSR_EC_DABT]           = kvm_handle_guest_abort,
+       [HSR_EC_DABT_HYP]       = handle_dabt_hyp,
+};
+
+static exit_handle_fn kvm_get_exit_handler(struct kvm_vcpu *vcpu)
+{
+       u8 hsr_ec = kvm_vcpu_trap_get_class(vcpu);
+
+       if (hsr_ec >= ARRAY_SIZE(arm_exit_handlers) ||
+           !arm_exit_handlers[hsr_ec]) {
+               kvm_err("Unkown exception class: hsr: %#08x\n",
+                       (unsigned int)kvm_vcpu_get_hsr(vcpu));
+               BUG();
+       }
+
+       return arm_exit_handlers[hsr_ec];
+}
+
+/*
+ * Return > 0 to return to guest, < 0 on error, 0 (and set exit_reason) on
+ * proper exit to userspace.
+ */
+int handle_exit(struct kvm_vcpu *vcpu, struct kvm_run *run,
+                      int exception_index)
+{
+       exit_handle_fn exit_handler;
+
+       switch (exception_index) {
+       case ARM_EXCEPTION_IRQ:
+               return 1;
+       case ARM_EXCEPTION_UNDEFINED:
+               kvm_err("Undefined exception in Hyp mode at: %#08lx\n",
+                       kvm_vcpu_get_hyp_pc(vcpu));
+               BUG();
+               panic("KVM: Hypervisor undefined exception!\n");
+       case ARM_EXCEPTION_DATA_ABORT:
+       case ARM_EXCEPTION_PREF_ABORT:
+       case ARM_EXCEPTION_HVC:
+               /*
+                * See ARM ARM B1.14.1: "Hyp traps on instructions
+                * that fail their condition code check"
+                */
+               if (!kvm_condition_valid(vcpu)) {
+                       kvm_skip_instr(vcpu, kvm_vcpu_trap_il_is32bit(vcpu));
+                       return 1;
+               }
+
+               exit_handler = kvm_get_exit_handler(vcpu);
+
+               return exit_handler(vcpu, run);
+       default:
+               kvm_pr_unimpl("Unsupported exception type: %d",
+                             exception_index);
+               run->exit_reason = KVM_EXIT_INTERNAL_ERROR;
+               return 0;
+       }
+}
index 8ca87ab0919d8efe6a3d771f293d0a5e6b87dcd2..f7793df62f5834ab60abb19528d2e31f9a756a11 100644 (file)
@@ -35,15 +35,18 @@ __kvm_hyp_code_start:
 /********************************************************************
  * Flush per-VMID TLBs
  *
- * void __kvm_tlb_flush_vmid(struct kvm *kvm);
+ * void __kvm_tlb_flush_vmid_ipa(struct kvm *kvm, phys_addr_t ipa);
  *
  * We rely on the hardware to broadcast the TLB invalidation to all CPUs
  * inside the inner-shareable domain (which is the case for all v7
  * implementations).  If we come across a non-IS SMP implementation, we'll
  * have to use an IPI based mechanism. Until then, we stick to the simple
  * hardware assisted version.
+ *
+ * As v7 does not support flushing per IPA, just nuke the whole TLB
+ * instead, ignoring the ipa value.
  */
-ENTRY(__kvm_tlb_flush_vmid)
+ENTRY(__kvm_tlb_flush_vmid_ipa)
        push    {r2, r3}
 
        add     r0, r0, #KVM_VTTBR
@@ -60,7 +63,7 @@ ENTRY(__kvm_tlb_flush_vmid)
 
        pop     {r2, r3}
        bx      lr
-ENDPROC(__kvm_tlb_flush_vmid)
+ENDPROC(__kvm_tlb_flush_vmid_ipa)
 
 /********************************************************************
  * Flush TLBs and instruction caches of all CPUs inside the inner-shareable
@@ -235,9 +238,9 @@ ENTRY(kvm_call_hyp)
  * instruction is issued since all traps are disabled when running the host
  * kernel as per the Hyp-mode initialization at boot time.
  *
- * HVC instructions cause a trap to the vector page + offset 0x18 (see hyp_hvc
+ * HVC instructions cause a trap to the vector page + offset 0x14 (see hyp_hvc
  * below) when the HVC instruction is called from SVC mode (i.e. a guest or the
- * host kernel) and they cause a trap to the vector page + offset 0xc when HVC
+ * host kernel) and they cause a trap to the vector page + offset 0x8 when HVC
  * instructions are called from within Hyp-mode.
  *
  * Hyp-ABI: Calling HYP-mode functions from host (in SVC mode):
index 98a870ff1a5c51a2d496f2df84cefe06de9a0aac..72a12f2171b26bba2937e3c288f4bd51f1b5540b 100644 (file)
  */
 int kvm_handle_mmio_return(struct kvm_vcpu *vcpu, struct kvm_run *run)
 {
-       __u32 *dest;
+       unsigned long *dest;
        unsigned int len;
        int mask;
 
        if (!run->mmio.is_write) {
                dest = vcpu_reg(vcpu, vcpu->arch.mmio_decode.rt);
-               memset(dest, 0, sizeof(int));
+               *dest = 0;
 
                len = run->mmio.len;
-               if (len > 4)
+               if (len > sizeof(unsigned long))
                        return -EINVAL;
 
                memcpy(dest, run->mmio.data, len);
@@ -50,7 +50,8 @@ int kvm_handle_mmio_return(struct kvm_vcpu *vcpu, struct kvm_run *run)
                trace_kvm_mmio(KVM_TRACE_MMIO_READ, len, run->mmio.phys_addr,
                                *((u64 *)run->mmio.data));
 
-               if (vcpu->arch.mmio_decode.sign_extend && len < 4) {
+               if (vcpu->arch.mmio_decode.sign_extend &&
+                   len < sizeof(unsigned long)) {
                        mask = 1U << ((len * 8) - 1);
                        *dest = (*dest ^ mask) - mask;
                }
@@ -65,40 +66,29 @@ static int decode_hsr(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
        unsigned long rt, len;
        bool is_write, sign_extend;
 
-       if ((vcpu->arch.hsr >> 8) & 1) {
+       if (kvm_vcpu_dabt_isextabt(vcpu)) {
                /* cache operation on I/O addr, tell guest unsupported */
-               kvm_inject_dabt(vcpu, vcpu->arch.hxfar);
+               kvm_inject_dabt(vcpu, kvm_vcpu_get_hfar(vcpu));
                return 1;
        }
 
-       if ((vcpu->arch.hsr >> 7) & 1) {
+       if (kvm_vcpu_dabt_iss1tw(vcpu)) {
                /* page table accesses IO mem: tell guest to fix its TTBR */
-               kvm_inject_dabt(vcpu, vcpu->arch.hxfar);
+               kvm_inject_dabt(vcpu, kvm_vcpu_get_hfar(vcpu));
                return 1;
        }
 
-       switch ((vcpu->arch.hsr >> 22) & 0x3) {
-       case 0:
-               len = 1;
-               break;
-       case 1:
-               len = 2;
-               break;
-       case 2:
-               len = 4;
-               break;
-       default:
-               kvm_err("Hardware is weird: SAS 0b11 is reserved\n");
-               return -EFAULT;
-       }
+       len = kvm_vcpu_dabt_get_as(vcpu);
+       if (unlikely(len < 0))
+               return len;
 
-       is_write = vcpu->arch.hsr & HSR_WNR;
-       sign_extend = vcpu->arch.hsr & HSR_SSE;
-       rt = (vcpu->arch.hsr & HSR_SRT_MASK) >> HSR_SRT_SHIFT;
+       is_write = kvm_vcpu_dabt_iswrite(vcpu);
+       sign_extend = kvm_vcpu_dabt_issext(vcpu);
+       rt = kvm_vcpu_dabt_get_rd(vcpu);
 
        if (kvm_vcpu_reg_is_pc(vcpu, rt)) {
                /* IO memory trying to read/write pc */
-               kvm_inject_pabt(vcpu, vcpu->arch.hxfar);
+               kvm_inject_pabt(vcpu, kvm_vcpu_get_hfar(vcpu));
                return 1;
        }
 
@@ -112,7 +102,7 @@ static int decode_hsr(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
         * The MMIO instruction is emulated and should not be re-executed
         * in the guest.
         */
-       kvm_skip_instr(vcpu, (vcpu->arch.hsr >> 25) & 1);
+       kvm_skip_instr(vcpu, kvm_vcpu_trap_il_is32bit(vcpu));
        return 0;
 }
 
@@ -130,7 +120,7 @@ int io_mem_abort(struct kvm_vcpu *vcpu, struct kvm_run *run,
         * space do its magic.
         */
 
-       if (vcpu->arch.hsr & HSR_ISV) {
+       if (kvm_vcpu_dabt_isvalid(vcpu)) {
                ret = decode_hsr(vcpu, fault_ipa, &mmio);
                if (ret)
                        return ret;
index 99e07c7dd7451fac2dfd4e3e2ea80f6f3c3f236a..2f12e4056408e93c43976bd24d6a4109ac27753b 100644 (file)
@@ -20,7 +20,6 @@
 #include <linux/kvm_host.h>
 #include <linux/io.h>
 #include <trace/events/kvm.h>
-#include <asm/idmap.h>
 #include <asm/pgalloc.h>
 #include <asm/cacheflush.h>
 #include <asm/kvm_arm.h>
@@ -28,8 +27,6 @@
 #include <asm/kvm_mmio.h>
 #include <asm/kvm_asm.h>
 #include <asm/kvm_emulate.h>
-#include <asm/mach/map.h>
-#include <trace/events/kvm.h>
 
 #include "trace.h"
 
@@ -37,19 +34,9 @@ extern char  __hyp_idmap_text_start[], __hyp_idmap_text_end[];
 
 static DEFINE_MUTEX(kvm_hyp_pgd_mutex);
 
-static void kvm_tlb_flush_vmid(struct kvm *kvm)
+static void kvm_tlb_flush_vmid_ipa(struct kvm *kvm, phys_addr_t ipa)
 {
-       kvm_call_hyp(__kvm_tlb_flush_vmid, kvm);
-}
-
-static void kvm_set_pte(pte_t *pte, pte_t new_pte)
-{
-       pte_val(*pte) = new_pte;
-       /*
-        * flush_pmd_entry just takes a void pointer and cleans the necessary
-        * cache entries, so we can reuse the function for ptes.
-        */
-       flush_pmd_entry(pte);
+       kvm_call_hyp(__kvm_tlb_flush_vmid_ipa, kvm, ipa);
 }
 
 static int mmu_topup_memory_cache(struct kvm_mmu_memory_cache *cache,
@@ -98,33 +85,42 @@ static void free_ptes(pmd_t *pmd, unsigned long addr)
        }
 }
 
+static void free_hyp_pgd_entry(unsigned long addr)
+{
+       pgd_t *pgd;
+       pud_t *pud;
+       pmd_t *pmd;
+       unsigned long hyp_addr = KERN_TO_HYP(addr);
+
+       pgd = hyp_pgd + pgd_index(hyp_addr);
+       pud = pud_offset(pgd, hyp_addr);
+
+       if (pud_none(*pud))
+               return;
+       BUG_ON(pud_bad(*pud));
+
+       pmd = pmd_offset(pud, hyp_addr);
+       free_ptes(pmd, addr);
+       pmd_free(NULL, pmd);
+       pud_clear(pud);
+}
+
 /**
  * free_hyp_pmds - free a Hyp-mode level-2 tables and child level-3 tables
  *
  * Assumes this is a page table used strictly in Hyp-mode and therefore contains
- * only mappings in the kernel memory area, which is above PAGE_OFFSET.
+ * either mappings in the kernel memory area (above PAGE_OFFSET), or
+ * device mappings in the vmalloc range (from VMALLOC_START to VMALLOC_END).
  */
 void free_hyp_pmds(void)
 {
-       pgd_t *pgd;
-       pud_t *pud;
-       pmd_t *pmd;
        unsigned long addr;
 
        mutex_lock(&kvm_hyp_pgd_mutex);
-       for (addr = PAGE_OFFSET; addr != 0; addr += PGDIR_SIZE) {
-               pgd = hyp_pgd + pgd_index(addr);
-               pud = pud_offset(pgd, addr);
-
-               if (pud_none(*pud))
-                       continue;
-               BUG_ON(pud_bad(*pud));
-
-               pmd = pmd_offset(pud, addr);
-               free_ptes(pmd, addr);
-               pmd_free(NULL, pmd);
-               pud_clear(pud);
-       }
+       for (addr = PAGE_OFFSET; virt_addr_valid(addr); addr += PGDIR_SIZE)
+               free_hyp_pgd_entry(addr);
+       for (addr = VMALLOC_START; is_vmalloc_addr((void*)addr); addr += PGDIR_SIZE)
+               free_hyp_pgd_entry(addr);
        mutex_unlock(&kvm_hyp_pgd_mutex);
 }
 
@@ -136,7 +132,9 @@ static void create_hyp_pte_mappings(pmd_t *pmd, unsigned long start,
        struct page *page;
 
        for (addr = start & PAGE_MASK; addr < end; addr += PAGE_SIZE) {
-               pte = pte_offset_kernel(pmd, addr);
+               unsigned long hyp_addr = KERN_TO_HYP(addr);
+
+               pte = pte_offset_kernel(pmd, hyp_addr);
                BUG_ON(!virt_addr_valid(addr));
                page = virt_to_page(addr);
                kvm_set_pte(pte, mk_pte(page, PAGE_HYP));
@@ -151,7 +149,9 @@ static void create_hyp_io_pte_mappings(pmd_t *pmd, unsigned long start,
        unsigned long addr;
 
        for (addr = start & PAGE_MASK; addr < end; addr += PAGE_SIZE) {
-               pte = pte_offset_kernel(pmd, addr);
+               unsigned long hyp_addr = KERN_TO_HYP(addr);
+
+               pte = pte_offset_kernel(pmd, hyp_addr);
                BUG_ON(pfn_valid(*pfn_base));
                kvm_set_pte(pte, pfn_pte(*pfn_base, PAGE_HYP_DEVICE));
                (*pfn_base)++;
@@ -166,12 +166,13 @@ static int create_hyp_pmd_mappings(pud_t *pud, unsigned long start,
        unsigned long addr, next;
 
        for (addr = start; addr < end; addr = next) {
-               pmd = pmd_offset(pud, addr);
+               unsigned long hyp_addr = KERN_TO_HYP(addr);
+               pmd = pmd_offset(pud, hyp_addr);
 
                BUG_ON(pmd_sect(*pmd));
 
                if (pmd_none(*pmd)) {
-                       pte = pte_alloc_one_kernel(NULL, addr);
+                       pte = pte_alloc_one_kernel(NULL, hyp_addr);
                        if (!pte) {
                                kvm_err("Cannot allocate Hyp pte\n");
                                return -ENOMEM;
@@ -206,17 +207,23 @@ static int __create_hyp_mappings(void *from, void *to, unsigned long *pfn_base)
        unsigned long addr, next;
        int err = 0;
 
-       BUG_ON(start > end);
-       if (start < PAGE_OFFSET)
+       if (start >= end)
+               return -EINVAL;
+       /* Check for a valid kernel memory mapping */
+       if (!pfn_base && (!virt_addr_valid(from) || !virt_addr_valid(to - 1)))
+               return -EINVAL;
+       /* Check for a valid kernel IO mapping */
+       if (pfn_base && (!is_vmalloc_addr(from) || !is_vmalloc_addr(to - 1)))
                return -EINVAL;
 
        mutex_lock(&kvm_hyp_pgd_mutex);
        for (addr = start; addr < end; addr = next) {
-               pgd = hyp_pgd + pgd_index(addr);
-               pud = pud_offset(pgd, addr);
+               unsigned long hyp_addr = KERN_TO_HYP(addr);
+               pgd = hyp_pgd + pgd_index(hyp_addr);
+               pud = pud_offset(pgd, hyp_addr);
 
                if (pud_none_or_clear_bad(pud)) {
-                       pmd = pmd_alloc_one(NULL, addr);
+                       pmd = pmd_alloc_one(NULL, hyp_addr);
                        if (!pmd) {
                                kvm_err("Cannot allocate Hyp pmd\n");
                                err = -ENOMEM;
@@ -236,12 +243,13 @@ out:
 }
 
 /**
- * create_hyp_mappings - map a kernel virtual address range in Hyp mode
+ * create_hyp_mappings - duplicate a kernel virtual address range in Hyp mode
  * @from:      The virtual kernel start address of the range
  * @to:                The virtual kernel end address of the range (exclusive)
  *
- * The same virtual address as the kernel virtual address is also used in
- * Hyp-mode mapping to the same underlying physical pages.
+ * The same virtual address as the kernel virtual address is also used
+ * in Hyp-mode mapping (modulo HYP_PAGE_OFFSET) to the same underlying
+ * physical pages.
  *
  * Note: Wrapping around zero in the "to" address is not supported.
  */
@@ -251,10 +259,13 @@ int create_hyp_mappings(void *from, void *to)
 }
 
 /**
- * create_hyp_io_mappings - map a physical IO range in Hyp mode
- * @from:      The virtual HYP start address of the range
- * @to:                The virtual HYP end address of the range (exclusive)
+ * create_hyp_io_mappings - duplicate a kernel IO mapping into Hyp mode
+ * @from:      The kernel start VA of the range
+ * @to:                The kernel end VA of the range (exclusive)
  * @addr:      The physical start address which gets mapped
+ *
+ * The resulting HYP VA is the same as the kernel VA, modulo
+ * HYP_PAGE_OFFSET.
  */
 int create_hyp_io_mappings(void *from, void *to, phys_addr_t addr)
 {
@@ -290,7 +301,7 @@ int kvm_alloc_stage2_pgd(struct kvm *kvm)
        VM_BUG_ON((unsigned long)pgd & (S2_PGD_SIZE - 1));
 
        memset(pgd, 0, PTRS_PER_S2_PGD * sizeof(pgd_t));
-       clean_dcache_area(pgd, PTRS_PER_S2_PGD * sizeof(pgd_t));
+       kvm_clean_pgd(pgd);
        kvm->arch.pgd = pgd;
 
        return 0;
@@ -422,22 +433,22 @@ static int stage2_set_pte(struct kvm *kvm, struct kvm_mmu_memory_cache *cache,
                        return 0; /* ignore calls from kvm_set_spte_hva */
                pmd = mmu_memory_cache_alloc(cache);
                pud_populate(NULL, pud, pmd);
-               pmd += pmd_index(addr);
                get_page(virt_to_page(pud));
-       } else
-               pmd = pmd_offset(pud, addr);
+       }
+
+       pmd = pmd_offset(pud, addr);
 
        /* Create 2nd stage page table mapping - Level 2 */
        if (pmd_none(*pmd)) {
                if (!cache)
                        return 0; /* ignore calls from kvm_set_spte_hva */
                pte = mmu_memory_cache_alloc(cache);
-               clean_pte_table(pte);
+               kvm_clean_pte(pte);
                pmd_populate_kernel(NULL, pmd, pte);
-               pte += pte_index(addr);
                get_page(virt_to_page(pmd));
-       } else
-               pte = pte_offset_kernel(pmd, addr);
+       }
+
+       pte = pte_offset_kernel(pmd, addr);
 
        if (iomap && pte_present(*pte))
                return -EFAULT;
@@ -446,7 +457,7 @@ static int stage2_set_pte(struct kvm *kvm, struct kvm_mmu_memory_cache *cache,
        old_pte = *pte;
        kvm_set_pte(pte, *new_pte);
        if (pte_present(old_pte))
-               kvm_tlb_flush_vmid(kvm);
+               kvm_tlb_flush_vmid_ipa(kvm, addr);
        else
                get_page(virt_to_page(pte));
 
@@ -473,7 +484,8 @@ int kvm_phys_addr_ioremap(struct kvm *kvm, phys_addr_t guest_ipa,
        pfn = __phys_to_pfn(pa);
 
        for (addr = guest_ipa; addr < end; addr += PAGE_SIZE) {
-               pte_t pte = pfn_pte(pfn, PAGE_S2_DEVICE | L_PTE_S2_RDWR);
+               pte_t pte = pfn_pte(pfn, PAGE_S2_DEVICE);
+               kvm_set_s2pte_writable(&pte);
 
                ret = mmu_topup_memory_cache(&cache, 2, 2);
                if (ret)
@@ -492,29 +504,6 @@ out:
        return ret;
 }
 
-static void coherent_icache_guest_page(struct kvm *kvm, gfn_t gfn)
-{
-       /*
-        * If we are going to insert an instruction page and the icache is
-        * either VIPT or PIPT, there is a potential problem where the host
-        * (or another VM) may have used the same page as this guest, and we
-        * read incorrect data from the icache.  If we're using a PIPT cache,
-        * we can invalidate just that page, but if we are using a VIPT cache
-        * we need to invalidate the entire icache - damn shame - as written
-        * in the ARM ARM (DDI 0406C.b - Page B3-1393).
-        *
-        * VIVT caches are tagged using both the ASID and the VMID and doesn't
-        * need any kind of flushing (DDI 0406C.b - Page B3-1392).
-        */
-       if (icache_is_pipt()) {
-               unsigned long hva = gfn_to_hva(kvm, gfn);
-               __cpuc_coherent_user_range(hva, hva + PAGE_SIZE);
-       } else if (!icache_is_vivt_asid_tagged()) {
-               /* any kind of VIPT cache */
-               __flush_icache_all();
-       }
-}
-
 static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
                          gfn_t gfn, struct kvm_memory_slot *memslot,
                          unsigned long fault_status)
@@ -526,7 +515,7 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
        unsigned long mmu_seq;
        struct kvm_mmu_memory_cache *memcache = &vcpu->arch.mmu_page_cache;
 
-       write_fault = kvm_is_write_fault(vcpu->arch.hsr);
+       write_fault = kvm_is_write_fault(kvm_vcpu_get_hsr(vcpu));
        if (fault_status == FSC_PERM && !write_fault) {
                kvm_err("Unexpected L2 read permission error\n");
                return -EFAULT;
@@ -560,7 +549,7 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
        if (mmu_notifier_retry(vcpu->kvm, mmu_seq))
                goto out_unlock;
        if (writable) {
-               pte_val(new_pte) |= L_PTE_S2_RDWR;
+               kvm_set_s2pte_writable(&new_pte);
                kvm_set_pfn_dirty(pfn);
        }
        stage2_set_pte(vcpu->kvm, memcache, fault_ipa, &new_pte, false);
@@ -585,7 +574,6 @@ out_unlock:
  */
 int kvm_handle_guest_abort(struct kvm_vcpu *vcpu, struct kvm_run *run)
 {
-       unsigned long hsr_ec;
        unsigned long fault_status;
        phys_addr_t fault_ipa;
        struct kvm_memory_slot *memslot;
@@ -593,18 +581,17 @@ int kvm_handle_guest_abort(struct kvm_vcpu *vcpu, struct kvm_run *run)
        gfn_t gfn;
        int ret, idx;
 
-       hsr_ec = vcpu->arch.hsr >> HSR_EC_SHIFT;
-       is_iabt = (hsr_ec == HSR_EC_IABT);
-       fault_ipa = ((phys_addr_t)vcpu->arch.hpfar & HPFAR_MASK) << 8;
+       is_iabt = kvm_vcpu_trap_is_iabt(vcpu);
+       fault_ipa = kvm_vcpu_get_fault_ipa(vcpu);
 
-       trace_kvm_guest_fault(*vcpu_pc(vcpu), vcpu->arch.hsr,
-                             vcpu->arch.hxfar, fault_ipa);
+       trace_kvm_guest_fault(*vcpu_pc(vcpu), kvm_vcpu_get_hsr(vcpu),
+                             kvm_vcpu_get_hfar(vcpu), fault_ipa);
 
        /* Check the stage-2 fault is trans. fault or write fault */
-       fault_status = (vcpu->arch.hsr & HSR_FSC_TYPE);
+       fault_status = kvm_vcpu_trap_get_fault(vcpu);
        if (fault_status != FSC_FAULT && fault_status != FSC_PERM) {
-               kvm_err("Unsupported fault status: EC=%#lx DFCS=%#lx\n",
-                       hsr_ec, fault_status);
+               kvm_err("Unsupported fault status: EC=%#x DFCS=%#lx\n",
+                       kvm_vcpu_trap_get_class(vcpu), fault_status);
                return -EFAULT;
        }
 
@@ -614,7 +601,7 @@ int kvm_handle_guest_abort(struct kvm_vcpu *vcpu, struct kvm_run *run)
        if (!kvm_is_visible_gfn(vcpu->kvm, gfn)) {
                if (is_iabt) {
                        /* Prefetch Abort on I/O address */
-                       kvm_inject_pabt(vcpu, vcpu->arch.hxfar);
+                       kvm_inject_pabt(vcpu, kvm_vcpu_get_hfar(vcpu));
                        ret = 1;
                        goto out_unlock;
                }
@@ -626,8 +613,13 @@ int kvm_handle_guest_abort(struct kvm_vcpu *vcpu, struct kvm_run *run)
                        goto out_unlock;
                }
 
-               /* Adjust page offset */
-               fault_ipa |= vcpu->arch.hxfar & ~PAGE_MASK;
+               /*
+                * The IPA is reported as [MAX:12], so we need to
+                * complement it with the bottom 12 bits from the
+                * faulting VA. This is always 12 bits, irrespective
+                * of the page size.
+                */
+               fault_ipa |= kvm_vcpu_get_hfar(vcpu) & ((1 << 12) - 1);
                ret = io_mem_abort(vcpu, run, fault_ipa);
                goto out_unlock;
        }
@@ -682,7 +674,7 @@ static void handle_hva_to_gpa(struct kvm *kvm,
 static void kvm_unmap_hva_handler(struct kvm *kvm, gpa_t gpa, void *data)
 {
        unmap_stage2_range(kvm, gpa, PAGE_SIZE);
-       kvm_tlb_flush_vmid(kvm);
+       kvm_tlb_flush_vmid_ipa(kvm, gpa);
 }
 
 int kvm_unmap_hva(struct kvm *kvm, unsigned long hva)
@@ -776,7 +768,7 @@ void kvm_clear_hyp_idmap(void)
                pmd = pmd_offset(pud, addr);
 
                pud_clear(pud);
-               clean_pmd_entry(pmd);
+               kvm_clean_pmd_entry(pmd);
                pmd_free(NULL, (pmd_t *)((unsigned long)pmd & PAGE_MASK));
        } while (pgd++, addr = next, addr < end);
 }
index c9a17316e9fe75f2a8dec4fe3893816d3d0730c9..161d5c15f0f0c5b124d6a931c30284d36f1e52f2 100644 (file)
@@ -1484,7 +1484,7 @@ int kvm_vgic_set_addr(struct kvm *kvm, unsigned long type, u64 addr)
        if (addr & ~KVM_PHYS_MASK)
                return -E2BIG;
 
-       if (addr & ~PAGE_MASK)
+       if (addr & (SZ_4K - 1))
                return -EINVAL;
 
        mutex_lock(&kvm->lock);