pass flags through close
[unix-history] / usr / src / sys / vax / stand / format.c
index b5e7598..42e38d4 100644 (file)
@@ -1,17 +1,37 @@
-/*     format.c        6.2     83/09/18        */
+/*
+ * Copyright (c) 1980, 1986 Regents of the University of California.
+ * All rights reserved.  The Berkeley software License Agreement
+ * specifies the terms and conditions for redistribution.
+ */
+
+#ifndef lint
+char copyright[] =
+"@(#) Copyright (c) 1980, 1986 Regents of the University of California.\n\
+ All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)format.c   7.6 (Berkeley) %G%";
+#endif /* not lint */
 
 /* 
  * Standalone program to do media checking
  * and record bad block information on any 
  * disk with the appropriate driver and RM03-style headers.
 
 /* 
  * Standalone program to do media checking
  * and record bad block information on any 
  * disk with the appropriate driver and RM03-style headers.
+ * TODO:
+ *     add new bad sectors to bad-sector table when formatting by track
+ *             (rearranging replacements ala bad144 -a)
+ *     multi-pass format for disks with skip-sector capability
  */
  */
-#include "../h/param.h"
-#include "../h/fs.h"
-#include "../h/inode.h"
-#include "../h/dkbad.h"
-#include "../h/vmmac.h"
+#include "sys/param.h"
+#include "sys/dkbad.h"
+#include "sys/vmmac.h"
+#include "sys/disklabel.h"
+
+#include "../include/cpu.h"
+#include "../include/mtpr.h"
 
 
-#include "saio.h"
+#include "stand/saio.h"
 #include "savax.h"
 
 #define MAXBADDESC     126             /* size of bad block table */
 #include "savax.h"
 
 #define MAXBADDESC     126             /* size of bad block table */
@@ -22,7 +42,8 @@
 #define SSERR          0
 #define BSERR          1
 
 #define SSERR          0
 #define BSERR          1
 
-#define SSDEV          ((ioctl(iob[fd-3], SAIOSSDEV, (char *)0) == 0))
+#define SSDEV(fd)      (ioctl((fd), SAIOSSDEV, (char *)0) == 0)
+#define MAXECCBITS     3
 
 struct sector {
        u_short header1;
 
 struct sector {
        u_short header1;
@@ -31,6 +52,7 @@ struct sector {
 };
 
 struct dkbad dkbad;            /* bad sector table */
 };
 
 struct dkbad dkbad;            /* bad sector table */
+struct dkbad oldbad;           /* old bad sector table */
 struct dkbad sstab;            /* skip sector table */
 
 #define        NERRORS         6
 struct dkbad sstab;            /* skip sector table */
 
 #define        NERRORS         6
@@ -41,153 +63,253 @@ errornames[NERRORS] = {
 #define        FE_WCE          1
        "Write check",
 #define        FE_ECC          2
 #define        FE_WCE          1
        "Write check",
 #define        FE_ECC          2
-       "ECC",
+       "Hard ECC",
 #define        FE_HARD         3
        "Other hard",
 #define        FE_TOTAL        4
 #define        FE_HARD         3
        "Other hard",
 #define        FE_TOTAL        4
-       "Total",
+       "Marked bad",
 #define        FE_SSE          5
 #define        FE_SSE          5
-       "Skip sector",
+       "Skipped",
 };
 
 int    errors[NERRORS];        /* histogram of errors */
 int    pattern;
 };
 
 int    errors[NERRORS];        /* histogram of errors */
 int    pattern;
