Subject: tlb_handler() fixup
To: None <port-sh3@netbsd.org>
From: Jason R Thorpe <thorpej@zembu.com>
List: port-sh3
Date: 02/02/2001 10:41:06
Folks...

I've been trying to hunt down a stability issue in the SH3/SH4 code,
and I noticed that the TLB fault handler doesn't exactly do the
right thing -- especially with fatal supervisor-mode faults.

I fixed this up a little, and would appreciate a sanity check from
the sh3 port experts.

-- 
        -- Jason R. Thorpe <thorpej@zembu.com>

Index: trap.c
===================================================================
RCS file: /cvsroot/syssrc/sys/arch/sh3/sh3/trap.c,v
retrieving revision 1.21
diff -c -r1.21 trap.c
*** trap.c	2000/11/22 21:12:28	1.21
--- trap.c	2001/02/02 18:35:55
***************
*** 279,286 ****
  	case T_ADDRESSERRR|T_USER:		/* protection fault */
  	case T_ADDRESSERRW|T_USER:
  	case T_INVALIDSLOT|T_USER:
! 		printf("trap type %x spc %x ssr %x \n",
! 			   type, frame.tf_spc, frame.tf_ssr);
  		trapsignal(p, SIGBUS, type &~ T_USER);
  		goto out;
  
--- 279,286 ----
  	case T_ADDRESSERRR|T_USER:		/* protection fault */
  	case T_ADDRESSERRW|T_USER:
  	case T_INVALIDSLOT|T_USER:
! 		printf("trap type %x spc %x ssr %x (%s)\n",
! 			   type, frame.tf_spc, frame.tf_ssr, p->p_comm);
  		trapsignal(p, SIGBUS, type &~ T_USER);
  		goto out;
  
***************
*** 601,607 ****
  	struct trapframe frame;
  {
  	vaddr_t va;
! 	int pde_index;
  	unsigned long *pde;
  	unsigned long pte;
  	unsigned long *pd_top;
--- 601,607 ----
  	struct trapframe frame;
  {
  	vaddr_t va;
! 	int pde_index, sig;
  	unsigned long *pde;
  	unsigned long pte;
  	unsigned long *pd_top;
***************
*** 611,623 ****
  	vm_map_t map;
  	int rv = 0;
  	u_quad_t sticks = 0;
- 	int type = 0;
  	vm_prot_t ftype;
  	extern vm_map_t kernel_map;
- 	unsigned int nss;
  	vaddr_t	va_save;
  	unsigned long pteh_save;
! 	int exptype;
  #ifdef RECURSE_TLB_HANDLER
  	int reentrant = 0;
  #endif
--- 611,621 ----
  	vm_map_t map;
  	int rv = 0;
  	u_quad_t sticks = 0;
  	vm_prot_t ftype;
  	extern vm_map_t kernel_map;
  	vaddr_t	va_save;
  	unsigned long pteh_save;
! 	int exptype, user;
  #ifdef RECURSE_TLB_HANDLER
  	int reentrant = 0;
  #endif
***************
*** 710,781 ****
  		printf("tlb_handler#:va(0x%lx),curproc(%p)\n", va, curproc);
  #endif
  
  	pteh_save = SHREG_PTEH;
  	va_save = va;
  	p = curproc;
  	if (p == NULL) {
  		rv = KERN_FAILURE;
! 		goto nogo;
  	} else {
! #if 1
! 		if (!KERNELMODE(frame.tf_r15)) {
! #else
! 		if (!KERNELMODE(frame.tf_spc, frame.tf_ssr)) {
! #endif
! 			type = T_USER;
  			sticks = p->p_sticks;
  			p->p_md.md_regs = &frame;
! 		}
! 		else
  			sticks = 0;
  	}
  
- 	vm = p->p_vmspace;
  	/*
  	 * It is only a kernel address space fault iff:
! 	 *	1. (type & T_USER) == 0  and
  	 *	2. pcb_onfault not set or
! 	 *	3. pcb_onfault set but supervisor space fault
  	 * The last can occur during an exec() copyin where the
  	 * argument space is lazy-allocated.
  	 */
! 	if (va >= KERNBASE)
  		map = kernel_map;
! 	else
  		map = &vm->vm_map;
  
- 	/* exptype = SHREG_EXPEVT; */
  	if (exptype == T_TLBMISSW || exptype == T_TLBPRIVW)
! 		ftype = VM_PROT_READ | VM_PROT_WRITE;
  	else
  		ftype = VM_PROT_READ;
  
- 	nss = 0;
- 
- #ifdef SH4
- 	if (vm != NULL && (caddr_t)va >= vm->vm_maxsaddr
- 	    && (caddr_t)va < (caddr_t)VM_MAXUSER_ADDRESS
- 	    && map != kernel_map) {
- #else
- 	if ((caddr_t)va >= vm->vm_maxsaddr
- 	    && (caddr_t)va < (caddr_t)VM_MAXUSER_ADDRESS
- 	    && map != kernel_map) {
- #endif
- 		nss = btoc(USRSTACK-(unsigned)va);
- 		if (nss > btoc(p->p_rlimit[RLIMIT_STACK].rlim_cur)) {
- 			/*
- 			 * We used to fail here. However, it may
- 			 * just have been an mmap()ed page low
- 			 * in the stack, which is legal. If it
- 			 * wasn't, uvm_fault() will fail below.
- 			 *
- 			 * Set nss to 0, since this case is not
- 			 * a "stack extension".
- 			 */
- 			nss = 0;
- 		}
- 	}
- 
  #ifdef RECURSE_TLB_HANDLER
  	if (((PSL_BL | PSL_IMASK) & frame.tf_ssr) == 0)
  		reentrant = 1;
--- 708,750 ----
  		printf("tlb_handler#:va(0x%lx),curproc(%p)\n", va, curproc);
  #endif
  
+ 	user = !KERNELMODE(frame.tf_r15);
+ 
  	pteh_save = SHREG_PTEH;
  	va_save = va;
  	p = curproc;
  	if (p == NULL) {
  		rv = KERN_FAILURE;
! 		goto dopanic;
  	} else {
! 		if (user) {
  			sticks = p->p_sticks;
  			p->p_md.md_regs = &frame;
! 		} else
  			sticks = 0;
  	}
  
  	/*
  	 * It is only a kernel address space fault iff:
! 	 *	1. !user and
  	 *	2. pcb_onfault not set or
! 	 *	3. pcb_onfault set but supervisor space data fault
  	 * The last can occur during an exec() copyin where the
  	 * argument space is lazy-allocated.
  	 */
! 	if (user == 0 && (va >= VM_MIN_KERNEL_ADDRESS ||
! 	    p->p_addr->u_pcb.pcb_onfault == 0))
  		map = kernel_map;
! 	else {
! 		vm = p->p_vmspace;
  		map = &vm->vm_map;
+ 	}
  
  	if (exptype == T_TLBMISSW || exptype == T_TLBPRIVW)
! 		ftype = VM_PROT_WRITE;
  	else
  		ftype = VM_PROT_READ;
  
  #ifdef RECURSE_TLB_HANDLER
  	if (((PSL_BL | PSL_IMASK) & frame.tf_ssr) == 0)
  		reentrant = 1;
***************
*** 790,804 ****
  	if (reentrant)
  		disable_ext_intr();
  #endif
! 	if (rv == KERN_SUCCESS) {
  #ifdef SH4
! 		if (vm != NULL && nss > vm->vm_ssize)
! 			vm->vm_ssize = nss;
! #else
! 		if (nss > vm->vm_ssize)
! 			vm->vm_ssize = nss;
  #endif
  
  		va = va_save;
  		SHREG_PTEH = pteh_save;
  		pde_index = pdei(va);
--- 759,790 ----
  	if (reentrant)
  		disable_ext_intr();
  #endif
! 
! 	/*
! 	 * If this was a stack access, we keep track of the
! 	 * maximum accessed stack size.  Also, if uvm_fault()
! 	 * gets a protection failure, it is due to accessing
! 	 * the stack region outside the current limit and
! 	 * we need to reflect that as an access error.
! 	 */
! 	if (map != kernel_map &&
  #ifdef SH4
! 	    /* XXX What is this about?  --thorpej */
! 	    vm != NULL &&
  #endif
+ 	    va >= (vaddr_t) vm->vm_maxsaddr &&
+ 	    va < USRSTACK) {
+ 		if (rv == KERN_SUCCESS) {
+ 			u_int nss;
  
+ 			nss = btoc(USRSTACK - va);
+ 			if (nss > vm->vm_ssize)
+ 				vm->vm_ssize = nss;
+ 		} else if (rv == KERN_PROTECTION_FAILURE)
+ 			rv = KERN_INVALID_ADDRESS;
+ 	}
+ 
+ 	if (rv == KERN_SUCCESS) {
  		va = va_save;
  		SHREG_PTEH = pteh_save;
  		pde_index = pdei(va);
***************
*** 862,900 ****
  
  				SHREG_PTEL = pte & PTEL_VALIDBITS;
  #endif
- 
- 				return;
  			}
  		}
  	}
  
!  nogo:
! 	if (p != NULL) {
! 		struct pcb *pcb = &p->p_addr->u_pcb;
! 		if (pcb->pcb_onfault != 0) {
! 			frame.tf_spc = (int)pcb->pcb_onfault;
  			return;
  		}
  	}
  
- #ifdef	DEBUG
- 	if (trapdebug) {
- 		printf("tlb_handler#NOGO:va(0x%lx),spc=%x\n",
- 		       va, frame.tf_spc);
- 	}
- #endif
  	if (rv == KERN_RESOURCE_SHORTAGE) {
  		printf("UVM: pid %d (%s), uid %d killed: out of swap\n",
  		       p->p_pid, p->p_comm,
  		       p->p_cred && p->p_ucred ?
  		       p->p_ucred->cr_uid : -1);
! 		trapsignal(p, SIGKILL, T_TLBINVALIDR);
  	} else
! 		trapsignal(p, SIGSEGV, T_TLBINVALIDR);
! 
! 	if ((type & T_USER) == 0)
! 		return;
  
! 	if (p != NULL)
! 		userret(p, frame.tf_spc, sticks);
  }
--- 848,895 ----
  
  				SHREG_PTEL = pte & PTEL_VALIDBITS;
  #endif
  			}
  		}
