summaryrefslogtreecommitdiffstats
path: root/arch/sparc/kernel/unaligned.c
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>2000-03-23 02:25:38 +0000
committerRalf Baechle <ralf@linux-mips.org>2000-03-23 02:25:38 +0000
commit16b5d462f73eb29d1f67fa01cc1ea66afdc72569 (patch)
tree5407bd573f4840e473ea27cbe61e5c7a07131fcd /arch/sparc/kernel/unaligned.c
parentce8a076e11e7e5ee36007f9a3eee5bb3744cb8f6 (diff)
Merge with Linux 2.3.99-pre2.
Diffstat (limited to 'arch/sparc/kernel/unaligned.c')
-rw-r--r--arch/sparc/kernel/unaligned.c40
1 files changed, 38 insertions, 2 deletions
diff --git a/arch/sparc/kernel/unaligned.c b/arch/sparc/kernel/unaligned.c
index 592a2a4c0..2f051a6aa 100644
--- a/arch/sparc/kernel/unaligned.c
+++ b/arch/sparc/kernel/unaligned.c
@@ -1,4 +1,4 @@
-/* $Id: unaligned.c,v 1.20 2000/01/21 11:38:42 jj Exp $
+/* $Id: unaligned.c,v 1.21 2000/03/15 08:50:16 anton Exp $
* unaligned.c: Unaligned load/store trap handling with special
* cases for the kernel to do them more quickly.
*
@@ -106,6 +106,26 @@ static inline unsigned long fetch_reg(unsigned int reg, struct pt_regs *regs)
return win->locals[reg - 16]; /* yes, I know what this does... */
}
+static inline unsigned long safe_fetch_reg(unsigned int reg, struct pt_regs *regs)
+{
+ struct reg_window *win;
+ unsigned long ret;
+
+ if(reg < 16)
+ return (!reg ? 0 : regs->u_regs[reg]);
+
+ /* Ho hum, the slightly complicated case. */
+ win = (struct reg_window *) regs->u_regs[UREG_FP];
+
+ if ((unsigned long)win & 3)
+ return -1;
+
+ if (get_user(ret, &win->locals[reg - 16]))
+ return -1;
+
+ return ret;
+}
+
static inline unsigned long *fetch_reg_addr(unsigned int reg, struct pt_regs *regs)
{
struct reg_window *win;
@@ -132,6 +152,22 @@ static inline unsigned long compute_effective_address(struct pt_regs *regs,
}
}
+static inline unsigned long safe_compute_effective_address(struct pt_regs *regs,
+ unsigned int insn)
+{
+ unsigned int rs1 = (insn >> 14) & 0x1f;
+ unsigned int rs2 = insn & 0x1f;
+ unsigned int rd = (insn >> 25) & 0x1f;
+
+ if(insn & 0x2000) {
+ maybe_flush_windows(rs1, 0, rd);
+ return (safe_fetch_reg(rs1, regs) + sign_extend_imm13(insn));
+ } else {
+ maybe_flush_windows(rs1, rs2, rd);
+ return (safe_fetch_reg(rs1, regs) + safe_fetch_reg(rs2, regs));
+ }
+}
+
/* This is just to make gcc think panic does return... */
static void unaligned_panic(char *str)
{
@@ -427,7 +463,7 @@ void user_mna_trap_fault(struct pt_regs *regs, unsigned int insn)
info.si_signo = SIGBUS;
info.si_errno = 0;
info.si_code = BUS_ADRALN;
- info.si_addr = (void *)compute_effective_address(regs, insn);
+ info.si_addr = (void *)safe_compute_effective_address(regs, insn);
info.si_trapno = 0;
send_sig_info(SIGBUS, &info, current);
}