+int    maxeccbits;
+
+/*
+ * Purdue/EE severe burnin patterns.
+ */
+unsigned short ppat[] = {
+0xf00f, 0xec6d, 0031463,0070707,0133333,0155555,0161616,0143434,
+0107070,0016161,0034343,0044444,0022222,0111111,0125252, 052525,
+0125252,0125252,0125252,0125252,0125252,0125252,0125252,0125252,
+#ifndef        SHORTPASS
+0125252,0125252,0125252,0125252,0125252,0125252,0125252,0125252,
+ 052525, 052525, 052525, 052525, 052525, 052525, 052525, 052525,
+#endif
+ 052525, 052525, 052525, 052525, 052525, 052525, 052525, 052525
+ };
+
+#define        NPT     (sizeof (ppat) / sizeof (short))
+int    maxpass, npat;  /* subscript to ppat[] */
+int    severe;         /* nz if running "severe" burnin */
+int    ssdev;                  /* device supports skip sectors */
+int    startcyl, endcyl, starttrack, endtrack;
+int    nbads;                  /* subscript for bads */
+daddr_t        bads[2*MAXBADDESC];     /* Bad blocks accumulated */
 
 char   *malloc();
 
 char   *malloc();
+int    qcompar();
 char   *prompt();
 char   *prompt();
