linux/arch/sh/mm/alignment.c
<<
>>
Prefs
   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 <linux/ratelimit.h>
  17#include <asm/alignment.h>
  18#include <asm/processor.h>
  19
  20static unsigned long se_user;
  21static unsigned long se_sys;
  22static unsigned long se_half;
  23static unsigned long se_word;
  24static unsigned long se_dword;
  25static unsigned long se_multi;
  26/* bitfield: 1: warn 2: fixup 4: signal -> combinations 2|4 && 1|2|4 are not
  27   valid! */
  28static int se_usermode = UM_WARN | UM_FIXUP;
  29/* 0: no warning 1: print a warning message, disabled by default */
  30static int se_kernmode_warn;
  31
  32core_param(alignment, se_usermode, int, 0600);
  33
  34void inc_unaligned_byte_access(void)
  35{
  36        se_half++;
  37}
  38
  39void inc_unaligned_word_access(void)
  40{
  41        se_word++;
  42}
  43
  44void inc_unaligned_dword_access(void)
  45{
  46        se_dword++;
  47}
  48
  49void inc_unaligned_multi_access(void)
  50{
  51        se_multi++;
  52}
  53
  54void inc_unaligned_user_access(void)
  55{
  56        se_user++;
  57}
  58
  59void inc_unaligned_kernel_access(void)
  60{
  61        se_sys++;
  62}
  63
  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 */
  68unsigned int unaligned_user_action(void)
  69{
  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
  83int 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
  89int 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;
  94}
  95
  96void unaligned_fixups_notify(struct task_struct *tsk, insn_size_t insn,
  97                             struct pt_regs *regs)
  98{
  99        if (user_mode(regs) && (se_usermode & UM_WARN))
 100                pr_notice_ratelimited("Fixing up unaligned userspace access "
 101                          "in \"%s\" pid=%d pc=0x%p ins=0x%04hx\n",
 102                          tsk->comm, task_pid_nr(tsk),
 103                          (void *)instruction_pointer(regs), insn);
 104        else if (se_kernmode_warn)
 105                pr_notice_ratelimited("Fixing up unaligned kernel access "
 106                          "in \"%s\" pid=%d pc=0x%p ins=0x%04hx\n",
 107                          tsk->comm, task_pid_nr(tsk),
 108                          (void *)instruction_pointer(regs), insn);
 109}
 110
 111static const char *se_usermode_action[] = {
 112        "ignored",
 113        "warn",
 114        "fixup",
 115        "fixup+warn",
 116        "signal",
 117        "signal+warn"
 118};
 119
 120static 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
 135static int alignment_proc_open(struct inode *inode, struct file *file)
 136{
 137        return single_open(file, alignment_proc_show, NULL);
 138}
 139
 140static ssize_t alignment_proc_write(struct file *file,
 141                const char __user *buffer, size_t count, loff_t *pos)
 142{
 143        int *data = PDE_DATA(file_inode(file));
 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
 155static const struct proc_ops alignment_proc_ops = {
 156        .proc_open      = alignment_proc_open,
 157        .proc_read      = seq_read,
 158        .proc_lseek     = seq_lseek,
 159        .proc_release   = single_release,
 160        .proc_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_ops, &se_usermode);
 179        if (!res)
 180                return -ENOMEM;
 181
 182        res = proc_create_data("kernel_alignment", S_IWUSR | S_IRUGO, dir,
 183                               &alignment_proc_ops, &se_kernmode_warn);
 184        if (!res)
 185                return -ENOMEM;
 186
 187        return 0;
 188}
 189fs_initcall(alignment_init);
 190