diff options
Diffstat (limited to 'arch')
-rw-r--r-- | arch/sh/include/asm/alignment.h | 21 | ||||
-rw-r--r-- | arch/sh/kernel/traps_32.c | 151 | ||||
-rw-r--r-- | arch/sh/mm/Makefile | 2 | ||||
-rw-r--r-- | arch/sh/mm/alignment.c | 159 |
4 files changed, 198 insertions, 135 deletions
diff --git a/arch/sh/include/asm/alignment.h b/arch/sh/include/asm/alignment.h new file mode 100644 index 000000000000..b12efecf5294 --- /dev/null +++ b/arch/sh/include/asm/alignment.h | |||
@@ -0,0 +1,21 @@ | |||
1 | #ifndef __ASM_SH_ALIGNMENT_H | ||
2 | #define __ASM_SH_ALIGNMENT_H | ||
3 | |||
4 | #include <linux/types.h> | ||
5 | |||
6 | extern void inc_unaligned_byte_access(void); | ||
7 | extern void inc_unaligned_word_access(void); | ||
8 | extern void inc_unaligned_dword_access(void); | ||
9 | extern void inc_unaligned_multi_access(void); | ||
10 | extern void inc_unaligned_user_access(void); | ||
11 | extern void inc_unaligned_kernel_access(void); | ||
12 | |||
13 | #define UM_WARN (1 << 0) | ||
14 | #define UM_FIXUP (1 << 1) | ||
15 | #define UM_SIGNAL (1 << 2) | ||
16 | |||
17 | extern unsigned int unaligned_user_action(void); | ||
18 | |||
19 | extern void unaligned_fixups_notify(struct task_struct *, insn_size_t, struct pt_regs *); | ||
20 | |||
21 | #endif /* __ASM_SH_ALIGNMENT_H */ | ||
diff --git a/arch/sh/kernel/traps_32.c b/arch/sh/kernel/traps_32.c index efcbdfe52f52..204def6ecb6a 100644 --- a/arch/sh/kernel/traps_32.c +++ b/arch/sh/kernel/traps_32.c | |||
@@ -24,11 +24,10 @@ | |||
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> | ||
28 | #include <linux/seq_file.h> | ||
29 | #include <linux/sysfs.h> | 27 | #include <linux/sysfs.h> |
28 | #include <linux/uaccess.h> | ||
30 | #include <asm/system.h> | 29 | #include <asm/system.h> |
31 | #include <asm/uaccess.h> | 30 | #include <asm/alignment.h> |
32 | #include <asm/fpu.h> | 31 | #include <asm/fpu.h> |
33 | #include <asm/kprobes.h> | 32 | #include <asm/kprobes.h> |
34 | #include <asm/sh_bios.h> | 33 | #include <asm/sh_bios.h> |
@@ -48,73 +47,6 @@ | |||
48 | #define TRAP_ILLEGAL_SLOT_INST 13 | 47 | #define TRAP_ILLEGAL_SLOT_INST 13 |
49 | #endif | 48 | #endif |
50 | 49 | ||
51 | static unsigned long se_user; | ||
52 | static unsigned long se_sys; | ||
53 | static unsigned long se_half; | ||
54 | static unsigned long se_word; | ||
55 | static unsigned long se_dword; | ||
56 | static unsigned long se_multi; | ||
57 | /* bitfield: 1: warn 2: fixup 4: signal -> combinations 2|4 && 1|2|4 are not | ||
58 | valid! */ | ||
59 | static int se_usermode = 3; | ||
60 | /* 0: no warning 1: print a warning message, disabled by default */ | ||
61 | static int se_kernmode_warn; | ||
62 | |||
63 | #ifdef CONFIG_PROC_FS | ||
64 | static const char *se_usermode_action[] = { | ||
65 | "ignored", | ||
66 | "warn", | ||
67 | "fixup", | ||
68 | "fixup+warn", | ||
69 | "signal", | ||
70 | "signal+warn" | ||
71 | }; | ||
72 | |||
73 | static int alignment_proc_show(struct seq_file *m, void *v) | ||
74 | { | ||
75 | seq_printf(m, "User:\t\t%lu\n", se_user); | ||
76 | seq_printf(m, "System:\t\t%lu\n", se_sys); | ||
77 | seq_printf(m, "Half:\t\t%lu\n", se_half); | ||
78 | seq_printf(m, "Word:\t\t%lu\n", se_word); | ||
79 | seq_printf(m, "DWord:\t\t%lu\n", se_dword); | ||
80 | seq_printf(m, "Multi:\t\t%lu\n", se_multi); | ||
81 | seq_printf(m, "User faults:\t%i (%s)\n", se_usermode, | ||
82 | se_usermode_action[se_usermode]); | ||
83 | seq_printf(m, "Kernel faults:\t%i (fixup%s)\n", se_kernmode_warn, | ||
84 | se_kernmode_warn ? "+warn" : ""); | ||
85 | return 0; | ||
86 | } | ||
87 | |||
88 | static int alignment_proc_open(struct inode *inode, struct file *file) | ||
89 | { | ||
90 | return single_open(file, alignment_proc_show, NULL); | ||
91 | } | ||
92 | |||
93 | static ssize_t alignment_proc_write(struct file *file, | ||
94 | const char __user *buffer, size_t count, loff_t *pos) | ||
95 | { | ||
96 | int *data = PDE(file->f_path.dentry->d_inode)->data; | ||
97 | char mode; | ||
98 | |||
99 | if (count > 0) { | ||
100 | if (get_user(mode, buffer)) | ||
101 | return -EFAULT; | ||
102 | if (mode >= '0' && mode <= '5') | ||
103 | *data = mode - '0'; | ||
104 | } | ||
105 | return count; | ||
106 | } | ||
107 | |||
108 | static const struct file_operations alignment_proc_fops = { | ||
109 | .owner = THIS_MODULE, | ||
110 | .open = alignment_proc_open, | ||
111 | .read = seq_read, | ||
112 | .llseek = seq_lseek, | ||
113 | .release = single_release, | ||
114 | .write = alignment_proc_write, | ||
115 | }; | ||
116 | #endif | ||
117 | |||
118 | static void dump_mem(const char *str, unsigned long bottom, unsigned long top) | 50 | static void dump_mem(const char *str, unsigned long bottom, unsigned long top) |
119 | { | 51 | { |
120 | unsigned long p; | 52 | unsigned long p; |
@@ -266,10 +198,10 @@ static int handle_unaligned_ins(insn_size_t instruction, struct pt_regs *regs, | |||
266 | count = 1<<(instruction&3); | 198 | count = 1<<(instruction&3); |
267 | 199 | ||
268 | switch (count) { | 200 | switch (count) { |
269 | case 1: se_half += 1; break; | 201 | case 1: inc_unaligned_byte_access(); break; |
270 | case 2: se_word += 1; break; | 202 | case 2: inc_unaligned_word_access(); break; |
271 | case 4: se_dword += 1; break; | 203 | case 4: inc_unaligned_dword_access(); break; |
272 | case 8: se_multi += 1; break; /* ??? */ | 204 | case 8: inc_unaligned_multi_access(); break; |
273 | } | 205 | } |
274 | 206 | ||
275 | ret = -EFAULT; | 207 | ret = -EFAULT; |
@@ -453,18 +385,8 @@ int handle_unaligned_access(insn_size_t instruction, struct pt_regs *regs, | |||
453 | rm = regs->regs[index]; | 385 | rm = regs->regs[index]; |
454 | 386 | ||
455 | /* shout about fixups */ | 387 | /* shout about fixups */ |
456 | if (!expected) { | 388 | if (!expected) |
457 | if (user_mode(regs) && (se_usermode & 1) && printk_ratelimit()) | 389 | unaligned_fixups_notify(current, instruction, regs); |
458 | pr_notice("Fixing up unaligned userspace access " | ||
459 | "in \"%s\" pid=%d pc=0x%p ins=0x%04hx\n", | ||
460 | current->comm, task_pid_nr(current), | ||
461 | (void *)regs->pc, instruction); | ||
462 | else if (se_kernmode_warn && printk_ratelimit()) | ||
463 | pr_notice("Fixing up unaligned kernel access " | ||
464 | "in \"%s\" pid=%d pc=0x%p ins=0x%04hx\n", | ||
465 | current->comm, task_pid_nr(current), | ||
466 | (void *)regs->pc, instruction); | ||
467 | } | ||
468 | 390 | ||
469 | ret = -EFAULT; | 391 | ret = -EFAULT; |
470 | switch (instruction&0xF000) { | 392 | switch (instruction&0xF000) { |
@@ -617,10 +539,10 @@ asmlinkage void do_address_error(struct pt_regs *regs, | |||
617 | 539 | ||
618 | if (user_mode(regs)) { | 540 | if (user_mode(regs)) { |
619 | int si_code = BUS_ADRERR; | 541 | int si_code = BUS_ADRERR; |
542 | unsigned int user_action; | ||
620 | 543 | ||
621 | local_irq_enable(); | 544 | local_irq_enable(); |
622 | 545 | inc_unaligned_user_access(); | |
623 | se_user += 1; | ||
624 | 546 | ||
625 | set_fs(USER_DS); | 547 | set_fs(USER_DS); |
626 | if (copy_from_user(&instruction, (insn_size_t *)(regs->pc & ~1), | 548 | if (copy_from_user(&instruction, (insn_size_t *)(regs->pc & ~1), |
@@ -631,16 +553,12 @@ asmlinkage void do_address_error(struct pt_regs *regs, | |||
631 | set_fs(oldfs); | 553 | set_fs(oldfs); |
632 | 554 | ||
633 | /* shout about userspace fixups */ | 555 | /* shout about userspace fixups */ |
634 | if (se_usermode & 1) | 556 | unaligned_fixups_notify(current, instruction, regs); |
635 | printk(KERN_NOTICE "Unaligned userspace access " | ||
636 | "in \"%s\" pid=%d pc=0x%p ins=0x%04hx\n", | ||
637 | current->comm, current->pid, (void *)regs->pc, | ||
638 | instruction); | ||
639 | 557 | ||
640 | if (se_usermode & 2) | 558 | user_action = unaligned_user_action(); |
559 | if (user_action & UM_FIXUP) | ||
641 | goto fixup; | 560 | goto fixup; |
642 | 561 | if (user_action & UM_SIGNAL) | |
643 | if (se_usermode & 4) | ||
644 | goto uspace_segv; | 562 | goto uspace_segv; |
645 | else { | 563 | else { |
646 | /* ignore */ | 564 | /* ignore */ |
@@ -660,7 +578,7 @@ fixup: | |||
660 | &user_mem_access, 0); | 578 | &user_mem_access, 0); |
661 | set_fs(oldfs); | 579 | set_fs(oldfs); |
662 | 580 | ||
663 | if (tmp==0) | 581 | if (tmp == 0) |
664 | return; /* sorted */ | 582 | return; /* sorted */ |
665 | uspace_segv: | 583 | uspace_segv: |
666 | printk(KERN_NOTICE "Sending SIGBUS to \"%s\" due to unaligned " | 584 | printk(KERN_NOTICE "Sending SIGBUS to \"%s\" due to unaligned " |
@@ -673,7 +591,7 @@ uspace_segv: | |||
673 | info.si_addr = (void __user *)address; | 591 | info.si_addr = (void __user *)address; |
674 | force_sig_info(SIGBUS, &info, current); | 592 | force_sig_info(SIGBUS, &info, current); |
675 | } else { | 593 | } else { |
676 | se_sys += 1; | 594 | inc_unaligned_kernel_access(); |
677 | 595 | ||
678 | if (regs->pc & 1) | 596 | if (regs->pc & 1) |
679 | die("unaligned program counter", regs, error_code); | 597 | die("unaligned program counter", regs, error_code); |
@@ -688,11 +606,7 @@ uspace_segv: | |||
688 | die("insn faulting in do_address_error", regs, 0); | 606 | die("insn faulting in do_address_error", regs, 0); |
689 | } | 607 | } |
690 | 608 | ||
691 | if (se_kernmode_warn) | 609 | unaligned_fixups_notify(current, instruction, regs); |
692 | printk(KERN_NOTICE "Unaligned kernel access " | ||
693 | "on behalf of \"%s\" pid=%d pc=0x%p ins=0x%04hx\n", | ||
694 | current->comm, current->pid, (void *)regs->pc, | ||
695 | instruction); | ||
696 | 610 | ||
697 | handle_unaligned_access(instruction, regs, | 611 | handle_unaligned_access(instruction, regs, |
698 | &user_mem_access, 0); | 612 | &user_mem_access, 0); |
@@ -964,34 +878,3 @@ void dump_stack(void) | |||
964 | show_stack(NULL, NULL); | 878 | show_stack(NULL, NULL); |
965 | } | 879 | } |
966 | EXPORT_SYMBOL(dump_stack); | 880 | EXPORT_SYMBOL(dump_stack); |
967 | |||
968 | #ifdef CONFIG_PROC_FS | ||
969 | /* | ||
970 | * This needs to be done after sysctl_init, otherwise sys/ will be | ||
971 | * overwritten. Actually, this shouldn't be in sys/ at all since | ||
972 | * it isn't a sysctl, and it doesn't contain sysctl information. | ||
973 | * We now locate it in /proc/cpu/alignment instead. | ||
974 | */ | ||
975 | static int __init alignment_init(void) | ||
976 | { | ||
977 | struct proc_dir_entry *dir, *res; | ||
978 | |||
979 | dir = proc_mkdir("cpu", NULL); | ||
980 | if (!dir) | ||
981 | return -ENOMEM; | ||
982 | |||
983 | res = proc_create_data("alignment", S_IWUSR | S_IRUGO, dir, | ||
984 | &alignment_proc_fops, &se_usermode); | ||
985 | if (!res) | ||
986 | return -ENOMEM; | ||
987 | |||
988 | res = proc_create_data("kernel_alignment", S_IWUSR | S_IRUGO, dir, | ||
989 | &alignment_proc_fops, &se_kernmode_warn); | ||
990 | if (!res) | ||
991 | return -ENOMEM; | ||
992 | |||
993 | return 0; | ||
994 | } | ||
995 | |||
996 | fs_initcall(alignment_init); | ||
997 | #endif | ||
diff --git a/arch/sh/mm/Makefile b/arch/sh/mm/Makefile index dd5010c708e0..9fa11d655044 100644 --- a/arch/sh/mm/Makefile +++ b/arch/sh/mm/Makefile | |||
@@ -2,7 +2,7 @@ | |||
2 | # Makefile for the Linux SuperH-specific parts of the memory manager. | 2 | # Makefile for the Linux SuperH-specific parts of the memory manager. |
3 | # | 3 | # |
4 | 4 | ||
5 | obj-y := cache.o init.o consistent.o mmap.o | 5 | obj-y := alignment.o cache.o init.o consistent.o mmap.o |
6 | 6 | ||
7 | cacheops-$(CONFIG_CPU_SH2) := cache-sh2.o | 7 | cacheops-$(CONFIG_CPU_SH2) := cache-sh2.o |
8 | cacheops-$(CONFIG_CPU_SH2A) := cache-sh2a.o | 8 | cacheops-$(CONFIG_CPU_SH2A) := cache-sh2a.o |
diff --git a/arch/sh/mm/alignment.c b/arch/sh/mm/alignment.c new file mode 100644 index 000000000000..e615151eac3b --- /dev/null +++ b/arch/sh/mm/alignment.c | |||
@@ -0,0 +1,159 @@ | |||
1 | /* | ||
2 | * Alignment access counters and corresponding user-space interfaces. | ||
3 | * | ||
4 | * Copyright (C) 2009 ST Microelectronics | ||
5 | * Copyright (C) 2009 - 2010 Paul Mundt | ||
6 | * | ||
7 | * This file is subject to the terms and conditions of the GNU General Public | ||
8 | * License. See the file "COPYING" in the main directory of this archive | ||
9 | * for more details. | ||
10 | */ | ||
11 | #include <linux/module.h> | ||
12 | #include <linux/kernel.h> | ||
13 | #include <linux/seq_file.h> | ||
14 | #include <linux/proc_fs.h> | ||
15 | #include <linux/uaccess.h> | ||
16 | #include <asm/alignment.h> | ||
17 | |||
18 | static unsigned long se_user; | ||
19 | static unsigned long se_sys; | ||
20 | static unsigned long se_half; | ||
21 | static unsigned long se_word; | ||
22 | static unsigned long se_dword; | ||
23 | static unsigned long se_multi; | ||
24 | /* bitfield: 1: warn 2: fixup 4: signal -> combinations 2|4 && 1|2|4 are not | ||
25 | valid! */ | ||
26 | static int se_usermode = UM_WARN | UM_FIXUP; | ||
27 | /* 0: no warning 1: print a warning message, disabled by default */ | ||
28 | static int se_kernmode_warn; | ||
29 | |||
30 | void inc_unaligned_byte_access(void) | ||
31 | { | ||
32 | se_half++; | ||
33 | } | ||
34 | |||
35 | void inc_unaligned_word_access(void) | ||
36 | { | ||
37 | se_word++; | ||
38 | } | ||
39 | |||
40 | void inc_unaligned_dword_access(void) | ||
41 | { | ||
42 | se_dword++; | ||
43 | } | ||
44 | |||
45 | void inc_unaligned_multi_access(void) | ||
46 | { | ||
47 | se_multi++; | ||
48 | } | ||
49 | |||
50 | void inc_unaligned_user_access(void) | ||
51 | { | ||
52 | se_user++; | ||
53 | } | ||
54 | |||
55 | void inc_unaligned_kernel_access(void) | ||
56 | { | ||
57 | se_sys++; | ||
58 | } | ||
59 | |||
60 | unsigned int unaligned_user_action(void) | ||
61 | { | ||
62 | return se_usermode; | ||
63 | } | ||
64 | |||
65 | void unaligned_fixups_notify(struct task_struct *tsk, insn_size_t insn, | ||
66 | struct pt_regs *regs) | ||
67 | { | ||
68 | if (user_mode(regs) && (se_usermode & UM_WARN) && printk_ratelimit()) | ||
69 | pr_notice("Fixing up unaligned userspace access " | ||
70 | "in \"%s\" pid=%d pc=0x%p ins=0x%04hx\n", | ||
71 | tsk->comm, task_pid_nr(tsk), | ||
72 | (void *)regs->pc, insn); | ||
73 | else if (se_kernmode_warn && printk_ratelimit()) | ||
74 | pr_notice("Fixing up unaligned kernel access " | ||
75 | "in \"%s\" pid=%d pc=0x%p ins=0x%04hx\n", | ||
76 | tsk->comm, task_pid_nr(tsk), | ||
77 | (void *)regs->pc, insn); | ||
78 | } | ||
79 | |||
80 | static const char *se_usermode_action[] = { | ||
81 | "ignored", | ||
82 | "warn", | ||
83 | "fixup", | ||
84 | "fixup+warn", | ||
85 | "signal", | ||
86 | "signal+warn" | ||
87 | }; | ||
88 | |||
89 | static int alignment_proc_show(struct seq_file *m, void *v) | ||
90 | { | ||
91 | seq_printf(m, "User:\t\t%lu\n", se_user); | ||
92 | seq_printf(m, "System:\t\t%lu\n", se_sys); | ||
93 | seq_printf(m, "Half:\t\t%lu\n", se_half); | ||
94 | seq_printf(m, "Word:\t\t%lu\n", se_word); | ||
95 | seq_printf(m, "DWord:\t\t%lu\n", se_dword); | ||
96 | seq_printf(m, "Multi:\t\t%lu\n", se_multi); | ||
97 | seq_printf(m, "User faults:\t%i (%s)\n", se_usermode, | ||
98 | se_usermode_action[se_usermode]); | ||
99 | seq_printf(m, "Kernel faults:\t%i (fixup%s)\n", se_kernmode_warn, | ||
100 | se_kernmode_warn ? "+warn" : ""); | ||
101 | return 0; | ||
102 | } | ||
103 | |||
104 | static int alignment_proc_open(struct inode *inode, struct file *file) | ||
105 | { | ||
106 | return single_open(file, alignment_proc_show, NULL); | ||
107 | } | ||
108 | |||
109 | static ssize_t alignment_proc_write(struct file *file, | ||
110 | const char __user *buffer, size_t count, loff_t *pos) | ||
111 | { | ||
112 | int *data = PDE(file->f_path.dentry->d_inode)->data; | ||
113 | char mode; | ||
114 | |||
115 | if (count > 0) { | ||
116 | if (get_user(mode, buffer)) | ||
117 | return -EFAULT; | ||
118 | if (mode >= '0' && mode <= '5') | ||
119 | *data = mode - '0'; | ||
120 | } | ||
121 | return count; | ||
122 | } | ||
123 | |||
124 | static const struct file_operations alignment_proc_fops = { | ||
125 | .owner = THIS_MODULE, | ||
126 | .open = alignment_proc_open, | ||
127 | .read = seq_read, | ||
128 | .llseek = seq_lseek, | ||
129 | .release = single_release, | ||
130 | .write = alignment_proc_write, | ||
131 | }; | ||
132 | |||
133 | /* | ||
134 | * This needs to be done after sysctl_init, otherwise sys/ will be | ||
135 | * overwritten. Actually, this shouldn't be in sys/ at all since | ||
136 | * it isn't a sysctl, and it doesn't contain sysctl information. | ||
137 | * We now locate it in /proc/cpu/alignment instead. | ||
138 | */ | ||
139 | static int __init alignment_init(void) | ||
140 | { | ||
141 | struct proc_dir_entry *dir, *res; | ||
142 | |||
143 | dir = proc_mkdir("cpu", NULL); | ||
144 | if (!dir) | ||
145 | return -ENOMEM; | ||
146 | |||
147 | res = proc_create_data("alignment", S_IWUSR | S_IRUGO, dir, | ||
148 | &alignment_proc_fops, &se_usermode); | ||
149 | if (!res) | ||
150 | return -ENOMEM; | ||
151 | |||
152 | res = proc_create_data("kernel_alignment", S_IWUSR | S_IRUGO, dir, | ||
153 | &alignment_proc_fops, &se_kernmode_warn); | ||
154 | if (!res) | ||
155 | return -ENOMEM; | ||
156 | |||
157 | return 0; | ||
158 | } | ||
159 | fs_initcall(alignment_init); | ||