+daddr_t        badsn();
 extern int end;
 
 main()
 {
 extern int end;
 
 main()
 {
-       register int sector, sn;
-       int lastsector, tracksize;
-       int unit, fd, resid, i, trk, cyl, debug;
-       struct st st;
+       register struct sector *hdr;
+       register int sector, sn, i;
+       struct disklabel dl;
        struct sector *bp, *cbp;
        struct sector *bp, *cbp;
-       char *cp;
+       int lastsector, tracksize, rtracksize;
+       int unit, fd, resid, trk, cyl, debug, pass;
+       char *cp, *rbp, *rcbp;
 
        printf("Disk format/check utility\n\n");
 
 
        printf("Disk format/check utility\n\n");
 
+       /* enable the cache, as every little bit helps */
+       switch (cpu) {
+       case VAX_8600:
+               mtpr(CSWP, 3);
+               break;
+       case VAX_8200:
+       case VAX_750:
+               mtpr(CADR, 0);
+               break;
+       case VAX_780:
+               mtpr(SBIMT, 0x200000);
+               break;
+       }
+
 again:
 again:
-       cp = prompt("Enable debugging (1=bse, 2=ecc, 3=bse+ecc)? ");
+       nbads = 0;
+       cp = prompt("Enable debugging (0=none, 1=bse, 2=ecc, 3=bse+ecc)? ");
        debug = atoi(cp);
        if (debug < 0)
                debug = 0;
        for (i = 0; i < NERRORS; i++)
                errors[i] = 0;
        fd = getdevice();
        debug = atoi(cp);
        if (debug < 0)
                debug = 0;
        for (i = 0; i < NERRORS; i++)
                errors[i] = 0;
        fd = getdevice();
-       ioctl(fd, SAIODEVDATA, &st);
+       ioctl(fd, SAIODEVDATA, &dl);
        printf("Device data: #cylinders=%d, #tracks=%d, #sectors=%d\n",
        printf("Device data: #cylinders=%d, #tracks=%d, #sectors=%d\n",
-         st.ncyl, st.ntrak, st.nsect);
+           dl.d_ncylinders, dl.d_ntracks, dl.d_nsectors);
+       ssdev = SSDEV(fd);
+       if (ssdev) {
+               ioctl(fd, SAIOSSI, (char *)0);  /* set skip sector inhibit */
+               dl.d_nsectors++;
+               dl.d_secpercyl += dl.d_ntracks;
+               printf("(not counting skip-sector replacement)\n");
+       }
+       getrange(&dl);
        if (getpattern())
                goto again;
        printf("Start formatting...make sure the drive is online\n");
        ioctl(fd, SAIONOBAD, (char *)0);
        if (getpattern())
                goto again;
        printf("Start formatting...make sure the drive is online\n");
        ioctl(fd, SAIONOBAD, (char *)0);
-       ioctl(fd, SAIOECCLIM, (char *)0);
+       ioctl(fd, SAIORETRIES, (char *)0);
+       ioctl(fd, SAIOECCLIM, (char *)maxeccbits);
        ioctl(fd, SAIODEBUG, (char *)debug);
        ioctl(fd, SAIODEBUG, (char *)debug);
-       if (SSDEV) {
-               ioctl(fd, SAIOSSI, (char *)0);  /* set skip sector inhibit */
-               st.nsect++;
-               st.nspc += st.ntrak;
-       }
-       tracksize = sizeof (struct sector) * st.nsect;
+       tracksize = sizeof (struct sector) * dl.d_nsectors;
+       rtracksize = SECTSIZ * dl.d_nsectors;
        bp = (struct sector *)malloc(tracksize);
        bp = (struct sector *)malloc(tracksize);
-       bufinit(bp, tracksize);
-       /*
-        * Begin check, for each track,
-        *
-        * 1) Write header and test pattern.
-        * 2) Write check header and data.
-        */
-       lastsector = st.nspc * st.ncyl;
-       for (sector = 0; sector < lastsector; sector += st.nsect) {
-               cyl = sector / st.nspc;
-               trk = (sector % st.nspc) / st.nsect;
-               for (i = 0; i < st.nsect; i++) {
-                       bp[i].header1 =
-                               (u_short) cyl | HDR1_FMT22 | HDR1_OKSCT;
-                       bp[i].header2 = ((u_short)trk << 8) + i;
-               }
-               if (sector && (sector % (st.nspc * 10)) == 0)
-                       printf("cylinder %d\n", cyl);
+       rbp = malloc(rtracksize);
+       pass = 0;
+       npat = 0;
+more:
+       for (; pass < maxpass; pass++) {
+               if (severe)
+                       printf("Begin pass %d\n", pass);
+               bufinit(bp, tracksize);
+               if (severe)
+                       npat++;
                /*
                /*
-                * Try and write the headers and data patterns into
-                * each sector in the track.  Continue until such
-                * we're done, or until there's less than a sector's
-                * worth of data to transfer.
+                * Begin check, for each track,
                 *
                 *
-                * The lseek call is necessary because of
-                * the odd sector size (516 bytes)
+                * 1) Write header and test pattern.
+                * 2) Read data.  Hardware checks header and data ECC.
+                *    Read data (esp on Eagles) is much faster than write check.
                 */
                 */
-               for (resid = tracksize, cbp = bp, sn = sector;;) {
-                       int cc;
-
-                       lseek(fd, sn * SECTSIZ, 0);
-                       ioctl(fd, SAIOHDR, (char *)0);
-                       cc = write(fd, cbp, resid);
-                       if (cc == resid)
-                               break;
+               sector = ((startcyl * dl.d_ntracks) + starttrack) *
+                       dl.d_nsectors;
+               lastsector = ((endcyl * dl.d_ntracks) + endtrack) *
+                       dl.d_nsectors + dl.d_nsectors;
+               for ( ; sector < lastsector; sector += dl.d_nsectors) {
+                       cyl = sector / dl.d_secpercyl;
+                       trk = ((sector % dl.d_secpercyl) / dl.d_nsectors) << 8;
+                       for (i = 0, hdr = bp; i < dl.d_nsectors; i++, hdr++) {
+                               hdr->header1 = cyl | HDR1_FMT22 | HDR1_OKSCT;
+                               hdr->header2 = trk + i;
+                       }
+                       if (sector && (sector % (dl.d_secpercyl * 50)) == 0)
+                               printf("cylinder %d\n", cyl);
                        /*
                        /*
-                        * Don't record errors during write,
-                        * all errors will be found during
-                        * writecheck performed below.
+                        * Try and write the headers and data patterns into
+                        * each sector in the track.  Continue until such
+                        * we're done, or until there's less than a sector's
+                        * worth of data to transfer.
+                        *
+                        * The lseek call is necessary because of
+                        * the odd sector size (516 bytes)
                         */
                         */
-                       sn = iob[fd - 3].i_errblk;
-                       cbp += sn - sector;
-                       resid -= (sn - sector) * sizeof (struct sector);
-                       if (resid < sizeof (struct sector)) 
-                               break;
-               }
-               /*
-                * Write check headers and test patterns.
-                * Retry remainder of track on error until
-                * we're done, or until there's less than a
-                * sector to verify.
-                */
-               for (resid = tracksize, cbp = bp, sn = sector;;) {
-                       int cc;
-
-                       lseek(fd, sn * SECTSIZ, 0);
-                       ioctl(fd, SAIOHCHECK, (char *)0);
-                       cc = read(fd, cbp, resid);
-                       if (cc == resid)
-                               break;
-                       sn = iob[fd-3].i_errblk;
-                       printf("sector %d, write check error\n", sn);
-                       recorderror(fd, sn, &st);
-                       /* advance past bad sector */
-                       sn++;
-                       cbp += sn - sector;
-                       resid -= (sn - sector) * sizeof (struct sector);
-                       if (resid < sizeof (struct sector)) 
-                               break;
+                       for (resid = tracksize, cbp = bp, sn = sector;;) {
+                               register int cc;
+
+                               lseek(fd, sn * SECTSIZ, L_SET);
+                               ioctl(fd, SAIOHDR, (char *)0);
+                               cc = write(fd, cbp, resid);
+                               if (cc == resid)
+                                       break;
+                               /*
+                                * Don't record errors during write,
+                                * all errors will be found during
+                                * check performed below.
+                                */
+                               sn = iob[fd - 3].i_errblk;
+                               cbp += sn - sector;
+                               resid -= (sn - sector) * sizeof (struct sector);
+                               if (resid < sizeof (struct sector)) 
+                                       break;
+                       }
+                       /*
+                        * Read test patterns.
+                        * Retry remainder of track on error until
+                        * we're done, or until there's less than a
+                        * sector to verify.
+                        */
+                       for (resid = rtracksize, rcbp = rbp, sn = sector;;) {
+                               register int cc, rsn;
+
+                               lseek(fd, sn * SECTSIZ, L_SET);
+                               cc = read(fd, rcbp, resid);
+                               if (cc == resid)
+                                       break;
+                               sn = iob[fd-3].i_errblk;
+                               if (ssdev) {
+                                       rsn = sn - (sn / dl.d_nsectors);
+                                       printf("data ");
+                               } else
+                                       rsn = sn;
+                               printf("sector %d, read error\n\n", rsn);
+                               if (recorderror(fd, sn, &dl) < 0 && pass > 0)
+                                       goto out;
+                               /* advance past bad sector */
+                               sn++;
+                               resid = rtracksize - ((sn - sector) * SECTSIZ);
+                               rcbp = rbp + ((sn - sector) * SECTSIZ);
+                               if (resid < SECTSIZ) 
+                                       break;
+                       }
                }
        }
        /*
         * Checking finished.
         */
                }
        }
        /*
         * Checking finished.
         */
+out:
+       if (severe && maxpass < NPT) {
+               cp = prompt("More passes? (0 or number) ");
+               maxpass = atoi(cp);
+               if (maxpass > 0) {
+                       maxpass += pass;
+                       goto more;
+               }
+       }
+       if (severe && nbads) {
+               /*
+                * Sort bads and insert in bad block table.
+                */
+               qsort(bads, nbads, sizeof (daddr_t), qcompar);
+               severe = 0;
+               errno = 0;
+               for (i = 0; i < nbads; i++)
+                       recorderror(fd, bads[i], &dl);
+               severe++;
+       }
+       if (errors[FE_TOTAL] || errors[FE_SSE]) {
+               /* change the headers of all the bad sectors */
+               writebb(fd, errors[FE_SSE], &sstab, &dl, SSERR);
+               writebb(fd, errors[FE_TOTAL], &dkbad, &dl, BSERR);
+       }
        if (errors[FE_TOTAL] || errors[FE_SSE]) {
                printf("Errors:\n");
                for (i = 0; i < NERRORS; i++)
                        printf("%s: %d\n", errornames[i], errors[i]);
        if (errors[FE_TOTAL] || errors[FE_SSE]) {
                printf("Errors:\n");
                for (i = 0; i < NERRORS; i++)
                        printf("%s: %d\n", errornames[i], errors[i]);
-               printf("Total of %d hard errors found\n",
+               printf("Total of %d hard errors revectored\n",
                        errors[FE_TOTAL] + errors[FE_SSE]);
                        errors[FE_TOTAL] + errors[FE_SSE]);
-               /* change the headers of all the bad sectors */
-               writebb(fd, errors[FE_SSE], &sstab, &st, SSERR);
-               writebb(fd, errors[FE_TOTAL], &dkbad, &st, BSERR);
        }
        }
-       while (errors[FE_TOTAL] < MAXBADDESC) {
-               int i = errors[FE_TOTAL]++;
+       if (endcyl == dl.d_ncylinders - 1 &&
+           (startcyl < dl.d_ncylinders - 1 || starttrack == 0)) {
+               while (errors[FE_TOTAL] < MAXBADDESC) {
+                       int i = errors[FE_TOTAL]++;
 
 
-               dkbad.bt_bad[i].bt_cyl = -1;
-               dkbad.bt_bad[i].bt_trksec = -1;
-       }
-       printf("\nWriting bad sector table at sector #%d\n",
-               st.ncyl * st.nspc - st.nsect);
-       /* place on disk */
-       for (i = 0; i < 10; i += 2) {
-               lseek(fd, SECTSIZ * (st.ncyl * st.nspc - st.nsect + i), 0);
-               write(fd, &dkbad, sizeof (dkbad));
+                       dkbad.bt_bad[i].bt_cyl = -1;
+                       dkbad.bt_bad[i].bt_trksec = -1;
+               }
+               printf("\nWriting bad sector table at sector #%d\n",
+                       dl.d_ncylinders * dl.d_secpercyl - dl.d_nsectors);
+               /* place on disk */
+               for (i = 0; i < 10 && i < dl.d_nsectors; i += 2) {
+                       lseek(fd, SECTSIZ * (dl.d_ncylinders *
+                               dl.d_secpercyl - dl.d_nsectors + i), 0);
+                       write(fd, &dkbad, sizeof (dkbad));
+               }
+       } else if (errors[FE_TOTAL]) {
+               struct bt_bad *bt;
+
+               printf("New bad sectors (not added to table):\n");
+               bt = dkbad.bt_bad;
+               for (i = 0; i < errors[FE_TOTAL]; i++) {
+                       printf("bn %d (cn=%d, tn=%d, sn=%d)\n", badsn(bt, &dl),
+                           bt->bt_cyl, bt->bt_trksec>>8, bt->bt_trksec&0xff);
+                       bt++;
+               }
        }
        printf("Done\n");
        ioctl(fd,SAIONOSSI,(char *)0);
        }
        printf("Done\n");
        ioctl(fd,SAIONOSSI,(char *)0);
@@ -197,13 +319,36 @@ again:
 #endif
 }
 
 #endif
 }
 
