summaryrefslogtreecommitdiff
path: root/sys/arch
diff options
context:
space:
mode:
authorDale Rahn <drahn@cvs.openbsd.org>2001-06-10 18:45:03 +0000
committerDale Rahn <drahn@cvs.openbsd.org>2001-06-10 18:45:03 +0000
commit1599ab11e3cf182e285b6d9204718a925d8ce13a (patch)
tree8d11b4b97e618f2ef6d97f22581716f149bf6328 /sys/arch
parent197b950a4f3efc82e91fbc951f8b23e19159a2a0 (diff)
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.
Diffstat (limited to 'sys/arch')
-rw-r--r--sys/arch/powerpc/include/trap.h19
-rw-r--r--sys/arch/powerpc/powerpc/locore.S28
-rw-r--r--sys/arch/powerpc/powerpc/machdep.c6
-rw-r--r--sys/arch/powerpc/powerpc/trap.c67
4 files changed, 113 insertions, 7 deletions
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)
@@ -392,6 +393,29 @@ _C_LABEL(trapcode):
_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
* and standard pagetable 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;
+}