diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2014-04-02 16:40:50 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-04-02 16:40:50 -0400 |
commit | bdfc7cbdeef8cadba0e5793079ac0130b8e2220c (patch) | |
tree | 82af0cae4898e259edcc6cbdad639087dc1189a8 /arch/mips/kernel/unaligned.c | |
parent | 62d1a3ba5adc5653d43f6cd3a90758bb6ad5d5bd (diff) | |
parent | ade63aada79c61bcd5f51cbd310f237399892268 (diff) |
Merge branch 'mips-for-linux-next' of git://git.linux-mips.org/pub/scm/ralf/upstream-sfr
Pull MIPS updates from Ralf Baechle:
- Support for Imgtec's Aptiv family of MIPS cores.
- Improved detection of BCM47xx configurations.
- Fix hiberation for certain configurations.
- Add support for the Chinese Loongson 3 CPU, a MIPS64 R2 core and
systems.
- Detection and support for the MIPS P5600 core.
- A few more random fixes that didn't make 3.14.
- Support for the EVA Extended Virtual Addressing
- Switch Alchemy to the platform PATA driver
- Complete unification of Alchemy support
- Allow availability of I/O cache coherency to be runtime detected
- Improvments to multiprocessing support for Imgtec platforms
- A few microoptimizations
- Cleanups of FPU support
- Paul Gortmaker's fixes for the init stuff
- Support for seccomp
* 'mips-for-linux-next' of git://git.linux-mips.org/pub/scm/ralf/upstream-sfr: (165 commits)
MIPS: CPC: Use __raw_ memory access functions
MIPS: CM: use __raw_ memory access functions
MIPS: Fix warning when including smp-ops.h with CONFIG_SMP=n
MIPS: Malta: GIC IPIs may be used without MT
MIPS: smp-mt: Use common GIC IPI implementation
MIPS: smp-cmp: Remove incorrect core number probe
MIPS: Fix gigaton of warning building with microMIPS.
MIPS: Fix core number detection for MT cores
MIPS: MT: core_nvpes function to retrieve VPE count
MIPS: Provide empty mips_mt_set_cpuoptions when CONFIG_MIPS_MT=n
MIPS: Lasat: Replace del_timer by del_timer_sync
MIPS: Malta: Setup PM I/O region on boot
MIPS: Loongson: Add a Loongson-3 default config file
MIPS: Loongson 3: Add CPU hotplug support
MIPS: Loongson 3: Add Loongson-3 SMP support
MIPS: Loongson: Add Loongson-3 Kconfig options
MIPS: Loongson: Add swiotlb to support All-Memory DMA
MIPS: Loongson 3: Add serial port support
MIPS: Loongson 3: Add IRQ init and dispatch support
MIPS: Loongson 3: Add HT-linked PCI support
...
Diffstat (limited to 'arch/mips/kernel/unaligned.c')
-rw-r--r-- | arch/mips/kernel/unaligned.c | 135 |
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; |