diff options
Diffstat (limited to 'arch/cris/mm')
-rw-r--r-- | arch/cris/mm/Makefile | 6 | ||||
-rw-r--r-- | arch/cris/mm/fault.c | 387 | ||||
-rw-r--r-- | arch/cris/mm/init.c | 225 | ||||
-rw-r--r-- | arch/cris/mm/ioremap.c | 146 | ||||
-rw-r--r-- | arch/cris/mm/tlb.c | 126 |
5 files changed, 890 insertions, 0 deletions
diff --git a/arch/cris/mm/Makefile b/arch/cris/mm/Makefile new file mode 100644 index 000000000000..d3ae08c90b4e --- /dev/null +++ b/arch/cris/mm/Makefile | |||
@@ -0,0 +1,6 @@ | |||
1 | # | ||
2 | # Makefile for the linux cris-specific parts of the memory manager. | ||
3 | # | ||
4 | |||
5 | obj-y := init.o fault.o tlb.o ioremap.o | ||
6 | |||
diff --git a/arch/cris/mm/fault.c b/arch/cris/mm/fault.c new file mode 100644 index 000000000000..03254b9eded1 --- /dev/null +++ b/arch/cris/mm/fault.c | |||
@@ -0,0 +1,387 @@ | |||
1 | /* | ||
2 | * linux/arch/cris/mm/fault.c | ||
3 | * | ||
4 | * Copyright (C) 2000, 2001 Axis Communications AB | ||
5 | * | ||
6 | * Authors: Bjorn Wesen | ||
7 | * | ||
8 | * $Log: fault.c,v $ | ||
9 | * Revision 1.11 2004/05/14 07:58:05 starvik | ||
10 | * Merge of changes from 2.4 | ||
11 | * | ||
12 | * Revision 1.10 2003/10/27 14:51:24 starvik | ||
13 | * Removed debugcode | ||
14 | * | ||
15 | * Revision 1.9 2003/10/27 14:50:42 starvik | ||
16 | * Changed do_page_fault signature | ||
17 | * | ||
18 | * Revision 1.8 2003/07/04 13:02:48 tobiasa | ||
19 | * Moved code snippet from arch/cris/mm/fault.c that searches for fixup code | ||
20 | * to seperate function in arch-specific files. | ||
21 | * | ||
22 | * Revision 1.7 2003/01/22 06:48:38 starvik | ||
23 | * Fixed warnings issued by GCC 3.2.1 | ||
24 | * | ||
25 | * Revision 1.6 2003/01/09 14:42:52 starvik | ||
26 | * Merge of Linux 2.5.55 | ||
27 | * | ||
28 | * Revision 1.5 2002/12/11 14:44:48 starvik | ||
29 | * Extracted v10 (ETRAX 100LX) specific stuff to arch/cris/arch-v10/mm | ||
30 | * | ||
31 | * Revision 1.4 2002/11/13 15:10:28 starvik | ||
32 | * pte_offset has been renamed to pte_offset_kernel | ||
33 | * | ||
34 | * Revision 1.3 2002/11/05 06:45:13 starvik | ||
35 | * Merge of Linux 2.5.45 | ||
36 | * | ||
37 | * Revision 1.2 2001/12/18 13:35:22 bjornw | ||
38 | * Applied the 2.4.13->2.4.16 CRIS patch to 2.5.1 (is a copy of 2.4.15). | ||
39 | * | ||
40 | * Revision 1.20 2001/11/22 13:34:06 bjornw | ||
41 | * * Bug workaround (LX TR89): force a rerun of the whole of an interrupted | ||
42 | * unaligned write, because the second half of the write will be corrupted | ||
43 | * otherwise. Affected unaligned writes spanning not-yet mapped pages. | ||
44 | * * Optimization: use the wr_rd bit in R_MMU_CAUSE to know whether a miss | ||
45 | * was due to a read or a write (before we didn't know this until the next | ||
46 | * restart of the interrupted instruction, thus wasting one fault-irq) | ||
47 | * | ||
48 | * Revision 1.19 2001/11/12 19:02:10 pkj | ||
49 | * Fixed compiler warnings. | ||
50 | * | ||
51 | * Revision 1.18 2001/07/18 22:14:32 bjornw | ||
52 | * Enable interrupts in the bulk of do_page_fault | ||
53 | * | ||
54 | * Revision 1.17 2001/07/18 13:07:23 bjornw | ||
55 | * * Detect non-existant PTE's in vmalloc pmd synchronization | ||
56 | * * Remove comment about fast-paths for VMALLOC_START etc, because all that | ||
57 | * was totally bogus anyway it turned out :) | ||
58 | * * Fix detection of vmalloc-area synchronization | ||
59 | * * Add some comments | ||
60 | * | ||
61 | * Revision 1.16 2001/06/13 00:06:08 bjornw | ||
62 | * current_pgd should be volatile | ||
63 | * | ||
64 | * Revision 1.15 2001/06/13 00:02:23 bjornw | ||
65 | * Use a separate variable to store the current pgd to avoid races in schedule | ||
66 | * | ||
67 | * Revision 1.14 2001/05/16 17:41:07 hp | ||
68 | * Last comment tweak further tweaked. | ||
69 | * | ||
70 | * Revision 1.13 2001/05/15 00:58:44 hp | ||
71 | * Expand a bit on the comment why we compare address >= TASK_SIZE rather | ||
72 | * than >= VMALLOC_START. | ||
73 | * | ||
74 | * Revision 1.12 2001/04/04 10:51:14 bjornw | ||
75 | * mmap_sem is grabbed for reading | ||
76 | * | ||
77 | * Revision 1.11 2001/03/23 07:36:07 starvik | ||
78 | * Corrected according to review remarks | ||
79 | * | ||
80 | * Revision 1.10 2001/03/21 16:10:11 bjornw | ||
81 | * CRIS_FRAME_FIXUP not needed anymore, use FRAME_NORMAL | ||
82 | * | ||
83 | * Revision 1.9 2001/03/05 13:22:20 bjornw | ||
84 | * Spell-fix and fix in vmalloc_fault handling | ||
85 | * | ||
86 | * Revision 1.8 2000/11/22 14:45:31 bjornw | ||
87 | * * 2.4.0-test10 removed the set_pgdir instantaneous kernel global mapping | ||
88 | * into all processes. Instead we fill in the missing PTE entries on demand. | ||
89 | * | ||
90 | * Revision 1.7 2000/11/21 16:39:09 bjornw | ||
91 | * fixup switches frametype | ||
92 | * | ||
93 | * Revision 1.6 2000/11/17 16:54:08 bjornw | ||
94 | * More detailed siginfo reporting | ||
95 | * | ||
96 | * | ||
97 | */ | ||
98 | |||
99 | #include <linux/mm.h> | ||
100 | #include <linux/interrupt.h> | ||
101 | #include <linux/module.h> | ||
102 | #include <asm/uaccess.h> | ||
103 | |||
104 | extern int find_fixup_code(struct pt_regs *); | ||
105 | extern void die_if_kernel(const char *, struct pt_regs *, long); | ||
106 | |||
107 | /* debug of low-level TLB reload */ | ||
108 | #undef DEBUG | ||
109 | |||
110 | #ifdef DEBUG | ||
111 | #define D(x) x | ||
112 | #else | ||
113 | #define D(x) | ||
114 | #endif | ||
115 | |||
116 | /* debug of higher-level faults */ | ||
117 | #define DPG(x) | ||
118 | |||
119 | /* current active page directory */ | ||
120 | |||
121 | volatile pgd_t *current_pgd; | ||
122 | |||
123 | /* | ||
124 | * This routine handles page faults. It determines the address, | ||
125 | * and the problem, and then passes it off to one of the appropriate | ||
126 | * routines. | ||
127 | * | ||
128 | * Notice that the address we're given is aligned to the page the fault | ||
129 | * occurred in, since we only get the PFN in R_MMU_CAUSE not the complete | ||
130 | * address. | ||
131 | * | ||
132 | * error_code: | ||
133 | * bit 0 == 0 means no page found, 1 means protection fault | ||
134 | * bit 1 == 0 means read, 1 means write | ||
135 | * | ||
136 | * If this routine detects a bad access, it returns 1, otherwise it | ||
137 | * returns 0. | ||
138 | */ | ||
139 | |||
140 | asmlinkage void | ||
141 | do_page_fault(unsigned long address, struct pt_regs *regs, | ||
142 | int protection, int writeaccess) | ||
143 | { | ||
144 | struct task_struct *tsk; | ||
145 | struct mm_struct *mm; | ||
146 | struct vm_area_struct * vma; | ||
147 | siginfo_t info; | ||
148 | |||
149 | D(printk("Page fault for %X at %X, prot %d write %d\n", | ||
150 | address, regs->erp, protection, writeaccess)); | ||
151 | |||
152 | tsk = current; | ||
153 | |||
154 | /* | ||
155 | * We fault-in kernel-space virtual memory on-demand. The | ||
156 | * 'reference' page table is init_mm.pgd. | ||
157 | * | ||
158 | * NOTE! We MUST NOT take any locks for this case. We may | ||
159 | * be in an interrupt or a critical region, and should | ||
160 | * only copy the information from the master page table, | ||
161 | * nothing more. | ||
162 | * | ||
163 | * NOTE2: This is done so that, when updating the vmalloc | ||
164 | * mappings we don't have to walk all processes pgdirs and | ||
165 | * add the high mappings all at once. Instead we do it as they | ||
166 | * are used. However vmalloc'ed page entries have the PAGE_GLOBAL | ||
167 | * bit set so sometimes the TLB can use a lingering entry. | ||
168 | * | ||
169 | * This verifies that the fault happens in kernel space | ||
170 | * and that the fault was not a protection error (error_code & 1). | ||
171 | */ | ||
172 | |||
173 | if (address >= VMALLOC_START && | ||
174 | !protection && | ||
175 | !user_mode(regs)) | ||
176 | goto vmalloc_fault; | ||
177 | |||
178 | /* we can and should enable interrupts at this point */ | ||
179 | sti(); | ||
180 | |||
181 | mm = tsk->mm; | ||
182 | info.si_code = SEGV_MAPERR; | ||
183 | |||
184 | /* | ||
185 | * If we're in an interrupt or have no user | ||
186 | * context, we must not take the fault.. | ||
187 | */ | ||
188 | |||
189 | if (in_interrupt() || !mm) | ||
190 | goto no_context; | ||
191 | |||
192 | down_read(&mm->mmap_sem); | ||
193 | vma = find_vma(mm, address); | ||
194 | if (!vma) | ||
195 | goto bad_area; | ||
196 | if (vma->vm_start <= address) | ||
197 | goto good_area; | ||
198 | if (!(vma->vm_flags & VM_GROWSDOWN)) | ||
199 | goto bad_area; | ||
200 | if (user_mode(regs)) { | ||
201 | /* | ||
202 | * accessing the stack below usp is always a bug. | ||
203 | * we get page-aligned addresses so we can only check | ||
204 | * if we're within a page from usp, but that might be | ||
205 | * enough to catch brutal errors at least. | ||
206 | */ | ||
207 | if (address + PAGE_SIZE < rdusp()) | ||
208 | goto bad_area; | ||
209 | } | ||
210 | if (expand_stack(vma, address)) | ||
211 | goto bad_area; | ||
212 | |||
213 | /* | ||
214 | * Ok, we have a good vm_area for this memory access, so | ||
215 | * we can handle it.. | ||
216 | */ | ||
217 | |||
218 | good_area: | ||
219 | info.si_code = SEGV_ACCERR; | ||
220 | |||
221 | /* first do some preliminary protection checks */ | ||
222 | |||
223 | if (writeaccess) { | ||
224 | if (!(vma->vm_flags & VM_WRITE)) | ||
225 | goto bad_area; | ||
226 | } else { | ||
227 | if (!(vma->vm_flags & (VM_READ | VM_EXEC))) | ||
228 | goto bad_area; | ||
229 | } | ||
230 | |||
231 | /* | ||
232 | * If for any reason at all we couldn't handle the fault, | ||
233 | * make sure we exit gracefully rather than endlessly redo | ||
234 | * the fault. | ||
235 | */ | ||
236 | |||
237 | switch (handle_mm_fault(mm, vma, address, writeaccess)) { | ||
238 | case 1: | ||
239 | tsk->min_flt++; | ||
240 | break; | ||
241 | case 2: | ||
242 | tsk->maj_flt++; | ||
243 | break; | ||
244 | case 0: | ||
245 | goto do_sigbus; | ||
246 | default: | ||
247 | goto out_of_memory; | ||
248 | } | ||
249 | |||
250 | up_read(&mm->mmap_sem); | ||
251 | return; | ||
252 | |||
253 | /* | ||
254 | * Something tried to access memory that isn't in our memory map.. | ||
255 | * Fix it, but check if it's kernel or user first.. | ||
256 | */ | ||
257 | |||
258 | bad_area: | ||
259 | up_read(&mm->mmap_sem); | ||
260 | |||
261 | bad_area_nosemaphore: | ||
262 | DPG(show_registers(regs)); | ||
263 | |||
264 | /* User mode accesses just cause a SIGSEGV */ | ||
265 | |||
266 | if (user_mode(regs)) { | ||
267 | info.si_signo = SIGSEGV; | ||
268 | info.si_errno = 0; | ||
269 | /* info.si_code has been set above */ | ||
270 | info.si_addr = (void *)address; | ||
271 | force_sig_info(SIGSEGV, &info, tsk); | ||
272 | return; | ||
273 | } | ||
274 | |||
275 | no_context: | ||
276 | |||
277 | /* Are we prepared to handle this kernel fault? | ||
278 | * | ||
279 | * (The kernel has valid exception-points in the source | ||
280 | * when it acesses user-memory. When it fails in one | ||
281 | * of those points, we find it in a table and do a jump | ||
282 | * to some fixup code that loads an appropriate error | ||
283 | * code) | ||
284 | */ | ||
285 | |||
286 | if (find_fixup_code(regs)) | ||
287 | return; | ||
288 | |||
289 | /* | ||
290 | * Oops. The kernel tried to access some bad page. We'll have to | ||
291 | * terminate things with extreme prejudice. | ||
292 | */ | ||
293 | |||
294 | if ((unsigned long) (address) < PAGE_SIZE) | ||
295 | printk(KERN_ALERT "Unable to handle kernel NULL pointer dereference"); | ||
296 | else | ||
297 | printk(KERN_ALERT "Unable to handle kernel access"); | ||
298 | printk(" at virtual address %08lx\n",address); | ||
299 | |||
300 | die_if_kernel("Oops", regs, (writeaccess << 1) | protection); | ||
301 | |||
302 | do_exit(SIGKILL); | ||
303 | |||
304 | /* | ||
305 | * We ran out of memory, or some other thing happened to us that made | ||
306 | * us unable to handle the page fault gracefully. | ||
307 | */ | ||
308 | |||
309 | out_of_memory: | ||
310 | up_read(&mm->mmap_sem); | ||
311 | printk("VM: killing process %s\n", tsk->comm); | ||
312 | if (user_mode(regs)) | ||
313 | do_exit(SIGKILL); | ||
314 | goto no_context; | ||
315 | |||
316 | do_sigbus: | ||
317 | up_read(&mm->mmap_sem); | ||
318 | |||
319 | /* | ||
320 | * Send a sigbus, regardless of whether we were in kernel | ||
321 | * or user mode. | ||
322 | */ | ||
323 | info.si_signo = SIGBUS; | ||
324 | info.si_errno = 0; | ||
325 | info.si_code = BUS_ADRERR; | ||
326 | info.si_addr = (void *)address; | ||
327 | force_sig_info(SIGBUS, &info, tsk); | ||
328 | |||
329 | /* Kernel mode? Handle exceptions or die */ | ||
330 | if (!user_mode(regs)) | ||
331 | goto no_context; | ||
332 | return; | ||
333 | |||
334 | vmalloc_fault: | ||
335 | { | ||
336 | /* | ||
337 | * Synchronize this task's top level page-table | ||
338 | * with the 'reference' page table. | ||
339 | * | ||
340 | * Use current_pgd instead of tsk->active_mm->pgd | ||
341 | * since the latter might be unavailable if this | ||
342 | * code is executed in a misfortunately run irq | ||
343 | * (like inside schedule() between switch_mm and | ||
344 | * switch_to...). | ||
345 | */ | ||
346 | |||
347 | int offset = pgd_index(address); | ||
348 | pgd_t *pgd, *pgd_k; | ||
349 | pmd_t *pmd, *pmd_k; | ||
350 | pte_t *pte_k; | ||
351 | |||
352 | pgd = (pgd_t *)current_pgd + offset; | ||
353 | pgd_k = init_mm.pgd + offset; | ||
354 | |||
355 | /* Since we're two-level, we don't need to do both | ||
356 | * set_pgd and set_pmd (they do the same thing). If | ||
357 | * we go three-level at some point, do the right thing | ||
358 | * with pgd_present and set_pgd here. | ||
359 | * | ||
360 | * Also, since the vmalloc area is global, we don't | ||
361 | * need to copy individual PTE's, it is enough to | ||
362 | * copy the pgd pointer into the pte page of the | ||
363 | * root task. If that is there, we'll find our pte if | ||
364 | * it exists. | ||
365 | */ | ||
366 | |||
367 | pmd = pmd_offset(pgd, address); | ||
368 | pmd_k = pmd_offset(pgd_k, address); | ||
369 | |||
370 | if (!pmd_present(*pmd_k)) | ||
371 | goto bad_area_nosemaphore; | ||
372 | |||
373 | set_pmd(pmd, *pmd_k); | ||
374 | |||
375 | /* Make sure the actual PTE exists as well to | ||
376 | * catch kernel vmalloc-area accesses to non-mapped | ||
377 | * addresses. If we don't do this, this will just | ||
378 | * silently loop forever. | ||
379 | */ | ||
380 | |||
381 | pte_k = pte_offset_kernel(pmd_k, address); | ||
382 | if (!pte_present(*pte_k)) | ||
383 | goto no_context; | ||
384 | |||
385 | return; | ||
386 | } | ||
387 | } | ||
diff --git a/arch/cris/mm/init.c b/arch/cris/mm/init.c new file mode 100644 index 000000000000..31a0018b525a --- /dev/null +++ b/arch/cris/mm/init.c | |||
@@ -0,0 +1,225 @@ | |||
1 | /* | ||
2 | * linux/arch/cris/mm/init.c | ||
3 | * | ||
4 | * Copyright (C) 1995 Linus Torvalds | ||
5 | * Copyright (C) 2000,2001 Axis Communications AB | ||
6 | * | ||
7 | * Authors: Bjorn Wesen (bjornw@axis.com) | ||
8 | * | ||
9 | * $Log: init.c,v $ | ||
10 | * Revision 1.11 2004/05/28 09:28:56 starvik | ||
11 | * Calculation of loops_per_usec moved because initalization order has changed | ||
12 | * in Linux 2.6. | ||
13 | * | ||
14 | * Revision 1.10 2004/05/14 07:58:05 starvik | ||
15 | * Merge of changes from 2.4 | ||
16 | * | ||
17 | * Revision 1.9 2003/07/04 08:27:54 starvik | ||
18 | * Merge of Linux 2.5.74 | ||
19 | * | ||
20 | * Revision 1.8 2003/04/09 05:20:48 starvik | ||
21 | * Merge of Linux 2.5.67 | ||
22 | * | ||
23 | * Revision 1.7 2003/01/22 06:48:38 starvik | ||
24 | * Fixed warnings issued by GCC 3.2.1 | ||
25 | * | ||
26 | * Revision 1.6 2002/12/11 14:44:48 starvik | ||
27 | * Extracted v10 (ETRAX 100LX) specific stuff to arch/cris/arch-v10/mm | ||
28 | * | ||
29 | * Revision 1.5 2002/11/18 07:37:37 starvik | ||
30 | * Added cache bug workaround (from Linux 2.4) | ||
31 | * | ||
32 | * Revision 1.4 2002/11/13 15:40:24 starvik | ||
33 | * Removed the page table cache stuff (as done in other archs) | ||
34 | * | ||
35 | * Revision 1.3 2002/11/05 06:45:13 starvik | ||
36 | * Merge of Linux 2.5.45 | ||
37 | * | ||
38 | * Revision 1.2 2001/12/18 13:35:22 bjornw | ||
39 | * Applied the 2.4.13->2.4.16 CRIS patch to 2.5.1 (is a copy of 2.4.15). | ||
40 | * | ||
41 | * Revision 1.31 2001/11/13 16:22:00 bjornw | ||
42 | * Skip calculating totalram and sharedram in si_meminfo | ||
43 | * | ||
44 | * Revision 1.30 2001/11/12 19:02:10 pkj | ||
45 | * Fixed compiler warnings. | ||
46 | * | ||
47 | * Revision 1.29 2001/07/25 16:09:50 bjornw | ||
48 | * val->sharedram will stay 0 | ||
49 | * | ||
50 | * Revision 1.28 2001/06/28 16:30:17 bjornw | ||
51 | * Oops. This needs to wait until 2.4.6 is merged | ||
52 | * | ||
53 | * Revision 1.27 2001/06/28 14:04:07 bjornw | ||
54 | * Fill in sharedram | ||
55 | * | ||
56 | * Revision 1.26 2001/06/18 06:36:02 hp | ||
57 | * Enable free_initmem of __init-type pages | ||
58 | * | ||
59 | * Revision 1.25 2001/06/13 00:02:23 bjornw | ||
60 | * Use a separate variable to store the current pgd to avoid races in schedule | ||
61 | * | ||
62 | * Revision 1.24 2001/05/15 00:52:20 hp | ||
63 | * Only map segment 0xa as seg if CONFIG_JULIETTE | ||
64 | * | ||
65 | * Revision 1.23 2001/04/04 14:35:40 bjornw | ||
66 | * * Removed get_pte_slow and friends (2.4.3 change) | ||
67 | * * Removed bad_pmd handling (2.4.3 change) | ||
68 | * | ||
69 | * Revision 1.22 2001/04/04 13:38:04 matsfg | ||
70 | * Moved ioremap to a separate function instead | ||
71 | * | ||
72 | * Revision 1.21 2001/03/27 09:28:33 bjornw | ||
73 | * ioremap used too early - lets try it in mem_init instead | ||
74 | * | ||
75 | * Revision 1.20 2001/03/23 07:39:21 starvik | ||
76 | * Corrected according to review remarks | ||
77 | * | ||
78 | * Revision 1.19 2001/03/15 14:25:17 bjornw | ||
79 | * More general shadow registers and ioremaped addresses for external I/O | ||
80 | * | ||
81 | * Revision 1.18 2001/02/23 12:46:44 bjornw | ||
82 | * * 0xc was not CSE1; 0x8 is, same as uncached flash, so we move the uncached | ||
83 | * flash during CRIS_LOW_MAP from 0xe to 0x8 so both the flash and the I/O | ||
84 | * is mapped straight over (for !CRIS_LOW_MAP the uncached flash is still 0xe) | ||
85 | * | ||
86 | * Revision 1.17 2001/02/22 15:05:21 bjornw | ||
87 | * Map 0x9 straight over during LOW_MAP to allow for memory mapped LEDs | ||
88 | * | ||
89 | * Revision 1.16 2001/02/22 15:02:35 bjornw | ||
90 | * Map 0xc straight over during LOW_MAP to allow for memory mapped I/O | ||
91 | * | ||
92 | * Revision 1.15 2001/01/10 21:12:10 bjornw | ||
93 | * loops_per_sec -> loops_per_jiffy | ||
94 | * | ||
95 | * Revision 1.14 2000/11/22 16:23:20 bjornw | ||
96 | * Initialize totalhigh counters to 0 to make /proc/meminfo look nice. | ||
97 | * | ||
98 | * Revision 1.13 2000/11/21 16:37:51 bjornw | ||
99 | * Temporarily disable initmem freeing | ||
100 | * | ||
101 | * Revision 1.12 2000/11/21 13:55:07 bjornw | ||
102 | * Use CONFIG_CRIS_LOW_MAP for the low VM map instead of explicit CPU type | ||
103 | * | ||
104 | * Revision 1.11 2000/10/06 12:38:22 bjornw | ||
105 | * Cast empty_bad_page correctly (should really be of * type from the start.. | ||
106 | * | ||
107 | * Revision 1.10 2000/10/04 16:53:57 bjornw | ||
108 | * Fix memory-map due to LX features | ||
109 | * | ||
110 | * Revision 1.9 2000/09/13 15:47:49 bjornw | ||
111 | * Wrong count in reserved-pages loop | ||
112 | * | ||
113 | * Revision 1.8 2000/09/13 14:35:10 bjornw | ||
114 | * 2.4.0-test8 added a new arg to free_area_init_node | ||
115 | * | ||
116 | * Revision 1.7 2000/08/17 15:35:55 bjornw | ||
117 | * 2.4.0-test6 removed MAP_NR and inserted virt_to_page | ||
118 | * | ||
119 | * | ||
120 | */ | ||
121 | |||
122 | #include <linux/init.h> | ||
123 | #include <linux/bootmem.h> | ||
124 | #include <asm/tlb.h> | ||
125 | |||
126 | DEFINE_PER_CPU(struct mmu_gather, mmu_gathers); | ||
127 | |||
128 | unsigned long empty_zero_page; | ||
129 | |||
130 | extern char _stext, _edata, _etext; /* From linkerscript */ | ||
131 | extern char __init_begin, __init_end; | ||
132 | |||
133 | void | ||
134 | show_mem(void) | ||
135 | { | ||
136 | int i,free = 0,total = 0,cached = 0, reserved = 0, nonshared = 0; | ||
137 | int shared = 0; | ||
138 | |||
139 | printk("\nMem-info:\n"); | ||
140 | show_free_areas(); | ||
141 | printk("Free swap: %6ldkB\n", nr_swap_pages<<(PAGE_SHIFT-10)); | ||
142 | i = max_mapnr; | ||
143 | while (i-- > 0) { | ||
144 | total++; | ||
145 | if (PageReserved(mem_map+i)) | ||
146 | reserved++; | ||
147 | else if (PageSwapCache(mem_map+i)) | ||
148 | cached++; | ||
149 | else if (!page_count(mem_map+i)) | ||
150 | free++; | ||
151 | else if (page_count(mem_map+i) == 1) | ||
152 | nonshared++; | ||
153 | else | ||
154 | shared += page_count(mem_map+i) - 1; | ||
155 | } | ||
156 | printk("%d pages of RAM\n",total); | ||
157 | printk("%d free pages\n",free); | ||
158 | printk("%d reserved pages\n",reserved); | ||
159 | printk("%d pages nonshared\n",nonshared); | ||
160 | printk("%d pages shared\n",shared); | ||
161 | printk("%d pages swap cached\n",cached); | ||
162 | } | ||
163 | |||
164 | void __init | ||
165 | mem_init(void) | ||
166 | { | ||
167 | int codesize, reservedpages, datasize, initsize; | ||
168 | unsigned long tmp; | ||
169 | |||
170 | if(!mem_map) | ||
171 | BUG(); | ||
172 | |||
173 | /* max/min_low_pfn was set by setup.c | ||
174 | * now we just copy it to some other necessary places... | ||
175 | * | ||
176 | * high_memory was also set in setup.c | ||
177 | */ | ||
178 | |||
179 | max_mapnr = num_physpages = max_low_pfn - min_low_pfn; | ||
180 | |||
181 | /* this will put all memory onto the freelists */ | ||
182 | totalram_pages = free_all_bootmem(); | ||
183 | |||
184 | reservedpages = 0; | ||
185 | for (tmp = 0; tmp < max_mapnr; tmp++) { | ||
186 | /* | ||
187 | * Only count reserved RAM pages | ||
188 | */ | ||
189 | if (PageReserved(mem_map + tmp)) | ||
190 | reservedpages++; | ||
191 | } | ||
192 | |||
193 | codesize = (unsigned long) &_etext - (unsigned long) &_stext; | ||
194 | datasize = (unsigned long) &_edata - (unsigned long) &_etext; | ||
195 | initsize = (unsigned long) &__init_end - (unsigned long) &__init_begin; | ||
196 | |||
197 | printk(KERN_INFO | ||
198 | "Memory: %luk/%luk available (%dk kernel code, %dk reserved, %dk data, " | ||
199 | "%dk init)\n" , | ||
200 | (unsigned long) nr_free_pages() << (PAGE_SHIFT-10), | ||
201 | max_mapnr << (PAGE_SHIFT-10), | ||
202 | codesize >> 10, | ||
203 | reservedpages << (PAGE_SHIFT-10), | ||
204 | datasize >> 10, | ||
205 | initsize >> 10 | ||
206 | ); | ||
207 | } | ||
208 | |||
209 | /* free the pages occupied by initialization code */ | ||
210 | |||
211 | void | ||
212 | free_initmem(void) | ||
213 | { | ||
214 | unsigned long addr; | ||
215 | |||
216 | addr = (unsigned long)(&__init_begin); | ||
217 | for (; addr < (unsigned long)(&__init_end); addr += PAGE_SIZE) { | ||
218 | ClearPageReserved(virt_to_page(addr)); | ||
219 | set_page_count(virt_to_page(addr), 1); | ||
220 | free_page(addr); | ||
221 | totalram_pages++; | ||
222 | } | ||
223 | printk (KERN_INFO "Freeing unused kernel memory: %luk freed\n", | ||
224 | (unsigned long)((&__init_end - &__init_begin) >> 10)); | ||
225 | } | ||
diff --git a/arch/cris/mm/ioremap.c b/arch/cris/mm/ioremap.c new file mode 100644 index 000000000000..6b9130bfb6c1 --- /dev/null +++ b/arch/cris/mm/ioremap.c | |||
@@ -0,0 +1,146 @@ | |||
1 | /* | ||
2 | * arch/cris/mm/ioremap.c | ||
3 | * | ||
4 | * Re-map IO memory to kernel address space so that we can access it. | ||
5 | * Needed for memory-mapped I/O devices mapped outside our normal DRAM | ||
6 | * window (that is, all memory-mapped I/O devices). | ||
7 | * | ||
8 | * (C) Copyright 1995 1996 Linus Torvalds | ||
9 | * CRIS-port by Axis Communications AB | ||
10 | */ | ||
11 | |||
12 | #include <linux/vmalloc.h> | ||
13 | #include <asm/io.h> | ||
14 | #include <asm/pgalloc.h> | ||
15 | #include <asm/cacheflush.h> | ||
16 | #include <asm/tlbflush.h> | ||
17 | |||
18 | extern inline void remap_area_pte(pte_t * pte, unsigned long address, unsigned long size, | ||
19 | unsigned long phys_addr, unsigned long flags) | ||
20 | { | ||
21 | unsigned long end; | ||
22 | |||
23 | address &= ~PMD_MASK; | ||
24 | end = address + size; | ||
25 | if (end > PMD_SIZE) | ||
26 | end = PMD_SIZE; | ||
27 | if (address >= end) | ||
28 | BUG(); | ||
29 | do { | ||
30 | if (!pte_none(*pte)) { | ||
31 | printk("remap_area_pte: page already exists\n"); | ||
32 | BUG(); | ||
33 | } | ||
34 | set_pte(pte, mk_pte_phys(phys_addr, __pgprot(_PAGE_PRESENT | __READABLE | | ||
35 | __WRITEABLE | _PAGE_GLOBAL | | ||
36 | _PAGE_KERNEL | flags))); | ||
37 | address += PAGE_SIZE; | ||
38 | phys_addr += PAGE_SIZE; | ||
39 | pte++; | ||
40 | } while (address && (address < end)); | ||
41 | } | ||
42 | |||
43 | static inline int remap_area_pmd(pmd_t * pmd, unsigned long address, unsigned long size, | ||
44 | unsigned long phys_addr, unsigned long flags) | ||
45 | { | ||
46 | unsigned long end; | ||
47 | |||
48 | address &= ~PGDIR_MASK; | ||
49 | end = address + size; | ||
50 | if (end > PGDIR_SIZE) | ||
51 | end = PGDIR_SIZE; | ||
52 | phys_addr -= address; | ||
53 | if (address >= end) | ||
54 | BUG(); | ||
55 | do { | ||
56 | pte_t * pte = pte_alloc_kernel(&init_mm, pmd, address); | ||
57 | if (!pte) | ||
58 | return -ENOMEM; | ||
59 | remap_area_pte(pte, address, end - address, address + phys_addr, flags); | ||
60 | address = (address + PMD_SIZE) & PMD_MASK; | ||
61 | pmd++; | ||
62 | } while (address && (address < end)); | ||
63 | return 0; | ||
64 | } | ||
65 | |||
66 | static int remap_area_pages(unsigned long address, unsigned long phys_addr, | ||
67 | unsigned long size, unsigned long flags) | ||
68 | { | ||
69 | int error; | ||
70 | pgd_t * dir; | ||
71 | unsigned long end = address + size; | ||
72 | |||
73 | phys_addr -= address; | ||
74 | dir = pgd_offset(&init_mm, address); | ||
75 | flush_cache_all(); | ||
76 | if (address >= end) | ||
77 | BUG(); | ||
78 | spin_lock(&init_mm.page_table_lock); | ||
79 | do { | ||
80 | pmd_t *pmd; | ||
81 | pmd = pmd_alloc(&init_mm, dir, address); | ||
82 | error = -ENOMEM; | ||
83 | if (!pmd) | ||
84 | break; | ||
85 | if (remap_area_pmd(pmd, address, end - address, | ||
86 | phys_addr + address, flags)) | ||
87 | break; | ||
88 | error = 0; | ||
89 | address = (address + PGDIR_SIZE) & PGDIR_MASK; | ||
90 | dir++; | ||
91 | } while (address && (address < end)); | ||
92 | spin_unlock(&init_mm.page_table_lock); | ||
93 | flush_tlb_all(); | ||
94 | return error; | ||
95 | } | ||
96 | |||
97 | /* | ||
98 | * Generic mapping function (not visible outside): | ||
99 | */ | ||
100 | |||
101 | /* | ||
102 | * Remap an arbitrary physical address space into the kernel virtual | ||
103 | * address space. Needed when the kernel wants to access high addresses | ||
104 | * directly. | ||
105 | * | ||
106 | * NOTE! We need to allow non-page-aligned mappings too: we will obviously | ||
107 | * have to convert them into an offset in a page-aligned mapping, but the | ||
108 | * caller shouldn't need to know that small detail. | ||
109 | */ | ||
110 | void * __ioremap(unsigned long phys_addr, unsigned long size, unsigned long flags) | ||
111 | { | ||
112 | void * addr; | ||
113 | struct vm_struct * area; | ||
114 | unsigned long offset, last_addr; | ||
115 | |||
116 | /* Don't allow wraparound or zero size */ | ||
117 | last_addr = phys_addr + size - 1; | ||
118 | if (!size || last_addr < phys_addr) | ||
119 | return NULL; | ||
120 | |||
121 | /* | ||
122 | * Mappings have to be page-aligned | ||
123 | */ | ||
124 | offset = phys_addr & ~PAGE_MASK; | ||
125 | phys_addr &= PAGE_MASK; | ||
126 | size = PAGE_ALIGN(last_addr+1) - phys_addr; | ||
127 | |||
128 | /* | ||
129 | * Ok, go for it.. | ||
130 | */ | ||
131 | area = get_vm_area(size, VM_IOREMAP); | ||
132 | if (!area) | ||
133 | return NULL; | ||
134 | addr = area->addr; | ||
135 | if (remap_area_pages((unsigned long) addr, phys_addr, size, flags)) { | ||
136 | vfree(addr); | ||
137 | return NULL; | ||
138 | } | ||
139 | return (void *) (offset + (char *)addr); | ||
140 | } | ||
141 | |||
142 | void iounmap(void *addr) | ||
143 | { | ||
144 | if (addr > high_memory) | ||
145 | return vfree((void *) (PAGE_MASK & (unsigned long) addr)); | ||
146 | } | ||
diff --git a/arch/cris/mm/tlb.c b/arch/cris/mm/tlb.c new file mode 100644 index 000000000000..23eca5ad7389 --- /dev/null +++ b/arch/cris/mm/tlb.c | |||
@@ -0,0 +1,126 @@ | |||
1 | /* | ||
2 | * linux/arch/cris/mm/tlb.c | ||
3 | * | ||
4 | * Copyright (C) 2000, 2001 Axis Communications AB | ||
5 | * | ||
6 | * Authors: Bjorn Wesen (bjornw@axis.com) | ||
7 | * | ||
8 | */ | ||
9 | |||
10 | #include <linux/init.h> | ||
11 | #include <asm/tlb.h> | ||
12 | |||
13 | #define D(x) | ||
14 | |||
15 | /* The TLB can host up to 64 different mm contexts at the same time. | ||
16 | * The running context is R_MMU_CONTEXT, and each TLB entry contains a | ||
17 | * page_id that has to match to give a hit. In page_id_map, we keep track | ||
18 | * of which mm's we have assigned which page_id's, so that we know when | ||
19 | * to invalidate TLB entries. | ||
20 | * | ||
21 | * The last page_id is never running - it is used as an invalid page_id | ||
22 | * so we can make TLB entries that will never match. | ||
23 | * | ||
24 | * Notice that we need to make the flushes atomic, otherwise an interrupt | ||
25 | * handler that uses vmalloced memory might cause a TLB load in the middle | ||
26 | * of a flush causing. | ||
27 | */ | ||
28 | |||
29 | struct mm_struct *page_id_map[NUM_PAGEID]; | ||
30 | static int map_replace_ptr = 1; /* which page_id_map entry to replace next */ | ||
31 | |||
32 | /* | ||
33 | * Initialize the context related info for a new mm_struct | ||
34 | * instance. | ||
35 | */ | ||
36 | |||
37 | int | ||
38 | init_new_context(struct task_struct *tsk, struct mm_struct *mm) | ||
39 | { | ||
40 | mm->context = NO_CONTEXT; | ||
41 | return 0; | ||
42 | } | ||
43 | |||
44 | /* the following functions are similar to those used in the PPC port */ | ||
45 | |||
46 | static inline void | ||
47 | alloc_context(struct mm_struct *mm) | ||
48 | { | ||
49 | struct mm_struct *old_mm; | ||
50 | |||
51 | D(printk("tlb: alloc context %d (%p)\n", map_replace_ptr, mm)); | ||
52 | |||
53 | /* did we replace an mm ? */ | ||
54 | |||
55 | old_mm = page_id_map[map_replace_ptr]; | ||
56 | |||
57 | if(old_mm) { | ||
58 | /* throw out any TLB entries belonging to the mm we replace | ||
59 | * in the map | ||
60 | */ | ||
61 | flush_tlb_mm(old_mm); | ||
62 | |||
63 | old_mm->context = NO_CONTEXT; | ||
64 | } | ||
65 | |||
66 | /* insert it into the page_id_map */ | ||
67 | |||
68 | mm->context = map_replace_ptr; | ||
69 | page_id_map[map_replace_ptr] = mm; | ||
70 | |||
71 | map_replace_ptr++; | ||
72 | |||
73 | if(map_replace_ptr == INVALID_PAGEID) | ||
74 | map_replace_ptr = 0; /* wrap around */ | ||
75 | } | ||
76 | |||
77 | /* | ||
78 | * if needed, get a new MMU context for the mm. otherwise nothing is done. | ||
79 | */ | ||
80 | |||
81 | void | ||
82 | get_mmu_context(struct mm_struct *mm) | ||
83 | { | ||
84 | if(mm->context == NO_CONTEXT) | ||
85 | alloc_context(mm); | ||
86 | } | ||
87 | |||
88 | /* called by __exit_mm to destroy the used MMU context if any before | ||
89 | * destroying the mm itself. this is only called when the last user of the mm | ||
90 | * drops it. | ||
91 | * | ||
92 | * the only thing we really need to do here is mark the used PID slot | ||
93 | * as empty. | ||
94 | */ | ||
95 | |||
96 | void | ||
97 | destroy_context(struct mm_struct *mm) | ||
98 | { | ||
99 | if(mm->context != NO_CONTEXT) { | ||
100 | D(printk("destroy_context %d (%p)\n", mm->context, mm)); | ||
101 | flush_tlb_mm(mm); /* TODO this might be redundant ? */ | ||
102 | page_id_map[mm->context] = NULL; | ||
103 | /* mm->context = NO_CONTEXT; redundant.. mm will be freed */ | ||
104 | } | ||
105 | } | ||
106 | |||
107 | /* called once during VM initialization, from init.c */ | ||
108 | |||
109 | void __init | ||
110 | tlb_init(void) | ||
111 | { | ||
112 | int i; | ||
113 | |||
114 | /* clear the page_id map */ | ||
115 | |||
116 | for (i = 1; i < sizeof (page_id_map) / sizeof (page_id_map[0]); i++) | ||
117 | page_id_map[i] = NULL; | ||
118 | |||
119 | /* invalidate the entire TLB */ | ||
120 | |||
121 | flush_tlb_all(); | ||
122 | |||
123 | /* the init_mm has context 0 from the boot */ | ||
124 | |||
125 | page_id_map[0] = &init_mm; | ||
126 | } | ||