xref: /openbmc/linux/arch/sparc/include/asm/mmu_context_64.h (revision cdd38c5f1ce4398ec58fec95904b75824daab7b5)
1b2441318SGreg Kroah-Hartman /* SPDX-License-Identifier: GPL-2.0 */
2a439fe51SSam Ravnborg #ifndef __SPARC64_MMU_CONTEXT_H
3a439fe51SSam Ravnborg #define __SPARC64_MMU_CONTEXT_H
4a439fe51SSam Ravnborg 
5a439fe51SSam Ravnborg /* Derived heavily from Linus's Alpha/AXP ASN code... */
6a439fe51SSam Ravnborg 
7a439fe51SSam Ravnborg #ifndef __ASSEMBLY__
8a439fe51SSam Ravnborg 
9a439fe51SSam Ravnborg #include <linux/spinlock.h>
10589ee628SIngo Molnar #include <linux/mm_types.h>
1101c3f0a4SGuenter Roeck #include <linux/smp.h>
1274a04967SKhalid Aziz #include <linux/sched.h>
13589ee628SIngo Molnar 
14a439fe51SSam Ravnborg #include <asm/spitfire.h>
1574a04967SKhalid Aziz #include <asm/adi_64.h>
16a439fe51SSam Ravnborg #include <asm-generic/mm_hooks.h>
1701c3f0a4SGuenter Roeck #include <asm/percpu.h>
18a439fe51SSam Ravnborg 
19a439fe51SSam Ravnborg extern spinlock_t ctx_alloc_lock;
20a439fe51SSam Ravnborg extern unsigned long tlb_context_cache;
21a439fe51SSam Ravnborg extern unsigned long mmu_context_bmap[];
22a439fe51SSam Ravnborg 
237a5b4bbfSPavel Tatashin DECLARE_PER_CPU(struct mm_struct *, per_cpu_secondary_mm);
24f05a6865SSam Ravnborg void get_new_mmu_context(struct mm_struct *mm);
25*ca0f34b5SNicholas Piggin 
26*ca0f34b5SNicholas Piggin #define init_new_context init_new_context
27f05a6865SSam Ravnborg int init_new_context(struct task_struct *tsk, struct mm_struct *mm);
28*ca0f34b5SNicholas Piggin #define destroy_context destroy_context
29f05a6865SSam Ravnborg void destroy_context(struct mm_struct *mm);
30a439fe51SSam Ravnborg 
31f05a6865SSam Ravnborg void __tsb_context_switch(unsigned long pgd_pa,
32a439fe51SSam Ravnborg 			  struct tsb_config *tsb_base,
33a439fe51SSam Ravnborg 			  struct tsb_config *tsb_huge,
34fc290a11SRob Gardner 			  unsigned long tsb_descr_pa,
35fc290a11SRob Gardner 			  unsigned long secondary_ctx);
36a439fe51SSam Ravnborg 
tsb_context_switch_ctx(struct mm_struct * mm,unsigned long ctx)37fc290a11SRob Gardner static inline void tsb_context_switch_ctx(struct mm_struct *mm,
38fc290a11SRob Gardner 					  unsigned long ctx)
39a439fe51SSam Ravnborg {
40a439fe51SSam Ravnborg 	__tsb_context_switch(__pa(mm->pgd),
414bbc84ffSMike Kravetz 			     &mm->context.tsb_block[MM_TSB_BASE],
429e695d2eSDavid Miller #if defined(CONFIG_HUGETLB_PAGE) || defined(CONFIG_TRANSPARENT_HUGEPAGE)
434bbc84ffSMike Kravetz 			     (mm->context.tsb_block[MM_TSB_HUGE].tsb ?
444bbc84ffSMike Kravetz 			      &mm->context.tsb_block[MM_TSB_HUGE] :
45a439fe51SSam Ravnborg 			      NULL)
46a439fe51SSam Ravnborg #else
47a439fe51SSam Ravnborg 			     NULL
48a439fe51SSam Ravnborg #endif
49fc290a11SRob Gardner 			     , __pa(&mm->context.tsb_descr[MM_TSB_BASE]),
50fc290a11SRob Gardner 			     ctx);
51a439fe51SSam Ravnborg }
52a439fe51SSam Ravnborg 
53fc290a11SRob Gardner #define tsb_context_switch(X) tsb_context_switch_ctx(X, 0)
54fc290a11SRob Gardner 
55f05a6865SSam Ravnborg void tsb_grow(struct mm_struct *mm,
56f05a6865SSam Ravnborg 	      unsigned long tsb_index,
57f05a6865SSam Ravnborg 	      unsigned long mm_rss);
58a439fe51SSam Ravnborg #ifdef CONFIG_SMP
59f05a6865SSam Ravnborg void smp_tsb_sync(struct mm_struct *mm);
60a439fe51SSam Ravnborg #else
61a439fe51SSam Ravnborg #define smp_tsb_sync(__mm) do { } while (0)
62a439fe51SSam Ravnborg #endif
63a439fe51SSam Ravnborg 
64a439fe51SSam Ravnborg /* Set MMU context in the actual hardware. */
65a439fe51SSam Ravnborg #define load_secondary_context(__mm) \
66a439fe51SSam Ravnborg 	__asm__ __volatile__( \
67a439fe51SSam Ravnborg 	"\n661:	stxa		%0, [%1] %2\n" \
68a439fe51SSam Ravnborg 	"	.section	.sun4v_1insn_patch, \"ax\"\n" \
69a439fe51SSam Ravnborg 	"	.word		661b\n" \
70a439fe51SSam Ravnborg 	"	stxa		%0, [%1] %3\n" \
71a439fe51SSam Ravnborg 	"	.previous\n" \
72a439fe51SSam Ravnborg 	"	flush		%%g6\n" \
73a439fe51SSam Ravnborg 	: /* No outputs */ \
74a439fe51SSam Ravnborg 	: "r" (CTX_HWBITS((__mm)->context)), \
75a439fe51SSam Ravnborg 	  "r" (SECONDARY_CONTEXT), "i" (ASI_DMMU), "i" (ASI_MMU))
76a439fe51SSam Ravnborg 
77f05a6865SSam Ravnborg void __flush_tlb_mm(unsigned long, unsigned long);
78a439fe51SSam Ravnborg 
7907df8418SKirill Tkhai /* Switch the current MM context. */
switch_mm(struct mm_struct * old_mm,struct mm_struct * mm,struct task_struct * tsk)80a439fe51SSam Ravnborg static inline void switch_mm(struct mm_struct *old_mm, struct mm_struct *mm, struct task_struct *tsk)
81a439fe51SSam Ravnborg {
82a439fe51SSam Ravnborg 	unsigned long ctx_valid, flags;
837a5b4bbfSPavel Tatashin 	int cpu = smp_processor_id();
84a439fe51SSam Ravnborg 
857a5b4bbfSPavel Tatashin 	per_cpu(per_cpu_secondary_mm, cpu) = mm;
86a439fe51SSam Ravnborg 	if (unlikely(mm == &init_mm))
87a439fe51SSam Ravnborg 		return;
88a439fe51SSam Ravnborg 
89a439fe51SSam Ravnborg 	spin_lock_irqsave(&mm->context.lock, flags);
90a439fe51SSam Ravnborg 	ctx_valid = CTX_VALID(mm->context);
91a439fe51SSam Ravnborg 	if (!ctx_valid)
92a439fe51SSam Ravnborg 		get_new_mmu_context(mm);
93a439fe51SSam Ravnborg 
94a439fe51SSam Ravnborg 	/* We have to be extremely careful here or else we will miss
95a439fe51SSam Ravnborg 	 * a TSB grow if we switch back and forth between a kernel
96a439fe51SSam Ravnborg 	 * thread and an address space which has it's TSB size increased
97a439fe51SSam Ravnborg 	 * on another processor.
98a439fe51SSam Ravnborg 	 *
99a439fe51SSam Ravnborg 	 * It is possible to play some games in order to optimize the
100a439fe51SSam Ravnborg 	 * switch, but the safest thing to do is to unconditionally
101a439fe51SSam Ravnborg 	 * perform the secondary context load and the TSB context switch.
102a439fe51SSam Ravnborg 	 *
103a439fe51SSam Ravnborg 	 * For reference the bad case is, for address space "A":
104a439fe51SSam Ravnborg 	 *
105a439fe51SSam Ravnborg 	 *		CPU 0			CPU 1
106a439fe51SSam Ravnborg 	 *	run address space A
107a439fe51SSam Ravnborg 	 *	set cpu0's bits in cpu_vm_mask
108a439fe51SSam Ravnborg 	 *	switch to kernel thread, borrow
109a439fe51SSam Ravnborg 	 *	address space A via entry_lazy_tlb
110a439fe51SSam Ravnborg 	 *					run address space A
111a439fe51SSam Ravnborg 	 *					set cpu1's bit in cpu_vm_mask
112a439fe51SSam Ravnborg 	 *					flush_tlb_pending()
113a439fe51SSam Ravnborg 	 *					reset cpu_vm_mask to just cpu1
114a439fe51SSam Ravnborg 	 *					TSB grow
115a439fe51SSam Ravnborg 	 *	run address space A
116a439fe51SSam Ravnborg 	 *	context was valid, so skip
117a439fe51SSam Ravnborg 	 *	TSB context switch
118a439fe51SSam Ravnborg 	 *
119a439fe51SSam Ravnborg 	 * At that point cpu0 continues to use a stale TSB, the one from
120a439fe51SSam Ravnborg 	 * before the TSB grow performed on cpu1.  cpu1 did not cross-call
121a439fe51SSam Ravnborg 	 * cpu0 to update it's TSB because at that point the cpu_vm_mask
122a439fe51SSam Ravnborg 	 * only had cpu1 set in it.
123a439fe51SSam Ravnborg 	 */
124fc290a11SRob Gardner 	tsb_context_switch_ctx(mm, CTX_HWBITS(mm->context));
125a439fe51SSam Ravnborg 
126a439fe51SSam Ravnborg 	/* Any time a processor runs a context on an address space
127a439fe51SSam Ravnborg 	 * for the first time, we must flush that context out of the
128a439fe51SSam Ravnborg 	 * local TLB.
129a439fe51SSam Ravnborg 	 */
13081f1adf0SRusty Russell 	if (!ctx_valid || !cpumask_test_cpu(cpu, mm_cpumask(mm))) {
13181f1adf0SRusty Russell 		cpumask_set_cpu(cpu, mm_cpumask(mm));
132a439fe51SSam Ravnborg 		__flush_tlb_mm(CTX_HWBITS(mm->context),
133a439fe51SSam Ravnborg 			       SECONDARY_CONTEXT);
134a439fe51SSam Ravnborg 	}
135a439fe51SSam Ravnborg 	spin_unlock_irqrestore(&mm->context.lock, flags);
136a439fe51SSam Ravnborg }
137a439fe51SSam Ravnborg 
13814d0334cSPavel Tatashin #define activate_mm(active_mm, mm) switch_mm(active_mm, mm, NULL)
13974a04967SKhalid Aziz 
14074a04967SKhalid Aziz #define  __HAVE_ARCH_START_CONTEXT_SWITCH
arch_start_context_switch(struct task_struct * prev)14174a04967SKhalid Aziz static inline void arch_start_context_switch(struct task_struct *prev)
14274a04967SKhalid Aziz {
14374a04967SKhalid Aziz 	/* Save the current state of MCDPER register for the process
14474a04967SKhalid Aziz 	 * we are switching from
14574a04967SKhalid Aziz 	 */
14674a04967SKhalid Aziz 	if (adi_capable()) {
14774a04967SKhalid Aziz 		register unsigned long tmp_mcdper;
14874a04967SKhalid Aziz 
14974a04967SKhalid Aziz 		__asm__ __volatile__(
15074a04967SKhalid Aziz 			".word 0x83438000\n\t"	/* rd  %mcdper, %g1 */
15174a04967SKhalid Aziz 			"mov %%g1, %0\n\t"
15274a04967SKhalid Aziz 			: "=r" (tmp_mcdper)
15374a04967SKhalid Aziz 			:
15474a04967SKhalid Aziz 			: "g1");
15574a04967SKhalid Aziz 		if (tmp_mcdper)
15674a04967SKhalid Aziz 			set_tsk_thread_flag(prev, TIF_MCDPER);
15774a04967SKhalid Aziz 		else
15874a04967SKhalid Aziz 			clear_tsk_thread_flag(prev, TIF_MCDPER);
15974a04967SKhalid Aziz 	}
16074a04967SKhalid Aziz }
16174a04967SKhalid Aziz 
16274a04967SKhalid Aziz #define finish_arch_post_lock_switch	finish_arch_post_lock_switch
finish_arch_post_lock_switch(void)16374a04967SKhalid Aziz static inline void finish_arch_post_lock_switch(void)
16474a04967SKhalid Aziz {
16574a04967SKhalid Aziz 	/* Restore the state of MCDPER register for the new process
16674a04967SKhalid Aziz 	 * just switched to.
16774a04967SKhalid Aziz 	 */
16874a04967SKhalid Aziz 	if (adi_capable()) {
16974a04967SKhalid Aziz 		register unsigned long tmp_mcdper;
17074a04967SKhalid Aziz 
17174a04967SKhalid Aziz 		tmp_mcdper = test_thread_flag(TIF_MCDPER);
17274a04967SKhalid Aziz 		__asm__ __volatile__(
17374a04967SKhalid Aziz 			"mov %0, %%g1\n\t"
17474a04967SKhalid Aziz 			".word 0x9d800001\n\t"	/* wr %g0, %g1, %mcdper" */
17574a04967SKhalid Aziz 			".word 0xaf902001\n\t"	/* wrpr %g0, 1, %pmcdper */
17674a04967SKhalid Aziz 			:
17774a04967SKhalid Aziz 			: "ir" (tmp_mcdper)
17874a04967SKhalid Aziz 			: "g1");
17974a04967SKhalid Aziz 		if (current && current->mm && current->mm->context.adi) {
18074a04967SKhalid Aziz 			struct pt_regs *regs;
18174a04967SKhalid Aziz 
18274a04967SKhalid Aziz 			regs = task_pt_regs(current);
18374a04967SKhalid Aziz 			regs->tstate |= TSTATE_MCDE;
18474a04967SKhalid Aziz 		}
18574a04967SKhalid Aziz 	}
18674a04967SKhalid Aziz }
18774a04967SKhalid Aziz 
188*ca0f34b5SNicholas Piggin #define mm_untag_mask mm_untag_mask
mm_untag_mask(struct mm_struct * mm)189*ca0f34b5SNicholas Piggin static inline unsigned long mm_untag_mask(struct mm_struct *mm)
190a439fe51SSam Ravnborg {
191a439fe51SSam Ravnborg        return -1UL >> adi_nbits();
192a439fe51SSam Ravnborg }
193 
194 #include <asm-generic/mmu_context.h>
195 
196 #endif /* !(__ASSEMBLY__) */
197 
198 #endif /* !(__SPARC64_MMU_CONTEXT_H) */
199