svn commit: r210595 - head/sys/mips/mips

Juli Mallett jmallett at FreeBSD.org
Thu Jul 29 02:32:21 UTC 2010


Author: jmallett
Date: Thu Jul 29 02:32:21 2010
New Revision: 210595
URL: http://svn.freebsd.org/changeset/base/210595

Log:
  o) Subtract 64K from the default userland stack pointer.  GCC generate code
     that with a 32-bit ABI on a system with 64-bit registers can attempt to
     access an invalid (well, kernel) memory address rather than the intended
     user address for stack-relative loads and stores.  Lowering the stack
     pointer works around this. [1]
  o) Make TRAP_DEBUG code conditional on the trap_debug variable.  Make
     trap_debug default to 0 instead of 1 now but make it possible to change it
     at runtime using sysctl.
  o) Kill programs that attempt an unaligned access of a kernel address.  Note
     that with some ABIs, calling useracc() is not sufficient since the register
     may be 64-bit but vm_offset_t is 32-bit so a kernel address could be
     truncated to what looks like a valid user address, allowing the user to
     crash the kernel.
  o) Clean up unaligned access emulation to support unaligned 16-bit and 64-bit
     accesses.  (For 16-bit accesses it was checking for user access to too much
     memory (4 bytes) and there was no 64-bit support.)  This still lacks support
     for unaligned load-linked and store-conditional.
  
  Reviewed by:	[1] gonzo

Modified:
  head/sys/mips/mips/pm_machdep.c
  head/sys/mips/mips/trap.c

Modified: head/sys/mips/mips/pm_machdep.c
==============================================================================
--- head/sys/mips/mips/pm_machdep.c	Thu Jul 29 02:24:21 2010	(r210594)
+++ head/sys/mips/mips/pm_machdep.c	Thu Jul 29 02:32:21 2010	(r210595)
@@ -479,9 +479,37 @@ exec_setregs(struct thread *td, struct i
 	bzero((caddr_t)td->td_frame, sizeof(struct trapframe));
 
 	/*
-	 * Make sp 64-bit aligned.
+	 * The stack pointer has to be aligned to accommodate the largest
+	 * datatype at minimum.  This probably means it should be 16-byte
+	 * aligned, but for now we're 8-byte aligning it.
 	 */
 	td->td_frame->sp = ((register_t) stack) & ~(sizeof(__int64_t) - 1);
+
+	/*
+	 * If we're running o32 or n32 programs but have 64-bit registers,
+	 * GCC may use stack-relative addressing near the top of user
+	 * address space that, due to sign extension, will yield an
+	 * invalid address.  For instance, if sp is 0x7fffff00 then GCC
+	 * might do something like this to load a word from 0x7ffffff0:
+	 *
+	 * 	addu	sp, sp, 32768
+	 * 	lw	t0, -32528(sp)
+	 *
+	 * On systems with 64-bit registers, sp is sign-extended to
+	 * 0xffffffff80007f00 and the load is instead done from
+	 * 0xffffffff7ffffff0.
+	 *
+	 * To prevent this, we subtract 64K from the stack pointer here.
+	 *
+	 * For consistency, we should just always do this unless we're
+	 * running n64 programs.  For now, since we don't support
+	 * COMPAT_FREEBSD32 on n64 kernels, we just do it unless we're
+	 * running n64 kernels.
+	 */
+#if !defined(__mips_n64)
+	td->td_frame->sp -= 65536;
+#endif
+
 	td->td_frame->pc = imgp->entry_addr & ~3;
 	td->td_frame->t9 = imgp->entry_addr & ~3; /* abicall req */
 	td->td_frame->sr = MIPS_SR_KSU_USER | MIPS_SR_EXL | MIPS_SR_INT_IE |

Modified: head/sys/mips/mips/trap.c
==============================================================================
--- head/sys/mips/mips/trap.c	Thu Jul 29 02:24:21 2010	(r210594)
+++ head/sys/mips/mips/trap.c	Thu Jul 29 02:32:21 2010	(r210595)
@@ -96,7 +96,9 @@ __FBSDID("$FreeBSD$");
 
 
 #ifdef TRAP_DEBUG
-int trap_debug = 1;
+int trap_debug = 0;
+SYSCTL_INT(_machdep, OID_AUTO, trap_debug, CTLFLAG_RW,
+    &trap_debug, 0, "Debug information on all traps");
 #endif
 
 static void log_illegal_instruction(const char *, struct trapframe *);
@@ -259,7 +261,7 @@ static int allow_unaligned_acc = 1;
 SYSCTL_INT(_vm, OID_AUTO, allow_unaligned_acc, CTLFLAG_RW,
     &allow_unaligned_acc, 0, "Allow unaligned accesses");
 
-static int emulate_unaligned_access(struct trapframe *frame);
+static int emulate_unaligned_access(struct trapframe *frame, int mode);
 
 extern void fswintrberr(void); /* XXX */
 
@@ -555,7 +557,10 @@ dofault:
 
 	case T_ADDR_ERR_LD + T_USER:	/* misaligned or kseg access */
 	case T_ADDR_ERR_ST + T_USER:	/* misaligned or kseg access */
-		if (allow_unaligned_acc) {
+		if (trapframe->badvaddr < 0 ||
+		    trapframe->badvaddr >= VM_MAXUSER_ADDRESS) {
+			msg = "ADDRESS_SPACE_ERR";
+		} else if (allow_unaligned_acc) {
 			int mode;
 
 			if (type == (T_ADDR_ERR_LD + T_USER))
@@ -563,23 +568,13 @@ dofault:
 			else
 				mode = VM_PROT_WRITE;
 
-			/*
-			 * ADDR_ERR faults have higher priority than TLB
-			 * Miss faults.  Therefore, it is necessary to
-			 * verify that the faulting address is a valid
-			 * virtual address within the process' address space
-			 * before trying to emulate the unaligned access.
-			 */
-			if (useracc((caddr_t)
-			    (((vm_offset_t)trapframe->badvaddr) &
-			    ~(sizeof(int) - 1)), sizeof(int) * 2, mode)) {
-				access_type = emulate_unaligned_access(
-				    trapframe);
-				if (access_type != 0)
-					goto out;
-			}
+			access_type = emulate_unaligned_access(trapframe, mode);
+			if (access_type != 0)
+				goto out;
+			msg = "ALIGNMENT_FIX_ERR";
+		} else {
+			msg = "ADDRESS_ERR";
 		}
-		msg = "ADDRESS_ERR";
 
 		/* FALL THROUGH */
 
@@ -686,7 +681,9 @@ dofault:
 #endif
 			}
 #ifdef TRAP_DEBUG
-			printf("SYSCALL #%d pid:%u\n", code, p->p_pid);
+			if (trap_debug) {
+				printf("SYSCALL #%d pid:%u\n", code, p->p_pid);
+			}
 #endif
 
 			if (p->p_sysent->sv_mask)
@@ -723,8 +720,10 @@ dofault:
 				}
 			}
 #ifdef TRAP_DEBUG
