162306a36Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 */ 262306a36Sopenharmony_ci#ifndef __M68K_MMU_CONTEXT_H 362306a36Sopenharmony_ci#define __M68K_MMU_CONTEXT_H 462306a36Sopenharmony_ci 562306a36Sopenharmony_ci#include <asm-generic/mm_hooks.h> 662306a36Sopenharmony_ci#include <linux/mm_types.h> 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#ifdef CONFIG_MMU 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#if defined(CONFIG_COLDFIRE) 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <asm/atomic.h> 1362306a36Sopenharmony_ci#include <asm/bitops.h> 1462306a36Sopenharmony_ci#include <asm/mcfmmu.h> 1562306a36Sopenharmony_ci#include <asm/mmu.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#define NO_CONTEXT 256 1862306a36Sopenharmony_ci#define LAST_CONTEXT 255 1962306a36Sopenharmony_ci#define FIRST_CONTEXT 1 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ciextern unsigned long context_map[]; 2262306a36Sopenharmony_ciextern mm_context_t next_mmu_context; 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ciextern atomic_t nr_free_contexts; 2562306a36Sopenharmony_ciextern struct mm_struct *context_mm[LAST_CONTEXT+1]; 2662306a36Sopenharmony_ciextern void steal_context(void); 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_cistatic inline void get_mmu_context(struct mm_struct *mm) 2962306a36Sopenharmony_ci{ 3062306a36Sopenharmony_ci mm_context_t ctx; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci if (mm->context != NO_CONTEXT) 3362306a36Sopenharmony_ci return; 3462306a36Sopenharmony_ci while (arch_atomic_dec_and_test_lt(&nr_free_contexts)) { 3562306a36Sopenharmony_ci atomic_inc(&nr_free_contexts); 3662306a36Sopenharmony_ci steal_context(); 3762306a36Sopenharmony_ci } 3862306a36Sopenharmony_ci ctx = next_mmu_context; 3962306a36Sopenharmony_ci while (test_and_set_bit(ctx, context_map)) { 4062306a36Sopenharmony_ci ctx = find_next_zero_bit(context_map, LAST_CONTEXT+1, ctx); 4162306a36Sopenharmony_ci if (ctx > LAST_CONTEXT) 4262306a36Sopenharmony_ci ctx = 0; 4362306a36Sopenharmony_ci } 4462306a36Sopenharmony_ci next_mmu_context = (ctx + 1) & LAST_CONTEXT; 4562306a36Sopenharmony_ci mm->context = ctx; 4662306a36Sopenharmony_ci context_mm[ctx] = mm; 4762306a36Sopenharmony_ci} 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci/* 5062306a36Sopenharmony_ci * Set up the context for a new address space. 5162306a36Sopenharmony_ci */ 5262306a36Sopenharmony_ci#define init_new_context(tsk, mm) (((mm)->context = NO_CONTEXT), 0) 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci/* 5562306a36Sopenharmony_ci * We're finished using the context for an address space. 5662306a36Sopenharmony_ci */ 5762306a36Sopenharmony_ci#define destroy_context destroy_context 5862306a36Sopenharmony_cistatic inline void destroy_context(struct mm_struct *mm) 5962306a36Sopenharmony_ci{ 6062306a36Sopenharmony_ci if (mm->context != NO_CONTEXT) { 6162306a36Sopenharmony_ci clear_bit(mm->context, context_map); 6262306a36Sopenharmony_ci mm->context = NO_CONTEXT; 6362306a36Sopenharmony_ci atomic_inc(&nr_free_contexts); 6462306a36Sopenharmony_ci } 6562306a36Sopenharmony_ci} 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_cistatic inline void set_context(mm_context_t context, pgd_t *pgd) 6862306a36Sopenharmony_ci{ 6962306a36Sopenharmony_ci __asm__ __volatile__ ("movec %0,%%asid" : : "d" (context)); 7062306a36Sopenharmony_ci} 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_cistatic inline void switch_mm(struct mm_struct *prev, struct mm_struct *next, 7362306a36Sopenharmony_ci struct task_struct *tsk) 7462306a36Sopenharmony_ci{ 7562306a36Sopenharmony_ci get_mmu_context(tsk->mm); 7662306a36Sopenharmony_ci set_context(tsk->mm->context, next->pgd); 7762306a36Sopenharmony_ci} 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci/* 8062306a36Sopenharmony_ci * After we have set current->mm to a new value, this activates 8162306a36Sopenharmony_ci * the context for the new mm so we see the new mappings. 8262306a36Sopenharmony_ci */ 8362306a36Sopenharmony_ci#define activate_mm activate_mm 8462306a36Sopenharmony_cistatic inline void activate_mm(struct mm_struct *active_mm, 8562306a36Sopenharmony_ci struct mm_struct *mm) 8662306a36Sopenharmony_ci{ 8762306a36Sopenharmony_ci get_mmu_context(mm); 8862306a36Sopenharmony_ci set_context(mm->context, mm->pgd); 8962306a36Sopenharmony_ci} 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci#define prepare_arch_switch(next) load_ksp_mmu(next) 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_cistatic inline void load_ksp_mmu(struct task_struct *task) 9462306a36Sopenharmony_ci{ 9562306a36Sopenharmony_ci unsigned long flags; 9662306a36Sopenharmony_ci struct mm_struct *mm; 9762306a36Sopenharmony_ci int asid; 9862306a36Sopenharmony_ci pgd_t *pgd; 9962306a36Sopenharmony_ci p4d_t *p4d; 10062306a36Sopenharmony_ci pud_t *pud; 10162306a36Sopenharmony_ci pmd_t *pmd; 10262306a36Sopenharmony_ci pte_t *pte = NULL; 10362306a36Sopenharmony_ci unsigned long mmuar; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci local_irq_save(flags); 10662306a36Sopenharmony_ci mmuar = task->thread.ksp; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci /* Search for a valid TLB entry, if one is found, don't remap */ 10962306a36Sopenharmony_ci mmu_write(MMUAR, mmuar); 11062306a36Sopenharmony_ci mmu_write(MMUOR, MMUOR_STLB | MMUOR_ADR); 11162306a36Sopenharmony_ci if (mmu_read(MMUSR) & MMUSR_HIT) 11262306a36Sopenharmony_ci goto end; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci if (mmuar >= PAGE_OFFSET) { 11562306a36Sopenharmony_ci mm = &init_mm; 11662306a36Sopenharmony_ci } else { 11762306a36Sopenharmony_ci pr_info("load_ksp_mmu: non-kernel mm found: 0x%p\n", task->mm); 11862306a36Sopenharmony_ci mm = task->mm; 11962306a36Sopenharmony_ci } 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci if (!mm) 12262306a36Sopenharmony_ci goto bug; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci pgd = pgd_offset(mm, mmuar); 12562306a36Sopenharmony_ci if (pgd_none(*pgd)) 12662306a36Sopenharmony_ci goto bug; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci p4d = p4d_offset(pgd, mmuar); 12962306a36Sopenharmony_ci if (p4d_none(*p4d)) 13062306a36Sopenharmony_ci goto bug; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci pud = pud_offset(p4d, mmuar); 13362306a36Sopenharmony_ci if (pud_none(*pud)) 13462306a36Sopenharmony_ci goto bug; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci pmd = pmd_offset(pud, mmuar); 13762306a36Sopenharmony_ci if (pmd_none(*pmd)) 13862306a36Sopenharmony_ci goto bug; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci pte = (mmuar >= PAGE_OFFSET) ? pte_offset_kernel(pmd, mmuar) 14162306a36Sopenharmony_ci : pte_offset_map(pmd, mmuar); 14262306a36Sopenharmony_ci if (!pte || pte_none(*pte) || !pte_present(*pte)) 14362306a36Sopenharmony_ci goto bug; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci set_pte(pte, pte_mkyoung(*pte)); 14662306a36Sopenharmony_ci asid = mm->context & 0xff; 14762306a36Sopenharmony_ci if (!pte_dirty(*pte) && mmuar <= PAGE_OFFSET) 14862306a36Sopenharmony_ci set_pte(pte, pte_wrprotect(*pte)); 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci mmu_write(MMUTR, (mmuar & PAGE_MASK) | (asid << MMUTR_IDN) | 15162306a36Sopenharmony_ci (((int)(pte->pte) & (int)CF_PAGE_MMUTR_MASK) 15262306a36Sopenharmony_ci >> CF_PAGE_MMUTR_SHIFT) | MMUTR_V); 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci mmu_write(MMUDR, (pte_val(*pte) & PAGE_MASK) | 15562306a36Sopenharmony_ci ((pte->pte) & CF_PAGE_MMUDR_MASK) | MMUDR_SZ_8KB | MMUDR_X); 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci mmu_write(MMUOR, MMUOR_ACC | MMUOR_UAA); 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci goto end; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_cibug: 16262306a36Sopenharmony_ci pr_info("ksp load failed: mm=0x%p ksp=0x08%lx\n", mm, mmuar); 16362306a36Sopenharmony_ciend: 16462306a36Sopenharmony_ci if (pte && mmuar < PAGE_OFFSET) 16562306a36Sopenharmony_ci pte_unmap(pte); 16662306a36Sopenharmony_ci local_irq_restore(flags); 16762306a36Sopenharmony_ci} 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci#elif defined(CONFIG_SUN3) 17062306a36Sopenharmony_ci#include <asm/sun3mmu.h> 17162306a36Sopenharmony_ci#include <linux/sched.h> 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ciextern unsigned long get_free_context(struct mm_struct *mm); 17462306a36Sopenharmony_ciextern void clear_context(unsigned long context); 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci/* set the context for a new task to unmapped */ 17762306a36Sopenharmony_ci#define init_new_context init_new_context 17862306a36Sopenharmony_cistatic inline int init_new_context(struct task_struct *tsk, 17962306a36Sopenharmony_ci struct mm_struct *mm) 18062306a36Sopenharmony_ci{ 18162306a36Sopenharmony_ci mm->context = SUN3_INVALID_CONTEXT; 18262306a36Sopenharmony_ci return 0; 18362306a36Sopenharmony_ci} 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci/* find the context given to this process, and if it hasn't already 18662306a36Sopenharmony_ci got one, go get one for it. */ 18762306a36Sopenharmony_cistatic inline void get_mmu_context(struct mm_struct *mm) 18862306a36Sopenharmony_ci{ 18962306a36Sopenharmony_ci if (mm->context == SUN3_INVALID_CONTEXT) 19062306a36Sopenharmony_ci mm->context = get_free_context(mm); 19162306a36Sopenharmony_ci} 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci/* flush context if allocated... */ 19462306a36Sopenharmony_ci#define destroy_context destroy_context 19562306a36Sopenharmony_cistatic inline void destroy_context(struct mm_struct *mm) 19662306a36Sopenharmony_ci{ 19762306a36Sopenharmony_ci if (mm->context != SUN3_INVALID_CONTEXT) 19862306a36Sopenharmony_ci clear_context(mm->context); 19962306a36Sopenharmony_ci} 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_cistatic inline void activate_context(struct mm_struct *mm) 20262306a36Sopenharmony_ci{ 20362306a36Sopenharmony_ci get_mmu_context(mm); 20462306a36Sopenharmony_ci sun3_put_context(mm->context); 20562306a36Sopenharmony_ci} 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_cistatic inline void switch_mm(struct mm_struct *prev, struct mm_struct *next, 20862306a36Sopenharmony_ci struct task_struct *tsk) 20962306a36Sopenharmony_ci{ 21062306a36Sopenharmony_ci activate_context(tsk->mm); 21162306a36Sopenharmony_ci} 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci#define activate_mm activate_mm 21462306a36Sopenharmony_cistatic inline void activate_mm(struct mm_struct *prev_mm, 21562306a36Sopenharmony_ci struct mm_struct *next_mm) 21662306a36Sopenharmony_ci{ 21762306a36Sopenharmony_ci activate_context(next_mm); 21862306a36Sopenharmony_ci} 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci#else 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci#include <asm/setup.h> 22362306a36Sopenharmony_ci#include <asm/page.h> 22462306a36Sopenharmony_ci#include <asm/cacheflush.h> 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci#define init_new_context init_new_context 22762306a36Sopenharmony_cistatic inline int init_new_context(struct task_struct *tsk, 22862306a36Sopenharmony_ci struct mm_struct *mm) 22962306a36Sopenharmony_ci{ 23062306a36Sopenharmony_ci mm->context = virt_to_phys(mm->pgd); 23162306a36Sopenharmony_ci return 0; 23262306a36Sopenharmony_ci} 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_cistatic inline void switch_mm_0230(struct mm_struct *mm) 23562306a36Sopenharmony_ci{ 23662306a36Sopenharmony_ci unsigned long crp[2] = { 23762306a36Sopenharmony_ci 0x80000000 | _PAGE_TABLE, mm->context 23862306a36Sopenharmony_ci }; 23962306a36Sopenharmony_ci unsigned long tmp; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci asm volatile (".chip 68030"); 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci /* flush MC68030/MC68020 caches (they are virtually addressed) */ 24462306a36Sopenharmony_ci asm volatile ( 24562306a36Sopenharmony_ci "movec %%cacr,%0;" 24662306a36Sopenharmony_ci "orw %1,%0; " 24762306a36Sopenharmony_ci "movec %0,%%cacr" 24862306a36Sopenharmony_ci : "=d" (tmp) : "di" (FLUSH_I_AND_D)); 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci /* Switch the root pointer. For a 030-only kernel, 25162306a36Sopenharmony_ci * avoid flushing the whole ATC, we only need to 25262306a36Sopenharmony_ci * flush the user entries. The 68851 does this by 25362306a36Sopenharmony_ci * itself. Avoid a runtime check here. 25462306a36Sopenharmony_ci */ 25562306a36Sopenharmony_ci asm volatile ( 25662306a36Sopenharmony_ci#ifdef CPU_M68030_ONLY 25762306a36Sopenharmony_ci "pmovefd %0,%%crp; " 25862306a36Sopenharmony_ci "pflush #0,#4" 25962306a36Sopenharmony_ci#else 26062306a36Sopenharmony_ci "pmove %0,%%crp" 26162306a36Sopenharmony_ci#endif 26262306a36Sopenharmony_ci : : "m" (crp[0])); 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci asm volatile (".chip 68k"); 26562306a36Sopenharmony_ci} 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_cistatic inline void switch_mm_0460(struct mm_struct *mm) 26862306a36Sopenharmony_ci{ 26962306a36Sopenharmony_ci asm volatile (".chip 68040"); 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci /* flush address translation cache (user entries) */ 27262306a36Sopenharmony_ci asm volatile ("pflushan"); 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci /* switch the root pointer */ 27562306a36Sopenharmony_ci asm volatile ("movec %0,%%urp" : : "r" (mm->context)); 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci if (CPU_IS_060) { 27862306a36Sopenharmony_ci unsigned long tmp; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci /* clear user entries in the branch cache */ 28162306a36Sopenharmony_ci asm volatile ( 28262306a36Sopenharmony_ci "movec %%cacr,%0; " 28362306a36Sopenharmony_ci "orl %1,%0; " 28462306a36Sopenharmony_ci "movec %0,%%cacr" 28562306a36Sopenharmony_ci : "=d" (tmp): "di" (0x00200000)); 28662306a36Sopenharmony_ci } 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci asm volatile (".chip 68k"); 28962306a36Sopenharmony_ci} 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_cistatic inline void switch_mm(struct mm_struct *prev, struct mm_struct *next, struct task_struct *tsk) 29262306a36Sopenharmony_ci{ 29362306a36Sopenharmony_ci if (prev != next) { 29462306a36Sopenharmony_ci if (CPU_IS_020_OR_030) 29562306a36Sopenharmony_ci switch_mm_0230(next); 29662306a36Sopenharmony_ci else 29762306a36Sopenharmony_ci switch_mm_0460(next); 29862306a36Sopenharmony_ci } 29962306a36Sopenharmony_ci} 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci#define activate_mm activate_mm 30262306a36Sopenharmony_cistatic inline void activate_mm(struct mm_struct *prev_mm, 30362306a36Sopenharmony_ci struct mm_struct *next_mm) 30462306a36Sopenharmony_ci{ 30562306a36Sopenharmony_ci next_mm->context = virt_to_phys(next_mm->pgd); 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci if (CPU_IS_020_OR_030) 30862306a36Sopenharmony_ci switch_mm_0230(next_mm); 30962306a36Sopenharmony_ci else 31062306a36Sopenharmony_ci switch_mm_0460(next_mm); 31162306a36Sopenharmony_ci} 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci#endif 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci#include <asm-generic/mmu_context.h> 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci#else /* !CONFIG_MMU */ 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci#include <asm-generic/nommu_context.h> 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci#endif /* CONFIG_MMU */ 32262306a36Sopenharmony_ci#endif /* __M68K_MMU_CONTEXT_H */ 323