Loading arch/arm/include/asm/kvm_arm.h +4 −0 Original line number Diff line number Diff line Loading @@ -211,4 +211,8 @@ #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__ */ arch/arm/include/asm/kvm_asm.h +1 −1 Original line number Diff line number Diff line Loading @@ -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 Loading arch/arm/include/asm/kvm_emulate.h +100 −7 Original line number Diff line number Diff line Loading @@ -22,11 +22,12 @@ #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); Loading @@ -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) Loading @@ -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__ */ arch/arm/include/asm/kvm_host.h +34 −8 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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; Loading Loading @@ -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__ */ arch/arm/include/asm/kvm_mmu.h +67 −0 Original line number Diff line number Diff line Loading @@ -19,6 +19,18 @@ #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); Loading @@ -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; Loading @@ -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__ */ Loading
arch/arm/include/asm/kvm_arm.h +4 −0 Original line number Diff line number Diff line Loading @@ -211,4 +211,8 @@ #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__ */
arch/arm/include/asm/kvm_asm.h +1 −1 Original line number Diff line number Diff line Loading @@ -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 Loading
arch/arm/include/asm/kvm_emulate.h +100 −7 Original line number Diff line number Diff line Loading @@ -22,11 +22,12 @@ #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); Loading @@ -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) Loading @@ -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__ */
arch/arm/include/asm/kvm_host.h +34 −8 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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; Loading Loading @@ -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__ */
arch/arm/include/asm/kvm_mmu.h +67 −0 Original line number Diff line number Diff line Loading @@ -19,6 +19,18 @@ #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); Loading @@ -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; Loading @@ -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__ */