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 19static unsigned long se_user; 20static unsigned long se_sys; 21static unsigned long se_half; 22static unsigned long se_word; 23static unsigned long se_dword; 24static unsigned long se_multi; 25/* bitfield: 1: warn 2: fixup 4: signal -> combinations 2|4 && 1|2|4 are not 26 valid! */ 27static int se_usermode = UM_WARN | UM_FIXUP; 28/* 0: no warning 1: print a warning message, disabled by default */ 29static int se_kernmode_warn; 30 31core_param(alignment, se_usermode, int, 0600); 32 33void inc_unaligned_byte_access(void) 34{ 35 se_half++; 36} 37 38void inc_unaligned_word_access(void) 39{ 40 se_word++; 41} 42 43void inc_unaligned_dword_access(void) 44{ 45 se_dword++; 46} 47 48void inc_unaligned_multi_access(void) 49{ 50 se_multi++; 51} 52 53void inc_unaligned_user_access(void) 54{ 55 se_user++; 56} 57 58void 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 */ 67unsigned 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 82int 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 88int 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 95void 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 110static const char *se_usermode_action[] = { 111 "ignored", 112 "warn", 113 "fixup", 114 "fixup+warn", 115 "signal", 116 "signal+warn" 117}; 118 119static 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 134static int alignment_proc_open(struct inode *inode, struct file *file) 135{ 136 return single_open(file, alignment_proc_show, NULL); 137} 138 139static 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 154static 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 */ 169static 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} 189fs_initcall(alignment_init); 190