diff options
Diffstat (limited to 'arch/sh/kernel/traps_32.c')
-rw-r--r-- | arch/sh/kernel/traps_32.c | 218 |
1 files changed, 169 insertions, 49 deletions
diff --git a/arch/sh/kernel/traps_32.c b/arch/sh/kernel/traps_32.c index 2b772776fcda..6aba9af79eaf 100644 --- a/arch/sh/kernel/traps_32.c +++ b/arch/sh/kernel/traps_32.c | |||
@@ -24,6 +24,7 @@ | |||
24 | #include <linux/kdebug.h> | 24 | #include <linux/kdebug.h> |
25 | #include <linux/kexec.h> | 25 | #include <linux/kexec.h> |
26 | #include <linux/limits.h> | 26 | #include <linux/limits.h> |
27 | #include <linux/proc_fs.h> | ||
27 | #include <asm/system.h> | 28 | #include <asm/system.h> |
28 | #include <asm/uaccess.h> | 29 | #include <asm/uaccess.h> |
29 | #include <asm/fpu.h> | 30 | #include <asm/fpu.h> |
@@ -44,6 +45,85 @@ | |||
44 | #define TRAP_ILLEGAL_SLOT_INST 13 | 45 | #define TRAP_ILLEGAL_SLOT_INST 13 |
45 | #endif | 46 | #endif |
46 | 47 | ||
48 | static unsigned long se_user; | ||
49 | static unsigned long se_sys; | ||
50 | static unsigned long se_half; | ||
51 | static unsigned long se_word; | ||
52 | static unsigned long se_dword; | ||
53 | static unsigned long se_multi; | ||
54 | /* bitfield: 1: warn 2: fixup 4: signal -> combinations 2|4 && 1|2|4 are not | ||
55 | valid! */ | ||
56 | static int se_usermode = 3; | ||
57 | /* 0: no warning 1: print a warning message */ | ||
58 | static int se_kernmode_warn = 1; | ||
59 | |||
60 | #ifdef CONFIG_PROC_FS | ||
61 | static const char *se_usermode_action[] = { | ||
62 | "ignored", | ||
63 | "warn", | ||
64 | "fixup", | ||
65 | "fixup+warn", | ||
66 | "signal", | ||
67 | "signal+warn" | ||
68 | }; | ||
69 | |||
70 | static int | ||
71 | proc_alignment_read(char *page, char **start, off_t off, int count, int *eof, | ||
72 | void *data) | ||
73 | { | ||
74 | char *p = page; | ||
75 | int len; | ||
76 | |||
77 | p += sprintf(p, "User:\t\t%lu\n", se_user); | ||
78 | p += sprintf(p, "System:\t\t%lu\n", se_sys); | ||
79 | p += sprintf(p, "Half:\t\t%lu\n", se_half); | ||
80 | p += sprintf(p, "Word:\t\t%lu\n", se_word); | ||
81 | p += sprintf(p, "DWord:\t\t%lu\n", se_dword); | ||
82 | p += sprintf(p, "Multi:\t\t%lu\n", se_multi); | ||
83 | p += sprintf(p, "User faults:\t%i (%s)\n", se_usermode, | ||
84 | se_usermode_action[se_usermode]); | ||
85 | p += sprintf(p, "Kernel faults:\t%i (fixup%s)\n", se_kernmode_warn, | ||
86 | se_kernmode_warn ? "+warn" : ""); | ||
87 | |||
88 | len = (p - page) - off; | ||
89 | if (len < 0) | ||
90 | len = 0; | ||
91 | |||
92 | *eof = (len <= count) ? 1 : 0; | ||
93 | *start = page + off; | ||
94 | |||
95 | return len; | ||
96 | } | ||
97 | |||
98 | static int proc_alignment_write(struct file *file, const char __user *buffer, | ||
99 | unsigned long count, void *data) | ||
100 | { | ||
101 | char mode; | ||
102 | |||
103 | if (count > 0) { | ||
104 | if (get_user(mode, buffer)) | ||
105 | return -EFAULT; | ||
106 | if (mode >= '0' && mode <= '5') | ||
107 | se_usermode = mode - '0'; | ||
108 | } | ||
109 | return count; | ||
110 | } | ||
111 | |||
112 | static int proc_alignment_kern_write(struct file *file, const char __user *buffer, | ||
113 | unsigned long count, void *data) | ||
114 | { | ||
115 | char mode; | ||
116 | |||
117 | if (count > 0) { | ||
118 | if (get_user(mode, buffer)) | ||
119 | return -EFAULT; | ||
120 | if (mode >= '0' && mode <= '1') | ||
121 | se_kernmode_warn = mode - '0'; | ||
122 | } | ||
123 | return count; | ||
124 | } | ||
125 | #endif | ||
126 | |||
47 | static void dump_mem(const char *str, unsigned long bottom, unsigned long top) | 127 | static void dump_mem(const char *str, unsigned long bottom, unsigned long top) |
48 | { | 128 | { |
49 | unsigned long p; | 129 | unsigned long p; |
@@ -136,6 +216,7 @@ static void die_if_no_fixup(const char * str, struct pt_regs * regs, long err) | |||
136 | regs->pc = fixup->fixup; | 216 | regs->pc = fixup->fixup; |
137 | return; | 217 | return; |
138 | } | 218 | } |
219 | |||
139 | die(str, regs, err); | 220 | die(str, regs, err); |
140 | } | 221 | } |
141 | } | 222 | } |
@@ -193,6 +274,13 @@ static int handle_unaligned_ins(insn_size_t instruction, struct pt_regs *regs, | |||
193 | 274 | ||
194 | count = 1<<(instruction&3); | 275 | count = 1<<(instruction&3); |
195 | 276 | ||
277 | switch (count) { | ||
278 | case 1: se_half += 1; break; | ||
279 | case 2: se_word += 1; break; | ||
280 | case 4: se_dword += 1; break; | ||
281 | case 8: se_multi += 1; break; /* ??? */ | ||
282 | } | ||
283 | |||
196 | ret = -EFAULT; | 284 | ret = -EFAULT; |
197 | switch (instruction>>12) { | 285 | switch (instruction>>12) { |
198 | case 0: /* mov.[bwl] to/from memory via r0+rn */ | 286 | case 0: /* mov.[bwl] to/from memory via r0+rn */ |
@@ -358,15 +446,8 @@ static inline int handle_delayslot(struct pt_regs *regs, | |||
358 | #define SH_PC_8BIT_OFFSET(instr) ((((signed char)(instr))*2) + 4) | 446 | #define SH_PC_8BIT_OFFSET(instr) ((((signed char)(instr))*2) + 4) |
359 | #define SH_PC_12BIT_OFFSET(instr) ((((signed short)(instr<<4))>>3) + 4) | 447 | #define SH_PC_12BIT_OFFSET(instr) ((((signed short)(instr<<4))>>3) + 4) |
360 | 448 | ||
361 | /* | ||
362 | * XXX: SH-2A needs this too, but it needs an overhaul thanks to mixed 32-bit | ||
363 | * opcodes.. | ||
364 | */ | ||
365 | |||
366 | static int handle_unaligned_notify_count = 10; | ||
367 | |||
368 | int handle_unaligned_access(insn_size_t instruction, struct pt_regs *regs, | 449 | int handle_unaligned_access(insn_size_t instruction, struct pt_regs *regs, |
369 | struct mem_access *ma) | 450 | struct mem_access *ma, int expected) |
370 | { | 451 | { |
371 | u_int rm; | 452 | u_int rm; |
372 | int ret, index; | 453 | int ret, index; |
@@ -374,15 +455,13 @@ int handle_unaligned_access(insn_size_t instruction, struct pt_regs *regs, | |||
374 | index = (instruction>>8)&15; /* 0x0F00 */ | 455 | index = (instruction>>8)&15; /* 0x0F00 */ |
375 | rm = regs->regs[index]; | 456 | rm = regs->regs[index]; |
376 | 457 | ||
377 | /* shout about the first ten userspace fixups */ | 458 | /* shout about fixups */ |
378 | if (user_mode(regs) && handle_unaligned_notify_count>0) { | 459 | if (!expected && printk_ratelimit()) |
379 | handle_unaligned_notify_count--; | 460 | printk(KERN_NOTICE "Fixing up unaligned %s access " |
380 | |||
381 | printk(KERN_NOTICE "Fixing up unaligned userspace access " | ||
382 | "in \"%s\" pid=%d pc=0x%p ins=0x%04hx\n", | 461 | "in \"%s\" pid=%d pc=0x%p ins=0x%04hx\n", |
462 | user_mode(regs) ? "userspace" : "kernel", | ||
383 | current->comm, task_pid_nr(current), | 463 | current->comm, task_pid_nr(current), |
384 | (void *)regs->pc, instruction); | 464 | (void *)regs->pc, instruction); |
385 | } | ||
386 | 465 | ||
387 | ret = -EFAULT; | 466 | ret = -EFAULT; |
388 | switch (instruction&0xF000) { | 467 | switch (instruction&0xF000) { |
@@ -538,6 +617,36 @@ asmlinkage void do_address_error(struct pt_regs *regs, | |||
538 | 617 | ||
539 | local_irq_enable(); | 618 | local_irq_enable(); |
540 | 619 | ||
620 | se_user += 1; | ||
621 | |||
622 | #ifndef CONFIG_CPU_SH2A | ||
623 | set_fs(USER_DS); | ||
624 | if (copy_from_user(&instruction, (u16 *)(regs->pc & ~1), 2)) { | ||
625 | set_fs(oldfs); | ||
626 | goto uspace_segv; | ||
627 | } | ||
628 | set_fs(oldfs); | ||
629 | |||
630 | /* shout about userspace fixups */ | ||
631 | if (se_usermode & 1) | ||
632 | printk(KERN_NOTICE "Unaligned userspace access " | ||
633 | "in \"%s\" pid=%d pc=0x%p ins=0x%04hx\n", | ||
634 | current->comm, current->pid, (void *)regs->pc, | ||
635 | instruction); | ||
636 | #endif | ||
637 | |||
638 | if (se_usermode & 2) | ||
639 | goto fixup; | ||
640 | |||
641 | if (se_usermode & 4) | ||
642 | goto uspace_segv; | ||
643 | else { | ||
644 | /* ignore */ | ||
645 | regs->pc += instruction_size(instruction); | ||
646 | return; | ||
647 | } | ||
648 | |||
649 | fixup: | ||
541 | /* bad PC is not something we can fix */ | 650 | /* bad PC is not something we can fix */ |
542 | if (regs->pc & 1) { | 651 | if (regs->pc & 1) { |
543 | si_code = BUS_ADRALN; | 652 | si_code = BUS_ADRALN; |
@@ -545,17 +654,8 @@ asmlinkage void do_address_error(struct pt_regs *regs, | |||
545 | } | 654 | } |
546 | 655 | ||
547 | set_fs(USER_DS); | 656 | set_fs(USER_DS); |
548 | if (copy_from_user(&instruction, (void __user *)(regs->pc), | ||
549 | sizeof(instruction))) { | ||
550 | /* Argh. Fault on the instruction itself. | ||
551 | This should never happen non-SMP | ||
552 | */ | ||
553 | set_fs(oldfs); | ||
554 | goto uspace_segv; | ||
555 | } | ||
556 | |||
557 | tmp = handle_unaligned_access(instruction, regs, | 657 | tmp = handle_unaligned_access(instruction, regs, |
558 | &user_mem_access); | 658 | &user_mem_access, 0); |
559 | set_fs(oldfs); | 659 | set_fs(oldfs); |
560 | 660 | ||
561 | if (tmp==0) | 661 | if (tmp==0) |
@@ -571,6 +671,14 @@ uspace_segv: | |||
571 | info.si_addr = (void __user *)address; | 671 | info.si_addr = (void __user *)address; |
572 | force_sig_info(SIGBUS, &info, current); | 672 | force_sig_info(SIGBUS, &info, current); |
573 | } else { | 673 | } else { |
674 | se_sys += 1; | ||
675 | |||
676 | if (se_kernmode_warn) | ||
677 | printk(KERN_NOTICE "Unaligned kernel access " | ||
678 | "on behalf of \"%s\" pid=%d pc=0x%p ins=0x%04hx\n", | ||
679 | current->comm, current->pid, (void *)regs->pc, | ||
680 | instruction); | ||
681 | |||
574 | if (regs->pc & 1) | 682 | if (regs->pc & 1) |
575 | die("unaligned program counter", regs, error_code); | 683 | die("unaligned program counter", regs, error_code); |
576 | 684 | ||
@@ -584,7 +692,8 @@ uspace_segv: | |||
584 | die("insn faulting in do_address_error", regs, 0); | 692 | die("insn faulting in do_address_error", regs, 0); |
585 | } | 693 | } |
586 | 694 | ||
587 | handle_unaligned_access(instruction, regs, &user_mem_access); | 695 | handle_unaligned_access(instruction, regs, |
696 | &user_mem_access, 0); | ||
588 | set_fs(oldfs); | 697 | set_fs(oldfs); |
589 | } | 698 | } |
590 | } | 699 | } |
@@ -858,30 +967,6 @@ void __init trap_init(void) | |||
858 | per_cpu_trap_init(); | 967 | per_cpu_trap_init(); |
859 | } | 968 | } |
860 | 969 | ||
861 | void show_trace(struct task_struct *tsk, unsigned long *sp, | ||
862 | struct pt_regs *regs) | ||
863 | { | ||
864 | unsigned long addr; | ||
865 | |||
866 | if (regs && user_mode(regs)) | ||
867 | return; | ||
868 | |||
869 | printk("\nCall trace:\n"); | ||
870 | |||
871 | while (!kstack_end(sp)) { | ||
872 | addr = *sp++; | ||
873 | if (kernel_text_address(addr)) | ||
874 | print_ip_sym(addr); | ||
875 | } | ||
876 | |||
877 | printk("\n"); | ||
878 | |||
879 | if (!tsk) | ||
880 | tsk = current; | ||
881 | |||
882 | debug_show_held_locks(tsk); | ||
883 | } | ||
884 | |||
885 | void show_stack(struct task_struct *tsk, unsigned long *sp) | 970 | void show_stack(struct task_struct *tsk, unsigned long *sp) |
886 | { | 971 | { |
887 | unsigned long stack; | 972 | unsigned long stack; |
@@ -904,3 +989,38 @@ void dump_stack(void) | |||
904 | show_stack(NULL, NULL); | 989 | show_stack(NULL, NULL); |
905 | } | 990 | } |
906 | EXPORT_SYMBOL(dump_stack); | 991 | EXPORT_SYMBOL(dump_stack); |
992 | |||
993 | #ifdef CONFIG_PROC_FS | ||
994 | /* | ||
995 | * This needs to be done after sysctl_init, otherwise sys/ will be | ||
996 | * overwritten. Actually, this shouldn't be in sys/ at all since | ||
997 | * it isn't a sysctl, and it doesn't contain sysctl information. | ||
998 | * We now locate it in /proc/cpu/alignment instead. | ||
999 | */ | ||
1000 | static int __init alignment_init(void) | ||
1001 | { | ||
1002 | struct proc_dir_entry *dir, *res; | ||
1003 | |||
1004 | dir = proc_mkdir("cpu", NULL); | ||
1005 | if (!dir) | ||
1006 | return -ENOMEM; | ||
1007 | |||
1008 | res = create_proc_entry("alignment", S_IWUSR | S_IRUGO, dir); | ||
1009 | if (!res) | ||
1010 | return -ENOMEM; | ||
1011 | |||
1012 | res->read_proc = proc_alignment_read; | ||
1013 | res->write_proc = proc_alignment_write; | ||
1014 | |||
1015 | res = create_proc_entry("kernel_alignment", S_IWUSR | S_IRUGO, dir); | ||
1016 | if (!res) | ||
1017 | return -ENOMEM; | ||
1018 | |||
1019 | res->read_proc = proc_alignment_read; | ||
1020 | res->write_proc = proc_alignment_kern_write; | ||
1021 | |||
1022 | return 0; | ||
1023 | } | ||
1024 | |||
1025 | fs_initcall(alignment_init); | ||
1026 | #endif | ||