162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * This file contains the routines setting up the linux page tables. 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (C) 2008 Michal Simek 562306a36Sopenharmony_ci * Copyright (C) 2008 PetaLogix 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Copyright (C) 2007 Xilinx, Inc. All rights reserved. 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * Derived from arch/ppc/mm/pgtable.c: 1062306a36Sopenharmony_ci * -- paulus 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * Derived from arch/ppc/mm/init.c: 1362306a36Sopenharmony_ci * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) 1462306a36Sopenharmony_ci * 1562306a36Sopenharmony_ci * Modifications by Paul Mackerras (PowerMac) (paulus@cs.anu.edu.au) 1662306a36Sopenharmony_ci * and Cort Dougan (PReP) (cort@cs.nmt.edu) 1762306a36Sopenharmony_ci * Copyright (C) 1996 Paul Mackerras 1862306a36Sopenharmony_ci * Amiga/APUS changes by Jesper Skov (jskov@cygnus.co.uk). 1962306a36Sopenharmony_ci * 2062306a36Sopenharmony_ci * Derived from "arch/i386/mm/init.c" 2162306a36Sopenharmony_ci * Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds 2262306a36Sopenharmony_ci * 2362306a36Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General 2462306a36Sopenharmony_ci * Public License. See the file COPYING in the main directory of this 2562306a36Sopenharmony_ci * archive for more details. 2662306a36Sopenharmony_ci * 2762306a36Sopenharmony_ci */ 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#include <linux/export.h> 3062306a36Sopenharmony_ci#include <linux/kernel.h> 3162306a36Sopenharmony_ci#include <linux/types.h> 3262306a36Sopenharmony_ci#include <linux/vmalloc.h> 3362306a36Sopenharmony_ci#include <linux/init.h> 3462306a36Sopenharmony_ci#include <linux/mm_types.h> 3562306a36Sopenharmony_ci#include <linux/pgtable.h> 3662306a36Sopenharmony_ci#include <linux/memblock.h> 3762306a36Sopenharmony_ci#include <linux/kallsyms.h> 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci#include <asm/pgalloc.h> 4062306a36Sopenharmony_ci#include <linux/io.h> 4162306a36Sopenharmony_ci#include <asm/mmu.h> 4262306a36Sopenharmony_ci#include <asm/sections.h> 4362306a36Sopenharmony_ci#include <asm/fixmap.h> 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ciunsigned long ioremap_base; 4662306a36Sopenharmony_ciunsigned long ioremap_bot; 4762306a36Sopenharmony_ciEXPORT_SYMBOL(ioremap_bot); 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cistatic void __iomem *__ioremap(phys_addr_t addr, unsigned long size, 5062306a36Sopenharmony_ci unsigned long flags) 5162306a36Sopenharmony_ci{ 5262306a36Sopenharmony_ci unsigned long v, i; 5362306a36Sopenharmony_ci phys_addr_t p; 5462306a36Sopenharmony_ci int err; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci /* 5762306a36Sopenharmony_ci * Choose an address to map it to. 5862306a36Sopenharmony_ci * Once the vmalloc system is running, we use it. 5962306a36Sopenharmony_ci * Before then, we use space going down from ioremap_base 6062306a36Sopenharmony_ci * (ioremap_bot records where we're up to). 6162306a36Sopenharmony_ci */ 6262306a36Sopenharmony_ci p = addr & PAGE_MASK; 6362306a36Sopenharmony_ci size = PAGE_ALIGN(addr + size) - p; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci /* 6662306a36Sopenharmony_ci * Don't allow anybody to remap normal RAM that we're using. 6762306a36Sopenharmony_ci * mem_init() sets high_memory so only do the check after that. 6862306a36Sopenharmony_ci * 6962306a36Sopenharmony_ci * However, allow remap of rootfs: TBD 7062306a36Sopenharmony_ci */ 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci if (mem_init_done && 7362306a36Sopenharmony_ci p >= memory_start && p < virt_to_phys(high_memory) && 7462306a36Sopenharmony_ci !(p >= __virt_to_phys((phys_addr_t)__bss_stop) && 7562306a36Sopenharmony_ci p < __virt_to_phys((phys_addr_t)__bss_stop))) { 7662306a36Sopenharmony_ci pr_warn("__ioremap(): phys addr "PTE_FMT" is RAM lr %ps\n", 7762306a36Sopenharmony_ci (unsigned long)p, __builtin_return_address(0)); 7862306a36Sopenharmony_ci return NULL; 7962306a36Sopenharmony_ci } 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci if (size == 0) 8262306a36Sopenharmony_ci return NULL; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci /* 8562306a36Sopenharmony_ci * Is it already mapped? If the whole area is mapped then we're 8662306a36Sopenharmony_ci * done, otherwise remap it since we want to keep the virt addrs for 8762306a36Sopenharmony_ci * each request contiguous. 8862306a36Sopenharmony_ci * 8962306a36Sopenharmony_ci * We make the assumption here that if the bottom and top 9062306a36Sopenharmony_ci * of the range we want are mapped then it's mapped to the 9162306a36Sopenharmony_ci * same virt address (and this is contiguous). 9262306a36Sopenharmony_ci * -- Cort 9362306a36Sopenharmony_ci */ 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci if (mem_init_done) { 9662306a36Sopenharmony_ci struct vm_struct *area; 9762306a36Sopenharmony_ci area = get_vm_area(size, VM_IOREMAP); 9862306a36Sopenharmony_ci if (area == NULL) 9962306a36Sopenharmony_ci return NULL; 10062306a36Sopenharmony_ci v = (unsigned long) area->addr; 10162306a36Sopenharmony_ci } else { 10262306a36Sopenharmony_ci v = (ioremap_bot -= size); 10362306a36Sopenharmony_ci } 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci if ((flags & _PAGE_PRESENT) == 0) 10662306a36Sopenharmony_ci flags |= _PAGE_KERNEL; 10762306a36Sopenharmony_ci if (flags & _PAGE_NO_CACHE) 10862306a36Sopenharmony_ci flags |= _PAGE_GUARDED; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci err = 0; 11162306a36Sopenharmony_ci for (i = 0; i < size && err == 0; i += PAGE_SIZE) 11262306a36Sopenharmony_ci err = map_page(v + i, p + i, flags); 11362306a36Sopenharmony_ci if (err) { 11462306a36Sopenharmony_ci if (mem_init_done) 11562306a36Sopenharmony_ci vfree((void *)v); 11662306a36Sopenharmony_ci return NULL; 11762306a36Sopenharmony_ci } 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci return (void __iomem *) (v + ((unsigned long)addr & ~PAGE_MASK)); 12062306a36Sopenharmony_ci} 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_civoid __iomem *ioremap(phys_addr_t addr, unsigned long size) 12362306a36Sopenharmony_ci{ 12462306a36Sopenharmony_ci return __ioremap(addr, size, _PAGE_NO_CACHE); 12562306a36Sopenharmony_ci} 12662306a36Sopenharmony_ciEXPORT_SYMBOL(ioremap); 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_civoid iounmap(volatile void __iomem *addr) 12962306a36Sopenharmony_ci{ 13062306a36Sopenharmony_ci if ((__force void *)addr > high_memory && 13162306a36Sopenharmony_ci (unsigned long) addr < ioremap_bot) 13262306a36Sopenharmony_ci vfree((void *) (PAGE_MASK & (unsigned long) addr)); 13362306a36Sopenharmony_ci} 13462306a36Sopenharmony_ciEXPORT_SYMBOL(iounmap); 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ciint map_page(unsigned long va, phys_addr_t pa, int flags) 13862306a36Sopenharmony_ci{ 13962306a36Sopenharmony_ci p4d_t *p4d; 14062306a36Sopenharmony_ci pud_t *pud; 14162306a36Sopenharmony_ci pmd_t *pd; 14262306a36Sopenharmony_ci pte_t *pg; 14362306a36Sopenharmony_ci int err = -ENOMEM; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci /* Use upper 10 bits of VA to index the first level map */ 14662306a36Sopenharmony_ci p4d = p4d_offset(pgd_offset_k(va), va); 14762306a36Sopenharmony_ci pud = pud_offset(p4d, va); 14862306a36Sopenharmony_ci pd = pmd_offset(pud, va); 14962306a36Sopenharmony_ci /* Use middle 10 bits of VA to index the second-level map */ 15062306a36Sopenharmony_ci pg = pte_alloc_kernel(pd, va); /* from powerpc - pgtable.c */ 15162306a36Sopenharmony_ci /* pg = pte_alloc_kernel(&init_mm, pd, va); */ 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci if (pg != NULL) { 15462306a36Sopenharmony_ci err = 0; 15562306a36Sopenharmony_ci set_pte_at(&init_mm, va, pg, pfn_pte(pa >> PAGE_SHIFT, 15662306a36Sopenharmony_ci __pgprot(flags))); 15762306a36Sopenharmony_ci if (unlikely(mem_init_done)) 15862306a36Sopenharmony_ci _tlbie(va); 15962306a36Sopenharmony_ci } 16062306a36Sopenharmony_ci return err; 16162306a36Sopenharmony_ci} 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci/* 16462306a36Sopenharmony_ci * Map in all of physical memory starting at CONFIG_KERNEL_START. 16562306a36Sopenharmony_ci */ 16662306a36Sopenharmony_civoid __init mapin_ram(void) 16762306a36Sopenharmony_ci{ 16862306a36Sopenharmony_ci unsigned long v, p, s, f; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci v = CONFIG_KERNEL_START; 17162306a36Sopenharmony_ci p = memory_start; 17262306a36Sopenharmony_ci for (s = 0; s < lowmem_size; s += PAGE_SIZE) { 17362306a36Sopenharmony_ci f = _PAGE_PRESENT | _PAGE_ACCESSED | 17462306a36Sopenharmony_ci _PAGE_SHARED | _PAGE_HWEXEC; 17562306a36Sopenharmony_ci if (!is_kernel_text(v)) 17662306a36Sopenharmony_ci f |= _PAGE_WRENABLE; 17762306a36Sopenharmony_ci else 17862306a36Sopenharmony_ci /* On the MicroBlaze, no user access 17962306a36Sopenharmony_ci forces R/W kernel access */ 18062306a36Sopenharmony_ci f |= _PAGE_USER; 18162306a36Sopenharmony_ci map_page(v, p, f); 18262306a36Sopenharmony_ci v += PAGE_SIZE; 18362306a36Sopenharmony_ci p += PAGE_SIZE; 18462306a36Sopenharmony_ci } 18562306a36Sopenharmony_ci} 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci/* is x a power of 2? */ 18862306a36Sopenharmony_ci#define is_power_of_2(x) ((x) != 0 && (((x) & ((x) - 1)) == 0)) 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci/* Scan the real Linux page tables and return a PTE pointer for 19162306a36Sopenharmony_ci * a virtual address in a context. 19262306a36Sopenharmony_ci * Returns true (1) if PTE was found, zero otherwise. The pointer to 19362306a36Sopenharmony_ci * the PTE pointer is unmodified if PTE is not found. 19462306a36Sopenharmony_ci */ 19562306a36Sopenharmony_cistatic int get_pteptr(struct mm_struct *mm, unsigned long addr, pte_t **ptep) 19662306a36Sopenharmony_ci{ 19762306a36Sopenharmony_ci pgd_t *pgd; 19862306a36Sopenharmony_ci p4d_t *p4d; 19962306a36Sopenharmony_ci pud_t *pud; 20062306a36Sopenharmony_ci pmd_t *pmd; 20162306a36Sopenharmony_ci pte_t *pte; 20262306a36Sopenharmony_ci int retval = 0; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci pgd = pgd_offset(mm, addr & PAGE_MASK); 20562306a36Sopenharmony_ci if (pgd) { 20662306a36Sopenharmony_ci p4d = p4d_offset(pgd, addr & PAGE_MASK); 20762306a36Sopenharmony_ci pud = pud_offset(p4d, addr & PAGE_MASK); 20862306a36Sopenharmony_ci pmd = pmd_offset(pud, addr & PAGE_MASK); 20962306a36Sopenharmony_ci if (pmd_present(*pmd)) { 21062306a36Sopenharmony_ci pte = pte_offset_kernel(pmd, addr & PAGE_MASK); 21162306a36Sopenharmony_ci if (pte) { 21262306a36Sopenharmony_ci retval = 1; 21362306a36Sopenharmony_ci *ptep = pte; 21462306a36Sopenharmony_ci } 21562306a36Sopenharmony_ci } 21662306a36Sopenharmony_ci } 21762306a36Sopenharmony_ci return retval; 21862306a36Sopenharmony_ci} 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci/* Find physical address for this virtual address. Normally used by 22162306a36Sopenharmony_ci * I/O functions, but anyone can call it. 22262306a36Sopenharmony_ci */ 22362306a36Sopenharmony_ciunsigned long iopa(unsigned long addr) 22462306a36Sopenharmony_ci{ 22562306a36Sopenharmony_ci unsigned long pa; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci pte_t *pte; 22862306a36Sopenharmony_ci struct mm_struct *mm; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci /* Allow mapping of user addresses (within the thread) 23162306a36Sopenharmony_ci * for DMA if necessary. 23262306a36Sopenharmony_ci */ 23362306a36Sopenharmony_ci if (addr < TASK_SIZE) 23462306a36Sopenharmony_ci mm = current->mm; 23562306a36Sopenharmony_ci else 23662306a36Sopenharmony_ci mm = &init_mm; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci pa = 0; 23962306a36Sopenharmony_ci if (get_pteptr(mm, addr, &pte)) 24062306a36Sopenharmony_ci pa = (pte_val(*pte) & PAGE_MASK) | (addr & ~PAGE_MASK); 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci return pa; 24362306a36Sopenharmony_ci} 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci__ref pte_t *pte_alloc_one_kernel(struct mm_struct *mm) 24662306a36Sopenharmony_ci{ 24762306a36Sopenharmony_ci if (mem_init_done) 24862306a36Sopenharmony_ci return (pte_t *)__get_free_page(GFP_KERNEL | __GFP_ZERO); 24962306a36Sopenharmony_ci else 25062306a36Sopenharmony_ci return memblock_alloc_try_nid(PAGE_SIZE, PAGE_SIZE, 25162306a36Sopenharmony_ci MEMBLOCK_LOW_LIMIT, 25262306a36Sopenharmony_ci memory_start + kernel_tlb, 25362306a36Sopenharmony_ci NUMA_NO_NODE); 25462306a36Sopenharmony_ci} 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_civoid __set_fixmap(enum fixed_addresses idx, phys_addr_t phys, pgprot_t flags) 25762306a36Sopenharmony_ci{ 25862306a36Sopenharmony_ci unsigned long address = __fix_to_virt(idx); 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci if (idx >= __end_of_fixed_addresses) 26162306a36Sopenharmony_ci BUG(); 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci map_page(address, phys, pgprot_val(flags)); 26462306a36Sopenharmony_ci} 265