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