diff options
author | Mikael Starvik <mikael.starvik@axis.com> | 2005-07-27 14:44:39 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2005-07-27 19:26:00 -0400 |
commit | 4f18cfbf0990bfc2e8e7706eeb9e5bef898ae923 (patch) | |
tree | b6bd033aac034e5d3cb83be8efb03506135866b2 /arch | |
parent | 7cf32cad153d63ac4f6f2d5dd16ddd32ad72d578 (diff) |
[PATCH] CRIS update: mm
Memory management patches.
* SMP support.
* Non-executable stack (on v32).
* 4-level page tables.
* Added simple Thread Local Storage support.
Signed-off-by: Mikael Starvik <starvik@axis.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'arch')
-rw-r--r-- | arch/cris/mm/fault.c | 95 | ||||
-rw-r--r-- | arch/cris/mm/ioremap.c | 58 | ||||
-rw-r--r-- | arch/cris/mm/tlb.c | 25 |
3 files changed, 131 insertions, 47 deletions
diff --git a/arch/cris/mm/fault.c b/arch/cris/mm/fault.c index 03254b9eded1..fe1cc36b5aca 100644 --- a/arch/cris/mm/fault.c +++ b/arch/cris/mm/fault.c | |||
@@ -6,6 +6,38 @@ | |||
6 | * Authors: Bjorn Wesen | 6 | * Authors: Bjorn Wesen |
7 | * | 7 | * |
8 | * $Log: fault.c,v $ | 8 | * $Log: fault.c,v $ |
9 | * Revision 1.20 2005/03/04 08:16:18 starvik | ||
10 | * Merge of Linux 2.6.11. | ||
11 | * | ||
12 | * Revision 1.19 2005/01/14 10:07:59 starvik | ||
13 | * Fixed warning. | ||
14 | * | ||
15 | * Revision 1.18 2005/01/12 08:10:14 starvik | ||
16 | * Readded the change of frametype when handling kernel page fault fixup | ||
17 | * for v10. This is necessary to avoid that the CPU remakes the faulting | ||
18 | * access. | ||
19 | * | ||
20 | * Revision 1.17 2005/01/11 13:53:05 starvik | ||
21 | * Use raw_printk. | ||
22 | * | ||
23 | * Revision 1.16 2004/12/17 11:39:41 starvik | ||
24 | * SMP support. | ||
25 | * | ||
26 | * Revision 1.15 2004/11/23 18:36:18 starvik | ||
27 | * Stack is now non-executable. | ||
28 | * Signal handler trampolines are placed in a reserved page mapped into all | ||
29 | * processes. | ||
30 | * | ||
31 | * Revision 1.14 2004/11/23 07:10:21 starvik | ||
32 | * Moved find_fixup_code to generic code. | ||
33 | * | ||
34 | * Revision 1.13 2004/11/23 07:00:54 starvik | ||
35 | * Actually use the execute permission bit in the MMU. This makes it possible | ||
36 | * to prevent e.g. attacks where executable code is put on the stack. | ||
37 | * | ||
38 | * Revision 1.12 2004/09/29 06:16:04 starvik | ||
39 | * Use instruction_pointer | ||
40 | * | ||
9 | * Revision 1.11 2004/05/14 07:58:05 starvik | 41 | * Revision 1.11 2004/05/14 07:58:05 starvik |
10 | * Merge of changes from 2.4 | 42 | * Merge of changes from 2.4 |
11 | * | 43 | * |
@@ -103,6 +135,7 @@ | |||
103 | 135 | ||
104 | extern int find_fixup_code(struct pt_regs *); | 136 | extern int find_fixup_code(struct pt_regs *); |
105 | extern void die_if_kernel(const char *, struct pt_regs *, long); | 137 | extern void die_if_kernel(const char *, struct pt_regs *, long); |
138 | extern int raw_printk(const char *fmt, ...); | ||
106 | 139 | ||
107 | /* debug of low-level TLB reload */ | 140 | /* debug of low-level TLB reload */ |
108 | #undef DEBUG | 141 | #undef DEBUG |
@@ -118,7 +151,8 @@ extern void die_if_kernel(const char *, struct pt_regs *, long); | |||
118 | 151 | ||
119 | /* current active page directory */ | 152 | /* current active page directory */ |
120 | 153 | ||
121 | volatile pgd_t *current_pgd; | 154 | volatile DEFINE_PER_CPU(pgd_t *,current_pgd); |
155 | unsigned long cris_signal_return_page; | ||
122 | 156 | ||
123 | /* | 157 | /* |
124 | * This routine handles page faults. It determines the address, | 158 | * This routine handles page faults. It determines the address, |
@@ -146,8 +180,9 @@ do_page_fault(unsigned long address, struct pt_regs *regs, | |||
146 | struct vm_area_struct * vma; | 180 | struct vm_area_struct * vma; |
147 | siginfo_t info; | 181 | siginfo_t info; |
148 | 182 | ||
149 | D(printk("Page fault for %X at %X, prot %d write %d\n", | 183 | D(printk("Page fault for %lX on %X at %lX, prot %d write %d\n", |
150 | address, regs->erp, protection, writeaccess)); | 184 | address, smp_processor_id(), instruction_pointer(regs), |
185 | protection, writeaccess)); | ||
151 | 186 | ||
152 | tsk = current; | 187 | tsk = current; |
153 | 188 | ||
@@ -175,8 +210,19 @@ do_page_fault(unsigned long address, struct pt_regs *regs, | |||
175 | !user_mode(regs)) | 210 | !user_mode(regs)) |
176 | goto vmalloc_fault; | 211 | goto vmalloc_fault; |
177 | 212 | ||
213 | /* When stack execution is not allowed we store the signal | ||
214 | * trampolines in the reserved cris_signal_return_page. | ||
215 | * Handle this in the exact same way as vmalloc (we know | ||
216 | * that the mapping is there and is valid so no need to | ||
217 | * call handle_mm_fault). | ||
218 | */ | ||
219 | if (cris_signal_return_page && | ||
220 | address == cris_signal_return_page && | ||
221 | !protection && user_mode(regs)) | ||
222 | goto vmalloc_fault; | ||
223 | |||
178 | /* we can and should enable interrupts at this point */ | 224 | /* we can and should enable interrupts at this point */ |
179 | sti(); | 225 | local_irq_enable(); |
180 | 226 | ||
181 | mm = tsk->mm; | 227 | mm = tsk->mm; |
182 | info.si_code = SEGV_MAPERR; | 228 | info.si_code = SEGV_MAPERR; |
@@ -220,7 +266,10 @@ do_page_fault(unsigned long address, struct pt_regs *regs, | |||
220 | 266 | ||
221 | /* first do some preliminary protection checks */ | 267 | /* first do some preliminary protection checks */ |
222 | 268 | ||
223 | if (writeaccess) { | 269 | if (writeaccess == 2){ |
270 | if (!(vma->vm_flags & VM_EXEC)) | ||
271 | goto bad_area; | ||
272 | } else if (writeaccess == 1) { | ||
224 | if (!(vma->vm_flags & VM_WRITE)) | 273 | if (!(vma->vm_flags & VM_WRITE)) |
225 | goto bad_area; | 274 | goto bad_area; |
226 | } else { | 275 | } else { |
@@ -234,7 +283,7 @@ do_page_fault(unsigned long address, struct pt_regs *regs, | |||
234 | * the fault. | 283 | * the fault. |
235 | */ | 284 | */ |
236 | 285 | ||
237 | switch (handle_mm_fault(mm, vma, address, writeaccess)) { | 286 | switch (handle_mm_fault(mm, vma, address, writeaccess & 1)) { |
238 | case 1: | 287 | case 1: |
239 | tsk->min_flt++; | 288 | tsk->min_flt++; |
240 | break; | 289 | break; |
@@ -292,10 +341,10 @@ do_page_fault(unsigned long address, struct pt_regs *regs, | |||
292 | */ | 341 | */ |
293 | 342 | ||
294 | if ((unsigned long) (address) < PAGE_SIZE) | 343 | if ((unsigned long) (address) < PAGE_SIZE) |
295 | printk(KERN_ALERT "Unable to handle kernel NULL pointer dereference"); | 344 | raw_printk(KERN_ALERT "Unable to handle kernel NULL pointer dereference"); |
296 | else | 345 | else |
297 | printk(KERN_ALERT "Unable to handle kernel access"); | 346 | raw_printk(KERN_ALERT "Unable to handle kernel access"); |
298 | printk(" at virtual address %08lx\n",address); | 347 | raw_printk(" at virtual address %08lx\n",address); |
299 | 348 | ||
300 | die_if_kernel("Oops", regs, (writeaccess << 1) | protection); | 349 | die_if_kernel("Oops", regs, (writeaccess << 1) | protection); |
301 | 350 | ||
@@ -346,10 +395,11 @@ vmalloc_fault: | |||
346 | 395 | ||
347 | int offset = pgd_index(address); | 396 | int offset = pgd_index(address); |
348 | pgd_t *pgd, *pgd_k; | 397 | pgd_t *pgd, *pgd_k; |
398 | pud_t *pud, *pud_k; | ||
349 | pmd_t *pmd, *pmd_k; | 399 | pmd_t *pmd, *pmd_k; |
350 | pte_t *pte_k; | 400 | pte_t *pte_k; |
351 | 401 | ||
352 | pgd = (pgd_t *)current_pgd + offset; | 402 | pgd = (pgd_t *)per_cpu(current_pgd, smp_processor_id()) + offset; |
353 | pgd_k = init_mm.pgd + offset; | 403 | pgd_k = init_mm.pgd + offset; |
354 | 404 | ||
355 | /* Since we're two-level, we don't need to do both | 405 | /* Since we're two-level, we don't need to do both |
@@ -364,8 +414,13 @@ vmalloc_fault: | |||
364 | * it exists. | 414 | * it exists. |
365 | */ | 415 | */ |
366 | 416 | ||
367 | pmd = pmd_offset(pgd, address); | 417 | pud = pud_offset(pgd, address); |
368 | pmd_k = pmd_offset(pgd_k, address); | 418 | pud_k = pud_offset(pgd_k, address); |
419 | if (!pud_present(*pud_k)) | ||
420 | goto no_context; | ||
421 | |||
422 | pmd = pmd_offset(pud, address); | ||
423 | pmd_k = pmd_offset(pud_k, address); | ||
369 | 424 | ||
370 | if (!pmd_present(*pmd_k)) | 425 | if (!pmd_present(*pmd_k)) |
371 | goto bad_area_nosemaphore; | 426 | goto bad_area_nosemaphore; |
@@ -385,3 +440,19 @@ vmalloc_fault: | |||
385 | return; | 440 | return; |
386 | } | 441 | } |
387 | } | 442 | } |
443 | |||
444 | /* Find fixup code. */ | ||
445 | int | ||
446 | find_fixup_code(struct pt_regs *regs) | ||
447 | { | ||
448 | const struct exception_table_entry *fixup; | ||
449 | |||
450 | if ((fixup = search_exception_tables(instruction_pointer(regs))) != 0) { | ||
451 | /* Adjust the instruction pointer in the stackframe. */ | ||
452 | instruction_pointer(regs) = fixup->fixup; | ||
453 | arch_fixup(regs); | ||
454 | return 1; | ||
455 | } | ||
456 | |||
457 | return 0; | ||
458 | } | ||
diff --git a/arch/cris/mm/ioremap.c b/arch/cris/mm/ioremap.c index 6b9130bfb6c1..ebba11e270fa 100644 --- a/arch/cris/mm/ioremap.c +++ b/arch/cris/mm/ioremap.c | |||
@@ -14,9 +14,10 @@ | |||
14 | #include <asm/pgalloc.h> | 14 | #include <asm/pgalloc.h> |
15 | #include <asm/cacheflush.h> | 15 | #include <asm/cacheflush.h> |
16 | #include <asm/tlbflush.h> | 16 | #include <asm/tlbflush.h> |
17 | #include <asm/arch/memmap.h> | ||
17 | 18 | ||
18 | extern inline void remap_area_pte(pte_t * pte, unsigned long address, unsigned long size, | 19 | extern inline void remap_area_pte(pte_t * pte, unsigned long address, unsigned long size, |
19 | unsigned long phys_addr, unsigned long flags) | 20 | unsigned long phys_addr, pgprot_t prot) |
20 | { | 21 | { |
21 | unsigned long end; | 22 | unsigned long end; |
22 | 23 | ||
@@ -31,9 +32,7 @@ extern inline void remap_area_pte(pte_t * pte, unsigned long address, unsigned l | |||
31 | printk("remap_area_pte: page already exists\n"); | 32 | printk("remap_area_pte: page already exists\n"); |
32 | BUG(); | 33 | BUG(); |
33 | } | 34 | } |
34 | set_pte(pte, mk_pte_phys(phys_addr, __pgprot(_PAGE_PRESENT | __READABLE | | 35 | set_pte(pte, mk_pte_phys(phys_addr, prot)); |
35 | __WRITEABLE | _PAGE_GLOBAL | | ||
36 | _PAGE_KERNEL | flags))); | ||
37 | address += PAGE_SIZE; | 36 | address += PAGE_SIZE; |
38 | phys_addr += PAGE_SIZE; | 37 | phys_addr += PAGE_SIZE; |
39 | pte++; | 38 | pte++; |
@@ -41,7 +40,7 @@ extern inline void remap_area_pte(pte_t * pte, unsigned long address, unsigned l | |||
41 | } | 40 | } |
42 | 41 | ||
43 | static inline int remap_area_pmd(pmd_t * pmd, unsigned long address, unsigned long size, | 42 | static inline int remap_area_pmd(pmd_t * pmd, unsigned long address, unsigned long size, |
44 | unsigned long phys_addr, unsigned long flags) | 43 | unsigned long phys_addr, pgprot_t prot) |
45 | { | 44 | { |
46 | unsigned long end; | 45 | unsigned long end; |
47 | 46 | ||
@@ -56,7 +55,7 @@ static inline int remap_area_pmd(pmd_t * pmd, unsigned long address, unsigned lo | |||
56 | pte_t * pte = pte_alloc_kernel(&init_mm, pmd, address); | 55 | pte_t * pte = pte_alloc_kernel(&init_mm, pmd, address); |
57 | if (!pte) | 56 | if (!pte) |
58 | return -ENOMEM; | 57 | return -ENOMEM; |
59 | remap_area_pte(pte, address, end - address, address + phys_addr, flags); | 58 | remap_area_pte(pte, address, end - address, address + phys_addr, prot); |
60 | address = (address + PMD_SIZE) & PMD_MASK; | 59 | address = (address + PMD_SIZE) & PMD_MASK; |
61 | pmd++; | 60 | pmd++; |
62 | } while (address && (address < end)); | 61 | } while (address && (address < end)); |
@@ -64,7 +63,7 @@ static inline int remap_area_pmd(pmd_t * pmd, unsigned long address, unsigned lo | |||
64 | } | 63 | } |
65 | 64 | ||
66 | static int remap_area_pages(unsigned long address, unsigned long phys_addr, | 65 | static int remap_area_pages(unsigned long address, unsigned long phys_addr, |
67 | unsigned long size, unsigned long flags) | 66 | unsigned long size, pgprot_t prot) |
68 | { | 67 | { |
69 | int error; | 68 | int error; |
70 | pgd_t * dir; | 69 | pgd_t * dir; |
@@ -77,13 +76,19 @@ static int remap_area_pages(unsigned long address, unsigned long phys_addr, | |||
77 | BUG(); | 76 | BUG(); |
78 | spin_lock(&init_mm.page_table_lock); | 77 | spin_lock(&init_mm.page_table_lock); |
79 | do { | 78 | do { |
79 | pud_t *pud; | ||
80 | pmd_t *pmd; | 80 | pmd_t *pmd; |
81 | pmd = pmd_alloc(&init_mm, dir, address); | 81 | |
82 | error = -ENOMEM; | 82 | error = -ENOMEM; |
83 | pud = pud_alloc(&init_mm, dir, address); | ||
84 | if (!pud) | ||
85 | break; | ||
86 | pmd = pmd_alloc(&init_mm, pud, address); | ||
87 | |||
83 | if (!pmd) | 88 | if (!pmd) |
84 | break; | 89 | break; |
85 | if (remap_area_pmd(pmd, address, end - address, | 90 | if (remap_area_pmd(pmd, address, end - address, |
86 | phys_addr + address, flags)) | 91 | phys_addr + address, prot)) |
87 | break; | 92 | break; |
88 | error = 0; | 93 | error = 0; |
89 | address = (address + PGDIR_SIZE) & PGDIR_MASK; | 94 | address = (address + PGDIR_SIZE) & PGDIR_MASK; |
@@ -107,9 +112,9 @@ static int remap_area_pages(unsigned long address, unsigned long phys_addr, | |||
107 | * have to convert them into an offset in a page-aligned mapping, but the | 112 | * have to convert them into an offset in a page-aligned mapping, but the |
108 | * caller shouldn't need to know that small detail. | 113 | * caller shouldn't need to know that small detail. |
109 | */ | 114 | */ |
110 | void * __ioremap(unsigned long phys_addr, unsigned long size, unsigned long flags) | 115 | void __iomem * __ioremap_prot(unsigned long phys_addr, unsigned long size, pgprot_t prot) |
111 | { | 116 | { |
112 | void * addr; | 117 | void __iomem * addr; |
113 | struct vm_struct * area; | 118 | struct vm_struct * area; |
114 | unsigned long offset, last_addr; | 119 | unsigned long offset, last_addr; |
115 | 120 | ||
@@ -131,15 +136,36 @@ void * __ioremap(unsigned long phys_addr, unsigned long size, unsigned long flag | |||
131 | area = get_vm_area(size, VM_IOREMAP); | 136 | area = get_vm_area(size, VM_IOREMAP); |
132 | if (!area) | 137 | if (!area) |
133 | return NULL; | 138 | return NULL; |
134 | addr = area->addr; | 139 | addr = (void __iomem *)area->addr; |
135 | if (remap_area_pages((unsigned long) addr, phys_addr, size, flags)) { | 140 | if (remap_area_pages((unsigned long) addr, phys_addr, size, prot)) { |
136 | vfree(addr); | 141 | vfree((void __force *)addr); |
137 | return NULL; | 142 | return NULL; |
138 | } | 143 | } |
139 | return (void *) (offset + (char *)addr); | 144 | return (void __iomem *) (offset + (char __iomem *)addr); |
145 | } | ||
146 | |||
147 | void __iomem * __ioremap(unsigned long phys_addr, unsigned long size, unsigned long flags) | ||
148 | { | ||
149 | return __ioremap_prot(phys_addr, size, | ||
150 | __pgprot(_PAGE_PRESENT | __READABLE | | ||
151 | __WRITEABLE | _PAGE_GLOBAL | | ||
152 | _PAGE_KERNEL | flags)); | ||
153 | } | ||
154 | |||
155 | /** | ||
156 | * ioremap_nocache - map bus memory into CPU space | ||
157 | * @offset: bus address of the memory | ||
158 | * @size: size of the resource to map | ||
159 | * | ||
160 | * Must be freed with iounmap. | ||
161 | */ | ||
162 | |||
163 | void __iomem *ioremap_nocache (unsigned long phys_addr, unsigned long size) | ||
164 | { | ||
165 | return __ioremap(phys_addr | MEM_NON_CACHEABLE, size, 0); | ||
140 | } | 166 | } |
141 | 167 | ||
142 | void iounmap(void *addr) | 168 | void iounmap(volatile void __iomem *addr) |
143 | { | 169 | { |
144 | if (addr > high_memory) | 170 | if (addr > high_memory) |
145 | return vfree((void *) (PAGE_MASK & (unsigned long) addr)); | 171 | return vfree((void *) (PAGE_MASK & (unsigned long) addr)); |
diff --git a/arch/cris/mm/tlb.c b/arch/cris/mm/tlb.c index 23eca5ad7389..0df390a656cd 100644 --- a/arch/cris/mm/tlb.c +++ b/arch/cris/mm/tlb.c | |||
@@ -29,18 +29,6 @@ | |||
29 | struct mm_struct *page_id_map[NUM_PAGEID]; | 29 | struct mm_struct *page_id_map[NUM_PAGEID]; |
30 | static int map_replace_ptr = 1; /* which page_id_map entry to replace next */ | 30 | static int map_replace_ptr = 1; /* which page_id_map entry to replace next */ |
31 | 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 */ | 32 | /* the following functions are similar to those used in the PPC port */ |
45 | 33 | ||
46 | static inline void | 34 | static inline void |
@@ -60,12 +48,12 @@ alloc_context(struct mm_struct *mm) | |||
60 | */ | 48 | */ |
61 | flush_tlb_mm(old_mm); | 49 | flush_tlb_mm(old_mm); |
62 | 50 | ||
63 | old_mm->context = NO_CONTEXT; | 51 | old_mm->context.page_id = NO_CONTEXT; |
64 | } | 52 | } |
65 | 53 | ||
66 | /* insert it into the page_id_map */ | 54 | /* insert it into the page_id_map */ |
67 | 55 | ||
68 | mm->context = map_replace_ptr; | 56 | mm->context.page_id = map_replace_ptr; |
69 | page_id_map[map_replace_ptr] = mm; | 57 | page_id_map[map_replace_ptr] = mm; |
70 | 58 | ||
71 | map_replace_ptr++; | 59 | map_replace_ptr++; |
@@ -81,7 +69,7 @@ alloc_context(struct mm_struct *mm) | |||
81 | void | 69 | void |
82 | get_mmu_context(struct mm_struct *mm) | 70 | get_mmu_context(struct mm_struct *mm) |
83 | { | 71 | { |
84 | if(mm->context == NO_CONTEXT) | 72 | if(mm->context.page_id == NO_CONTEXT) |
85 | alloc_context(mm); | 73 | alloc_context(mm); |
86 | } | 74 | } |
87 | 75 | ||
@@ -96,11 +84,10 @@ get_mmu_context(struct mm_struct *mm) | |||
96 | void | 84 | void |
97 | destroy_context(struct mm_struct *mm) | 85 | destroy_context(struct mm_struct *mm) |
98 | { | 86 | { |
99 | if(mm->context != NO_CONTEXT) { | 87 | if(mm->context.page_id != NO_CONTEXT) { |
100 | D(printk("destroy_context %d (%p)\n", mm->context, mm)); | 88 | D(printk("destroy_context %d (%p)\n", mm->context.page_id, mm)); |
101 | flush_tlb_mm(mm); /* TODO this might be redundant ? */ | 89 | flush_tlb_mm(mm); /* TODO this might be redundant ? */ |
102 | page_id_map[mm->context] = NULL; | 90 | page_id_map[mm->context.page_id] = NULL; |
103 | /* mm->context = NO_CONTEXT; redundant.. mm will be freed */ | ||
104 | } | 91 | } |
105 | } | 92 | } |
106 | 93 | ||