162306a36Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 */ 262306a36Sopenharmony_ci#ifndef __ASM_POWERPC_MMU_CONTEXT_H 362306a36Sopenharmony_ci#define __ASM_POWERPC_MMU_CONTEXT_H 462306a36Sopenharmony_ci#ifdef __KERNEL__ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/kernel.h> 762306a36Sopenharmony_ci#include <linux/mm.h> 862306a36Sopenharmony_ci#include <linux/sched.h> 962306a36Sopenharmony_ci#include <linux/spinlock.h> 1062306a36Sopenharmony_ci#include <asm/mmu.h> 1162306a36Sopenharmony_ci#include <asm/cputable.h> 1262306a36Sopenharmony_ci#include <asm/cputhreads.h> 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci/* 1562306a36Sopenharmony_ci * Most if the context management is out of line 1662306a36Sopenharmony_ci */ 1762306a36Sopenharmony_ci#define init_new_context init_new_context 1862306a36Sopenharmony_ciextern int init_new_context(struct task_struct *tsk, struct mm_struct *mm); 1962306a36Sopenharmony_ci#define destroy_context destroy_context 2062306a36Sopenharmony_ciextern void destroy_context(struct mm_struct *mm); 2162306a36Sopenharmony_ci#ifdef CONFIG_SPAPR_TCE_IOMMU 2262306a36Sopenharmony_cistruct mm_iommu_table_group_mem_t; 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ciextern bool mm_iommu_preregistered(struct mm_struct *mm); 2562306a36Sopenharmony_ciextern long mm_iommu_new(struct mm_struct *mm, 2662306a36Sopenharmony_ci unsigned long ua, unsigned long entries, 2762306a36Sopenharmony_ci struct mm_iommu_table_group_mem_t **pmem); 2862306a36Sopenharmony_ciextern long mm_iommu_newdev(struct mm_struct *mm, unsigned long ua, 2962306a36Sopenharmony_ci unsigned long entries, unsigned long dev_hpa, 3062306a36Sopenharmony_ci struct mm_iommu_table_group_mem_t **pmem); 3162306a36Sopenharmony_ciextern long mm_iommu_put(struct mm_struct *mm, 3262306a36Sopenharmony_ci struct mm_iommu_table_group_mem_t *mem); 3362306a36Sopenharmony_ciextern void mm_iommu_init(struct mm_struct *mm); 3462306a36Sopenharmony_ciextern struct mm_iommu_table_group_mem_t *mm_iommu_lookup(struct mm_struct *mm, 3562306a36Sopenharmony_ci unsigned long ua, unsigned long size); 3662306a36Sopenharmony_ciextern struct mm_iommu_table_group_mem_t *mm_iommu_get(struct mm_struct *mm, 3762306a36Sopenharmony_ci unsigned long ua, unsigned long entries); 3862306a36Sopenharmony_ciextern long mm_iommu_ua_to_hpa(struct mm_iommu_table_group_mem_t *mem, 3962306a36Sopenharmony_ci unsigned long ua, unsigned int pageshift, unsigned long *hpa); 4062306a36Sopenharmony_ciextern bool mm_iommu_is_devmem(struct mm_struct *mm, unsigned long hpa, 4162306a36Sopenharmony_ci unsigned int pageshift, unsigned long *size); 4262306a36Sopenharmony_ciextern long mm_iommu_mapped_inc(struct mm_iommu_table_group_mem_t *mem); 4362306a36Sopenharmony_ciextern void mm_iommu_mapped_dec(struct mm_iommu_table_group_mem_t *mem); 4462306a36Sopenharmony_ci#else 4562306a36Sopenharmony_cistatic inline bool mm_iommu_is_devmem(struct mm_struct *mm, unsigned long hpa, 4662306a36Sopenharmony_ci unsigned int pageshift, unsigned long *size) 4762306a36Sopenharmony_ci{ 4862306a36Sopenharmony_ci return false; 4962306a36Sopenharmony_ci} 5062306a36Sopenharmony_cistatic inline void mm_iommu_init(struct mm_struct *mm) { } 5162306a36Sopenharmony_ci#endif 5262306a36Sopenharmony_ciextern void switch_slb(struct task_struct *tsk, struct mm_struct *mm); 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci#ifdef CONFIG_PPC_BOOK3S_64 5562306a36Sopenharmony_ciextern void radix__switch_mmu_context(struct mm_struct *prev, 5662306a36Sopenharmony_ci struct mm_struct *next); 5762306a36Sopenharmony_cistatic inline void switch_mmu_context(struct mm_struct *prev, 5862306a36Sopenharmony_ci struct mm_struct *next, 5962306a36Sopenharmony_ci struct task_struct *tsk) 6062306a36Sopenharmony_ci{ 6162306a36Sopenharmony_ci if (radix_enabled()) 6262306a36Sopenharmony_ci return radix__switch_mmu_context(prev, next); 6362306a36Sopenharmony_ci return switch_slb(tsk, next); 6462306a36Sopenharmony_ci} 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ciextern int hash__alloc_context_id(void); 6762306a36Sopenharmony_civoid __init hash__reserve_context_id(int id); 6862306a36Sopenharmony_ciextern void __destroy_context(int context_id); 6962306a36Sopenharmony_cistatic inline void mmu_context_init(void) { } 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci#ifdef CONFIG_PPC_64S_HASH_MMU 7262306a36Sopenharmony_cistatic inline int alloc_extended_context(struct mm_struct *mm, 7362306a36Sopenharmony_ci unsigned long ea) 7462306a36Sopenharmony_ci{ 7562306a36Sopenharmony_ci int context_id; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci int index = ea >> MAX_EA_BITS_PER_CONTEXT; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci context_id = hash__alloc_context_id(); 8062306a36Sopenharmony_ci if (context_id < 0) 8162306a36Sopenharmony_ci return context_id; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci VM_WARN_ON(mm->context.extended_id[index]); 8462306a36Sopenharmony_ci mm->context.extended_id[index] = context_id; 8562306a36Sopenharmony_ci return context_id; 8662306a36Sopenharmony_ci} 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_cistatic inline bool need_extra_context(struct mm_struct *mm, unsigned long ea) 8962306a36Sopenharmony_ci{ 9062306a36Sopenharmony_ci int context_id; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci context_id = get_user_context(&mm->context, ea); 9362306a36Sopenharmony_ci if (!context_id) 9462306a36Sopenharmony_ci return true; 9562306a36Sopenharmony_ci return false; 9662306a36Sopenharmony_ci} 9762306a36Sopenharmony_ci#endif 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci#else 10062306a36Sopenharmony_ciextern void switch_mmu_context(struct mm_struct *prev, struct mm_struct *next, 10162306a36Sopenharmony_ci struct task_struct *tsk); 10262306a36Sopenharmony_ciextern unsigned long __init_new_context(void); 10362306a36Sopenharmony_ciextern void __destroy_context(unsigned long context_id); 10462306a36Sopenharmony_ciextern void mmu_context_init(void); 10562306a36Sopenharmony_cistatic inline int alloc_extended_context(struct mm_struct *mm, 10662306a36Sopenharmony_ci unsigned long ea) 10762306a36Sopenharmony_ci{ 10862306a36Sopenharmony_ci /* non book3s_64 should never find this called */ 10962306a36Sopenharmony_ci WARN_ON(1); 11062306a36Sopenharmony_ci return -ENOMEM; 11162306a36Sopenharmony_ci} 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_cistatic inline bool need_extra_context(struct mm_struct *mm, unsigned long ea) 11462306a36Sopenharmony_ci{ 11562306a36Sopenharmony_ci return false; 11662306a36Sopenharmony_ci} 11762306a36Sopenharmony_ci#endif 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ciextern int use_cop(unsigned long acop, struct mm_struct *mm); 12062306a36Sopenharmony_ciextern void drop_cop(unsigned long acop, struct mm_struct *mm); 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci#ifdef CONFIG_PPC_BOOK3S_64 12362306a36Sopenharmony_cistatic inline void inc_mm_active_cpus(struct mm_struct *mm) 12462306a36Sopenharmony_ci{ 12562306a36Sopenharmony_ci atomic_inc(&mm->context.active_cpus); 12662306a36Sopenharmony_ci} 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_cistatic inline void dec_mm_active_cpus(struct mm_struct *mm) 12962306a36Sopenharmony_ci{ 13062306a36Sopenharmony_ci VM_WARN_ON_ONCE(atomic_read(&mm->context.active_cpus) <= 0); 13162306a36Sopenharmony_ci atomic_dec(&mm->context.active_cpus); 13262306a36Sopenharmony_ci} 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_cistatic inline void mm_context_add_copro(struct mm_struct *mm) 13562306a36Sopenharmony_ci{ 13662306a36Sopenharmony_ci /* 13762306a36Sopenharmony_ci * If any copro is in use, increment the active CPU count 13862306a36Sopenharmony_ci * in order to force TLB invalidations to be global as to 13962306a36Sopenharmony_ci * propagate to the Nest MMU. 14062306a36Sopenharmony_ci */ 14162306a36Sopenharmony_ci if (atomic_inc_return(&mm->context.copros) == 1) 14262306a36Sopenharmony_ci inc_mm_active_cpus(mm); 14362306a36Sopenharmony_ci} 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_cistatic inline void mm_context_remove_copro(struct mm_struct *mm) 14662306a36Sopenharmony_ci{ 14762306a36Sopenharmony_ci int c; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci /* 15062306a36Sopenharmony_ci * When removing the last copro, we need to broadcast a global 15162306a36Sopenharmony_ci * flush of the full mm, as the next TLBI may be local and the 15262306a36Sopenharmony_ci * nMMU and/or PSL need to be cleaned up. 15362306a36Sopenharmony_ci * 15462306a36Sopenharmony_ci * Both the 'copros' and 'active_cpus' counts are looked at in 15562306a36Sopenharmony_ci * radix__flush_all_mm() to determine the scope (local/global) 15662306a36Sopenharmony_ci * of the TLBIs, so we need to flush first before decrementing 15762306a36Sopenharmony_ci * 'copros'. If this API is used by several callers for the 15862306a36Sopenharmony_ci * same context, it can lead to over-flushing. It's hopefully 15962306a36Sopenharmony_ci * not common enough to be a problem. 16062306a36Sopenharmony_ci * 16162306a36Sopenharmony_ci * Skip on hash, as we don't know how to do the proper flush 16262306a36Sopenharmony_ci * for the time being. Invalidations will remain global if 16362306a36Sopenharmony_ci * used on hash. Note that we can't drop 'copros' either, as 16462306a36Sopenharmony_ci * it could make some invalidations local with no flush 16562306a36Sopenharmony_ci * in-between. 16662306a36Sopenharmony_ci */ 16762306a36Sopenharmony_ci if (radix_enabled()) { 16862306a36Sopenharmony_ci radix__flush_all_mm(mm); 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci c = atomic_dec_if_positive(&mm->context.copros); 17162306a36Sopenharmony_ci /* Detect imbalance between add and remove */ 17262306a36Sopenharmony_ci WARN_ON(c < 0); 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci if (c == 0) 17562306a36Sopenharmony_ci dec_mm_active_cpus(mm); 17662306a36Sopenharmony_ci } 17762306a36Sopenharmony_ci} 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci/* 18062306a36Sopenharmony_ci * vas_windows counter shows number of open windows in the mm 18162306a36Sopenharmony_ci * context. During context switch, use this counter to clear the 18262306a36Sopenharmony_ci * foreign real address mapping (CP_ABORT) for the thread / process 18362306a36Sopenharmony_ci * that intend to use COPY/PASTE. When a process closes all windows, 18462306a36Sopenharmony_ci * disable CP_ABORT which is expensive to run. 18562306a36Sopenharmony_ci * 18662306a36Sopenharmony_ci * For user context, register a copro so that TLBIs are seen by the 18762306a36Sopenharmony_ci * nest MMU. mm_context_add/remove_vas_window() are used only for user 18862306a36Sopenharmony_ci * space windows. 18962306a36Sopenharmony_ci */ 19062306a36Sopenharmony_cistatic inline void mm_context_add_vas_window(struct mm_struct *mm) 19162306a36Sopenharmony_ci{ 19262306a36Sopenharmony_ci atomic_inc(&mm->context.vas_windows); 19362306a36Sopenharmony_ci mm_context_add_copro(mm); 19462306a36Sopenharmony_ci} 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_cistatic inline void mm_context_remove_vas_window(struct mm_struct *mm) 19762306a36Sopenharmony_ci{ 19862306a36Sopenharmony_ci int v; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci mm_context_remove_copro(mm); 20162306a36Sopenharmony_ci v = atomic_dec_if_positive(&mm->context.vas_windows); 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci /* Detect imbalance between add and remove */ 20462306a36Sopenharmony_ci WARN_ON(v < 0); 20562306a36Sopenharmony_ci} 20662306a36Sopenharmony_ci#else 20762306a36Sopenharmony_cistatic inline void inc_mm_active_cpus(struct mm_struct *mm) { } 20862306a36Sopenharmony_cistatic inline void dec_mm_active_cpus(struct mm_struct *mm) { } 20962306a36Sopenharmony_cistatic inline void mm_context_add_copro(struct mm_struct *mm) { } 21062306a36Sopenharmony_cistatic inline void mm_context_remove_copro(struct mm_struct *mm) { } 21162306a36Sopenharmony_ci#endif 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci#if defined(CONFIG_KVM_BOOK3S_HV_POSSIBLE) && defined(CONFIG_PPC_RADIX_MMU) 21462306a36Sopenharmony_civoid do_h_rpt_invalidate_prt(unsigned long pid, unsigned long lpid, 21562306a36Sopenharmony_ci unsigned long type, unsigned long pg_sizes, 21662306a36Sopenharmony_ci unsigned long start, unsigned long end); 21762306a36Sopenharmony_ci#else 21862306a36Sopenharmony_cistatic inline void do_h_rpt_invalidate_prt(unsigned long pid, 21962306a36Sopenharmony_ci unsigned long lpid, 22062306a36Sopenharmony_ci unsigned long type, 22162306a36Sopenharmony_ci unsigned long pg_sizes, 22262306a36Sopenharmony_ci unsigned long start, 22362306a36Sopenharmony_ci unsigned long end) { } 22462306a36Sopenharmony_ci#endif 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ciextern void switch_mm_irqs_off(struct mm_struct *prev, struct mm_struct *next, 22762306a36Sopenharmony_ci struct task_struct *tsk); 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_cistatic inline void switch_mm(struct mm_struct *prev, struct mm_struct *next, 23062306a36Sopenharmony_ci struct task_struct *tsk) 23162306a36Sopenharmony_ci{ 23262306a36Sopenharmony_ci unsigned long flags; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci local_irq_save(flags); 23562306a36Sopenharmony_ci switch_mm_irqs_off(prev, next, tsk); 23662306a36Sopenharmony_ci local_irq_restore(flags); 23762306a36Sopenharmony_ci} 23862306a36Sopenharmony_ci#define switch_mm_irqs_off switch_mm_irqs_off 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci/* 24162306a36Sopenharmony_ci * After we have set current->mm to a new value, this activates 24262306a36Sopenharmony_ci * the context for the new mm so we see the new mappings. 24362306a36Sopenharmony_ci */ 24462306a36Sopenharmony_ci#define activate_mm activate_mm 24562306a36Sopenharmony_cistatic inline void activate_mm(struct mm_struct *prev, struct mm_struct *next) 24662306a36Sopenharmony_ci{ 24762306a36Sopenharmony_ci switch_mm_irqs_off(prev, next, current); 24862306a36Sopenharmony_ci} 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci/* We don't currently use enter_lazy_tlb() for anything */ 25162306a36Sopenharmony_ci#ifdef CONFIG_PPC_BOOK3E_64 25262306a36Sopenharmony_ci#define enter_lazy_tlb enter_lazy_tlb 25362306a36Sopenharmony_cistatic inline void enter_lazy_tlb(struct mm_struct *mm, 25462306a36Sopenharmony_ci struct task_struct *tsk) 25562306a36Sopenharmony_ci{ 25662306a36Sopenharmony_ci /* 64-bit Book3E keeps track of current PGD in the PACA */ 25762306a36Sopenharmony_ci get_paca()->pgd = NULL; 25862306a36Sopenharmony_ci} 25962306a36Sopenharmony_ci#endif 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ciextern void arch_exit_mmap(struct mm_struct *mm); 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_cistatic inline void arch_unmap(struct mm_struct *mm, 26462306a36Sopenharmony_ci unsigned long start, unsigned long end) 26562306a36Sopenharmony_ci{ 26662306a36Sopenharmony_ci unsigned long vdso_base = (unsigned long)mm->context.vdso; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci if (start <= vdso_base && vdso_base < end) 26962306a36Sopenharmony_ci mm->context.vdso = NULL; 27062306a36Sopenharmony_ci} 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci#ifdef CONFIG_PPC_MEM_KEYS 27362306a36Sopenharmony_cibool arch_vma_access_permitted(struct vm_area_struct *vma, bool write, 27462306a36Sopenharmony_ci bool execute, bool foreign); 27562306a36Sopenharmony_civoid arch_dup_pkeys(struct mm_struct *oldmm, struct mm_struct *mm); 27662306a36Sopenharmony_ci#else /* CONFIG_PPC_MEM_KEYS */ 27762306a36Sopenharmony_cistatic inline bool arch_vma_access_permitted(struct vm_area_struct *vma, 27862306a36Sopenharmony_ci bool write, bool execute, bool foreign) 27962306a36Sopenharmony_ci{ 28062306a36Sopenharmony_ci /* by default, allow everything */ 28162306a36Sopenharmony_ci return true; 28262306a36Sopenharmony_ci} 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci#define pkey_mm_init(mm) 28562306a36Sopenharmony_ci#define arch_dup_pkeys(oldmm, mm) 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_cistatic inline u64 pte_to_hpte_pkey_bits(u64 pteflags, unsigned long flags) 28862306a36Sopenharmony_ci{ 28962306a36Sopenharmony_ci return 0x0UL; 29062306a36Sopenharmony_ci} 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci#endif /* CONFIG_PPC_MEM_KEYS */ 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_cistatic inline int arch_dup_mmap(struct mm_struct *oldmm, 29562306a36Sopenharmony_ci struct mm_struct *mm) 29662306a36Sopenharmony_ci{ 29762306a36Sopenharmony_ci arch_dup_pkeys(oldmm, mm); 29862306a36Sopenharmony_ci return 0; 29962306a36Sopenharmony_ci} 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci#include <asm-generic/mmu_context.h> 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci#endif /* __KERNEL__ */ 30462306a36Sopenharmony_ci#endif /* __ASM_POWERPC_MMU_CONTEXT_H */ 305