aboutsummaryrefslogtreecommitdiffstats
path: root/arch/openrisc/mm
diff options
context:
space:
mode:
authorJonas Bonn <jonas@southpole.se>2011-06-04 04:06:11 -0400
committerJonas Bonn <jonas@southpole.se>2011-07-22 12:46:28 -0400
commit61e85e367535a7b6385b404bef93928768140f96 (patch)
treea0b8cb40dff683d3d09268f55080b5539d25b9a5 /arch/openrisc/mm
parent4f246ba30e1a9a31fcfd91d2ab8f5c75f1362bbf (diff)
OpenRISC: Memory management
Signed-off-by: Jonas Bonn <jonas@southpole.se> Reviewed-by: Arnd Bergmann <arnd@arndb.de>
Diffstat (limited to 'arch/openrisc/mm')
-rw-r--r--arch/openrisc/mm/fault.c338
-rw-r--r--arch/openrisc/mm/init.c283
-rw-r--r--arch/openrisc/mm/ioremap.c137
-rw-r--r--arch/openrisc/mm/tlb.c193
4 files changed, 951 insertions, 0 deletions
diff --git a/arch/openrisc/mm/fault.c b/arch/openrisc/mm/fault.c
new file mode 100644
index 000000000000..a5dce82f864b
--- /dev/null
+++ b/arch/openrisc/mm/fault.c
@@ -0,0 +1,338 @@
1/*
2 * OpenRISC fault.c
3 *
4 * Linux architectural port borrowing liberally from similar works of
5 * others. All original copyrights apply as per the original source
6 * declaration.
7 *
8 * Modifications for the OpenRISC architecture:
9 * Copyright (C) 2003 Matjaz Breskvar <phoenix@bsemi.com>
10 * Copyright (C) 2010-2011 Jonas Bonn <jonas@southpole.se>
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version
15 * 2 of the License, or (at your option) any later version.
16 */
17
18#include <linux/mm.h>
19#include <linux/interrupt.h>
20#include <linux/module.h>
21#include <linux/sched.h>
22
23#include <asm/uaccess.h>
24#include <asm/siginfo.h>
25#include <asm/signal.h>
26
27#define NUM_TLB_ENTRIES 64
28#define TLB_OFFSET(add) (((add) >> PAGE_SHIFT) & (NUM_TLB_ENTRIES-1))
29
30unsigned long pte_misses; /* updated by do_page_fault() */
31unsigned long pte_errors; /* updated by do_page_fault() */
32
33/* __PHX__ :: - check the vmalloc_fault in do_page_fault()
34 * - also look into include/asm-or32/mmu_context.h
35 */
36volatile pgd_t *current_pgd;
37
38extern void die(char *, struct pt_regs *, long);
39
40/*
41 * This routine handles page faults. It determines the address,
42 * and the problem, and then passes it off to one of the appropriate
43 * routines.
44 *
45 * If this routine detects a bad access, it returns 1, otherwise it
46 * returns 0.
47 */
48
49asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long address,
50 unsigned long vector, int write_acc)
51{
52 struct task_struct *tsk;
53 struct mm_struct *mm;
54 struct vm_area_struct *vma;
55 siginfo_t info;
56 int fault;
57
58 tsk = current;
59
60 /*
61 * We fault-in kernel-space virtual memory on-demand. The
62 * 'reference' page table is init_mm.pgd.
63 *
64 * NOTE! We MUST NOT take any locks for this case. We may
65 * be in an interrupt or a critical region, and should
66 * only copy the information from the master page table,
67 * nothing more.
68 *
69 * NOTE2: This is done so that, when updating the vmalloc
70 * mappings we don't have to walk all processes pgdirs and
71 * add the high mappings all at once. Instead we do it as they
72 * are used. However vmalloc'ed page entries have the PAGE_GLOBAL
73 * bit set so sometimes the TLB can use a lingering entry.
74 *
75 * This verifies that the fault happens in kernel space
76 * and that the fault was not a protection error.
77 */
78
79 if (address >= VMALLOC_START &&
80 (vector != 0x300 && vector != 0x400) &&
81 !user_mode(regs))
82 goto vmalloc_fault;
83
84 /* If exceptions were enabled, we can reenable them here */
85 if (user_mode(regs)) {
86 /* Exception was in userspace: reenable interrupts */
87 local_irq_enable();
88 } else {
89 /* If exception was in a syscall, then IRQ's may have
90 * been enabled or disabled. If they were enabled,
91 * reenable them.
92 */
93 if (regs->sr && (SPR_SR_IEE | SPR_SR_TEE))
94 local_irq_enable();
95 }
96
97 mm = tsk->mm;
98 info.si_code = SEGV_MAPERR;
99
100 /*
101 * If we're in an interrupt or have no user
102 * context, we must not take the fault..
103 */
104
105 if (in_interrupt() || !mm)
106 goto no_context;
107
108 down_read(&mm->mmap_sem);
109 vma = find_vma(mm, address);
110
111 if (!vma)
112 goto bad_area;
113
114 if (vma->vm_start <= address)
115 goto good_area;
116
117 if (!(vma->vm_flags & VM_GROWSDOWN))
118 goto bad_area;
119
120 if (user_mode(regs)) {
121 /*
122 * accessing the stack below usp is always a bug.
123 * we get page-aligned addresses so we can only check
124 * if we're within a page from usp, but that might be
125 * enough to catch brutal errors at least.
126 */
127 if (address + PAGE_SIZE < regs->sp)
128 goto bad_area;
129 }
130 if (expand_stack(vma, address))
131 goto bad_area;
132
133 /*
134 * Ok, we have a good vm_area for this memory access, so
135 * we can handle it..
136 */
137
138good_area:
139 info.si_code = SEGV_ACCERR;
140
141 /* first do some preliminary protection checks */
142
143 if (write_acc) {
144 if (!(vma->vm_flags & VM_WRITE))
145 goto bad_area;
146 } else {
147 /* not present */
148 if (!(vma->vm_flags & (VM_READ | VM_EXEC)))
149 goto bad_area;
150 }
151
152 /* are we trying to execute nonexecutable area */
153 if ((vector == 0x400) && !(vma->vm_page_prot.pgprot & _PAGE_EXEC))
154 goto bad_area;
155
156 /*
157 * If for any reason at all we couldn't handle the fault,
158 * make sure we exit gracefully rather than endlessly redo
159 * the fault.
160 */
161
162 fault = handle_mm_fault(mm, vma, address, write_acc);
163 if (unlikely(fault & VM_FAULT_ERROR)) {
164 if (fault & VM_FAULT_OOM)
165 goto out_of_memory;
166 else if (fault & VM_FAULT_SIGBUS)
167 goto do_sigbus;
168 BUG();
169 }
170 /*RGD modeled on Cris */
171 if (fault & VM_FAULT_MAJOR)
172 tsk->maj_flt++;
173 else
174 tsk->min_flt++;
175
176 up_read(&mm->mmap_sem);
177 return;
178
179 /*
180 * Something tried to access memory that isn't in our memory map..
181 * Fix it, but check if it's kernel or user first..
182 */
183
184bad_area:
185 up_read(&mm->mmap_sem);
186
187bad_area_nosemaphore:
188
189 /* User mode accesses just cause a SIGSEGV */
190
191 if (user_mode(regs)) {
192 info.si_signo = SIGSEGV;
193 info.si_errno = 0;
194 /* info.si_code has been set above */
195 info.si_addr = (void *)address;
196 force_sig_info(SIGSEGV, &info, tsk);
197 return;
198 }
199
200no_context:
201
202 /* Are we prepared to handle this kernel fault?
203 *
204 * (The kernel has valid exception-points in the source
205 * when it acesses user-memory. When it fails in one
206 * of those points, we find it in a table and do a jump
207 * to some fixup code that loads an appropriate error
208 * code)
209 */
210
211 {
212 const struct exception_table_entry *entry;
213
214 __asm__ __volatile__("l.nop 42");
215
216 if ((entry = search_exception_tables(regs->pc)) != NULL) {
217 /* Adjust the instruction pointer in the stackframe */
218 regs->pc = entry->fixup;
219 return;
220 }
221 }
222
223 /*
224 * Oops. The kernel tried to access some bad page. We'll have to
225 * terminate things with extreme prejudice.
226 */
227
228 if ((unsigned long)(address) < PAGE_SIZE)
229 printk(KERN_ALERT
230 "Unable to handle kernel NULL pointer dereference");
231 else
232 printk(KERN_ALERT "Unable to handle kernel access");
233 printk(" at virtual address 0x%08lx\n", address);
234
235 die("Oops", regs, write_acc);
236
237 do_exit(SIGKILL);
238
239 /*
240 * We ran out of memory, or some other thing happened to us that made
241 * us unable to handle the page fault gracefully.
242 */
243
244out_of_memory:
245 __asm__ __volatile__("l.nop 42");
246 __asm__ __volatile__("l.nop 1");
247
248 up_read(&mm->mmap_sem);
249 printk("VM: killing process %s\n", tsk->comm);
250 if (user_mode(regs))
251 do_exit(SIGKILL);
252 goto no_context;
253
254do_sigbus:
255 up_read(&mm->mmap_sem);
256
257 /*
258 * Send a sigbus, regardless of whether we were in kernel
259 * or user mode.
260 */
261 info.si_signo = SIGBUS;
262 info.si_errno = 0;
263 info.si_code = BUS_ADRERR;
264 info.si_addr = (void *)address;
265 force_sig_info(SIGBUS, &info, tsk);
266
267 /* Kernel mode? Handle exceptions or die */
268 if (!user_mode(regs))
269 goto no_context;
270 return;
271
272vmalloc_fault:
273 {
274 /*
275 * Synchronize this task's top level page-table
276 * with the 'reference' page table.
277 *
278 * Use current_pgd instead of tsk->active_mm->pgd
279 * since the latter might be unavailable if this
280 * code is executed in a misfortunately run irq
281 * (like inside schedule() between switch_mm and
282 * switch_to...).
283 */
284
285 int offset = pgd_index(address);
286 pgd_t *pgd, *pgd_k;
287 pud_t *pud, *pud_k;
288 pmd_t *pmd, *pmd_k;
289 pte_t *pte_k;
290
291/*
292 phx_warn("do_page_fault(): vmalloc_fault will not work, "
293 "since current_pgd assign a proper value somewhere\n"
294 "anyhow we don't need this at the moment\n");
295
296 phx_mmu("vmalloc_fault");
297*/
298 pgd = (pgd_t *)current_pgd + offset;
299 pgd_k = init_mm.pgd + offset;
300
301 /* Since we're two-level, we don't need to do both
302 * set_pgd and set_pmd (they do the same thing). If
303 * we go three-level at some point, do the right thing
304 * with pgd_present and set_pgd here.
305 *
306 * Also, since the vmalloc area is global, we don't
307 * need to copy individual PTE's, it is enough to
308 * copy the pgd pointer into the pte page of the
309 * root task. If that is there, we'll find our pte if
310 * it exists.
311 */
312
313 pud = pud_offset(pgd, address);
314 pud_k = pud_offset(pgd_k, address);
315 if (!pud_present(*pud_k))
316 goto no_context;
317
318 pmd = pmd_offset(pud, address);
319 pmd_k = pmd_offset(pud_k, address);
320
321 if (!pmd_present(*pmd_k))
322 goto bad_area_nosemaphore;
323
324 set_pmd(pmd, *pmd_k);
325
326 /* Make sure the actual PTE exists as well to
327 * catch kernel vmalloc-area accesses to non-mapped
328 * addresses. If we don't do this, this will just
329 * silently loop forever.
330 */
331
332 pte_k = pte_offset_kernel(pmd_k, address);
333 if (!pte_present(*pte_k))
334 goto no_context;
335
336 return;
337 }
338}
diff --git a/arch/openrisc/mm/init.c b/arch/openrisc/mm/init.c
new file mode 100644
index 000000000000..359dcb20fe85
--- /dev/null
+++ b/arch/openrisc/mm/init.c
@@ -0,0 +1,283 @@
1/*
2 * OpenRISC idle.c
3 *
4 * Linux architectural port borrowing liberally from similar works of
5 * others. All original copyrights apply as per the original source
6 * declaration.
7 *
8 * Modifications for the OpenRISC architecture:
9 * Copyright (C) 2003 Matjaz Breskvar <phoenix@bsemi.com>
10 * Copyright (C) 2010-2011 Jonas Bonn <jonas@southpole.se>
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version
15 * 2 of the License, or (at your option) any later version.
16 */
17
18#include <linux/signal.h>
19#include <linux/sched.h>
20#include <linux/kernel.h>
21#include <linux/errno.h>
22#include <linux/string.h>
23#include <linux/types.h>
24#include <linux/ptrace.h>
25#include <linux/mman.h>
26#include <linux/mm.h>
27#include <linux/swap.h>
28#include <linux/smp.h>
29#include <linux/bootmem.h>
30#include <linux/init.h>
31#include <linux/delay.h>
32#include <linux/blkdev.h> /* for initrd_* */
33#include <linux/pagemap.h>
34#include <linux/memblock.h>
35
36#include <asm/system.h>
37#include <asm/segment.h>
38#include <asm/pgalloc.h>
39#include <asm/pgtable.h>
40#include <asm/dma.h>
41#include <asm/io.h>
42#include <asm/tlb.h>
43#include <asm/mmu_context.h>
44#include <asm/kmap_types.h>
45#include <asm/fixmap.h>
46#include <asm/tlbflush.h>
47
48int mem_init_done;
49
50DEFINE_PER_CPU(struct mmu_gather, mmu_gathers);
51
52static void __init zone_sizes_init(void)
53{
54 unsigned long zones_size[MAX_NR_ZONES];
55
56 /* Clear the zone sizes */
57 memset(zones_size, 0, sizeof(zones_size));
58
59 /*
60 * We use only ZONE_NORMAL
61 */
62 zones_size[ZONE_NORMAL] = max_low_pfn;
63
64 free_area_init(zones_size);
65}
66
67extern const char _s_kernel_ro[], _e_kernel_ro[];
68
69/*
70 * Map all physical memory into kernel's address space.
71 *
72 * This is explicitly coded for two-level page tables, so if you need
73 * something else then this needs to change.
74 */
75static void __init map_ram(void)
76{
77 unsigned long v, p, e;
78 pgprot_t prot;
79 pgd_t *pge;
80 pud_t *pue;
81 pmd_t *pme;
82 pte_t *pte;
83 /* These mark extents of read-only kernel pages...
84 * ...from vmlinux.lds.S
85 */
86 struct memblock_region *region;
87
88 v = PAGE_OFFSET;
89
90 for_each_memblock(memory, region) {
91 p = (u32) region->base & PAGE_MASK;
92 e = p + (u32) region->size;
93
94 v = (u32) __va(p);
95 pge = pgd_offset_k(v);
96
97 while (p < e) {
98 int j;
99 pue = pud_offset(pge, v);
100 pme = pmd_offset(pue, v);
101
102 if ((u32) pue != (u32) pge || (u32) pme != (u32) pge) {
103 panic("%s: OR1K kernel hardcoded for "
104 "two-level page tables",
105 __func__);
106 }
107
108 /* Alloc one page for holding PTE's... */
109 pte = (pte_t *) alloc_bootmem_low_pages(PAGE_SIZE);
110 set_pmd(pme, __pmd(_KERNPG_TABLE + __pa(pte)));
111
112 /* Fill the newly allocated page with PTE'S */
113 for (j = 0; p < e && j < PTRS_PER_PGD;
114 v += PAGE_SIZE, p += PAGE_SIZE, j++, pte++) {
115 if (v >= (u32) _e_kernel_ro ||
116 v < (u32) _s_kernel_ro)
117 prot = PAGE_KERNEL;
118 else
119 prot = PAGE_KERNEL_RO;
120
121 set_pte(pte, mk_pte_phys(p, prot));
122 }
123
124 pge++;
125 }
126
127 printk(KERN_INFO "%s: Memory: 0x%x-0x%x\n", __func__,
128 region->base, region->base + region->size);
129 }
130}
131
132void __init paging_init(void)
133{
134 extern void tlb_init(void);
135
136 unsigned long end;
137 int i;
138
139 printk(KERN_INFO "Setting up paging and PTEs.\n");
140
141 /* clear out the init_mm.pgd that will contain the kernel's mappings */
142
143 for (i = 0; i < PTRS_PER_PGD; i++)
144 swapper_pg_dir[i] = __pgd(0);
145
146 /* make sure the current pgd table points to something sane
147 * (even if it is most probably not used until the next
148 * switch_mm)
149 */
150 current_pgd = init_mm.pgd;
151
152 end = (unsigned long)__va(max_low_pfn * PAGE_SIZE);
153
154 map_ram();
155
156 zone_sizes_init();
157
158 /* self modifying code ;) */
159 /* Since the old TLB miss handler has been running up until now,
160 * the kernel pages are still all RW, so we can still modify the
161 * text directly... after this change and a TLB flush, the kernel
162 * pages will become RO.
163 */
164 {
165 extern unsigned long dtlb_miss_handler;
166 extern unsigned long itlb_miss_handler;
167
168 unsigned long *dtlb_vector = __va(0x900);
169 unsigned long *itlb_vector = __va(0xa00);
170
171 printk(KERN_INFO "dtlb_miss_handler %p\n", &dtlb_miss_handler);
172 *dtlb_vector = ((unsigned long)&dtlb_miss_handler -
173 (unsigned long)dtlb_vector) >> 2;
174
175 printk(KERN_INFO "itlb_miss_handler %p\n", &itlb_miss_handler);
176 *itlb_vector = ((unsigned long)&itlb_miss_handler -
177 (unsigned long)itlb_vector) >> 2;
178 }
179
180 /* Invalidate instruction caches after code modification */
181 mtspr(SPR_ICBIR, 0x900);
182 mtspr(SPR_ICBIR, 0xa00);
183
184 /* New TLB miss handlers and kernel page tables are in now place.
185 * Make sure that page flags get updated for all pages in TLB by
186 * flushing the TLB and forcing all TLB entries to be recreated
187 * from their page table flags.
188 */
189 flush_tlb_all();
190}
191
192/* References to section boundaries */
193
194extern char _stext, _etext, _edata, __bss_start, _end;
195extern char __init_begin, __init_end;
196
197static int __init free_pages_init(void)
198{
199 int reservedpages, pfn;
200
201 /* this will put all low memory onto the freelists */
202 totalram_pages = free_all_bootmem();
203
204 reservedpages = 0;
205 for (pfn = 0; pfn < max_low_pfn; pfn++) {
206 /*
207 * Only count reserved RAM pages
208 */
209 if (PageReserved(mem_map + pfn))
210 reservedpages++;
211 }
212
213 return reservedpages;
214}
215
216static void __init set_max_mapnr_init(void)
217{
218 max_mapnr = num_physpages = max_low_pfn;
219}
220
221void __init mem_init(void)
222{
223 int codesize, reservedpages, datasize, initsize;
224
225 if (!mem_map)
226 BUG();
227
228 set_max_mapnr_init();
229
230 high_memory = (void *)__va(max_low_pfn * PAGE_SIZE);
231
232 /* clear the zero-page */
233 memset((void *)empty_zero_page, 0, PAGE_SIZE);
234
235 reservedpages = free_pages_init();
236
237 codesize = (unsigned long)&_etext - (unsigned long)&_stext;
238 datasize = (unsigned long)&_edata - (unsigned long)&_etext;
239 initsize = (unsigned long)&__init_end - (unsigned long)&__init_begin;
240
241 printk(KERN_INFO
242 "Memory: %luk/%luk available (%dk kernel code, %dk reserved, %dk data, %dk init, %ldk highmem)\n",
243 (unsigned long)nr_free_pages() << (PAGE_SHIFT - 10),
244 max_mapnr << (PAGE_SHIFT - 10), codesize >> 10,
245 reservedpages << (PAGE_SHIFT - 10), datasize >> 10,
246 initsize >> 10, (unsigned long)(0 << (PAGE_SHIFT - 10))
247 );
248
249 printk("mem_init_done ...........................................\n");
250 mem_init_done = 1;
251 return;
252}
253
254#ifdef CONFIG_BLK_DEV_INITRD
255void free_initrd_mem(unsigned long start, unsigned long end)
256{
257 printk(KERN_INFO "Freeing initrd memory: %ldk freed\n",
258 (end - start) >> 10);
259
260 for (; start < end; start += PAGE_SIZE) {
261 ClearPageReserved(virt_to_page(start));
262 init_page_count(virt_to_page(start));
263 free_page(start);
264 totalram_pages++;
265 }
266}
267#endif
268
269void free_initmem(void)
270{
271 unsigned long addr;
272
273 addr = (unsigned long)(&__init_begin);
274 for (; addr < (unsigned long)(&__init_end); addr += PAGE_SIZE) {
275 ClearPageReserved(virt_to_page(addr));
276 init_page_count(virt_to_page(addr));
277 free_page(addr);
278 totalram_pages++;
279 }
280 printk(KERN_INFO "Freeing unused kernel memory: %luk freed\n",
281 ((unsigned long)&__init_end -
282 (unsigned long)&__init_begin) >> 10);
283}
diff --git a/arch/openrisc/mm/ioremap.c b/arch/openrisc/mm/ioremap.c
new file mode 100644
index 000000000000..62b08ef392be
--- /dev/null
+++ b/arch/openrisc/mm/ioremap.c
@@ -0,0 +1,137 @@
1/*
2 * OpenRISC ioremap.c
3 *
4 * Linux architectural port borrowing liberally from similar works of
5 * others. All original copyrights apply as per the original source
6 * declaration.
7 *
8 * Modifications for the OpenRISC architecture:
9 * Copyright (C) 2003 Matjaz Breskvar <phoenix@bsemi.com>
10 * Copyright (C) 2010-2011 Jonas Bonn <jonas@southpole.se>
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version
15 * 2 of the License, or (at your option) any later version.
16 */
17
18#include <linux/vmalloc.h>
19#include <linux/io.h>
20#include <asm/pgalloc.h>
21#include <asm/kmap_types.h>
22#include <asm/fixmap.h>
23#include <asm/bug.h>
24#include <asm/pgtable.h>
25#include <linux/sched.h>
26#include <asm/tlbflush.h>
27
28extern int mem_init_done;
29
30static unsigned int fixmaps_used __initdata;
31
32/*
33 * Remap an arbitrary physical address space into the kernel virtual
34 * address space. Needed when the kernel wants to access high addresses
35 * directly.
36 *
37 * NOTE! We need to allow non-page-aligned mappings too: we will obviously
38 * have to convert them into an offset in a page-aligned mapping, but the
39 * caller shouldn't need to know that small detail.
40 */
41void __iomem *__init_refok
42__ioremap(phys_addr_t addr, unsigned long size, pgprot_t prot)
43{
44 phys_addr_t p;
45 unsigned long v;
46 unsigned long offset, last_addr;
47 struct vm_struct *area = NULL;
48
49 /* Don't allow wraparound or zero size */
50 last_addr = addr + size - 1;
51 if (!size || last_addr < addr)
52 return NULL;
53
54 /*
55 * Mappings have to be page-aligned
56 */
57 offset = addr & ~PAGE_MASK;
58 p = addr & PAGE_MASK;
59 size = PAGE_ALIGN(last_addr + 1) - p;
60
61 if (likely(mem_init_done)) {
62 area = get_vm_area(size, VM_IOREMAP);
63 if (!area)
64 return NULL;
65 v = (unsigned long)area->addr;
66 } else {
67 if ((fixmaps_used + (size >> PAGE_SHIFT)) > FIX_N_IOREMAPS)
68 return NULL;
69 v = fix_to_virt(FIX_IOREMAP_BEGIN + fixmaps_used);
70 fixmaps_used += (size >> PAGE_SHIFT);
71 }
72
73 if (ioremap_page_range(v, v + size, p, prot)) {
74 if (likely(mem_init_done))
75 vfree(area->addr);
76 else
77 fixmaps_used -= (size >> PAGE_SHIFT);
78 return NULL;
79 }
80
81 return (void __iomem *)(offset + (char *)v);
82}
83
84void iounmap(void *addr)
85{
86 /* If the page is from the fixmap pool then we just clear out
87 * the fixmap mapping.
88 */
89 if (unlikely((unsigned long)addr > FIXADDR_START)) {
90 /* This is a bit broken... we don't really know
91 * how big the area is so it's difficult to know
92 * how many fixed pages to invalidate...
93 * just flush tlb and hope for the best...
94 * consider this a FIXME
95 *
96 * Really we should be clearing out one or more page
97 * table entries for these virtual addresses so that
98 * future references cause a page fault... for now, we
99 * rely on two things:
100 * i) this code never gets called on known boards
101 * ii) invalid accesses to the freed areas aren't made
102 */
103 flush_tlb_all();
104 return;
105 }
106
107 return vfree((void *)(PAGE_MASK & (unsigned long)addr));
108}
109
110/**
111 * OK, this one's a bit tricky... ioremap can get called before memory is
112 * initialized (early serial console does this) and will want to alloc a page
113 * for its mapping. No userspace pages will ever get allocated before memory
114 * is initialized so this applies only to kernel pages. In the event that
115 * this is called before memory is initialized we allocate the page using
116 * the memblock infrastructure.
117 */
118
119pte_t __init_refok *pte_alloc_one_kernel(struct mm_struct *mm,
120 unsigned long address)
121{
122 pte_t *pte;
123
124 if (likely(mem_init_done)) {
125 pte = (pte_t *) __get_free_page(GFP_KERNEL | __GFP_REPEAT);
126 } else {
127 pte = (pte_t *) alloc_bootmem_low_pages(PAGE_SIZE);
128#if 0
129 /* FIXME: use memblock... */
130 pte = (pte_t *) __va(memblock_alloc(PAGE_SIZE, PAGE_SIZE));
131#endif
132 }
133
134 if (pte)
135 clear_page(pte);
136 return pte;
137}
diff --git a/arch/openrisc/mm/tlb.c b/arch/openrisc/mm/tlb.c
new file mode 100644
index 000000000000..56b0b89624af
--- /dev/null
+++ b/arch/openrisc/mm/tlb.c
@@ -0,0 +1,193 @@
1/*
2 * OpenRISC tlb.c
3 *
4 * Linux architectural port borrowing liberally from similar works of
5 * others. All original copyrights apply as per the original source
6 * declaration.
7 *
8 * Modifications for the OpenRISC architecture:
9 * Copyright (C) 2003 Matjaz Breskvar <phoenix@bsemi.com>
10 * Copyright (C) 2010-2011 Julius Baxter <julius.baxter@orsoc.se>
11 * Copyright (C) 2010-2011 Jonas Bonn <jonas@southpole.se>
12 *
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License
15 * as published by the Free Software Foundation; either version
16 * 2 of the License, or (at your option) any later version.
17 */
18
19#include <linux/sched.h>
20#include <linux/kernel.h>
21#include <linux/errno.h>
22#include <linux/string.h>
23#include <linux/types.h>
24#include <linux/ptrace.h>
25#include <linux/mman.h>
26#include <linux/mm.h>
27#include <linux/init.h>
28
29#include <asm/system.h>
30#include <asm/segment.h>
31#include <asm/tlbflush.h>
32#include <asm/pgtable.h>
33#include <asm/mmu_context.h>
34#include <asm/spr_defs.h>
35
36#define NO_CONTEXT -1
37
38#define NUM_DTLB_SETS (1 << ((mfspr(SPR_IMMUCFGR) & SPR_IMMUCFGR_NTS) >> \
39 SPR_DMMUCFGR_NTS_OFF))
40#define NUM_ITLB_SETS (1 << ((mfspr(SPR_IMMUCFGR) & SPR_IMMUCFGR_NTS) >> \
41 SPR_IMMUCFGR_NTS_OFF))
42#define DTLB_OFFSET(addr) (((addr) >> PAGE_SHIFT) & (NUM_DTLB_SETS-1))
43#define ITLB_OFFSET(addr) (((addr) >> PAGE_SHIFT) & (NUM_ITLB_SETS-1))
44/*
45 * Invalidate all TLB entries.
46 *
47 * This comes down to setting the 'valid' bit for all xTLBMR registers to 0.
48 * Easiest way to accomplish this is to just zero out the xTLBMR register
49 * completely.
50 *
51 */
52
53void flush_tlb_all(void)
54{
55 int i;
56 unsigned long num_tlb_sets;
57
58 /* Determine number of sets for IMMU. */
59 /* FIXME: Assumption is I & D nsets equal. */
60 num_tlb_sets = NUM_ITLB_SETS;
61
62 for (i = 0; i < num_tlb_sets; i++) {
63 mtspr_off(SPR_DTLBMR_BASE(0), i, 0);
64 mtspr_off(SPR_ITLBMR_BASE(0), i, 0);
65 }
66}
67
68#define have_dtlbeir (mfspr(SPR_DMMUCFGR) & SPR_DMMUCFGR_TEIRI)
69#define have_itlbeir (mfspr(SPR_IMMUCFGR) & SPR_IMMUCFGR_TEIRI)
70
71/*
72 * Invalidate a single page. This is what the xTLBEIR register is for.
73 *
74 * There's no point in checking the vma for PAGE_EXEC to determine whether it's
75 * the data or instruction TLB that should be flushed... that would take more
76 * than the few instructions that the following compiles down to!
77 *
78 * The case where we don't have the xTLBEIR register really only works for
79 * MMU's with a single way and is hard-coded that way.
80 */
81
82#define flush_dtlb_page_eir(addr) mtspr(SPR_DTLBEIR, addr)
83#define flush_dtlb_page_no_eir(addr) \
84 mtspr_off(SPR_DTLBMR_BASE(0), DTLB_OFFSET(addr), 0);
85
86#define flush_itlb_page_eir(addr) mtspr(SPR_ITLBEIR, addr)
87#define flush_itlb_page_no_eir(addr) \
88 mtspr_off(SPR_ITLBMR_BASE(0), ITLB_OFFSET(addr), 0);
89
90void flush_tlb_page(struct vm_area_struct *vma, unsigned long addr)
91{
92 if (have_dtlbeir)
93 flush_dtlb_page_eir(addr);
94 else
95 flush_dtlb_page_no_eir(addr);
96
97 if (have_itlbeir)
98 flush_itlb_page_eir(addr);
99 else
100 flush_itlb_page_no_eir(addr);
101}
102
103void flush_tlb_range(struct vm_area_struct *vma,
104 unsigned long start, unsigned long end)
105{
106 int addr;
107 bool dtlbeir;
108 bool itlbeir;
109
110 dtlbeir = have_dtlbeir;
111 itlbeir = have_itlbeir;
112
113 for (addr = start; addr < end; addr += PAGE_SIZE) {
114 if (dtlbeir)
115 flush_dtlb_page_eir(addr);
116 else
117 flush_dtlb_page_no_eir(addr);
118
119 if (itlbeir)
120 flush_itlb_page_eir(addr);
121 else
122 flush_itlb_page_no_eir(addr);
123 }
124}
125
126/*
127 * Invalidate the selected mm context only.
128 *
129 * FIXME: Due to some bug here, we're flushing everything for now.
130 * This should be changed to loop over over mm and call flush_tlb_range.
131 */
132
133void flush_tlb_mm(struct mm_struct *mm)
134{
135
136 /* Was seeing bugs with the mm struct passed to us. Scrapped most of
137 this function. */
138 /* Several architctures do this */
139 flush_tlb_all();
140}
141
142/* called in schedule() just before actually doing the switch_to */
143
144void switch_mm(struct mm_struct *prev, struct mm_struct *next,
145 struct task_struct *next_tsk)
146{
147 /* remember the pgd for the fault handlers
148 * this is similar to the pgd register in some other CPU's.
149 * we need our own copy of it because current and active_mm
150 * might be invalid at points where we still need to derefer
151 * the pgd.
152 */
153 current_pgd = next->pgd;
154
155 /* We don't have context support implemented, so flush all
156 * entries belonging to previous map
157 */
158
159 if (prev != next)
160 flush_tlb_mm(prev);
161
162}
163
164/*
165 * Initialize the context related info for a new mm_struct
166 * instance.
167 */
168
169int init_new_context(struct task_struct *tsk, struct mm_struct *mm)
170{
171 mm->context = NO_CONTEXT;
172 return 0;
173}
174
175/* called by __exit_mm to destroy the used MMU context if any before
176 * destroying the mm itself. this is only called when the last user of the mm
177 * drops it.
178 */
179
180void destroy_context(struct mm_struct *mm)
181{
182 flush_tlb_mm(mm);
183
184}
185
186/* called once during VM initialization, from init.c */
187
188void __init tlb_init(void)
189{
190 /* Do nothing... */
191 /* invalidate the entire TLB */
192 /* flush_tlb_all(); */
193}