11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * Handle unaligned accesses by emulation. 31da177e4SLinus Torvalds * 41da177e4SLinus Torvalds * This file is subject to the terms and conditions of the GNU General Public 51da177e4SLinus Torvalds * License. See the file "COPYING" in the main directory of this archive 61da177e4SLinus Torvalds * for more details. 71da177e4SLinus Torvalds * 81da177e4SLinus Torvalds * Copyright (C) 1996, 1998, 1999, 2002 by Ralf Baechle 91da177e4SLinus Torvalds * Copyright (C) 1999 Silicon Graphics, Inc. 101da177e4SLinus Torvalds * 111da177e4SLinus Torvalds * This file contains exception handler for address error exception with the 121da177e4SLinus Torvalds * special capability to execute faulting instructions in software. The 131da177e4SLinus Torvalds * handler does not try to handle the case when the program counter points 141da177e4SLinus Torvalds * to an address not aligned to a word boundary. 151da177e4SLinus Torvalds * 161da177e4SLinus Torvalds * Putting data to unaligned addresses is a bad practice even on Intel where 171da177e4SLinus Torvalds * only the performance is affected. Much worse is that such code is non- 181da177e4SLinus Torvalds * portable. Due to several programs that die on MIPS due to alignment 191da177e4SLinus Torvalds * problems I decided to implement this handler anyway though I originally 201da177e4SLinus Torvalds * didn't intend to do this at all for user code. 211da177e4SLinus Torvalds * 221da177e4SLinus Torvalds * For now I enable fixing of address errors by default to make life easier. 231da177e4SLinus Torvalds * I however intend to disable this somewhen in the future when the alignment 241da177e4SLinus Torvalds * problems with user programs have been fixed. For programmers this is the 251da177e4SLinus Torvalds * right way to go. 261da177e4SLinus Torvalds * 271da177e4SLinus Torvalds * Fixing address errors is a per process option. The option is inherited 281da177e4SLinus Torvalds * across fork(2) and execve(2) calls. If you really want to use the 291da177e4SLinus Torvalds * option in your user programs - I discourage the use of the software 301da177e4SLinus Torvalds * emulation strongly - use the following code in your userland stuff: 311da177e4SLinus Torvalds * 321da177e4SLinus Torvalds * #include <sys/sysmips.h> 331da177e4SLinus Torvalds * 341da177e4SLinus Torvalds * ... 351da177e4SLinus Torvalds * sysmips(MIPS_FIXADE, x); 361da177e4SLinus Torvalds * ... 371da177e4SLinus Torvalds * 381da177e4SLinus Torvalds * The argument x is 0 for disabling software emulation, enabled otherwise. 391da177e4SLinus Torvalds * 401da177e4SLinus Torvalds * Below a little program to play around with this feature. 411da177e4SLinus Torvalds * 421da177e4SLinus Torvalds * #include <stdio.h> 431da177e4SLinus Torvalds * #include <sys/sysmips.h> 441da177e4SLinus Torvalds * 451da177e4SLinus Torvalds * struct foo { 461da177e4SLinus Torvalds * unsigned char bar[8]; 471da177e4SLinus Torvalds * }; 481da177e4SLinus Torvalds * 491da177e4SLinus Torvalds * main(int argc, char *argv[]) 501da177e4SLinus Torvalds * { 511da177e4SLinus Torvalds * struct foo x = {0, 1, 2, 3, 4, 5, 6, 7}; 521da177e4SLinus Torvalds * unsigned int *p = (unsigned int *) (x.bar + 3); 531da177e4SLinus Torvalds * int i; 541da177e4SLinus Torvalds * 551da177e4SLinus Torvalds * if (argc > 1) 561da177e4SLinus Torvalds * sysmips(MIPS_FIXADE, atoi(argv[1])); 571da177e4SLinus Torvalds * 581da177e4SLinus Torvalds * printf("*p = %08lx\n", *p); 591da177e4SLinus Torvalds * 601da177e4SLinus Torvalds * *p = 0xdeadface; 611da177e4SLinus Torvalds * 621da177e4SLinus Torvalds * for(i = 0; i <= 7; i++) 631da177e4SLinus Torvalds * printf("%02x ", x.bar[i]); 641da177e4SLinus Torvalds * printf("\n"); 651da177e4SLinus Torvalds * } 661da177e4SLinus Torvalds * 671da177e4SLinus Torvalds * Coprocessor loads are not supported; I think this case is unimportant 681da177e4SLinus Torvalds * in the practice. 691da177e4SLinus Torvalds * 701da177e4SLinus Torvalds * TODO: Handle ndc (attempted store to doubleword in uncached memory) 711da177e4SLinus Torvalds * exception for the R6000. 721da177e4SLinus Torvalds * A store crossing a page boundary might be executed only partially. 731da177e4SLinus Torvalds * Undo the partial store in this case. 741da177e4SLinus Torvalds */ 75c3fc5cd5SRalf Baechle #include <linux/context_tracking.h> 761da177e4SLinus Torvalds #include <linux/mm.h> 771da177e4SLinus Torvalds #include <linux/signal.h> 781da177e4SLinus Torvalds #include <linux/smp.h> 79e8edc6e0SAlexey Dobriyan #include <linux/sched.h> 806312e0eeSAtsushi Nemoto #include <linux/debugfs.h> 817f788d2dSDeng-Cheng Zhu #include <linux/perf_event.h> 827f788d2dSDeng-Cheng Zhu 831da177e4SLinus Torvalds #include <asm/asm.h> 841da177e4SLinus Torvalds #include <asm/branch.h> 851da177e4SLinus Torvalds #include <asm/byteorder.h> 8669f3a7deSRalf Baechle #include <asm/cop2.h> 87102cedc3SLeonid Yegoshin #include <asm/fpu.h> 88102cedc3SLeonid Yegoshin #include <asm/fpu_emulator.h> 891da177e4SLinus Torvalds #include <asm/inst.h> 901da177e4SLinus Torvalds #include <asm/uaccess.h> 9134c2f668SLeonid Yegoshin #include <asm/fpu.h> 9234c2f668SLeonid Yegoshin #include <asm/fpu_emulator.h> 931da177e4SLinus Torvalds 941da177e4SLinus Torvalds #define STR(x) __STR(x) 951da177e4SLinus Torvalds #define __STR(x) #x 961da177e4SLinus Torvalds 976312e0eeSAtsushi Nemoto enum { 986312e0eeSAtsushi Nemoto UNALIGNED_ACTION_QUIET, 996312e0eeSAtsushi Nemoto UNALIGNED_ACTION_SIGNAL, 1006312e0eeSAtsushi Nemoto UNALIGNED_ACTION_SHOW, 1016312e0eeSAtsushi Nemoto }; 1026312e0eeSAtsushi Nemoto #ifdef CONFIG_DEBUG_FS 1036312e0eeSAtsushi Nemoto static u32 unaligned_instructions; 1046312e0eeSAtsushi Nemoto static u32 unaligned_action; 1056312e0eeSAtsushi Nemoto #else 1066312e0eeSAtsushi Nemoto #define unaligned_action UNALIGNED_ACTION_QUIET 1071da177e4SLinus Torvalds #endif 1086312e0eeSAtsushi Nemoto extern void show_registers(struct pt_regs *regs); 1091da177e4SLinus Torvalds 11034c2f668SLeonid Yegoshin #ifdef __BIG_ENDIAN 11134c2f668SLeonid Yegoshin #define LoadHW(addr, value, res) \ 11234c2f668SLeonid Yegoshin __asm__ __volatile__ (".set\tnoat\n" \ 11334c2f668SLeonid Yegoshin "1:\tlb\t%0, 0(%2)\n" \ 11434c2f668SLeonid Yegoshin "2:\tlbu\t$1, 1(%2)\n\t" \ 11534c2f668SLeonid Yegoshin "sll\t%0, 0x8\n\t" \ 11634c2f668SLeonid Yegoshin "or\t%0, $1\n\t" \ 11734c2f668SLeonid Yegoshin "li\t%1, 0\n" \ 11834c2f668SLeonid Yegoshin "3:\t.set\tat\n\t" \ 11934c2f668SLeonid Yegoshin ".insn\n\t" \ 12034c2f668SLeonid Yegoshin ".section\t.fixup,\"ax\"\n\t" \ 12134c2f668SLeonid Yegoshin "4:\tli\t%1, %3\n\t" \ 12234c2f668SLeonid Yegoshin "j\t3b\n\t" \ 12334c2f668SLeonid Yegoshin ".previous\n\t" \ 12434c2f668SLeonid Yegoshin ".section\t__ex_table,\"a\"\n\t" \ 12534c2f668SLeonid Yegoshin STR(PTR)"\t1b, 4b\n\t" \ 12634c2f668SLeonid Yegoshin STR(PTR)"\t2b, 4b\n\t" \ 12734c2f668SLeonid Yegoshin ".previous" \ 12834c2f668SLeonid Yegoshin : "=&r" (value), "=r" (res) \ 12934c2f668SLeonid Yegoshin : "r" (addr), "i" (-EFAULT)); 13034c2f668SLeonid Yegoshin 13134c2f668SLeonid Yegoshin #define LoadW(addr, value, res) \ 13234c2f668SLeonid Yegoshin __asm__ __volatile__ ( \ 13334c2f668SLeonid Yegoshin "1:\tlwl\t%0, (%2)\n" \ 13434c2f668SLeonid Yegoshin "2:\tlwr\t%0, 3(%2)\n\t" \ 13534c2f668SLeonid Yegoshin "li\t%1, 0\n" \ 13634c2f668SLeonid Yegoshin "3:\n\t" \ 13734c2f668SLeonid Yegoshin ".insn\n\t" \ 13834c2f668SLeonid Yegoshin ".section\t.fixup,\"ax\"\n\t" \ 13934c2f668SLeonid Yegoshin "4:\tli\t%1, %3\n\t" \ 14034c2f668SLeonid Yegoshin "j\t3b\n\t" \ 14134c2f668SLeonid Yegoshin ".previous\n\t" \ 14234c2f668SLeonid Yegoshin ".section\t__ex_table,\"a\"\n\t" \ 14334c2f668SLeonid Yegoshin STR(PTR)"\t1b, 4b\n\t" \ 14434c2f668SLeonid Yegoshin STR(PTR)"\t2b, 4b\n\t" \ 14534c2f668SLeonid Yegoshin ".previous" \ 14634c2f668SLeonid Yegoshin : "=&r" (value), "=r" (res) \ 14734c2f668SLeonid Yegoshin : "r" (addr), "i" (-EFAULT)); 14834c2f668SLeonid Yegoshin 14934c2f668SLeonid Yegoshin #define LoadHWU(addr, value, res) \ 15034c2f668SLeonid Yegoshin __asm__ __volatile__ ( \ 15134c2f668SLeonid Yegoshin ".set\tnoat\n" \ 15234c2f668SLeonid Yegoshin "1:\tlbu\t%0, 0(%2)\n" \ 15334c2f668SLeonid Yegoshin "2:\tlbu\t$1, 1(%2)\n\t" \ 15434c2f668SLeonid Yegoshin "sll\t%0, 0x8\n\t" \ 15534c2f668SLeonid Yegoshin "or\t%0, $1\n\t" \ 15634c2f668SLeonid Yegoshin "li\t%1, 0\n" \ 15734c2f668SLeonid Yegoshin "3:\n\t" \ 15834c2f668SLeonid Yegoshin ".insn\n\t" \ 15934c2f668SLeonid Yegoshin ".set\tat\n\t" \ 16034c2f668SLeonid Yegoshin ".section\t.fixup,\"ax\"\n\t" \ 16134c2f668SLeonid Yegoshin "4:\tli\t%1, %3\n\t" \ 16234c2f668SLeonid Yegoshin "j\t3b\n\t" \ 16334c2f668SLeonid Yegoshin ".previous\n\t" \ 16434c2f668SLeonid Yegoshin ".section\t__ex_table,\"a\"\n\t" \ 16534c2f668SLeonid Yegoshin STR(PTR)"\t1b, 4b\n\t" \ 16634c2f668SLeonid Yegoshin STR(PTR)"\t2b, 4b\n\t" \ 16734c2f668SLeonid Yegoshin ".previous" \ 16834c2f668SLeonid Yegoshin : "=&r" (value), "=r" (res) \ 16934c2f668SLeonid Yegoshin : "r" (addr), "i" (-EFAULT)); 17034c2f668SLeonid Yegoshin 17134c2f668SLeonid Yegoshin #define LoadWU(addr, value, res) \ 17234c2f668SLeonid Yegoshin __asm__ __volatile__ ( \ 17334c2f668SLeonid Yegoshin "1:\tlwl\t%0, (%2)\n" \ 17434c2f668SLeonid Yegoshin "2:\tlwr\t%0, 3(%2)\n\t" \ 17534c2f668SLeonid Yegoshin "dsll\t%0, %0, 32\n\t" \ 17634c2f668SLeonid Yegoshin "dsrl\t%0, %0, 32\n\t" \ 17734c2f668SLeonid Yegoshin "li\t%1, 0\n" \ 17834c2f668SLeonid Yegoshin "3:\n\t" \ 17934c2f668SLeonid Yegoshin ".insn\n\t" \ 18034c2f668SLeonid Yegoshin "\t.section\t.fixup,\"ax\"\n\t" \ 18134c2f668SLeonid Yegoshin "4:\tli\t%1, %3\n\t" \ 18234c2f668SLeonid Yegoshin "j\t3b\n\t" \ 18334c2f668SLeonid Yegoshin ".previous\n\t" \ 18434c2f668SLeonid Yegoshin ".section\t__ex_table,\"a\"\n\t" \ 18534c2f668SLeonid Yegoshin STR(PTR)"\t1b, 4b\n\t" \ 18634c2f668SLeonid Yegoshin STR(PTR)"\t2b, 4b\n\t" \ 18734c2f668SLeonid Yegoshin ".previous" \ 18834c2f668SLeonid Yegoshin : "=&r" (value), "=r" (res) \ 18934c2f668SLeonid Yegoshin : "r" (addr), "i" (-EFAULT)); 19034c2f668SLeonid Yegoshin 19134c2f668SLeonid Yegoshin #define LoadDW(addr, value, res) \ 19234c2f668SLeonid Yegoshin __asm__ __volatile__ ( \ 19334c2f668SLeonid Yegoshin "1:\tldl\t%0, (%2)\n" \ 19434c2f668SLeonid Yegoshin "2:\tldr\t%0, 7(%2)\n\t" \ 19534c2f668SLeonid Yegoshin "li\t%1, 0\n" \ 19634c2f668SLeonid Yegoshin "3:\n\t" \ 19734c2f668SLeonid Yegoshin ".insn\n\t" \ 19834c2f668SLeonid Yegoshin "\t.section\t.fixup,\"ax\"\n\t" \ 19934c2f668SLeonid Yegoshin "4:\tli\t%1, %3\n\t" \ 20034c2f668SLeonid Yegoshin "j\t3b\n\t" \ 20134c2f668SLeonid Yegoshin ".previous\n\t" \ 20234c2f668SLeonid Yegoshin ".section\t__ex_table,\"a\"\n\t" \ 20334c2f668SLeonid Yegoshin STR(PTR)"\t1b, 4b\n\t" \ 20434c2f668SLeonid Yegoshin STR(PTR)"\t2b, 4b\n\t" \ 20534c2f668SLeonid Yegoshin ".previous" \ 20634c2f668SLeonid Yegoshin : "=&r" (value), "=r" (res) \ 20734c2f668SLeonid Yegoshin : "r" (addr), "i" (-EFAULT)); 20834c2f668SLeonid Yegoshin 20934c2f668SLeonid Yegoshin #define StoreHW(addr, value, res) \ 21034c2f668SLeonid Yegoshin __asm__ __volatile__ ( \ 21134c2f668SLeonid Yegoshin ".set\tnoat\n" \ 21234c2f668SLeonid Yegoshin "1:\tsb\t%1, 1(%2)\n\t" \ 21334c2f668SLeonid Yegoshin "srl\t$1, %1, 0x8\n" \ 21434c2f668SLeonid Yegoshin "2:\tsb\t$1, 0(%2)\n\t" \ 21534c2f668SLeonid Yegoshin ".set\tat\n\t" \ 21634c2f668SLeonid Yegoshin "li\t%0, 0\n" \ 21734c2f668SLeonid Yegoshin "3:\n\t" \ 21834c2f668SLeonid Yegoshin ".insn\n\t" \ 21934c2f668SLeonid Yegoshin ".section\t.fixup,\"ax\"\n\t" \ 22034c2f668SLeonid Yegoshin "4:\tli\t%0, %3\n\t" \ 22134c2f668SLeonid Yegoshin "j\t3b\n\t" \ 22234c2f668SLeonid Yegoshin ".previous\n\t" \ 22334c2f668SLeonid Yegoshin ".section\t__ex_table,\"a\"\n\t" \ 22434c2f668SLeonid Yegoshin STR(PTR)"\t1b, 4b\n\t" \ 22534c2f668SLeonid Yegoshin STR(PTR)"\t2b, 4b\n\t" \ 22634c2f668SLeonid Yegoshin ".previous" \ 22734c2f668SLeonid Yegoshin : "=r" (res) \ 22834c2f668SLeonid Yegoshin : "r" (value), "r" (addr), "i" (-EFAULT)); 22934c2f668SLeonid Yegoshin 23034c2f668SLeonid Yegoshin #define StoreW(addr, value, res) \ 23134c2f668SLeonid Yegoshin __asm__ __volatile__ ( \ 23234c2f668SLeonid Yegoshin "1:\tswl\t%1,(%2)\n" \ 23334c2f668SLeonid Yegoshin "2:\tswr\t%1, 3(%2)\n\t" \ 23434c2f668SLeonid Yegoshin "li\t%0, 0\n" \ 23534c2f668SLeonid Yegoshin "3:\n\t" \ 23634c2f668SLeonid Yegoshin ".insn\n\t" \ 23734c2f668SLeonid Yegoshin ".section\t.fixup,\"ax\"\n\t" \ 23834c2f668SLeonid Yegoshin "4:\tli\t%0, %3\n\t" \ 23934c2f668SLeonid Yegoshin "j\t3b\n\t" \ 24034c2f668SLeonid Yegoshin ".previous\n\t" \ 24134c2f668SLeonid Yegoshin ".section\t__ex_table,\"a\"\n\t" \ 24234c2f668SLeonid Yegoshin STR(PTR)"\t1b, 4b\n\t" \ 24334c2f668SLeonid Yegoshin STR(PTR)"\t2b, 4b\n\t" \ 24434c2f668SLeonid Yegoshin ".previous" \ 24534c2f668SLeonid Yegoshin : "=r" (res) \ 24634c2f668SLeonid Yegoshin : "r" (value), "r" (addr), "i" (-EFAULT)); 24734c2f668SLeonid Yegoshin 24834c2f668SLeonid Yegoshin #define StoreDW(addr, value, res) \ 24934c2f668SLeonid Yegoshin __asm__ __volatile__ ( \ 25034c2f668SLeonid Yegoshin "1:\tsdl\t%1,(%2)\n" \ 25134c2f668SLeonid Yegoshin "2:\tsdr\t%1, 7(%2)\n\t" \ 25234c2f668SLeonid Yegoshin "li\t%0, 0\n" \ 25334c2f668SLeonid Yegoshin "3:\n\t" \ 25434c2f668SLeonid Yegoshin ".insn\n\t" \ 25534c2f668SLeonid Yegoshin ".section\t.fixup,\"ax\"\n\t" \ 25634c2f668SLeonid Yegoshin "4:\tli\t%0, %3\n\t" \ 25734c2f668SLeonid Yegoshin "j\t3b\n\t" \ 25834c2f668SLeonid Yegoshin ".previous\n\t" \ 25934c2f668SLeonid Yegoshin ".section\t__ex_table,\"a\"\n\t" \ 26034c2f668SLeonid Yegoshin STR(PTR)"\t1b, 4b\n\t" \ 26134c2f668SLeonid Yegoshin STR(PTR)"\t2b, 4b\n\t" \ 26234c2f668SLeonid Yegoshin ".previous" \ 26334c2f668SLeonid Yegoshin : "=r" (res) \ 26434c2f668SLeonid Yegoshin : "r" (value), "r" (addr), "i" (-EFAULT)); 26534c2f668SLeonid Yegoshin #endif 26634c2f668SLeonid Yegoshin 26734c2f668SLeonid Yegoshin #ifdef __LITTLE_ENDIAN 26834c2f668SLeonid Yegoshin #define LoadHW(addr, value, res) \ 26934c2f668SLeonid Yegoshin __asm__ __volatile__ (".set\tnoat\n" \ 27034c2f668SLeonid Yegoshin "1:\tlb\t%0, 1(%2)\n" \ 27134c2f668SLeonid Yegoshin "2:\tlbu\t$1, 0(%2)\n\t" \ 27234c2f668SLeonid Yegoshin "sll\t%0, 0x8\n\t" \ 27334c2f668SLeonid Yegoshin "or\t%0, $1\n\t" \ 27434c2f668SLeonid Yegoshin "li\t%1, 0\n" \ 27534c2f668SLeonid Yegoshin "3:\t.set\tat\n\t" \ 27634c2f668SLeonid Yegoshin ".insn\n\t" \ 27734c2f668SLeonid Yegoshin ".section\t.fixup,\"ax\"\n\t" \ 27834c2f668SLeonid Yegoshin "4:\tli\t%1, %3\n\t" \ 27934c2f668SLeonid Yegoshin "j\t3b\n\t" \ 28034c2f668SLeonid Yegoshin ".previous\n\t" \ 28134c2f668SLeonid Yegoshin ".section\t__ex_table,\"a\"\n\t" \ 28234c2f668SLeonid Yegoshin STR(PTR)"\t1b, 4b\n\t" \ 28334c2f668SLeonid Yegoshin STR(PTR)"\t2b, 4b\n\t" \ 28434c2f668SLeonid Yegoshin ".previous" \ 28534c2f668SLeonid Yegoshin : "=&r" (value), "=r" (res) \ 28634c2f668SLeonid Yegoshin : "r" (addr), "i" (-EFAULT)); 28734c2f668SLeonid Yegoshin 28834c2f668SLeonid Yegoshin #define LoadW(addr, value, res) \ 28934c2f668SLeonid Yegoshin __asm__ __volatile__ ( \ 29034c2f668SLeonid Yegoshin "1:\tlwl\t%0, 3(%2)\n" \ 29134c2f668SLeonid Yegoshin "2:\tlwr\t%0, (%2)\n\t" \ 29234c2f668SLeonid Yegoshin "li\t%1, 0\n" \ 29334c2f668SLeonid Yegoshin "3:\n\t" \ 29434c2f668SLeonid Yegoshin ".insn\n\t" \ 29534c2f668SLeonid Yegoshin ".section\t.fixup,\"ax\"\n\t" \ 29634c2f668SLeonid Yegoshin "4:\tli\t%1, %3\n\t" \ 29734c2f668SLeonid Yegoshin "j\t3b\n\t" \ 29834c2f668SLeonid Yegoshin ".previous\n\t" \ 29934c2f668SLeonid Yegoshin ".section\t__ex_table,\"a\"\n\t" \ 30034c2f668SLeonid Yegoshin STR(PTR)"\t1b, 4b\n\t" \ 30134c2f668SLeonid Yegoshin STR(PTR)"\t2b, 4b\n\t" \ 30234c2f668SLeonid Yegoshin ".previous" \ 30334c2f668SLeonid Yegoshin : "=&r" (value), "=r" (res) \ 30434c2f668SLeonid Yegoshin : "r" (addr), "i" (-EFAULT)); 30534c2f668SLeonid Yegoshin 30634c2f668SLeonid Yegoshin #define LoadHWU(addr, value, res) \ 30734c2f668SLeonid Yegoshin __asm__ __volatile__ ( \ 30834c2f668SLeonid Yegoshin ".set\tnoat\n" \ 30934c2f668SLeonid Yegoshin "1:\tlbu\t%0, 1(%2)\n" \ 31034c2f668SLeonid Yegoshin "2:\tlbu\t$1, 0(%2)\n\t" \ 31134c2f668SLeonid Yegoshin "sll\t%0, 0x8\n\t" \ 31234c2f668SLeonid Yegoshin "or\t%0, $1\n\t" \ 31334c2f668SLeonid Yegoshin "li\t%1, 0\n" \ 31434c2f668SLeonid Yegoshin "3:\n\t" \ 31534c2f668SLeonid Yegoshin ".insn\n\t" \ 31634c2f668SLeonid Yegoshin ".set\tat\n\t" \ 31734c2f668SLeonid Yegoshin ".section\t.fixup,\"ax\"\n\t" \ 31834c2f668SLeonid Yegoshin "4:\tli\t%1, %3\n\t" \ 31934c2f668SLeonid Yegoshin "j\t3b\n\t" \ 32034c2f668SLeonid Yegoshin ".previous\n\t" \ 32134c2f668SLeonid Yegoshin ".section\t__ex_table,\"a\"\n\t" \ 32234c2f668SLeonid Yegoshin STR(PTR)"\t1b, 4b\n\t" \ 32334c2f668SLeonid Yegoshin STR(PTR)"\t2b, 4b\n\t" \ 32434c2f668SLeonid Yegoshin ".previous" \ 32534c2f668SLeonid Yegoshin : "=&r" (value), "=r" (res) \ 32634c2f668SLeonid Yegoshin : "r" (addr), "i" (-EFAULT)); 32734c2f668SLeonid Yegoshin 32834c2f668SLeonid Yegoshin #define LoadWU(addr, value, res) \ 32934c2f668SLeonid Yegoshin __asm__ __volatile__ ( \ 33034c2f668SLeonid Yegoshin "1:\tlwl\t%0, 3(%2)\n" \ 33134c2f668SLeonid Yegoshin "2:\tlwr\t%0, (%2)\n\t" \ 33234c2f668SLeonid Yegoshin "dsll\t%0, %0, 32\n\t" \ 33334c2f668SLeonid Yegoshin "dsrl\t%0, %0, 32\n\t" \ 33434c2f668SLeonid Yegoshin "li\t%1, 0\n" \ 33534c2f668SLeonid Yegoshin "3:\n\t" \ 33634c2f668SLeonid Yegoshin ".insn\n\t" \ 33734c2f668SLeonid Yegoshin "\t.section\t.fixup,\"ax\"\n\t" \ 33834c2f668SLeonid Yegoshin "4:\tli\t%1, %3\n\t" \ 33934c2f668SLeonid Yegoshin "j\t3b\n\t" \ 34034c2f668SLeonid Yegoshin ".previous\n\t" \ 34134c2f668SLeonid Yegoshin ".section\t__ex_table,\"a\"\n\t" \ 34234c2f668SLeonid Yegoshin STR(PTR)"\t1b, 4b\n\t" \ 34334c2f668SLeonid Yegoshin STR(PTR)"\t2b, 4b\n\t" \ 34434c2f668SLeonid Yegoshin ".previous" \ 34534c2f668SLeonid Yegoshin : "=&r" (value), "=r" (res) \ 34634c2f668SLeonid Yegoshin : "r" (addr), "i" (-EFAULT)); 34734c2f668SLeonid Yegoshin 34834c2f668SLeonid Yegoshin #define LoadDW(addr, value, res) \ 34934c2f668SLeonid Yegoshin __asm__ __volatile__ ( \ 35034c2f668SLeonid Yegoshin "1:\tldl\t%0, 7(%2)\n" \ 35134c2f668SLeonid Yegoshin "2:\tldr\t%0, (%2)\n\t" \ 35234c2f668SLeonid Yegoshin "li\t%1, 0\n" \ 35334c2f668SLeonid Yegoshin "3:\n\t" \ 35434c2f668SLeonid Yegoshin ".insn\n\t" \ 35534c2f668SLeonid Yegoshin "\t.section\t.fixup,\"ax\"\n\t" \ 35634c2f668SLeonid Yegoshin "4:\tli\t%1, %3\n\t" \ 35734c2f668SLeonid Yegoshin "j\t3b\n\t" \ 35834c2f668SLeonid Yegoshin ".previous\n\t" \ 35934c2f668SLeonid Yegoshin ".section\t__ex_table,\"a\"\n\t" \ 36034c2f668SLeonid Yegoshin STR(PTR)"\t1b, 4b\n\t" \ 36134c2f668SLeonid Yegoshin STR(PTR)"\t2b, 4b\n\t" \ 36234c2f668SLeonid Yegoshin ".previous" \ 36334c2f668SLeonid Yegoshin : "=&r" (value), "=r" (res) \ 36434c2f668SLeonid Yegoshin : "r" (addr), "i" (-EFAULT)); 36534c2f668SLeonid Yegoshin 36634c2f668SLeonid Yegoshin #define StoreHW(addr, value, res) \ 36734c2f668SLeonid Yegoshin __asm__ __volatile__ ( \ 36834c2f668SLeonid Yegoshin ".set\tnoat\n" \ 36934c2f668SLeonid Yegoshin "1:\tsb\t%1, 0(%2)\n\t" \ 37034c2f668SLeonid Yegoshin "srl\t$1,%1, 0x8\n" \ 37134c2f668SLeonid Yegoshin "2:\tsb\t$1, 1(%2)\n\t" \ 37234c2f668SLeonid Yegoshin ".set\tat\n\t" \ 37334c2f668SLeonid Yegoshin "li\t%0, 0\n" \ 37434c2f668SLeonid Yegoshin "3:\n\t" \ 37534c2f668SLeonid Yegoshin ".insn\n\t" \ 37634c2f668SLeonid Yegoshin ".section\t.fixup,\"ax\"\n\t" \ 37734c2f668SLeonid Yegoshin "4:\tli\t%0, %3\n\t" \ 37834c2f668SLeonid Yegoshin "j\t3b\n\t" \ 37934c2f668SLeonid Yegoshin ".previous\n\t" \ 38034c2f668SLeonid Yegoshin ".section\t__ex_table,\"a\"\n\t" \ 38134c2f668SLeonid Yegoshin STR(PTR)"\t1b, 4b\n\t" \ 38234c2f668SLeonid Yegoshin STR(PTR)"\t2b, 4b\n\t" \ 38334c2f668SLeonid Yegoshin ".previous" \ 38434c2f668SLeonid Yegoshin : "=r" (res) \ 38534c2f668SLeonid Yegoshin : "r" (value), "r" (addr), "i" (-EFAULT)); 38634c2f668SLeonid Yegoshin 38734c2f668SLeonid Yegoshin #define StoreW(addr, value, res) \ 38834c2f668SLeonid Yegoshin __asm__ __volatile__ ( \ 38934c2f668SLeonid Yegoshin "1:\tswl\t%1, 3(%2)\n" \ 39034c2f668SLeonid Yegoshin "2:\tswr\t%1, (%2)\n\t" \ 39134c2f668SLeonid Yegoshin "li\t%0, 0\n" \ 39234c2f668SLeonid Yegoshin "3:\n\t" \ 39334c2f668SLeonid Yegoshin ".insn\n\t" \ 39434c2f668SLeonid Yegoshin ".section\t.fixup,\"ax\"\n\t" \ 39534c2f668SLeonid Yegoshin "4:\tli\t%0, %3\n\t" \ 39634c2f668SLeonid Yegoshin "j\t3b\n\t" \ 39734c2f668SLeonid Yegoshin ".previous\n\t" \ 39834c2f668SLeonid Yegoshin ".section\t__ex_table,\"a\"\n\t" \ 39934c2f668SLeonid Yegoshin STR(PTR)"\t1b, 4b\n\t" \ 40034c2f668SLeonid Yegoshin STR(PTR)"\t2b, 4b\n\t" \ 40134c2f668SLeonid Yegoshin ".previous" \ 40234c2f668SLeonid Yegoshin : "=r" (res) \ 40334c2f668SLeonid Yegoshin : "r" (value), "r" (addr), "i" (-EFAULT)); 40434c2f668SLeonid Yegoshin 40534c2f668SLeonid Yegoshin #define StoreDW(addr, value, res) \ 40634c2f668SLeonid Yegoshin __asm__ __volatile__ ( \ 40734c2f668SLeonid Yegoshin "1:\tsdl\t%1, 7(%2)\n" \ 40834c2f668SLeonid Yegoshin "2:\tsdr\t%1, (%2)\n\t" \ 40934c2f668SLeonid Yegoshin "li\t%0, 0\n" \ 41034c2f668SLeonid Yegoshin "3:\n\t" \ 41134c2f668SLeonid Yegoshin ".insn\n\t" \ 41234c2f668SLeonid Yegoshin ".section\t.fixup,\"ax\"\n\t" \ 41334c2f668SLeonid Yegoshin "4:\tli\t%0, %3\n\t" \ 41434c2f668SLeonid Yegoshin "j\t3b\n\t" \ 41534c2f668SLeonid Yegoshin ".previous\n\t" \ 41634c2f668SLeonid Yegoshin ".section\t__ex_table,\"a\"\n\t" \ 41734c2f668SLeonid Yegoshin STR(PTR)"\t1b, 4b\n\t" \ 41834c2f668SLeonid Yegoshin STR(PTR)"\t2b, 4b\n\t" \ 41934c2f668SLeonid Yegoshin ".previous" \ 42034c2f668SLeonid Yegoshin : "=r" (res) \ 42134c2f668SLeonid Yegoshin : "r" (value), "r" (addr), "i" (-EFAULT)); 42234c2f668SLeonid Yegoshin #endif 42334c2f668SLeonid Yegoshin 4247f18f151SRalf Baechle static void emulate_load_store_insn(struct pt_regs *regs, 4257f18f151SRalf Baechle void __user *addr, unsigned int __user *pc) 4261da177e4SLinus Torvalds { 4271da177e4SLinus Torvalds union mips_instruction insn; 4281da177e4SLinus Torvalds unsigned long value; 4291da177e4SLinus Torvalds unsigned int res; 43034c2f668SLeonid Yegoshin unsigned long origpc; 43134c2f668SLeonid Yegoshin unsigned long orig31; 432102cedc3SLeonid Yegoshin void __user *fault_addr = NULL; 4331da177e4SLinus Torvalds 43434c2f668SLeonid Yegoshin origpc = (unsigned long)pc; 43534c2f668SLeonid Yegoshin orig31 = regs->regs[31]; 43634c2f668SLeonid Yegoshin 437a8b0ca17SPeter Zijlstra perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS, 1, regs, 0); 4387f788d2dSDeng-Cheng Zhu 4391da177e4SLinus Torvalds /* 4401da177e4SLinus Torvalds * This load never faults. 4411da177e4SLinus Torvalds */ 442fe00f943SRalf Baechle __get_user(insn.word, pc); 4431da177e4SLinus Torvalds 4441da177e4SLinus Torvalds switch (insn.i_format.opcode) { 4451da177e4SLinus Torvalds /* 4461da177e4SLinus Torvalds * These are instructions that a compiler doesn't generate. We 4471da177e4SLinus Torvalds * can assume therefore that the code is MIPS-aware and 4481da177e4SLinus Torvalds * really buggy. Emulating these instructions would break the 4491da177e4SLinus Torvalds * semantics anyway. 4501da177e4SLinus Torvalds */ 4511da177e4SLinus Torvalds case ll_op: 4521da177e4SLinus Torvalds case lld_op: 4531da177e4SLinus Torvalds case sc_op: 4541da177e4SLinus Torvalds case scd_op: 4551da177e4SLinus Torvalds 4561da177e4SLinus Torvalds /* 4571da177e4SLinus Torvalds * For these instructions the only way to create an address 4581da177e4SLinus Torvalds * error is an attempted access to kernel/supervisor address 4591da177e4SLinus Torvalds * space. 4601da177e4SLinus Torvalds */ 4611da177e4SLinus Torvalds case ldl_op: 4621da177e4SLinus Torvalds case ldr_op: 4631da177e4SLinus Torvalds case lwl_op: 4641da177e4SLinus Torvalds case lwr_op: 4651da177e4SLinus Torvalds case sdl_op: 4661da177e4SLinus Torvalds case sdr_op: 4671da177e4SLinus Torvalds case swl_op: 4681da177e4SLinus Torvalds case swr_op: 4691da177e4SLinus Torvalds case lb_op: 4701da177e4SLinus Torvalds case lbu_op: 4711da177e4SLinus Torvalds case sb_op: 4721da177e4SLinus Torvalds goto sigbus; 4731da177e4SLinus Torvalds 4741da177e4SLinus Torvalds /* 47534c2f668SLeonid Yegoshin * The remaining opcodes are the ones that are really of 47634c2f668SLeonid Yegoshin * interest. 4771da177e4SLinus Torvalds */ 4781da177e4SLinus Torvalds case lh_op: 4791da177e4SLinus Torvalds if (!access_ok(VERIFY_READ, addr, 2)) 4801da177e4SLinus Torvalds goto sigbus; 4811da177e4SLinus Torvalds 48234c2f668SLeonid Yegoshin LoadHW(addr, value, res); 4831da177e4SLinus Torvalds if (res) 4841da177e4SLinus Torvalds goto fault; 4857f18f151SRalf Baechle compute_return_epc(regs); 4867f18f151SRalf Baechle regs->regs[insn.i_format.rt] = value; 4871da177e4SLinus Torvalds break; 4881da177e4SLinus Torvalds 4891da177e4SLinus Torvalds case lw_op: 4901da177e4SLinus Torvalds if (!access_ok(VERIFY_READ, addr, 4)) 4911da177e4SLinus Torvalds goto sigbus; 4921da177e4SLinus Torvalds 49334c2f668SLeonid Yegoshin LoadW(addr, value, res); 4941da177e4SLinus Torvalds if (res) 4951da177e4SLinus Torvalds goto fault; 4967f18f151SRalf Baechle compute_return_epc(regs); 4977f18f151SRalf Baechle regs->regs[insn.i_format.rt] = value; 4981da177e4SLinus Torvalds break; 4991da177e4SLinus Torvalds 5001da177e4SLinus Torvalds case lhu_op: 5011da177e4SLinus Torvalds if (!access_ok(VERIFY_READ, addr, 2)) 5021da177e4SLinus Torvalds goto sigbus; 5031da177e4SLinus Torvalds 50434c2f668SLeonid Yegoshin LoadHWU(addr, value, res); 5051da177e4SLinus Torvalds if (res) 5061da177e4SLinus Torvalds goto fault; 5077f18f151SRalf Baechle compute_return_epc(regs); 5087f18f151SRalf Baechle regs->regs[insn.i_format.rt] = value; 5091da177e4SLinus Torvalds break; 5101da177e4SLinus Torvalds 5111da177e4SLinus Torvalds case lwu_op: 512875d43e7SRalf Baechle #ifdef CONFIG_64BIT 5131da177e4SLinus Torvalds /* 5141da177e4SLinus Torvalds * A 32-bit kernel might be running on a 64-bit processor. But 5151da177e4SLinus Torvalds * if we're on a 32-bit processor and an i-cache incoherency 5161da177e4SLinus Torvalds * or race makes us see a 64-bit instruction here the sdl/sdr 5171da177e4SLinus Torvalds * would blow up, so for now we don't handle unaligned 64-bit 5181da177e4SLinus Torvalds * instructions on 32-bit kernels. 5191da177e4SLinus Torvalds */ 5201da177e4SLinus Torvalds if (!access_ok(VERIFY_READ, addr, 4)) 5211da177e4SLinus Torvalds goto sigbus; 5221da177e4SLinus Torvalds 52334c2f668SLeonid Yegoshin LoadWU(addr, value, res); 5241da177e4SLinus Torvalds if (res) 5251da177e4SLinus Torvalds goto fault; 5267f18f151SRalf Baechle compute_return_epc(regs); 5277f18f151SRalf Baechle regs->regs[insn.i_format.rt] = value; 5281da177e4SLinus Torvalds break; 529875d43e7SRalf Baechle #endif /* CONFIG_64BIT */ 5301da177e4SLinus Torvalds 5311da177e4SLinus Torvalds /* Cannot handle 64-bit instructions in 32-bit kernel */ 5321da177e4SLinus Torvalds goto sigill; 5331da177e4SLinus Torvalds 5341da177e4SLinus Torvalds case ld_op: 535875d43e7SRalf Baechle #ifdef CONFIG_64BIT 5361da177e4SLinus Torvalds /* 5371da177e4SLinus Torvalds * A 32-bit kernel might be running on a 64-bit processor. But 5381da177e4SLinus Torvalds * if we're on a 32-bit processor and an i-cache incoherency 5391da177e4SLinus Torvalds * or race makes us see a 64-bit instruction here the sdl/sdr 5401da177e4SLinus Torvalds * would blow up, so for now we don't handle unaligned 64-bit 5411da177e4SLinus Torvalds * instructions on 32-bit kernels. 5421da177e4SLinus Torvalds */ 5431da177e4SLinus Torvalds if (!access_ok(VERIFY_READ, addr, 8)) 5441da177e4SLinus Torvalds goto sigbus; 5451da177e4SLinus Torvalds 54634c2f668SLeonid Yegoshin LoadDW(addr, value, res); 5471da177e4SLinus Torvalds if (res) 5481da177e4SLinus Torvalds goto fault; 5497f18f151SRalf Baechle compute_return_epc(regs); 5507f18f151SRalf Baechle regs->regs[insn.i_format.rt] = value; 5511da177e4SLinus Torvalds break; 552875d43e7SRalf Baechle #endif /* CONFIG_64BIT */ 5531da177e4SLinus Torvalds 5541da177e4SLinus Torvalds /* Cannot handle 64-bit instructions in 32-bit kernel */ 5551da177e4SLinus Torvalds goto sigill; 5561da177e4SLinus Torvalds 5571da177e4SLinus Torvalds case sh_op: 5581da177e4SLinus Torvalds if (!access_ok(VERIFY_WRITE, addr, 2)) 5591da177e4SLinus Torvalds goto sigbus; 5601da177e4SLinus Torvalds 56134c2f668SLeonid Yegoshin compute_return_epc(regs); 5621da177e4SLinus Torvalds value = regs->regs[insn.i_format.rt]; 56334c2f668SLeonid Yegoshin StoreHW(addr, value, res); 5641da177e4SLinus Torvalds if (res) 5651da177e4SLinus Torvalds goto fault; 5661da177e4SLinus Torvalds break; 5671da177e4SLinus Torvalds 5681da177e4SLinus Torvalds case sw_op: 5691da177e4SLinus Torvalds if (!access_ok(VERIFY_WRITE, addr, 4)) 5701da177e4SLinus Torvalds goto sigbus; 5711da177e4SLinus Torvalds 57234c2f668SLeonid Yegoshin compute_return_epc(regs); 5731da177e4SLinus Torvalds value = regs->regs[insn.i_format.rt]; 57434c2f668SLeonid Yegoshin StoreW(addr, value, res); 5751da177e4SLinus Torvalds if (res) 5761da177e4SLinus Torvalds goto fault; 5771da177e4SLinus Torvalds break; 5781da177e4SLinus Torvalds 5791da177e4SLinus Torvalds case sd_op: 580875d43e7SRalf Baechle #ifdef CONFIG_64BIT 5811da177e4SLinus Torvalds /* 5821da177e4SLinus Torvalds * A 32-bit kernel might be running on a 64-bit processor. But 5831da177e4SLinus Torvalds * if we're on a 32-bit processor and an i-cache incoherency 5841da177e4SLinus Torvalds * or race makes us see a 64-bit instruction here the sdl/sdr 5851da177e4SLinus Torvalds * would blow up, so for now we don't handle unaligned 64-bit 5861da177e4SLinus Torvalds * instructions on 32-bit kernels. 5871da177e4SLinus Torvalds */ 5881da177e4SLinus Torvalds if (!access_ok(VERIFY_WRITE, addr, 8)) 5891da177e4SLinus Torvalds goto sigbus; 5901da177e4SLinus Torvalds 59134c2f668SLeonid Yegoshin compute_return_epc(regs); 5921da177e4SLinus Torvalds value = regs->regs[insn.i_format.rt]; 59334c2f668SLeonid Yegoshin StoreDW(addr, value, res); 5941da177e4SLinus Torvalds if (res) 5951da177e4SLinus Torvalds goto fault; 5961da177e4SLinus Torvalds break; 597875d43e7SRalf Baechle #endif /* CONFIG_64BIT */ 5981da177e4SLinus Torvalds 5991da177e4SLinus Torvalds /* Cannot handle 64-bit instructions in 32-bit kernel */ 6001da177e4SLinus Torvalds goto sigill; 6011da177e4SLinus Torvalds 6021da177e4SLinus Torvalds case lwc1_op: 6031da177e4SLinus Torvalds case ldc1_op: 6041da177e4SLinus Torvalds case swc1_op: 6051da177e4SLinus Torvalds case sdc1_op: 606102cedc3SLeonid Yegoshin die_if_kernel("Unaligned FP access in kernel code", regs); 607102cedc3SLeonid Yegoshin BUG_ON(!used_math()); 608102cedc3SLeonid Yegoshin BUG_ON(!is_fpu_owner()); 609102cedc3SLeonid Yegoshin 610102cedc3SLeonid Yegoshin lose_fpu(1); /* Save FPU state for the emulator. */ 611102cedc3SLeonid Yegoshin res = fpu_emulator_cop1Handler(regs, ¤t->thread.fpu, 1, 612102cedc3SLeonid Yegoshin &fault_addr); 613102cedc3SLeonid Yegoshin own_fpu(1); /* Restore FPU state. */ 614102cedc3SLeonid Yegoshin 615102cedc3SLeonid Yegoshin /* Signal if something went wrong. */ 616102cedc3SLeonid Yegoshin process_fpemu_return(res, fault_addr); 617102cedc3SLeonid Yegoshin 618102cedc3SLeonid Yegoshin if (res == 0) 619102cedc3SLeonid Yegoshin break; 620102cedc3SLeonid Yegoshin return; 6211da177e4SLinus Torvalds 6221da177e4SLinus Torvalds /* 62369f3a7deSRalf Baechle * COP2 is available to implementor for application specific use. 62469f3a7deSRalf Baechle * It's up to applications to register a notifier chain and do 62569f3a7deSRalf Baechle * whatever they have to do, including possible sending of signals. 6261da177e4SLinus Torvalds */ 62769f3a7deSRalf Baechle case lwc2_op: 62869f3a7deSRalf Baechle cu2_notifier_call_chain(CU2_LWC2_OP, regs); 62969f3a7deSRalf Baechle break; 63069f3a7deSRalf Baechle 63169f3a7deSRalf Baechle case ldc2_op: 63269f3a7deSRalf Baechle cu2_notifier_call_chain(CU2_LDC2_OP, regs); 63369f3a7deSRalf Baechle break; 63469f3a7deSRalf Baechle 63569f3a7deSRalf Baechle case swc2_op: 63669f3a7deSRalf Baechle cu2_notifier_call_chain(CU2_SWC2_OP, regs); 63769f3a7deSRalf Baechle break; 63869f3a7deSRalf Baechle 63969f3a7deSRalf Baechle case sdc2_op: 64069f3a7deSRalf Baechle cu2_notifier_call_chain(CU2_SDC2_OP, regs); 64169f3a7deSRalf Baechle break; 64269f3a7deSRalf Baechle 6431da177e4SLinus Torvalds default: 6441da177e4SLinus Torvalds /* 6451da177e4SLinus Torvalds * Pheeee... We encountered an yet unknown instruction or 6461da177e4SLinus Torvalds * cache coherence problem. Die sucker, die ... 6471da177e4SLinus Torvalds */ 6481da177e4SLinus Torvalds goto sigill; 6491da177e4SLinus Torvalds } 6501da177e4SLinus Torvalds 6516312e0eeSAtsushi Nemoto #ifdef CONFIG_DEBUG_FS 6521da177e4SLinus Torvalds unaligned_instructions++; 6531da177e4SLinus Torvalds #endif 6541da177e4SLinus Torvalds 6557f18f151SRalf Baechle return; 6561da177e4SLinus Torvalds 6571da177e4SLinus Torvalds fault: 65834c2f668SLeonid Yegoshin /* roll back jump/branch */ 65934c2f668SLeonid Yegoshin regs->cp0_epc = origpc; 66034c2f668SLeonid Yegoshin regs->regs[31] = orig31; 6611da177e4SLinus Torvalds /* Did we have an exception handler installed? */ 6621da177e4SLinus Torvalds if (fixup_exception(regs)) 6637f18f151SRalf Baechle return; 6641da177e4SLinus Torvalds 6651da177e4SLinus Torvalds die_if_kernel("Unhandled kernel unaligned access", regs); 666a6d5ff04SDavid Daney force_sig(SIGSEGV, current); 6671da177e4SLinus Torvalds 6687f18f151SRalf Baechle return; 6691da177e4SLinus Torvalds 6701da177e4SLinus Torvalds sigbus: 6711da177e4SLinus Torvalds die_if_kernel("Unhandled kernel unaligned access", regs); 672a6d5ff04SDavid Daney force_sig(SIGBUS, current); 6731da177e4SLinus Torvalds 6747f18f151SRalf Baechle return; 6751da177e4SLinus Torvalds 6761da177e4SLinus Torvalds sigill: 67734c2f668SLeonid Yegoshin die_if_kernel 67834c2f668SLeonid Yegoshin ("Unhandled kernel unaligned access or invalid instruction", regs); 67934c2f668SLeonid Yegoshin force_sig(SIGILL, current); 68034c2f668SLeonid Yegoshin } 68134c2f668SLeonid Yegoshin 68234c2f668SLeonid Yegoshin /* Recode table from 16-bit register notation to 32-bit GPR. */ 68334c2f668SLeonid Yegoshin const int reg16to32[] = { 16, 17, 2, 3, 4, 5, 6, 7 }; 68434c2f668SLeonid Yegoshin 68534c2f668SLeonid Yegoshin /* Recode table from 16-bit STORE register notation to 32-bit GPR. */ 68634c2f668SLeonid Yegoshin const int reg16to32st[] = { 0, 17, 2, 3, 4, 5, 6, 7 }; 68734c2f668SLeonid Yegoshin 688*74338805SDavid Daney static void emulate_load_store_microMIPS(struct pt_regs *regs, 689*74338805SDavid Daney void __user *addr) 69034c2f668SLeonid Yegoshin { 69134c2f668SLeonid Yegoshin unsigned long value; 69234c2f668SLeonid Yegoshin unsigned int res; 69334c2f668SLeonid Yegoshin int i; 69434c2f668SLeonid Yegoshin unsigned int reg = 0, rvar; 69534c2f668SLeonid Yegoshin unsigned long orig31; 69634c2f668SLeonid Yegoshin u16 __user *pc16; 69734c2f668SLeonid Yegoshin u16 halfword; 69834c2f668SLeonid Yegoshin unsigned int word; 69934c2f668SLeonid Yegoshin unsigned long origpc, contpc; 70034c2f668SLeonid Yegoshin union mips_instruction insn; 70134c2f668SLeonid Yegoshin struct mm_decoded_insn mminsn; 70234c2f668SLeonid Yegoshin void __user *fault_addr = NULL; 70334c2f668SLeonid Yegoshin 70434c2f668SLeonid Yegoshin origpc = regs->cp0_epc; 70534c2f668SLeonid Yegoshin orig31 = regs->regs[31]; 70634c2f668SLeonid Yegoshin 70734c2f668SLeonid Yegoshin mminsn.micro_mips_mode = 1; 70834c2f668SLeonid Yegoshin 70934c2f668SLeonid Yegoshin /* 71034c2f668SLeonid Yegoshin * This load never faults. 71134c2f668SLeonid Yegoshin */ 71234c2f668SLeonid Yegoshin pc16 = (unsigned short __user *)msk_isa16_mode(regs->cp0_epc); 71334c2f668SLeonid Yegoshin __get_user(halfword, pc16); 71434c2f668SLeonid Yegoshin pc16++; 71534c2f668SLeonid Yegoshin contpc = regs->cp0_epc + 2; 71634c2f668SLeonid Yegoshin word = ((unsigned int)halfword << 16); 71734c2f668SLeonid Yegoshin mminsn.pc_inc = 2; 71834c2f668SLeonid Yegoshin 71934c2f668SLeonid Yegoshin if (!mm_insn_16bit(halfword)) { 72034c2f668SLeonid Yegoshin __get_user(halfword, pc16); 72134c2f668SLeonid Yegoshin pc16++; 72234c2f668SLeonid Yegoshin contpc = regs->cp0_epc + 4; 72334c2f668SLeonid Yegoshin mminsn.pc_inc = 4; 72434c2f668SLeonid Yegoshin word |= halfword; 72534c2f668SLeonid Yegoshin } 72634c2f668SLeonid Yegoshin mminsn.insn = word; 72734c2f668SLeonid Yegoshin 72834c2f668SLeonid Yegoshin if (get_user(halfword, pc16)) 72934c2f668SLeonid Yegoshin goto fault; 73034c2f668SLeonid Yegoshin mminsn.next_pc_inc = 2; 73134c2f668SLeonid Yegoshin word = ((unsigned int)halfword << 16); 73234c2f668SLeonid Yegoshin 73334c2f668SLeonid Yegoshin if (!mm_insn_16bit(halfword)) { 73434c2f668SLeonid Yegoshin pc16++; 73534c2f668SLeonid Yegoshin if (get_user(halfword, pc16)) 73634c2f668SLeonid Yegoshin goto fault; 73734c2f668SLeonid Yegoshin mminsn.next_pc_inc = 4; 73834c2f668SLeonid Yegoshin word |= halfword; 73934c2f668SLeonid Yegoshin } 74034c2f668SLeonid Yegoshin mminsn.next_insn = word; 74134c2f668SLeonid Yegoshin 74234c2f668SLeonid Yegoshin insn = (union mips_instruction)(mminsn.insn); 74334c2f668SLeonid Yegoshin if (mm_isBranchInstr(regs, mminsn, &contpc)) 74434c2f668SLeonid Yegoshin insn = (union mips_instruction)(mminsn.next_insn); 74534c2f668SLeonid Yegoshin 74634c2f668SLeonid Yegoshin /* Parse instruction to find what to do */ 74734c2f668SLeonid Yegoshin 74834c2f668SLeonid Yegoshin switch (insn.mm_i_format.opcode) { 74934c2f668SLeonid Yegoshin 75034c2f668SLeonid Yegoshin case mm_pool32a_op: 75134c2f668SLeonid Yegoshin switch (insn.mm_x_format.func) { 75234c2f668SLeonid Yegoshin case mm_lwxs_op: 75334c2f668SLeonid Yegoshin reg = insn.mm_x_format.rd; 75434c2f668SLeonid Yegoshin goto loadW; 75534c2f668SLeonid Yegoshin } 75634c2f668SLeonid Yegoshin 75734c2f668SLeonid Yegoshin goto sigbus; 75834c2f668SLeonid Yegoshin 75934c2f668SLeonid Yegoshin case mm_pool32b_op: 76034c2f668SLeonid Yegoshin switch (insn.mm_m_format.func) { 76134c2f668SLeonid Yegoshin case mm_lwp_func: 76234c2f668SLeonid Yegoshin reg = insn.mm_m_format.rd; 76334c2f668SLeonid Yegoshin if (reg == 31) 76434c2f668SLeonid Yegoshin goto sigbus; 76534c2f668SLeonid Yegoshin 76634c2f668SLeonid Yegoshin if (!access_ok(VERIFY_READ, addr, 8)) 76734c2f668SLeonid Yegoshin goto sigbus; 76834c2f668SLeonid Yegoshin 76934c2f668SLeonid Yegoshin LoadW(addr, value, res); 77034c2f668SLeonid Yegoshin if (res) 77134c2f668SLeonid Yegoshin goto fault; 77234c2f668SLeonid Yegoshin regs->regs[reg] = value; 77334c2f668SLeonid Yegoshin addr += 4; 77434c2f668SLeonid Yegoshin LoadW(addr, value, res); 77534c2f668SLeonid Yegoshin if (res) 77634c2f668SLeonid Yegoshin goto fault; 77734c2f668SLeonid Yegoshin regs->regs[reg + 1] = value; 77834c2f668SLeonid Yegoshin goto success; 77934c2f668SLeonid Yegoshin 78034c2f668SLeonid Yegoshin case mm_swp_func: 78134c2f668SLeonid Yegoshin reg = insn.mm_m_format.rd; 78234c2f668SLeonid Yegoshin if (reg == 31) 78334c2f668SLeonid Yegoshin goto sigbus; 78434c2f668SLeonid Yegoshin 78534c2f668SLeonid Yegoshin if (!access_ok(VERIFY_WRITE, addr, 8)) 78634c2f668SLeonid Yegoshin goto sigbus; 78734c2f668SLeonid Yegoshin 78834c2f668SLeonid Yegoshin value = regs->regs[reg]; 78934c2f668SLeonid Yegoshin StoreW(addr, value, res); 79034c2f668SLeonid Yegoshin if (res) 79134c2f668SLeonid Yegoshin goto fault; 79234c2f668SLeonid Yegoshin addr += 4; 79334c2f668SLeonid Yegoshin value = regs->regs[reg + 1]; 79434c2f668SLeonid Yegoshin StoreW(addr, value, res); 79534c2f668SLeonid Yegoshin if (res) 79634c2f668SLeonid Yegoshin goto fault; 79734c2f668SLeonid Yegoshin goto success; 79834c2f668SLeonid Yegoshin 79934c2f668SLeonid Yegoshin case mm_ldp_func: 80034c2f668SLeonid Yegoshin #ifdef CONFIG_64BIT 80134c2f668SLeonid Yegoshin reg = insn.mm_m_format.rd; 80234c2f668SLeonid Yegoshin if (reg == 31) 80334c2f668SLeonid Yegoshin goto sigbus; 80434c2f668SLeonid Yegoshin 80534c2f668SLeonid Yegoshin if (!access_ok(VERIFY_READ, addr, 16)) 80634c2f668SLeonid Yegoshin goto sigbus; 80734c2f668SLeonid Yegoshin 80834c2f668SLeonid Yegoshin LoadDW(addr, value, res); 80934c2f668SLeonid Yegoshin if (res) 81034c2f668SLeonid Yegoshin goto fault; 81134c2f668SLeonid Yegoshin regs->regs[reg] = value; 81234c2f668SLeonid Yegoshin addr += 8; 81334c2f668SLeonid Yegoshin LoadDW(addr, value, res); 81434c2f668SLeonid Yegoshin if (res) 81534c2f668SLeonid Yegoshin goto fault; 81634c2f668SLeonid Yegoshin regs->regs[reg + 1] = value; 81734c2f668SLeonid Yegoshin goto success; 81834c2f668SLeonid Yegoshin #endif /* CONFIG_64BIT */ 81934c2f668SLeonid Yegoshin 82034c2f668SLeonid Yegoshin goto sigill; 82134c2f668SLeonid Yegoshin 82234c2f668SLeonid Yegoshin case mm_sdp_func: 82334c2f668SLeonid Yegoshin #ifdef CONFIG_64BIT 82434c2f668SLeonid Yegoshin reg = insn.mm_m_format.rd; 82534c2f668SLeonid Yegoshin if (reg == 31) 82634c2f668SLeonid Yegoshin goto sigbus; 82734c2f668SLeonid Yegoshin 82834c2f668SLeonid Yegoshin if (!access_ok(VERIFY_WRITE, addr, 16)) 82934c2f668SLeonid Yegoshin goto sigbus; 83034c2f668SLeonid Yegoshin 83134c2f668SLeonid Yegoshin value = regs->regs[reg]; 83234c2f668SLeonid Yegoshin StoreDW(addr, value, res); 83334c2f668SLeonid Yegoshin if (res) 83434c2f668SLeonid Yegoshin goto fault; 83534c2f668SLeonid Yegoshin addr += 8; 83634c2f668SLeonid Yegoshin value = regs->regs[reg + 1]; 83734c2f668SLeonid Yegoshin StoreDW(addr, value, res); 83834c2f668SLeonid Yegoshin if (res) 83934c2f668SLeonid Yegoshin goto fault; 84034c2f668SLeonid Yegoshin goto success; 84134c2f668SLeonid Yegoshin #endif /* CONFIG_64BIT */ 84234c2f668SLeonid Yegoshin 84334c2f668SLeonid Yegoshin goto sigill; 84434c2f668SLeonid Yegoshin 84534c2f668SLeonid Yegoshin case mm_lwm32_func: 84634c2f668SLeonid Yegoshin reg = insn.mm_m_format.rd; 84734c2f668SLeonid Yegoshin rvar = reg & 0xf; 84834c2f668SLeonid Yegoshin if ((rvar > 9) || !reg) 84934c2f668SLeonid Yegoshin goto sigill; 85034c2f668SLeonid Yegoshin if (reg & 0x10) { 85134c2f668SLeonid Yegoshin if (!access_ok 85234c2f668SLeonid Yegoshin (VERIFY_READ, addr, 4 * (rvar + 1))) 85334c2f668SLeonid Yegoshin goto sigbus; 85434c2f668SLeonid Yegoshin } else { 85534c2f668SLeonid Yegoshin if (!access_ok(VERIFY_READ, addr, 4 * rvar)) 85634c2f668SLeonid Yegoshin goto sigbus; 85734c2f668SLeonid Yegoshin } 85834c2f668SLeonid Yegoshin if (rvar == 9) 85934c2f668SLeonid Yegoshin rvar = 8; 86034c2f668SLeonid Yegoshin for (i = 16; rvar; rvar--, i++) { 86134c2f668SLeonid Yegoshin LoadW(addr, value, res); 86234c2f668SLeonid Yegoshin if (res) 86334c2f668SLeonid Yegoshin goto fault; 86434c2f668SLeonid Yegoshin addr += 4; 86534c2f668SLeonid Yegoshin regs->regs[i] = value; 86634c2f668SLeonid Yegoshin } 86734c2f668SLeonid Yegoshin if ((reg & 0xf) == 9) { 86834c2f668SLeonid Yegoshin LoadW(addr, value, res); 86934c2f668SLeonid Yegoshin if (res) 87034c2f668SLeonid Yegoshin goto fault; 87134c2f668SLeonid Yegoshin addr += 4; 87234c2f668SLeonid Yegoshin regs->regs[30] = value; 87334c2f668SLeonid Yegoshin } 87434c2f668SLeonid Yegoshin if (reg & 0x10) { 87534c2f668SLeonid Yegoshin LoadW(addr, value, res); 87634c2f668SLeonid Yegoshin if (res) 87734c2f668SLeonid Yegoshin goto fault; 87834c2f668SLeonid Yegoshin regs->regs[31] = value; 87934c2f668SLeonid Yegoshin } 88034c2f668SLeonid Yegoshin goto success; 88134c2f668SLeonid Yegoshin 88234c2f668SLeonid Yegoshin case mm_swm32_func: 88334c2f668SLeonid Yegoshin reg = insn.mm_m_format.rd; 88434c2f668SLeonid Yegoshin rvar = reg & 0xf; 88534c2f668SLeonid Yegoshin if ((rvar > 9) || !reg) 88634c2f668SLeonid Yegoshin goto sigill; 88734c2f668SLeonid Yegoshin if (reg & 0x10) { 88834c2f668SLeonid Yegoshin if (!access_ok 88934c2f668SLeonid Yegoshin (VERIFY_WRITE, addr, 4 * (rvar + 1))) 89034c2f668SLeonid Yegoshin goto sigbus; 89134c2f668SLeonid Yegoshin } else { 89234c2f668SLeonid Yegoshin if (!access_ok(VERIFY_WRITE, addr, 4 * rvar)) 89334c2f668SLeonid Yegoshin goto sigbus; 89434c2f668SLeonid Yegoshin } 89534c2f668SLeonid Yegoshin if (rvar == 9) 89634c2f668SLeonid Yegoshin rvar = 8; 89734c2f668SLeonid Yegoshin for (i = 16; rvar; rvar--, i++) { 89834c2f668SLeonid Yegoshin value = regs->regs[i]; 89934c2f668SLeonid Yegoshin StoreW(addr, value, res); 90034c2f668SLeonid Yegoshin if (res) 90134c2f668SLeonid Yegoshin goto fault; 90234c2f668SLeonid Yegoshin addr += 4; 90334c2f668SLeonid Yegoshin } 90434c2f668SLeonid Yegoshin if ((reg & 0xf) == 9) { 90534c2f668SLeonid Yegoshin value = regs->regs[30]; 90634c2f668SLeonid Yegoshin StoreW(addr, value, res); 90734c2f668SLeonid Yegoshin if (res) 90834c2f668SLeonid Yegoshin goto fault; 90934c2f668SLeonid Yegoshin addr += 4; 91034c2f668SLeonid Yegoshin } 91134c2f668SLeonid Yegoshin if (reg & 0x10) { 91234c2f668SLeonid Yegoshin value = regs->regs[31]; 91334c2f668SLeonid Yegoshin StoreW(addr, value, res); 91434c2f668SLeonid Yegoshin if (res) 91534c2f668SLeonid Yegoshin goto fault; 91634c2f668SLeonid Yegoshin } 91734c2f668SLeonid Yegoshin goto success; 91834c2f668SLeonid Yegoshin 91934c2f668SLeonid Yegoshin case mm_ldm_func: 92034c2f668SLeonid Yegoshin #ifdef CONFIG_64BIT 92134c2f668SLeonid Yegoshin reg = insn.mm_m_format.rd; 92234c2f668SLeonid Yegoshin rvar = reg & 0xf; 92334c2f668SLeonid Yegoshin if ((rvar > 9) || !reg) 92434c2f668SLeonid Yegoshin goto sigill; 92534c2f668SLeonid Yegoshin if (reg & 0x10) { 92634c2f668SLeonid Yegoshin if (!access_ok 92734c2f668SLeonid Yegoshin (VERIFY_READ, addr, 8 * (rvar + 1))) 92834c2f668SLeonid Yegoshin goto sigbus; 92934c2f668SLeonid Yegoshin } else { 93034c2f668SLeonid Yegoshin if (!access_ok(VERIFY_READ, addr, 8 * rvar)) 93134c2f668SLeonid Yegoshin goto sigbus; 93234c2f668SLeonid Yegoshin } 93334c2f668SLeonid Yegoshin if (rvar == 9) 93434c2f668SLeonid Yegoshin rvar = 8; 93534c2f668SLeonid Yegoshin 93634c2f668SLeonid Yegoshin for (i = 16; rvar; rvar--, i++) { 93734c2f668SLeonid Yegoshin LoadDW(addr, value, res); 93834c2f668SLeonid Yegoshin if (res) 93934c2f668SLeonid Yegoshin goto fault; 94034c2f668SLeonid Yegoshin addr += 4; 94134c2f668SLeonid Yegoshin regs->regs[i] = value; 94234c2f668SLeonid Yegoshin } 94334c2f668SLeonid Yegoshin if ((reg & 0xf) == 9) { 94434c2f668SLeonid Yegoshin LoadDW(addr, value, res); 94534c2f668SLeonid Yegoshin if (res) 94634c2f668SLeonid Yegoshin goto fault; 94734c2f668SLeonid Yegoshin addr += 8; 94834c2f668SLeonid Yegoshin regs->regs[30] = value; 94934c2f668SLeonid Yegoshin } 95034c2f668SLeonid Yegoshin if (reg & 0x10) { 95134c2f668SLeonid Yegoshin LoadDW(addr, value, res); 95234c2f668SLeonid Yegoshin if (res) 95334c2f668SLeonid Yegoshin goto fault; 95434c2f668SLeonid Yegoshin regs->regs[31] = value; 95534c2f668SLeonid Yegoshin } 95634c2f668SLeonid Yegoshin goto success; 95734c2f668SLeonid Yegoshin #endif /* CONFIG_64BIT */ 95834c2f668SLeonid Yegoshin 95934c2f668SLeonid Yegoshin goto sigill; 96034c2f668SLeonid Yegoshin 96134c2f668SLeonid Yegoshin case mm_sdm_func: 96234c2f668SLeonid Yegoshin #ifdef CONFIG_64BIT 96334c2f668SLeonid Yegoshin reg = insn.mm_m_format.rd; 96434c2f668SLeonid Yegoshin rvar = reg & 0xf; 96534c2f668SLeonid Yegoshin if ((rvar > 9) || !reg) 96634c2f668SLeonid Yegoshin goto sigill; 96734c2f668SLeonid Yegoshin if (reg & 0x10) { 96834c2f668SLeonid Yegoshin if (!access_ok 96934c2f668SLeonid Yegoshin (VERIFY_WRITE, addr, 8 * (rvar + 1))) 97034c2f668SLeonid Yegoshin goto sigbus; 97134c2f668SLeonid Yegoshin } else { 97234c2f668SLeonid Yegoshin if (!access_ok(VERIFY_WRITE, addr, 8 * rvar)) 97334c2f668SLeonid Yegoshin goto sigbus; 97434c2f668SLeonid Yegoshin } 97534c2f668SLeonid Yegoshin if (rvar == 9) 97634c2f668SLeonid Yegoshin rvar = 8; 97734c2f668SLeonid Yegoshin 97834c2f668SLeonid Yegoshin for (i = 16; rvar; rvar--, i++) { 97934c2f668SLeonid Yegoshin value = regs->regs[i]; 98034c2f668SLeonid Yegoshin StoreDW(addr, value, res); 98134c2f668SLeonid Yegoshin if (res) 98234c2f668SLeonid Yegoshin goto fault; 98334c2f668SLeonid Yegoshin addr += 8; 98434c2f668SLeonid Yegoshin } 98534c2f668SLeonid Yegoshin if ((reg & 0xf) == 9) { 98634c2f668SLeonid Yegoshin value = regs->regs[30]; 98734c2f668SLeonid Yegoshin StoreDW(addr, value, res); 98834c2f668SLeonid Yegoshin if (res) 98934c2f668SLeonid Yegoshin goto fault; 99034c2f668SLeonid Yegoshin addr += 8; 99134c2f668SLeonid Yegoshin } 99234c2f668SLeonid Yegoshin if (reg & 0x10) { 99334c2f668SLeonid Yegoshin value = regs->regs[31]; 99434c2f668SLeonid Yegoshin StoreDW(addr, value, res); 99534c2f668SLeonid Yegoshin if (res) 99634c2f668SLeonid Yegoshin goto fault; 99734c2f668SLeonid Yegoshin } 99834c2f668SLeonid Yegoshin goto success; 99934c2f668SLeonid Yegoshin #endif /* CONFIG_64BIT */ 100034c2f668SLeonid Yegoshin 100134c2f668SLeonid Yegoshin goto sigill; 100234c2f668SLeonid Yegoshin 100334c2f668SLeonid Yegoshin /* LWC2, SWC2, LDC2, SDC2 are not serviced */ 100434c2f668SLeonid Yegoshin } 100534c2f668SLeonid Yegoshin 100634c2f668SLeonid Yegoshin goto sigbus; 100734c2f668SLeonid Yegoshin 100834c2f668SLeonid Yegoshin case mm_pool32c_op: 100934c2f668SLeonid Yegoshin switch (insn.mm_m_format.func) { 101034c2f668SLeonid Yegoshin case mm_lwu_func: 101134c2f668SLeonid Yegoshin reg = insn.mm_m_format.rd; 101234c2f668SLeonid Yegoshin goto loadWU; 101334c2f668SLeonid Yegoshin } 101434c2f668SLeonid Yegoshin 101534c2f668SLeonid Yegoshin /* LL,SC,LLD,SCD are not serviced */ 101634c2f668SLeonid Yegoshin goto sigbus; 101734c2f668SLeonid Yegoshin 101834c2f668SLeonid Yegoshin case mm_pool32f_op: 101934c2f668SLeonid Yegoshin switch (insn.mm_x_format.func) { 102034c2f668SLeonid Yegoshin case mm_lwxc1_func: 102134c2f668SLeonid Yegoshin case mm_swxc1_func: 102234c2f668SLeonid Yegoshin case mm_ldxc1_func: 102334c2f668SLeonid Yegoshin case mm_sdxc1_func: 102434c2f668SLeonid Yegoshin goto fpu_emul; 102534c2f668SLeonid Yegoshin } 102634c2f668SLeonid Yegoshin 102734c2f668SLeonid Yegoshin goto sigbus; 102834c2f668SLeonid Yegoshin 102934c2f668SLeonid Yegoshin case mm_ldc132_op: 103034c2f668SLeonid Yegoshin case mm_sdc132_op: 103134c2f668SLeonid Yegoshin case mm_lwc132_op: 103234c2f668SLeonid Yegoshin case mm_swc132_op: 103334c2f668SLeonid Yegoshin fpu_emul: 103434c2f668SLeonid Yegoshin /* roll back jump/branch */ 103534c2f668SLeonid Yegoshin regs->cp0_epc = origpc; 103634c2f668SLeonid Yegoshin regs->regs[31] = orig31; 103734c2f668SLeonid Yegoshin 103834c2f668SLeonid Yegoshin die_if_kernel("Unaligned FP access in kernel code", regs); 103934c2f668SLeonid Yegoshin BUG_ON(!used_math()); 104034c2f668SLeonid Yegoshin BUG_ON(!is_fpu_owner()); 104134c2f668SLeonid Yegoshin 104234c2f668SLeonid Yegoshin lose_fpu(1); /* save the FPU state for the emulator */ 104334c2f668SLeonid Yegoshin res = fpu_emulator_cop1Handler(regs, ¤t->thread.fpu, 1, 104434c2f668SLeonid Yegoshin &fault_addr); 104534c2f668SLeonid Yegoshin own_fpu(1); /* restore FPU state */ 104634c2f668SLeonid Yegoshin 104734c2f668SLeonid Yegoshin /* If something went wrong, signal */ 104834c2f668SLeonid Yegoshin process_fpemu_return(res, fault_addr); 104934c2f668SLeonid Yegoshin 105034c2f668SLeonid Yegoshin if (res == 0) 105134c2f668SLeonid Yegoshin goto success; 105234c2f668SLeonid Yegoshin return; 105334c2f668SLeonid Yegoshin 105434c2f668SLeonid Yegoshin case mm_lh32_op: 105534c2f668SLeonid Yegoshin reg = insn.mm_i_format.rt; 105634c2f668SLeonid Yegoshin goto loadHW; 105734c2f668SLeonid Yegoshin 105834c2f668SLeonid Yegoshin case mm_lhu32_op: 105934c2f668SLeonid Yegoshin reg = insn.mm_i_format.rt; 106034c2f668SLeonid Yegoshin goto loadHWU; 106134c2f668SLeonid Yegoshin 106234c2f668SLeonid Yegoshin case mm_lw32_op: 106334c2f668SLeonid Yegoshin reg = insn.mm_i_format.rt; 106434c2f668SLeonid Yegoshin goto loadW; 106534c2f668SLeonid Yegoshin 106634c2f668SLeonid Yegoshin case mm_sh32_op: 106734c2f668SLeonid Yegoshin reg = insn.mm_i_format.rt; 106834c2f668SLeonid Yegoshin goto storeHW; 106934c2f668SLeonid Yegoshin 107034c2f668SLeonid Yegoshin case mm_sw32_op: 107134c2f668SLeonid Yegoshin reg = insn.mm_i_format.rt; 107234c2f668SLeonid Yegoshin goto storeW; 107334c2f668SLeonid Yegoshin 107434c2f668SLeonid Yegoshin case mm_ld32_op: 107534c2f668SLeonid Yegoshin reg = insn.mm_i_format.rt; 107634c2f668SLeonid Yegoshin goto loadDW; 107734c2f668SLeonid Yegoshin 107834c2f668SLeonid Yegoshin case mm_sd32_op: 107934c2f668SLeonid Yegoshin reg = insn.mm_i_format.rt; 108034c2f668SLeonid Yegoshin goto storeDW; 108134c2f668SLeonid Yegoshin 108234c2f668SLeonid Yegoshin case mm_pool16c_op: 108334c2f668SLeonid Yegoshin switch (insn.mm16_m_format.func) { 108434c2f668SLeonid Yegoshin case mm_lwm16_op: 108534c2f668SLeonid Yegoshin reg = insn.mm16_m_format.rlist; 108634c2f668SLeonid Yegoshin rvar = reg + 1; 108734c2f668SLeonid Yegoshin if (!access_ok(VERIFY_READ, addr, 4 * rvar)) 108834c2f668SLeonid Yegoshin goto sigbus; 108934c2f668SLeonid Yegoshin 109034c2f668SLeonid Yegoshin for (i = 16; rvar; rvar--, i++) { 109134c2f668SLeonid Yegoshin LoadW(addr, value, res); 109234c2f668SLeonid Yegoshin if (res) 109334c2f668SLeonid Yegoshin goto fault; 109434c2f668SLeonid Yegoshin addr += 4; 109534c2f668SLeonid Yegoshin regs->regs[i] = value; 109634c2f668SLeonid Yegoshin } 109734c2f668SLeonid Yegoshin LoadW(addr, value, res); 109834c2f668SLeonid Yegoshin if (res) 109934c2f668SLeonid Yegoshin goto fault; 110034c2f668SLeonid Yegoshin regs->regs[31] = value; 110134c2f668SLeonid Yegoshin 110234c2f668SLeonid Yegoshin goto success; 110334c2f668SLeonid Yegoshin 110434c2f668SLeonid Yegoshin case mm_swm16_op: 110534c2f668SLeonid Yegoshin reg = insn.mm16_m_format.rlist; 110634c2f668SLeonid Yegoshin rvar = reg + 1; 110734c2f668SLeonid Yegoshin if (!access_ok(VERIFY_WRITE, addr, 4 * rvar)) 110834c2f668SLeonid Yegoshin goto sigbus; 110934c2f668SLeonid Yegoshin 111034c2f668SLeonid Yegoshin for (i = 16; rvar; rvar--, i++) { 111134c2f668SLeonid Yegoshin value = regs->regs[i]; 111234c2f668SLeonid Yegoshin StoreW(addr, value, res); 111334c2f668SLeonid Yegoshin if (res) 111434c2f668SLeonid Yegoshin goto fault; 111534c2f668SLeonid Yegoshin addr += 4; 111634c2f668SLeonid Yegoshin } 111734c2f668SLeonid Yegoshin value = regs->regs[31]; 111834c2f668SLeonid Yegoshin StoreW(addr, value, res); 111934c2f668SLeonid Yegoshin if (res) 112034c2f668SLeonid Yegoshin goto fault; 112134c2f668SLeonid Yegoshin 112234c2f668SLeonid Yegoshin goto success; 112334c2f668SLeonid Yegoshin 112434c2f668SLeonid Yegoshin } 112534c2f668SLeonid Yegoshin 112634c2f668SLeonid Yegoshin goto sigbus; 112734c2f668SLeonid Yegoshin 112834c2f668SLeonid Yegoshin case mm_lhu16_op: 112934c2f668SLeonid Yegoshin reg = reg16to32[insn.mm16_rb_format.rt]; 113034c2f668SLeonid Yegoshin goto loadHWU; 113134c2f668SLeonid Yegoshin 113234c2f668SLeonid Yegoshin case mm_lw16_op: 113334c2f668SLeonid Yegoshin reg = reg16to32[insn.mm16_rb_format.rt]; 113434c2f668SLeonid Yegoshin goto loadW; 113534c2f668SLeonid Yegoshin 113634c2f668SLeonid Yegoshin case mm_sh16_op: 113734c2f668SLeonid Yegoshin reg = reg16to32st[insn.mm16_rb_format.rt]; 113834c2f668SLeonid Yegoshin goto storeHW; 113934c2f668SLeonid Yegoshin 114034c2f668SLeonid Yegoshin case mm_sw16_op: 114134c2f668SLeonid Yegoshin reg = reg16to32st[insn.mm16_rb_format.rt]; 114234c2f668SLeonid Yegoshin goto storeW; 114334c2f668SLeonid Yegoshin 114434c2f668SLeonid Yegoshin case mm_lwsp16_op: 114534c2f668SLeonid Yegoshin reg = insn.mm16_r5_format.rt; 114634c2f668SLeonid Yegoshin goto loadW; 114734c2f668SLeonid Yegoshin 114834c2f668SLeonid Yegoshin case mm_swsp16_op: 114934c2f668SLeonid Yegoshin reg = insn.mm16_r5_format.rt; 115034c2f668SLeonid Yegoshin goto storeW; 115134c2f668SLeonid Yegoshin 115234c2f668SLeonid Yegoshin case mm_lwgp16_op: 115334c2f668SLeonid Yegoshin reg = reg16to32[insn.mm16_r3_format.rt]; 115434c2f668SLeonid Yegoshin goto loadW; 115534c2f668SLeonid Yegoshin 115634c2f668SLeonid Yegoshin default: 115734c2f668SLeonid Yegoshin goto sigill; 115834c2f668SLeonid Yegoshin } 115934c2f668SLeonid Yegoshin 116034c2f668SLeonid Yegoshin loadHW: 116134c2f668SLeonid Yegoshin if (!access_ok(VERIFY_READ, addr, 2)) 116234c2f668SLeonid Yegoshin goto sigbus; 116334c2f668SLeonid Yegoshin 116434c2f668SLeonid Yegoshin LoadHW(addr, value, res); 116534c2f668SLeonid Yegoshin if (res) 116634c2f668SLeonid Yegoshin goto fault; 116734c2f668SLeonid Yegoshin regs->regs[reg] = value; 116834c2f668SLeonid Yegoshin goto success; 116934c2f668SLeonid Yegoshin 117034c2f668SLeonid Yegoshin loadHWU: 117134c2f668SLeonid Yegoshin if (!access_ok(VERIFY_READ, addr, 2)) 117234c2f668SLeonid Yegoshin goto sigbus; 117334c2f668SLeonid Yegoshin 117434c2f668SLeonid Yegoshin LoadHWU(addr, value, res); 117534c2f668SLeonid Yegoshin if (res) 117634c2f668SLeonid Yegoshin goto fault; 117734c2f668SLeonid Yegoshin regs->regs[reg] = value; 117834c2f668SLeonid Yegoshin goto success; 117934c2f668SLeonid Yegoshin 118034c2f668SLeonid Yegoshin loadW: 118134c2f668SLeonid Yegoshin if (!access_ok(VERIFY_READ, addr, 4)) 118234c2f668SLeonid Yegoshin goto sigbus; 118334c2f668SLeonid Yegoshin 118434c2f668SLeonid Yegoshin LoadW(addr, value, res); 118534c2f668SLeonid Yegoshin if (res) 118634c2f668SLeonid Yegoshin goto fault; 118734c2f668SLeonid Yegoshin regs->regs[reg] = value; 118834c2f668SLeonid Yegoshin goto success; 118934c2f668SLeonid Yegoshin 119034c2f668SLeonid Yegoshin loadWU: 119134c2f668SLeonid Yegoshin #ifdef CONFIG_64BIT 119234c2f668SLeonid Yegoshin /* 119334c2f668SLeonid Yegoshin * A 32-bit kernel might be running on a 64-bit processor. But 119434c2f668SLeonid Yegoshin * if we're on a 32-bit processor and an i-cache incoherency 119534c2f668SLeonid Yegoshin * or race makes us see a 64-bit instruction here the sdl/sdr 119634c2f668SLeonid Yegoshin * would blow up, so for now we don't handle unaligned 64-bit 119734c2f668SLeonid Yegoshin * instructions on 32-bit kernels. 119834c2f668SLeonid Yegoshin */ 119934c2f668SLeonid Yegoshin if (!access_ok(VERIFY_READ, addr, 4)) 120034c2f668SLeonid Yegoshin goto sigbus; 120134c2f668SLeonid Yegoshin 120234c2f668SLeonid Yegoshin LoadWU(addr, value, res); 120334c2f668SLeonid Yegoshin if (res) 120434c2f668SLeonid Yegoshin goto fault; 120534c2f668SLeonid Yegoshin regs->regs[reg] = value; 120634c2f668SLeonid Yegoshin goto success; 120734c2f668SLeonid Yegoshin #endif /* CONFIG_64BIT */ 120834c2f668SLeonid Yegoshin 120934c2f668SLeonid Yegoshin /* Cannot handle 64-bit instructions in 32-bit kernel */ 121034c2f668SLeonid Yegoshin goto sigill; 121134c2f668SLeonid Yegoshin 121234c2f668SLeonid Yegoshin loadDW: 121334c2f668SLeonid Yegoshin #ifdef CONFIG_64BIT 121434c2f668SLeonid Yegoshin /* 121534c2f668SLeonid Yegoshin * A 32-bit kernel might be running on a 64-bit processor. But 121634c2f668SLeonid Yegoshin * if we're on a 32-bit processor and an i-cache incoherency 121734c2f668SLeonid Yegoshin * or race makes us see a 64-bit instruction here the sdl/sdr 121834c2f668SLeonid Yegoshin * would blow up, so for now we don't handle unaligned 64-bit 121934c2f668SLeonid Yegoshin * instructions on 32-bit kernels. 122034c2f668SLeonid Yegoshin */ 122134c2f668SLeonid Yegoshin if (!access_ok(VERIFY_READ, addr, 8)) 122234c2f668SLeonid Yegoshin goto sigbus; 122334c2f668SLeonid Yegoshin 122434c2f668SLeonid Yegoshin LoadDW(addr, value, res); 122534c2f668SLeonid Yegoshin if (res) 122634c2f668SLeonid Yegoshin goto fault; 122734c2f668SLeonid Yegoshin regs->regs[reg] = value; 122834c2f668SLeonid Yegoshin goto success; 122934c2f668SLeonid Yegoshin #endif /* CONFIG_64BIT */ 123034c2f668SLeonid Yegoshin 123134c2f668SLeonid Yegoshin /* Cannot handle 64-bit instructions in 32-bit kernel */ 123234c2f668SLeonid Yegoshin goto sigill; 123334c2f668SLeonid Yegoshin 123434c2f668SLeonid Yegoshin storeHW: 123534c2f668SLeonid Yegoshin if (!access_ok(VERIFY_WRITE, addr, 2)) 123634c2f668SLeonid Yegoshin goto sigbus; 123734c2f668SLeonid Yegoshin 123834c2f668SLeonid Yegoshin value = regs->regs[reg]; 123934c2f668SLeonid Yegoshin StoreHW(addr, value, res); 124034c2f668SLeonid Yegoshin if (res) 124134c2f668SLeonid Yegoshin goto fault; 124234c2f668SLeonid Yegoshin goto success; 124334c2f668SLeonid Yegoshin 124434c2f668SLeonid Yegoshin storeW: 124534c2f668SLeonid Yegoshin if (!access_ok(VERIFY_WRITE, addr, 4)) 124634c2f668SLeonid Yegoshin goto sigbus; 124734c2f668SLeonid Yegoshin 124834c2f668SLeonid Yegoshin value = regs->regs[reg]; 124934c2f668SLeonid Yegoshin StoreW(addr, value, res); 125034c2f668SLeonid Yegoshin if (res) 125134c2f668SLeonid Yegoshin goto fault; 125234c2f668SLeonid Yegoshin goto success; 125334c2f668SLeonid Yegoshin 125434c2f668SLeonid Yegoshin storeDW: 125534c2f668SLeonid Yegoshin #ifdef CONFIG_64BIT 125634c2f668SLeonid Yegoshin /* 125734c2f668SLeonid Yegoshin * A 32-bit kernel might be running on a 64-bit processor. But 125834c2f668SLeonid Yegoshin * if we're on a 32-bit processor and an i-cache incoherency 125934c2f668SLeonid Yegoshin * or race makes us see a 64-bit instruction here the sdl/sdr 126034c2f668SLeonid Yegoshin * would blow up, so for now we don't handle unaligned 64-bit 126134c2f668SLeonid Yegoshin * instructions on 32-bit kernels. 126234c2f668SLeonid Yegoshin */ 126334c2f668SLeonid Yegoshin if (!access_ok(VERIFY_WRITE, addr, 8)) 126434c2f668SLeonid Yegoshin goto sigbus; 126534c2f668SLeonid Yegoshin 126634c2f668SLeonid Yegoshin value = regs->regs[reg]; 126734c2f668SLeonid Yegoshin StoreDW(addr, value, res); 126834c2f668SLeonid Yegoshin if (res) 126934c2f668SLeonid Yegoshin goto fault; 127034c2f668SLeonid Yegoshin goto success; 127134c2f668SLeonid Yegoshin #endif /* CONFIG_64BIT */ 127234c2f668SLeonid Yegoshin 127334c2f668SLeonid Yegoshin /* Cannot handle 64-bit instructions in 32-bit kernel */ 127434c2f668SLeonid Yegoshin goto sigill; 127534c2f668SLeonid Yegoshin 127634c2f668SLeonid Yegoshin success: 127734c2f668SLeonid Yegoshin regs->cp0_epc = contpc; /* advance or branch */ 127834c2f668SLeonid Yegoshin 127934c2f668SLeonid Yegoshin #ifdef CONFIG_DEBUG_FS 128034c2f668SLeonid Yegoshin unaligned_instructions++; 128134c2f668SLeonid Yegoshin #endif 128234c2f668SLeonid Yegoshin return; 128334c2f668SLeonid Yegoshin 128434c2f668SLeonid Yegoshin fault: 128534c2f668SLeonid Yegoshin /* roll back jump/branch */ 128634c2f668SLeonid Yegoshin regs->cp0_epc = origpc; 128734c2f668SLeonid Yegoshin regs->regs[31] = orig31; 128834c2f668SLeonid Yegoshin /* Did we have an exception handler installed? */ 128934c2f668SLeonid Yegoshin if (fixup_exception(regs)) 129034c2f668SLeonid Yegoshin return; 129134c2f668SLeonid Yegoshin 129234c2f668SLeonid Yegoshin die_if_kernel("Unhandled kernel unaligned access", regs); 129334c2f668SLeonid Yegoshin force_sig(SIGSEGV, current); 129434c2f668SLeonid Yegoshin 129534c2f668SLeonid Yegoshin return; 129634c2f668SLeonid Yegoshin 129734c2f668SLeonid Yegoshin sigbus: 129834c2f668SLeonid Yegoshin die_if_kernel("Unhandled kernel unaligned access", regs); 129934c2f668SLeonid Yegoshin force_sig(SIGBUS, current); 130034c2f668SLeonid Yegoshin 130134c2f668SLeonid Yegoshin return; 130234c2f668SLeonid Yegoshin 130334c2f668SLeonid Yegoshin sigill: 130434c2f668SLeonid Yegoshin die_if_kernel 130534c2f668SLeonid Yegoshin ("Unhandled kernel unaligned access or invalid instruction", regs); 1306a6d5ff04SDavid Daney force_sig(SIGILL, current); 13071da177e4SLinus Torvalds } 13081da177e4SLinus Torvalds 1309451b001bSSteven J. Hill static void emulate_load_store_MIPS16e(struct pt_regs *regs, void __user * addr) 1310451b001bSSteven J. Hill { 1311451b001bSSteven J. Hill unsigned long value; 1312451b001bSSteven J. Hill unsigned int res; 1313451b001bSSteven J. Hill int reg; 1314451b001bSSteven J. Hill unsigned long orig31; 1315451b001bSSteven J. Hill u16 __user *pc16; 1316451b001bSSteven J. Hill unsigned long origpc; 1317451b001bSSteven J. Hill union mips16e_instruction mips16inst, oldinst; 1318451b001bSSteven J. Hill 1319451b001bSSteven J. Hill origpc = regs->cp0_epc; 1320451b001bSSteven J. Hill orig31 = regs->regs[31]; 1321451b001bSSteven J. Hill pc16 = (unsigned short __user *)msk_isa16_mode(origpc); 1322451b001bSSteven J. Hill /* 1323451b001bSSteven J. Hill * This load never faults. 1324451b001bSSteven J. Hill */ 1325451b001bSSteven J. Hill __get_user(mips16inst.full, pc16); 1326451b001bSSteven J. Hill oldinst = mips16inst; 1327451b001bSSteven J. Hill 1328451b001bSSteven J. Hill /* skip EXTEND instruction */ 1329451b001bSSteven J. Hill if (mips16inst.ri.opcode == MIPS16e_extend_op) { 1330451b001bSSteven J. Hill pc16++; 1331451b001bSSteven J. Hill __get_user(mips16inst.full, pc16); 1332451b001bSSteven J. Hill } else if (delay_slot(regs)) { 1333451b001bSSteven J. Hill /* skip jump instructions */ 1334451b001bSSteven J. Hill /* JAL/JALX are 32 bits but have OPCODE in first short int */ 1335451b001bSSteven J. Hill if (mips16inst.ri.opcode == MIPS16e_jal_op) 1336451b001bSSteven J. Hill pc16++; 1337451b001bSSteven J. Hill pc16++; 1338451b001bSSteven J. Hill if (get_user(mips16inst.full, pc16)) 1339451b001bSSteven J. Hill goto sigbus; 1340451b001bSSteven J. Hill } 1341451b001bSSteven J. Hill 1342451b001bSSteven J. Hill switch (mips16inst.ri.opcode) { 1343451b001bSSteven J. Hill case MIPS16e_i64_op: /* I64 or RI64 instruction */ 1344451b001bSSteven J. Hill switch (mips16inst.i64.func) { /* I64/RI64 func field check */ 1345451b001bSSteven J. Hill case MIPS16e_ldpc_func: 1346451b001bSSteven J. Hill case MIPS16e_ldsp_func: 1347451b001bSSteven J. Hill reg = reg16to32[mips16inst.ri64.ry]; 1348451b001bSSteven J. Hill goto loadDW; 1349451b001bSSteven J. Hill 1350451b001bSSteven J. Hill case MIPS16e_sdsp_func: 1351451b001bSSteven J. Hill reg = reg16to32[mips16inst.ri64.ry]; 1352451b001bSSteven J. Hill goto writeDW; 1353451b001bSSteven J. Hill 1354451b001bSSteven J. Hill case MIPS16e_sdrasp_func: 1355451b001bSSteven J. Hill reg = 29; /* GPRSP */ 1356451b001bSSteven J. Hill goto writeDW; 1357451b001bSSteven J. Hill } 1358451b001bSSteven J. Hill 1359451b001bSSteven J. Hill goto sigbus; 1360451b001bSSteven J. Hill 1361451b001bSSteven J. Hill case MIPS16e_swsp_op: 1362451b001bSSteven J. Hill case MIPS16e_lwpc_op: 1363451b001bSSteven J. Hill case MIPS16e_lwsp_op: 1364451b001bSSteven J. Hill reg = reg16to32[mips16inst.ri.rx]; 1365451b001bSSteven J. Hill break; 1366451b001bSSteven J. Hill 1367451b001bSSteven J. Hill case MIPS16e_i8_op: 1368451b001bSSteven J. Hill if (mips16inst.i8.func != MIPS16e_swrasp_func) 1369451b001bSSteven J. Hill goto sigbus; 1370451b001bSSteven J. Hill reg = 29; /* GPRSP */ 1371451b001bSSteven J. Hill break; 1372451b001bSSteven J. Hill 1373451b001bSSteven J. Hill default: 1374451b001bSSteven J. Hill reg = reg16to32[mips16inst.rri.ry]; 1375451b001bSSteven J. Hill break; 1376451b001bSSteven J. Hill } 1377451b001bSSteven J. Hill 1378451b001bSSteven J. Hill switch (mips16inst.ri.opcode) { 1379451b001bSSteven J. Hill 1380451b001bSSteven J. Hill case MIPS16e_lb_op: 1381451b001bSSteven J. Hill case MIPS16e_lbu_op: 1382451b001bSSteven J. Hill case MIPS16e_sb_op: 1383451b001bSSteven J. Hill goto sigbus; 1384451b001bSSteven J. Hill 1385451b001bSSteven J. Hill case MIPS16e_lh_op: 1386451b001bSSteven J. Hill if (!access_ok(VERIFY_READ, addr, 2)) 1387451b001bSSteven J. Hill goto sigbus; 1388451b001bSSteven J. Hill 1389451b001bSSteven J. Hill LoadHW(addr, value, res); 1390451b001bSSteven J. Hill if (res) 1391451b001bSSteven J. Hill goto fault; 1392451b001bSSteven J. Hill MIPS16e_compute_return_epc(regs, &oldinst); 1393451b001bSSteven J. Hill regs->regs[reg] = value; 1394451b001bSSteven J. Hill break; 1395451b001bSSteven J. Hill 1396451b001bSSteven J. Hill case MIPS16e_lhu_op: 1397451b001bSSteven J. Hill if (!access_ok(VERIFY_READ, addr, 2)) 1398451b001bSSteven J. Hill goto sigbus; 1399451b001bSSteven J. Hill 1400451b001bSSteven J. Hill LoadHWU(addr, value, res); 1401451b001bSSteven J. Hill if (res) 1402451b001bSSteven J. Hill goto fault; 1403451b001bSSteven J. Hill MIPS16e_compute_return_epc(regs, &oldinst); 1404451b001bSSteven J. Hill regs->regs[reg] = value; 1405451b001bSSteven J. Hill break; 1406451b001bSSteven J. Hill 1407451b001bSSteven J. Hill case MIPS16e_lw_op: 1408451b001bSSteven J. Hill case MIPS16e_lwpc_op: 1409451b001bSSteven J. Hill case MIPS16e_lwsp_op: 1410451b001bSSteven J. Hill if (!access_ok(VERIFY_READ, addr, 4)) 1411451b001bSSteven J. Hill goto sigbus; 1412451b001bSSteven J. Hill 1413451b001bSSteven J. Hill LoadW(addr, value, res); 1414451b001bSSteven J. Hill if (res) 1415451b001bSSteven J. Hill goto fault; 1416451b001bSSteven J. Hill MIPS16e_compute_return_epc(regs, &oldinst); 1417451b001bSSteven J. Hill regs->regs[reg] = value; 1418451b001bSSteven J. Hill break; 1419451b001bSSteven J. Hill 1420451b001bSSteven J. Hill case MIPS16e_lwu_op: 1421451b001bSSteven J. Hill #ifdef CONFIG_64BIT 1422451b001bSSteven J. Hill /* 1423451b001bSSteven J. Hill * A 32-bit kernel might be running on a 64-bit processor. But 1424451b001bSSteven J. Hill * if we're on a 32-bit processor and an i-cache incoherency 1425451b001bSSteven J. Hill * or race makes us see a 64-bit instruction here the sdl/sdr 1426451b001bSSteven J. Hill * would blow up, so for now we don't handle unaligned 64-bit 1427451b001bSSteven J. Hill * instructions on 32-bit kernels. 1428451b001bSSteven J. Hill */ 1429451b001bSSteven J. Hill if (!access_ok(VERIFY_READ, addr, 4)) 1430451b001bSSteven J. Hill goto sigbus; 1431451b001bSSteven J. Hill 1432451b001bSSteven J. Hill LoadWU(addr, value, res); 1433451b001bSSteven J. Hill if (res) 1434451b001bSSteven J. Hill goto fault; 1435451b001bSSteven J. Hill MIPS16e_compute_return_epc(regs, &oldinst); 1436451b001bSSteven J. Hill regs->regs[reg] = value; 1437451b001bSSteven J. Hill break; 1438451b001bSSteven J. Hill #endif /* CONFIG_64BIT */ 1439451b001bSSteven J. Hill 1440451b001bSSteven J. Hill /* Cannot handle 64-bit instructions in 32-bit kernel */ 1441451b001bSSteven J. Hill goto sigill; 1442451b001bSSteven J. Hill 1443451b001bSSteven J. Hill case MIPS16e_ld_op: 1444451b001bSSteven J. Hill loadDW: 1445451b001bSSteven J. Hill #ifdef CONFIG_64BIT 1446451b001bSSteven J. Hill /* 1447451b001bSSteven J. Hill * A 32-bit kernel might be running on a 64-bit processor. But 1448451b001bSSteven J. Hill * if we're on a 32-bit processor and an i-cache incoherency 1449451b001bSSteven J. Hill * or race makes us see a 64-bit instruction here the sdl/sdr 1450451b001bSSteven J. Hill * would blow up, so for now we don't handle unaligned 64-bit 1451451b001bSSteven J. Hill * instructions on 32-bit kernels. 1452451b001bSSteven J. Hill */ 1453451b001bSSteven J. Hill if (!access_ok(VERIFY_READ, addr, 8)) 1454451b001bSSteven J. Hill goto sigbus; 1455451b001bSSteven J. Hill 1456451b001bSSteven J. Hill LoadDW(addr, value, res); 1457451b001bSSteven J. Hill if (res) 1458451b001bSSteven J. Hill goto fault; 1459451b001bSSteven J. Hill MIPS16e_compute_return_epc(regs, &oldinst); 1460451b001bSSteven J. Hill regs->regs[reg] = value; 1461451b001bSSteven J. Hill break; 1462451b001bSSteven J. Hill #endif /* CONFIG_64BIT */ 1463451b001bSSteven J. Hill 1464451b001bSSteven J. Hill /* Cannot handle 64-bit instructions in 32-bit kernel */ 1465451b001bSSteven J. Hill goto sigill; 1466451b001bSSteven J. Hill 1467451b001bSSteven J. Hill case MIPS16e_sh_op: 1468451b001bSSteven J. Hill if (!access_ok(VERIFY_WRITE, addr, 2)) 1469451b001bSSteven J. Hill goto sigbus; 1470451b001bSSteven J. Hill 1471451b001bSSteven J. Hill MIPS16e_compute_return_epc(regs, &oldinst); 1472451b001bSSteven J. Hill value = regs->regs[reg]; 1473451b001bSSteven J. Hill StoreHW(addr, value, res); 1474451b001bSSteven J. Hill if (res) 1475451b001bSSteven J. Hill goto fault; 1476451b001bSSteven J. Hill break; 1477451b001bSSteven J. Hill 1478451b001bSSteven J. Hill case MIPS16e_sw_op: 1479451b001bSSteven J. Hill case MIPS16e_swsp_op: 1480451b001bSSteven J. Hill case MIPS16e_i8_op: /* actually - MIPS16e_swrasp_func */ 1481451b001bSSteven J. Hill if (!access_ok(VERIFY_WRITE, addr, 4)) 1482451b001bSSteven J. Hill goto sigbus; 1483451b001bSSteven J. Hill 1484451b001bSSteven J. Hill MIPS16e_compute_return_epc(regs, &oldinst); 1485451b001bSSteven J. Hill value = regs->regs[reg]; 1486451b001bSSteven J. Hill StoreW(addr, value, res); 1487451b001bSSteven J. Hill if (res) 1488451b001bSSteven J. Hill goto fault; 1489451b001bSSteven J. Hill break; 1490451b001bSSteven J. Hill 1491451b001bSSteven J. Hill case MIPS16e_sd_op: 1492451b001bSSteven J. Hill writeDW: 1493451b001bSSteven J. Hill #ifdef CONFIG_64BIT 1494451b001bSSteven J. Hill /* 1495451b001bSSteven J. Hill * A 32-bit kernel might be running on a 64-bit processor. But 1496451b001bSSteven J. Hill * if we're on a 32-bit processor and an i-cache incoherency 1497451b001bSSteven J. Hill * or race makes us see a 64-bit instruction here the sdl/sdr 1498451b001bSSteven J. Hill * would blow up, so for now we don't handle unaligned 64-bit 1499451b001bSSteven J. Hill * instructions on 32-bit kernels. 1500451b001bSSteven J. Hill */ 1501451b001bSSteven J. Hill if (!access_ok(VERIFY_WRITE, addr, 8)) 1502451b001bSSteven J. Hill goto sigbus; 1503451b001bSSteven J. Hill 1504451b001bSSteven J. Hill MIPS16e_compute_return_epc(regs, &oldinst); 1505451b001bSSteven J. Hill value = regs->regs[reg]; 1506451b001bSSteven J. Hill StoreDW(addr, value, res); 1507451b001bSSteven J. Hill if (res) 1508451b001bSSteven J. Hill goto fault; 1509451b001bSSteven J. Hill break; 1510451b001bSSteven J. Hill #endif /* CONFIG_64BIT */ 1511451b001bSSteven J. Hill 1512451b001bSSteven J. Hill /* Cannot handle 64-bit instructions in 32-bit kernel */ 1513451b001bSSteven J. Hill goto sigill; 1514451b001bSSteven J. Hill 1515451b001bSSteven J. Hill default: 1516451b001bSSteven J. Hill /* 1517451b001bSSteven J. Hill * Pheeee... We encountered an yet unknown instruction or 1518451b001bSSteven J. Hill * cache coherence problem. Die sucker, die ... 1519451b001bSSteven J. Hill */ 1520451b001bSSteven J. Hill goto sigill; 1521451b001bSSteven J. Hill } 1522451b001bSSteven J. Hill 1523451b001bSSteven J. Hill #ifdef CONFIG_DEBUG_FS 1524451b001bSSteven J. Hill unaligned_instructions++; 1525451b001bSSteven J. Hill #endif 1526451b001bSSteven J. Hill 1527451b001bSSteven J. Hill return; 1528451b001bSSteven J. Hill 1529451b001bSSteven J. Hill fault: 1530451b001bSSteven J. Hill /* roll back jump/branch */ 1531451b001bSSteven J. Hill regs->cp0_epc = origpc; 1532451b001bSSteven J. Hill regs->regs[31] = orig31; 1533451b001bSSteven J. Hill /* Did we have an exception handler installed? */ 1534451b001bSSteven J. Hill if (fixup_exception(regs)) 1535451b001bSSteven J. Hill return; 1536451b001bSSteven J. Hill 1537451b001bSSteven J. Hill die_if_kernel("Unhandled kernel unaligned access", regs); 1538451b001bSSteven J. Hill force_sig(SIGSEGV, current); 1539451b001bSSteven J. Hill 1540451b001bSSteven J. Hill return; 1541451b001bSSteven J. Hill 1542451b001bSSteven J. Hill sigbus: 1543451b001bSSteven J. Hill die_if_kernel("Unhandled kernel unaligned access", regs); 1544451b001bSSteven J. Hill force_sig(SIGBUS, current); 1545451b001bSSteven J. Hill 1546451b001bSSteven J. Hill return; 1547451b001bSSteven J. Hill 1548451b001bSSteven J. Hill sigill: 1549451b001bSSteven J. Hill die_if_kernel 1550451b001bSSteven J. Hill ("Unhandled kernel unaligned access or invalid instruction", regs); 1551451b001bSSteven J. Hill force_sig(SIGILL, current); 1552451b001bSSteven J. Hill } 1553fc192e50STony Wu 15541da177e4SLinus Torvalds asmlinkage void do_ade(struct pt_regs *regs) 15551da177e4SLinus Torvalds { 1556c3fc5cd5SRalf Baechle enum ctx_state prev_state; 1557fe00f943SRalf Baechle unsigned int __user *pc; 15581da177e4SLinus Torvalds mm_segment_t seg; 15591da177e4SLinus Torvalds 1560c3fc5cd5SRalf Baechle prev_state = exception_enter(); 15617f788d2dSDeng-Cheng Zhu perf_sw_event(PERF_COUNT_SW_ALIGNMENT_FAULTS, 1562a8b0ca17SPeter Zijlstra 1, regs, regs->cp0_badvaddr); 15631da177e4SLinus Torvalds /* 15641da177e4SLinus Torvalds * Did we catch a fault trying to load an instruction? 15651da177e4SLinus Torvalds */ 156634c2f668SLeonid Yegoshin if (regs->cp0_badvaddr == regs->cp0_epc) 15671da177e4SLinus Torvalds goto sigbus; 15681da177e4SLinus Torvalds 1569293c5bd1SRalf Baechle if (user_mode(regs) && !test_thread_flag(TIF_FIXADE)) 15701da177e4SLinus Torvalds goto sigbus; 15716312e0eeSAtsushi Nemoto if (unaligned_action == UNALIGNED_ACTION_SIGNAL) 15726312e0eeSAtsushi Nemoto goto sigbus; 15731da177e4SLinus Torvalds 15741da177e4SLinus Torvalds /* 15751da177e4SLinus Torvalds * Do branch emulation only if we didn't forward the exception. 15761da177e4SLinus Torvalds * This is all so but ugly ... 15771da177e4SLinus Torvalds */ 157834c2f668SLeonid Yegoshin 157934c2f668SLeonid Yegoshin /* 158034c2f668SLeonid Yegoshin * Are we running in microMIPS mode? 158134c2f668SLeonid Yegoshin */ 158234c2f668SLeonid Yegoshin if (get_isa16_mode(regs->cp0_epc)) { 158334c2f668SLeonid Yegoshin /* 158434c2f668SLeonid Yegoshin * Did we catch a fault trying to load an instruction in 158534c2f668SLeonid Yegoshin * 16-bit mode? 158634c2f668SLeonid Yegoshin */ 158734c2f668SLeonid Yegoshin if (regs->cp0_badvaddr == msk_isa16_mode(regs->cp0_epc)) 158834c2f668SLeonid Yegoshin goto sigbus; 158934c2f668SLeonid Yegoshin if (unaligned_action == UNALIGNED_ACTION_SHOW) 159034c2f668SLeonid Yegoshin show_registers(regs); 159134c2f668SLeonid Yegoshin 159234c2f668SLeonid Yegoshin if (cpu_has_mmips) { 159334c2f668SLeonid Yegoshin seg = get_fs(); 159434c2f668SLeonid Yegoshin if (!user_mode(regs)) 159534c2f668SLeonid Yegoshin set_fs(KERNEL_DS); 159634c2f668SLeonid Yegoshin emulate_load_store_microMIPS(regs, 159734c2f668SLeonid Yegoshin (void __user *)regs->cp0_badvaddr); 159834c2f668SLeonid Yegoshin set_fs(seg); 159934c2f668SLeonid Yegoshin 160034c2f668SLeonid Yegoshin return; 160134c2f668SLeonid Yegoshin } 160234c2f668SLeonid Yegoshin 1603451b001bSSteven J. Hill if (cpu_has_mips16) { 1604451b001bSSteven J. Hill seg = get_fs(); 1605451b001bSSteven J. Hill if (!user_mode(regs)) 1606451b001bSSteven J. Hill set_fs(KERNEL_DS); 1607451b001bSSteven J. Hill emulate_load_store_MIPS16e(regs, 1608451b001bSSteven J. Hill (void __user *)regs->cp0_badvaddr); 1609451b001bSSteven J. Hill set_fs(seg); 1610451b001bSSteven J. Hill 1611451b001bSSteven J. Hill return; 1612451b001bSSteven J. Hill } 1613451b001bSSteven J. Hill 161434c2f668SLeonid Yegoshin goto sigbus; 161534c2f668SLeonid Yegoshin } 161634c2f668SLeonid Yegoshin 161734c2f668SLeonid Yegoshin if (unaligned_action == UNALIGNED_ACTION_SHOW) 161834c2f668SLeonid Yegoshin show_registers(regs); 161934c2f668SLeonid Yegoshin pc = (unsigned int __user *)exception_epc(regs); 162034c2f668SLeonid Yegoshin 16211da177e4SLinus Torvalds seg = get_fs(); 16221da177e4SLinus Torvalds if (!user_mode(regs)) 16231da177e4SLinus Torvalds set_fs(KERNEL_DS); 16247f18f151SRalf Baechle emulate_load_store_insn(regs, (void __user *)regs->cp0_badvaddr, pc); 16251da177e4SLinus Torvalds set_fs(seg); 16261da177e4SLinus Torvalds 16271da177e4SLinus Torvalds return; 16281da177e4SLinus Torvalds 16291da177e4SLinus Torvalds sigbus: 16301da177e4SLinus Torvalds die_if_kernel("Kernel unaligned instruction access", regs); 16311da177e4SLinus Torvalds force_sig(SIGBUS, current); 16321da177e4SLinus Torvalds 16331da177e4SLinus Torvalds /* 16341da177e4SLinus Torvalds * XXX On return from the signal handler we should advance the epc 16351da177e4SLinus Torvalds */ 1636c3fc5cd5SRalf Baechle exception_exit(prev_state); 16371da177e4SLinus Torvalds } 16386312e0eeSAtsushi Nemoto 16396312e0eeSAtsushi Nemoto #ifdef CONFIG_DEBUG_FS 16406312e0eeSAtsushi Nemoto extern struct dentry *mips_debugfs_dir; 16416312e0eeSAtsushi Nemoto static int __init debugfs_unaligned(void) 16426312e0eeSAtsushi Nemoto { 16436312e0eeSAtsushi Nemoto struct dentry *d; 16446312e0eeSAtsushi Nemoto 16456312e0eeSAtsushi Nemoto if (!mips_debugfs_dir) 16466312e0eeSAtsushi Nemoto return -ENODEV; 16476312e0eeSAtsushi Nemoto d = debugfs_create_u32("unaligned_instructions", S_IRUGO, 16486312e0eeSAtsushi Nemoto mips_debugfs_dir, &unaligned_instructions); 1649b517531cSZhaolei if (!d) 1650b517531cSZhaolei return -ENOMEM; 16516312e0eeSAtsushi Nemoto d = debugfs_create_u32("unaligned_action", S_IRUGO | S_IWUSR, 16526312e0eeSAtsushi Nemoto mips_debugfs_dir, &unaligned_action); 1653b517531cSZhaolei if (!d) 1654b517531cSZhaolei return -ENOMEM; 16556312e0eeSAtsushi Nemoto return 0; 16566312e0eeSAtsushi Nemoto } 16576312e0eeSAtsushi Nemoto __initcall(debugfs_unaligned); 16586312e0eeSAtsushi Nemoto #endif 1659