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