+ 		if (user)
+ 			userret(p, frame.tf_spc, sticks);
+ 		return;
  	}
  
! 	if (user == 0) {
! 		/* Check for copyin/copyout fault. */
! 		if (p != NULL &&
! 		    p->p_addr->u_pcb.pcb_onfault != 0) {
! 			frame.tf_spc = (int) p->p_addr->u_pcb.pcb_onfault;
  			return;
  		}
+ 		goto dopanic;
  	}
  
  	if (rv == KERN_RESOURCE_SHORTAGE) {
  		printf("UVM: pid %d (%s), uid %d killed: out of swap\n",
  		       p->p_pid, p->p_comm,
  		       p->p_cred && p->p_ucred ?
  		       p->p_ucred->cr_uid : -1);
! 		sig = SIGKILL;
  	} else
! 		sig = SIGSEGV;
! #ifdef DEBUG
! 	if (trapdebug) {
! 		printf("tlb_handler: fatal user fault: va 0x%lx, spc 0x%x, "
! 		    "exptype 0x%x (%s)\n", va, frame.tf_spc, exptype,
! 		    p->p_comm);
! 	}
! #endif
! 	trapsignal(p, sig, T_TLBINVALIDR);
! 	userret(p, frame.tf_spc, sticks);
! 	return;
  
!  dopanic:
! 	printf("tlb_handler: va 0x%lx, spc 0x%x, exptype 0x%x (%s)\n",
! 	    va, frame.tf_spc, exptype, p != NULL ? p->p_comm : "no curproc");
! #if defined(DDB)
! 	Debugger();
! #else
! 	panic("tlb_handler");
! #endif
  }