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 00000000000..b2595b8548e --- /dev/null +++ b/arch/sh/mm/alignment.c @@ -0,0 +1,189 @@ +/* + * Alignment access counters and corresponding user-space interfaces. + * + * Copyright (C) 2009 ST Microelectronics + * Copyright (C) 2009 - 2010 Paul Mundt + * + * This file is subject to the terms and conditions of the GNU General Public + * License.  See the file "COPYING" in the main directory of this archive + * for more details. + */ +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/seq_file.h> +#include <linux/proc_fs.h> +#include <linux/uaccess.h> +#include <asm/alignment.h> +#include <asm/processor.h> + +static unsigned long se_user; +static unsigned long se_sys; +static unsigned long se_half; +static unsigned long se_word; +static unsigned long se_dword; +static unsigned long se_multi; +/* bitfield: 1: warn 2: fixup 4: signal -> combinations 2|4 && 1|2|4 are not +   valid! */ +static int se_usermode = UM_WARN | UM_FIXUP; +/* 0: no warning 1: print a warning message, disabled by default */ +static int se_kernmode_warn; + +core_param(alignment, se_usermode, int, 0600); + +void inc_unaligned_byte_access(void) +{ +	se_half++; +} + +void inc_unaligned_word_access(void) +{ +	se_word++; +} + +void inc_unaligned_dword_access(void) +{ +	se_dword++; +} + +void inc_unaligned_multi_access(void) +{ +	se_multi++; +} + +void inc_unaligned_user_access(void) +{ +	se_user++; +} + +void inc_unaligned_kernel_access(void) +{ +	se_sys++; +} + +/* + * This defaults to the global policy which can be set from the command + * line, while processes can overload their preferences via prctl(). + */ +unsigned int unaligned_user_action(void) +{ +	unsigned int action = se_usermode; + +	if (current->thread.flags & SH_THREAD_UAC_SIGBUS) { +		action &= ~UM_FIXUP; +		action |= UM_SIGNAL; +	} + +	if (current->thread.flags & SH_THREAD_UAC_NOPRINT) +		action &= ~UM_WARN; + +	return action; +} + +int get_unalign_ctl(struct task_struct *tsk, unsigned long addr) +{ +	return put_user(tsk->thread.flags & SH_THREAD_UAC_MASK, +			(unsigned int __user *)addr); +} + +int set_unalign_ctl(struct task_struct *tsk, unsigned int val) +{ +	tsk->thread.flags = (tsk->thread.flags & ~SH_THREAD_UAC_MASK) | +			    (val & SH_THREAD_UAC_MASK); +	return 0; +} + +void unaligned_fixups_notify(struct task_struct *tsk, insn_size_t insn, +			     struct pt_regs *regs) +{ +	if (user_mode(regs) && (se_usermode & UM_WARN) && printk_ratelimit()) +		pr_notice("Fixing up unaligned userspace access " +			  "in \"%s\" pid=%d pc=0x%p ins=0x%04hx\n", +			  tsk->comm, task_pid_nr(tsk), +			  (void *)instruction_pointer(regs), insn); +	else if (se_kernmode_warn && printk_ratelimit()) +		pr_notice("Fixing up unaligned kernel access " +			  "in \"%s\" pid=%d pc=0x%p ins=0x%04hx\n", +			  tsk->comm, task_pid_nr(tsk), +			  (void *)instruction_pointer(regs), insn); +} + +static const char *se_usermode_action[] = { +	"ignored", +	"warn", +	"fixup", +	"fixup+warn", +	"signal", +	"signal+warn" +}; + +static int alignment_proc_show(struct seq_file *m, void *v) +{ +	seq_printf(m, "User:\t\t%lu\n", se_user); +	seq_printf(m, "System:\t\t%lu\n", se_sys); +	seq_printf(m, "Half:\t\t%lu\n", se_half); +	seq_printf(m, "Word:\t\t%lu\n", se_word); +	seq_printf(m, "DWord:\t\t%lu\n", se_dword); +	seq_printf(m, "Multi:\t\t%lu\n", se_multi); +	seq_printf(m, "User faults:\t%i (%s)\n", se_usermode, +			se_usermode_action[se_usermode]); +	seq_printf(m, "Kernel faults:\t%i (fixup%s)\n", se_kernmode_warn, +			se_kernmode_warn ? "+warn" : ""); +	return 0; +} + +static int alignment_proc_open(struct inode *inode, struct file *file) +{ +	return single_open(file, alignment_proc_show, NULL); +} + +static ssize_t alignment_proc_write(struct file *file, +		const char __user *buffer, size_t count, loff_t *pos) +{ +	int *data = PDE(file->f_path.dentry->d_inode)->data; +	char mode; + +	if (count > 0) { +		if (get_user(mode, buffer)) +			return -EFAULT; +		if (mode >= '0' && mode <= '5') +			*data = mode - '0'; +	} +	return count; +} + +static const struct file_operations alignment_proc_fops = { +	.owner		= THIS_MODULE, +	.open		= alignment_proc_open, +	.read		= seq_read, +	.llseek		= seq_lseek, +	.release	= single_release, +	.write		= alignment_proc_write, +}; + +/* + * This needs to be done after sysctl_init, otherwise sys/ will be + * overwritten.  Actually, this shouldn't be in sys/ at all since + * it isn't a sysctl, and it doesn't contain sysctl information. + * We now locate it in /proc/cpu/alignment instead. + */ +static int __init alignment_init(void) +{ +	struct proc_dir_entry *dir, *res; + +	dir = proc_mkdir("cpu", NULL); +	if (!dir) +		return -ENOMEM; + +	res = proc_create_data("alignment", S_IWUSR | S_IRUGO, dir, +			       &alignment_proc_fops, &se_usermode); +	if (!res) +		return -ENOMEM; + +        res = proc_create_data("kernel_alignment", S_IWUSR | S_IRUGO, dir, +			       &alignment_proc_fops, &se_kernmode_warn); +        if (!res) +                return -ENOMEM; + +	return 0; +} +fs_initcall(alignment_init);  |