Subject: stack expansion bug on KDJ-11 cpus (#150)
Index:	sys/pdp/{trap.c,mch_start.s,mch_vars.s} 2.11BSD

Description:
	There is a serious bug in the stack expansion which only affects
	KDJ-11 based systems (pdp-11/53,73,83,84,93,94).  The fault is
	not actually in the stack expansion code but apparently in the KDJ-11
	trap generation.  The fix below is a workaround.
	
Repeat-By:
	Originally this bug was spotted in "rcp".  Depending how large
	of an environment i had 'rcp' would dump core in the middle of
	the libc.a routine "ldiv".  If you have system programs that
	work most of the time but unexpectedly dump core on a KDJ-11 system 
	you may have also experienced the bug.

	The following code will demonstrate the bug, i single
	stepped the program after doing a "as stackbug.s".   The value
	'175002' is dependent on how many environment variables are
	present.
	
	Normally a program starts execution with 030 "clicks" (03000 bytes) 
	of stack space.  This means that the valid stack range is from
	0175000 thru 0177776 inclusive (with enviroment at the top).

	setl
	setd
	mov	$175002,sp	/ set stack to one word from the end
	movif	l1,fr0
	movfi	fr0,-(sp)	/ do a double word store operation
	rts	pc
	.data
l1:	0; 3777

	Note that the 'movfi' instruction segment faults.  It should not
	have faulted because the kernel should have grown the stack
	to include the address 0174776 and then restarted the instruction.
	
	If you are on a KDJ-11 based system and a fault does not occur then 
	you may need to adjust the constant '175002' to a lower value (i.e.
	the initial stack allocation may be larger than 03000).  What you
	want to load 'sp' with is the address which is one word before the
	end of the stack segment.

Fix:
	The problem  is that the KDJ-11 processes the double word store
	of the 'movfi' differently than the 11/44 or 11/70.  On other
	systems (such as the 11/44) the first word is stored successfully
	at 0175000 then the program faults when trying to access 0174776
	but SP is left at 0174776 with SSR1 (memory management
	status register 1) indicating that 'sp' was decremented by 4.  The
	kernel adjusts 'sp', grows the stack and restarts the instruction.
	The 'movfi' then completes successfully.

	On a KDJ-11 cpu the story is different.  The fault is generated
	as expected BUT 'SP' IS STILL 0175002!  The kernel sees that 'sp'
	is still within the "valid stack region" and DOES NOT grow the
	stack at all.   SSR1 indicates that no registers were modified
	so the kernel does no adjustment of 'sp'.  The instruction is
	NOT restarted and a SIGSEGV signal is sent to the program.

	The problem appears to be only when doing FP instructions, fixed point 
	operations do not experience any difficulty.  The instruction
	"cmp -(sp),-(sp)" for example is handled correctly.

	The fix below changes 3 files in /sys/pdp:

	mch_start.s - check for a KDJ-11 cpu and set a flag.  Also, in
		      anticipation of "NONSEPARATE" going away (doesn't
		      make sense to support non-split applications when
		      running the required split I/D kernel) the one #ifdef
		      NONSEPARATE in this module was removed.  Also some
		      minor cleanup of the comments was done.

	mch_vars.s  - declare the global variable "_kdj11".

	trap.c	    - a check for the cpu being a KDJ-11 is made when
		      processing a segmentation trap.  The stack pointer
		      is locally adjusted down by 4 before calling the 'grow'
		      routine.  Arguably a check for the faulting instruction
		      being FP should be made.

	Save the text below into /tmp/p and do a "patch -p0 < /tmp/p'.  Then
	if you have a KDJ-11 system recompile and install a new kernel.
================================cut here=================================
*** /sys/pdp/mch_start.s.old	Thu Dec 24 17:16:54 1992
--- /sys/pdp/mch_start.s	Mon Aug 23 19:16:05 1993
***************
*** 3,9 ****
   * All rights reserved.  The Berkeley software License Agreement
   * specifies the terms and conditions for redistribution.
   *
!  *	@(#)mch_start.s	1.3 (2.11BSD GTE) 12/24/92
   */
  
  #include "DEFS.h"
--- 3,9 ----
   * All rights reserved.  The Berkeley software License Agreement
   * specifies the terms and conditions for redistribution.
   *
!  *	@(#)mch_start.s	1.4 (2.11BSD GTE) 8/23/93
   */
  
  #include "DEFS.h"
***************
*** 96,103 ****
  	clr	-(sp)			/   mode, location zero,
  	rtt				/   and book ...
  
! 
! .data
  /*
   * Icode is copied out to process 1 to exec /etc/init.
   * If the exec fails, process 1 exits.
--- 96,102 ----
  	clr	-(sp)			/   mode, location zero,
  	rtt				/   and book ...
  
! 	.data
  /*
   * Icode is copied out to process 1 to exec /etc/init.
   * If the exec fails, process 1 exits.
***************
*** 127,148 ****
  	0				/ boot major#,unit
  _bootcsr:
  	0				/ csr of booting controller
! .text
  
- 
  /*
!  * Check out (and if necessary, set up) the hardware.  A lot of the work done
!  * here is to figure out what we've really got.  In many cases even if a
!  * certain capability is defined (separate I/D, etc.), we check to see if it's
!  * really there.  This allows us to distribute a generic kernel that will run
!  * on any processor but still take advantage of hardware that is present.
!  * This is also a plus for a site which wants to maintain one kernel for a
!  * number of different processors.
!  *
!  * Might as well use the cpu type from /boot after all the hoops it jumped
!  * thru to figure it out.  No sense in duplicating that code here in the
!  * kernel.  /boot also stuffed the right bits into the MSCR register to 
!  * disable cache and unibus traps.
   */
  hardprobe:
  	mov	$1f,nofault
--- 126,141 ----
  	0				/ boot major#,unit
  _bootcsr:
  	0				/ csr of booting controller
! 	.text
  
  /*
!  * Determine a couple of facts about the hardware and finishing setting
!  * up what 'boot' hasn't done already.
! 
!  * We use the cpu type passed thru from /boot.  No sense in duplicating 
!  * that code here in the kernel.  We do have to repeat the KDJ-11 test
!  * (for use in trap.c) though.  /boot also stuffed the right bits into 
!  * the MSCR register to disable cache and unibus traps.
   */
  hardprobe:
  	mov	$1f,nofault
***************
*** 160,184 ****
  	beq	1f
  	incb	_ubmap
  1:
- 
- #ifdef NONSEPARATE
- 	/*
- 	 * Don't attempt to determine whether we've got separate I/D
- 	 * (but just in case we do, we must force user unseparated
- 	 * because boot will have turned on separation if possible).
- 	 */
- 	bic	$1,SSR3
- #else
  	bit	$1,SSR3			/ Test for separate I/D capability
  	beq	2f
  	incb	_sep_id
- #endif
  2:
  		/ Test for stack limit register; set it if present.
  	mov	$1f,nofault
  	mov	$intstk-256.,STACKLIM
  1:
! 
  #ifdef ENABLE34
  	/*
  	 * Test for an ENABLE/34.  We are very cautious since the ENABLE's
--- 153,173 ----
  	beq	1f
  	incb	_ubmap
  1:
  	bit	$1,SSR3			/ Test for separate I/D capability
  	beq	2f
  	incb	_sep_id
  2:
  		/ Test for stack limit register; set it if present.
  	mov	$1f,nofault
  	mov	$intstk-256.,STACKLIM
  1:
! 	clr	_kdj11
! 	mov	$1f,nofault
! 	mfpt
! 	cmp	r0,$5			/ KDJ-11 returns 5 (11/44 returns 1)
! 	bne	1f
! 	mov	r0,_kdj11
! 1:
  #ifdef ENABLE34
  	/*
  	 * Test for an ENABLE/34.  We are very cautious since the ENABLE's
*** /sys/pdp/mch_vars.s.old	Sat Jul  4 00:26:50 1992
--- /sys/pdp/mch_vars.s	Mon Aug 23 19:08:22 1993
***************
*** 3,9 ****
   * All rights reserved.  The Berkeley software License Agreement
   * specifies the terms and conditions for redistribution.
   *
!  *	@(#)mch_vars.s	1.1 (2.10BSD Berkeley) 6/11/88
   */
  #include "DEFS.h"
  #include "../machine/mch_iopage.h"
--- 3,9 ----
   * All rights reserved.  The Berkeley software License Agreement
   * specifies the terms and conditions for redistribution.
   *
!  *	@(#)mch_vars.s	1.2 (2.11BSD GTE) 8/23/93
   */
  #include "DEFS.h"
  #include "../machine/mch_iopage.h"
***************
*** 13,18 ****
--- 13,19 ----
  INT(GLOBAL, _fpp, 0)			/ we have a floating point processor
  INT(GLOBAL, _ubmap, 0)			/ we have a unibus map
  INT(GLOBAL, _cputype, 0)		/ cpu type
+ INT(GLOBAL, _kdj11, 0)			/ cpu is a KDJ-11
  CHAR(GLOBAL, _sep_id, 0)		/ we have a separate I&D CPU
  
  #ifdef ENABLE34
*** /sys/pdp/trap.c.old	Sat Dec 26 19:05:02 1992
--- /sys/pdp/trap.c	Mon Aug 23 19:06:46 1993
***************
*** 3,9 ****
   * All rights reserved.  The Berkeley software License Agreement
   * specifies the terms and conditions for redistribution.
   *
!  *	@(#)trap.c	1.2 (2.11BSD GTE) 12/24/92
   */
  
  #include "param.h"
--- 3,9 ----
   * All rights reserved.  The Berkeley software License Agreement
   * specifies the terms and conditions for redistribution.
   *
!  *	@(#)trap.c	1.3 (2.11BSD GTE) 8/23/93
   */
  
  #include "param.h"
***************
*** 18,24 ****
  #include "proc.h"
  #include "vm.h"
  
! extern int fpp;
  
  #ifdef INET
  extern int netoff;
--- 18,24 ----
  #include "proc.h"
  #include "vm.h"
  
! extern int fpp, kdj11;
  
  #ifdef INET
  extern int netoff;
***************
*** 219,235 ****
  	 * is not the case and the routine backup/mch.s may fail.
  	 * The classic example is on the instruction
  	 *	cmp	-(sp),-(sp)
  	 */
  	case T_SEGFLT + USER:
  		{
! 			caddr_t osp;
  
! 			osp = sp;
! 			if (backup(u.u_ar0) == 0)
! 				if (!u.u_onstack && grow((u_int)osp))
! 					goto out;
! 			i = SIGSEGV;
! 			break;
  		}
  
  	/*
--- 219,250 ----
  	 * is not the case and the routine backup/mch.s may fail.
  	 * The classic example is on the instruction
  	 *	cmp	-(sp),-(sp)
+ 	 *
+ 	 * The KDJ-11 (11/53,73,83,84,93,94) handles the trap when doing
+ 	 * a double word store differently than the other pdp-11s.  When
+ 	 * doing:
+ 	 *	setl
+ 	 *	movfi fr0,-(sp)
+ 	 * and the stack segment becomes invalid part way thru then the
+ 	 * trap is generated (as expected) BUT 'sp' IS NOT LEFT DECREMENTED!
+ 	 * The 'grow' routine sees that SP is still within the (valid) stack
+ 	 * segment and does not extend the stack, resulting in a 'segmentation
+ 	 * violation' rather than a successfull floating to long store.
+ 	 * The "fix" is to pretend that SP is 4 bytes lower than it really
+ 	 * is (for KDJ-11 systems only) when calling 'grow'.
  	 */
  	case T_SEGFLT + USER:
  		{
! 		caddr_t osp;
  
! 		osp = sp;
! 		if (kdj11)
! 			osp -= 4;
! 		if (backup(u.u_ar0) == 0)
! 			if (!u.u_onstack && grow((u_int)osp))
! 				goto out;
! 		i = SIGSEGV;
! 		break;
  		}
  
  	/*
