aboutsummaryrefslogtreecommitdiffstats
path: root/arch/sh/kernel/traps_32.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/sh/kernel/traps_32.c')
-rw-r--r--arch/sh/kernel/traps_32.c218
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
48static unsigned long se_user;
49static unsigned long se_sys;
50static unsigned long se_half;
51static unsigned long se_word;
52static unsigned long se_dword;
53static unsigned long se_multi;
54/* bitfield: 1: warn 2: fixup 4: signal -> combinations 2|4 && 1|2|4 are not
55 valid! */
56static int se_usermode = 3;
57/* 0: no warning 1: print a warning message */
58static int se_kernmode_warn = 1;
59
60#ifdef CONFIG_PROC_FS
61static const char *se_usermode_action[] = {
62 "ignored",
63 "warn",
64 "fixup",
65 "fixup+warn",
66 "signal",
67 "signal+warn"
68};
69
70static int
71proc_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
98static 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
112static 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
47static void dump_mem(const char *str, unsigned long bottom, unsigned long top) 127static 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
366static int handle_unaligned_notify_count = 10;
367
368int handle_unaligned_access(insn_size_t instruction, struct pt_regs *regs, 449int 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
649fixup:
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
861void 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
885void show_stack(struct task_struct *tsk, unsigned long *sp) 970void 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}
906EXPORT_SYMBOL(dump_stack); 991EXPORT_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 */
1000static 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
1025fs_initcall(alignment_init);
1026#endif