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