#ifndef _ASM_X86_TLBFLUSH_H #define _ASM_X86_TLBFLUSH_H #include #include #include #include #include #ifdef CONFIG_PARAVIRT #include #else #define __flush_tlb() __native_flush_tlb() #define __flush_tlb_global() __native_flush_tlb_global() #define __flush_tlb_single(addr) __native_flush_tlb_single(addr) #endif static inline void __invpcid(unsigned long pcid, unsigned long addr, unsigned long type) { struct { u64 d[2]; } desc = { { pcid, addr } }; /* * The memory clobber is because the whole point is to invalidate * stale TLB entries and, especially if we're flushing global * mappings, we don't want the compiler to reorder any subsequent * memory accesses before the TLB flush. * * The hex opcode is invpcid (%ecx), %eax in 32-bit mode and * invpcid (%rcx), %rax in long mode. */ asm volatile (".byte 0x66, 0x0f, 0x38, 0x82, 0x01" : : "m" (desc), "a" (type), "c" (&desc) : "memory"); } #define INVPCID_TYPE_INDIV_ADDR 0 #define INVPCID_TYPE_SINGLE_CTXT 1 #define INVPCID_TYPE_ALL_INCL_GLOBAL 2 #define INVPCID_TYPE_ALL_NON_GLOBAL 3 /* Flush all mappings for a given pcid and addr, not including globals. */ static inline void invpcid_flush_one(unsigned long pcid, unsigned long addr) { __invpcid(pcid, addr, INVPCID_TYPE_INDIV_ADDR); } /* Flush all mappings for a given PCID, not including globals. */ static inline void invpcid_flush_single_context(unsigned long pcid) { __invpcid(pcid, 0, INVPCID_TYPE_SINGLE_CTXT); } /* Flush all mappings, including globals, for all PCIDs. */ static inline void invpcid_flush_all(void) { __invpcid(0, 0, INVPCID_TYPE_ALL_INCL_GLOBAL); } /* Flush all mappings for all PCIDs except globals. */ static inline void invpcid_flush_all_nonglobals(void) { __invpcid(0, 0, INVPCID_TYPE_ALL_NON_GLOBAL); } #ifdef CONFIG_PAGE_TABLE_ISOLATION /* * RHEL7 Only */ #ifdef CONFIG_EFI /* * Test whether this is the EFI pgd_t CR3 value used for EFI runtime services */ static inline bool is_efi_pgd_cr3(unsigned long cr3) { extern unsigned long efi_pgd_cr3; return cr3 == efi_pgd_cr3; } #else static inline bool is_efi_pgd_cr3(unsigned long cr3) { return false; } #endif static __always_inline void __load_cr3(unsigned long cr3) { if (static_cpu_has(X86_FEATURE_PCID) && kaiser_active()) { unsigned long shadow_cr3; VM_WARN_ON(cr3 & KAISER_SHADOW_PCID_ASID); VM_WARN_ON(cr3 & (1<active_mm) __flush_tlb(); } static inline void flush_tlb_page(struct vm_area_struct *vma, unsigned long addr) { if (vma->vm_mm == current->active_mm) __flush_tlb_one(addr); } static inline void flush_tlb_range(struct vm_area_struct *vma, unsigned long start, unsigned long end) { if (vma->vm_mm == current->active_mm) __flush_tlb(); } static inline void flush_tlb_mm_range(struct mm_struct *mm, unsigned long start, unsigned long end, unsigned long vmflag) { if (mm == current->active_mm) __flush_tlb(); } static inline void native_flush_tlb_others(const struct cpumask *cpumask, struct mm_struct *mm, unsigned long start, unsigned long end) { } static inline void reset_lazy_tlbstate(void) { } static inline void flush_tlb_kernel_range(unsigned long start, unsigned long end) { flush_tlb_all(); } #else /* SMP */ #include #define local_flush_tlb() __flush_tlb() #define flush_tlb_mm(mm) flush_tlb_mm_range(mm, 0UL, TLB_FLUSH_ALL, 0UL) #define flush_tlb_range(vma, start, end) \ flush_tlb_mm_range(vma->vm_mm, start, end, vma->vm_flags) extern void flush_tlb_all(void); extern void flush_tlb_page(struct vm_area_struct *, unsigned long); extern void flush_tlb_mm_range(struct mm_struct *mm, unsigned long start, unsigned long end, unsigned long vmflag); extern void flush_tlb_kernel_range(unsigned long start, unsigned long end); void native_flush_tlb_others(const struct cpumask *cpumask, struct mm_struct *mm, unsigned long start, unsigned long end); #define TLBSTATE_OK 1 #define TLBSTATE_LAZY 2 struct tlb_state { struct mm_struct *active_mm; int state; }; DECLARE_PER_CPU_SHARED_ALIGNED(struct tlb_state, cpu_tlbstate); static inline void reset_lazy_tlbstate(void) { this_cpu_write(cpu_tlbstate.state, 0); this_cpu_write(cpu_tlbstate.active_mm, &init_mm); } #endif /* SMP */ /* Not inlined due to inc_irq_stat not being defined yet */ #define flush_tlb_local() { \ inc_irq_stat(irq_tlb_count); \ local_flush_tlb(); \ } #ifndef CONFIG_PARAVIRT #define flush_tlb_others(mask, mm, start, end) \ native_flush_tlb_others(mask, mm, start, end) #endif #endif /* _ASM_X86_TLBFLUSH_H */