+qcompar(l1, l2)
+register daddr_t *l1, *l2;
+{
+       if (*l1 < *l2)
+               return(-1);
+       if (*l1 == *l2)
+               return(0);
+       return(1);
+}
+
+daddr_t
+badsn(bt, lp)
+       register struct bt_bad *bt;
+       register struct disklabel *lp;
+{
+       register int ssoff = ssdev ? 1 : 0;
+
+       return ((bt->bt_cyl * lp->d_ntracks + (bt->bt_trksec >> 8)) *
+               (lp->d_nsectors - ssoff) + (bt->bt_trksec & 0xff) - ssoff);
+}
+
 /*
 /*
- * Write out the bad blocks.
+ * Mark the bad/skipped sectors.
+ * Bad sectors on skip-sector devices are assumed to be skipped also,
+ * and must be done after the (earlier) first skipped sector.
  */
  */
-writebb(fd, nsects, dbad, st, sw)
+writebb(fd, nsects, dbad, lp, sw)
        int nsects, fd;
        struct dkbad *dbad;
        int nsects, fd;
        struct dkbad *dbad;
-       register struct st *st;
+       register struct disklabel *lp;
 {
        struct sector bb_buf; /* buffer for one sector plus 4 byte header */
        register int i;
 {
        struct sector bb_buf; /* buffer for one sector plus 4 byte header */
        register int i;
@@ -214,28 +359,30 @@ writebb(fd, nsects, dbad, st, sw)
                btp = &dbad->bt_bad[i];
                if (sw == BSERR) {
                        bb_buf.header1 = HDR1_FMT22|btp->bt_cyl;
                btp = &dbad->bt_bad[i];
                if (sw == BSERR) {
                        bb_buf.header1 = HDR1_FMT22|btp->bt_cyl;
-                       if (SSDEV)
+                       if (ssdev)
                                bb_buf.header1 |= HDR1_SSF;
                } else
                        bb_buf.header1 =
                               btp->bt_cyl | HDR1_FMT22 | HDR1_SSF | HDR1_OKSCT;
                bb_buf.header2 = btp->bt_trksec;
                                bb_buf.header1 |= HDR1_SSF;
                } else
                        bb_buf.header1 =
                               btp->bt_cyl | HDR1_FMT22 | HDR1_SSF | HDR1_OKSCT;
                bb_buf.header2 = btp->bt_trksec;
-               bn = st->nspc * btp->bt_cyl +
-                    st->nsect * (btp->bt_trksec >> 8) +
+               bn = lp->d_secpercyl * btp->bt_cyl +
+                    lp->d_nsectors * (btp->bt_trksec >> 8) +
                     (btp->bt_trksec & 0xff);
                     (btp->bt_trksec & 0xff);
-               lseek(fd, bn * SECTSIZ, 0);
+               lseek(fd, bn * SECTSIZ, L_SET);
                ioctl(fd, SAIOHDR, (char *)0);
                write(fd, &bb_buf, sizeof (bb_buf));
                ioctl(fd, SAIOHDR, (char *)0);
                write(fd, &bb_buf, sizeof (bb_buf));
-               if (!SSDEV)
-                       continue;
                /*
                 * If skip sector, mark all remaining
                 * sectors on the track.
                 */
                /*
                 * If skip sector, mark all remaining
                 * sectors on the track.
                 */
-               for (j = (btp->bt_trksec & 0xff) + 1; j < st->nsect; j++) {
-                       bb_buf.header1 = j | HDR1_FMT22 | HDR1_SSF;
-                       ioctl(fd, SAIOHDR, (char *)0);
-                       write(fd, &bb_buf, sizeof (bb_buf));
+               if (sw == SSERR) {
+                       for (j = (btp->bt_trksec & 0xff) + 1, bn++;
+                           j < lp->d_nsectors; j++, bn++) {
+                               bb_buf.header2 = j | (btp->bt_trksec & 0xff00);
+                               lseek(fd, bn * SECTSIZ, L_SET);
+                               ioctl(fd, SAIOHDR, (char *)0);
+                               write(fd, &bb_buf, sizeof (bb_buf));
+                       }
                }
        }
 }
                }
        }
 }
