1
2
3
4#include <linux/kernel.h>
5#include <linux/uaccess.h>
6#include <linux/ptrace.h>
7
8static int align_enable = 1;
9static int align_count;
10
11static inline uint32_t get_ptreg(struct pt_regs *regs, uint32_t rx)
12{
13 return rx == 15 ? regs->lr : *((uint32_t *)&(regs->a0) - 2 + rx);
14}
15
16static inline void put_ptreg(struct pt_regs *regs, uint32_t rx, uint32_t val)
17{
18 if (rx == 15)
19 regs->lr = val;
20 else
21 *((uint32_t *)&(regs->a0) - 2 + rx) = val;
22}
23
24
25
26
27
28
29
30static int ldb_asm(uint32_t addr, uint32_t *valp)
31{
32 uint32_t val;
33 int err;
34
35 if (!access_ok((void *)addr, 1))
36 return 1;
37
38 asm volatile (
39 "movi %0, 0\n"
40 "1:\n"
41 "ldb %1, (%2)\n"
42 "br 3f\n"
43 "2:\n"
44 "movi %0, 1\n"
45 "br 3f\n"
46 ".section __ex_table,\"a\"\n"
47 ".align 2\n"
48 ".long 1b, 2b\n"
49 ".previous\n"
50 "3:\n"
51 : "=&r"(err), "=r"(val)
52 : "r" (addr)
53 );
54
55 *valp = val;
56
57 return err;
58}
59
60
61
62
63
64
65
66static int stb_asm(uint32_t addr, uint32_t val)
67{
68 int err;
69
70 if (!access_ok((void *)addr, 1))
71 return 1;
72
73 asm volatile (
74 "movi %0, 0\n"
75 "1:\n"
76 "stb %1, (%2)\n"
77 "br 3f\n"
78 "2:\n"
79 "movi %0, 1\n"
80 "br 3f\n"
81 ".section __ex_table,\"a\"\n"
82 ".align 2\n"
83 ".long 1b, 2b\n"
84 ".previous\n"
85 "3:\n"
86 : "=&r"(err)
87 : "r"(val), "r" (addr)
88 );
89
90 return err;
91}
92
93
94
95
96
97
98
99static int ldh_c(struct pt_regs *regs, uint32_t rz, uint32_t addr)
100{
101 uint32_t byte0, byte1;
102
103 if (ldb_asm(addr, &byte0))
104 return 1;
105 addr += 1;
106 if (ldb_asm(addr, &byte1))
107 return 1;
108
109 byte0 |= byte1 << 8;
110 put_ptreg(regs, rz, byte0);
111
112 return 0;
113}
114
115
116
117
118
119
120
121static int sth_c(struct pt_regs *regs, uint32_t rz, uint32_t addr)
122{
123 uint32_t byte0, byte1;
124
125 byte0 = byte1 = get_ptreg(regs, rz);
126
127 byte0 &= 0xff;
128
129 if (stb_asm(addr, byte0))
130 return 1;
131
132 addr += 1;
133 byte1 = (byte1 >> 8) & 0xff;
134 if (stb_asm(addr, byte1))
135 return 1;
136
137 return 0;
138}
139
140
141
142
143
144
145
146static int ldw_c(struct pt_regs *regs, uint32_t rz, uint32_t addr)
147{
148 uint32_t byte0, byte1, byte2, byte3;
149
150 if (ldb_asm(addr, &byte0))
151 return 1;
152
153 addr += 1;
154 if (ldb_asm(addr, &byte1))
155 return 1;
156
157 addr += 1;
158 if (ldb_asm(addr, &byte2))
159 return 1;
160
161 addr += 1;
162 if (ldb_asm(addr, &byte3))
163 return 1;
164
165 byte0 |= byte1 << 8;
166 byte0 |= byte2 << 16;
167 byte0 |= byte3 << 24;
168
169 put_ptreg(regs, rz, byte0);
170
171 return 0;
172}
173
174
175
176
177
178
179
180static int stw_c(struct pt_regs *regs, uint32_t rz, uint32_t addr)
181{
182 uint32_t byte0, byte1, byte2, byte3;
183
184 byte0 = byte1 = byte2 = byte3 = get_ptreg(regs, rz);
185
186 byte0 &= 0xff;
187
188 if (stb_asm(addr, byte0))
189 return 1;
190
191 addr += 1;
192 byte1 = (byte1 >> 8) & 0xff;
193 if (stb_asm(addr, byte1))
194 return 1;
195
196 addr += 1;
197 byte2 = (byte2 >> 16) & 0xff;
198 if (stb_asm(addr, byte2))
199 return 1;
200
201 addr += 1;
202 byte3 = (byte3 >> 24) & 0xff;
203 if (stb_asm(addr, byte3))
204 return 1;
205
206 align_count++;
207
208 return 0;
209}
210
211extern int fixup_exception(struct pt_regs *regs);
212
213#define OP_LDH 0xc000
214#define OP_STH 0xd000
215#define OP_LDW 0x8000
216#define OP_STW 0x9000
217
218void csky_alignment(struct pt_regs *regs)
219{
220 int ret;
221 uint16_t tmp;
222 uint32_t opcode = 0;
223 uint32_t rx = 0;
224 uint32_t rz = 0;
225 uint32_t imm = 0;
226 uint32_t addr = 0;
227
228 if (!user_mode(regs))
229 goto bad_area;
230
231 ret = get_user(tmp, (uint16_t *)instruction_pointer(regs));
232 if (ret) {
233 pr_err("%s get_user failed.\n", __func__);
234 goto bad_area;
235 }
236
237 opcode = (uint32_t)tmp;
238
239 rx = opcode & 0xf;
240 imm = (opcode >> 4) & 0xf;
241 rz = (opcode >> 8) & 0xf;
242 opcode &= 0xf000;
243
244 if (rx == 0 || rx == 1 || rz == 0 || rz == 1)
245 goto bad_area;
246
247 switch (opcode) {
248 case OP_LDH:
249 addr = get_ptreg(regs, rx) + (imm << 1);
250 ret = ldh_c(regs, rz, addr);
251 break;
252 case OP_LDW:
253 addr = get_ptreg(regs, rx) + (imm << 2);
254 ret = ldw_c(regs, rz, addr);
255 break;
256 case OP_STH:
257 addr = get_ptreg(regs, rx) + (imm << 1);
258 ret = sth_c(regs, rz, addr);
259 break;
260 case OP_STW:
261 addr = get_ptreg(regs, rx) + (imm << 2);
262 ret = stw_c(regs, rz, addr);
263 break;
264 }
265
266 if (ret)
267 goto bad_area;
268
269 regs->pc += 2;
270
271 return;
272
273bad_area:
274 if (!user_mode(regs)) {
275 if (fixup_exception(regs))
276 return;
277
278 bust_spinlocks(1);
279 pr_alert("%s opcode: %x, rz: %d, rx: %d, imm: %d, addr: %x.\n",
280 __func__, opcode, rz, rx, imm, addr);
281 show_regs(regs);
282 bust_spinlocks(0);
283 do_exit(SIGKILL);
284 }
285
286 force_sig_fault(SIGBUS, BUS_ADRALN, (void __user *)addr);
287}
288
289static struct ctl_table alignment_tbl[4] = {
290 {
291 .procname = "enable",
292 .data = &align_enable,
293 .maxlen = sizeof(align_enable),
294 .mode = 0666,
295 .proc_handler = &proc_dointvec
296 },
297 {
298 .procname = "count",
299 .data = &align_count,
300 .maxlen = sizeof(align_count),
301 .mode = 0666,
302 .proc_handler = &proc_dointvec
303 },
304 {}
305};
306
307static struct ctl_table sysctl_table[2] = {
308 {
309 .procname = "csky_alignment",
310 .mode = 0555,
311 .child = alignment_tbl},
312 {}
313};
314
315static struct ctl_path sysctl_path[2] = {
316 {.procname = "csky"},
317 {}
318};
319
320static int __init csky_alignment_init(void)
321{
322 register_sysctl_paths(sysctl_path, sysctl_table);
323 return 0;
324}
325
326arch_initcall(csky_alignment_init);
327