From 6c14ba0dbafacc1338d77d071f16b70f5f60c6b0 Mon Sep 17 00:00:00 2001 From: danw Date: Wed, 19 Jan 2000 03:30:12 +0000 Subject: [PATCH] Fill in the alignment trap handler a bit: now it can fix unaligned floating point loads and stores (to work around a gcc bug), but will still cause a bus error on other sorts of unaligned accesses. --- sys/arch/powerpc/include/trap.h | 20 ++++++++++- sys/arch/powerpc/powerpc/trap.c | 59 +++++++++++++++++++++++++++++++-- 2 files changed, 75 insertions(+), 4 deletions(-) diff --git a/sys/arch/powerpc/include/trap.h b/sys/arch/powerpc/include/trap.h index 3087a3ff90e0..5b0d4bf48924 100644 --- a/sys/arch/powerpc/include/trap.h +++ b/sys/arch/powerpc/include/trap.h @@ -1,4 +1,4 @@ -/* $NetBSD: trap.h,v 1.1 1996/09/30 16:34:35 ws Exp $ */ +/* $NetBSD: trap.h,v 1.2 2000/01/19 03:30:12 danw Exp $ */ /* * Copyright (C) 1995, 1996 Wolfgang Solfrank. @@ -64,4 +64,22 @@ /* 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/trap.c b/sys/arch/powerpc/powerpc/trap.c index 684a352eeea5..08d5e3a79045 100644 --- a/sys/arch/powerpc/powerpc/trap.c +++ b/sys/arch/powerpc/powerpc/trap.c @@ -1,4 +1,4 @@ -/* $NetBSD: trap.c,v 1.22 1999/05/03 10:02:20 tsubai Exp $ */ +/* $NetBSD: trap.c,v 1.23 2000/01/19 03:30:13 danw Exp $ */ /* * Copyright (C) 1995, 1996 Wolfgang Solfrank. @@ -62,6 +62,8 @@ volatile int astpending; volatile int want_resched; +static int fix_unaligned __P((struct proc *p, struct trapframe *frame)); + void trap(frame) struct trapframe *frame; @@ -262,8 +264,10 @@ syscall_bad: break; case EXC_ALI|EXC_USER: -/* XXX temporarily */ - trapsignal(p, SIGBUS, EXC_ALI); + if (fix_unaligned(p, frame) != 0) + trapsignal(p, SIGBUS, EXC_ALI); + else + frame->srr0 += 4; break; case EXC_PGM|EXC_USER: @@ -531,3 +535,52 @@ badaddr_read(addr, size, rptr) 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; +}