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