-			for (i = 0; i < nargs; i++) {
-				printf("args[%d] = %#jx\n", i, (intmax_t)args[i]);
+			if (trap_debug) {
+				for (i = 0; i < nargs; i++) {
+					printf("args[%d] = %#jx\n", i, (intmax_t)args[i]);
+				}
 			}
 #endif
 #ifdef SYSCALL_TRACING
@@ -937,8 +936,10 @@ dofault:
 	case T_ADDR_ERR_LD:	/* misaligned access */
 	case T_ADDR_ERR_ST:	/* misaligned access */
 #ifdef TRAP_DEBUG
-		printf("+++ ADDR_ERR: type = %d, badvaddr = %#jx\n", type,
-		    (intmax_t)trapframe->badvaddr);
+		if (trap_debug) {
+			printf("+++ ADDR_ERR: type = %d, badvaddr = %#jx\n", type,
+			    (intmax_t)trapframe->badvaddr);
+		}
 #endif
 		/* Only allow emulation on a user address */
 		if (allow_unaligned_acc &&
@@ -950,22 +951,9 @@ dofault:
 			else
 				mode = VM_PROT_WRITE;
 
-			/*
-			 * ADDR_ERR faults have higher priority than TLB
-			 * Miss faults.  Therefore, it is necessary to
-			 * verify that the faulting address is a valid
-			 * virtual address within the process' address space
-			 * before trying to emulate the unaligned access.
-			 */
-			if (useracc((caddr_t)
-			    (((vm_offset_t)trapframe->badvaddr) &
-			    ~(sizeof(int) - 1)), sizeof(int) * 2, mode)) {
-				access_type = emulate_unaligned_access(
-				    trapframe);
-				if (access_type != 0) {
-					return (trapframe->pc);
-				}
-			}
+			access_type = emulate_unaligned_access(trapframe, mode);
+			if (access_type != 0)
+				return (trapframe->pc);
 		}
 		/* FALLTHROUGH */
 
@@ -997,9 +985,10 @@ err:
 			printf("kernel mode)\n");
 
 #ifdef TRAP_DEBUG
-		printf("badvaddr = %#jx, pc = %#jx, ra = %#jx, sr = %#jxx\n",
-		       (intmax_t)trapframe->badvaddr, (intmax_t)trapframe->pc, (intmax_t)trapframe->ra,
-		       (intmax_t)trapframe->sr);
+		if (trap_debug)
+			printf("badvaddr = %#jx, pc = %#jx, ra = %#jx, sr = %#jxx\n",
+			       (intmax_t)trapframe->badvaddr, (intmax_t)trapframe->pc, (intmax_t)trapframe->ra,
+			       (intmax_t)trapframe->sr);
 #endif
 
 #ifdef KDB
@@ -1433,76 +1422,120 @@ log_bad_page_fault(char *msg, struct tra
  * Unaligned load/store emulation
  */
 static int
-mips_unaligned_load_store(struct trapframe *frame, register_t addr, register_t pc)
+mips_unaligned_load_store(struct trapframe *frame, int mode, register_t addr, register_t pc)
 {
 	register_t *reg = (register_t *) frame;
 	u_int32_t inst = *((u_int32_t *)(intptr_t)pc);
-	u_int32_t value_msb, value;
-	int access_type = 0;
+	register_t value_msb, value;
+	unsigned size;
 
+	/*
+	 * ADDR_ERR faults have higher priority than TLB
+	 * Miss faults.  Therefore, it is necessary to
+	 * verify that the faulting address is a valid
+	 * virtual address within the process' address space
+	 * before trying to emulate the unaligned access.
+	 */
+	switch (MIPS_INST_OPCODE(inst)) {
+	case OP_LHU: case OP_LH:
+	case OP_SH:
+		size = 2;
+		break;
+	case OP_LWU: case OP_LW:
+	case OP_SW:
+		size = 4;
+		break;
+	case OP_LD:
+	case OP_SD:
+		size = 8;
+		break;
+	default:
+		printf("%s: unhandled opcode in address error: %#x\n", __func__, MIPS_INST_OPCODE(inst));
+		return (0);
+	}
+
+	if (!useracc((void *)((vm_offset_t)addr & ~(size - 1)), size * 2, mode))
+		return (0);
+
+	/*
+	 * XXX
+	 * Handle LL/SC LLD/SCD.
+	 */
 	switch (MIPS_INST_OPCODE(inst)) {
 	case OP_LHU:
+		KASSERT(mode == VM_PROT_READ, ("access mode must be read for load instruction."));
 		lbu_macro(value_msb, addr);
 		addr += 1;
 		lbu_macro(value, addr);
 		value |= value_msb << 8;
 		reg[MIPS_INST_RT(inst)] = value;
-		access_type = MIPS_LHU_ACCESS;
-		break;
+		return (MIPS_LHU_ACCESS);
 
 	case OP_LH:
+		KASSERT(mode == VM_PROT_READ, ("access mode must be read for load instruction."));
 		lb_macro(value_msb, addr);
 		addr += 1;
 		lbu_macro(value, addr);
 		value |= value_msb << 8;
 		reg[MIPS_INST_RT(inst)] = value;
-		access_type = MIPS_LH_ACCESS;
-		break;
+		return (MIPS_LH_ACCESS);
 
 	case OP_LWU:
+		KASSERT(mode == VM_PROT_READ, ("access mode must be read for load instruction."));
 		lwl_macro(value, addr);
 		addr += 3;
 		lwr_macro(value, addr);
 		value &= 0xffffffff;
 		reg[MIPS_INST_RT(inst)] = value;
-		access_type = MIPS_LWU_ACCESS;
-		break;
+		return (MIPS_LWU_ACCESS);
 
 	case OP_LW:
+		KASSERT(mode == VM_PROT_READ, ("access mode must be read for load instruction."));
 		lwl_macro(value, addr);
 		addr += 3;
 		lwr_macro(value, addr);
 		reg[MIPS_INST_RT(inst)] = value;
-		access_type = MIPS_LW_ACCESS;
-		break;
+		return (MIPS_LW_ACCESS);
+
+	case OP_LD:
+		KASSERT(mode == VM_PROT_READ, ("access mode must be read for load instruction."));
+		ldl_macro(value, addr);
+		addr += 7;
+		ldr_macro(value, addr);
+		reg[MIPS_INST_RT(inst)] = value;
+		return (MIPS_LD_ACCESS);
 
 	case OP_SH:
+		KASSERT(mode == VM_PROT_WRITE, ("access mode must be write for store instruction."));
 		value = reg[MIPS_INST_RT(inst)];
 		value_msb = value >> 8;
 		sb_macro(value_msb, addr);
 		addr += 1;
 		sb_macro(value, addr);
-		access_type = MIPS_SH_ACCESS;
-		break;
+		return (MIPS_SH_ACCESS);
 
 	case OP_SW:
+		KASSERT(mode == VM_PROT_WRITE, ("access mode must be write for store instruction."));
 		value = reg[MIPS_INST_RT(inst)];
 		swl_macro(value, addr);
 		addr += 3;
 		swr_macro(value, addr);
-		access_type = MIPS_SW_ACCESS;
-		break;
+		return (MIPS_SW_ACCESS);
 
-	default:
-		break;
+	case OP_SD:
+		KASSERT(mode == VM_PROT_WRITE, ("access mode must be write for store instruction."));
+		value = reg[MIPS_INST_RT(inst)];
+		sdl_macro(value, addr);
+		addr += 7;
+		sdr_macro(value, addr);
+		return (MIPS_SD_ACCESS);
 	}
-
-	return access_type;
+	panic("%s: should not be reached.", __func__);
 }
 
 
 static int
-emulate_unaligned_access(struct trapframe *frame)
+emulate_unaligned_access(struct trapframe *frame, int mode)
 {
 	register_t pc;
 	int access_type = 0;
@@ -1523,7 +1556,7 @@ emulate_unaligned_access(struct trapfram
 		 * Otherwise restore pc and fall through.
 		 */
 		access_type = mips_unaligned_load_store(frame,
-		    frame->badvaddr, pc);
+		    mode, frame->badvaddr, pc);
 
 		if (access_type) {
 			if (DELAYBRANCH(frame->cause))


More information about the svn-src-head mailing list