aboutsummaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
Diffstat (limited to 'arch')
-rw-r--r--arch/sh/include/asm/alignment.h21
-rw-r--r--arch/sh/kernel/traps_32.c151
-rw-r--r--arch/sh/mm/Makefile2
-rw-r--r--arch/sh/mm/alignment.c159
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
6extern void inc_unaligned_byte_access(void);
7extern void inc_unaligned_word_access(void);
8extern void inc_unaligned_dword_access(void);
9extern void inc_unaligned_multi_access(void);
10extern void inc_unaligned_user_access(void);
11extern 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
17extern unsigned int unaligned_user_action(void);
18
19extern 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
51static unsigned long se_user;
52static unsigned long se_sys;
53static unsigned long se_half;
54static unsigned long se_word;
55static unsigned long se_dword;
56static unsigned long se_multi;
57/* bitfield: 1: warn 2: fixup 4: signal -> combinations 2|4 && 1|2|4 are not
58 valid! */
59static int se_usermode = 3;
60/* 0: no warning 1: print a warning message, disabled by default */
61static int se_kernmode_warn;
62
63#ifdef CONFIG_PROC_FS
64static const char *se_usermode_action[] = {
65 "ignored",
66 "warn",
67 "fixup",
68 "fixup+warn",
69 "signal",
70 "signal+warn"
71};
72
73static 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
88static int alignment_proc_open(struct inode *inode, struct file *file)
89{
90 return single_open(file, alignment_proc_show, NULL);
91}
92
93static 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
108static 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
118static void dump_mem(const char *str, unsigned long bottom, unsigned long top) 50static 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 */
665uspace_segv: 583uspace_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}
966EXPORT_SYMBOL(dump_stack); 880EXPORT_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 */
975static 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
996fs_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
5obj-y := cache.o init.o consistent.o mmap.o 5obj-y := alignment.o cache.o init.o consistent.o mmap.o
6 6
7cacheops-$(CONFIG_CPU_SH2) := cache-sh2.o 7cacheops-$(CONFIG_CPU_SH2) := cache-sh2.o
8cacheops-$(CONFIG_CPU_SH2A) := cache-sh2a.o 8cacheops-$(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
18static unsigned long se_user;
19static unsigned long se_sys;
20static unsigned long se_half;
21static unsigned long se_word;
22static unsigned long se_dword;
23static unsigned long se_multi;
24/* bitfield: 1: warn 2: fixup 4: signal -> combinations 2|4 && 1|2|4 are not
25 valid! */
26static int se_usermode = UM_WARN | UM_FIXUP;
27/* 0: no warning 1: print a warning message, disabled by default */
28static int se_kernmode_warn;
29
30void inc_unaligned_byte_access(void)
31{
32 se_half++;
33}
34
35void inc_unaligned_word_access(void)
36{
37 se_word++;
38}
39
40void inc_unaligned_dword_access(void)
41{
42 se_dword++;
43}
44
45void inc_unaligned_multi_access(void)
46{
47 se_multi++;
48}
49
50void inc_unaligned_user_access(void)
51{
52 se_user++;
53}
54
55void inc_unaligned_kernel_access(void)
56{
57 se_sys++;
58}
59
60unsigned int unaligned_user_action(void)
61{
62 return se_usermode;
63}
64
65void 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
80static const char *se_usermode_action[] = {
81 "ignored",
82 "warn",
83 "fixup",
84 "fixup+warn",
85 "signal",
86 "signal+warn"
87};
88
89static 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
104static int alignment_proc_open(struct inode *inode, struct file *file)
105{
106 return single_open(file, alignment_proc_show, NULL);
107}
108
109static 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
124static 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 */
139static 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}
159fs_initcall(alignment_init);