18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * OpenRISC tlb.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Linux architectural port borrowing liberally from similar works of 68c2ecf20Sopenharmony_ci * others. All original copyrights apply as per the original source 78c2ecf20Sopenharmony_ci * declaration. 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Modifications for the OpenRISC architecture: 108c2ecf20Sopenharmony_ci * Copyright (C) 2003 Matjaz Breskvar <phoenix@bsemi.com> 118c2ecf20Sopenharmony_ci * Copyright (C) 2010-2011 Julius Baxter <julius.baxter@orsoc.se> 128c2ecf20Sopenharmony_ci * Copyright (C) 2010-2011 Jonas Bonn <jonas@southpole.se> 138c2ecf20Sopenharmony_ci */ 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include <linux/sched.h> 168c2ecf20Sopenharmony_ci#include <linux/kernel.h> 178c2ecf20Sopenharmony_ci#include <linux/errno.h> 188c2ecf20Sopenharmony_ci#include <linux/string.h> 198c2ecf20Sopenharmony_ci#include <linux/types.h> 208c2ecf20Sopenharmony_ci#include <linux/ptrace.h> 218c2ecf20Sopenharmony_ci#include <linux/mman.h> 228c2ecf20Sopenharmony_ci#include <linux/mm.h> 238c2ecf20Sopenharmony_ci#include <linux/init.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#include <asm/tlbflush.h> 268c2ecf20Sopenharmony_ci#include <asm/mmu_context.h> 278c2ecf20Sopenharmony_ci#include <asm/spr_defs.h> 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#define NO_CONTEXT -1 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#define NUM_DTLB_SETS (1 << ((mfspr(SPR_IMMUCFGR) & SPR_IMMUCFGR_NTS) >> \ 328c2ecf20Sopenharmony_ci SPR_DMMUCFGR_NTS_OFF)) 338c2ecf20Sopenharmony_ci#define NUM_ITLB_SETS (1 << ((mfspr(SPR_IMMUCFGR) & SPR_IMMUCFGR_NTS) >> \ 348c2ecf20Sopenharmony_ci SPR_IMMUCFGR_NTS_OFF)) 358c2ecf20Sopenharmony_ci#define DTLB_OFFSET(addr) (((addr) >> PAGE_SHIFT) & (NUM_DTLB_SETS-1)) 368c2ecf20Sopenharmony_ci#define ITLB_OFFSET(addr) (((addr) >> PAGE_SHIFT) & (NUM_ITLB_SETS-1)) 378c2ecf20Sopenharmony_ci/* 388c2ecf20Sopenharmony_ci * Invalidate all TLB entries. 398c2ecf20Sopenharmony_ci * 408c2ecf20Sopenharmony_ci * This comes down to setting the 'valid' bit for all xTLBMR registers to 0. 418c2ecf20Sopenharmony_ci * Easiest way to accomplish this is to just zero out the xTLBMR register 428c2ecf20Sopenharmony_ci * completely. 438c2ecf20Sopenharmony_ci * 448c2ecf20Sopenharmony_ci */ 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_civoid local_flush_tlb_all(void) 478c2ecf20Sopenharmony_ci{ 488c2ecf20Sopenharmony_ci int i; 498c2ecf20Sopenharmony_ci unsigned long num_tlb_sets; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci /* Determine number of sets for IMMU. */ 528c2ecf20Sopenharmony_ci /* FIXME: Assumption is I & D nsets equal. */ 538c2ecf20Sopenharmony_ci num_tlb_sets = NUM_ITLB_SETS; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci for (i = 0; i < num_tlb_sets; i++) { 568c2ecf20Sopenharmony_ci mtspr_off(SPR_DTLBMR_BASE(0), i, 0); 578c2ecf20Sopenharmony_ci mtspr_off(SPR_ITLBMR_BASE(0), i, 0); 588c2ecf20Sopenharmony_ci } 598c2ecf20Sopenharmony_ci} 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci#define have_dtlbeir (mfspr(SPR_DMMUCFGR) & SPR_DMMUCFGR_TEIRI) 628c2ecf20Sopenharmony_ci#define have_itlbeir (mfspr(SPR_IMMUCFGR) & SPR_IMMUCFGR_TEIRI) 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci/* 658c2ecf20Sopenharmony_ci * Invalidate a single page. This is what the xTLBEIR register is for. 668c2ecf20Sopenharmony_ci * 678c2ecf20Sopenharmony_ci * There's no point in checking the vma for PAGE_EXEC to determine whether it's 688c2ecf20Sopenharmony_ci * the data or instruction TLB that should be flushed... that would take more 698c2ecf20Sopenharmony_ci * than the few instructions that the following compiles down to! 708c2ecf20Sopenharmony_ci * 718c2ecf20Sopenharmony_ci * The case where we don't have the xTLBEIR register really only works for 728c2ecf20Sopenharmony_ci * MMU's with a single way and is hard-coded that way. 738c2ecf20Sopenharmony_ci */ 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci#define flush_dtlb_page_eir(addr) mtspr(SPR_DTLBEIR, addr) 768c2ecf20Sopenharmony_ci#define flush_dtlb_page_no_eir(addr) \ 778c2ecf20Sopenharmony_ci mtspr_off(SPR_DTLBMR_BASE(0), DTLB_OFFSET(addr), 0); 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci#define flush_itlb_page_eir(addr) mtspr(SPR_ITLBEIR, addr) 808c2ecf20Sopenharmony_ci#define flush_itlb_page_no_eir(addr) \ 818c2ecf20Sopenharmony_ci mtspr_off(SPR_ITLBMR_BASE(0), ITLB_OFFSET(addr), 0); 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_civoid local_flush_tlb_page(struct vm_area_struct *vma, unsigned long addr) 848c2ecf20Sopenharmony_ci{ 858c2ecf20Sopenharmony_ci if (have_dtlbeir) 868c2ecf20Sopenharmony_ci flush_dtlb_page_eir(addr); 878c2ecf20Sopenharmony_ci else 888c2ecf20Sopenharmony_ci flush_dtlb_page_no_eir(addr); 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci if (have_itlbeir) 918c2ecf20Sopenharmony_ci flush_itlb_page_eir(addr); 928c2ecf20Sopenharmony_ci else 938c2ecf20Sopenharmony_ci flush_itlb_page_no_eir(addr); 948c2ecf20Sopenharmony_ci} 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_civoid local_flush_tlb_range(struct vm_area_struct *vma, 978c2ecf20Sopenharmony_ci unsigned long start, unsigned long end) 988c2ecf20Sopenharmony_ci{ 998c2ecf20Sopenharmony_ci int addr; 1008c2ecf20Sopenharmony_ci bool dtlbeir; 1018c2ecf20Sopenharmony_ci bool itlbeir; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci dtlbeir = have_dtlbeir; 1048c2ecf20Sopenharmony_ci itlbeir = have_itlbeir; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci for (addr = start; addr < end; addr += PAGE_SIZE) { 1078c2ecf20Sopenharmony_ci if (dtlbeir) 1088c2ecf20Sopenharmony_ci flush_dtlb_page_eir(addr); 1098c2ecf20Sopenharmony_ci else 1108c2ecf20Sopenharmony_ci flush_dtlb_page_no_eir(addr); 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci if (itlbeir) 1138c2ecf20Sopenharmony_ci flush_itlb_page_eir(addr); 1148c2ecf20Sopenharmony_ci else 1158c2ecf20Sopenharmony_ci flush_itlb_page_no_eir(addr); 1168c2ecf20Sopenharmony_ci } 1178c2ecf20Sopenharmony_ci} 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci/* 1208c2ecf20Sopenharmony_ci * Invalidate the selected mm context only. 1218c2ecf20Sopenharmony_ci * 1228c2ecf20Sopenharmony_ci * FIXME: Due to some bug here, we're flushing everything for now. 1238c2ecf20Sopenharmony_ci * This should be changed to loop over over mm and call flush_tlb_range. 1248c2ecf20Sopenharmony_ci */ 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_civoid local_flush_tlb_mm(struct mm_struct *mm) 1278c2ecf20Sopenharmony_ci{ 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci /* Was seeing bugs with the mm struct passed to us. Scrapped most of 1308c2ecf20Sopenharmony_ci this function. */ 1318c2ecf20Sopenharmony_ci /* Several architctures do this */ 1328c2ecf20Sopenharmony_ci local_flush_tlb_all(); 1338c2ecf20Sopenharmony_ci} 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci/* called in schedule() just before actually doing the switch_to */ 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_civoid switch_mm(struct mm_struct *prev, struct mm_struct *next, 1388c2ecf20Sopenharmony_ci struct task_struct *next_tsk) 1398c2ecf20Sopenharmony_ci{ 1408c2ecf20Sopenharmony_ci unsigned int cpu; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci if (unlikely(prev == next)) 1438c2ecf20Sopenharmony_ci return; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci cpu = smp_processor_id(); 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci cpumask_clear_cpu(cpu, mm_cpumask(prev)); 1488c2ecf20Sopenharmony_ci cpumask_set_cpu(cpu, mm_cpumask(next)); 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci /* remember the pgd for the fault handlers 1518c2ecf20Sopenharmony_ci * this is similar to the pgd register in some other CPU's. 1528c2ecf20Sopenharmony_ci * we need our own copy of it because current and active_mm 1538c2ecf20Sopenharmony_ci * might be invalid at points where we still need to derefer 1548c2ecf20Sopenharmony_ci * the pgd. 1558c2ecf20Sopenharmony_ci */ 1568c2ecf20Sopenharmony_ci current_pgd[cpu] = next->pgd; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci /* We don't have context support implemented, so flush all 1598c2ecf20Sopenharmony_ci * entries belonging to previous map 1608c2ecf20Sopenharmony_ci */ 1618c2ecf20Sopenharmony_ci local_flush_tlb_mm(prev); 1628c2ecf20Sopenharmony_ci} 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci/* 1658c2ecf20Sopenharmony_ci * Initialize the context related info for a new mm_struct 1668c2ecf20Sopenharmony_ci * instance. 1678c2ecf20Sopenharmony_ci */ 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ciint init_new_context(struct task_struct *tsk, struct mm_struct *mm) 1708c2ecf20Sopenharmony_ci{ 1718c2ecf20Sopenharmony_ci mm->context = NO_CONTEXT; 1728c2ecf20Sopenharmony_ci return 0; 1738c2ecf20Sopenharmony_ci} 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci/* called by __exit_mm to destroy the used MMU context if any before 1768c2ecf20Sopenharmony_ci * destroying the mm itself. this is only called when the last user of the mm 1778c2ecf20Sopenharmony_ci * drops it. 1788c2ecf20Sopenharmony_ci */ 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_civoid destroy_context(struct mm_struct *mm) 1818c2ecf20Sopenharmony_ci{ 1828c2ecf20Sopenharmony_ci flush_tlb_mm(mm); 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci} 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci/* called once during VM initialization, from init.c */ 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_civoid __init tlb_init(void) 1898c2ecf20Sopenharmony_ci{ 1908c2ecf20Sopenharmony_ci /* Do nothing... */ 1918c2ecf20Sopenharmony_ci /* invalidate the entire TLB */ 1928c2ecf20Sopenharmony_ci /* flush_tlb_all(); */ 1938c2ecf20Sopenharmony_ci} 194