diff options
author | Dale Rahn <drahn@cvs.openbsd.org> | 2001-06-10 18:45:03 +0000 |
---|---|---|
committer | Dale Rahn <drahn@cvs.openbsd.org> | 2001-06-10 18:45:03 +0000 |
commit | 1599ab11e3cf182e285b6d9204718a925d8ce13a (patch) | |
tree | 8d11b4b97e618f2ef6d97f22581716f149bf6328 /sys/arch | |
parent | 197b950a4f3efc82e91fbc951f8b23e19159a2a0 (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.h | 19 | ||||
-rw-r--r-- | sys/arch/powerpc/powerpc/locore.S | 28 | ||||
-rw-r--r-- | sys/arch/powerpc/powerpc/machdep.c | 6 | ||||
-rw-r--r-- | sys/arch/powerpc/powerpc/trap.c | 67 |
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; +} |