162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Based upon linux/arch/m68k/mm/sun3mmu.c 462306a36Sopenharmony_ci * Based upon linux/arch/ppc/mm/mmu_context.c 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Implementations of mm routines specific to the Coldfire MMU. 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Copyright (c) 2008 Freescale Semiconductor, Inc. 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/kernel.h> 1262306a36Sopenharmony_ci#include <linux/types.h> 1362306a36Sopenharmony_ci#include <linux/mm.h> 1462306a36Sopenharmony_ci#include <linux/init.h> 1562306a36Sopenharmony_ci#include <linux/string.h> 1662306a36Sopenharmony_ci#include <linux/memblock.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include <asm/setup.h> 1962306a36Sopenharmony_ci#include <asm/page.h> 2062306a36Sopenharmony_ci#include <asm/mmu_context.h> 2162306a36Sopenharmony_ci#include <asm/mcf_pgalloc.h> 2262306a36Sopenharmony_ci#include <asm/tlbflush.h> 2362306a36Sopenharmony_ci#include <asm/pgalloc.h> 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#define KMAPAREA(x) ((x >= VMALLOC_START) && (x < KMAP_END)) 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_cimm_context_t next_mmu_context; 2862306a36Sopenharmony_ciunsigned long context_map[LAST_CONTEXT / BITS_PER_LONG + 1]; 2962306a36Sopenharmony_ciatomic_t nr_free_contexts; 3062306a36Sopenharmony_cistruct mm_struct *context_mm[LAST_CONTEXT+1]; 3162306a36Sopenharmony_ciunsigned long num_pages; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci/* 3462306a36Sopenharmony_ci * ColdFire paging_init derived from sun3. 3562306a36Sopenharmony_ci */ 3662306a36Sopenharmony_civoid __init paging_init(void) 3762306a36Sopenharmony_ci{ 3862306a36Sopenharmony_ci pgd_t *pg_dir; 3962306a36Sopenharmony_ci pte_t *pg_table; 4062306a36Sopenharmony_ci unsigned long address, size; 4162306a36Sopenharmony_ci unsigned long next_pgtable, bootmem_end; 4262306a36Sopenharmony_ci unsigned long max_zone_pfn[MAX_NR_ZONES] = { 0 }; 4362306a36Sopenharmony_ci int i; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci empty_zero_page = memblock_alloc(PAGE_SIZE, PAGE_SIZE); 4662306a36Sopenharmony_ci if (!empty_zero_page) 4762306a36Sopenharmony_ci panic("%s: Failed to allocate %lu bytes align=0x%lx\n", 4862306a36Sopenharmony_ci __func__, PAGE_SIZE, PAGE_SIZE); 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci pg_dir = swapper_pg_dir; 5162306a36Sopenharmony_ci memset(swapper_pg_dir, 0, sizeof(swapper_pg_dir)); 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci size = num_pages * sizeof(pte_t); 5462306a36Sopenharmony_ci size = (size + PAGE_SIZE) & ~(PAGE_SIZE-1); 5562306a36Sopenharmony_ci next_pgtable = (unsigned long) memblock_alloc(size, PAGE_SIZE); 5662306a36Sopenharmony_ci if (!next_pgtable) 5762306a36Sopenharmony_ci panic("%s: Failed to allocate %lu bytes align=0x%lx\n", 5862306a36Sopenharmony_ci __func__, size, PAGE_SIZE); 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci bootmem_end = (next_pgtable + size + PAGE_SIZE) & PAGE_MASK; 6162306a36Sopenharmony_ci pg_dir += PAGE_OFFSET >> PGDIR_SHIFT; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci address = PAGE_OFFSET; 6462306a36Sopenharmony_ci while (address < (unsigned long)high_memory) { 6562306a36Sopenharmony_ci pg_table = (pte_t *) next_pgtable; 6662306a36Sopenharmony_ci next_pgtable += PTRS_PER_PTE * sizeof(pte_t); 6762306a36Sopenharmony_ci pgd_val(*pg_dir) = (unsigned long) pg_table; 6862306a36Sopenharmony_ci pg_dir++; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci /* now change pg_table to kernel virtual addresses */ 7162306a36Sopenharmony_ci for (i = 0; i < PTRS_PER_PTE; ++i, ++pg_table) { 7262306a36Sopenharmony_ci pte_t pte = pfn_pte(virt_to_pfn((void *)address), 7362306a36Sopenharmony_ci PAGE_INIT); 7462306a36Sopenharmony_ci if (address >= (unsigned long) high_memory) 7562306a36Sopenharmony_ci pte_val(pte) = 0; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci set_pte(pg_table, pte); 7862306a36Sopenharmony_ci address += PAGE_SIZE; 7962306a36Sopenharmony_ci } 8062306a36Sopenharmony_ci } 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci current->mm = NULL; 8362306a36Sopenharmony_ci max_zone_pfn[ZONE_DMA] = PFN_DOWN(_ramend); 8462306a36Sopenharmony_ci free_area_init(max_zone_pfn); 8562306a36Sopenharmony_ci} 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ciint cf_tlb_miss(struct pt_regs *regs, int write, int dtlb, int extension_word) 8862306a36Sopenharmony_ci{ 8962306a36Sopenharmony_ci unsigned long flags, mmuar, mmutr; 9062306a36Sopenharmony_ci struct mm_struct *mm; 9162306a36Sopenharmony_ci pgd_t *pgd; 9262306a36Sopenharmony_ci p4d_t *p4d; 9362306a36Sopenharmony_ci pud_t *pud; 9462306a36Sopenharmony_ci pmd_t *pmd; 9562306a36Sopenharmony_ci pte_t *pte = NULL; 9662306a36Sopenharmony_ci int ret = -1; 9762306a36Sopenharmony_ci int asid; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci local_irq_save(flags); 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci mmuar = (dtlb) ? mmu_read(MMUAR) : 10262306a36Sopenharmony_ci regs->pc + (extension_word * sizeof(long)); 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci mm = (!user_mode(regs) && KMAPAREA(mmuar)) ? &init_mm : current->mm; 10562306a36Sopenharmony_ci if (!mm) 10662306a36Sopenharmony_ci goto out; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci pgd = pgd_offset(mm, mmuar); 10962306a36Sopenharmony_ci if (pgd_none(*pgd)) 11062306a36Sopenharmony_ci goto out; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci p4d = p4d_offset(pgd, mmuar); 11362306a36Sopenharmony_ci if (p4d_none(*p4d)) 11462306a36Sopenharmony_ci goto out; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci pud = pud_offset(p4d, mmuar); 11762306a36Sopenharmony_ci if (pud_none(*pud)) 11862306a36Sopenharmony_ci goto out; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci pmd = pmd_offset(pud, mmuar); 12162306a36Sopenharmony_ci if (pmd_none(*pmd)) 12262306a36Sopenharmony_ci goto out; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci pte = (KMAPAREA(mmuar)) ? pte_offset_kernel(pmd, mmuar) 12562306a36Sopenharmony_ci : pte_offset_map(pmd, mmuar); 12662306a36Sopenharmony_ci if (!pte || pte_none(*pte) || !pte_present(*pte)) 12762306a36Sopenharmony_ci goto out; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci if (write) { 13062306a36Sopenharmony_ci if (!pte_write(*pte)) 13162306a36Sopenharmony_ci goto out; 13262306a36Sopenharmony_ci set_pte(pte, pte_mkdirty(*pte)); 13362306a36Sopenharmony_ci } 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci set_pte(pte, pte_mkyoung(*pte)); 13662306a36Sopenharmony_ci asid = mm->context & 0xff; 13762306a36Sopenharmony_ci if (!pte_dirty(*pte) && !KMAPAREA(mmuar)) 13862306a36Sopenharmony_ci set_pte(pte, pte_wrprotect(*pte)); 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci mmutr = (mmuar & PAGE_MASK) | (asid << MMUTR_IDN) | MMUTR_V; 14162306a36Sopenharmony_ci if ((mmuar < TASK_UNMAPPED_BASE) || (mmuar >= TASK_SIZE)) 14262306a36Sopenharmony_ci mmutr |= (pte->pte & CF_PAGE_MMUTR_MASK) >> CF_PAGE_MMUTR_SHIFT; 14362306a36Sopenharmony_ci mmu_write(MMUTR, mmutr); 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci mmu_write(MMUDR, (pte_val(*pte) & PAGE_MASK) | 14662306a36Sopenharmony_ci ((pte->pte) & CF_PAGE_MMUDR_MASK) | MMUDR_SZ_8KB | MMUDR_X); 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci if (dtlb) 14962306a36Sopenharmony_ci mmu_write(MMUOR, MMUOR_ACC | MMUOR_UAA); 15062306a36Sopenharmony_ci else 15162306a36Sopenharmony_ci mmu_write(MMUOR, MMUOR_ITLB | MMUOR_ACC | MMUOR_UAA); 15262306a36Sopenharmony_ci ret = 0; 15362306a36Sopenharmony_ciout: 15462306a36Sopenharmony_ci if (pte && !KMAPAREA(mmuar)) 15562306a36Sopenharmony_ci pte_unmap(pte); 15662306a36Sopenharmony_ci local_irq_restore(flags); 15762306a36Sopenharmony_ci return ret; 15862306a36Sopenharmony_ci} 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_civoid __init cf_bootmem_alloc(void) 16162306a36Sopenharmony_ci{ 16262306a36Sopenharmony_ci unsigned long memstart; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci /* _rambase and _ramend will be naturally page aligned */ 16562306a36Sopenharmony_ci m68k_memory[0].addr = _rambase; 16662306a36Sopenharmony_ci m68k_memory[0].size = _ramend - _rambase; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci memblock_add_node(m68k_memory[0].addr, m68k_memory[0].size, 0, 16962306a36Sopenharmony_ci MEMBLOCK_NONE); 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci /* compute total pages in system */ 17262306a36Sopenharmony_ci num_pages = PFN_DOWN(_ramend - _rambase); 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci /* page numbers */ 17562306a36Sopenharmony_ci memstart = PAGE_ALIGN(_ramstart); 17662306a36Sopenharmony_ci min_low_pfn = PFN_DOWN(_rambase); 17762306a36Sopenharmony_ci max_pfn = max_low_pfn = PFN_DOWN(_ramend); 17862306a36Sopenharmony_ci high_memory = (void *)_ramend; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci /* Reserve kernel text/data/bss */ 18162306a36Sopenharmony_ci memblock_reserve(_rambase, memstart - _rambase); 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci m68k_virt_to_node_shift = fls(_ramend - 1) - 6; 18462306a36Sopenharmony_ci module_fixup(NULL, __start_fixup, __stop_fixup); 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci /* setup node data */ 18762306a36Sopenharmony_ci m68k_setup_node(0); 18862306a36Sopenharmony_ci} 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci/* 19162306a36Sopenharmony_ci * Initialize the context management stuff. 19262306a36Sopenharmony_ci * The following was taken from arch/ppc/mmu_context.c 19362306a36Sopenharmony_ci */ 19462306a36Sopenharmony_civoid __init cf_mmu_context_init(void) 19562306a36Sopenharmony_ci{ 19662306a36Sopenharmony_ci /* 19762306a36Sopenharmony_ci * Some processors have too few contexts to reserve one for 19862306a36Sopenharmony_ci * init_mm, and require using context 0 for a normal task. 19962306a36Sopenharmony_ci * Other processors reserve the use of context zero for the kernel. 20062306a36Sopenharmony_ci * This code assumes FIRST_CONTEXT < 32. 20162306a36Sopenharmony_ci */ 20262306a36Sopenharmony_ci context_map[0] = (1 << FIRST_CONTEXT) - 1; 20362306a36Sopenharmony_ci next_mmu_context = FIRST_CONTEXT; 20462306a36Sopenharmony_ci atomic_set(&nr_free_contexts, LAST_CONTEXT - FIRST_CONTEXT + 1); 20562306a36Sopenharmony_ci} 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci/* 20862306a36Sopenharmony_ci * Steal a context from a task that has one at the moment. 20962306a36Sopenharmony_ci * This isn't an LRU system, it just frees up each context in 21062306a36Sopenharmony_ci * turn (sort-of pseudo-random replacement :). This would be the 21162306a36Sopenharmony_ci * place to implement an LRU scheme if anyone was motivated to do it. 21262306a36Sopenharmony_ci * -- paulus 21362306a36Sopenharmony_ci */ 21462306a36Sopenharmony_civoid steal_context(void) 21562306a36Sopenharmony_ci{ 21662306a36Sopenharmony_ci struct mm_struct *mm; 21762306a36Sopenharmony_ci /* 21862306a36Sopenharmony_ci * free up context `next_mmu_context' 21962306a36Sopenharmony_ci * if we shouldn't free context 0, don't... 22062306a36Sopenharmony_ci */ 22162306a36Sopenharmony_ci if (next_mmu_context < FIRST_CONTEXT) 22262306a36Sopenharmony_ci next_mmu_context = FIRST_CONTEXT; 22362306a36Sopenharmony_ci mm = context_mm[next_mmu_context]; 22462306a36Sopenharmony_ci flush_tlb_mm(mm); 22562306a36Sopenharmony_ci destroy_context(mm); 22662306a36Sopenharmony_ci} 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_cistatic const pgprot_t protection_map[16] = { 22962306a36Sopenharmony_ci [VM_NONE] = PAGE_NONE, 23062306a36Sopenharmony_ci [VM_READ] = __pgprot(CF_PAGE_VALID | 23162306a36Sopenharmony_ci CF_PAGE_ACCESSED | 23262306a36Sopenharmony_ci CF_PAGE_READABLE), 23362306a36Sopenharmony_ci [VM_WRITE] = __pgprot(CF_PAGE_VALID | 23462306a36Sopenharmony_ci CF_PAGE_ACCESSED | 23562306a36Sopenharmony_ci CF_PAGE_WRITABLE), 23662306a36Sopenharmony_ci [VM_WRITE | VM_READ] = __pgprot(CF_PAGE_VALID | 23762306a36Sopenharmony_ci CF_PAGE_ACCESSED | 23862306a36Sopenharmony_ci CF_PAGE_READABLE | 23962306a36Sopenharmony_ci CF_PAGE_WRITABLE), 24062306a36Sopenharmony_ci [VM_EXEC] = __pgprot(CF_PAGE_VALID | 24162306a36Sopenharmony_ci CF_PAGE_ACCESSED | 24262306a36Sopenharmony_ci CF_PAGE_EXEC), 24362306a36Sopenharmony_ci [VM_EXEC | VM_READ] = __pgprot(CF_PAGE_VALID | 24462306a36Sopenharmony_ci CF_PAGE_ACCESSED | 24562306a36Sopenharmony_ci CF_PAGE_READABLE | 24662306a36Sopenharmony_ci CF_PAGE_EXEC), 24762306a36Sopenharmony_ci [VM_EXEC | VM_WRITE] = __pgprot(CF_PAGE_VALID | 24862306a36Sopenharmony_ci CF_PAGE_ACCESSED | 24962306a36Sopenharmony_ci CF_PAGE_WRITABLE | 25062306a36Sopenharmony_ci CF_PAGE_EXEC), 25162306a36Sopenharmony_ci [VM_EXEC | VM_WRITE | VM_READ] = __pgprot(CF_PAGE_VALID | 25262306a36Sopenharmony_ci CF_PAGE_ACCESSED | 25362306a36Sopenharmony_ci CF_PAGE_READABLE | 25462306a36Sopenharmony_ci CF_PAGE_WRITABLE | 25562306a36Sopenharmony_ci CF_PAGE_EXEC), 25662306a36Sopenharmony_ci [VM_SHARED] = PAGE_NONE, 25762306a36Sopenharmony_ci [VM_SHARED | VM_READ] = __pgprot(CF_PAGE_VALID | 25862306a36Sopenharmony_ci CF_PAGE_ACCESSED | 25962306a36Sopenharmony_ci CF_PAGE_READABLE), 26062306a36Sopenharmony_ci [VM_SHARED | VM_WRITE] = PAGE_SHARED, 26162306a36Sopenharmony_ci [VM_SHARED | VM_WRITE | VM_READ] = __pgprot(CF_PAGE_VALID | 26262306a36Sopenharmony_ci CF_PAGE_ACCESSED | 26362306a36Sopenharmony_ci CF_PAGE_READABLE | 26462306a36Sopenharmony_ci CF_PAGE_SHARED), 26562306a36Sopenharmony_ci [VM_SHARED | VM_EXEC] = __pgprot(CF_PAGE_VALID | 26662306a36Sopenharmony_ci CF_PAGE_ACCESSED | 26762306a36Sopenharmony_ci CF_PAGE_EXEC), 26862306a36Sopenharmony_ci [VM_SHARED | VM_EXEC | VM_READ] = __pgprot(CF_PAGE_VALID | 26962306a36Sopenharmony_ci CF_PAGE_ACCESSED | 27062306a36Sopenharmony_ci CF_PAGE_READABLE | 27162306a36Sopenharmony_ci CF_PAGE_EXEC), 27262306a36Sopenharmony_ci [VM_SHARED | VM_EXEC | VM_WRITE] = __pgprot(CF_PAGE_VALID | 27362306a36Sopenharmony_ci CF_PAGE_ACCESSED | 27462306a36Sopenharmony_ci CF_PAGE_SHARED | 27562306a36Sopenharmony_ci CF_PAGE_EXEC), 27662306a36Sopenharmony_ci [VM_SHARED | VM_EXEC | VM_WRITE | VM_READ] = __pgprot(CF_PAGE_VALID | 27762306a36Sopenharmony_ci CF_PAGE_ACCESSED | 27862306a36Sopenharmony_ci CF_PAGE_READABLE | 27962306a36Sopenharmony_ci CF_PAGE_SHARED | 28062306a36Sopenharmony_ci CF_PAGE_EXEC) 28162306a36Sopenharmony_ci}; 28262306a36Sopenharmony_ciDECLARE_VM_GET_PAGE_PROT 283