File Index
Files and directories
Sign in
FILE

mmu_context.h

File preview with syntax highlighting.

linux-6.19.14/arch/alpha/include/asm/mmu_context.h C
  1/* SPDX-License-Identifier: GPL-2.0 */
  2#ifndef __ALPHA_MMU_CONTEXT_H
  3#define __ALPHA_MMU_CONTEXT_H
  4
  5/*
  6 * get a new mmu context..
  7 *
  8 * Copyright (C) 1996, Linus Torvalds
  9 */
 10
 11#include <linux/mm_types.h>
 12#include <linux/sched.h>
 13
 14#include <asm/machvec.h>
 15#include <asm/compiler.h>
 16#include <asm-generic/mm_hooks.h>
 17
 18/*
 19 * Force a context reload. This is needed when we change the page
 20 * table pointer or when we update the ASN of the current process.
 21 */
 22
 23/* Don't get into trouble with dueling __EXTERN_INLINEs.  */
 24#ifndef __EXTERN_INLINE
 25#include <asm/io.h>
 26#endif
 27
 28
 29static inline unsigned long
 30__reload_thread(struct pcb_struct *pcb)
 31{
 32	register unsigned long a0 __asm__("$16");
 33	register unsigned long v0 __asm__("$0");
 34
 35	a0 = virt_to_phys(pcb);
 36	__asm__ __volatile__(
 37		"call_pal %2 #__reload_thread"
 38		: "=r"(v0), "=r"(a0)
 39		: "i"(PAL_swpctx), "r"(a0)
 40		: "$1", "$22", "$23", "$24", "$25");
 41
 42	return v0;
 43}
 44
 45
 46/*
 47 * The maximum ASN's the processor supports.  On the EV4 this is 63
 48 * but the PAL-code doesn't actually use this information.  On the
 49 * EV5 this is 127, and EV6 has 255.
 50 *
 51 * On the EV4, the ASNs are more-or-less useless anyway, as they are
 52 * only used as an icache tag, not for TB entries.  On the EV5 and EV6,
 53 * ASN's also validate the TB entries, and thus make a lot more sense.
 54 *
 55 * The EV4 ASN's don't even match the architecture manual, ugh.  And
 56 * I quote: "If a processor implements address space numbers (ASNs),
 57 * and the old PTE has the Address Space Match (ASM) bit clear (ASNs
 58 * in use) and the Valid bit set, then entries can also effectively be
 59 * made coherent by assigning a new, unused ASN to the currently
 60 * running process and not reusing the previous ASN before calling the
 61 * appropriate PALcode routine to invalidate the translation buffer (TB)". 
 62 *
 63 * In short, the EV4 has a "kind of" ASN capability, but it doesn't actually
 64 * work correctly and can thus not be used (explaining the lack of PAL-code
 65 * support).
 66 */
 67#define EV4_MAX_ASN 63
 68#define EV5_MAX_ASN 127
 69#define EV6_MAX_ASN 255
 70
 71#ifdef CONFIG_ALPHA_GENERIC
 72# define MAX_ASN	(alpha_mv.max_asn)
 73#else
 74# if defined(CONFIG_ALPHA_EV56)
 75#  define MAX_ASN	EV5_MAX_ASN
 76# else
 77#  define MAX_ASN	EV6_MAX_ASN
 78# endif
 79#endif
 80
 81/*
 82 * cpu_last_asn(processor):
 83 * 63                                            0
 84 * +-------------+----------------+--------------+
 85 * | asn version | this processor | hardware asn |
 86 * +-------------+----------------+--------------+
 87 */
 88
 89#include <asm/smp.h>
 90#ifdef CONFIG_SMP
 91#define cpu_last_asn(cpuid)	(cpu_data[cpuid].last_asn)
 92#else
 93extern unsigned long last_asn;
 94#define cpu_last_asn(cpuid)	last_asn
 95#endif /* CONFIG_SMP */
 96
 97#define WIDTH_HARDWARE_ASN	8
 98#define ASN_FIRST_VERSION (1UL << WIDTH_HARDWARE_ASN)
 99#define HARDWARE_ASN_MASK ((1UL << WIDTH_HARDWARE_ASN) - 1)
