Subject: fixes for libkern, autoconfig, umount, tar; add rkboot.s (#446) Index: ulrem.s,rk.c,conf.c,rkboot.s,rkauto.c,umount.c,tar.c 2.11BSD Description: 1. Standalone mkfs fails to handle a default () answer to the 'file sys size' properly, and create a file system with a small number of inodes. This is caused by a bug in the unsigned long modulo (%) routine ulrem in the C runtime system when compiled for libkern. 2. Booting a 211BSD system of a RK05 drive fails with 'panic: iinit' 3. No BOOTDEV option for 'rk' drives available No BOOTDEV option for 'xp' drives documented 4. First access to a RK05 drive causes a hard error when no drive 0 attached to the system. 5. the umount command returns inverted exit() codes in some cases, thus 0 when a command failed and 1 when it succeeded. 6. tar core dumps when a large file system is written to a tarball. Repeat-By: 1. Create distribution tape with 'make tmscptape', boot with 'tm(0,2)' into standalone mkfs. Use defaults for 'file sys size' and 'bytes per inode', the resulting isize is much smaller than expected. 2-3. Observation. 4. start 211BSD in simh, attach drive rk1 but not rk0 to a file, mount the file system on /dev/rk1h. You'll get one hard error: rk0b: hard error sn2 er=100000 ds=4707 after that access to rk1h works fine. 5. Observation, try 'umount' and check exit status 6. For a full 211BSD system a 'tar c -C / .' ends in a Segmentation fault (core dumped) a tar.core is written somewhere into the file system. Fix: 1. mkfs / ulrem: The ulrem routine in the C runtime system is, like several other long int routines, available in two implementations, with and without floating point instructions. The version with FPP instructions is faster, used when libc is created, and thus by all normal programs. The version without FPP instructions is used when libkern is created, and intended for kernel and standalone code usage. ulrem as of patch level 445 miscalculates the remainder in some cases when the quotient is zero: a= 1; b= 10; ==> a%b= 0; Should be 1 ! a= 99; b= 32766; ==> a%b= 99; a/b=0, but ok The ltoa() function in /sys/pdpstand/sys.c uses u_long modulo and involved in the processing the default file system size in standalone mkfs, the ulrem bug leads to a misdetermination of the file system size when just is answered. To fix this the non-FPP part of ulrem was rewritten. When both operands are non-negative the new code simply jumps to the lrem routine (signed long modulo). When one or both operands are negative, the remainder is calculated quite simplistically by rem = lhs-(lhs/rhs)*rhs using ldiv and lmul. This is slow, but save. Since this case is in practice rare, preformance isn't an issue here. At this point one wonders how 211BSD worked at all with such elementary arithmetic problems. The answer is: - the FPP version of ulrem works correctly - ulrem isn't used by the kernel (unix and netnix) - up to patch 434 the standalone programs where mistakenly linked against libc rather libkern, thus used the FPP version, which works correctly. - /boot does link against ulrem but apparently seems not to be affected much.... 2. Booting 211BSD of a RK05 drive: During kernel startup the root file system and the swap space must be accessed, well before autoconfig is executed. The kernel calls in /sys/sys/init_main.c (void)(*bdevsw[major(bootdev)].d_root)(bootcsr); which executes the 'root attach' function of the driver. The rk.c driver doesn't provide this function, the boot device attach fails, and the kernel panics with 'iinit'. The fix is simple: - rk.c now implements rkroot(), the root attach function - the block device switch bdevsw[] entry for rk is updated. 3a. No BOOTDEV option for 'rk' drives available During a 'reboot' the kernel finally calls doboot(). The directory /sys/conf/boot hold implementations for a variety of boot devices, but not for the admittedly exotic case of rk05 boot device. Fix: rkboot.s is added with this patch 3b. No BOOTDEV option for 'xp' drives documented The 'BOOTDEV rm' option can be used in case of a xp boot device. RM and RP drives have different ..uboot.s implementations because in this case knowledge of the disk geometry is needed, but the primary boot block loader ..boot.s only loads lbn 0 and is thus common for RM and RP drives. Updated the comment on the 'BOOTDEV rm' line and added a 'BOOTDEV rk' line to the GENERIC and non-networking (NONET) configurations shipped with the distribution. 4. First access to RK drive: The rkauto() function used by autoconfig to probe for RK11 controllers executes a 'drive reset' on drive 0: stuff(RKCS_IDE | RKCS_DRESET | RKCS_GO, (&(addr->rkcs))); If no drive 0 is attached, the RK11 controller will set the hard error bit in RKER and refuse all further commands until a control reset is executed. In a configuration with no drive 0 attached, the autoconfig will thus detect and properly attach the RK11 controller, but leave it in an error state. The next request, typically a read caused by a mount, will fail. The rk.c driver does a 'control reset' and retry in this case, so the mount will succeed, fortunately. autoconfig should leave devices in operational state, so extra code was added to rkauto() to execute 'control reset' in the case described above. 5. umount exit status: The main() of umount.c accumulates the return codes for calls of umountfs(), intention is to return an exit status of 1 of any of the unmounts fails: for (errs = 0; *argv != NULL; ++argv) if (umountfs(*argv) == 0) errs = 1; umountfs returns 0 in case of success, 1 in case of error, so the if() should use != and not ==. 6. tar memory faults when large tarballs are created: In order to be able to reconstruct files with several hard links from a tarball tar has to keep track of all ordinary files with a link count > 1. A struct linkbuf is malloc'ed for each file with additional hard links. Eventually the data segment expands into the stack segment and tar will core dump. As such, this is a principal limitation of tar on a 16 bit system. It's worth mentioning that the tar man pages state: DIAGNOSTICS Complaints if enough memory is not available to hold the link tables. The getmem() function indeed checks whether the malloc() succeeded. However, the failure mechanism is more subtle. First the data area expands beyond 140000, than the stack area tries to grow below 160000, which is not possible. In all tests done tar crashed due to a failure to expand the stack. Last but not least: because tar changes the current working directory to the directory it's currently processing the tar.core ends up in this directory, and not the cwd where tar was started in. Now the good news: struct linkbuf a much larger than needed: struct linkbuf { ino_t inum; dev_t devnum; int count; char pathname[NAMSIZ]; struct linkbuf *nextp; }; It allocates NAMSIZ bytes for the path name, NAMSIZ is #defined'ed to 100 in tar.c. This is way larger than the typical file name size, thus wasting precious space. A simple modification is to change this to char *pathname; and allocate with a separate malloc a minimally sized buffer for the pathname. This reduces the memory consumption to a point where a tarball with a full 211BSD system disk can be created. The test system had 201 ordinary files with multiple links, the tar needs 11.2 kB for linkbuf's, the data area grows to about 120000. The original tar needed more than twice as much space for linkbuf structures. The patches above where submitted by Walter F.J. Mueller The following files are affected by this update: modified: /usr/src/lib/libc/pdp/crt/ulrem.s modified: /usr/src/sys/pdpuba/rk.c modified: /usr/src/sys/pdp/conf.c added: /usr/src/sys/conf/boot/rkboot.s modified: /usr/src/sys/conf/boot/rmboot.s modified: /usr/src/sys/conf/GENERIC modified: /usr/src/sys/conf/NONET modified: /usr/src/sys/autoconfig/rkauto.c modified: /usr/src/sbin/umount/umount.c modified: /usr/src/bin/tar.c To apply this update save the patch (for example /tmp/446.patch): # The rkboot.s should not exist but rm -f to be certain it is absent rm -f /usr/src/sys/conf/boot/rkboot.s patch -p0 < /tmp/446.patch cd /usr/src/lib/libkern make clean make make install make clean cd /sys/pdpstand make clean make cp boot disklabel toyset / cd /usr/src/sys/autoconfig make clean make make install make clean cd /usr/src/sbin/umount make make install make clean cd /usr/src/bin make tar install -s -m 751 -g staff tar /bin You may wish to recompile the kernel (both the current and the GENERIC) but that can be deferred if desired. ---------------------------446.patch cut here---------------------- *** /usr/src/sys/pdpuba/rk.c.old Sat Jan 18 16:44:45 1997 --- /usr/src/sys/pdpuba/rk.c Sat Dec 27 17:22:21 2008 *************** *** 43,48 **** --- 43,55 ---- */ #endif + /* added rkroot() to support RK11 based systems (2008-12-14,wfjm) */ + + rkroot() + { + rkattach((struct rkdevice *)0177400, 0); + } + rkattach(addr, unit) struct rkdevice *addr; { *** /usr/src/sys/conf/boot/rmboot.s.old Fri Apr 19 21:03:43 1991 --- /usr/src/sys/conf/boot/rmboot.s Sat Dec 27 17:27:04 2008 *************** *** 50,56 **** / Otherwise, use a BOOT opcode, if available; / if necessary, read in block 0 to location 0 "by hand". ! / rm02/3/5 bootstrap - salkind@nyu WC = -256. READ = 70 --- 50,56 ---- / Otherwise, use a BOOT opcode, if available; / if necessary, read in block 0 to location 0 "by hand". ! / rm02/3/5 rp06 bootstrap - salkind@nyu WC = -256. READ = 70 *** /usr/src/sys/conf/boot/rkboot.s.old Sat Dec 27 17:28:29 2008 --- /usr/src/sys/conf/boot/rkboot.s Fri Dec 26 12:51:47 2008 *************** *** 0 **** --- 1,77 ---- + /* + * SCCS id @(#)rkboot.s 1.0 (2.11BSD) 12/26/2008 + */ + #include "localopts.h" + + / The boot options and device are placed in the last SZFLAGS bytes + / at the end of core for the bootstrap. + ENDCORE= 160000 / end of core, mem. management off + SZFLAGS= 6 / size of boot flags + BOOTOPTS= 2 / location of options, bytes below ENDCORE + BOOTDEV= 4 / boot unit + CHECKWORD= 6 + + .globl _doboot, hardboot, _bootcsr + .text + _doboot: + mov 4(sp),r4 / boot options + mov 2(sp),r3 / boot device + + #ifndef KERN_NONSEP + / If running separate I/D, need to turn off memory management. + / Call the routine unmap in low text, after setting up a jump + / in low data where the PC will be pointing. + .globl unmap + mov $137,*$unmap+2 / jmp *$hardboot + mov $hardboot,*$unmap+4 + jmp unmap + / "return" from unmap will be to hardboot in data + .data + #else + / Reset to turn off memory management + reset + #endif + + / On power fail, hardboot is the entry point (map is already off) + / and the args are in r4 (RB_POWRFAIL), r3 (rootdev) + + hardboot: + mov r4, ENDCORE-BOOTOPTS + ash $-3,r3 / shift out the partition number + bic $!7,r3 / save only the drive number + mov r3, ENDCORE-BOOTDEV + com r4 / if CHECKWORD == ~bootopts, flags are believed + mov r4, ENDCORE-CHECKWORD + 1: + reset + + / The remainder of the code is dependent on the boot device. + / If you have a bootstrap ROM, just jump to the correct entry. + / Otherwise, use a BOOT opcode, if available; + / if necessary, read in block 0 to location 0 "by hand". + + / Bootstrap for rk05 drive - wfjm + + WC = -256. + + rkcs = 4 / offset from base csr: control & status + rkda = 12 / desired disk address + + / RK05 constants. + iocom = 005 / read + go + + / initialize rk + + mov _bootcsr,r1 / bootcsr points to rkcs + add $rkda-rkcs,r1 / r1 -> rkda + mov ENDCORE-BOOTDEV,r2 / drive number + ash $13,r2 / drsel is in bits 15:13 + mov r2,(r1) / setup rkda (disk address) + clr -(r1) / clear rkba (memory address) + mov $WC,-(r1) / setup rkwc (transfer length) + mov $iocom,-(r1) / issue read+go + + 1: tstb (r1) + bge 1b / wait for iocom to complete + mov ENDCORE-BOOTDEV,r0 + clr pc *** /usr/src/sys/conf/GENERIC.old Sat Oct 13 18:20:02 2001 --- /usr/src/sys/conf/GENERIC Sat Dec 27 17:17:24 2008 *************** *** 52,59 **** #BOOTDEV hk6 # rk06 boot device #BOOTDEV hk7 # rk07 boot device #BOOTDEV ra # MSCP boot device #BOOTDEV rl # rl01/02 boot device ! #BOOTDEV rm # rm02/03/05 boot device #BOOTDEV br # Eaton BR1537/BR1711 boot device #BOOTDEV sc11 # Emulex SC11/B boot device #BOOTDEV sc21 # Emulex SC21 boot device --- 52,60 ---- #BOOTDEV hk6 # rk06 boot device #BOOTDEV hk7 # rk07 boot device #BOOTDEV ra # MSCP boot device + #BOOTDEV rk # rk05 boot device #BOOTDEV rl # rl01/02 boot device ! #BOOTDEV rm # rm02/03/05 rp06 boot device #BOOTDEV br # Eaton BR1537/BR1711 boot device #BOOTDEV sc11 # Emulex SC11/B boot device #BOOTDEV sc21 # Emulex SC21 boot device *** /usr/src/sys/conf/NONET.old Wed Nov 22 21:26:15 1995 --- /usr/src/sys/conf/NONET Sat Dec 27 17:19:22 2008 *************** *** 50,57 **** #BOOTDEV hk6 # rk06 boot device #BOOTDEV hk7 # rk07 boot device BOOTDEV ra # MSCP boot device #BOOTDEV rl # rl01/02 boot device ! #BOOTDEV rm # rm02/03/05 boot device #BOOTDEV br # Eaton BR1537/BR1711 boot device #BOOTDEV sc11 # Emulex SC11/B boot device #BOOTDEV sc21 # Emulex SC21 boot device --- 50,58 ---- #BOOTDEV hk6 # rk06 boot device #BOOTDEV hk7 # rk07 boot device BOOTDEV ra # MSCP boot device + #BOOTDEV rk # rk05 boot device #BOOTDEV rl # rl01/02 boot device ! #BOOTDEV rm # rm02/03/05 rp06 boot device #BOOTDEV br # Eaton BR1537/BR1711 boot device #BOOTDEV sc11 # Emulex SC11/B boot device #BOOTDEV sc21 # Emulex SC21 boot device *** /usr/src/sys/autoconfig/rkauto.c.old Wed Dec 30 17:56:32 1992 --- /usr/src/sys/autoconfig/rkauto.c Sat Dec 27 17:25:32 2008 *************** *** 16,23 **** --- 16,39 ---- struct rkdevice *addr; int vector; { + extern int errno; + stuff(RKCS_IDE | RKCS_DRESET | RKCS_GO, (&(addr->rkcs))); DELAY(10L); stuff(0, (&(addr->rkcs))); + + /* in case no drive 0 was attached, the DRESET above will leave + * the controller in an error state. The grab below checks for this + * and the stuff below executes a RESET (control reset) to clear + * the error condition. Without this the first access to any drive + * will fail when the system is up. + * 2008-12-25 wfjm + */ + errno = 0; + if ((grab(&(addr->rker)) & RKER_DRE) && !errno) { + stuff(RKCS_RESET | RKCS_GO, (&(addr->rkcs))); + DELAY(10L); + } + return(ACP_IFINTR); } *** /usr/src/sys/pdp/conf.c.old Sat Oct 13 18:20:03 2001 --- /usr/src/sys/pdp/conf.c Sat Dec 27 17:21:22 2008 *************** *** 23,34 **** #include "rk.h" #if NRK > 0 ! int rkopen(), rkstrategy(); daddr_t rksize(); #define rkclose nulldev #else #define rkopen nodev #define rkclose nodev #define rkstrategy nodev #define rksize NULL #endif --- 23,35 ---- #include "rk.h" #if NRK > 0 ! int rkopen(), rkroot(), rkstrategy(); daddr_t rksize(); #define rkclose nulldev #else #define rkopen nodev #define rkclose nodev + #define rkroot nulldev #define rkstrategy nodev #define rksize NULL #endif *************** *** 192,198 **** raopen, raclose, rastrategy, raroot, rasize, 0, /* rk = 6 */ ! rkopen, rkclose, rkstrategy, nulldev, rksize, 0, /* rl = 7 */ rlopen, rlclose, rlstrategy, rlroot, rlsize, --- 193,199 ---- raopen, raclose, rastrategy, raroot, rasize, 0, /* rk = 6 */ ! rkopen, rkclose, rkstrategy, rkroot, rksize, 0, /* rl = 7 */ rlopen, rlclose, rlstrategy, rlroot, rlsize, *** /usr/src/bin/tar.c.old Tue Oct 3 12:40:03 1989 --- /usr/src/bin/tar.c Sat Dec 27 17:29:22 2008 *************** *** 55,61 **** ino_t inum; dev_t devnum; int count; ! char pathname[NAMSIZ]; struct linkbuf *nextp; }; --- 55,61 ---- ino_t inum; dev_t devnum; int count; ! char *pathname; struct linkbuf *nextp; }; *************** *** 604,615 **** } lp = (struct linkbuf *) getmem(sizeof(*lp)); if (lp != NULL) { ! lp->nextp = ihead; ! ihead = lp; ! lp->inum = stbuf.st_ino; ! lp->devnum = stbuf.st_dev; ! lp->count = stbuf.st_nlink - 1; ! strcpy(lp->pathname, longname); } } blocks = (stbuf.st_size + (TBLOCK-1)) / TBLOCK; --- 604,618 ---- } lp = (struct linkbuf *) getmem(sizeof(*lp)); if (lp != NULL) { ! lp->pathname = getmem(strlen(longname)+1); ! if (lp->pathname != NULL) { ! lp->nextp = ihead; ! ihead = lp; ! lp->inum = stbuf.st_ino; ! lp->devnum = stbuf.st_dev; ! lp->count = stbuf.st_nlink - 1; ! strcpy(lp->pathname, longname); ! } } } blocks = (stbuf.st_size + (TBLOCK-1)) / TBLOCK; *** /usr/src/lib/libc/pdp/crt/ulrem.s.old Sun Jun 6 20:47:00 1993 --- /usr/src/lib/libc/pdp/crt/ulrem.s Sat Dec 27 17:30:34 2008 *************** *** 6,11 **** --- 6,12 ---- * Version Date Modification * 0.0 02Feb91 1. Initial inspiration struck. * 1.0 05Jun93 2. Released into the Public Domain. + * 1.1 23Dec08 Revised, corrected KERNEL (no-FPP) code wfjm */ #include "DEFS.h" *************** *** 56,62 **** --- 57,105 ---- #else /* * ulrem for the kernel (uses only fixed point - no FP) + * + * uses lrem if lhs and rhs non-negative, otherwise reminder is calculated + * 'brute-force' via lhs-(lhs/rhs)*rhs using uldiv and lmul. (wfjm) */ + + .globl ulrem + .globl lrem, uldiv, lmul + ulrem: + ENTRY(ulrem) + tst 2(sp) / hi(lhs) + bmi 1f / if negative, a-(a/b)*b handling + tst 6(sp) / hi(rhs) + bmi 1f / if negative, a-(a/b)*b handling + jmp lrem / if lhs and rhs >=0, use lrem + + 1: mov r5,-(sp) / need frame pointer + mov sp,r5 / setup frame pointer + mov 12(r5),-(sp) / lo(rhs) for * + mov 10(r5),-(sp) / hi(rhs) + mov 12(r5),-(sp) / lo(rhs) for / + mov 10(r5),-(sp) / hi(rhs) + mov 6(r5),-(sp) / lo(lhs) for / + mov 4(r5),-(sp) / hi(lhs) + jsr pc,uldiv / do lhs/rhs + add $10,sp / clean up stack + mov r1,-(sp) / lo(lhs/rhs) + mov r0,-(sp) / hi(lhs/rhs) + jsr pc,lmul / do (lhs/rhs)*rhs + add $10,sp / clean up stack + mov r1,-(sp) / lo((lhs/rhs)*rhs)) + mov r0,-(sp) / hi((lhs/rhs)*rhs)) + mov 6(r5),r1 / lo(lhs) + mov 4(r5),r0 / hi(lhs) + sub (sp)+,r0 / hi(lhs-((lhs/rhs)*rhs)) + sub (sp)+,r1 / lo(lhs-((lhs/rhs)*rhs)) + sbc r0 / handle carry from lo to hi part + mov (sp)+,r5 / restore r5 + rts pc + + #ifdef NEVER + /* the old code here for reference and further debugging. + * a 1%10 produced a 0 instead of a 1. + */ .globl ulrem ulrem: ENTRY(ulrem) *************** *** 131,136 **** --- 174,180 ---- mov (sp)+,r3 mov (sp)+,r2 rts pc + #endif NEVER #endif KERNEL /* *** /usr/src/sbin/umount/umount.c.old Tue Jan 16 23:25:05 1996 --- /usr/src/sbin/umount/umount.c Sat Dec 27 17:29:58 2008 *************** *** 67,73 **** int argc; register char *argv[]; { ! int all, ch, errs; /* Start disks transferring immediately. */ sync(); --- 67,73 ---- int argc; register char *argv[]; { ! int all, ch, errs=0; /* Start disks transferring immediately. */ sync(); *************** *** 108,114 **** errs = umountall(); } else for (errs = 0; *argv != NULL; ++argv) ! if (umountfs(*argv) == 0) errs = 1; exit(errs); } --- 108,114 ---- errs = umountall(); } else for (errs = 0; *argv != NULL; ++argv) ! if (umountfs(*argv) != 0) errs = 1; exit(errs); } *** /VERSION.old Tue Dec 26 14:53:42 2006 --- /VERSION Sat Dec 27 17:32:05 2008 *************** *** 1,5 **** ! Current Patch Level: 445 ! Date: December 26, 2006 2.11 BSD ============ --- 1,5 ---- ! Current Patch Level: 446 ! Date: December 27, 2008 2.11 BSD ============