aboutsummaryrefslogtreecommitdiffstats
path: root/arch/mips/kernel/unaligned.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/mips/kernel/unaligned.c')
-rw-r--r--arch/mips/kernel/unaligned.c135
1 files changed, 110 insertions, 25 deletions
diff --git a/arch/mips/kernel/unaligned.c b/arch/mips/kernel/unaligned.c
index c369a5d35527..2b3517214d6d 100644
--- a/arch/mips/kernel/unaligned.c
+++ b/arch/mips/kernel/unaligned.c
@@ -7,6 +7,7 @@
7 * 7 *
8 * Copyright (C) 1996, 1998, 1999, 2002 by Ralf Baechle 8 * Copyright (C) 1996, 1998, 1999, 2002 by Ralf Baechle
9 * Copyright (C) 1999 Silicon Graphics, Inc. 9 * Copyright (C) 1999 Silicon Graphics, Inc.
10 * Copyright (C) 2014 Imagination Technologies Ltd.
10 * 11 *
11 * This file contains exception handler for address error exception with the 12 * This file contains exception handler for address error exception with the
12 * special capability to execute faulting instructions in software. The 13 * special capability to execute faulting instructions in software. The
@@ -110,8 +111,8 @@ extern void show_registers(struct pt_regs *regs);
110#ifdef __BIG_ENDIAN 111#ifdef __BIG_ENDIAN
111#define LoadHW(addr, value, res) \ 112#define LoadHW(addr, value, res) \
112 __asm__ __volatile__ (".set\tnoat\n" \ 113 __asm__ __volatile__ (".set\tnoat\n" \
113 "1:\tlb\t%0, 0(%2)\n" \ 114 "1:\t"user_lb("%0", "0(%2)")"\n" \
114 "2:\tlbu\t$1, 1(%2)\n\t" \ 115 "2:\t"user_lbu("$1", "1(%2)")"\n\t" \
115 "sll\t%0, 0x8\n\t" \ 116 "sll\t%0, 0x8\n\t" \
116 "or\t%0, $1\n\t" \ 117 "or\t%0, $1\n\t" \
117 "li\t%1, 0\n" \ 118 "li\t%1, 0\n" \
@@ -130,8 +131,8 @@ extern void show_registers(struct pt_regs *regs);
130 131
131#define LoadW(addr, value, res) \ 132#define LoadW(addr, value, res) \
132 __asm__ __volatile__ ( \ 133 __asm__ __volatile__ ( \
133 "1:\tlwl\t%0, (%2)\n" \ 134 "1:\t"user_lwl("%0", "(%2)")"\n" \
134 "2:\tlwr\t%0, 3(%2)\n\t" \ 135 "2:\t"user_lwr("%0", "3(%2)")"\n\t" \
135 "li\t%1, 0\n" \ 136 "li\t%1, 0\n" \
136 "3:\n\t" \ 137 "3:\n\t" \
137 ".insn\n\t" \ 138 ".insn\n\t" \
@@ -149,8 +150,8 @@ extern void show_registers(struct pt_regs *regs);
149#define LoadHWU(addr, value, res) \ 150#define LoadHWU(addr, value, res) \
150 __asm__ __volatile__ ( \ 151 __asm__ __volatile__ ( \
151 ".set\tnoat\n" \ 152 ".set\tnoat\n" \
152 "1:\tlbu\t%0, 0(%2)\n" \ 153 "1:\t"user_lbu("%0", "0(%2)")"\n" \
153 "2:\tlbu\t$1, 1(%2)\n\t" \ 154 "2:\t"user_lbu("$1", "1(%2)")"\n\t" \
154 "sll\t%0, 0x8\n\t" \ 155 "sll\t%0, 0x8\n\t" \
155 "or\t%0, $1\n\t" \ 156 "or\t%0, $1\n\t" \
156 "li\t%1, 0\n" \ 157 "li\t%1, 0\n" \
@@ -170,8 +171,8 @@ extern void show_registers(struct pt_regs *regs);
170 171
171#define LoadWU(addr, value, res) \ 172#define LoadWU(addr, value, res) \
172 __asm__ __volatile__ ( \ 173 __asm__ __volatile__ ( \
173 "1:\tlwl\t%0, (%2)\n" \ 174 "1:\t"user_lwl("%0", "(%2)")"\n" \
174 "2:\tlwr\t%0, 3(%2)\n\t" \ 175 "2:\t"user_lwr("%0", "3(%2)")"\n\t" \
175 "dsll\t%0, %0, 32\n\t" \ 176 "dsll\t%0, %0, 32\n\t" \
176 "dsrl\t%0, %0, 32\n\t" \ 177 "dsrl\t%0, %0, 32\n\t" \
177 "li\t%1, 0\n" \ 178 "li\t%1, 0\n" \
@@ -209,9 +210,9 @@ extern void show_registers(struct pt_regs *regs);
209#define StoreHW(addr, value, res) \ 210#define StoreHW(addr, value, res) \
210 __asm__ __volatile__ ( \ 211 __asm__ __volatile__ ( \
211 ".set\tnoat\n" \ 212 ".set\tnoat\n" \
212 "1:\tsb\t%1, 1(%2)\n\t" \ 213 "1:\t"user_sb("%1", "1(%2)")"\n" \
213 "srl\t$1, %1, 0x8\n" \ 214 "srl\t$1, %1, 0x8\n" \
214 "2:\tsb\t$1, 0(%2)\n\t" \ 215 "2:\t"user_sb("$1", "0(%2)")"\n" \
215 ".set\tat\n\t" \ 216 ".set\tat\n\t" \
216 "li\t%0, 0\n" \ 217 "li\t%0, 0\n" \
217 "3:\n\t" \ 218 "3:\n\t" \
@@ -229,8 +230,8 @@ extern void show_registers(struct pt_regs *regs);
229 230
230#define StoreW(addr, value, res) \ 231#define StoreW(addr, value, res) \
231 __asm__ __volatile__ ( \ 232 __asm__ __volatile__ ( \
232 "1:\tswl\t%1,(%2)\n" \ 233 "1:\t"user_swl("%1", "(%2)")"\n" \
233 "2:\tswr\t%1, 3(%2)\n\t" \ 234 "2:\t"user_swr("%1", "3(%2)")"\n\t" \
234 "li\t%0, 0\n" \ 235 "li\t%0, 0\n" \
235 "3:\n\t" \ 236 "3:\n\t" \
236 ".insn\n\t" \ 237 ".insn\n\t" \
@@ -267,8 +268,8 @@ extern void show_registers(struct pt_regs *regs);
267#ifdef __LITTLE_ENDIAN 268#ifdef __LITTLE_ENDIAN
268#define LoadHW(addr, value, res) \ 269#define LoadHW(addr, value, res) \
269 __asm__ __volatile__ (".set\tnoat\n" \ 270 __asm__ __volatile__ (".set\tnoat\n" \
270 "1:\tlb\t%0, 1(%2)\n" \ 271 "1:\t"user_lb("%0", "1(%2)")"\n" \
271 "2:\tlbu\t$1, 0(%2)\n\t" \ 272 "2:\t"user_lbu("$1", "0(%2)")"\n\t" \
272 "sll\t%0, 0x8\n\t" \ 273 "sll\t%0, 0x8\n\t" \
273 "or\t%0, $1\n\t" \ 274 "or\t%0, $1\n\t" \
274 "li\t%1, 0\n" \ 275 "li\t%1, 0\n" \
@@ -287,8 +288,8 @@ extern void show_registers(struct pt_regs *regs);
287 288
288#define LoadW(addr, value, res) \ 289#define LoadW(addr, value, res) \
289 __asm__ __volatile__ ( \ 290 __asm__ __volatile__ ( \
290 "1:\tlwl\t%0, 3(%2)\n" \ 291 "1:\t"user_lwl("%0", "3(%2)")"\n" \
291 "2:\tlwr\t%0, (%2)\n\t" \ 292 "2:\t"user_lwr("%0", "(%2)")"\n\t" \
292 "li\t%1, 0\n" \ 293 "li\t%1, 0\n" \
293 "3:\n\t" \ 294 "3:\n\t" \
294 ".insn\n\t" \ 295 ".insn\n\t" \
@@ -306,8 +307,8 @@ extern void show_registers(struct pt_regs *regs);
306#define LoadHWU(addr, value, res) \ 307#define LoadHWU(addr, value, res) \
307 __asm__ __volatile__ ( \ 308 __asm__ __volatile__ ( \
308 ".set\tnoat\n" \ 309 ".set\tnoat\n" \
309 "1:\tlbu\t%0, 1(%2)\n" \ 310 "1:\t"user_lbu("%0", "1(%2)")"\n" \
310 "2:\tlbu\t$1, 0(%2)\n\t" \ 311 "2:\t"user_lbu("$1", "0(%2)")"\n\t" \
311 "sll\t%0, 0x8\n\t" \ 312 "sll\t%0, 0x8\n\t" \
312 "or\t%0, $1\n\t" \ 313 "or\t%0, $1\n\t" \
313 "li\t%1, 0\n" \ 314 "li\t%1, 0\n" \
@@ -327,8 +328,8 @@ extern void show_registers(struct pt_regs *regs);
327 328
328#define LoadWU(addr, value, res) \ 329#define LoadWU(addr, value, res) \
329 __asm__ __volatile__ ( \ 330 __asm__ __volatile__ ( \
330 "1:\tlwl\t%0, 3(%2)\n" \ 331 "1:\t"user_lwl("%0", "3(%2)")"\n" \
331 "2:\tlwr\t%0, (%2)\n\t" \ 332 "2:\t"user_lwr("%0", "(%2)")"\n\t" \
332 "dsll\t%0, %0, 32\n\t" \ 333 "dsll\t%0, %0, 32\n\t" \
333 "dsrl\t%0, %0, 32\n\t" \ 334 "dsrl\t%0, %0, 32\n\t" \
334 "li\t%1, 0\n" \ 335 "li\t%1, 0\n" \
@@ -366,9 +367,9 @@ extern void show_registers(struct pt_regs *regs);
366#define StoreHW(addr, value, res) \ 367#define StoreHW(addr, value, res) \
367 __asm__ __volatile__ ( \ 368 __asm__ __volatile__ ( \
368 ".set\tnoat\n" \ 369 ".set\tnoat\n" \
369 "1:\tsb\t%1, 0(%2)\n\t" \ 370 "1:\t"user_sb("%1", "0(%2)")"\n" \
370 "srl\t$1,%1, 0x8\n" \ 371 "srl\t$1,%1, 0x8\n" \
371 "2:\tsb\t$1, 1(%2)\n\t" \ 372 "2:\t"user_sb("$1", "1(%2)")"\n" \
372 ".set\tat\n\t" \ 373 ".set\tat\n\t" \
373 "li\t%0, 0\n" \ 374 "li\t%0, 0\n" \
374 "3:\n\t" \ 375 "3:\n\t" \
@@ -386,8 +387,8 @@ extern void show_registers(struct pt_regs *regs);
386 387
387#define StoreW(addr, value, res) \ 388#define StoreW(addr, value, res) \
388 __asm__ __volatile__ ( \ 389 __asm__ __volatile__ ( \
389 "1:\tswl\t%1, 3(%2)\n" \ 390 "1:\t"user_swl("%1", "3(%2)")"\n" \
390 "2:\tswr\t%1, (%2)\n\t" \ 391 "2:\t"user_swr("%1", "(%2)")"\n\t" \
391 "li\t%0, 0\n" \ 392 "li\t%0, 0\n" \
392 "3:\n\t" \ 393 "3:\n\t" \
393 ".insn\n\t" \ 394 ".insn\n\t" \
@@ -430,7 +431,9 @@ static void emulate_load_store_insn(struct pt_regs *regs,
430 unsigned long origpc; 431 unsigned long origpc;
431 unsigned long orig31; 432 unsigned long orig31;
432 void __user *fault_addr = NULL; 433 void __user *fault_addr = NULL;
433 434#ifdef CONFIG_EVA
435 mm_segment_t seg;
436#endif
434 origpc = (unsigned long)pc; 437 origpc = (unsigned long)pc;
435 orig31 = regs->regs[31]; 438 orig31 = regs->regs[31];
436 439
@@ -475,6 +478,88 @@ static void emulate_load_store_insn(struct pt_regs *regs,
475 * The remaining opcodes are the ones that are really of 478 * The remaining opcodes are the ones that are really of
476 * interest. 479 * interest.
477 */ 480 */
481#ifdef CONFIG_EVA
482 case spec3_op:
483 /*
484 * we can land here only from kernel accessing user memory,
485 * so we need to "switch" the address limit to user space, so
486 * address check can work properly.
487 */
488 seg = get_fs();
489 set_fs(USER_DS);
490 switch (insn.spec3_format.func) {
491 case lhe_op:
492 if (!access_ok(VERIFY_READ, addr, 2)) {
493 set_fs(seg);
494 goto sigbus;
495 }
496 LoadHW(addr, value, res);
497 if (res) {
498 set_fs(seg);
499 goto fault;
500 }
501 compute_return_epc(regs);
502 regs->regs[insn.spec3_format.rt] = value;
503 break;
504 case lwe_op:
505 if (!access_ok(VERIFY_READ, addr, 4)) {
506 set_fs(seg);
507 goto sigbus;
508 }
509 LoadW(addr, value, res);
510 if (res) {
511 set_fs(seg);
512 goto fault;
513 }
514 compute_return_epc(regs);
515 regs->regs[insn.spec3_format.rt] = value;
516 break;
517 case lhue_op:
518 if (!access_ok(VERIFY_READ, addr, 2)) {
519 set_fs(seg);
520 goto sigbus;
521 }
522 LoadHWU(addr, value, res);
523 if (res) {
524 set_fs(seg);
525 goto fault;
526 }
527 compute_return_epc(regs);
528 regs->regs[insn.spec3_format.rt] = value;
529 break;
530 case she_op:
531 if (!access_ok(VERIFY_WRITE, addr, 2)) {
532 set_fs(seg);
533 goto sigbus;
534 }
535 compute_return_epc(regs);
536 value = regs->regs[insn.spec3_format.rt];
537 StoreHW(addr, value, res);
538 if (res) {
539 set_fs(seg);
540 goto fault;
541 }
542 break;
543 case swe_op:
544 if (!access_ok(VERIFY_WRITE, addr, 4)) {
545 set_fs(seg);
546 goto sigbus;
547 }
548 compute_return_epc(regs);
549 value = regs->regs[insn.spec3_format.rt];
550 StoreW(addr, value, res);
551 if (res) {
552 set_fs(seg);
553 goto fault;
554 }
555 break;
556 default:
557 set_fs(seg);
558 goto sigill;
559 }
560 set_fs(seg);
561 break;
562#endif
478 case lh_op: 563 case lh_op:
479 if (!access_ok(VERIFY_READ, addr, 2)) 564 if (!access_ok(VERIFY_READ, addr, 2))
480 goto sigbus; 565 goto sigbus;