linux/arch/microblaze/mm/pgtable.c
<<
>>
Prefs
   1/*
   2 *  This file contains the routines setting up the linux page tables.
   3 *
   4 * Copyright (C) 2008 Michal Simek
   5 * Copyright (C) 2008 PetaLogix
   6 *
   7 *    Copyright (C) 2007 Xilinx, Inc.  All rights reserved.
   8 *
   9 *  Derived from arch/ppc/mm/pgtable.c:
  10 *    -- paulus
  11 *
  12 *  Derived from arch/ppc/mm/init.c:
  13 *    Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org)
  14 *
  15 *  Modifications by Paul Mackerras (PowerMac) (paulus@cs.anu.edu.au)
  16 *  and Cort Dougan (PReP) (cort@cs.nmt.edu)
  17 *    Copyright (C) 1996 Paul Mackerras
  18 *  Amiga/APUS changes by Jesper Skov (jskov@cygnus.co.uk).
  19 *
  20 *  Derived from "arch/i386/mm/init.c"
  21 *    Copyright (C) 1991, 1992, 1993, 1994  Linus Torvalds
  22 *
  23 *  This file is subject to the terms and conditions of the GNU General
  24 *  Public License.  See the file COPYING in the main directory of this
  25 *  archive for more details.
  26 *
  27 */
  28
  29#include <linux/export.h>
  30#include <linux/kernel.h>
  31#include <linux/types.h>
  32#include <linux/vmalloc.h>
  33#include <linux/init.h>
  34#include <linux/mm_types.h>
  35#include <linux/pgtable.h>
  36#include <linux/memblock.h>
  37
  38#include <asm/pgalloc.h>
  39#include <linux/io.h>
  40#include <asm/mmu.h>
  41#include <asm/sections.h>
  42#include <asm/fixmap.h>
  43
  44unsigned long ioremap_base;
  45unsigned long ioremap_bot;
  46EXPORT_SYMBOL(ioremap_bot);
  47
  48static void __iomem *__ioremap(phys_addr_t addr, unsigned long size,
  49                unsigned long flags)
  50{
  51        unsigned long v, i;
  52        phys_addr_t p;
  53        int err;
  54
  55        /*
  56         * Choose an address to map it to.
  57         * Once the vmalloc system is running, we use it.
  58         * Before then, we use space going down from ioremap_base
  59         * (ioremap_bot records where we're up to).
  60         */
  61        p = addr & PAGE_MASK;
  62        size = PAGE_ALIGN(addr + size) - p;
  63
  64        /*
  65         * Don't allow anybody to remap normal RAM that we're using.
  66         * mem_init() sets high_memory so only do the check after that.
  67         *
  68         * However, allow remap of rootfs: TBD
  69         */
  70
  71        if (mem_init_done &&
  72                p >= memory_start && p < virt_to_phys(high_memory) &&
  73                !(p >= __virt_to_phys((phys_addr_t)__bss_stop) &&
  74                p < __virt_to_phys((phys_addr_t)__bss_stop))) {
  75                pr_warn("__ioremap(): phys addr "PTE_FMT" is RAM lr %ps\n",
  76                        (unsigned long)p, __builtin_return_address(0));
  77                return NULL;
  78        }
  79
  80        if (size == 0)
  81                return NULL;
  82
  83        /*
  84         * Is it already mapped? If the whole area is mapped then we're
  85         * done, otherwise remap it since we want to keep the virt addrs for
  86         * each request contiguous.
  87         *
  88         * We make the assumption here that if the bottom and top
  89         * of the range we want are mapped then it's mapped to the
  90         * same virt address (and this is contiguous).
  91         *  -- Cort
  92         */
  93
  94        if (mem_init_done) {
  95                struct vm_struct *area;
  96                area = get_vm_area(size, VM_IOREMAP);
  97                if (area == NULL)
  98                        return NULL;
  99                v = (unsigned long) area->addr;
 100        } else {
 101                v = (ioremap_bot -= size);
 102        }
 103
 104        if ((flags & _PAGE_PRESENT) == 0)
 105                flags |= _PAGE_KERNEL;
 106        if (flags & _PAGE_NO_CACHE)
 107                flags |= _PAGE_GUARDED;
 108
 109        err = 0;
 110        for (i = 0; i < size && err == 0; i += PAGE_SIZE)
 111                err = map_page(v + i, p + i, flags);
 112        if (err) {
 113                if (mem_init_done)
 114                        vfree((void *)v);
 115                return NULL;
 116        }
 117
 118        return (void __iomem *) (v + ((unsigned long)addr & ~PAGE_MASK));
 119}
 120
 121void __iomem *ioremap(phys_addr_t addr, unsigned long size)
 122{
 123        return __ioremap(addr, size, _PAGE_NO_CACHE);
 124}
 125EXPORT_SYMBOL(ioremap);
 126
 127void iounmap(volatile void __iomem *addr)
 128{
 129        if ((__force void *)addr > high_memory &&
 130                                        (unsigned long) addr < ioremap_bot)
 131                vfree((void *) (PAGE_MASK & (unsigned long) addr));
 132}
 133EXPORT_SYMBOL(iounmap);
 134
 135
 136int map_page(unsigned long va, phys_addr_t pa, int flags)
 137{
 138        p4d_t *p4d;
 139        pud_t *pud;
 140        pmd_t *pd;
 141        pte_t *pg;
 142        int err = -ENOMEM;
 143
 144        /* Use upper 10 bits of VA to index the first level map */
 145        p4d = p4d_offset(pgd_offset_k(va), va);
 146        pud = pud_offset(p4d, va);
 147        pd = pmd_offset(pud, va);
 148        /* Use middle 10 bits of VA to index the second-level map */
 149        pg = pte_alloc_kernel(pd, va); /* from powerpc - pgtable.c */
 150        /* pg = pte_alloc_kernel(&init_mm, pd, va); */
 151
 152        if (pg != NULL) {
 153                err = 0;
 154                set_pte_at(&init_mm, va, pg, pfn_pte(pa >> PAGE_SHIFT,
 155                                __pgprot(flags)));
 156                if (unlikely(mem_init_done))
 157                        _tlbie(va);
 158        }
 159        return err;
 160}
 161
 162/*
 163 * Map in all of physical memory starting at CONFIG_KERNEL_START.
 164 */
 165void __init mapin_ram(void)
 166{
 167        unsigned long v, p, s, f;
 168
 169        v = CONFIG_KERNEL_START;
 170        p = memory_start;
 171        for (s = 0; s < lowmem_size; s += PAGE_SIZE) {
 172                f = _PAGE_PRESENT | _PAGE_ACCESSED |
 173                                _PAGE_SHARED | _PAGE_HWEXEC;
 174                if ((char *) v < _stext || (char *) v >= _etext)
 175                        f |= _PAGE_WRENABLE;
 176                else
 177                        /* On the MicroBlaze, no user access
 178                           forces R/W kernel access */
 179                        f |= _PAGE_USER;
 180                map_page(v, p, f);
 181                v += PAGE_SIZE;
 182                p += PAGE_SIZE;
 183        }
 184}
 185
 186/* is x a power of 2? */
 187#define is_power_of_2(x)        ((x) != 0 && (((x) & ((x) - 1)) == 0))
 188
 189/* Scan the real Linux page tables and return a PTE pointer for
 190 * a virtual address in a context.
 191 * Returns true (1) if PTE was found, zero otherwise.  The pointer to
 192 * the PTE pointer is unmodified if PTE is not found.
 193 */
 194static int get_pteptr(struct mm_struct *mm, unsigned long addr, pte_t **ptep)
 195{
 196        pgd_t   *pgd;
 197        p4d_t   *p4d;
 198        pud_t   *pud;
 199        pmd_t   *pmd;
 200        pte_t   *pte;
 201        int     retval = 0;
 202
 203        pgd = pgd_offset(mm, addr & PAGE_MASK);
 204        if (pgd) {
 205                p4d = p4d_offset(pgd, addr & PAGE_MASK);
 206                pud = pud_offset(p4d, addr & PAGE_MASK);
 207                pmd = pmd_offset(pud, addr & PAGE_MASK);
 208                if (pmd_present(*pmd)) {
 209                        pte = pte_offset_kernel(pmd, addr & PAGE_MASK);
 210                        if (pte) {
 211                                retval = 1;
 212                                *ptep = pte;
 213                        }
 214                }
 215        }
 216        return retval;
 217}
 218
 219/* Find physical address for this virtual address.  Normally used by
 220 * I/O functions, but anyone can call it.
 221 */
 222unsigned long iopa(unsigned long addr)
 223{
 224        unsigned long pa;
 225
 226        pte_t *pte;
 227        struct mm_struct *mm;
 228
 229        /* Allow mapping of user addresses (within the thread)
 230         * for DMA if necessary.
 231         */
 232        if (addr < TASK_SIZE)
 233                mm = current->mm;
 234        else
 235                mm = &init_mm;
 236
 237        pa = 0;
 238        if (get_pteptr(mm, addr, &pte))
 239                pa = (pte_val(*pte) & PAGE_MASK) | (addr & ~PAGE_MASK);
 240
 241        return pa;
 242}
 243
 244__ref pte_t *pte_alloc_one_kernel(struct mm_struct *mm)
 245{
 246        if (mem_init_done)
 247                return (pte_t *)__get_free_page(GFP_KERNEL | __GFP_ZERO);
 248        else
 249                return memblock_alloc_try_nid(PAGE_SIZE, PAGE_SIZE,
 250                                              MEMBLOCK_LOW_LIMIT,
 251                                              memory_start + kernel_tlb,
 252                                              NUMA_NO_NODE);
 253}
 254
 255void __set_fixmap(enum fixed_addresses idx, phys_addr_t phys, pgprot_t flags)
 256{
 257        unsigned long address = __fix_to_virt(idx);
 258
 259        if (idx >= __end_of_fixed_addresses)
 260                BUG();
 261
 262        map_page(address, phys, pgprot_val(flags));
 263}
 264