18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Based upon linux/arch/m68k/mm/sun3mmu.c 48c2ecf20Sopenharmony_ci * Based upon linux/arch/ppc/mm/mmu_context.c 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Implementations of mm routines specific to the Coldfire MMU. 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Copyright (c) 2008 Freescale Semiconductor, Inc. 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/kernel.h> 128c2ecf20Sopenharmony_ci#include <linux/types.h> 138c2ecf20Sopenharmony_ci#include <linux/mm.h> 148c2ecf20Sopenharmony_ci#include <linux/init.h> 158c2ecf20Sopenharmony_ci#include <linux/string.h> 168c2ecf20Sopenharmony_ci#include <linux/memblock.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include <asm/setup.h> 198c2ecf20Sopenharmony_ci#include <asm/page.h> 208c2ecf20Sopenharmony_ci#include <asm/mmu_context.h> 218c2ecf20Sopenharmony_ci#include <asm/mcf_pgalloc.h> 228c2ecf20Sopenharmony_ci#include <asm/tlbflush.h> 238c2ecf20Sopenharmony_ci#include <asm/pgalloc.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#define KMAPAREA(x) ((x >= VMALLOC_START) && (x < KMAP_END)) 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cimm_context_t next_mmu_context; 288c2ecf20Sopenharmony_ciunsigned long context_map[LAST_CONTEXT / BITS_PER_LONG + 1]; 298c2ecf20Sopenharmony_ciatomic_t nr_free_contexts; 308c2ecf20Sopenharmony_cistruct mm_struct *context_mm[LAST_CONTEXT+1]; 318c2ecf20Sopenharmony_ciunsigned long num_pages; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci/* 348c2ecf20Sopenharmony_ci * ColdFire paging_init derived from sun3. 358c2ecf20Sopenharmony_ci */ 368c2ecf20Sopenharmony_civoid __init paging_init(void) 378c2ecf20Sopenharmony_ci{ 388c2ecf20Sopenharmony_ci pgd_t *pg_dir; 398c2ecf20Sopenharmony_ci pte_t *pg_table; 408c2ecf20Sopenharmony_ci unsigned long address, size; 418c2ecf20Sopenharmony_ci unsigned long next_pgtable, bootmem_end; 428c2ecf20Sopenharmony_ci unsigned long max_zone_pfn[MAX_NR_ZONES] = { 0 }; 438c2ecf20Sopenharmony_ci int i; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci empty_zero_page = memblock_alloc(PAGE_SIZE, PAGE_SIZE); 468c2ecf20Sopenharmony_ci if (!empty_zero_page) 478c2ecf20Sopenharmony_ci panic("%s: Failed to allocate %lu bytes align=0x%lx\n", 488c2ecf20Sopenharmony_ci __func__, PAGE_SIZE, PAGE_SIZE); 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci pg_dir = swapper_pg_dir; 518c2ecf20Sopenharmony_ci memset(swapper_pg_dir, 0, sizeof(swapper_pg_dir)); 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci size = num_pages * sizeof(pte_t); 548c2ecf20Sopenharmony_ci size = (size + PAGE_SIZE) & ~(PAGE_SIZE-1); 558c2ecf20Sopenharmony_ci next_pgtable = (unsigned long) memblock_alloc(size, PAGE_SIZE); 568c2ecf20Sopenharmony_ci if (!next_pgtable) 578c2ecf20Sopenharmony_ci panic("%s: Failed to allocate %lu bytes align=0x%lx\n", 588c2ecf20Sopenharmony_ci __func__, size, PAGE_SIZE); 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci bootmem_end = (next_pgtable + size + PAGE_SIZE) & PAGE_MASK; 618c2ecf20Sopenharmony_ci pg_dir += PAGE_OFFSET >> PGDIR_SHIFT; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci address = PAGE_OFFSET; 648c2ecf20Sopenharmony_ci while (address < (unsigned long)high_memory) { 658c2ecf20Sopenharmony_ci pg_table = (pte_t *) next_pgtable; 668c2ecf20Sopenharmony_ci next_pgtable += PTRS_PER_PTE * sizeof(pte_t); 678c2ecf20Sopenharmony_ci pgd_val(*pg_dir) = (unsigned long) pg_table; 688c2ecf20Sopenharmony_ci pg_dir++; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci /* now change pg_table to kernel virtual addresses */ 718c2ecf20Sopenharmony_ci for (i = 0; i < PTRS_PER_PTE; ++i, ++pg_table) { 728c2ecf20Sopenharmony_ci pte_t pte = pfn_pte(virt_to_pfn(address), PAGE_INIT); 738c2ecf20Sopenharmony_ci if (address >= (unsigned long) high_memory) 748c2ecf20Sopenharmony_ci pte_val(pte) = 0; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci set_pte(pg_table, pte); 778c2ecf20Sopenharmony_ci address += PAGE_SIZE; 788c2ecf20Sopenharmony_ci } 798c2ecf20Sopenharmony_ci } 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci current->mm = NULL; 828c2ecf20Sopenharmony_ci max_zone_pfn[ZONE_DMA] = PFN_DOWN(_ramend); 838c2ecf20Sopenharmony_ci free_area_init(max_zone_pfn); 848c2ecf20Sopenharmony_ci} 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ciint cf_tlb_miss(struct pt_regs *regs, int write, int dtlb, int extension_word) 878c2ecf20Sopenharmony_ci{ 888c2ecf20Sopenharmony_ci unsigned long flags, mmuar, mmutr; 898c2ecf20Sopenharmony_ci struct mm_struct *mm; 908c2ecf20Sopenharmony_ci pgd_t *pgd; 918c2ecf20Sopenharmony_ci p4d_t *p4d; 928c2ecf20Sopenharmony_ci pud_t *pud; 938c2ecf20Sopenharmony_ci pmd_t *pmd; 948c2ecf20Sopenharmony_ci pte_t *pte; 958c2ecf20Sopenharmony_ci int asid; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci local_irq_save(flags); 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci mmuar = (dtlb) ? mmu_read(MMUAR) : 1008c2ecf20Sopenharmony_ci regs->pc + (extension_word * sizeof(long)); 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci mm = (!user_mode(regs) && KMAPAREA(mmuar)) ? &init_mm : current->mm; 1038c2ecf20Sopenharmony_ci if (!mm) { 1048c2ecf20Sopenharmony_ci local_irq_restore(flags); 1058c2ecf20Sopenharmony_ci return -1; 1068c2ecf20Sopenharmony_ci } 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci pgd = pgd_offset(mm, mmuar); 1098c2ecf20Sopenharmony_ci if (pgd_none(*pgd)) { 1108c2ecf20Sopenharmony_ci local_irq_restore(flags); 1118c2ecf20Sopenharmony_ci return -1; 1128c2ecf20Sopenharmony_ci } 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci p4d = p4d_offset(pgd, mmuar); 1158c2ecf20Sopenharmony_ci if (p4d_none(*p4d)) { 1168c2ecf20Sopenharmony_ci local_irq_restore(flags); 1178c2ecf20Sopenharmony_ci return -1; 1188c2ecf20Sopenharmony_ci } 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci pud = pud_offset(p4d, mmuar); 1218c2ecf20Sopenharmony_ci if (pud_none(*pud)) { 1228c2ecf20Sopenharmony_ci local_irq_restore(flags); 1238c2ecf20Sopenharmony_ci return -1; 1248c2ecf20Sopenharmony_ci } 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci pmd = pmd_offset(pud, mmuar); 1278c2ecf20Sopenharmony_ci if (pmd_none(*pmd)) { 1288c2ecf20Sopenharmony_ci local_irq_restore(flags); 1298c2ecf20Sopenharmony_ci return -1; 1308c2ecf20Sopenharmony_ci } 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci pte = (KMAPAREA(mmuar)) ? pte_offset_kernel(pmd, mmuar) 1338c2ecf20Sopenharmony_ci : pte_offset_map(pmd, mmuar); 1348c2ecf20Sopenharmony_ci if (pte_none(*pte) || !pte_present(*pte)) { 1358c2ecf20Sopenharmony_ci local_irq_restore(flags); 1368c2ecf20Sopenharmony_ci return -1; 1378c2ecf20Sopenharmony_ci } 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci if (write) { 1408c2ecf20Sopenharmony_ci if (!pte_write(*pte)) { 1418c2ecf20Sopenharmony_ci local_irq_restore(flags); 1428c2ecf20Sopenharmony_ci return -1; 1438c2ecf20Sopenharmony_ci } 1448c2ecf20Sopenharmony_ci set_pte(pte, pte_mkdirty(*pte)); 1458c2ecf20Sopenharmony_ci } 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci set_pte(pte, pte_mkyoung(*pte)); 1488c2ecf20Sopenharmony_ci asid = mm->context & 0xff; 1498c2ecf20Sopenharmony_ci if (!pte_dirty(*pte) && !KMAPAREA(mmuar)) 1508c2ecf20Sopenharmony_ci set_pte(pte, pte_wrprotect(*pte)); 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci mmutr = (mmuar & PAGE_MASK) | (asid << MMUTR_IDN) | MMUTR_V; 1538c2ecf20Sopenharmony_ci if ((mmuar < TASK_UNMAPPED_BASE) || (mmuar >= TASK_SIZE)) 1548c2ecf20Sopenharmony_ci mmutr |= (pte->pte & CF_PAGE_MMUTR_MASK) >> CF_PAGE_MMUTR_SHIFT; 1558c2ecf20Sopenharmony_ci mmu_write(MMUTR, mmutr); 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci mmu_write(MMUDR, (pte_val(*pte) & PAGE_MASK) | 1588c2ecf20Sopenharmony_ci ((pte->pte) & CF_PAGE_MMUDR_MASK) | MMUDR_SZ_8KB | MMUDR_X); 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci if (dtlb) 1618c2ecf20Sopenharmony_ci mmu_write(MMUOR, MMUOR_ACC | MMUOR_UAA); 1628c2ecf20Sopenharmony_ci else 1638c2ecf20Sopenharmony_ci mmu_write(MMUOR, MMUOR_ITLB | MMUOR_ACC | MMUOR_UAA); 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci local_irq_restore(flags); 1668c2ecf20Sopenharmony_ci return 0; 1678c2ecf20Sopenharmony_ci} 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_civoid __init cf_bootmem_alloc(void) 1708c2ecf20Sopenharmony_ci{ 1718c2ecf20Sopenharmony_ci unsigned long memstart; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci /* _rambase and _ramend will be naturally page aligned */ 1748c2ecf20Sopenharmony_ci m68k_memory[0].addr = _rambase; 1758c2ecf20Sopenharmony_ci m68k_memory[0].size = _ramend - _rambase; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci memblock_add_node(m68k_memory[0].addr, m68k_memory[0].size, 0); 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci /* compute total pages in system */ 1808c2ecf20Sopenharmony_ci num_pages = PFN_DOWN(_ramend - _rambase); 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci /* page numbers */ 1838c2ecf20Sopenharmony_ci memstart = PAGE_ALIGN(_ramstart); 1848c2ecf20Sopenharmony_ci min_low_pfn = PFN_DOWN(_rambase); 1858c2ecf20Sopenharmony_ci max_pfn = max_low_pfn = PFN_DOWN(_ramend); 1868c2ecf20Sopenharmony_ci high_memory = (void *)_ramend; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci /* Reserve kernel text/data/bss */ 1898c2ecf20Sopenharmony_ci memblock_reserve(_rambase, memstart - _rambase); 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci m68k_virt_to_node_shift = fls(_ramend - 1) - 6; 1928c2ecf20Sopenharmony_ci module_fixup(NULL, __start_fixup, __stop_fixup); 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci /* setup node data */ 1958c2ecf20Sopenharmony_ci m68k_setup_node(0); 1968c2ecf20Sopenharmony_ci} 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci/* 1998c2ecf20Sopenharmony_ci * Initialize the context management stuff. 2008c2ecf20Sopenharmony_ci * The following was taken from arch/ppc/mmu_context.c 2018c2ecf20Sopenharmony_ci */ 2028c2ecf20Sopenharmony_civoid __init cf_mmu_context_init(void) 2038c2ecf20Sopenharmony_ci{ 2048c2ecf20Sopenharmony_ci /* 2058c2ecf20Sopenharmony_ci * Some processors have too few contexts to reserve one for 2068c2ecf20Sopenharmony_ci * init_mm, and require using context 0 for a normal task. 2078c2ecf20Sopenharmony_ci * Other processors reserve the use of context zero for the kernel. 2088c2ecf20Sopenharmony_ci * This code assumes FIRST_CONTEXT < 32. 2098c2ecf20Sopenharmony_ci */ 2108c2ecf20Sopenharmony_ci context_map[0] = (1 << FIRST_CONTEXT) - 1; 2118c2ecf20Sopenharmony_ci next_mmu_context = FIRST_CONTEXT; 2128c2ecf20Sopenharmony_ci atomic_set(&nr_free_contexts, LAST_CONTEXT - FIRST_CONTEXT + 1); 2138c2ecf20Sopenharmony_ci} 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci/* 2168c2ecf20Sopenharmony_ci * Steal a context from a task that has one at the moment. 2178c2ecf20Sopenharmony_ci * This isn't an LRU system, it just frees up each context in 2188c2ecf20Sopenharmony_ci * turn (sort-of pseudo-random replacement :). This would be the 2198c2ecf20Sopenharmony_ci * place to implement an LRU scheme if anyone was motivated to do it. 2208c2ecf20Sopenharmony_ci * -- paulus 2218c2ecf20Sopenharmony_ci */ 2228c2ecf20Sopenharmony_civoid steal_context(void) 2238c2ecf20Sopenharmony_ci{ 2248c2ecf20Sopenharmony_ci struct mm_struct *mm; 2258c2ecf20Sopenharmony_ci /* 2268c2ecf20Sopenharmony_ci * free up context `next_mmu_context' 2278c2ecf20Sopenharmony_ci * if we shouldn't free context 0, don't... 2288c2ecf20Sopenharmony_ci */ 2298c2ecf20Sopenharmony_ci if (next_mmu_context < FIRST_CONTEXT) 2308c2ecf20Sopenharmony_ci next_mmu_context = FIRST_CONTEXT; 2318c2ecf20Sopenharmony_ci mm = context_mm[next_mmu_context]; 2328c2ecf20Sopenharmony_ci flush_tlb_mm(mm); 2338c2ecf20Sopenharmony_ci destroy_context(mm); 2348c2ecf20Sopenharmony_ci} 2358c2ecf20Sopenharmony_ci 236