PageRenderTime 35ms CodeModel.GetById 12ms app.highlight 16ms RepoModel.GetById 1ms app.codeStats 0ms

/arch/ppc/mm/pgtable.c

https://bitbucket.org/evzijst/gittest
C | 471 lines | 309 code | 64 blank | 98 comment | 66 complexity | 4d547a5c99059dfa583219ca09cb1c65 MD5 | raw file
  1/*
  2 * This file contains the routines setting up the linux page tables.
  3 *  -- paulus
  4 *
  5 *  Derived from arch/ppc/mm/init.c:
  6 *    Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org)
  7 *
  8 *  Modifications by Paul Mackerras (PowerMac) (paulus@cs.anu.edu.au)
  9 *  and Cort Dougan (PReP) (cort@cs.nmt.edu)
 10 *    Copyright (C) 1996 Paul Mackerras
 11 *  Amiga/APUS changes by Jesper Skov (jskov@cygnus.co.uk).
 12 *
 13 *  Derived from "arch/i386/mm/init.c"
 14 *    Copyright (C) 1991, 1992, 1993, 1994  Linus Torvalds
 15 *
 16 *  This program is free software; you can redistribute it and/or
 17 *  modify it under the terms of the GNU General Public License
 18 *  as published by the Free Software Foundation; either version
 19 *  2 of the License, or (at your option) any later version.
 20 *
 21 */
 22
 23#include <linux/config.h>
 24#include <linux/kernel.h>
 25#include <linux/module.h>
 26#include <linux/types.h>
 27#include <linux/mm.h>
 28#include <linux/vmalloc.h>
 29#include <linux/init.h>
 30#include <linux/highmem.h>
 31
 32#include <asm/pgtable.h>
 33#include <asm/pgalloc.h>
 34#include <asm/io.h>
 35
 36#include "mmu_decl.h"
 37
 38unsigned long ioremap_base;
 39unsigned long ioremap_bot;
 40int io_bat_index;
 41
 42#if defined(CONFIG_6xx) || defined(CONFIG_POWER3)
 43#define HAVE_BATS	1
 44#endif
 45
 46#if defined(CONFIG_FSL_BOOKE)
 47#define HAVE_TLBCAM	1
 48#endif
 49
 50extern char etext[], _stext[];
 51
 52#ifdef CONFIG_SMP
 53extern void hash_page_sync(void);
 54#endif
 55
 56#ifdef HAVE_BATS
 57extern unsigned long v_mapped_by_bats(unsigned long va);
 58extern unsigned long p_mapped_by_bats(unsigned long pa);
 59void setbat(int index, unsigned long virt, unsigned long phys,
 60	    unsigned int size, int flags);
 61
 62#else /* !HAVE_BATS */
 63#define v_mapped_by_bats(x)	(0UL)
 64#define p_mapped_by_bats(x)	(0UL)
 65#endif /* HAVE_BATS */
 66
 67#ifdef HAVE_TLBCAM
 68extern unsigned int tlbcam_index;
 69extern unsigned int num_tlbcam_entries;
 70extern unsigned long v_mapped_by_tlbcam(unsigned long va);
 71extern unsigned long p_mapped_by_tlbcam(unsigned long pa);
 72#else /* !HAVE_TLBCAM */
 73#define v_mapped_by_tlbcam(x)	(0UL)
 74#define p_mapped_by_tlbcam(x)	(0UL)
 75#endif /* HAVE_TLBCAM */
 76
 77#ifdef CONFIG_44x
 78/* 44x uses an 8kB pgdir because it has 8-byte Linux PTEs. */
 79#define PGDIR_ORDER	1
 80#else
 81#define PGDIR_ORDER	0
 82#endif
 83
 84pgd_t *pgd_alloc(struct mm_struct *mm)
 85{
 86	pgd_t *ret;
 87
 88	ret = (pgd_t *)__get_free_pages(GFP_KERNEL|__GFP_ZERO, PGDIR_ORDER);
 89	return ret;
 90}
 91
 92void pgd_free(pgd_t *pgd)
 93{
 94	free_pages((unsigned long)pgd, PGDIR_ORDER);
 95}
 96
 97pte_t *pte_alloc_one_kernel(struct mm_struct *mm, unsigned long address)
 98{
 99	pte_t *pte;
100	extern int mem_init_done;
101	extern void *early_get_page(void);
102
103	if (mem_init_done) {
104		pte = (pte_t *)__get_free_page(GFP_KERNEL|__GFP_REPEAT|__GFP_ZERO);
105	} else {
106		pte = (pte_t *)early_get_page();
107		if (pte)
108			clear_page(pte);
109	}
110	return pte;
111}
112
113struct page *pte_alloc_one(struct mm_struct *mm, unsigned long address)
114{
115	struct page *ptepage;
116
117#ifdef CONFIG_HIGHPTE
118	int flags = GFP_KERNEL | __GFP_HIGHMEM | __GFP_REPEAT;
119#else
120	int flags = GFP_KERNEL | __GFP_REPEAT;
121#endif
122
123	ptepage = alloc_pages(flags, 0);
124	if (ptepage)
125		clear_highpage(ptepage);
126	return ptepage;
127}
128
129void pte_free_kernel(pte_t *pte)
130{
131#ifdef CONFIG_SMP
132	hash_page_sync();
133#endif
134	free_page((unsigned long)pte);
135}
136
137void pte_free(struct page *ptepage)
138{
139#ifdef CONFIG_SMP
140	hash_page_sync();
141#endif
142	__free_page(ptepage);
143}
144
145#ifndef CONFIG_44x
146void __iomem *
147ioremap(phys_addr_t addr, unsigned long size)
148{
149	return __ioremap(addr, size, _PAGE_NO_CACHE);
150}
151#else /* CONFIG_44x */
152void __iomem *
153ioremap64(unsigned long long addr, unsigned long size)
154{
155	return __ioremap(addr, size, _PAGE_NO_CACHE);
156}
157
158void __iomem *
159ioremap(phys_addr_t addr, unsigned long size)
160{
161	phys_addr_t addr64 = fixup_bigphys_addr(addr, size);
162
163	return ioremap64(addr64, size);
164}
165#endif /* CONFIG_44x */
166
167void __iomem *
168__ioremap(phys_addr_t addr, unsigned long size, unsigned long flags)
169{
170	unsigned long v, i;
171	phys_addr_t p;
172	int err;
173
174	/*
175	 * Choose an address to map it to.
176	 * Once the vmalloc system is running, we use it.
177	 * Before then, we use space going down from ioremap_base
178	 * (ioremap_bot records where we're up to).
179	 */
180	p = addr & PAGE_MASK;
181	size = PAGE_ALIGN(addr + size) - p;
182
183	/*
184	 * If the address lies within the first 16 MB, assume it's in ISA
185	 * memory space
186	 */
187	if (p < 16*1024*1024)
188		p += _ISA_MEM_BASE;
189
190	/*
191	 * Don't allow anybody to remap normal RAM that we're using.
192	 * mem_init() sets high_memory so only do the check after that.
193	 */
194	if ( mem_init_done && (p < virt_to_phys(high_memory)) )
195	{
196		printk("__ioremap(): phys addr "PTE_FMT" is RAM lr %p\n", p,
197		       __builtin_return_address(0));
198		return NULL;
199	}
200
201	if (size == 0)
202		return NULL;
203
204	/*
205	 * Is it already mapped?  Perhaps overlapped by a previous
206	 * BAT mapping.  If the whole area is mapped then we're done,
207	 * otherwise remap it since we want to keep the virt addrs for
208	 * each request contiguous.
209	 *
210	 * We make the assumption here that if the bottom and top
211	 * of the range we want are mapped then it's mapped to the
212	 * same virt address (and this is contiguous).
213	 *  -- Cort
214	 */
215	if ((v = p_mapped_by_bats(p)) /*&& p_mapped_by_bats(p+size-1)*/ )
216		goto out;
217
218	if ((v = p_mapped_by_tlbcam(p)))
219		goto out;
220
221	if (mem_init_done) {
222		struct vm_struct *area;
223		area = get_vm_area(size, VM_IOREMAP);
224		if (area == 0)
225			return NULL;
226		v = (unsigned long) area->addr;
227	} else {
228		v = (ioremap_bot -= size);
229	}
230
231	if ((flags & _PAGE_PRESENT) == 0)
232		flags |= _PAGE_KERNEL;
233	if (flags & _PAGE_NO_CACHE)
234		flags |= _PAGE_GUARDED;
235
236	/*
237	 * Should check if it is a candidate for a BAT mapping
238	 */
239
240	err = 0;
241	for (i = 0; i < size && err == 0; i += PAGE_SIZE)
242		err = map_page(v+i, p+i, flags);
243	if (err) {
244		if (mem_init_done)
245			vunmap((void *)v);
246		return NULL;
247	}
248
249out:
250	return (void __iomem *) (v + ((unsigned long)addr & ~PAGE_MASK));
251}
252
253void iounmap(volatile void __iomem *addr)
254{
255	/*
256	 * If mapped by BATs then there is nothing to do.
257	 * Calling vfree() generates a benign warning.
258	 */
259	if (v_mapped_by_bats((unsigned long)addr)) return;
260
261	if (addr > high_memory && (unsigned long) addr < ioremap_bot)
262		vunmap((void *) (PAGE_MASK & (unsigned long)addr));
263}
264
265void __iomem *ioport_map(unsigned long port, unsigned int len)
266{
267	return (void __iomem *) (port + _IO_BASE);
268}
269
270void ioport_unmap(void __iomem *addr)
271{
272	/* Nothing to do */
273}
274EXPORT_SYMBOL(ioport_map);
275EXPORT_SYMBOL(ioport_unmap);
276
277int
278map_page(unsigned long va, phys_addr_t pa, int flags)
279{
280	pmd_t *pd;
281	pte_t *pg;
282	int err = -ENOMEM;
283
284	spin_lock(&init_mm.page_table_lock);
285	/* Use upper 10 bits of VA to index the first level map */
286	pd = pmd_offset(pgd_offset_k(va), va);
287	/* Use middle 10 bits of VA to index the second-level map */
288	pg = pte_alloc_kernel(&init_mm, pd, va);
289	if (pg != 0) {
290		err = 0;
291		set_pte_at(&init_mm, va, pg, pfn_pte(pa >> PAGE_SHIFT, __pgprot(flags)));
292		if (mem_init_done)
293			flush_HPTE(0, va, pmd_val(*pd));
294	}
295	spin_unlock(&init_mm.page_table_lock);
296	return err;
297}
298
299/*
300 * Map in all of physical memory starting at KERNELBASE.
301 */
302void __init mapin_ram(void)
303{
304	unsigned long v, p, s, f;
305
306	s = mmu_mapin_ram();
307	v = KERNELBASE + s;
308	p = PPC_MEMSTART + s;
309	for (; s < total_lowmem; s += PAGE_SIZE) {
310		if ((char *) v >= _stext && (char *) v < etext)
311			f = _PAGE_RAM_TEXT;
312		else
313			f = _PAGE_RAM;
314		map_page(v, p, f);
315		v += PAGE_SIZE;
316		p += PAGE_SIZE;
317	}
318}
319
320/* is x a power of 2? */
321#define is_power_of_2(x)	((x) != 0 && (((x) & ((x) - 1)) == 0))
322
323/* is x a power of 4? */
324#define is_power_of_4(x)	((x) != 0 && (((x) & (x-1)) == 0) && (ffs(x) & 1))
325
326/*
327 * Set up a mapping for a block of I/O.
328 * virt, phys, size must all be page-aligned.
329 * This should only be called before ioremap is called.
330 */
331void __init io_block_mapping(unsigned long virt, phys_addr_t phys,
332			     unsigned int size, int flags)
333{
334	int i;
335
336	if (virt > KERNELBASE && virt < ioremap_bot)
337		ioremap_bot = ioremap_base = virt;
338
339#ifdef HAVE_BATS
340	/*
341	 * Use a BAT for this if possible...
342	 */
343	if (io_bat_index < 2 && is_power_of_2(size)
344	    && (virt & (size - 1)) == 0 && (phys & (size - 1)) == 0) {
345		setbat(io_bat_index, virt, phys, size, flags);
346		++io_bat_index;
347		return;
348	}
349#endif /* HAVE_BATS */
350
351#ifdef HAVE_TLBCAM
352	/*
353	 * Use a CAM for this if possible...
354	 */
355	if (tlbcam_index < num_tlbcam_entries && is_power_of_4(size)
356	    && (virt & (size - 1)) == 0 && (phys & (size - 1)) == 0) {
357		settlbcam(tlbcam_index, virt, phys, size, flags, 0);
358		++tlbcam_index;
359		return;
360	}
361#endif /* HAVE_TLBCAM */
362
363	/* No BATs available, put it in the page tables. */
364	for (i = 0; i < size; i += PAGE_SIZE)
365		map_page(virt + i, phys + i, flags);
366}
367
368/* Scan the real Linux page tables and return a PTE pointer for
369 * a virtual address in a context.
370 * Returns true (1) if PTE was found, zero otherwise.  The pointer to
371 * the PTE pointer is unmodified if PTE is not found.
372 */
373int
374get_pteptr(struct mm_struct *mm, unsigned long addr, pte_t **ptep)
375{
376        pgd_t	*pgd;
377        pmd_t	*pmd;
378        pte_t	*pte;
379        int     retval = 0;
380
381        pgd = pgd_offset(mm, addr & PAGE_MASK);
382        if (pgd) {
383                pmd = pmd_offset(pgd, addr & PAGE_MASK);
384                if (pmd_present(*pmd)) {
385                        pte = pte_offset_map(pmd, addr & PAGE_MASK);
386                        if (pte) {
387				retval = 1;
388				*ptep = pte;
389				/* XXX caller needs to do pte_unmap, yuck */
390                        }
391                }
392        }
393        return(retval);
394}
395
396/* Find physical address for this virtual address.  Normally used by
397 * I/O functions, but anyone can call it.
398 */
399unsigned long iopa(unsigned long addr)
400{
401	unsigned long pa;
402
403	/* I don't know why this won't work on PMacs or CHRP.  It
404	 * appears there is some bug, or there is some implicit
405	 * mapping done not properly represented by BATs or in page
406	 * tables.......I am actively working on resolving this, but
407	 * can't hold up other stuff.  -- Dan
408	 */
409	pte_t *pte;
410	struct mm_struct *mm;
411
412	/* Check the BATs */
413	pa = v_mapped_by_bats(addr);
414	if (pa)
415		return pa;
416
417	/* Allow mapping of user addresses (within the thread)
418	 * for DMA if necessary.
419	 */
420	if (addr < TASK_SIZE)
421		mm = current->mm;
422	else
423		mm = &init_mm;
424
425	pa = 0;
426	if (get_pteptr(mm, addr, &pte)) {
427		pa = (pte_val(*pte) & PAGE_MASK) | (addr & ~PAGE_MASK);
428		pte_unmap(pte);
429	}
430
431	return(pa);
432}
433
434/* This is will find the virtual address for a physical one....
435 * Swiped from APUS, could be dangerous :-).
436 * This is only a placeholder until I really find a way to make this
437 * work.  -- Dan
438 */
439unsigned long
440mm_ptov (unsigned long paddr)
441{
442	unsigned long ret;
443#if 0
444	if (paddr < 16*1024*1024)
445		ret = ZTWO_VADDR(paddr);
446	else {
447		int i;
448
449		for (i = 0; i < kmap_chunk_count;){
450			unsigned long phys = kmap_chunks[i++];
451			unsigned long size = kmap_chunks[i++];
452			unsigned long virt = kmap_chunks[i++];
453			if (paddr >= phys
454			    && paddr < (phys + size)){
455				ret = virt + paddr - phys;
456				goto exit;
457			}
458		}
459	
460		ret = (unsigned long) __va(paddr);
461	}
462exit:
463#ifdef DEBUGPV
464	printk ("PTOV(%lx)=%lx\n", paddr, ret);
465#endif
466#else
467	ret = (unsigned long)paddr + KERNELBASE;
468#endif
469	return ret;
470}
471