diff options
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 c369a5d3552..2b3517214d6 100644 --- a/arch/mips/kernel/unaligned.c +++ b/arch/mips/kernel/unaligned.c @@ -7,6 +7,7 @@   *   * Copyright (C) 1996, 1998, 1999, 2002 by Ralf Baechle   * Copyright (C) 1999 Silicon Graphics, Inc. + * Copyright (C) 2014 Imagination Technologies Ltd.   *   * This file contains exception handler for address error exception with the   * special capability to execute faulting instructions in software.  The @@ -110,8 +111,8 @@ extern void show_registers(struct pt_regs *regs);  #ifdef __BIG_ENDIAN  #define     LoadHW(addr, value, res)  \  		__asm__ __volatile__ (".set\tnoat\n"        \ -			"1:\tlb\t%0, 0(%2)\n"               \ -			"2:\tlbu\t$1, 1(%2)\n\t"            \ +			"1:\t"user_lb("%0", "0(%2)")"\n"    \ +			"2:\t"user_lbu("$1", "1(%2)")"\n\t" \  			"sll\t%0, 0x8\n\t"                  \  			"or\t%0, $1\n\t"                    \  			"li\t%1, 0\n"                       \ @@ -130,8 +131,8 @@ extern void show_registers(struct pt_regs *regs);  #define     LoadW(addr, value, res)   \  		__asm__ __volatile__ (                      \ -			"1:\tlwl\t%0, (%2)\n"               \ -			"2:\tlwr\t%0, 3(%2)\n\t"            \ +			"1:\t"user_lwl("%0", "(%2)")"\n"    \ +			"2:\t"user_lwr("%0", "3(%2)")"\n\t" \  			"li\t%1, 0\n"                       \  			"3:\n\t"                            \  			".insn\n\t"                         \ @@ -149,8 +150,8 @@ extern void show_registers(struct pt_regs *regs);  #define     LoadHWU(addr, value, res) \  		__asm__ __volatile__ (                      \  			".set\tnoat\n"                      \ -			"1:\tlbu\t%0, 0(%2)\n"              \ -			"2:\tlbu\t$1, 1(%2)\n\t"            \ +			"1:\t"user_lbu("%0", "0(%2)")"\n"   \ +			"2:\t"user_lbu("$1", "1(%2)")"\n\t" \  			"sll\t%0, 0x8\n\t"                  \  			"or\t%0, $1\n\t"                    \  			"li\t%1, 0\n"                       \ @@ -170,8 +171,8 @@ extern void show_registers(struct pt_regs *regs);  #define     LoadWU(addr, value, res)  \  		__asm__ __volatile__ (                      \ -			"1:\tlwl\t%0, (%2)\n"               \ -			"2:\tlwr\t%0, 3(%2)\n\t"            \ +			"1:\t"user_lwl("%0", "(%2)")"\n"    \ +			"2:\t"user_lwr("%0", "3(%2)")"\n\t" \  			"dsll\t%0, %0, 32\n\t"              \  			"dsrl\t%0, %0, 32\n\t"              \  			"li\t%1, 0\n"                       \ @@ -209,9 +210,9 @@ extern void show_registers(struct pt_regs *regs);  #define     StoreHW(addr, value, res) \  		__asm__ __volatile__ (                      \  			".set\tnoat\n"                      \ -			"1:\tsb\t%1, 1(%2)\n\t"             \ +			"1:\t"user_sb("%1", "1(%2)")"\n"    \  			"srl\t$1, %1, 0x8\n"                \ -			"2:\tsb\t$1, 0(%2)\n\t"             \ +			"2:\t"user_sb("$1", "0(%2)")"\n"    \  			".set\tat\n\t"                      \  			"li\t%0, 0\n"                       \  			"3:\n\t"                            \ @@ -229,8 +230,8 @@ extern void show_registers(struct pt_regs *regs);  #define     StoreW(addr, value, res)  \  		__asm__ __volatile__ (                      \ -			"1:\tswl\t%1,(%2)\n"                \ -			"2:\tswr\t%1, 3(%2)\n\t"            \ +			"1:\t"user_swl("%1", "(%2)")"\n"    \ +			"2:\t"user_swr("%1", "3(%2)")"\n\t" \  			"li\t%0, 0\n"                       \  			"3:\n\t"                            \  			".insn\n\t"                         \ @@ -267,8 +268,8 @@ extern void show_registers(struct pt_regs *regs);  #ifdef __LITTLE_ENDIAN  #define     LoadHW(addr, value, res)  \  		__asm__ __volatile__ (".set\tnoat\n"        \ -			"1:\tlb\t%0, 1(%2)\n"               \ -			"2:\tlbu\t$1, 0(%2)\n\t"            \ +			"1:\t"user_lb("%0", "1(%2)")"\n"    \ +			"2:\t"user_lbu("$1", "0(%2)")"\n\t" \  			"sll\t%0, 0x8\n\t"                  \  			"or\t%0, $1\n\t"                    \  			"li\t%1, 0\n"                       \ @@ -287,8 +288,8 @@ extern void show_registers(struct pt_regs *regs);  #define     LoadW(addr, value, res)   \  		__asm__ __volatile__ (                      \ -			"1:\tlwl\t%0, 3(%2)\n"              \ -			"2:\tlwr\t%0, (%2)\n\t"             \ +			"1:\t"user_lwl("%0", "3(%2)")"\n"   \ +			"2:\t"user_lwr("%0", "(%2)")"\n\t"  \  			"li\t%1, 0\n"                       \  			"3:\n\t"                            \  			".insn\n\t"                         \ @@ -306,8 +307,8 @@ extern void show_registers(struct pt_regs *regs);  #define     LoadHWU(addr, value, res) \  		__asm__ __volatile__ (                      \  			".set\tnoat\n"                      \ -			"1:\tlbu\t%0, 1(%2)\n"              \ -			"2:\tlbu\t$1, 0(%2)\n\t"            \ +			"1:\t"user_lbu("%0", "1(%2)")"\n"   \ +			"2:\t"user_lbu("$1", "0(%2)")"\n\t" \  			"sll\t%0, 0x8\n\t"                  \  			"or\t%0, $1\n\t"                    \  			"li\t%1, 0\n"                       \ @@ -327,8 +328,8 @@ extern void show_registers(struct pt_regs *regs);  #define     LoadWU(addr, value, res)  \  		__asm__ __volatile__ (                      \ -			"1:\tlwl\t%0, 3(%2)\n"              \ -			"2:\tlwr\t%0, (%2)\n\t"             \ +			"1:\t"user_lwl("%0", "3(%2)")"\n"   \ +			"2:\t"user_lwr("%0", "(%2)")"\n\t"  \  			"dsll\t%0, %0, 32\n\t"              \  			"dsrl\t%0, %0, 32\n\t"              \  			"li\t%1, 0\n"                       \ @@ -366,9 +367,9 @@ extern void show_registers(struct pt_regs *regs);  #define     StoreHW(addr, value, res) \  		__asm__ __volatile__ (                      \  			".set\tnoat\n"                      \ -			"1:\tsb\t%1, 0(%2)\n\t"             \ +			"1:\t"user_sb("%1", "0(%2)")"\n"    \  			"srl\t$1,%1, 0x8\n"                 \ -			"2:\tsb\t$1, 1(%2)\n\t"             \ +			"2:\t"user_sb("$1", "1(%2)")"\n"    \  			".set\tat\n\t"                      \  			"li\t%0, 0\n"                       \  			"3:\n\t"                            \ @@ -386,8 +387,8 @@ extern void show_registers(struct pt_regs *regs);  #define     StoreW(addr, value, res)  \  		__asm__ __volatile__ (                      \ -			"1:\tswl\t%1, 3(%2)\n"              \ -			"2:\tswr\t%1, (%2)\n\t"             \ +			"1:\t"user_swl("%1", "3(%2)")"\n"   \ +			"2:\t"user_swr("%1", "(%2)")"\n\t"  \  			"li\t%0, 0\n"                       \  			"3:\n\t"                            \  			".insn\n\t"                         \ @@ -430,7 +431,9 @@ static void emulate_load_store_insn(struct pt_regs *regs,  	unsigned long origpc;  	unsigned long orig31;  	void __user *fault_addr = NULL; - +#ifdef	CONFIG_EVA +	mm_segment_t seg; +#endif  	origpc = (unsigned long)pc;  	orig31 = regs->regs[31]; @@ -475,6 +478,88 @@ static void emulate_load_store_insn(struct pt_regs *regs,  		 * The remaining opcodes are the ones that are really of  		 * interest.  		 */ +#ifdef CONFIG_EVA +	case spec3_op: +		/* +		 * we can land here only from kernel accessing user memory, +		 * so we need to "switch" the address limit to user space, so +		 * address check can work properly. +		 */ +		seg = get_fs(); +		set_fs(USER_DS); +		switch (insn.spec3_format.func) { +		case lhe_op: +			if (!access_ok(VERIFY_READ, addr, 2)) { +				set_fs(seg); +				goto sigbus; +			} +			LoadHW(addr, value, res); +			if (res) { +				set_fs(seg); +				goto fault; +			} +			compute_return_epc(regs); +			regs->regs[insn.spec3_format.rt] = value; +			break; +		case lwe_op: +			if (!access_ok(VERIFY_READ, addr, 4)) { +				set_fs(seg); +				goto sigbus; +			} +				LoadW(addr, value, res); +			if (res) { +				set_fs(seg); +				goto fault; +			} +			compute_return_epc(regs); +			regs->regs[insn.spec3_format.rt] = value; +			break; +		case lhue_op: +			if (!access_ok(VERIFY_READ, addr, 2)) { +				set_fs(seg); +				goto sigbus; +			} +			LoadHWU(addr, value, res); +			if (res) { +				set_fs(seg); +				goto fault; +			} +			compute_return_epc(regs); +			regs->regs[insn.spec3_format.rt] = value; +			break; +		case she_op: +			if (!access_ok(VERIFY_WRITE, addr, 2)) { +				set_fs(seg); +				goto sigbus; +			} +			compute_return_epc(regs); +			value = regs->regs[insn.spec3_format.rt]; +			StoreHW(addr, value, res); +			if (res) { +				set_fs(seg); +				goto fault; +			} +			break; +		case swe_op: +			if (!access_ok(VERIFY_WRITE, addr, 4)) { +				set_fs(seg); +				goto sigbus; +			} +			compute_return_epc(regs); +			value = regs->regs[insn.spec3_format.rt]; +			StoreW(addr, value, res); +			if (res) { +				set_fs(seg); +				goto fault; +			} +			break; +		default: +			set_fs(seg); +			goto sigill; +		} +		set_fs(seg); +		break; +#endif  	case lh_op:  		if (!access_ok(VERIFY_READ, addr, 2))  			goto sigbus;  | 
