diff options
Diffstat (limited to 'arch/arm/kernel/traps.c')
-rw-r--r-- | arch/arm/kernel/traps.c | 47 |
1 files changed, 29 insertions, 18 deletions
diff --git a/arch/arm/kernel/traps.c b/arch/arm/kernel/traps.c index f6de76e0a45d..45e9ea6cd2a5 100644 --- a/arch/arm/kernel/traps.c +++ b/arch/arm/kernel/traps.c | |||
@@ -198,25 +198,16 @@ void show_stack(struct task_struct *tsk, unsigned long *sp) | |||
198 | barrier(); | 198 | barrier(); |
199 | } | 199 | } |
200 | 200 | ||
201 | DEFINE_SPINLOCK(die_lock); | 201 | static void __die(const char *str, int err, struct thread_info *thread, struct pt_regs *regs) |
202 | |||
203 | /* | ||
204 | * This function is protected against re-entrancy. | ||
205 | */ | ||
206 | NORET_TYPE void die(const char *str, struct pt_regs *regs, int err) | ||
207 | { | 202 | { |
208 | struct task_struct *tsk = current; | 203 | struct task_struct *tsk = thread->task; |
209 | static int die_counter; | 204 | static int die_counter; |
210 | 205 | ||
211 | console_verbose(); | ||
212 | spin_lock_irq(&die_lock); | ||
213 | bust_spinlocks(1); | ||
214 | |||
215 | printk("Internal error: %s: %x [#%d]\n", str, err, ++die_counter); | 206 | printk("Internal error: %s: %x [#%d]\n", str, err, ++die_counter); |
216 | print_modules(); | 207 | print_modules(); |
217 | __show_regs(regs); | 208 | __show_regs(regs); |
218 | printk("Process %s (pid: %d, stack limit = 0x%p)\n", | 209 | printk("Process %s (pid: %d, stack limit = 0x%p)\n", |
219 | tsk->comm, tsk->pid, tsk->thread_info + 1); | 210 | tsk->comm, tsk->pid, thread + 1); |
220 | 211 | ||
221 | if (!user_mode(regs) || in_interrupt()) { | 212 | if (!user_mode(regs) || in_interrupt()) { |
222 | dump_mem("Stack: ", regs->ARM_sp, | 213 | dump_mem("Stack: ", regs->ARM_sp, |
@@ -224,7 +215,21 @@ NORET_TYPE void die(const char *str, struct pt_regs *regs, int err) | |||
224 | dump_backtrace(regs, tsk); | 215 | dump_backtrace(regs, tsk); |
225 | dump_instr(regs); | 216 | dump_instr(regs); |
226 | } | 217 | } |
218 | } | ||
219 | |||
220 | DEFINE_SPINLOCK(die_lock); | ||
221 | |||
222 | /* | ||
223 | * This function is protected against re-entrancy. | ||
224 | */ | ||
225 | NORET_TYPE void die(const char *str, struct pt_regs *regs, int err) | ||
226 | { | ||
227 | struct thread_info *thread = current_thread_info(); | ||
227 | 228 | ||
229 | console_verbose(); | ||
230 | spin_lock_irq(&die_lock); | ||
231 | bust_spinlocks(1); | ||
232 | __die(str, err, thread, regs); | ||
228 | bust_spinlocks(0); | 233 | bust_spinlocks(0); |
229 | spin_unlock_irq(&die_lock); | 234 | spin_unlock_irq(&die_lock); |
230 | do_exit(SIGSEGV); | 235 | do_exit(SIGSEGV); |
@@ -345,7 +350,9 @@ static int bad_syscall(int n, struct pt_regs *regs) | |||
345 | struct thread_info *thread = current_thread_info(); | 350 | struct thread_info *thread = current_thread_info(); |
346 | siginfo_t info; | 351 | siginfo_t info; |
347 | 352 | ||
348 | if (current->personality != PER_LINUX && thread->exec_domain->handler) { | 353 | if (current->personality != PER_LINUX && |
354 | current->personality != PER_LINUX_32BIT && | ||
355 | thread->exec_domain->handler) { | ||
349 | thread->exec_domain->handler(n, regs); | 356 | thread->exec_domain->handler(n, regs); |
350 | return regs->ARM_r0; | 357 | return regs->ARM_r0; |
351 | } | 358 | } |
@@ -481,29 +488,33 @@ asmlinkage int arm_syscall(int no, struct pt_regs *regs) | |||
481 | unsigned long addr = regs->ARM_r2; | 488 | unsigned long addr = regs->ARM_r2; |
482 | struct mm_struct *mm = current->mm; | 489 | struct mm_struct *mm = current->mm; |
483 | pgd_t *pgd; pmd_t *pmd; pte_t *pte; | 490 | pgd_t *pgd; pmd_t *pmd; pte_t *pte; |
491 | spinlock_t *ptl; | ||
484 | 492 | ||
485 | regs->ARM_cpsr &= ~PSR_C_BIT; | 493 | regs->ARM_cpsr &= ~PSR_C_BIT; |
486 | spin_lock(&mm->page_table_lock); | 494 | down_read(&mm->mmap_sem); |
487 | pgd = pgd_offset(mm, addr); | 495 | pgd = pgd_offset(mm, addr); |
488 | if (!pgd_present(*pgd)) | 496 | if (!pgd_present(*pgd)) |
489 | goto bad_access; | 497 | goto bad_access; |
490 | pmd = pmd_offset(pgd, addr); | 498 | pmd = pmd_offset(pgd, addr); |
491 | if (!pmd_present(*pmd)) | 499 | if (!pmd_present(*pmd)) |
492 | goto bad_access; | 500 | goto bad_access; |
493 | pte = pte_offset_map(pmd, addr); | 501 | pte = pte_offset_map_lock(mm, pmd, addr, &ptl); |
494 | if (!pte_present(*pte) || !pte_write(*pte)) | 502 | if (!pte_present(*pte) || !pte_write(*pte)) { |
503 | pte_unmap_unlock(pte, ptl); | ||
495 | goto bad_access; | 504 | goto bad_access; |
505 | } | ||
496 | val = *(unsigned long *)addr; | 506 | val = *(unsigned long *)addr; |
497 | val -= regs->ARM_r0; | 507 | val -= regs->ARM_r0; |
498 | if (val == 0) { | 508 | if (val == 0) { |
499 | *(unsigned long *)addr = regs->ARM_r1; | 509 | *(unsigned long *)addr = regs->ARM_r1; |
500 | regs->ARM_cpsr |= PSR_C_BIT; | 510 | regs->ARM_cpsr |= PSR_C_BIT; |
501 | } | 511 | } |
502 | spin_unlock(&mm->page_table_lock); | 512 | pte_unmap_unlock(pte, ptl); |
513 | up_read(&mm->mmap_sem); | ||
503 | return val; | 514 | return val; |
504 | 515 | ||
505 | bad_access: | 516 | bad_access: |
506 | spin_unlock(&mm->page_table_lock); | 517 | up_read(&mm->mmap_sem); |
507 | /* simulate a write access fault */ | 518 | /* simulate a write access fault */ |
508 | do_DataAbort(addr, 15 + (1 << 11), regs); | 519 | do_DataAbort(addr, 15 + (1 << 11), regs); |
509 | return -1; | 520 | return -1; |