diff options
Diffstat (limited to 'arch/sh/mm/alignment.c')
-rw-r--r-- | arch/sh/mm/alignment.c | 189 |
1 files changed, 189 insertions, 0 deletions
diff --git a/arch/sh/mm/alignment.c b/arch/sh/mm/alignment.c new file mode 100644 index 000000000000..b2595b8548ee --- /dev/null +++ b/arch/sh/mm/alignment.c | |||
@@ -0,0 +1,189 @@ | |||
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 | #include <asm/processor.h> | ||
18 | |||
19 | static unsigned long se_user; | ||
20 | static unsigned long se_sys; | ||
21 | static unsigned long se_half; | ||
22 | static unsigned long se_word; | ||
23 | static unsigned long se_dword; | ||
24 | static unsigned long se_multi; | ||
25 | /* bitfield: 1: warn 2: fixup 4: signal -> combinations 2|4 && 1|2|4 are not | ||
26 | valid! */ | ||
27 | static int se_usermode = UM_WARN | UM_FIXUP; | ||
28 | /* 0: no warning 1: print a warning message, disabled by default */ | ||
29 | static int se_kernmode_warn; | ||
30 | |||
31 | core_param(alignment, se_usermode, int, 0600); | ||
32 | |||
33 | void inc_unaligned_byte_access(void) | ||
34 | { | ||
35 | se_half++; | ||
36 | } | ||
37 | |||
38 | void inc_unaligned_word_access(void) | ||
39 | { | ||
40 | se_word++; | ||
41 | } | ||
42 | |||
43 | void inc_unaligned_dword_access(void) | ||
44 | { | ||
45 | se_dword++; | ||
46 | } | ||
47 | |||
48 | void inc_unaligned_multi_access(void) | ||
49 | { | ||
50 | se_multi++; | ||
51 | } | ||
52 | |||
53 | void inc_unaligned_user_access(void) | ||
54 | { | ||
55 | se_user++; | ||
56 | } | ||
57 | |||
58 | void inc_unaligned_kernel_access(void) | ||
59 | { | ||
60 | se_sys++; | ||
61 | } | ||
62 | |||
63 | /* | ||
64 | * This defaults to the global policy which can be set from the command | ||
65 | * line, while processes can overload their preferences via prctl(). | ||
66 | */ | ||
67 | unsigned int unaligned_user_action(void) | ||
68 | { | ||
69 | unsigned int action = se_usermode; | ||
70 | |||
71 | if (current->thread.flags & SH_THREAD_UAC_SIGBUS) { | ||
72 | action &= ~UM_FIXUP; | ||
73 | action |= UM_SIGNAL; | ||
74 | } | ||
75 | |||
76 | if (current->thread.flags & SH_THREAD_UAC_NOPRINT) | ||
77 | action &= ~UM_WARN; | ||
78 | |||
79 | return action; | ||
80 | } | ||
81 | |||
82 | int get_unalign_ctl(struct task_struct *tsk, unsigned long addr) | ||
83 | { | ||
84 | return put_user(tsk->thread.flags & SH_THREAD_UAC_MASK, | ||
85 | (unsigned int __user *)addr); | ||
86 | } | ||
87 | |||
88 | int set_unalign_ctl(struct task_struct *tsk, unsigned int val) | ||
89 | { | ||
90 | tsk->thread.flags = (tsk->thread.flags & ~SH_THREAD_UAC_MASK) | | ||
91 | (val & SH_THREAD_UAC_MASK); | ||
92 | return 0; | ||
93 | } | ||
94 | |||
95 | void unaligned_fixups_notify(struct task_struct *tsk, insn_size_t insn, | ||
96 | struct pt_regs *regs) | ||
97 | { | ||
98 | if (user_mode(regs) && (se_usermode & UM_WARN) && printk_ratelimit()) | ||
99 | pr_notice("Fixing up unaligned userspace access " | ||
100 | "in \"%s\" pid=%d pc=0x%p ins=0x%04hx\n", | ||
101 | tsk->comm, task_pid_nr(tsk), | ||
102 | (void *)instruction_pointer(regs), insn); | ||
103 | else if (se_kernmode_warn && printk_ratelimit()) | ||
104 | pr_notice("Fixing up unaligned kernel access " | ||
105 | "in \"%s\" pid=%d pc=0x%p ins=0x%04hx\n", | ||
106 | tsk->comm, task_pid_nr(tsk), | ||
107 | (void *)instruction_pointer(regs), insn); | ||
108 | } | ||
109 | |||
110 | static const char *se_usermode_action[] = { | ||
111 | "ignored", | ||
112 | "warn", | ||
113 | "fixup", | ||
114 | "fixup+warn", | ||
115 | "signal", | ||
116 | "signal+warn" | ||
117 | }; | ||
118 | |||
119 | static int alignment_proc_show(struct seq_file *m, void *v) | ||
120 | { | ||
121 | seq_printf(m, "User:\t\t%lu\n", se_user); | ||
122 | seq_printf(m, "System:\t\t%lu\n", se_sys); | ||
123 | seq_printf(m, "Half:\t\t%lu\n", se_half); | ||
124 | seq_printf(m, "Word:\t\t%lu\n", se_word); | ||
125 | seq_printf(m, "DWord:\t\t%lu\n", se_dword); | ||
126 | seq_printf(m, "Multi:\t\t%lu\n", se_multi); | ||
127 | seq_printf(m, "User faults:\t%i (%s)\n", se_usermode, | ||
128 | se_usermode_action[se_usermode]); | ||
129 | seq_printf(m, "Kernel faults:\t%i (fixup%s)\n", se_kernmode_warn, | ||
130 | se_kernmode_warn ? "+warn" : ""); | ||
131 | return 0; | ||
132 | } | ||
133 | |||
134 | static int alignment_proc_open(struct inode *inode, struct file *file) | ||
135 | { | ||
136 | return single_open(file, alignment_proc_show, NULL); | ||
137 | } | ||
138 | |||
139 | static ssize_t alignment_proc_write(struct file *file, | ||
140 | const char __user *buffer, size_t count, loff_t *pos) | ||
141 | { | ||
142 | int *data = PDE(file->f_path.dentry->d_inode)->data; | ||
143 | char mode; | ||
144 | |||
145 | if (count > 0) { | ||
146 | if (get_user(mode, buffer)) | ||
147 | return -EFAULT; | ||
148 | if (mode >= '0' && mode <= '5') | ||
149 | *data = mode - '0'; | ||
150 | } | ||
151 | return count; | ||
152 | } | ||
153 | |||
154 | static const struct file_operations alignment_proc_fops = { | ||
155 | .owner = THIS_MODULE, | ||
156 | .open = alignment_proc_open, | ||
157 | .read = seq_read, | ||
158 | .llseek = seq_lseek, | ||
159 | .release = single_release, | ||
160 | .write = alignment_proc_write, | ||
161 | }; | ||
162 | |||
163 | /* | ||
164 | * This needs to be done after sysctl_init, otherwise sys/ will be | ||
165 | * overwritten. Actually, this shouldn't be in sys/ at all since | ||
166 | * it isn't a sysctl, and it doesn't contain sysctl information. | ||
167 | * We now locate it in /proc/cpu/alignment instead. | ||
168 | */ | ||
169 | static int __init alignment_init(void) | ||
170 | { | ||
171 | struct proc_dir_entry *dir, *res; | ||
172 | |||
173 | dir = proc_mkdir("cpu", NULL); | ||
174 | if (!dir) | ||
175 | return -ENOMEM; | ||
176 | |||
177 | res = proc_create_data("alignment", S_IWUSR | S_IRUGO, dir, | ||
178 | &alignment_proc_fops, &se_usermode); | ||
179 | if (!res) | ||
180 | return -ENOMEM; | ||
181 | |||
182 | res = proc_create_data("kernel_alignment", S_IWUSR | S_IRUGO, dir, | ||
183 | &alignment_proc_fops, &se_kernmode_warn); | ||
184 | if (!res) | ||
185 | return -ENOMEM; | ||
186 | |||
187 | return 0; | ||
188 | } | ||
189 | fs_initcall(alignment_init); | ||