From 1599ab11e3cf182e285b6d9204718a925d8ce13a Mon Sep 17 00:00:00 2001
From: Dale Rahn <drahn@cvs.openbsd.org>
Date: Sun, 10 Jun 2001 18:45:03 +0000
Subject: Hack/workaround for gcc bug. GCC will generate a load/store double
 instruction to do data copies, however it will not correctly identify that a
 load/store double will not correctly copy data where the address is
 misaligned. This emulates the load/store double operations in the kernel.
 Fixes gtk+ runtime problems. From NetBSD.

---
 sys/arch/powerpc/include/trap.h    | 19 ++++++++++-
 sys/arch/powerpc/powerpc/locore.S  | 28 ++++++++++++++--
 sys/arch/powerpc/powerpc/machdep.c |  6 +++-
 sys/arch/powerpc/powerpc/trap.c    | 67 ++++++++++++++++++++++++++++++++++++--
 4 files changed, 113 insertions(+), 7 deletions(-)

(limited to 'sys/arch')

diff --git a/sys/arch/powerpc/include/trap.h b/sys/arch/powerpc/include/trap.h
index 5f858988c9e..9dd4ba98335 100644
--- a/sys/arch/powerpc/include/trap.h
+++ b/sys/arch/powerpc/include/trap.h
@@ -1,4 +1,4 @@
-/*	$OpenBSD: trap.h,v 1.2 2001/03/29 18:52:19 drahn Exp $	*/
+/*	$OpenBSD: trap.h,v 1.3 2001/06/10 18:45:02 drahn Exp $	*/
 /*	$NetBSD: trap.h,v 1.1 1996/09/30 16:34:35 ws Exp $	*/
 
 /*
@@ -65,4 +65,21 @@
 /* Trap was in user mode */
 #define	EXC_USER	0x10000
 
+/*
+ * EXC_ALI sets bits in the DSISR and DAR to provide enough
+ * information to recover from the unaligned access without needing to
+ * parse the offending instruction. This includes certain bits of the
+ * opcode, and information about what registers are used. The opcode
+ * indicator values below come from Appendix F of Book III of "The
+ * PowerPC Architecture".
+ */
+
+#define EXC_ALI_OPCODE_INDICATOR(dsisr) ((dsisr >> 10) & 0x7f)
+#define EXC_ALI_LFD	0x09
+#define EXC_ALI_STFD	0x0b
+
+/* Macros to extract register information */
+#define EXC_ALI_RST(dsisr) ((dsisr >> 5) & 0x1f)   /* source or target */
+#define EXC_ALI_RA(dsisr) (dsisr & 0x1f)
+
 #endif	/* _MACHINE_TRAP_H_ */
diff --git a/sys/arch/powerpc/powerpc/locore.S b/sys/arch/powerpc/powerpc/locore.S
index 76d6f5a1daa..e34731b35a6 100644
--- a/sys/arch/powerpc/powerpc/locore.S
+++ b/sys/arch/powerpc/powerpc/locore.S
@@ -1,4 +1,4 @@
-/*	$OpenBSD: locore.S,v 1.15 2001/04/08 05:00:27 drahn Exp $	*/
+/*	$OpenBSD: locore.S,v 1.16 2001/06/10 18:45:02 drahn Exp $	*/
 /*	$NetBSD: locore.S,v 1.2 1996/10/16 19:33:09 ws Exp $	*/
 
 /*
@@ -369,7 +369,8 @@ _C_LABEL(intr_depth):
 
 /*
  * This code gets copied to all the trap vectors
- * (except ISI/DSI, the interrupts, and possibly the debugging traps when using IPKDB).
+ * (except ISI/DSI, ALI, the interrupts, and possibly the debugging traps
+ * when using IPKDB).
  */
 	.text
 	.globl	_C_LABEL(trapcode),_C_LABEL(trapsize)
@@ -391,6 +392,29 @@ _C_LABEL(trapcode):
 	bla	s_trap
 _C_LABEL(trapsize) =	.-_C_LABEL(trapcode)
 
