18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Switch a MMU context. 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public 58c2ecf20Sopenharmony_ci * License. See the file "COPYING" in the main directory of this archive 68c2ecf20Sopenharmony_ci * for more details. 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Copyright (C) 1996, 1997, 1998, 1999 by Ralf Baechle 98c2ecf20Sopenharmony_ci * Copyright (C) 1999 Silicon Graphics, Inc. 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci#ifndef _ASM_MMU_CONTEXT_H 128c2ecf20Sopenharmony_ci#define _ASM_MMU_CONTEXT_H 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <linux/errno.h> 158c2ecf20Sopenharmony_ci#include <linux/sched.h> 168c2ecf20Sopenharmony_ci#include <linux/mm_types.h> 178c2ecf20Sopenharmony_ci#include <linux/smp.h> 188c2ecf20Sopenharmony_ci#include <linux/slab.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include <asm/barrier.h> 218c2ecf20Sopenharmony_ci#include <asm/cacheflush.h> 228c2ecf20Sopenharmony_ci#include <asm/dsemul.h> 238c2ecf20Sopenharmony_ci#include <asm/ginvt.h> 248c2ecf20Sopenharmony_ci#include <asm/hazards.h> 258c2ecf20Sopenharmony_ci#include <asm/tlbflush.h> 268c2ecf20Sopenharmony_ci#include <asm-generic/mm_hooks.h> 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#define htw_set_pwbase(pgd) \ 298c2ecf20Sopenharmony_cido { \ 308c2ecf20Sopenharmony_ci if (cpu_has_htw) { \ 318c2ecf20Sopenharmony_ci write_c0_pwbase(pgd); \ 328c2ecf20Sopenharmony_ci back_to_back_c0_hazard(); \ 338c2ecf20Sopenharmony_ci } \ 348c2ecf20Sopenharmony_ci} while (0) 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ciextern void tlbmiss_handler_setup_pgd(unsigned long); 378c2ecf20Sopenharmony_ciextern char tlbmiss_handler_setup_pgd_end[]; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci/* Note: This is also implemented with uasm in arch/mips/kvm/entry.c */ 408c2ecf20Sopenharmony_ci#define TLBMISS_HANDLER_SETUP_PGD(pgd) \ 418c2ecf20Sopenharmony_cido { \ 428c2ecf20Sopenharmony_ci tlbmiss_handler_setup_pgd((unsigned long)(pgd)); \ 438c2ecf20Sopenharmony_ci htw_set_pwbase((unsigned long)pgd); \ 448c2ecf20Sopenharmony_ci} while (0) 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci#ifdef CONFIG_MIPS_PGD_C0_CONTEXT 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci#define TLBMISS_HANDLER_RESTORE() \ 498c2ecf20Sopenharmony_ci write_c0_xcontext((unsigned long) smp_processor_id() << \ 508c2ecf20Sopenharmony_ci SMP_CPUID_REGSHIFT) 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci#define TLBMISS_HANDLER_SETUP() \ 538c2ecf20Sopenharmony_ci do { \ 548c2ecf20Sopenharmony_ci TLBMISS_HANDLER_SETUP_PGD(swapper_pg_dir); \ 558c2ecf20Sopenharmony_ci TLBMISS_HANDLER_RESTORE(); \ 568c2ecf20Sopenharmony_ci } while (0) 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci#else /* !CONFIG_MIPS_PGD_C0_CONTEXT: using pgd_current*/ 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci/* 618c2ecf20Sopenharmony_ci * For the fast tlb miss handlers, we keep a per cpu array of pointers 628c2ecf20Sopenharmony_ci * to the current pgd for each processor. Also, the proc. id is stuffed 638c2ecf20Sopenharmony_ci * into the context register. 648c2ecf20Sopenharmony_ci */ 658c2ecf20Sopenharmony_ciextern unsigned long pgd_current[]; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci#define TLBMISS_HANDLER_RESTORE() \ 688c2ecf20Sopenharmony_ci write_c0_context((unsigned long) smp_processor_id() << \ 698c2ecf20Sopenharmony_ci SMP_CPUID_REGSHIFT) 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci#define TLBMISS_HANDLER_SETUP() \ 728c2ecf20Sopenharmony_ci TLBMISS_HANDLER_RESTORE(); \ 738c2ecf20Sopenharmony_ci back_to_back_c0_hazard(); \ 748c2ecf20Sopenharmony_ci TLBMISS_HANDLER_SETUP_PGD(swapper_pg_dir) 758c2ecf20Sopenharmony_ci#endif /* CONFIG_MIPS_PGD_C0_CONTEXT*/ 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci/* 788c2ecf20Sopenharmony_ci * The ginvt instruction will invalidate wired entries when its type field 798c2ecf20Sopenharmony_ci * targets anything other than the entire TLB. That means that if we were to 808c2ecf20Sopenharmony_ci * allow the kernel to create wired entries with the MMID of current->active_mm 818c2ecf20Sopenharmony_ci * then those wired entries could be invalidated when we later use ginvt to 828c2ecf20Sopenharmony_ci * invalidate TLB entries with that MMID. 838c2ecf20Sopenharmony_ci * 848c2ecf20Sopenharmony_ci * In order to prevent ginvt from trashing wired entries, we reserve one MMID 858c2ecf20Sopenharmony_ci * for use by the kernel when creating wired entries. This MMID will never be 868c2ecf20Sopenharmony_ci * assigned to a struct mm, and we'll never target it with a ginvt instruction. 878c2ecf20Sopenharmony_ci */ 888c2ecf20Sopenharmony_ci#define MMID_KERNEL_WIRED 0 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci/* 918c2ecf20Sopenharmony_ci * All unused by hardware upper bits will be considered 928c2ecf20Sopenharmony_ci * as a software asid extension. 938c2ecf20Sopenharmony_ci */ 948c2ecf20Sopenharmony_cistatic inline u64 asid_version_mask(unsigned int cpu) 958c2ecf20Sopenharmony_ci{ 968c2ecf20Sopenharmony_ci unsigned long asid_mask = cpu_asid_mask(&cpu_data[cpu]); 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci return ~(u64)(asid_mask | (asid_mask - 1)); 998c2ecf20Sopenharmony_ci} 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_cistatic inline u64 asid_first_version(unsigned int cpu) 1028c2ecf20Sopenharmony_ci{ 1038c2ecf20Sopenharmony_ci return ~asid_version_mask(cpu) + 1; 1048c2ecf20Sopenharmony_ci} 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_cistatic inline u64 cpu_context(unsigned int cpu, const struct mm_struct *mm) 1078c2ecf20Sopenharmony_ci{ 1088c2ecf20Sopenharmony_ci if (cpu_has_mmid) 1098c2ecf20Sopenharmony_ci return atomic64_read(&mm->context.mmid); 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci return mm->context.asid[cpu]; 1128c2ecf20Sopenharmony_ci} 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_cistatic inline void set_cpu_context(unsigned int cpu, 1158c2ecf20Sopenharmony_ci struct mm_struct *mm, u64 ctx) 1168c2ecf20Sopenharmony_ci{ 1178c2ecf20Sopenharmony_ci if (cpu_has_mmid) 1188c2ecf20Sopenharmony_ci atomic64_set(&mm->context.mmid, ctx); 1198c2ecf20Sopenharmony_ci else 1208c2ecf20Sopenharmony_ci mm->context.asid[cpu] = ctx; 1218c2ecf20Sopenharmony_ci} 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci#define asid_cache(cpu) (cpu_data[cpu].asid_cache) 1248c2ecf20Sopenharmony_ci#define cpu_asid(cpu, mm) \ 1258c2ecf20Sopenharmony_ci (cpu_context((cpu), (mm)) & cpu_asid_mask(&cpu_data[cpu])) 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_cistatic inline void enter_lazy_tlb(struct mm_struct *mm, struct task_struct *tsk) 1288c2ecf20Sopenharmony_ci{ 1298c2ecf20Sopenharmony_ci} 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ciextern void get_new_mmu_context(struct mm_struct *mm); 1328c2ecf20Sopenharmony_ciextern void check_mmu_context(struct mm_struct *mm); 1338c2ecf20Sopenharmony_ciextern void check_switch_mmu_context(struct mm_struct *mm); 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci/* 1368c2ecf20Sopenharmony_ci * Initialize the context related info for a new mm_struct 1378c2ecf20Sopenharmony_ci * instance. 1388c2ecf20Sopenharmony_ci */ 1398c2ecf20Sopenharmony_cistatic inline int 1408c2ecf20Sopenharmony_ciinit_new_context(struct task_struct *tsk, struct mm_struct *mm) 1418c2ecf20Sopenharmony_ci{ 1428c2ecf20Sopenharmony_ci int i; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci if (cpu_has_mmid) { 1458c2ecf20Sopenharmony_ci set_cpu_context(0, mm, 0); 1468c2ecf20Sopenharmony_ci } else { 1478c2ecf20Sopenharmony_ci for_each_possible_cpu(i) 1488c2ecf20Sopenharmony_ci set_cpu_context(i, mm, 0); 1498c2ecf20Sopenharmony_ci } 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci mm->context.bd_emupage_allocmap = NULL; 1528c2ecf20Sopenharmony_ci spin_lock_init(&mm->context.bd_emupage_lock); 1538c2ecf20Sopenharmony_ci init_waitqueue_head(&mm->context.bd_emupage_queue); 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci return 0; 1568c2ecf20Sopenharmony_ci} 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_cistatic inline void switch_mm(struct mm_struct *prev, struct mm_struct *next, 1598c2ecf20Sopenharmony_ci struct task_struct *tsk) 1608c2ecf20Sopenharmony_ci{ 1618c2ecf20Sopenharmony_ci unsigned int cpu = smp_processor_id(); 1628c2ecf20Sopenharmony_ci unsigned long flags; 1638c2ecf20Sopenharmony_ci local_irq_save(flags); 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci htw_stop(); 1668c2ecf20Sopenharmony_ci check_switch_mmu_context(next); 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci /* 1698c2ecf20Sopenharmony_ci * Mark current->active_mm as not "active" anymore. 1708c2ecf20Sopenharmony_ci * We don't want to mislead possible IPI tlb flush routines. 1718c2ecf20Sopenharmony_ci */ 1728c2ecf20Sopenharmony_ci cpumask_clear_cpu(cpu, mm_cpumask(prev)); 1738c2ecf20Sopenharmony_ci cpumask_set_cpu(cpu, mm_cpumask(next)); 1748c2ecf20Sopenharmony_ci htw_start(); 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci local_irq_restore(flags); 1778c2ecf20Sopenharmony_ci} 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci/* 1808c2ecf20Sopenharmony_ci * Destroy context related info for an mm_struct that is about 1818c2ecf20Sopenharmony_ci * to be put to rest. 1828c2ecf20Sopenharmony_ci */ 1838c2ecf20Sopenharmony_cistatic inline void destroy_context(struct mm_struct *mm) 1848c2ecf20Sopenharmony_ci{ 1858c2ecf20Sopenharmony_ci dsemul_mm_cleanup(mm); 1868c2ecf20Sopenharmony_ci} 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci#define activate_mm(prev, next) switch_mm(prev, next, current) 1898c2ecf20Sopenharmony_ci#define deactivate_mm(tsk, mm) do { } while (0) 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_cistatic inline void 1928c2ecf20Sopenharmony_cidrop_mmu_context(struct mm_struct *mm) 1938c2ecf20Sopenharmony_ci{ 1948c2ecf20Sopenharmony_ci unsigned long flags; 1958c2ecf20Sopenharmony_ci unsigned int cpu; 1968c2ecf20Sopenharmony_ci u32 old_mmid; 1978c2ecf20Sopenharmony_ci u64 ctx; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci local_irq_save(flags); 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci cpu = smp_processor_id(); 2028c2ecf20Sopenharmony_ci ctx = cpu_context(cpu, mm); 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci if (!ctx) { 2058c2ecf20Sopenharmony_ci /* no-op */ 2068c2ecf20Sopenharmony_ci } else if (cpu_has_mmid) { 2078c2ecf20Sopenharmony_ci /* 2088c2ecf20Sopenharmony_ci * Globally invalidating TLB entries associated with the MMID 2098c2ecf20Sopenharmony_ci * is pretty cheap using the GINVT instruction, so we'll do 2108c2ecf20Sopenharmony_ci * that rather than incur the overhead of allocating a new 2118c2ecf20Sopenharmony_ci * MMID. The latter would be especially difficult since MMIDs 2128c2ecf20Sopenharmony_ci * are global & other CPUs may be actively using ctx. 2138c2ecf20Sopenharmony_ci */ 2148c2ecf20Sopenharmony_ci htw_stop(); 2158c2ecf20Sopenharmony_ci old_mmid = read_c0_memorymapid(); 2168c2ecf20Sopenharmony_ci write_c0_memorymapid(ctx & cpu_asid_mask(&cpu_data[cpu])); 2178c2ecf20Sopenharmony_ci mtc0_tlbw_hazard(); 2188c2ecf20Sopenharmony_ci ginvt_mmid(); 2198c2ecf20Sopenharmony_ci sync_ginv(); 2208c2ecf20Sopenharmony_ci write_c0_memorymapid(old_mmid); 2218c2ecf20Sopenharmony_ci instruction_hazard(); 2228c2ecf20Sopenharmony_ci htw_start(); 2238c2ecf20Sopenharmony_ci } else if (cpumask_test_cpu(cpu, mm_cpumask(mm))) { 2248c2ecf20Sopenharmony_ci /* 2258c2ecf20Sopenharmony_ci * mm is currently active, so we can't really drop it. 2268c2ecf20Sopenharmony_ci * Instead we bump the ASID. 2278c2ecf20Sopenharmony_ci */ 2288c2ecf20Sopenharmony_ci htw_stop(); 2298c2ecf20Sopenharmony_ci get_new_mmu_context(mm); 2308c2ecf20Sopenharmony_ci write_c0_entryhi(cpu_asid(cpu, mm)); 2318c2ecf20Sopenharmony_ci htw_start(); 2328c2ecf20Sopenharmony_ci } else { 2338c2ecf20Sopenharmony_ci /* will get a new context next time */ 2348c2ecf20Sopenharmony_ci set_cpu_context(cpu, mm, 0); 2358c2ecf20Sopenharmony_ci } 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci local_irq_restore(flags); 2388c2ecf20Sopenharmony_ci} 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci#endif /* _ASM_MMU_CONTEXT_H */ 241