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