diff options
Diffstat (limited to 'arch/arm/kernel/traps.c')
-rw-r--r-- | arch/arm/kernel/traps.c | 63 |
1 files changed, 58 insertions, 5 deletions
diff --git a/arch/arm/kernel/traps.c b/arch/arm/kernel/traps.c index 3a001fe5540b..45d2a032d890 100644 --- a/arch/arm/kernel/traps.c +++ b/arch/arm/kernel/traps.c | |||
@@ -218,7 +218,8 @@ NORET_TYPE void die(const char *str, struct pt_regs *regs, int err) | |||
218 | tsk->comm, tsk->pid, tsk->thread_info + 1); | 218 | tsk->comm, tsk->pid, tsk->thread_info + 1); |
219 | 219 | ||
220 | if (!user_mode(regs) || in_interrupt()) { | 220 | if (!user_mode(regs) || in_interrupt()) { |
221 | dump_mem("Stack: ", regs->ARM_sp, 8192+(unsigned long)tsk->thread_info); | 221 | dump_mem("Stack: ", regs->ARM_sp, |
222 | THREAD_SIZE + (unsigned long)tsk->thread_info); | ||
222 | dump_backtrace(regs, tsk); | 223 | dump_backtrace(regs, tsk); |
223 | dump_instr(regs); | 224 | dump_instr(regs); |
224 | } | 225 | } |
@@ -450,9 +451,9 @@ asmlinkage int arm_syscall(int no, struct pt_regs *regs) | |||
450 | 451 | ||
451 | case NR(set_tls): | 452 | case NR(set_tls): |
452 | thread->tp_value = regs->ARM_r0; | 453 | thread->tp_value = regs->ARM_r0; |
453 | #ifdef CONFIG_HAS_TLS_REG | 454 | #if defined(CONFIG_HAS_TLS_REG) |
454 | asm ("mcr p15, 0, %0, c13, c0, 3" : : "r" (regs->ARM_r0) ); | 455 | asm ("mcr p15, 0, %0, c13, c0, 3" : : "r" (regs->ARM_r0) ); |
455 | #else | 456 | #elif !defined(CONFIG_TLS_REG_EMUL) |
456 | /* | 457 | /* |
457 | * User space must never try to access this directly. | 458 | * User space must never try to access this directly. |
458 | * Expect your app to break eventually if you do so. | 459 | * Expect your app to break eventually if you do so. |
@@ -463,6 +464,55 @@ asmlinkage int arm_syscall(int no, struct pt_regs *regs) | |||
463 | #endif | 464 | #endif |
464 | return 0; | 465 | return 0; |
465 | 466 | ||
467 | #ifdef CONFIG_NEEDS_SYSCALL_FOR_CMPXCHG | ||
468 | /* | ||
469 | * Atomically store r1 in *r2 if *r2 is equal to r0 for user space. | ||
470 | * Return zero in r0 if *MEM was changed or non-zero if no exchange | ||
471 | * happened. Also set the user C flag accordingly. | ||
472 | * If access permissions have to be fixed up then non-zero is | ||
473 | * returned and the operation has to be re-attempted. | ||
474 | * | ||
475 | * *NOTE*: This is a ghost syscall private to the kernel. Only the | ||
476 | * __kuser_cmpxchg code in entry-armv.S should be aware of its | ||
477 | * existence. Don't ever use this from user code. | ||
478 | */ | ||
479 | case 0xfff0: | ||
480 | { | ||
481 | extern void do_DataAbort(unsigned long addr, unsigned int fsr, | ||
482 | struct pt_regs *regs); | ||
483 | unsigned long val; | ||
484 | unsigned long addr = regs->ARM_r2; | ||
485 | struct mm_struct *mm = current->mm; | ||
486 | pgd_t *pgd; pmd_t *pmd; pte_t *pte; | ||
487 | |||
488 | regs->ARM_cpsr &= ~PSR_C_BIT; | ||
489 | spin_lock(&mm->page_table_lock); | ||
490 | pgd = pgd_offset(mm, addr); | ||
491 | if (!pgd_present(*pgd)) | ||
492 | goto bad_access; | ||
493 | pmd = pmd_offset(pgd, addr); | ||
494 | if (!pmd_present(*pmd)) | ||
495 | goto bad_access; | ||
496 | pte = pte_offset_map(pmd, addr); | ||
497 | if (!pte_present(*pte) || !pte_write(*pte)) | ||
498 | goto bad_access; | ||
499 | val = *(unsigned long *)addr; | ||
500 | val -= regs->ARM_r0; | ||
501 | if (val == 0) { | ||
502 | *(unsigned long *)addr = regs->ARM_r1; | ||
503 | regs->ARM_cpsr |= PSR_C_BIT; | ||
504 | } | ||
505 | spin_unlock(&mm->page_table_lock); | ||
506 | return val; | ||
507 | |||
508 | bad_access: | ||
509 | spin_unlock(&mm->page_table_lock); | ||
510 | /* simulate a read access fault */ | ||
511 | do_DataAbort(addr, 15 + (1 << 11), regs); | ||
512 | return -1; | ||
513 | } | ||
514 | #endif | ||
515 | |||
466 | default: | 516 | default: |
467 | /* Calls 9f00xx..9f07ff are defined to return -ENOSYS | 517 | /* Calls 9f00xx..9f07ff are defined to return -ENOSYS |
468 | if not implemented, rather than raising SIGILL. This | 518 | if not implemented, rather than raising SIGILL. This |
@@ -497,11 +547,14 @@ asmlinkage int arm_syscall(int no, struct pt_regs *regs) | |||
497 | return 0; | 547 | return 0; |
498 | } | 548 | } |
499 | 549 | ||
500 | #if defined(CONFIG_CPU_32v6) && !defined(CONFIG_HAS_TLS_REG) | 550 | #ifdef CONFIG_TLS_REG_EMUL |
501 | 551 | ||
502 | /* | 552 | /* |
503 | * We might be running on an ARMv6+ processor which should have the TLS | 553 | * We might be running on an ARMv6+ processor which should have the TLS |
504 | * register, but for some reason we can't use it and have to emulate it. | 554 | * register but for some reason we can't use it, or maybe an SMP system |
555 | * using a pre-ARMv6 processor (there are apparently a few prototypes like | ||
556 | * that in existence) and therefore access to that register must be | ||
557 | * emulated. | ||
505 | */ | 558 | */ |
506 | 559 | ||
507 | static int get_tp_trap(struct pt_regs *regs, unsigned int instr) | 560 | static int get_tp_trap(struct pt_regs *regs, unsigned int instr) |