xref: /openbmc/linux/arch/sh/mm/alignment.c (revision 03ab8e6297acd1bc0eedaa050e2a1635c576fd11)
1a99eae54SPaul Mundt /*
2a99eae54SPaul Mundt  * Alignment access counters and corresponding user-space interfaces.
3a99eae54SPaul Mundt  *
4a99eae54SPaul Mundt  * Copyright (C) 2009 ST Microelectronics
5a99eae54SPaul Mundt  * Copyright (C) 2009 - 2010 Paul Mundt
6a99eae54SPaul Mundt  *
7a99eae54SPaul Mundt  * This file is subject to the terms and conditions of the GNU General Public
8a99eae54SPaul Mundt  * License.  See the file "COPYING" in the main directory of this archive
9a99eae54SPaul Mundt  * for more details.
10a99eae54SPaul Mundt  */
11a99eae54SPaul Mundt #include <linux/module.h>
12a99eae54SPaul Mundt #include <linux/kernel.h>
13a99eae54SPaul Mundt #include <linux/seq_file.h>
14a99eae54SPaul Mundt #include <linux/proc_fs.h>
15a99eae54SPaul Mundt #include <linux/uaccess.h>
169ab3a15dSPaul Mundt #include <linux/ratelimit.h>
17a99eae54SPaul Mundt #include <asm/alignment.h>
1894ea5e44SPaul Mundt #include <asm/processor.h>
19a99eae54SPaul Mundt 
20a99eae54SPaul Mundt static unsigned long se_user;
21a99eae54SPaul Mundt static unsigned long se_sys;
22a99eae54SPaul Mundt static unsigned long se_half;
23a99eae54SPaul Mundt static unsigned long se_word;
24a99eae54SPaul Mundt static unsigned long se_dword;
25a99eae54SPaul Mundt static unsigned long se_multi;
26a99eae54SPaul Mundt /* bitfield: 1: warn 2: fixup 4: signal -> combinations 2|4 && 1|2|4 are not
27a99eae54SPaul Mundt    valid! */
28a99eae54SPaul Mundt static int se_usermode = UM_WARN | UM_FIXUP;
29a99eae54SPaul Mundt /* 0: no warning 1: print a warning message, disabled by default */
30a99eae54SPaul Mundt static int se_kernmode_warn;
31a99eae54SPaul Mundt 
327c1b2c68SPaul Mundt core_param(alignment, se_usermode, int, 0600);
337c1b2c68SPaul Mundt 
inc_unaligned_byte_access(void)34a99eae54SPaul Mundt void inc_unaligned_byte_access(void)
35a99eae54SPaul Mundt {
36a99eae54SPaul Mundt 	se_half++;
37a99eae54SPaul Mundt }
38a99eae54SPaul Mundt 
inc_unaligned_word_access(void)39a99eae54SPaul Mundt void inc_unaligned_word_access(void)
40a99eae54SPaul Mundt {
41a99eae54SPaul Mundt 	se_word++;
42a99eae54SPaul Mundt }
43a99eae54SPaul Mundt 
inc_unaligned_dword_access(void)44a99eae54SPaul Mundt void inc_unaligned_dword_access(void)
45a99eae54SPaul Mundt {
46a99eae54SPaul Mundt 	se_dword++;
47a99eae54SPaul Mundt }
48a99eae54SPaul Mundt 
inc_unaligned_multi_access(void)49a99eae54SPaul Mundt void inc_unaligned_multi_access(void)
50a99eae54SPaul Mundt {
51a99eae54SPaul Mundt 	se_multi++;
52a99eae54SPaul Mundt }
53a99eae54SPaul Mundt 
inc_unaligned_user_access(void)54a99eae54SPaul Mundt void inc_unaligned_user_access(void)
55a99eae54SPaul Mundt {
56a99eae54SPaul Mundt 	se_user++;
57a99eae54SPaul Mundt }
58a99eae54SPaul Mundt 
inc_unaligned_kernel_access(void)59a99eae54SPaul Mundt void inc_unaligned_kernel_access(void)
60a99eae54SPaul Mundt {
61a99eae54SPaul Mundt 	se_sys++;
62a99eae54SPaul Mundt }
63a99eae54SPaul Mundt 
6494ea5e44SPaul Mundt /*
6594ea5e44SPaul Mundt  * This defaults to the global policy which can be set from the command
6694ea5e44SPaul Mundt  * line, while processes can overload their preferences via prctl().
6794ea5e44SPaul Mundt  */
unaligned_user_action(void)68a99eae54SPaul Mundt unsigned int unaligned_user_action(void)
69a99eae54SPaul Mundt {
7094ea5e44SPaul Mundt 	unsigned int action = se_usermode;
7194ea5e44SPaul Mundt 
7294ea5e44SPaul Mundt 	if (current->thread.flags & SH_THREAD_UAC_SIGBUS) {
7394ea5e44SPaul Mundt 		action &= ~UM_FIXUP;
7494ea5e44SPaul Mundt 		action |= UM_SIGNAL;
7594ea5e44SPaul Mundt 	}
7694ea5e44SPaul Mundt 
7794ea5e44SPaul Mundt 	if (current->thread.flags & SH_THREAD_UAC_NOPRINT)
7894ea5e44SPaul Mundt 		action &= ~UM_WARN;
7994ea5e44SPaul Mundt 
8094ea5e44SPaul Mundt 	return action;
8194ea5e44SPaul Mundt }
8294ea5e44SPaul Mundt 
get_unalign_ctl(struct task_struct * tsk,unsigned long addr)8394ea5e44SPaul Mundt int get_unalign_ctl(struct task_struct *tsk, unsigned long addr)
8494ea5e44SPaul Mundt {
8594ea5e44SPaul Mundt 	return put_user(tsk->thread.flags & SH_THREAD_UAC_MASK,
8694ea5e44SPaul Mundt 			(unsigned int __user *)addr);
8794ea5e44SPaul Mundt }
8894ea5e44SPaul Mundt 
set_unalign_ctl(struct task_struct * tsk,unsigned int val)8994ea5e44SPaul Mundt int set_unalign_ctl(struct task_struct *tsk, unsigned int val)
9094ea5e44SPaul Mundt {
9194ea5e44SPaul Mundt 	tsk->thread.flags = (tsk->thread.flags & ~SH_THREAD_UAC_MASK) |
9294ea5e44SPaul Mundt 			    (val & SH_THREAD_UAC_MASK);
9394ea5e44SPaul Mundt 	return 0;
94a99eae54SPaul Mundt }
95a99eae54SPaul Mundt 
unaligned_fixups_notify(struct task_struct * tsk,insn_size_t insn,struct pt_regs * regs)96a99eae54SPaul Mundt void unaligned_fixups_notify(struct task_struct *tsk, insn_size_t insn,
97a99eae54SPaul Mundt 			     struct pt_regs *regs)
98a99eae54SPaul Mundt {
999ab3a15dSPaul Mundt 	if (user_mode(regs) && (se_usermode & UM_WARN))
1009ab3a15dSPaul Mundt 		pr_notice_ratelimited("Fixing up unaligned userspace access "
101a99eae54SPaul Mundt 			  "in \"%s\" pid=%d pc=0x%p ins=0x%04hx\n",
102a99eae54SPaul Mundt 			  tsk->comm, task_pid_nr(tsk),
10388ea1a44SPaul Mundt 			  (void *)instruction_pointer(regs), insn);
1049ab3a15dSPaul Mundt 	else if (se_kernmode_warn)
1059ab3a15dSPaul Mundt 		pr_notice_ratelimited("Fixing up unaligned kernel access "
106a99eae54SPaul Mundt 			  "in \"%s\" pid=%d pc=0x%p ins=0x%04hx\n",
107a99eae54SPaul Mundt 			  tsk->comm, task_pid_nr(tsk),
10888ea1a44SPaul Mundt 			  (void *)instruction_pointer(regs), insn);
109a99eae54SPaul Mundt }
110a99eae54SPaul Mundt 
111a99eae54SPaul Mundt static const char *se_usermode_action[] = {
112a99eae54SPaul Mundt 	"ignored",
113a99eae54SPaul Mundt 	"warn",
114a99eae54SPaul Mundt 	"fixup",
115a99eae54SPaul Mundt 	"fixup+warn",
116a99eae54SPaul Mundt 	"signal",
117a99eae54SPaul Mundt 	"signal+warn"
118a99eae54SPaul Mundt };
119a99eae54SPaul Mundt 
alignment_proc_show(struct seq_file * m,void * v)120a99eae54SPaul Mundt static int alignment_proc_show(struct seq_file *m, void *v)
121a99eae54SPaul Mundt {
122a99eae54SPaul Mundt 	seq_printf(m, "User:\t\t%lu\n", se_user);
123a99eae54SPaul Mundt 	seq_printf(m, "System:\t\t%lu\n", se_sys);
124a99eae54SPaul Mundt 	seq_printf(m, "Half:\t\t%lu\n", se_half);
125a99eae54SPaul Mundt 	seq_printf(m, "Word:\t\t%lu\n", se_word);
126a99eae54SPaul Mundt 	seq_printf(m, "DWord:\t\t%lu\n", se_dword);
127a99eae54SPaul Mundt 	seq_printf(m, "Multi:\t\t%lu\n", se_multi);
128a99eae54SPaul Mundt 	seq_printf(m, "User faults:\t%i (%s)\n", se_usermode,
129a99eae54SPaul Mundt 			se_usermode_action[se_usermode]);
130a99eae54SPaul Mundt 	seq_printf(m, "Kernel faults:\t%i (fixup%s)\n", se_kernmode_warn,
131a99eae54SPaul Mundt 			se_kernmode_warn ? "+warn" : "");
132a99eae54SPaul Mundt 	return 0;
133a99eae54SPaul Mundt }
134a99eae54SPaul Mundt 
alignment_proc_open(struct inode * inode,struct file * file)135a99eae54SPaul Mundt static int alignment_proc_open(struct inode *inode, struct file *file)
136a99eae54SPaul Mundt {
137a99eae54SPaul Mundt 	return single_open(file, alignment_proc_show, NULL);
138a99eae54SPaul Mundt }
139a99eae54SPaul Mundt 
alignment_proc_write(struct file * file,const char __user * buffer,size_t count,loff_t * pos)140a99eae54SPaul Mundt static ssize_t alignment_proc_write(struct file *file,
141a99eae54SPaul Mundt 		const char __user *buffer, size_t count, loff_t *pos)
142a99eae54SPaul Mundt {
143*359745d7SMuchun Song 	int *data = pde_data(file_inode(file));
144a99eae54SPaul Mundt 	char mode;
145a99eae54SPaul Mundt 
146a99eae54SPaul Mundt 	if (count > 0) {
147a99eae54SPaul Mundt 		if (get_user(mode, buffer))
148a99eae54SPaul Mundt 			return -EFAULT;
149a99eae54SPaul Mundt 		if (mode >= '0' && mode <= '5')
150a99eae54SPaul Mundt 			*data = mode - '0';
151a99eae54SPaul Mundt 	}
152a99eae54SPaul Mundt 	return count;
153a99eae54SPaul Mundt }
154a99eae54SPaul Mundt 
15597a32539SAlexey Dobriyan static const struct proc_ops alignment_proc_ops = {
15697a32539SAlexey Dobriyan 	.proc_open	= alignment_proc_open,
15797a32539SAlexey Dobriyan 	.proc_read	= seq_read,
15897a32539SAlexey Dobriyan 	.proc_lseek	= seq_lseek,
15997a32539SAlexey Dobriyan 	.proc_release	= single_release,
16097a32539SAlexey Dobriyan 	.proc_write	= alignment_proc_write,
161a99eae54SPaul Mundt };
162a99eae54SPaul Mundt 
163a99eae54SPaul Mundt /*
164d8c0418aSLuis Chamberlain  * This needs to be done after sysctl_init_bases(), otherwise sys/ will be
165a99eae54SPaul Mundt  * overwritten.  Actually, this shouldn't be in sys/ at all since
166a99eae54SPaul Mundt  * it isn't a sysctl, and it doesn't contain sysctl information.
167a99eae54SPaul Mundt  * We now locate it in /proc/cpu/alignment instead.
168a99eae54SPaul Mundt  */
alignment_init(void)169a99eae54SPaul Mundt static int __init alignment_init(void)
170a99eae54SPaul Mundt {
171a99eae54SPaul Mundt 	struct proc_dir_entry *dir, *res;
172a99eae54SPaul Mundt 
173a99eae54SPaul Mundt 	dir = proc_mkdir("cpu", NULL);
174a99eae54SPaul Mundt 	if (!dir)
175a99eae54SPaul Mundt 		return -ENOMEM;
176a99eae54SPaul Mundt 
177a99eae54SPaul Mundt 	res = proc_create_data("alignment", S_IWUSR | S_IRUGO, dir,
17897a32539SAlexey Dobriyan 			       &alignment_proc_ops, &se_usermode);
179a99eae54SPaul Mundt 	if (!res)
180a99eae54SPaul Mundt 		return -ENOMEM;
181a99eae54SPaul Mundt 
182a99eae54SPaul Mundt         res = proc_create_data("kernel_alignment", S_IWUSR | S_IRUGO, dir,
18397a32539SAlexey Dobriyan 			       &alignment_proc_ops, &se_kernmode_warn);
184a99eae54SPaul Mundt         if (!res)
185a99eae54SPaul Mundt                 return -ENOMEM;
186a99eae54SPaul Mundt 
187a99eae54SPaul Mundt 	return 0;
188a99eae54SPaul Mundt }
189a99eae54SPaul Mundt fs_initcall(alignment_init);
190