linux/arch/arc/kernel/unaligned.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2011-2012 Synopsys (www.synopsys.com)
   3 *
   4 * This program is free software; you can redistribute it and/or modify
   5 * it under the terms of the GNU General Public License version 2 as
   6 * published by the Free Software Foundation.
   7 *
   8 * vineetg : May 2011
   9 *  -Adapted (from .26 to .35)
  10 *  -original contribution by Tim.yao@amlogic.com
  11 *
  12 */
  13
  14#include <linux/types.h>
  15#include <linux/ptrace.h>
  16#include <linux/uaccess.h>
  17#include <asm/disasm.h>
  18
  19#define __get8_unaligned_check(val, addr, err)          \
  20        __asm__(                                        \
  21        "1:     ldb.ab  %1, [%2, 1]\n"                  \
  22        "2:\n"                                          \
  23        "       .section .fixup,\"ax\"\n"               \
  24        "       .align  4\n"                            \
  25        "3:     mov     %0, 1\n"                        \
  26        "       b       2b\n"                           \
  27        "       .previous\n"                            \
  28        "       .section __ex_table,\"a\"\n"            \
  29        "       .align  4\n"                            \
  30        "       .long   1b, 3b\n"                       \
  31        "       .previous\n"                            \
  32        : "=r" (err), "=&r" (val), "=r" (addr)          \
  33        : "0" (err), "2" (addr))
  34
  35#define get16_unaligned_check(val, addr)                \
  36        do {                                            \
  37                unsigned int err = 0, v, a = addr;      \
  38                __get8_unaligned_check(v, a, err);      \
  39                val =  v ;                              \
  40                __get8_unaligned_check(v, a, err);      \
  41                val |= v << 8;                          \
  42                if (err)                                \
  43                        goto fault;                     \
  44        } while (0)
  45
  46#define get32_unaligned_check(val, addr)                \
  47        do {                                            \
  48                unsigned int err = 0, v, a = addr;      \
  49                __get8_unaligned_check(v, a, err);      \
  50                val =  v << 0;                          \
  51                __get8_unaligned_check(v, a, err);      \
  52                val |= v << 8;                          \
  53                __get8_unaligned_check(v, a, err);      \
  54                val |= v << 16;                         \
  55                __get8_unaligned_check(v, a, err);      \
  56                val |= v << 24;                         \
  57                if (err)                                \
  58                        goto fault;                     \
  59        } while (0)
  60
  61#define put16_unaligned_check(val, addr)                \
  62        do {                                            \
  63                unsigned int err = 0, v = val, a = addr;\
  64                                                        \
  65                __asm__(                                \
  66                "1:     stb.ab  %1, [%2, 1]\n"          \
  67                "       lsr %1, %1, 8\n"                \
  68                "2:     stb     %1, [%2]\n"             \
  69                "3:\n"                                  \
  70                "       .section .fixup,\"ax\"\n"       \
  71                "       .align  4\n"                    \
  72                "4:     mov     %0, 1\n"                \
  73                "       b       3b\n"                   \
  74                "       .previous\n"                    \
  75                "       .section __ex_table,\"a\"\n"    \
  76                "       .align  4\n"                    \
  77                "       .long   1b, 4b\n"               \
  78                "       .long   2b, 4b\n"               \
  79                "       .previous\n"                    \
  80                : "=r" (err), "=&r" (v), "=&r" (a)      \
  81                : "0" (err), "1" (v), "2" (a));         \
  82                                                        \
  83                if (err)                                \
  84                        goto fault;                     \
  85        } while (0)
  86
  87#define put32_unaligned_check(val, addr)                \
  88        do {                                            \
  89                unsigned int err = 0, v = val, a = addr;\
  90                __asm__(                                \
  91                                                        \
  92                "1:     stb.ab  %1, [%2, 1]\n"          \
  93                "       lsr %1, %1, 8\n"                \
  94                "2:     stb.ab  %1, [%2, 1]\n"          \
  95                "       lsr %1, %1, 8\n"                \
  96                "3:     stb.ab  %1, [%2, 1]\n"          \
  97                "       lsr %1, %1, 8\n"                \
  98                "4:     stb     %1, [%2]\n"             \
  99                "5:\n"                                  \
 100                "       .section .fixup,\"ax\"\n"       \
 101                "       .align  4\n"                    \
 102                "6:     mov     %0, 1\n"                \
 103                "       b       5b\n"                   \
 104                "       .previous\n"                    \
 105                "       .section __ex_table,\"a\"\n"    \
 106                "       .align  4\n"                    \
 107                "       .long   1b, 6b\n"               \
 108                "       .long   2b, 6b\n"               \
 109                "       .long   3b, 6b\n"               \
 110                "       .long   4b, 6b\n"               \
 111                "       .previous\n"                    \
 112                : "=r" (err), "=&r" (v), "=&r" (a)      \
 113                : "0" (err), "1" (v), "2" (a));         \
 114                                                        \
 115                if (err)                                \
 116                        goto fault;                     \
 117        } while (0)
 118
 119/* sysctl hooks */
 120int unaligned_enabled __read_mostly = 1;        /* Enabled by default */
 121int no_unaligned_warning __read_mostly = 1;     /* Only 1 warning by default */
 122
 123static void fixup_load(struct disasm_state *state, struct pt_regs *regs,
 124                        struct callee_regs *cregs)
 125{
 126        int val;
 127
 128        /* register write back */
 129        if ((state->aa == 1) || (state->aa == 2)) {
 130                set_reg(state->wb_reg, state->src1 + state->src2, regs, cregs);
 131
 132                if (state->aa == 2)
 133                        state->src2 = 0;
 134        }
 135
 136        if (state->zz == 0) {
 137                get32_unaligned_check(val, state->src1 + state->src2);
 138        } else {
 139                get16_unaligned_check(val, state->src1 + state->src2);
 140
 141                if (state->x)
 142                        val = (val << 16) >> 16;
 143        }
 144
 145        if (state->pref == 0)
 146                set_reg(state->dest, val, regs, cregs);
 147
 148        return;
 149
 150fault:  state->fault = 1;
 151}
 152
 153static void fixup_store(struct disasm_state *state, struct pt_regs *regs,
 154                        struct callee_regs *cregs)
 155{
 156        /* register write back */
 157        if ((state->aa == 1) || (state->aa == 2)) {
 158                set_reg(state->wb_reg, state->src2 + state->src3, regs, cregs);
 159
 160                if (state->aa == 3)
 161                        state->src3 = 0;
 162        } else if (state->aa == 3) {
 163                if (state->zz == 2) {
 164                        set_reg(state->wb_reg, state->src2 + (state->src3 << 1),
 165                                regs, cregs);
 166                } else if (!state->zz) {
 167                        set_reg(state->wb_reg, state->src2 + (state->src3 << 2),
 168                                regs, cregs);
 169                } else {
 170                        goto fault;
 171                }
 172        }
 173
 174        /* write fix-up */
 175        if (!state->zz)
 176                put32_unaligned_check(state->src1, state->src2 + state->src3);
 177        else
 178                put16_unaligned_check(state->src1, state->src2 + state->src3);
 179
 180        return;
 181
 182fault:  state->fault = 1;
 183}
 184
 185/*
 186 * Handle an unaligned access
 187 * Returns 0 if successfully handled, 1 if some error happened
 188 */
 189int misaligned_fixup(unsigned long address, struct pt_regs *regs,
 190                     unsigned long cause, struct callee_regs *cregs)
 191{
 192        struct disasm_state state;
 193        char buf[TASK_COMM_LEN];
 194
 195        /* handle user mode only and only if enabled by sysadmin */
 196        if (!user_mode(regs) || !unaligned_enabled)
 197                return 1;
 198
 199        if (no_unaligned_warning) {
 200                pr_warn_once("%s(%d) made unaligned access which was emulated"
 201                             " by kernel assist\n. This can degrade application"
 202                             " performance significantly\n. To enable further"
 203                             " logging of such instances, please \n"
 204                             " echo 0 > /proc/sys/kernel/ignore-unaligned-usertrap\n",
 205                             get_task_comm(buf, current), task_pid_nr(current));
 206        } else {
 207                /* Add rate limiting if it gets down to it */
 208                pr_warn("%s(%d): unaligned access to/from 0x%lx by PC: 0x%lx\n",
 209                        get_task_comm(buf, current), task_pid_nr(current),
 210                        address, regs->ret);
 211
 212        }
 213
 214        disasm_instr(regs->ret, &state, 1, regs, cregs);
 215
 216        if (state.fault)
 217                goto fault;
 218
 219        /* ldb/stb should not have unaligned exception */
 220        if ((state.zz == 1) || (state.di))
 221                goto fault;
 222
 223        if (!state.write)
 224                fixup_load(&state, regs, cregs);
 225        else
 226                fixup_store(&state, regs, cregs);
 227
 228        if (state.fault)
 229                goto fault;
 230
 231        if (delay_mode(regs)) {
 232                regs->ret = regs->bta;
 233                regs->status32 &= ~STATUS_DE_MASK;
 234        } else {
 235                regs->ret += state.instr_len;
 236        }
 237
 238        return 0;
 239
 240fault:
 241        pr_err("Alignment trap: fault in fix-up %08lx at [<%08lx>]\n",
 242                state.words[0], address);
 243
 244        return 1;
 245}
 246