4.3BSD-UWisc/src/sys/stand/format.c

Compare this file to the similar file:
Show the results in this format:

/*
 * 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 rcs_id[] = {"$Header: format.c,v 2.1 86/08/13 10:48:14 root Exp $"};
#endif not lint
/*
 * RCS Info
 *	$Locker: root $
 */

#ifndef lint
static char sccsid[] = "@(#)format.c	7.1 (Berkeley) 6/5/86";
#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/time.h"
#include "../h/vfs.h"
#include "../h/vnode.h"
#include "../ufs/inode.h"
#include "../ufs/fs.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);
}