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