@@ -243,46 +390,72 @@ writebb(fd, nsects, dbad, st, sw)
 /*
  * Record an error, and if there's room, put
  * it in the appropriate bad sector table.
 /*
  * Record an error, and if there's room, put
  * it in the appropriate bad sector table.
+ *
+ * If severe burnin store block in a list after making sure
+ * we have not already found it on a prev pass.
  */
  */
-recorderror(fd, bn, st)
+recorderror(fd, bn, lp)
        int fd, bn;
        int fd, bn;
-       register struct st *st;
+       register struct disklabel *lp;
 {
 {
-       int cn, tn, sn, strk;
+       int cn, tn, sn;
+       register int i;
 
 
-       if (errors[FE_TOTAL] >= MAXBADDESC) {
-               printf("Too many bad sectors\n");
-               return;
+       if (severe) {
+               for (i = 0; i < nbads; i++)
+                       if (bads[i] == bn)
+                               return(0);      /* bn already flagged */
+               if (nbads >= (ssdev ? 2 * MAXBADDESC : MAXBADDESC)) {
+                       printf("Bad sector table full, format terminating\n");
+                       return(-1);
+               }
+               bads[nbads++] = bn;
+               if (errno < EBSE || errno > EHER)
+                       return(0);
+               errno -= EBSE;
+               errors[errno]++;
+               return(0);
        }
        }
-       if (errors[FE_SSE] >= MAXBADDESC) {
-               printf("Too many skip sector errors\n");
-               return;
+       if (errno >= EBSE && errno <= EHER) {
+               errno -= EBSE;
+               errors[errno]++;
        }
        }
-       if (errno < EBSE || errno > EHER)
-               return;
-       errno -= EBSE;
-       errors[errno]++;
-       cn = bn / st->nspc;
-       sn = bn % st->nspc;
-       tn = sn / st->nsect;
-       sn %= st->nsect;
-       if (SSDEV) {            /* if drive has skip sector capability */
-               int ss = errors[FE_SSE]++;
-
-               if (ss)
-                       strk = sstab.bt_bad[ss - 1].bt_trksec >> 8;
-               else
-                       strk = -1;
-               if (tn != strk) {         /* only one skip sector/track */
+       cn = bn / lp->d_secpercyl;
+       sn = bn % lp->d_secpercyl;
+       tn = sn / lp->d_nsectors;
+       sn %= lp->d_nsectors;
+       if (ssdev) {            /* if drive has skip sector capability */
+               int ss = errors[FE_SSE];
+
+               if (errors[FE_SSE] >= MAXBADDESC) {
+                       /* this is bogus, we don't maintain skip sector table */
+                       printf("Too many skip sector errors\n");
+                       return(-1);
+               }
+                 /* only one skip sector/track */
+               if (ss == 0 ||
+                   tn != (sstab.bt_bad[ss - 1].bt_trksec >> 8) ||
+                   cn != sstab.bt_bad[ss - 1].bt_cyl) {
+                       /*
+                        * Don't bother with skipping the extra sector
+                        * at the end of the track.
+                        */
+                       if (sn == lp->d_nsectors - 1)
+                               return(0);
                        sstab.bt_bad[ss].bt_cyl = cn;
                        sstab.bt_bad[ss].bt_trksec = (tn<<8) + sn;
                        sstab.bt_bad[ss].bt_cyl = cn;
                        sstab.bt_bad[ss].bt_trksec = (tn<<8) + sn;
-                       return;
+                       errors[FE_SSE]++;
+                       return(0);
                }
                }
-               cn = -cn;
+       }
+       if (errors[FE_TOTAL] >= MAXBADDESC) {
+               printf("Too many bad sectors\n");
+               return(-1);
        }
        /* record the bad sector address and continue */
        dkbad.bt_bad[errors[FE_TOTAL]].bt_cyl = cn;
        dkbad.bt_bad[errors[FE_TOTAL]++].bt_trksec = (tn << 8) + sn;
        }
        /* record the bad sector address and continue */
        dkbad.bt_bad[errors[FE_TOTAL]].bt_cyl = cn;
        dkbad.bt_bad[errors[FE_TOTAL]++].bt_trksec = (tn << 8) + sn;
+       return(0);
 }
 
 /*
 }
 
 /*
@@ -311,20 +484,17 @@ malloc(size)
 getdevice()
 {
        register char *cp;
 getdevice()
 {
        register char *cp;
-       register struct devsw *dp;
        int fd;
 
 top:
        int fd;
 
 top:
-       cp = prompt("Device to format? ");
-       if ((fd = open(cp, 2)) < 0) {
-               printf("Known devices are: ");
-               for (dp = devsw; dp->dv_name; dp++)
-                       printf("%s ",dp->dv_name);
-               printf("\n");
-               goto top;
-       }
-       printf("Formatting drive %d on %c%c%d ",
-               iob[fd - 3].i_unit % 8, cp[0], cp[1], iob[fd - 3].i_unit / 8);
+       do {
+               printf(
+               "Enter device name as \"type(adaptor,controller,drive,0)\"\n");
+               cp = prompt("Device to format? ");
+       } while ((fd = open(cp, 2)) < 0);
+       printf("Formatting %c%c drive %d on controller %d, adaptor %d: ",
+               cp[0], cp[1], iob[fd - 3].i_unit,
+               iob[fd - 3].i_ctlr, iob[fd - 3].i_adapt);
        cp = prompt("verify (yes/no)? ");
        while (*cp != 'y' && *cp != 'n')
                cp = prompt("Huh, yes or no? ");
        cp = prompt("verify (yes/no)? ");
        while (*cp != 'y' && *cp != 'n')
                cp = prompt("Huh, yes or no? ");
@@ -333,6 +503,38 @@ top:
        goto top;
 }
 
        goto top;
 }
 
+/*
+ * Find range of tracks to format.
+ */
+getrange(lp)
+       register struct disklabel *lp;
+{
+       startcyl = getnum("Starting cylinder", 0, lp->d_ncylinders - 1, 0);
+       starttrack = getnum("Starting track", 0, lp->d_ntracks - 1, 0);
+       endcyl = getnum("Ending cylinder", 0, lp->d_ncylinders - 1,
+               lp->d_ncylinders - 1);
+       endtrack = getnum("Ending track", 0, lp->d_ntracks - 1,
+               lp->d_ntracks - 1);
+}
+
+getnum(s, low, high, dflt)
+       int s, low, high, dflt;
+{
+       char buf[132];
+       u_int val;
+
+       for(;;) {
+               printf("%s (%d): ", s, dflt);
+               gets(buf);
+               if (buf[0] == 0)
+                       return (dflt);
+               val = atoi(buf);
+               if (val >= low && val <= high)
+                       return ((int)val);
+               printf("Value must be in range [%d,%d]\n", low, high);
+       }
+}
+
 static struct pattern {
        long    pa_value;
        char    *pa_name;
 static struct pattern {
        long    pa_value;
        char    *pa_name;
@@ -340,6 +542,7 @@ static struct pattern {
        { 0xf00ff00f,   "RH750 worst case" },
        { 0xec6dec6d,   "media worst case" },
        { 0xa5a5a5a5,   "alternate 1's and 0's" },
        { 0xf00ff00f,   "RH750 worst case" },
        { 0xec6dec6d,   "media worst case" },
        { 0xa5a5a5a5,   "alternate 1's and 0's" },
+       { 0xFFFFFFFF,   "Severe burnin (up to 48 passes)" },
        { 0, 0 },
 };
 
        { 0, 0 },
 };
 
@@ -356,7 +559,21 @@ getpattern()
        npatterns = p - pat;
        cp = prompt("Pattern (one of the above, other to restart)? ");
        pattern = atoi(cp) - 1;
        npatterns = p - pat;
        cp = prompt("Pattern (one of the above, other to restart)? ");
        pattern = atoi(cp) - 1;
-       return (pattern < 0 || pattern >= npatterns);
+       if (pattern < 0 || pattern >= npatterns)
+               return(1);
+       severe = 0;
+       maxpass = 1;
+       if (pat[pattern].pa_value == -1) {
+               severe = 1;
+               cp = prompt("How many passes (up to 48)? ");
+               maxpass = atoi(cp);
+               if (maxpass > NPT)
+                       maxpass = NPT;
+       }
+       maxeccbits = getnum(
+               "Maximum number of bit errors to allow for soft ECC",
+               0, 11, MAXECCBITS);
+       return (0);
 }
 
 struct xsect {
 }
 
 struct xsect {
@@ -375,14 +592,21 @@ bufinit(bp, size)
        register struct pattern *pptr;
        register long *pp, *last;
        register struct xsect *lastbuf;
        register struct pattern *pptr;
        register long *pp, *last;
        register struct xsect *lastbuf;
+       int patt;
 
        size /= sizeof (struct sector);
        lastbuf = bp + size;
 
        size /= sizeof (struct sector);
        lastbuf = bp + size;
-       pptr = &pat[pattern];
+       if (severe) {
+               patt = ppat[npat] | ((long)ppat[npat] << 16);
+               printf("Write pattern 0x%x\n", patt&0xffff);
+       } else {
+               pptr = &pat[pattern];
+               patt = pptr->pa_value;
+       }
        while (bp < lastbuf) {
                last = &bp->buf[128];
                for (pp = bp->buf; pp < last; pp++)
        while (bp < lastbuf) {
                last = &bp->buf[128];
                for (pp = bp->buf; pp < last; pp++)
-                       *pp = pptr->pa_value;
+                       *pp = patt;
                bp++;
        }
 }
                bp++;
        }
 }