+/*
+ * For ALI: has to save DSISR and DAR
+ */
+	.globl  _C_LABEL(alitrap),_C_LABEL(alisize)
+_C_LABEL(alitrap):
+	mtsprg  1,1                     /* save SP */
+	stmw    28,tempsave(0)          /* free r28-r31 */
+	mfdar   30
+	mfdsisr 31
+	stmw    30,tempsave+16(0)
+	mflr    28                      /* save LR */
+	mfcr    29                      /* save CR */
+/* Test whether we already had PR set */
+	mfsrr1  31
+	mtcr    31
+	bc      4,17,1f                 /* branch if PSL_PR is clear */
+	lis     1,_C_LABEL(curpcb)@ha
+	lwz     1,_C_LABEL(curpcb)@l(1)
+	addi    1,1,USPACE              /* stack is top of user struct */
+1:
+	bla     s_trap
+_C_LABEL(alisize) = .-_C_LABEL(alitrap)
+
 /*
  * Similar to the above for DSI
  * Has to handle BAT spills
diff --git a/sys/arch/powerpc/powerpc/machdep.c b/sys/arch/powerpc/powerpc/machdep.c
index 142a74f0063..3cbcd30faa7 100644
--- a/sys/arch/powerpc/powerpc/machdep.c
+++ b/sys/arch/powerpc/powerpc/machdep.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: machdep.c,v 1.62 2001/06/08 08:09:22 art Exp $	*/
+/*	$OpenBSD: machdep.c,v 1.63 2001/06/10 18:45:02 drahn Exp $	*/
 /*	$NetBSD: machdep.c,v 1.4 1996/10/16 19:33:11 ws Exp $	*/
 
 /*
@@ -154,6 +154,7 @@ initppc(startkernel, endkernel, args)
 	extern trapcode, trapsize;
 	extern dsitrap, dsisize;
 	extern isitrap, isisize;
+	extern alitrap, alisize;
 	extern decrint, decrsize;
 	extern tlbimiss, tlbimsize;
 	extern tlbdlmiss, tlbdlmsize;
@@ -261,6 +262,9 @@ where = 3;
 		case EXC_ISI:
 			bcopy(&isitrap, (void *)EXC_ISI, (size_t)&isisize);
 			break;
+		case EXC_ALI:
+			bcopy(&alitrap, (void *)EXC_ALI, (size_t)&alisize);
+			break;
 #endif
 		case EXC_DECR:
 			bcopy(&decrint, (void *)EXC_DECR, (size_t)&decrsize);
diff --git a/sys/arch/powerpc/powerpc/trap.c b/sys/arch/powerpc/powerpc/trap.c
index 4c511e37549..2447d255a9b 100644
--- a/sys/arch/powerpc/powerpc/trap.c
+++ b/sys/arch/powerpc/powerpc/trap.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: trap.c,v 1.24 2001/03/30 05:13:46 drahn Exp $	*/
+/*	$OpenBSD: trap.c,v 1.25 2001/06/10 18:45:02 drahn Exp $	*/
 /*	$NetBSD: trap.c,v 1.3 1996/10/13 03:31:37 christos Exp $	*/
 
 /*
@@ -55,6 +55,8 @@
 #include <machine/trap.h>
 #include <machine/db_machdep.h>
 
+static int fix_unaligned __P((struct proc *p, struct trapframe *frame));
+
 /* These definitions should probably be somewhere else				XXX */
 #define	FIRSTARG	3		/* first argument is in reg 3 */
 #define	NARGREG		8		/* 8 args are in registers */
@@ -338,8 +340,18 @@ syscall_bad:
 		break;
 
 	case EXC_ALI|EXC_USER:
-		/* alignment exception, kill process */
-		trapsignal(p, SIGSEGV, VM_PROT_EXECUTE, SEGV_MAPERR, sv);
+		/* alignment exception 
+		 * we check to see if this can be fixed up
+		 * by the code that fixes the typical gcc misaligned code
+		 * then kill the process if not.
+		 */
+		if (fix_unaligned(p, frame) == 0) {
+			frame->srr0 += 4;
+		} else {
+			sv.sival_int = frame->srr0;
+			trapsignal(p, SIGSEGV, VM_PROT_EXECUTE, SEGV_MAPERR,
+				sv);
+		}
 		break;
 
 	default:
@@ -589,3 +601,52 @@ copyout(kaddr, udaddr, len)
 	curpcb->pcb_onfault = 0;
 	return 0;
 }
+
+/*
+ * For now, this only deals with the particular unaligned access case
+ * that gcc tends to generate.  Eventually it should handle all of the
+ * possibilities that can happen on a 32-bit PowerPC in big-endian mode.
+ */
+
+static int
+fix_unaligned(p, frame)
+	struct proc *p;
+	struct trapframe *frame;
+{
+	int indicator = EXC_ALI_OPCODE_INDICATOR(frame->dsisr);
+
+	switch (indicator) {
+	case EXC_ALI_LFD:
+	case EXC_ALI_STFD:
+		{
+			int reg = EXC_ALI_RST(frame->dsisr);
+			double *fpr = &p->p_addr->u_pcb.pcb_fpu.fpr[reg];
+
+			/* Juggle the FPU to ensure that we've initialized
+			 * the FPRs, and that their current state is in
+			 * the PCB.
+			 */
+			if (fpuproc != p) {
+				if (fpuproc)
+					save_fpu(fpuproc);
+				enable_fpu(p);
+			}
+			save_fpu(p);
+
+			if (indicator == EXC_ALI_LFD) {
+				if (copyin((void *)frame->dar, fpr,
+				    sizeof(double)) != 0)
+					return -1;
+				enable_fpu(p);
+			} else {
+				if (copyout(fpr, (void *)frame->dar,
+				    sizeof(double)) != 0)
+					return -1;
+			}
+			return 0;
+		}
+		break;
+	}
+
+	return -1;
+}
-- 
cgit v1.2.3