conservative fix for increasing FIN
[unix-history] / usr / src / sys / vax / stand / format.c
/*
* Copyright (c) 1980 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 Regents of the University of California.\n\
All rights reserved.\n";
#endif not lint
#ifndef lint
static char sccsid[] = "@(#)format.c 6.7 (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.
* 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 "saio.h"
#include "savax.h"
#define MAXBADDESC 126 /* size of bad block table */
#define CHUNK 48 /* max # of sectors/io operation */
#define SECTSIZ 512 /* standard sector size */
#define HDRSIZ 4 /* number of bytes in sector header */
#define SSERR 0
#define BSERR 1
#define SSDEV(fd) (ioctl((fd), SAIOSSDEV, (char *)0) == 0)
#define MAXECCBITS 3
struct sector {
u_short header1;
u_short header2;
char buf[SECTSIZ];
};
struct dkbad dkbad; /* bad sector table */
struct dkbad oldbad; /* old bad sector table */
struct dkbad sstab; /* skip sector table */
#define NERRORS 6
static char *
errornames[NERRORS] = {
#define FE_BSE 0
"Bad sector",
#define FE_WCE 1
"Write check",
#define FE_ECC 2
"Hard ECC",
#define FE_HARD 3
"Other hard",
#define FE_TOTAL 4
"Marked bad",
#define FE_SSE 5
"Skipped",
};
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();
int qcompar();
char *prompt();
daddr_t badsn();
extern int end;
main()
{
register int sector, sn;
int lastsector, tracksize, rtracksize;
int unit, fd, resid, i, trk, cyl, debug;
struct st st;
struct sector *bp, *cbp;
char *rbp, *rcbp;
int pass;
char *cp;
printf("Disk format/check utility\n\n");
again:
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();
ioctl(fd, SAIODEVDATA, &st);
printf("Device data: #cylinders=%d, #tracks=%d, #sectors=%d\n",
st.ncyl, st.ntrak, st.nsect);
ssdev = SSDEV(fd);
if (ssdev) {
ioctl(fd, SAIOSSI, (char *)0); /* set skip sector inhibit */
st.nsect++;
st.nspc += st.ntrak;
printf("(not counting skip-sector replacement)\n");
}
getrange(&st);
if (getpattern())
goto again;
printf("Start formatting...make sure the drive is online\n");
ioctl(fd, SAIONOBAD, (char *)0);
ioctl(fd, SAIORETRIES, (char *)0);
ioctl(fd, SAIOECCLIM, (char *)maxeccbits);
ioctl(fd, SAIODEBUG, (char *)debug);
tracksize = sizeof (struct sector) * st.nsect;
rtracksize = SECTSIZ * st.nsect;
bp = (struct sector *)malloc(tracksize);
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++;
/*
* Begin check, for each track,
*
* 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.
*/
sector = ((startcyl * st.ntrak) + starttrack) * st.nsect;
lastsector = ((endcyl * st.ntrak) + endtrack) * st.nsect
+ st.nsect;
for ( ; 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 * 100)) == 0)
printf("cylinder %d\n", cyl);
/*
* 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)
*/
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;
/*
* 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;;) {
int cc, rsn;
lseek(fd, sn * SECTSIZ, 0);
cc = read(fd, rcbp, resid);
if (cc == resid)
break;
sn = iob[fd-3].i_errblk;
if (ssdev) {
rsn = sn - (sn / st.nsect);
printf("data ");
} else
rsn = sn;
printf("sector %d, read error\n\n", rsn);
if (recorderror(fd, sn, &st) < 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.
*/
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], &st);
severe++;
}
if (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);
}
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 revectored\n",
errors[FE_TOTAL] + errors[FE_SSE]);
}
if (endcyl == st.ncyl - 1 &&
(startcyl < st.ncyl - 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 < st.nsect; i += 2) {
lseek(fd, SECTSIZ * (st.ncyl * st.nspc - st.nsect + 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, &st),
bt->bt_cyl, bt->bt_trksec>>8, bt->bt_trksec&0xff);
bt++;
}
}
printf("Done\n");
ioctl(fd,SAIONOSSI,(char *)0);
close(fd);
#ifndef JUSTEXIT
goto again;
#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, st)
register struct bt_bad *bt;
register struct st *st;
{
if (ssdev)
return ((bt->bt_cyl * st->ntrak + (bt->bt_trksec>>8)) *
(st->nsect - 1) + (bt->bt_trksec&0xff)) - 1;
else
return ((bt->bt_cyl*st->ntrak + (bt->bt_trksec>>8)) * st->nsect
+ (bt->bt_trksec&0xff));
}
/*
* 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)
int nsects, fd;
struct dkbad *dbad;
register struct st *st;
{
struct sector bb_buf; /* buffer for one sector plus 4 byte header */
register int i;
int bn, j;
struct bt_bad *btp;
for (i = 0; i < nsects; i++) {
btp = &dbad->bt_bad[i];
if (sw == BSERR) {
bb_buf.header1 = HDR1_FMT22|btp->bt_cyl;
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;
bn = st->nspc * btp->bt_cyl +
st->nsect * (btp->bt_trksec >> 8) +
(btp->bt_trksec & 0xff);
lseek(fd, bn * SECTSIZ, 0);
ioctl(fd, SAIOHDR, (char *)0);
write(fd, &bb_buf, sizeof (bb_buf));
/*
* If skip sector, mark all remaining
* sectors on the track.
*/
if (sw == SSERR) {
for (j = (btp->bt_trksec & 0xff) + 1, bn++;
j < st->nsect; j++, bn++) {
bb_buf.header2 = j | (btp->bt_trksec & 0xff00);
lseek(fd, bn * SECTSIZ, 0);
ioctl(fd, SAIOHDR, (char *)0);
write(fd, &bb_buf, sizeof (bb_buf));
}
}
}
}
/*
* 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)
int fd, bn;
register struct st *st;
{
int cn, tn, sn;
register i;
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 (errno >= EBSE && errno <= EHER) {
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 (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 == st->nsect - 1)
return(0);
sstab.bt_bad[ss].bt_cyl = cn;
sstab.bt_bad[ss].bt_trksec = (tn<<8) + sn;
errors[FE_SSE]++;
return(0);
}
}
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;
return(0);
}
/*
* Allocate memory on a page-aligned address.
* Round allocated chunk to a page multiple to
* ease next request.
*/
char *
malloc(size)
int size;
{
char *result;
static caddr_t last = 0;
if (last == 0)
last = (caddr_t)(((int)&end + 511) & ~0x1ff);
size = (size + 511) & ~0x1ff;
result = (char *)last;
last += size;
return (result);
}
/*
* Prompt and verify a device name from the user.
*/
getdevice()
{
register char *cp;
register struct devsw *dp;
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 %c%c%d on adaptor %d: ",
cp[0], cp[1], iob[fd - 3].i_unit % 8, iob[fd - 3].i_unit / 8);
cp = prompt("verify (yes/no)? ");
while (*cp != 'y' && *cp != 'n')
cp = prompt("Huh, yes or no? ");
if (*cp == 'y')
return (fd);
goto top;
}
/*
* Find range of tracks to format.
*/
getrange(st)
struct st *st;
{
startcyl = getnum("Starting cylinder", 0, st->ncyl - 1, 0);
starttrack = getnum("Starting track", 0, st->ntrak - 1, 0);
endcyl = getnum("Ending cylinder", 0, st->ncyl - 1, st->ncyl - 1);
endtrack = getnum("Ending track", 0, st->ntrak - 1, st->ntrak - 1);
}
getnum(s, low, high, dflt)
{
char buf[132];
unsigned val;
while (1) {
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;
} pat[] = {
{ 0xf00ff00f, "RH750 worst case" },
{ 0xec6dec6d, "media worst case" },
{ 0xa5a5a5a5, "alternate 1's and 0's" },
{ 0xFFFFFFFF, "Severe burnin (up to 48 passes)" },
{ 0, 0 },
};
getpattern()
{
register struct pattern *p;
int npatterns;
char *cp;
printf("Available test patterns are:\n");
for (p = pat; p->pa_value; p++)
printf("\t%d - (%x) %s\n", (p - pat) + 1,
p->pa_value & 0xffff, p->pa_name);
npatterns = p - pat;
cp = prompt("Pattern (one of the above, other to restart)? ");
pattern = atoi(cp) - 1;
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 {
u_short hd1;
u_short hd2;
long buf[128];
};
/*
* Initialize the buffer with the requested pattern.
*/
bufinit(bp, size)
register struct xsect *bp;
int size;
{
register struct pattern *pptr;
register long *pp, *last;
register struct xsect *lastbuf;
int patt;
size /= sizeof (struct sector);
lastbuf = bp + size;
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++)
*pp = patt;
bp++;
}
}
char *
prompt(msg)
char *msg;
{
static char buf[132];
printf("%s", msg);
gets(buf);
return (buf);
}