18c2ecf20Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 */ 28c2ecf20Sopenharmony_ci#ifndef __ALPHA_MMU_CONTEXT_H 38c2ecf20Sopenharmony_ci#define __ALPHA_MMU_CONTEXT_H 48c2ecf20Sopenharmony_ci 58c2ecf20Sopenharmony_ci/* 68c2ecf20Sopenharmony_ci * get a new mmu context.. 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Copyright (C) 1996, Linus Torvalds 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/mm_types.h> 128c2ecf20Sopenharmony_ci#include <linux/sched.h> 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <asm/machvec.h> 158c2ecf20Sopenharmony_ci#include <asm/compiler.h> 168c2ecf20Sopenharmony_ci#include <asm-generic/mm_hooks.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci/* 198c2ecf20Sopenharmony_ci * Force a context reload. This is needed when we change the page 208c2ecf20Sopenharmony_ci * table pointer or when we update the ASN of the current process. 218c2ecf20Sopenharmony_ci */ 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci/* Don't get into trouble with dueling __EXTERN_INLINEs. */ 248c2ecf20Sopenharmony_ci#ifndef __EXTERN_INLINE 258c2ecf20Sopenharmony_ci#include <asm/io.h> 268c2ecf20Sopenharmony_ci#endif 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_cistatic inline unsigned long 308c2ecf20Sopenharmony_ci__reload_thread(struct pcb_struct *pcb) 318c2ecf20Sopenharmony_ci{ 328c2ecf20Sopenharmony_ci register unsigned long a0 __asm__("$16"); 338c2ecf20Sopenharmony_ci register unsigned long v0 __asm__("$0"); 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci a0 = virt_to_phys(pcb); 368c2ecf20Sopenharmony_ci __asm__ __volatile__( 378c2ecf20Sopenharmony_ci "call_pal %2 #__reload_thread" 388c2ecf20Sopenharmony_ci : "=r"(v0), "=r"(a0) 398c2ecf20Sopenharmony_ci : "i"(PAL_swpctx), "r"(a0) 408c2ecf20Sopenharmony_ci : "$1", "$22", "$23", "$24", "$25"); 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci return v0; 438c2ecf20Sopenharmony_ci} 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci/* 478c2ecf20Sopenharmony_ci * The maximum ASN's the processor supports. On the EV4 this is 63 488c2ecf20Sopenharmony_ci * but the PAL-code doesn't actually use this information. On the 498c2ecf20Sopenharmony_ci * EV5 this is 127, and EV6 has 255. 508c2ecf20Sopenharmony_ci * 518c2ecf20Sopenharmony_ci * On the EV4, the ASNs are more-or-less useless anyway, as they are 528c2ecf20Sopenharmony_ci * only used as an icache tag, not for TB entries. On the EV5 and EV6, 538c2ecf20Sopenharmony_ci * ASN's also validate the TB entries, and thus make a lot more sense. 548c2ecf20Sopenharmony_ci * 558c2ecf20Sopenharmony_ci * The EV4 ASN's don't even match the architecture manual, ugh. And 568c2ecf20Sopenharmony_ci * I quote: "If a processor implements address space numbers (ASNs), 578c2ecf20Sopenharmony_ci * and the old PTE has the Address Space Match (ASM) bit clear (ASNs 588c2ecf20Sopenharmony_ci * in use) and the Valid bit set, then entries can also effectively be 598c2ecf20Sopenharmony_ci * made coherent by assigning a new, unused ASN to the currently 608c2ecf20Sopenharmony_ci * running process and not reusing the previous ASN before calling the 618c2ecf20Sopenharmony_ci * appropriate PALcode routine to invalidate the translation buffer (TB)". 628c2ecf20Sopenharmony_ci * 638c2ecf20Sopenharmony_ci * In short, the EV4 has a "kind of" ASN capability, but it doesn't actually 648c2ecf20Sopenharmony_ci * work correctly and can thus not be used (explaining the lack of PAL-code 658c2ecf20Sopenharmony_ci * support). 668c2ecf20Sopenharmony_ci */ 678c2ecf20Sopenharmony_ci#define EV4_MAX_ASN 63 688c2ecf20Sopenharmony_ci#define EV5_MAX_ASN 127 698c2ecf20Sopenharmony_ci#define EV6_MAX_ASN 255 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci#ifdef CONFIG_ALPHA_GENERIC 728c2ecf20Sopenharmony_ci# define MAX_ASN (alpha_mv.max_asn) 738c2ecf20Sopenharmony_ci#else 748c2ecf20Sopenharmony_ci# ifdef CONFIG_ALPHA_EV4 758c2ecf20Sopenharmony_ci# define MAX_ASN EV4_MAX_ASN 768c2ecf20Sopenharmony_ci# elif defined(CONFIG_ALPHA_EV5) 778c2ecf20Sopenharmony_ci# define MAX_ASN EV5_MAX_ASN 788c2ecf20Sopenharmony_ci# else 798c2ecf20Sopenharmony_ci# define MAX_ASN EV6_MAX_ASN 808c2ecf20Sopenharmony_ci# endif 818c2ecf20Sopenharmony_ci#endif 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci/* 848c2ecf20Sopenharmony_ci * cpu_last_asn(processor): 858c2ecf20Sopenharmony_ci * 63 0 868c2ecf20Sopenharmony_ci * +-------------+----------------+--------------+ 878c2ecf20Sopenharmony_ci * | asn version | this processor | hardware asn | 888c2ecf20Sopenharmony_ci * +-------------+----------------+--------------+ 898c2ecf20Sopenharmony_ci */ 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci#include <asm/smp.h> 928c2ecf20Sopenharmony_ci#ifdef CONFIG_SMP 938c2ecf20Sopenharmony_ci#define cpu_last_asn(cpuid) (cpu_data[cpuid].last_asn) 948c2ecf20Sopenharmony_ci#else 958c2ecf20Sopenharmony_ciextern unsigned long last_asn; 968c2ecf20Sopenharmony_ci#define cpu_last_asn(cpuid) last_asn 978c2ecf20Sopenharmony_ci#endif /* CONFIG_SMP */ 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci#define WIDTH_HARDWARE_ASN 8 1008c2ecf20Sopenharmony_ci#define ASN_FIRST_VERSION (1UL << WIDTH_HARDWARE_ASN) 1018c2ecf20Sopenharmony_ci#define HARDWARE_ASN_MASK ((1UL << WIDTH_HARDWARE_ASN) - 1) 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci/* 1048c2ecf20Sopenharmony_ci * NOTE! The way this is set up, the high bits of the "asn_cache" (and 1058c2ecf20Sopenharmony_ci * the "mm->context") are the ASN _version_ code. A version of 0 is 1068c2ecf20Sopenharmony_ci * always considered invalid, so to invalidate another process you only 1078c2ecf20Sopenharmony_ci * need to do "p->mm->context = 0". 1088c2ecf20Sopenharmony_ci * 1098c2ecf20Sopenharmony_ci * If we need more ASN's than the processor has, we invalidate the old 1108c2ecf20Sopenharmony_ci * user TLB's (tbiap()) and start a new ASN version. That will automatically 1118c2ecf20Sopenharmony_ci * force a new asn for any other processes the next time they want to 1128c2ecf20Sopenharmony_ci * run. 1138c2ecf20Sopenharmony_ci */ 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci#ifndef __EXTERN_INLINE 1168c2ecf20Sopenharmony_ci#define __EXTERN_INLINE extern inline 1178c2ecf20Sopenharmony_ci#define __MMU_EXTERN_INLINE 1188c2ecf20Sopenharmony_ci#endif 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ciextern inline unsigned long 1218c2ecf20Sopenharmony_ci__get_new_mm_context(struct mm_struct *mm, long cpu) 1228c2ecf20Sopenharmony_ci{ 1238c2ecf20Sopenharmony_ci unsigned long asn = cpu_last_asn(cpu); 1248c2ecf20Sopenharmony_ci unsigned long next = asn + 1; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci if ((asn & HARDWARE_ASN_MASK) >= MAX_ASN) { 1278c2ecf20Sopenharmony_ci tbiap(); 1288c2ecf20Sopenharmony_ci imb(); 1298c2ecf20Sopenharmony_ci next = (asn & ~HARDWARE_ASN_MASK) + ASN_FIRST_VERSION; 1308c2ecf20Sopenharmony_ci } 1318c2ecf20Sopenharmony_ci cpu_last_asn(cpu) = next; 1328c2ecf20Sopenharmony_ci return next; 1338c2ecf20Sopenharmony_ci} 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci__EXTERN_INLINE void 1368c2ecf20Sopenharmony_ciev5_switch_mm(struct mm_struct *prev_mm, struct mm_struct *next_mm, 1378c2ecf20Sopenharmony_ci struct task_struct *next) 1388c2ecf20Sopenharmony_ci{ 1398c2ecf20Sopenharmony_ci /* Check if our ASN is of an older version, and thus invalid. */ 1408c2ecf20Sopenharmony_ci unsigned long asn; 1418c2ecf20Sopenharmony_ci unsigned long mmc; 1428c2ecf20Sopenharmony_ci long cpu = smp_processor_id(); 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci#ifdef CONFIG_SMP 1458c2ecf20Sopenharmony_ci cpu_data[cpu].asn_lock = 1; 1468c2ecf20Sopenharmony_ci barrier(); 1478c2ecf20Sopenharmony_ci#endif 1488c2ecf20Sopenharmony_ci asn = cpu_last_asn(cpu); 1498c2ecf20Sopenharmony_ci mmc = next_mm->context[cpu]; 1508c2ecf20Sopenharmony_ci if ((mmc ^ asn) & ~HARDWARE_ASN_MASK) { 1518c2ecf20Sopenharmony_ci mmc = __get_new_mm_context(next_mm, cpu); 1528c2ecf20Sopenharmony_ci next_mm->context[cpu] = mmc; 1538c2ecf20Sopenharmony_ci } 1548c2ecf20Sopenharmony_ci#ifdef CONFIG_SMP 1558c2ecf20Sopenharmony_ci else 1568c2ecf20Sopenharmony_ci cpu_data[cpu].need_new_asn = 1; 1578c2ecf20Sopenharmony_ci#endif 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci /* Always update the PCB ASN. Another thread may have allocated 1608c2ecf20Sopenharmony_ci a new mm->context (via flush_tlb_mm) without the ASN serial 1618c2ecf20Sopenharmony_ci number wrapping. We have no way to detect when this is needed. */ 1628c2ecf20Sopenharmony_ci task_thread_info(next)->pcb.asn = mmc & HARDWARE_ASN_MASK; 1638c2ecf20Sopenharmony_ci} 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci__EXTERN_INLINE void 1668c2ecf20Sopenharmony_ciev4_switch_mm(struct mm_struct *prev_mm, struct mm_struct *next_mm, 1678c2ecf20Sopenharmony_ci struct task_struct *next) 1688c2ecf20Sopenharmony_ci{ 1698c2ecf20Sopenharmony_ci /* As described, ASN's are broken for TLB usage. But we can 1708c2ecf20Sopenharmony_ci optimize for switching between threads -- if the mm is 1718c2ecf20Sopenharmony_ci unchanged from current we needn't flush. */ 1728c2ecf20Sopenharmony_ci /* ??? May not be needed because EV4 PALcode recognizes that 1738c2ecf20Sopenharmony_ci ASN's are broken and does a tbiap itself on swpctx, under 1748c2ecf20Sopenharmony_ci the "Must set ASN or flush" rule. At least this is true 1758c2ecf20Sopenharmony_ci for a 1992 SRM, reports Joseph Martin (jmartin@hlo.dec.com). 1768c2ecf20Sopenharmony_ci I'm going to leave this here anyway, just to Be Sure. -- r~ */ 1778c2ecf20Sopenharmony_ci if (prev_mm != next_mm) 1788c2ecf20Sopenharmony_ci tbiap(); 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci /* Do continue to allocate ASNs, because we can still use them 1818c2ecf20Sopenharmony_ci to avoid flushing the icache. */ 1828c2ecf20Sopenharmony_ci ev5_switch_mm(prev_mm, next_mm, next); 1838c2ecf20Sopenharmony_ci} 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ciextern void __load_new_mm_context(struct mm_struct *); 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci#ifdef CONFIG_SMP 1888c2ecf20Sopenharmony_ci#define check_mmu_context() \ 1898c2ecf20Sopenharmony_cido { \ 1908c2ecf20Sopenharmony_ci int cpu = smp_processor_id(); \ 1918c2ecf20Sopenharmony_ci cpu_data[cpu].asn_lock = 0; \ 1928c2ecf20Sopenharmony_ci barrier(); \ 1938c2ecf20Sopenharmony_ci if (cpu_data[cpu].need_new_asn) { \ 1948c2ecf20Sopenharmony_ci struct mm_struct * mm = current->active_mm; \ 1958c2ecf20Sopenharmony_ci cpu_data[cpu].need_new_asn = 0; \ 1968c2ecf20Sopenharmony_ci if (!mm->context[cpu]) \ 1978c2ecf20Sopenharmony_ci __load_new_mm_context(mm); \ 1988c2ecf20Sopenharmony_ci } \ 1998c2ecf20Sopenharmony_ci} while(0) 2008c2ecf20Sopenharmony_ci#else 2018c2ecf20Sopenharmony_ci#define check_mmu_context() do { } while(0) 2028c2ecf20Sopenharmony_ci#endif 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci__EXTERN_INLINE void 2058c2ecf20Sopenharmony_ciev5_activate_mm(struct mm_struct *prev_mm, struct mm_struct *next_mm) 2068c2ecf20Sopenharmony_ci{ 2078c2ecf20Sopenharmony_ci __load_new_mm_context(next_mm); 2088c2ecf20Sopenharmony_ci} 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci__EXTERN_INLINE void 2118c2ecf20Sopenharmony_ciev4_activate_mm(struct mm_struct *prev_mm, struct mm_struct *next_mm) 2128c2ecf20Sopenharmony_ci{ 2138c2ecf20Sopenharmony_ci __load_new_mm_context(next_mm); 2148c2ecf20Sopenharmony_ci tbiap(); 2158c2ecf20Sopenharmony_ci} 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci#define deactivate_mm(tsk,mm) do { } while (0) 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci#ifdef CONFIG_ALPHA_GENERIC 2208c2ecf20Sopenharmony_ci# define switch_mm(a,b,c) alpha_mv.mv_switch_mm((a),(b),(c)) 2218c2ecf20Sopenharmony_ci# define activate_mm(x,y) alpha_mv.mv_activate_mm((x),(y)) 2228c2ecf20Sopenharmony_ci#else 2238c2ecf20Sopenharmony_ci# ifdef CONFIG_ALPHA_EV4 2248c2ecf20Sopenharmony_ci# define switch_mm(a,b,c) ev4_switch_mm((a),(b),(c)) 2258c2ecf20Sopenharmony_ci# define activate_mm(x,y) ev4_activate_mm((x),(y)) 2268c2ecf20Sopenharmony_ci# else 2278c2ecf20Sopenharmony_ci# define switch_mm(a,b,c) ev5_switch_mm((a),(b),(c)) 2288c2ecf20Sopenharmony_ci# define activate_mm(x,y) ev5_activate_mm((x),(y)) 2298c2ecf20Sopenharmony_ci# endif 2308c2ecf20Sopenharmony_ci#endif 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_cistatic inline int 2338c2ecf20Sopenharmony_ciinit_new_context(struct task_struct *tsk, struct mm_struct *mm) 2348c2ecf20Sopenharmony_ci{ 2358c2ecf20Sopenharmony_ci int i; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci for_each_online_cpu(i) 2388c2ecf20Sopenharmony_ci mm->context[i] = 0; 2398c2ecf20Sopenharmony_ci if (tsk != current) 2408c2ecf20Sopenharmony_ci task_thread_info(tsk)->pcb.ptbr 2418c2ecf20Sopenharmony_ci = ((unsigned long)mm->pgd - IDENT_ADDR) >> PAGE_SHIFT; 2428c2ecf20Sopenharmony_ci return 0; 2438c2ecf20Sopenharmony_ci} 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ciextern inline void 2468c2ecf20Sopenharmony_cidestroy_context(struct mm_struct *mm) 2478c2ecf20Sopenharmony_ci{ 2488c2ecf20Sopenharmony_ci /* Nothing to do. */ 2498c2ecf20Sopenharmony_ci} 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_cistatic inline void 2528c2ecf20Sopenharmony_cienter_lazy_tlb(struct mm_struct *mm, struct task_struct *tsk) 2538c2ecf20Sopenharmony_ci{ 2548c2ecf20Sopenharmony_ci task_thread_info(tsk)->pcb.ptbr 2558c2ecf20Sopenharmony_ci = ((unsigned long)mm->pgd - IDENT_ADDR) >> PAGE_SHIFT; 2568c2ecf20Sopenharmony_ci} 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci#ifdef __MMU_EXTERN_INLINE 2598c2ecf20Sopenharmony_ci#undef __EXTERN_INLINE 2608c2ecf20Sopenharmony_ci#undef __MMU_EXTERN_INLINE 2618c2ecf20Sopenharmony_ci#endif 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci#endif /* __ALPHA_MMU_CONTEXT_H */ 264