diff options
author | Andre Draszik <andre.draszik@st.com> | 2009-08-24 01:53:46 -0400 |
---|---|---|
committer | Paul Mundt <lethal@linux-sh.org> | 2009-08-24 01:53:46 -0400 |
commit | 7436cde6b2ca71049051620c68c26522bb3403bf (patch) | |
tree | c4574582b77553d8acf06d342b3f8b83c38400a5 /arch/sh/kernel/traps_32.c | |
parent | 9a4af027a03e10e97f56081cd7dd1fda5282bd9c (diff) |
sh: Allow user control over misaligned fixup handling
This patch brings the SH4 misaligned trap handler in line with what
happens on ARM:
Add a /proc/cpu/alignment which can be read from to get alignment
trap statistics and written to to influence the behaviour of the
alignment trap handling. The value to write is a bitfield, which
has the following meaning: 1 warn, 2 fixup, 4 signal
In addition, we add a /proc/cpu/kernel_alignment, to enable or
disable warnings in case of kernel code causing alignment errors.
Signed-off by: Andre Draszik <andre.draszik@st.com>
Signed-off-by: Stuart Menefy <stuart.menefy@st.com>
Signed-off-by: Paul Mundt <lethal@linux-sh.org>
Diffstat (limited to 'arch/sh/kernel/traps_32.c')
-rw-r--r-- | arch/sh/kernel/traps_32.c | 153 |
1 files changed, 153 insertions, 0 deletions
diff --git a/arch/sh/kernel/traps_32.c b/arch/sh/kernel/traps_32.c index 866c7c7a8236..0ad356d00ac8 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,87 @@ | |||
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_skipped; | ||
51 | static unsigned long se_half; | ||
52 | static unsigned long se_word; | ||
53 | static unsigned long se_dword; | ||
54 | static unsigned long se_multi; | ||
55 | /* bitfield: 1: warn 2: fixup 4: signal -> combinations 2|4 && 1|2|4 are not | ||
56 | valid! */ | ||
57 | static int se_usermode = 3; | ||
58 | /* 0: no warning 1: print a warning message */ | ||
59 | static int se_kernmode_warn = 1; | ||
60 | |||
61 | #ifdef CONFIG_PROC_FS | ||
62 | static const char *se_usermode_action[] = { | ||
63 | "ignored", | ||
64 | "warn", | ||
65 | "fixup", | ||
66 | "fixup+warn", | ||
67 | "signal", | ||
68 | "signal+warn" | ||
69 | }; | ||
70 | |||
71 | static int | ||
72 | proc_alignment_read(char *page, char **start, off_t off, int count, int *eof, | ||
73 | void *data) | ||
74 | { | ||
75 | char *p = page; | ||
76 | int len; | ||
77 | |||
78 | p += sprintf(p, "User:\t\t%lu\n", se_user); | ||
79 | p += sprintf(p, "System:\t\t%lu\n", se_sys); | ||
80 | p += sprintf(p, "Skipped:\t%lu\n", se_skipped); | ||
81 | p += sprintf(p, "Half:\t\t%lu\n", se_half); | ||
82 | p += sprintf(p, "Word:\t\t%lu\n", se_word); | ||
83 | p += sprintf(p, "DWord:\t\t%lu\n", se_dword); | ||
84 | p += sprintf(p, "Multi:\t\t%lu\n", se_multi); | ||
85 | p += sprintf(p, "User faults:\t%i (%s)\n", se_usermode, | ||
86 | se_usermode_action[se_usermode]); | ||
87 | p += sprintf(p, "Kernel faults:\t%i (fixup%s)\n", se_kernmode_warn, | ||
88 | se_kernmode_warn ? "+warn" : ""); | ||
89 | |||
90 | len = (p - page) - off; | ||
91 | if (len < 0) | ||
92 | len = 0; | ||
93 | |||
94 | *eof = (len <= count) ? 1 : 0; | ||
95 | *start = page + off; | ||
96 | |||
97 | return len; | ||
98 | } | ||
99 | |||
100 | static int proc_alignment_write(struct file *file, const char __user *buffer, | ||
101 | unsigned long count, void *data) | ||
102 | { | ||
103 | char mode; | ||
104 | |||
105 | if (count > 0) { | ||
106 | if (get_user(mode, buffer)) | ||
107 | return -EFAULT; | ||
108 | if (mode >= '0' && mode <= '5') | ||
109 | se_usermode = mode - '0'; | ||
110 | } | ||
111 | return count; | ||
112 | } | ||
113 | |||
114 | static int proc_alignment_kern_write(struct file *file, const char __user *buffer, | ||
115 | unsigned long count, void *data) | ||
116 | { | ||
117 | char mode; | ||
118 | |||
119 | if (count > 0) { | ||
120 | if (get_user(mode, buffer)) | ||
121 | return -EFAULT; | ||
122 | if (mode >= '0' && mode <= '1') | ||
123 | se_kernmode_warn = mode - '0'; | ||
124 | } | ||
125 | return count; | ||
126 | } | ||
127 | #endif | ||
128 | |||
47 | static void dump_mem(const char *str, unsigned long bottom, unsigned long top) | 129 | static void dump_mem(const char *str, unsigned long bottom, unsigned long top) |
48 | { | 130 | { |
49 | unsigned long p; | 131 | unsigned long p; |
@@ -194,6 +276,13 @@ static int handle_unaligned_ins(insn_size_t instruction, struct pt_regs *regs, | |||
194 | 276 | ||
195 | count = 1<<(instruction&3); | 277 | count = 1<<(instruction&3); |
196 | 278 | ||
279 | switch (count) { | ||
280 | case 1: se_half += 1; break; | ||
281 | case 2: se_word += 1; break; | ||
282 | case 4: se_dword += 1; break; | ||
283 | case 8: se_multi += 1; break; /* ??? */ | ||
284 | } | ||
285 | |||
197 | ret = -EFAULT; | 286 | ret = -EFAULT; |
198 | switch (instruction>>12) { | 287 | switch (instruction>>12) { |
199 | case 0: /* mov.[bwl] to/from memory via r0+rn */ | 288 | case 0: /* mov.[bwl] to/from memory via r0+rn */ |
@@ -530,6 +619,27 @@ asmlinkage void do_address_error(struct pt_regs *regs, | |||
530 | 619 | ||
531 | local_irq_enable(); | 620 | local_irq_enable(); |
532 | 621 | ||
622 | se_user += 1; | ||
623 | |||
624 | /* shout about userspace fixups */ | ||
625 | if (se_usermode & 1) | ||
626 | printk(KERN_NOTICE "Unaligned userspace access " | ||
627 | "in \"%s\" pid=%d pc=0x%p ins=0x%04hx\n", | ||
628 | current->comm, current->pid, (void *)regs->pc, | ||
629 | instruction); | ||
630 | |||
631 | if (se_usermode & 2) | ||
632 | goto fixup; | ||
633 | |||
634 | if (se_usermode & 4) | ||
635 | goto uspace_segv; | ||
636 | else { | ||
637 | /* ignore */ | ||
638 | trace_mark(kernel_arch_trap_exit, MARK_NOARGS); | ||
639 | return; | ||
640 | } | ||
641 | |||
642 | fixup: | ||
533 | /* bad PC is not something we can fix */ | 643 | /* bad PC is not something we can fix */ |
534 | if (regs->pc & 1) { | 644 | if (regs->pc & 1) { |
535 | si_code = BUS_ADRALN; | 645 | si_code = BUS_ADRALN; |
@@ -563,6 +673,14 @@ uspace_segv: | |||
563 | info.si_addr = (void __user *)address; | 673 | info.si_addr = (void __user *)address; |
564 | force_sig_info(SIGBUS, &info, current); | 674 | force_sig_info(SIGBUS, &info, current); |
565 | } else { | 675 | } else { |
676 | se_sys += 1; | ||
677 | |||
678 | if (se_kernmode_warn) | ||
679 | printk(KERN_NOTICE "Unaligned kernel access " | ||
680 | "on behalf of \"%s\" pid=%d pc=0x%p ins=0x%04hx\n", | ||
681 | current->comm, current->pid, (void *)regs->pc, | ||
682 | instruction); | ||
683 | |||
566 | if (regs->pc & 1) | 684 | if (regs->pc & 1) |
567 | die("unaligned program counter", regs, error_code); | 685 | die("unaligned program counter", regs, error_code); |
568 | 686 | ||
@@ -872,3 +990,38 @@ void dump_stack(void) | |||
872 | show_stack(NULL, NULL); | 990 | show_stack(NULL, NULL); |
873 | } | 991 | } |
874 | EXPORT_SYMBOL(dump_stack); | 992 | EXPORT_SYMBOL(dump_stack); |
993 | |||
994 | #ifdef CONFIG_PROC_FS | ||
995 | /* | ||
996 | * This needs to be done after sysctl_init, otherwise sys/ will be | ||
997 | * overwritten. Actually, this shouldn't be in sys/ at all since | ||
998 | * it isn't a sysctl, and it doesn't contain sysctl information. | ||
999 | * We now locate it in /proc/cpu/alignment instead. | ||
1000 | */ | ||
1001 | static int __init alignment_init(void) | ||
1002 | { | ||
1003 | struct proc_dir_entry *dir, *res; | ||
1004 | |||
1005 | dir = proc_mkdir("cpu", NULL); | ||
1006 | if (!dir) | ||
1007 | return -ENOMEM; | ||
1008 | |||
1009 | res = create_proc_entry("alignment", S_IWUSR | S_IRUGO, dir); | ||
1010 | if (!res) | ||
1011 | return -ENOMEM; | ||
1012 | |||
1013 | res->read_proc = proc_alignment_read; | ||
1014 | res->write_proc = proc_alignment_write; | ||
1015 | |||
1016 | res = create_proc_entry("kernel_alignment", S_IWUSR | S_IRUGO, dir); | ||
1017 | if (!res) | ||
1018 | return -ENOMEM; | ||
1019 | |||
1020 | res->read_proc = proc_alignment_read; | ||
1021 | res->write_proc = proc_alignment_kern_write; | ||
1022 | |||
1023 | return 0; | ||
1024 | } | ||
1025 | |||
1026 | fs_initcall(alignment_init); | ||
1027 | #endif | ||