100
101/*
102 * NOTE! The way this is set up, the high bits of the "asn_cache" (and
103 * the "mm->context") are the ASN _version_ code. A version of 0 is
104 * always considered invalid, so to invalidate another process you only
105 * need to do "p->mm->context = 0".
106 *
107 * If we need more ASN's than the processor has, we invalidate the old
108 * user TLB's (tbiap()) and start a new ASN version. That will automatically
109 * force a new asn for any other processes the next time they want to
110 * run.
111 */
112
113#ifndef __EXTERN_INLINE
114#define __EXTERN_INLINE extern inline
115#define __MMU_EXTERN_INLINE
116#endif
117
118extern inline unsigned long
119__get_new_mm_context(struct mm_struct *mm, long cpu)
120{
121	unsigned long asn = cpu_last_asn(cpu);
122	unsigned long next = asn + 1;
123
124	if ((asn & HARDWARE_ASN_MASK) >= MAX_ASN) {
125		tbiap();
126		imb();
127		next = (asn & ~HARDWARE_ASN_MASK) + ASN_FIRST_VERSION;
128	}
129	cpu_last_asn(cpu) = next;
130	return next;
131}
132
133__EXTERN_INLINE void
134ev5_switch_mm(struct mm_struct *prev_mm, struct mm_struct *next_mm,
135	      struct task_struct *next)
136{
137	/* Check if our ASN is of an older version, and thus invalid. */
138	unsigned long asn;
139	unsigned long mmc;
140	long cpu = smp_processor_id();
141
142#ifdef CONFIG_SMP
143	cpu_data[cpu].asn_lock = 1;
144	barrier();
145#endif
146	asn = cpu_last_asn(cpu);
147	mmc = next_mm->context[cpu];
148	if ((mmc ^ asn) & ~HARDWARE_ASN_MASK) {
149		mmc = __get_new_mm_context(next_mm, cpu);
150		next_mm->context[cpu] = mmc;
151	}
152#ifdef CONFIG_SMP
153	else
154		cpu_data[cpu].need_new_asn = 1;
155#endif
156
157	/* Always update the PCB ASN.  Another thread may have allocated
158	   a new mm->context (via flush_tlb_mm) without the ASN serial
159	   number wrapping.  We have no way to detect when this is needed.  */
160	task_thread_info(next)->pcb.asn = mmc & HARDWARE_ASN_MASK;
161}
162
163extern void __load_new_mm_context(struct mm_struct *);
164asmlinkage void do_page_fault(unsigned long address, unsigned long mmcsr,
165			      long cause, struct pt_regs *regs);
166
167#ifdef CONFIG_SMP
168#define check_mmu_context()					\
169do {								\
170	int cpu = smp_processor_id();				\
171	cpu_data[cpu].asn_lock = 0;				\
172	barrier();						\
173	if (cpu_data[cpu].need_new_asn) {			\
174		struct mm_struct * mm = current->active_mm;	\
175		cpu_data[cpu].need_new_asn = 0;			\
176		if (!mm->context[cpu])			\
177			__load_new_mm_context(mm);		\
178	}							\
179} while(0)
180#else
181#define check_mmu_context()  do { } while(0)
182#endif
183
184__EXTERN_INLINE void
185ev5_activate_mm(struct mm_struct *prev_mm, struct mm_struct *next_mm)
186{
187	__load_new_mm_context(next_mm);
188}
189
190#define switch_mm(a,b,c)	ev5_switch_mm((a),(b),(c))
191#define activate_mm(x,y)	ev5_activate_mm((x),(y))
192
193#define init_new_context init_new_context
194static inline int
195init_new_context(struct task_struct *tsk, struct mm_struct *mm)
196{
197	int i;
198
199	for_each_online_cpu(i)
200		mm->context[i] = 0;
201	if (tsk != current)
202		task_thread_info(tsk)->pcb.ptbr
203		  = ((unsigned long)mm->pgd - IDENT_ADDR) >> PAGE_SHIFT;
204	return 0;
205}
206
207#define enter_lazy_tlb enter_lazy_tlb
208static inline void
209enter_lazy_tlb(struct mm_struct *mm, struct task_struct *tsk)
210{
211	task_thread_info(tsk)->pcb.ptbr
212	  = ((unsigned long)mm->pgd - IDENT_ADDR) >> PAGE_SHIFT;
213}
214
215#include <asm-generic/mmu_context.h>
216
217#ifdef __MMU_EXTERN_INLINE
218#undef __EXTERN_INLINE
219#undef __MMU_EXTERN_INLINE
220#endif
221
222#endif /* __ALPHA_MMU_CONTEXT_H */
~/ Read-only file preview.