Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

[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>

authored by

Mikael Starvik and committed by
Linus Torvalds
4f18cfbf 7cf32cad

+135 -48
+83 -12
arch/cris/mm/fault.c
··· 6 6 * Authors: Bjorn Wesen 7 7 * 8 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 41 * Revision 1.11 2004/05/14 07:58:05 starvik 10 42 * Merge of changes from 2.4 11 43 * ··· 135 103 136 104 extern int find_fixup_code(struct pt_regs *); 137 105 extern void die_if_kernel(const char *, struct pt_regs *, long); 106 + extern int raw_printk(const char *fmt, ...); 138 107 139 108 /* debug of low-level TLB reload */ 140 109 #undef DEBUG ··· 151 118 152 119 /* current active page directory */ 153 120 154 - volatile pgd_t *current_pgd; 121 + volatile DEFINE_PER_CPU(pgd_t *,current_pgd); 122 + unsigned long cris_signal_return_page; 155 123 156 124 /* 157 125 * This routine handles page faults. It determines the address, ··· 180 146 struct vm_area_struct * vma; 181 147 siginfo_t info; 182 148 183 - D(printk("Page fault for %X at %X, prot %d write %d\n", 184 - address, regs->erp, protection, writeaccess)); 149 + D(printk("Page fault for %lX on %X at %lX, prot %d write %d\n", 150 + address, smp_processor_id(), instruction_pointer(regs), 151 + protection, writeaccess)); 185 152 186 153 tsk = current; 187 154 ··· 210 175 !user_mode(regs)) 211 176 goto vmalloc_fault; 212 177 178 + /* When stack execution is not allowed we store the signal 179 + * trampolines in the reserved cris_signal_return_page. 180 + * Handle this in the exact same way as vmalloc (we know 181 + * that the mapping is there and is valid so no need to 182 + * call handle_mm_fault). 183 + */ 184 + if (cris_signal_return_page && 185 + address == cris_signal_return_page && 186 + !protection && user_mode(regs)) 187 + goto vmalloc_fault; 188 + 213 189 /* we can and should enable interrupts at this point */ 214 - sti(); 190 + local_irq_enable(); 215 191 216 192 mm = tsk->mm; 217 193 info.si_code = SEGV_MAPERR; ··· 266 220 267 221 /* first do some preliminary protection checks */ 268 222 269 - if (writeaccess) { 223 + if (writeaccess == 2){ 224 + if (!(vma->vm_flags & VM_EXEC)) 225 + goto bad_area; 226 + } else if (writeaccess == 1) { 270 227 if (!(vma->vm_flags & VM_WRITE)) 271 228 goto bad_area; 272 229 } else { ··· 283 234 * the fault. 284 235 */ 285 236 286 - switch (handle_mm_fault(mm, vma, address, writeaccess)) { 237 + switch (handle_mm_fault(mm, vma, address, writeaccess & 1)) { 287 238 case 1: 288 239 tsk->min_flt++; 289 240 break; ··· 341 292 */ 342 293 343 294 if ((unsigned long) (address) < PAGE_SIZE) 344 - printk(KERN_ALERT "Unable to handle kernel NULL pointer dereference"); 295 + raw_printk(KERN_ALERT "Unable to handle kernel NULL pointer dereference"); 345 296 else 346 - printk(KERN_ALERT "Unable to handle kernel access"); 347 - printk(" at virtual address %08lx\n",address); 297 + raw_printk(KERN_ALERT "Unable to handle kernel access"); 298 + raw_printk(" at virtual address %08lx\n",address); 348 299 349 300 die_if_kernel("Oops", regs, (writeaccess << 1) | protection); 350 301 ··· 395 346 396 347 int offset = pgd_index(address); 397 348 pgd_t *pgd, *pgd_k; 349 + pud_t *pud, *pud_k; 398 350 pmd_t *pmd, *pmd_k; 399 351 pte_t *pte_k; 400 352 401 - pgd = (pgd_t *)current_pgd + offset; 353 + pgd = (pgd_t *)per_cpu(current_pgd, smp_processor_id()) + offset; 402 354 pgd_k = init_mm.pgd + offset; 403 355 404 356 /* Since we're two-level, we don't need to do both ··· 414 364 * it exists. 415 365 */ 416 366 417 - pmd = pmd_offset(pgd, address); 418 - pmd_k = pmd_offset(pgd_k, address); 367 + pud = pud_offset(pgd, address); 368 + pud_k = pud_offset(pgd_k, address); 369 + if (!pud_present(*pud_k)) 370 + goto no_context; 371 + 372 + pmd = pmd_offset(pud, address); 373 + pmd_k = pmd_offset(pud_k, address); 419 374 420 375 if (!pmd_present(*pmd_k)) 421 376 goto bad_area_nosemaphore; ··· 439 384 440 385 return; 441 386 } 387 + } 388 + 389 + /* Find fixup code. */ 390 + int 391 + find_fixup_code(struct pt_regs *regs) 392 + { 393 + const struct exception_table_entry *fixup; 394 + 395 + if ((fixup = search_exception_tables(instruction_pointer(regs))) != 0) { 396 + /* Adjust the instruction pointer in the stackframe. */ 397 + instruction_pointer(regs) = fixup->fixup; 398 + arch_fixup(regs); 399 + return 1; 400 + } 401 + 402 + return 0; 442 403 }
+42 -16
arch/cris/mm/ioremap.c
··· 14 14 #include <asm/pgalloc.h> 15 15 #include <asm/cacheflush.h> 16 16 #include <asm/tlbflush.h> 17 + #include <asm/arch/memmap.h> 17 18 18 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 22 unsigned long end; 22 23 ··· 32 31 printk("remap_area_pte: page already exists\n"); 33 32 BUG(); 34 33 } 35 - set_pte(pte, mk_pte_phys(phys_addr, __pgprot(_PAGE_PRESENT | __READABLE | 36 - __WRITEABLE | _PAGE_GLOBAL | 37 - _PAGE_KERNEL | flags))); 34 + set_pte(pte, mk_pte_phys(phys_addr, prot)); 38 35 address += PAGE_SIZE; 39 36 phys_addr += PAGE_SIZE; 40 37 pte++; ··· 40 41 } 41 42 42 43 static inline int remap_area_pmd(pmd_t * pmd, unsigned long address, unsigned long size, 43 - unsigned long phys_addr, unsigned long flags) 44 + unsigned long phys_addr, pgprot_t prot) 44 45 { 45 46 unsigned long end; 46 47 ··· 55 56 pte_t * pte = pte_alloc_kernel(&init_mm, pmd, address); 56 57 if (!pte) 57 58 return -ENOMEM; 58 - remap_area_pte(pte, address, end - address, address + phys_addr, flags); 59 + remap_area_pte(pte, address, end - address, address + phys_addr, prot); 59 60 address = (address + PMD_SIZE) & PMD_MASK; 60 61 pmd++; 61 62 } while (address && (address < end)); ··· 63 64 } 64 65 65 66 static int remap_area_pages(unsigned long address, unsigned long phys_addr, 66 - unsigned long size, unsigned long flags) 67 + unsigned long size, pgprot_t prot) 67 68 { 68 69 int error; 69 70 pgd_t * dir; ··· 76 77 BUG(); 77 78 spin_lock(&init_mm.page_table_lock); 78 79 do { 80 + pud_t *pud; 79 81 pmd_t *pmd; 80 - pmd = pmd_alloc(&init_mm, dir, address); 82 + 81 83 error = -ENOMEM; 84 + pud = pud_alloc(&init_mm, dir, address); 85 + if (!pud) 86 + break; 87 + pmd = pmd_alloc(&init_mm, pud, address); 88 + 82 89 if (!pmd) 83 90 break; 84 91 if (remap_area_pmd(pmd, address, end - address, 85 - phys_addr + address, flags)) 92 + phys_addr + address, prot)) 86 93 break; 87 94 error = 0; 88 95 address = (address + PGDIR_SIZE) & PGDIR_MASK; ··· 112 107 * have to convert them into an offset in a page-aligned mapping, but the 113 108 * caller shouldn't need to know that small detail. 114 109 */ 115 - void * __ioremap(unsigned long phys_addr, unsigned long size, unsigned long flags) 110 + void __iomem * __ioremap_prot(unsigned long phys_addr, unsigned long size, pgprot_t prot) 116 111 { 117 - void * addr; 112 + void __iomem * addr; 118 113 struct vm_struct * area; 119 114 unsigned long offset, last_addr; 120 115 ··· 136 131 area = get_vm_area(size, VM_IOREMAP); 137 132 if (!area) 138 133 return NULL; 139 - addr = area->addr; 140 - if (remap_area_pages((unsigned long) addr, phys_addr, size, flags)) { 141 - vfree(addr); 134 + addr = (void __iomem *)area->addr; 135 + if (remap_area_pages((unsigned long) addr, phys_addr, size, prot)) { 136 + vfree((void __force *)addr); 142 137 return NULL; 143 138 } 144 - return (void *) (offset + (char *)addr); 139 + return (void __iomem *) (offset + (char __iomem *)addr); 145 140 } 146 141 147 - void iounmap(void *addr) 142 + void __iomem * __ioremap(unsigned long phys_addr, unsigned long size, unsigned long flags) 143 + { 144 + return __ioremap_prot(phys_addr, size, 145 + __pgprot(_PAGE_PRESENT | __READABLE | 146 + __WRITEABLE | _PAGE_GLOBAL | 147 + _PAGE_KERNEL | flags)); 148 + } 149 + 150 + /** 151 + * ioremap_nocache - map bus memory into CPU space 152 + * @offset: bus address of the memory 153 + * @size: size of the resource to map 154 + * 155 + * Must be freed with iounmap. 156 + */ 157 + 158 + void __iomem *ioremap_nocache (unsigned long phys_addr, unsigned long size) 159 + { 160 + return __ioremap(phys_addr | MEM_NON_CACHEABLE, size, 0); 161 + } 162 + 163 + void iounmap(volatile void __iomem *addr) 148 164 { 149 165 if (addr > high_memory) 150 166 return vfree((void *) (PAGE_MASK & (unsigned long) addr));
+6 -19
arch/cris/mm/tlb.c
··· 29 29 struct mm_struct *page_id_map[NUM_PAGEID]; 30 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 32 /* the following functions are similar to those used in the PPC port */ 45 33 46 34 static inline void ··· 48 60 */ 49 61 flush_tlb_mm(old_mm); 50 62 51 - old_mm->context = NO_CONTEXT; 63 + old_mm->context.page_id = NO_CONTEXT; 52 64 } 53 65 54 66 /* insert it into the page_id_map */ 55 67 56 - mm->context = map_replace_ptr; 68 + mm->context.page_id = map_replace_ptr; 57 69 page_id_map[map_replace_ptr] = mm; 58 70 59 71 map_replace_ptr++; ··· 69 81 void 70 82 get_mmu_context(struct mm_struct *mm) 71 83 { 72 - if(mm->context == NO_CONTEXT) 84 + if(mm->context.page_id == NO_CONTEXT) 73 85 alloc_context(mm); 74 86 } 75 87 ··· 84 96 void 85 97 destroy_context(struct mm_struct *mm) 86 98 { 87 - if(mm->context != NO_CONTEXT) { 88 - D(printk("destroy_context %d (%p)\n", mm->context, mm)); 99 + if(mm->context.page_id != NO_CONTEXT) { 100 + D(printk("destroy_context %d (%p)\n", mm->context.page_id, mm)); 89 101 flush_tlb_mm(mm); /* TODO this might be redundant ? */ 90 - page_id_map[mm->context] = NULL; 91 - /* mm->context = NO_CONTEXT; redundant.. mm will be freed */ 102 + page_id_map[mm->context.page_id] = NULL; 92 103 } 93 104 } 94 105
+4 -1
include/asm-cris/arch-v10/mmu.h
··· 7 7 8 8 /* type used in struct mm to couple an MMU context to an active mm */ 9 9 10 - typedef unsigned int mm_context_t; 10 + typedef struct 11 + { 12 + unsigned int page_id; 13 + } mm_context_t; 11 14 12 15 /* kernel memory segments */ 13 16