patch-2.1.36 linux/arch/alpha/kernel/traps.c

Next file: linux/arch/alpha/mm/init.c
Previous file: linux/arch/alpha/kernel/signal.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.1.35/linux/arch/alpha/kernel/traps.c linux/arch/alpha/kernel/traps.c
@@ -16,6 +16,9 @@
 #include <asm/gentrap.h>
 #include <asm/uaccess.h>
 #include <asm/unaligned.h>
+#include <asm/sysinfo.h>
+#include <asm/smp_lock.h>
+
 
 void die_if_kernel(char * str, struct pt_regs * regs, long err,
 		   unsigned long *r9_15)
@@ -81,10 +84,13 @@
 			return;		/* emulation was successful */
 		}
 	}
+
+	lock_kernel();
 	printk("%s: arithmetic trap at %016lx: %02lx %016lx\n",
 		current->comm, regs.pc, summary, write_mask);
 	die_if_kernel("Arithmetic fault", &regs, 0, 0);
 	force_sig(SIGFPE, current);
+	unlock_kernel();
 }
 
 asmlinkage void do_entIF(unsigned long type, unsigned long a1,
@@ -93,6 +99,7 @@
 {
 	extern int ptrace_cancel_bpt (struct task_struct *who);
 
+	lock_kernel();
 	die_if_kernel("Instruction fault", &regs, type, 0);
 	switch (type) {
 	      case 0: /* breakpoint */
@@ -171,6 +178,7 @@
 	      default:
 		panic("do_entIF: unexpected instruction-fault type");
 	}
+	unlock_kernel();
 }
 
 /*
@@ -204,21 +212,10 @@
 	unsigned long a3, unsigned long a4, unsigned long a5,
 	struct allregs regs)
 {
-	static int cnt = 0;
-	static long last_time = 0;
 	long error, tmp1, tmp2, tmp3, tmp4;
 	unsigned long pc = regs.pc - 4;
 	unsigned fixup;
 
-	if (cnt >= 5 && jiffies - last_time > 5*HZ) {
-		cnt = 0;
-	}
-	if (++cnt < 5) {
-		printk("kernel: unaligned trap at %016lx: %p %lx %ld\n",
-		       pc, va, opcode, reg);
-	}
-	last_time = jiffies;
-
 	unaligned[0].count++;
 	unaligned[0].va = (unsigned long) va;
 	unaligned[0].pc = pc;
@@ -228,7 +225,6 @@
 	   exception will we decide whether we should have caught it.  */
 
 	switch (opcode) {
-#ifdef __HAVE_CPU_BWX
 	case 0x0c: /* ldwu */
 		__asm__ __volatile__(
 		"1:	ldq_u %1,0(%3)\n"
@@ -248,7 +244,6 @@
 			goto got_exception;
 		una_reg(reg) = tmp1|tmp2;
 		return;
-#endif
 
 	case 0x28: /* ldl */
 		__asm__ __volatile__(
@@ -293,7 +288,6 @@
 	/* Note that the store sequences do not indicate that they change
 	   memory because it _should_ be affecting nothing in this context.
 	   (Otherwise we have other, much larger, problems.)  */
-#ifdef __HAVE_CPU_BWX
 	case 0x0d: /* stw */
 		__asm__ __volatile__(
 		"1:	ldq_u %2,1(%5)\n"
@@ -323,7 +317,6 @@
 		if (error)
 			goto got_exception;
 		return;
-#endif
 
 	case 0x2c: /* stl */
 		__asm__ __volatile__(
@@ -385,9 +378,12 @@
 			goto got_exception;
 		return;
 	}
+
+	lock_kernel();
 	printk("Bad unaligned kernel access at %016lx: %p %lx %ld\n",
 		pc, va, opcode, reg);
 	do_exit(SIGSEGV);
+	unlock_kernel();
 	return;
 
 got_exception:
@@ -396,17 +392,23 @@
 	if ((fixup = search_exception_table(pc)) != 0) {
 		unsigned long newpc;
 		newpc = fixup_exception(una_reg, fixup, pc);
+
+		lock_kernel();
 		printk("Forwarding unaligned exception at %lx (%lx)\n",
 		       pc, newpc);
+		unlock_kernel();
+
 		(&regs)->pc = newpc;
 		return;
 	}
 
 	/* Yikes!  No one to forward the exception to.  */
+	lock_kernel();
 	printk("%s: unhandled unaligned exception at pc=%lx ra=%lx"
 	       " (bad address = %p)\n", current->comm,
 	       pc, una_reg(26), va);
 	do_exit(SIGSEGV);
+	unlock_kernel();
 }
 
 /*
@@ -466,50 +468,68 @@
  * uses them as temporary storage for integer memory to memory copies.
  * However, we need to deal with stt/ldt and sts/lds only.
  */
-asmlinkage void do_entUnaUser(void * va, unsigned long opcode, unsigned long reg,
-			      unsigned long * frame)
+
+#define OP_INT_MASK	( 1L << 0x28 | 1L << 0x2c   /* ldl stl */	\
+			| 1L << 0x29 | 1L << 0x2d   /* ldq stq */	\
+			| 1L << 0x0c | 1L << 0x0d ) /* ldwu stw */
+
+#define OP_WRITE_MASK	( 1L << 0x26 | 1L << 0x27   /* sts stt */	\
+			| 1L << 0x2c | 1L << 0x2d   /* stl stq */	\
+			| 1L << 0xd )		    /* stw */
+
+asmlinkage void do_entUnaUser(void * va, unsigned long opcode,
+			      unsigned long reg, unsigned long * frame)
 {
-	long dir, size;
-	unsigned long *reg_addr, *pc_addr, usp, zero = 0;
-	static int cnt = 0;
-	static long last_time = 0;
 	extern void alpha_write_fp_reg (unsigned long reg, unsigned long val);
 	extern unsigned long alpha_read_fp_reg (unsigned long reg);
 
+	static int cnt = 0;
+	static long last_time = 0;
+
+	unsigned long tmp1, tmp2, tmp3, tmp4;
+	unsigned long *reg_addr, *pc_addr, fake_reg;
+	unsigned long uac_bits;
+	long error;
+
 	pc_addr = frame + 7 + 20 + 1;			/* pc in PAL frame */
 
-	if (cnt >= 5 && jiffies - last_time > 5*HZ) {
-		cnt = 0;
+	/* Check the UAC bits to decide what the user wants us to do
+	   with the unaliged access.  */
+
+	uac_bits = (current->tss.flags >> UAC_SHIFT) & UAC_BITMASK;
+	if (!(uac_bits & UAC_NOPRINT)) {
+		if (cnt >= 5 && jiffies - last_time > 5*HZ) {
+			cnt = 0;
+		}
+		if (++cnt < 5) {
+			lock_kernel();
+			printk("%s(%d): unaligned trap at %016lx: %p %lx %ld\n",
+			       current->comm, current->pid,
+			       *pc_addr - 4, va, opcode, reg);
+			unlock_kernel();
+		}
+		last_time = jiffies;
 	}
-	if (++cnt < 5) {
-		printk("%s(%d): unaligned trap at %016lx: %p %lx %ld\n",
-		       current->comm, current->pid,
-		       *pc_addr - 4, va, opcode, reg);
+	if (uac_bits & UAC_SIGBUS) {
+		goto give_sigbus;
 	}
-	last_time = jiffies;
-
-	++unaligned[1].count;
-	unaligned[1].va = (unsigned long) va - 4;
-	unaligned[1].pc = *pc_addr;
-
-	dir = VERIFY_READ;
-	if (opcode & 0x4) {
-		/* it's a stl, stq, stt, or sts */
-		dir = VERIFY_WRITE;
-	}
-	size = 4;
-	if (opcode & 0x1) {
-		/* it's a quadword op */
-		size = 8;
-	}
-	if (verify_area(dir, va, size)) {
-		*pc_addr -= 4;	/* make pc point to faulting insn */
-		force_sig(SIGSEGV, current);
+	if (uac_bits & UAC_NOFIX) {
+		/* Not sure why you'd want to use this, but... */
 		return;
 	}
 
+	/* Don't bother reading ds in the access check since we already
+	   know that this came from the user.  Also rely on the fact that
+	   the page at TASK_SIZE is unmapped and so can't be touched anyway. */
+	if (!__access_ok((unsigned long)va, 0, USER_DS))
+		goto give_sigsegv;
+
+	++unaligned[1].count;
+	unaligned[1].va = (unsigned long)va;
+	unaligned[1].pc = *pc_addr - 4;
+
 	reg_addr = frame;
-	if (opcode >= 0x28) {
+	if ((1L << opcode) & OP_INT_MASK) {
 		/* it's an integer load/store */
 		switch (reg) {
 		      case 0: case 1: case 2: case 3: case 4:
@@ -542,57 +562,249 @@
 
 		      case 30:
 			/* usp in PAL regs */
-			usp = rdusp();
-			reg_addr = &usp;
+			fake_reg = rdusp();
+			reg_addr = &fake_reg;
 			break;
 
 		      case 31:
 			/* zero "register" */
-			reg_addr = &zero;
+			fake_reg = 0;
+			reg_addr = &fake_reg;
 			break;
 		}
 	}
 
+	/* We don't want to use the generic get/put unaligned macros as
+	   we want to trap exceptions.  Only if we actually get an
+	   exception will we decide whether we should have caught it.  */
+
 	switch (opcode) {
-	      case 0x22: /* lds */
-		alpha_write_fp_reg(reg, s_mem_to_reg(
-			get_unaligned((unsigned int *)va)));
-		break;
-	      case 0x26: /* sts */
-		put_unaligned(s_reg_to_mem(alpha_read_fp_reg(reg)),
-			      (unsigned int *)va);
+	case 0x0c: /* ldwu */
+		__asm__ __volatile__(
+		"1:	ldq_u %1,0(%3)\n"
+		"2:	ldq_u %2,1(%3)\n"
+		"	extwl %1,%3,%1\n"
+		"	extwh %2,%3,%2\n"
+		"3:\n"
+		".section __ex_table,\"a\"\n"
+		"	.gprel32 1b\n"
+		"	lda %1,3b-1b(%0)\n"
+		"	.gprel32 2b\n"
+		"	lda %2,3b-2b(%0)\n"
+		".previous"
+			: "=r"(error), "=&r"(tmp1), "=&r"(tmp2)
+			: "r"(va), "0"(0));
+		if (error)
+			goto give_sigsegv;
+		*reg_addr = tmp1|tmp2;
 		break;
 
-	      case 0x23: /* ldt */
-		alpha_write_fp_reg(reg, get_unaligned((unsigned long *)va));
-		break;
-	      case 0x27: /* stt */
-		put_unaligned(alpha_read_fp_reg(reg), (unsigned long *)va);
-		break;
+	case 0x22: /* lds */
+		__asm__ __volatile__(
+		"1:	ldq_u %1,0(%3)\n"
+		"2:	ldq_u %2,3(%3)\n"
+		"	extll %1,%3,%1\n"
+		"	extlh %2,%3,%2\n"
+		"3:\n"
+		".section __ex_table,\"a\"\n"
+		"	.gprel32 1b\n"
+		"	lda %1,3b-1b(%0)\n"
+		"	.gprel32 2b\n"
+		"	lda %2,3b-2b(%0)\n"
+		".previous"
+			: "=r"(error), "=&r"(tmp1), "=&r"(tmp2)
+			: "r"(va), "0"(0));
+		if (error)
+			goto give_sigsegv;
+		alpha_write_fp_reg(reg, s_mem_to_reg((int)(tmp1|tmp2)));
+		return;
 
-	      case 0x28: /* ldl */
-		*reg_addr = get_unaligned((int *)va);
-		break;
-	      case 0x2c: /* stl */
-		put_unaligned(*reg_addr, (int *)va);
-		break;
+	case 0x23: /* ldt */
+		__asm__ __volatile__(
+		"1:	ldq_u %1,0(%3)\n"
+		"2:	ldq_u %2,7(%3)\n"
+		"	extql %1,%3,%1\n"
+		"	extqh %2,%3,%2\n"
+		"3:\n"
+		".section __ex_table,\"a\"\n"
+		"	.gprel32 1b\n"
+		"	lda %1,3b-1b(%0)\n"
+		"	.gprel32 2b\n"
+		"	lda %2,3b-2b(%0)\n"
+		".previous"
+			: "=r"(error), "=&r"(tmp1), "=&r"(tmp2)
+			: "r"(va), "0"(0));
+		if (error)
+			goto give_sigsegv;
+		alpha_write_fp_reg(reg, tmp1|tmp2);
+		return;
 
-	      case 0x29: /* ldq */
-		*reg_addr = get_unaligned((long *)va);
+	case 0x28: /* ldl */
+		__asm__ __volatile__(
+		"1:	ldq_u %1,0(%3)\n"
+		"2:	ldq_u %2,3(%3)\n"
+		"	extll %1,%3,%1\n"
+		"	extlh %2,%3,%2\n"
+		"3:\n"
+		".section __ex_table,\"a\"\n"
+		"	.gprel32 1b\n"
+		"	lda %1,3b-1b(%0)\n"
+		"	.gprel32 2b\n"
+		"	lda %2,3b-2b(%0)\n"
+		".previous"
+			: "=r"(error), "=&r"(tmp1), "=&r"(tmp2)
+			: "r"(va), "0"(0));
+		if (error)
+			goto give_sigsegv;
+		*reg_addr = (int)(tmp1|tmp2);
 		break;
-	      case 0x2d: /* stq */
-		put_unaligned(*reg_addr, (long *)va);
+
+	case 0x29: /* ldq */
+		__asm__ __volatile__(
+		"1:	ldq_u %1,0(%3)\n"
+		"2:	ldq_u %2,7(%3)\n"
+		"	extql %1,%3,%1\n"
+		"	extqh %2,%3,%2\n"
+		"3:\n"
+		".section __ex_table,\"a\"\n"
+		"	.gprel32 1b\n"
+		"	lda %1,3b-1b(%0)\n"
+		"	.gprel32 2b\n"
+		"	lda %2,3b-2b(%0)\n"
+		".previous"
+			: "=r"(error), "=&r"(tmp1), "=&r"(tmp2)
+			: "r"(va), "0"(0));
+		if (error)
+			goto give_sigsegv;
+		*reg_addr = tmp1|tmp2;
 		break;
 
-	      default:
-		*pc_addr -= 4;	/* make pc point to faulting insn */
-		force_sig(SIGBUS, current);
+	/* Note that the store sequences do not indicate that they change
+	   memory because it _should_ be affecting nothing in this context.
+	   (Otherwise we have other, much larger, problems.)  */
+	case 0x0d: /* stw */
+		__asm__ __volatile__(
+		"1:	ldq_u %2,1(%5)\n"
+		"2:	ldq_u %1,0(%5)\n"
+		"	inswh %6,%5,%4\n"
+		"	inswl %6,%5,%3\n"
+		"	mskwh %2,%5,%2\n"
+		"	mskwl %1,%5,%1\n"
+		"	or %2,%4,%2\n"
+		"	or %1,%3,%1\n"
+		"3:	stq_u %2,1(%5)\n"
+		"4:	stq_u %1,0(%5)\n"
+		"5:\n"
+		".section __ex_table,\"a\"\n"
+		"	.gprel32 1b\n"
+		"	lda %2,5b-1b(%0)\n"
+		"	.gprel32 2b\n"
+		"	lda %1,5b-2b(%0)\n"
+		"	.gprel32 3b\n"
+		"	lda $31,5b-3b(%0)\n"
+		"	.gprel32 4b\n"
+		"	lda $31,5b-4b(%0)\n"
+		".previous"
+			: "=r"(error), "=&r"(tmp1), "=&r"(tmp2),
+			  "=&r"(tmp3), "=&r"(tmp4)
+			: "r"(va), "r"(*reg_addr), "0"(0));
+		if (error)
+			goto give_sigsegv;
+		return;
+
+	case 0x26: /* sts */
+		fake_reg = s_reg_to_mem(alpha_read_fp_reg(reg));
+		reg_addr = &fake_reg;
+		/* FALLTHRU */
+
+	case 0x2c: /* stl */
+		__asm__ __volatile__(
+		"1:	ldq_u %2,3(%5)\n"
+		"2:	ldq_u %1,0(%5)\n"
+		"	inslh %6,%5,%4\n"
+		"	insll %6,%5,%3\n"
+		"	msklh %2,%5,%2\n"
+		"	mskll %1,%5,%1\n"
+		"	or %2,%4,%2\n"
+		"	or %1,%3,%1\n"
+		"3:	stq_u %2,3(%5)\n"
+		"4:	stq_u %1,0(%5)\n"
+		"5:\n"
+		".section __ex_table,\"a\"\n"
+		"	.gprel32 1b\n"
+		"	lda %2,5b-1b(%0)\n"
+		"	.gprel32 2b\n"
+		"	lda %1,5b-2b(%0)\n"
+		"	.gprel32 3b\n"
+		"	lda $31,5b-3b(%0)\n"
+		"	.gprel32 4b\n"
+		"	lda $31,5b-4b(%0)\n"
+		".previous"
+			: "=r"(error), "=&r"(tmp1), "=&r"(tmp2),
+			  "=&r"(tmp3), "=&r"(tmp4)
+			: "r"(va), "r"(*reg_addr), "0"(0));
+		if (error)
+			goto give_sigsegv;
+		return;
+
+	case 0x27: /* stt */
+		fake_reg = alpha_read_fp_reg(reg);
+		reg_addr = &fake_reg;
+		/* FALLTHRU */
+
+	case 0x2d: /* stq */
+		__asm__ __volatile__(
+		"1:	ldq_u %2,7(%5)\n"
+		"2:	ldq_u %1,0(%5)\n"
+		"	insqh %6,%5,%4\n"
+		"	insql %6,%5,%3\n"
+		"	mskqh %2,%5,%2\n"
+		"	mskql %1,%5,%1\n"
+		"	or %2,%4,%2\n"
+		"	or %1,%3,%1\n"
+		"3:	stq_u %2,7(%5)\n"
+		"4:	stq_u %1,0(%5)\n"
+		"5:\n"
+		".section __ex_table,\"a\"\n\t"
+		"	.gprel32 1b\n"
+		"	lda %2,5b-1b(%0)\n"
+		"	.gprel32 2b\n"
+		"	lda %1,5b-2b(%0)\n"
+		"	.gprel32 3b\n"
+		"	lda $31,5b-3b(%0)\n"
+		"	.gprel32 4b\n"
+		"	lda $31,5b-4b(%0)\n"
+		".previous"
+			: "=r"(error), "=&r"(tmp1), "=&r"(tmp2),
+			  "=&r"(tmp3), "=&r"(tmp4)
+			: "r"(va), "r"(*reg_addr), "0"(0));
+		if (error)
+			goto give_sigsegv;
 		return;
-	}
 
-	if (opcode >= 0x28 && reg == 30 && dir == VERIFY_WRITE) {
-		wrusp(usp);
+	default:
+		/* What instruction were you trying to use, exactly?  */
+		goto give_sigbus;
 	}
+
+	/* Only integer loads should get here; everyone else returns early. */
+	if (reg == 30)
+		wrusp(fake_reg);
+	return;
+
+give_sigsegv:
+	*pc_addr -= 4;  /* make pc point to faulting insn */
+	lock_kernel();
+	force_sig(SIGSEGV, current);
+	unlock_kernel();
+	return;
+
+give_sigbus:
+	*pc_addr -= 4;
+	lock_kernel();
+	force_sig(SIGBUS, current);
+	unlock_kernel();
+	return;
 }
 
 /*
@@ -610,8 +822,11 @@
 			  unsigned long a3, unsigned long a4, unsigned long a5,
 			  struct pt_regs regs)
 {
-	if (regs.r0 != 112)
+	lock_kernel();
+	/* Only report OSF system calls.  */
+	if (regs.r0 != 112 && regs.r0 < 300)
 		printk("<sc %ld(%lx,%lx,%lx)>", regs.r0, a0, a1, a2);
+	unlock_kernel();
 	return -1;
 }
 
@@ -621,16 +836,11 @@
 extern asmlinkage void entUna(void);
 extern asmlinkage void entSys(void);
 
+register unsigned long gptr __asm__("$29");
+
 void trap_init(void)
 {
-	unsigned long gptr;
-
-	/*
-	 * Tell PAL-code what global pointer we want in the kernel..
-	 */
-	__asm__("br %0,___tmp\n"
-		"___tmp:\tldgp %0,0(%0)"
-		: "=r" (gptr));
+	/* Tell PAL-code what global pointer we want in the kernel.  */
 	wrkgp(gptr);
 
 	wrent(entArith, 1);

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov