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; } /*