Merge remote-tracking branch 'lsk/v3.10/topic/gator' into linux-linaro-lsk
[firefly-linux-kernel-4.4.55.git] / arch / arc / kernel / unaligned.c
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 */
120 int unaligned_enabled __read_mostly = 1;        /* Enabled by default */
121 int no_unaligned_warning __read_mostly = 1;     /* Only 1 warning by default */
122
123 static 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
150 fault:  state->fault = 1;
151 }
152
153 static 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
182 fault:  state->fault = 1;
183 }
184
185 /*
186  * Handle an unaligned access
187  * Returns 0 if successfully handled, 1 if some error happened
188  */
189 int 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                 /* handle zero-overhead-loop */
238                 if ((regs->ret == regs->lp_end) && (regs->lp_count)) {
239                         regs->ret = regs->lp_start;
240                         regs->lp_count--;
241                 }
242         }
243
244         return 0;
245
246 fault:
247         pr_err("Alignment trap: fault in fix-up %08lx at [<%08lx>]\n",
248                 state.words[0], address);
249
250         return 1;
251 }