aboutsummaryrefslogtreecommitdiffstats
path: root/arch/sh
diff options
context:
space:
mode:
authorPaul Mundt <lethal@linux-sh.org>2010-01-12 02:12:25 -0500
committerPaul Mundt <lethal@linux-sh.org>2010-01-12 02:12:25 -0500
commita99eae5417a09e0be66bf574a9a79a2a7388c967 (patch)
tree5024736c9afd76124e2f5f5424ecc153f6218c8e /arch/sh
parent776258df925acd0563f471ee4b3f19bbffb3c04f (diff)
sh: Split out the unaligned counters and user bits.
This splits out the unaligned access counters and userspace bits in to their own generic interface, which will allow them to be wired up on sh64 too. Signed-off-by: Paul Mundt <lethal@linux-sh.org>
Diffstat (limited to 'arch/sh')
-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);