From ab1ce0a6cd51ca83194a865837f3b90f366a733d Mon Sep 17 00:00:00 2001
From: Lluis Batlle i Rossell <viric@viric.name>
Date: Sat, 16 Jun 2012 00:22:53 +0200
Subject: [PATCH] MIPS: Add emulation for fpureg-mem unaligned access
To: linux-mips@linux-mips.org
Cc: loongson-dev@googlegroups.com

Reusing most of the code from lw,ld,sw,sd emulation,
I add the emulation for lwc1,ldc1,swc1,sdc1.

This avoids the direct SIGBUS sent to userspace processes that have
misaligned memory accesses.

I've tested the change in Loongson2F, with an own test program, and
WebKit 1.4.0, as both were killed by sigbus without this patch.

Signed-off: Lluis Batlle i Rossell <viric@viric.name>
---
 arch/mips/kernel/unaligned.c |   43 +++++++++++++++++++++++++++++-------------
 1 file changed, 30 insertions(+), 13 deletions(-)

diff --git a/arch/mips/kernel/unaligned.c b/arch/mips/kernel/unaligned.c
index 9c58bdf..4531e6c 100644
--- a/arch/mips/kernel/unaligned.c
+++ b/arch/mips/kernel/unaligned.c
@@ -85,6 +85,7 @@
 #include <asm/cop2.h>
 #include <asm/inst.h>
 #include <asm/uaccess.h>
+#include <asm/fpu.h>
 
 #define STR(x)  __STR(x)
 #define __STR(x)  #x
@@ -108,6 +109,7 @@ static void emulate_load_store_insn(struct pt_regs *regs,
 	union mips_instruction insn;
 	unsigned long value;
 	unsigned int res;
+	fpureg_t *fpuregs;
 
 	perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS, 1, regs, 0);
 
@@ -183,6 +185,7 @@ static void emulate_load_store_insn(struct pt_regs *regs,
 		break;
 
 	case lw_op:
+	case lwc1_op:
 		if (!access_ok(VERIFY_READ, addr, 4))
 			goto sigbus;
 
@@ -209,7 +212,12 @@ static void emulate_load_store_insn(struct pt_regs *regs,
 		if (res)
 			goto fault;
 		compute_return_epc(regs);
-		regs->regs[insn.i_format.rt] = value;
+		if (insn.i_format.opcode == lw_op) {
+			regs->regs[insn.i_format.rt] = value;
+		} else {
+			fpuregs = get_fpu_regs(current);
+			fpuregs[insn.i_format.rt] = value;
+		}
 		break;
 
 	case lhu_op:
@@ -291,6 +299,7 @@ static void emulate_load_store_insn(struct pt_regs *regs,
 		goto sigill;
 
 	case ld_op:
+	case ldc1_op:
 #ifdef CONFIG_64BIT
 		/*
 		 * A 32-bit kernel might be running on a 64-bit processor.  But
@@ -325,7 +334,12 @@ static void emulate_load_store_insn(struct pt_regs *regs,
 		if (res)
 			goto fault;
 		compute_return_epc(regs);
-		regs->regs[insn.i_format.rt] = value;
+		if (insn.i_format.opcode == ld_op) {
+			regs->regs[insn.i_format.rt] = value;
+		} else {
+			fpuregs = get_fpu_regs(current);
+			fpuregs[insn.i_format.rt] = value;
+		}
 		break;
 #endif /* CONFIG_64BIT */
 
@@ -370,10 +384,16 @@ static void emulate_load_store_insn(struct pt_regs *regs,
 		break;
 
 	case sw_op:
+	case swc1_op:
 		if (!access_ok(VERIFY_WRITE, addr, 4))
 			goto sigbus;
 
-		value = regs->regs[insn.i_format.rt];
+		if (insn.i_format.opcode == sw_op) {
+			value = regs->regs[insn.i_format.rt];
+		} else {
+			fpuregs = get_fpu_regs(current);
+			value = fpuregs[insn.i_format.rt];
+		}
 		__asm__ __volatile__ (
 #ifdef __BIG_ENDIAN
 			"1:\tswl\t%1,(%2)\n"
@@ -401,6 +421,7 @@ static void emulate_load_store_insn(struct pt_regs *regs,
 		break;
 
 	case sd_op:
+	case sdc1_op:
 #ifdef CONFIG_64BIT
 		/*
 		 * A 32-bit kernel might be running on a 64-bit processor.  But
@@ -412,7 +433,12 @@ static void emulate_load_store_insn(struct pt_regs *regs,
 		if (!access_ok(VERIFY_WRITE, addr, 8))
 			goto sigbus;
 
-		value = regs->regs[insn.i_format.rt];
+		if (insn.i_format.opcode == sd_op) {
+			value = regs->regs[insn.i_format.rt];
+		} else {
+			fpuregs = get_fpu_regs(current);
+			value = fpuregs[insn.i_format.rt];
+		}
 		__asm__ __volatile__ (
 #ifdef __BIG_ENDIAN
 			"1:\tsdl\t%1,(%2)\n"
@@ -443,15 +469,6 @@ static void emulate_load_store_insn(struct pt_regs *regs,
 		/* Cannot handle 64-bit instructions in 32-bit kernel */
 		goto sigill;
 
-	case lwc1_op:
-	case ldc1_op:
-	case swc1_op:
-	case sdc1_op:
-		/*
-		 * I herewith declare: this does not happen.  So send SIGBUS.
-		 */
-		goto sigbus;
-
 	/*
 	 * COP2 is available to implementor for application specific use.
 	 * It's up to applications to register a notifier chain and do
-- 
1.7.9.5

