patch-1.3.22 linux/arch/alpha/kernel/traps.c
Next file: linux/arch/i386/config.in
Previous file: linux/arch/alpha/kernel/signal.c
Back to the patch index
Back to the overall index
- Lines: 154
- Date:
Tue Aug 29 10:15:48 1995
- Orig file:
v1.3.21/linux/arch/alpha/kernel/traps.c
- Orig date:
Thu Jul 27 10:13:16 1995
diff -u --recursive --new-file v1.3.21/linux/arch/alpha/kernel/traps.c linux/arch/alpha/kernel/traps.c
@@ -8,6 +8,7 @@
* This file initializes the trap entry points
*/
+#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/tty.h>
@@ -102,21 +103,27 @@
struct unaligned_stat {
unsigned long count, va, pc;
-} unaligned;
+} unaligned[2];
asmlinkage void do_entUna(void * va, unsigned long opcode, unsigned long reg,
unsigned long a3, unsigned long a4, unsigned long a5,
struct allregs regs)
{
static int cnt = 0;
+ static long last_time = 0;
- if (++cnt < 5)
- printk("Unaligned trap at %016lx: %p %lx %ld\n",
- regs.pc, va, opcode, reg);
-
- ++unaligned.count;
- unaligned.va = (unsigned long) va - 4;
- unaligned.pc = regs.pc;
+ if (cnt >= 5 && jiffies - last_time > 5*HZ) {
+ cnt = 0;
+ }
+ if (++cnt < 5) {
+ printk("kernel: unaligned trap at %016lx: %p %lx %ld\n",
+ regs.pc - 4, va, opcode, reg);
+ }
+ last_time = jiffies;
+
+ ++unaligned[0].count;
+ unaligned[0].va = (unsigned long) va - 4;
+ unaligned[0].pc = regs.pc;
/* $16-$18 are PAL-saved, and are offset by 19 entries */
if (reg >= 16 && reg <= 18)
@@ -141,19 +148,98 @@
}
/*
- * Handle user-level unaligned fault. For now, simply send a
- * SIGBUS---there should be little reason for users not wanting to
- * fix their code instead. Notice that we have the regular kernel
- * stack layout here, so finding the appropriate registers is a little
- * more difficult than in the kernel case. Also, we'd need to do
- * a "verify_area()" before accessing memory on behalf of the user.
+ * Handle user-level unaligned fault. Handling user-level unaligned
+ * faults is *extremely* slow and produces nasty messages. A user
+ * program *should* fix unaligned faults ASAP.
+ *
+ * Notice that we have (almost) the regular kernel stack layout here,
+ * so finding the appropriate registers is a little more difficult
+ * than in the kernel case.
+ *
+ * Finally, we handle regular integer load/stores only. In
+ * particular, load-linked/store-conditionally and floating point
+ * load/stores are not supported. The former make no sense with
+ * unaligned faults (they are guaranteed to fail) and I don't think
+ * the latter will occur in any decent program.
*/
-asmlinkage void do_entUnaUser(void *va, unsigned long opcode, unsigned long reg,
- unsigned long a3, unsigned long a4, unsigned long a5,
- struct pt_regs regs)
+asmlinkage void do_entUnaUser(void * va, unsigned long opcode, unsigned long reg,
+ unsigned long * frame)
{
- regs.pc -= 4; /* make pc point to faulting insn */
- send_sig(SIGBUS, current, 1);
+ long dir, size;
+ unsigned long *reg_addr, *pc_addr, usp, zero = 0;
+ static int cnt = 0;
+ static long last_time = 0;
+
+ pc_addr = frame + 7 + 20 + 1; /* pc in PAL frame */
+
+ if (cnt >= 5 && jiffies - last_time > 5*HZ) {
+ cnt = 0;
+ }
+ if (++cnt < 5) {
+ printk("%s(%d): unaligned trap at %016lx: %p %lx %ld\n",
+ current->comm, current->pid,
+ *pc_addr - 4, va, opcode, reg);
+ }
+ last_time = jiffies;
+
+ ++unaligned[1].count;
+ unaligned[1].va = (unsigned long) va - 4;
+ unaligned[1].pc = *pc_addr;
+
+ dir = VERIFY_READ;
+ if (opcode > 0x29) {
+ /* it's a stl or stq */
+ dir = VERIFY_WRITE;
+ }
+ size = 4;
+ if (opcode & 1) {
+ /* it's a quadword op */
+ size = 8;
+ }
+ if (verify_area(dir, va, size)) {
+ *pc_addr -= 4; /* make pc point to faulting insn */
+ send_sig(SIGSEGV, current, 1);
+ return;
+ }
+
+ reg_addr = frame;
+ if (reg < 9) {
+ reg_addr += 7 + reg; /* v0-t7 in SAVE_ALL frame */
+ } else if (reg < 16) {
+ reg_addr += (reg - 9); /* s0-s6 in entUna frame */
+ } else if (reg < 19) {
+ reg_addr += 7 + 20 + 3 + (reg - 16); /* a0-a2 in PAL frame */
+ } else if (reg < 29) {
+ reg_addr += 7 + 9 + (reg - 19); /* a3-at in SAVE_ALL frame */
+ } else {
+ switch (reg) {
+ case 29: /* gp in PAL frame */
+ reg_addr += 7 + 20 + 2;
+ break;
+ case 30: /* usp in PAL regs */
+ usp = rdusp();
+ reg_addr = &usp;
+ break;
+ case 31: /* zero "register" */
+ reg_addr = &zero;
+ break;
+ }
+ }
+
+ switch (opcode) {
+ case 0x28: *reg_addr = (int) ldl_u(va); break; /* ldl */
+ case 0x29: *reg_addr = ldq_u(va); break; /* ldq */
+ case 0x2c: stl_u(*reg_addr, va); break; /* stl */
+ case 0x2d: stq_u(*reg_addr, va); break; /* stq */
+ default:
+ *pc_addr -= 4; /* make pc point to faulting insn */
+ send_sig(SIGBUS, current, 1);
+ return;
+ }
+
+ if (reg == 30 && dir == VERIFY_WRITE) {
+ wrusp(usp);
+ }
}
/*
FUNET's LINUX-ADM group, [email protected]
TCL-scripts by Sam Shen, [email protected]
with Sam's (original) version of this