[net.sources] VAX 4.2bsd online disk formater

rick@nyit.UUCP (Rick Ace) (11/13/85)

#!/bin/sh-----cut here-----cut here-----cut here-----cut here-----
# shar:	Shell Archiver
#	Run the following text with /bin/sh to create:
#	README badm.c badm.8 hp.diffs dkio.add
echo shar: extracting README
cat - << \SHAR_EOF > README
badm Rev 2.0						31-Oct-85

The "badm" utility is a formater for certain MASSBUS disks, operating
under full multi-user timesharing 4.2bsd Vax system.  This software
has been placed in the public domain, for the benefit of all Vax
4.2bsd sites.  The following files are included in this distribution:

	badm.c		C source code for badm utility
	badm.8		Manual entry for badm - READ THIS FIRST!
	hp.diffs	Additions/differences to vaxmba/hp.c
	dkio.add	Additions to vax/dkio.h

Installation consists of some minor additions and modifications to the
4.2bsd MASSBUS disk driver (vaxmba/hp.c) and some additions to the
disk ioctl header file (vax/dkio.h).  badm will *not* work without
these changes to the 4.2bsd kernel.

To install:
	1.  Read hp.diffs and dkio.add, and make coding changes
	    specified therein.
	2.  Recompile kernel and boot it.
	3.  Compile badm.c.
	4.  READ MANUAL ENTRY BEFORE USING badm.

This is the 2.0 revision of badm, supporting the following disks:
	RM05  RM03  Eagle(SC780,SC750)  RP06  RP05  RP04

Additional disks can be supported by adding entries to the disktypes[]
table in badm.c (this may also require a new whdr_XXX() subroutine,
depending upon the header design of the target disk).

***** WARNING *****
Several sites have reported severe (pronounced "crash") problems
when running badm on Emulex SC7000 controllers.  At the time of
this writing, the problem is not completely understood, so you
are advised not to use badm to format a disk on a SC7000 controller.
Badm works quite well, however, with SC780 and SC750 controllers.


Please address comments, questions, flames to the author:

Rick Ace
New York Institute of Technology
Computer Graphics Laboratory
Wheatley Road
Old Westbury, NY  11568

USENET:	{decvax,seismo}!philabs!nyit!rick
SHAR_EOF
echo shar: extracting badm.c
cat - << \SHAR_EOF > badm.c
#ifndef lint
static char rcsid[] = "$Header: badm.c,v 2.0 85/10/31 12:57:16 rick Exp $";
#endif

/* 
 * Utility to do media checking/formating during timesharing
 *
 * $Log:	badm.c,v $
 * Revision 2.0  85/10/31  12:57:16  rick
 * * First publicly-released version *
 * 
 * Rick Ace
 * New York Institute of Technology
 * Computer Graphics Laboratory
 * Old Westbury, New York  11568
 *
 * {seismo,decvax}!philabs!nyit!rick
 */

#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/dkbad.h>
#include <vax/dkio.h>
#include <disktab.h>
#include <signal.h>
#include <stdio.h>
#include <sysexits.h>

/*
 * There are a few hp.c driver bugs that can be circumvented by
 * workaround code in badm.  These workarounds appear under the
 * HPBUG conditional.  The `normal' (i.e., driver functions
 * properly) case appears after the `#else'.
 *
 * Of course, the right thing is to fix the driver instead,
 * but who knows... the same bugs might be in 4.Xbsd forever.
 */
#define HPBUG	1	/* hp driver bugs are present */

#define MAXNUMBAD	126	/* max # of bad sectors on one disk */

/*
 * Function codes for drive-specific header-write routines
 */
#define WHDR_VALID	0	/* mark the header as valid */
#define WHDR_BAD	1	/* mark the header as a bad sector */

/*
 * Special values returned by Btsector() 
 */
#define BTEMPTY		((daddr_t) -1)	/* entry is empty */
#define BTINVALID	((daddr_t) -2)	/* entry contains an illegal c/t/s */

int	bflag,iflag;
char	*cmd;		/* command and options, from argv */
char	diskdevice[100] = "/dev/r"; /* disk device name, built from argv */
int	dfd;		/* file descriptor of disk device */
char	*patternbuf;	/* output buffer, contains patterns, 0 if none */
char	*readbackbuf;	/* input buffer, for readback from disk */
unsigned short short_ones = ~0; /* all 1 bits set */
int	wantout;	/* set by SIGINT to request graceful exit */
int	bsi;		/* T/F: bst has been initialized */
struct dkbad bst;	/* bad sector table */
int	whdr_unknown();

/*
 * Drive description table
 */
struct st {
	char	vname[12];	/* vendor's name for disk */
	int	sectsize;	/* # bytes per sector */
	daddr_t	nsect;		/* # sectors/track */
	daddr_t	ntrack;		/* # tracks/surfaces/heads */
	daddr_t	nspc;		/* # sectors/cylinder */
	daddr_t	ncyl;		/* # cylinders */
	int	(*wheader)();	/* routine to write sector header */
	daddr_t	bad0sn;		/* sector # of first bad sector table */
} st =
	{ "default",	  0,  0,  0,     0,   0, whdr_unknown };

int	whdr_rm(),whdr_rp();

struct st disktypes[] = {
	{ "eagle",	512, 48, 20, 48*20, 842, whdr_rm },
	{ "rm05",	512, 32, 19, 32*19, 823, whdr_rm },
	{ "rm03",	512, 32,  5, 32* 5, 823, whdr_rm },
	{ "rp06",	512, 22, 19, 22*19, 815, whdr_rp },
	{ "rp05",	512, 22, 19, 22*19, 411, whdr_rp },
	{ "rp04",	512, 22, 19, 22*19, 411, whdr_rp },
	{ "\0" }		/* LAST ENTRY SENTINEL */
};

/*
 * Bit patterns for media surface testing
 */
unsigned short testpattern[] = {
	0x3333, 0x71C7, 0xB6DB, 0xDB6D, 0xE38E, 0xC71C, 0x83E8, 0x0FF0,
	0xEC6D, 0x1C71, 0x38E3, 0xAAAA, 0x5555, 0xF00F, 0xA5A5, 0x6DEC
};

#define	NPATTERN	(sizeof testpattern / sizeof testpattern[0]) 

/*
 * Torture-seek routine tables
 *
 * Routines take ordinal iteration number, returning
 * a sector number to seek to
 */
daddr_t	tort0(),tort1(),tort2(),tort3();
daddr_t	(*tortvec[])() = {
	tort0, tort1, tort2, tort3
};

/*
 * Command/options table
 *
 * WHEN CHANGING THIS TABLE BE SURE TO CHANGE THE USAGE MESSAGE TOO!
 *
 * Any option characters appearing in co_opt must also appear as
 * cases of the `switch' statement in Main().
 */
int	cmda(),cmdrw(),cmdt();

struct cotab {
	char	co_cmd;		/* command name, 1 letter */
	char	co_opt[7];	/* legal options for the command */
	int	(*co_rtn)();	/* driver */
} cotab[] = {
	'w',	"bi",	cmdrw,
	'r',	"",	cmdrw,
	'a',	"",	cmda,
	't',	"",	cmdt,
	0				/* TERMINATOR */
};

long	atol(),lseek();
char	*ctime(),*index(),*malloc();
int	catchint();
daddr_t	btsector();

main(argc,argv)
	int argc;
	char **argv;
{
	register struct cotab *co;
	register char *p;

	if (signal(SIGINT,SIG_IGN) != SIG_IGN) 
		(void) signal(SIGINT,catchint);
	if (argc < 2) 
		usage();
	/*
	 * Identify the command
	 */
	cmd = argv[1];
	co = &cotab[0];
	co--;
	do if ((++co)->co_cmd == 0) usage();
	 while (cmd[0] != co->co_cmd);
	/*
	 * Inspect and validate the options to the main command
	 */
	for (p = cmd; *++p; ) {
		if (index(co->co_opt,*p) == 0) 
			usage();
		/*
		 * All flag characters specified in the co_opt strings
		 * must have `case' entries in this switch statement.
		 * Arriving at the `should not happen' default case indicates
		 * that at least one such `case' is missing (programmer error).
		 */
		switch (*p) {
		case 'b':	bflag++; break;
		case 'i':	iflag++; break;
		default:	quit(EX_SOFTWARE,"UNEXPECTED FLAG: %c\n",*p);
		}
	}
	/*
	 * Command/options scan is complete, invoke the driver for this command
	 */
	(*co->co_rtn)(argc-2,argv+2);
}

/*
 * Add a sector to the bad sector table and rewrite the header for the sector
 *
 * Returns T/F: sector was added to the bad sector table
 */
addbad(sn)
	daddr_t sn;		/* sector number */
{
	register struct bt_bad *e,*e1;
	register char **bsp;
	daddr_t esn;
	char *bsbuf[MAXNUMBAD];

	/*
	 * Abort now if I don't know how to write headers on this drive
	 */
	if (st.wheader == whdr_unknown) {
		whdr_unknown((daddr_t)0,0,WHDR_BAD); /* only print error msg */
		exit(EX_UNAVAILABLE);	/* should never get here, but... */
	}
	if (!bsi)	/* double-check for programming errors */
		quit(EX_SOFTWARE,"bst NOT SET UP!\n");
	printf("sn%d: ",sn);
	if (btsector(&bst.bt_bad[MAXNUMBAD - 1]) != BTEMPTY) {
		printf("BAD SECTOR TABLE IS FULL, CANNOT ADD\n");
		return 0;
	}
	/*
	 * Locate the position in the table where the new entry will go.
	 */
	e = &bst.bt_bad[-1];
	do {
		esn = btsector(++e);
		if (esn == BTINVALID) {
			/*
			 * This should never happen because the sector
			 * entries in bt_bad have already been validated
			 */
			printf("BAD SECTOR TABLE ENTRY %d CONTAINS AN ILLEGAL ADDRESS cyl=%x trksec=%x\n\
NO ACTION TAKEN\n", e - &bst.bt_bad[0], e->bt_cyl, e->bt_trksec);
			return 0;
		}
		if (sn == esn) {
			printf("ALREADY IN BAD SECTOR TABLE (ALTERNATE IS BAD!)\n");
			return 0;
		}
	} while (esn != BTEMPTY && sn > esn);
	/*
	 * Found the proper position for the new entry.  If there are any
	 * active entries with higher disk addresses, they must be shuffled
	 * down one slot to make room for the new entry, because DEC std 144
	 * dictates that the table must be in ascending order.
	 *
	 * Shuffling the entries also entails shuffling the contents of the
	 * alternate sectors, so the first task is to grab those contents.
	 */
	bsp = &bsbuf[0];
	e1 = e;
	do {
		esn = btsector(e1);
		if (esn == BTEMPTY) 
			break;
		if ((*bsp = malloc((unsigned)st.sectsize)) == 0) {
			printf("Cannot Malloc memory, no action taken\n");
			goto fre0;
		}
		if (bread(esn, *bsp++, st.sectsize) < 0) {
			printf("Error reading sector %d, no action taken\n",esn);
			*bsp = 0;
			goto fre0;
		}
	} while (++e1 < &bst.bt_bad[MAXNUMBAD]);
	*bsp = 0;			/* tie off buffer list */
	/*
	 * Shuffle the table entries to make room, then install the new entry
	 */
	for (e1 = &bst.bt_bad[MAXNUMBAD - 2]; e1 >= e; e1--) 
		e1[1] = e1[0];
	e->bt_cyl = sn / st.nspc;
	e->bt_trksec = sn % st.nspc / st.nsect << 8 | sn % st.nsect;
	writebst();			/* write bad sector table, 5 copies */
	/*
	 * Rewrite the header of the new bad sector to mark it bad
	 */
	(*st.wheader)(sn,1,WHDR_BAD);
	printf("added to bad sector table\n");
	/*
	 * Write the contents of the shuffled sectors back to their
	 * new alternates on disk
	 */
	e1 = e;
	for (bsp = &bsbuf[0]; *bsp; ) {
		/*
		 * For each core buffer there should be a sector number.
		 * Abort if I can't determine the sector number (this
		 * would imply a logic error in this subroutine).
		 */
		if (++e1 >= &bst.bt_bad[MAXNUMBAD] ||
		    (esn = btsector(e1)) == BTEMPTY ||
		    esn == BTINVALID) 
			quit(EX_SOFTWARE,"LOST SECTOR NUMBERS FOR RE-WRITE\n");
		if (bwrite(esn, *bsp, st.sectsize) < 0) 
			printf("FAILED to re-write sector %d\n",esn);
		free(*bsp++);			/* discard buffer */
	}
	return 1;				/* added successfully */

	/*
	 * Free Malloc buffers and error exit
	 */
fre0:	for (bsp = &bsbuf[0]; *bsp; ) 
		free(*bsp++);
	return 0;
}

/*
 * Read from disk
 *
 * Returns 0 OK, -1 error
 */
bread(sn,buf,size)
	daddr_t sn;		/* sector number */
	char *buf;		/* buffer */
	int size;		/* size (bytes) */
{
	(void) lseek(dfd, (long)sn * (long)st.sectsize, 0);
	if (read(dfd,buf,size) == size) 
		return 0;
	return -1;
}

/*
 * Given a bad sector table (bt_bad) entry, convert it to a sector number
 *
 * Returns sector number, or
 *	BTEMPTY if the entry is all 1s (i.e., unoccupied), or
 *	BTINVALID if entry's cylinder, track, or sector number is out of range
 */
daddr_t
btsector(e)
	register struct bt_bad *e;	/* entry to be converted */
{
	register unsigned int etrack,esector;

	if (e->bt_cyl == short_ones && e->bt_trksec == short_ones) 
		return BTEMPTY;
	etrack = e->bt_trksec >> 8;
	esector = e->bt_trksec & 0xFF;
	if (e->bt_cyl >= st.ncyl ||
	    etrack >= st.ntrack ||
	    esector >= st.nsect) 
		return BTINVALID;
	return e->bt_cyl * st.nspc + etrack * st.nsect + esector;
}

/*
 * Write to disk
 *
 * Returns 0 OK, -1 error
 */
bwrite(sn,buf,size)
	daddr_t sn;		/* sector number */
	char *buf;		/* buffer */
	int size;		/* size (bytes) */
{
	(void) lseek(dfd, (long)sn * (long)st.sectsize, 0);
	if (write(dfd,buf,size) == size) 
		return 0;
	return -1;
}

/*
 * SIGINT (Control-C) handler
 */
catchint()
{
	wantout++;
}

/*
 * Driver for `a' command
 */
cmda(ac,av)
	int ac;
	char **av;	/* main() argv sans first two entries */
{
	register int snx;
	char *sbuf;
	daddr_t sn;
	int brv;

	if (ac < 2) 
		usage();
	diskopen(av[0],2);
	get_devdata();		/* get disk geometry, etc. */
	readbst();		/* read bad sector table off pack */
	snx = 1;
	sbuf = malloc((unsigned)st.sectsize);
	do {
		sn = atol(av[snx]);
		if (sn < 0 || sn >= st.bad0sn - MAXNUMBAD) {
			/*
			 * Illegal to specify sectors that are
			 * 1.  beyond the end of the disk
			 * 2.  in the last track (where the bad sector file is) 
			 * 3.  in the alternate-sector region (the 126 sectors
			 *     preceding the last track on the disk) 
			 */
			printf("sn%d: out of range for `%s' disk\n",sn,st.vname);
			continue;
		}
		/*
		 * Read sector contents, mark sector bad, and then
		 * write the old contents out to the alternate.
		 * If the data is so far gone that it cannot be
		 * read, then put something clean (i.e., zeros) 
		 * in the alternate in lieu of the data.
		 */
		if ((brv = bread(sn,sbuf,st.sectsize)) < 0) {
			printf("sn%d: CANNOT READ DATA - substituting zeros\n",sn);
			bzero(sbuf,st.sectsize);	/* substitute zeros */
		}
		if (addbad(sn)) {	
			/* rewrite contents to alternate */
			printf("sn%d: copy ",sn);
			if (bwrite(sn,sbuf,st.sectsize) < 0 || brv < 0) 
				printf("failed\n");
			else
				printf("successful\n");
		}
	} while (++snx < ac);
	free(sbuf);
}

/*
 * Driver for `r' and `w' commands
 */
cmdrw(ac,av)
	int ac;
	char **av;	/* main() argv sans first two entries */
{
	register daddr_t sector;
	int pass,tracksize;
	time_t now;

	if (ac != (iflag ? 2 : 1)) 
		usage();
	(void) time(&now);
	printf(ctime(&now));
	diskopen(av[0], cmd[0]=='w'?2:0);
	get_devdata();		/* get disk geometry, etc. */
	printf("%s: %s  #cylinders=%d, #tracks=%d, #sectors=%d, bytes/sector=%d\n",
		diskdevice, st.vname, st.ncyl, st.ntrack, st.nsect, st.sectsize);
	if (iflag) {
		int serialno;

		printf("\nWriting valid headers on all sectors\n");
		initheaders();		/* writes in last track too */
		serialno = atoi(av[1]);
		printf("\nInitializing empty bad sector table, pack serial # %d\n",serialno);
		initbst(serialno);
		writebst();
	}
	else {
		if (bflag)		/* possibly updating the bad table? */
			readbst();	/* yes, must read it in then */
	}
	printf("\nBeginning surface test\n");
	tracksize = st.sectsize * st.nsect;	/* 1 track's worth of bytes */
	if (*cmd == 'w') 
		patternbuf = malloc((unsigned)tracksize);
	readbackbuf = malloc((unsigned)tracksize);
	/*
	 * Loop forever:
	 *	FOR (all patterns, repeat ad infinitum) 
	 *		FOR (all tracks on disk) 
	 *			write/read all sectors in track
	 *		END
	 *	END
	 */
	for (pass = 0; ; pass = (pass + 1) % NPATTERN) {
		if (patternbuf) 
			initpattern(patternbuf,tracksize,pass);
		for (sector = 0; sector < st.bad0sn; sector += st.nsect) {
			if (wantout) {
				(void) time(&now);
				printf("\n*** badm interrupted at %s",ctime(&now));
				exit(EX_OK);
			}
			if (sector && (sector % (st.nspc * 100)) == 0) 
				printf("cylinder %d\n", sector / st.nspc);
			format_track(sector,tracksize);
		}
	}
}

/*
 * Driver for `t' command (torture disk) 
 */
cmdt(ac,av)
	int ac;
	char **av;	/* main() argv sans first two entries */
{
#define TORTURE_MAX	5000
	int pass,sbint,torture;
	time_t now;
	char *sbuf;

	if (ac != 1) 
		usage();
	(void) time(&now);
	printf("Torture test, %s\n",ctime(&now));
	diskopen(av[0],0);
	get_devdata();		/* get disk geometry, etc. */
	/*
	 * Slightly more optimal page-aligned buffer,
	 * so kernel spends less time in vslock/vsunlock
	 */
	sbint = (int) malloc((unsigned)st.sectsize + 512);
	sbint += 511;		/* round up to next page */
	sbint &= ~511;
	sbuf = (char *)sbint;
	for (pass = 1; ; pass++) {
		printf("Pass %d\n",pass);
		torture = sizeof tortvec / sizeof tortvec[0] - 1;
		do {
			register daddr_t n,(*trou)();

			n = 0;
			trou = tortvec[torture];
			do {
#ifdef TTEST
				printf("%d\t%6d\n",torture,(*trou)(n));
#else
				(void) bread((*trou)(n),sbuf,st.sectsize);
#endif
				if (wantout) {
					printf("\n*** torture interrupted\n");
					exit(EX_OK);
				}
			} while (++n < TORTURE_MAX);
		} while (--torture >= 0);
	}
}

/*
 * Construct pathname of raw disk device and open it
 *
 * Outputs:
 *	dfd		file descriptor on disk device
 *	diskdevice	ASCII name of disk device (/dev/...) 
 */
diskopen(devun,omode)
	char *devun;	/* device/unit specifier, e.g., "hp1" */
	int omode;	/* mode for Open() syscall */
{
	strcat(diskdevice,devun);
	strcat(diskdevice,"c");
	if ((dfd = open(diskdevice,omode)) < 0) {
		perror(diskdevice);
		exit(EX_NOINPUT);
	}
#ifdef HPBUG
	/*
	 * hp.c driver bug.  After spinning up a new disk, the Volume
	 * Valid bit in the RM/RP drive is reset.  hp.c notices this
	 * on the first I/O request following spinup, and sneaks in
	 * a read of the bad sector table prior to doing the requested
	 * disk transfer.  The bug comes when the first transfer
	 * done by badm includes the header; hp.c mistakenly
	 * 1) applies the DKIOCHDR request to the bad sector table
	 *    read operation,
	 * 2) then resets sc_hdr, failing to honor badm's request for
	 *    header I/O.
	 * Bummer # 1 causes hp.c to get a mangled copy of the bad
	 * sector table, and bummer # 2 causes errors when badm tries
	 * to write sector headers during a `badm wbi' operation.
	 *
	 * Workaround:  perform a gratuitous non-header read operation
	 * upon opening the disk to get the driver's bad sector table
	 * read operation over and done with.  My read here will probably
	 * fail with an HCE, but even so, it will have served its purpose.
	 */
	{
		char gbuf[512];

		(void) read(dfd,gbuf,sizeof gbuf);
	}
#endif HPBUG
}

/*
 * Per-track logic for surface testing and bad sector flagging
 */
format_track(startsn,tracksize)
	daddr_t startsn;	/* starting sector number */
	int tracksize;		/* number of bytes in the track */
{
	register int errorval,i,readpass;
	char badmap[256];

	/*
	 * If doing the `w' command, write test patterns
	 */
	if (patternbuf && bwrite(startsn,patternbuf,tracksize) < 0) {
		/*
		 * Problems... slow down to 1 sector at a time
		 * ignoring errors (I'll catch them on readback) 
		 */
		i = 0;
		do (void) bwrite(startsn+i,patternbuf,st.sectsize);
		 while (++i < st.nsect);
	}
	/*
	 * Read test patterns (or whatever) back
	 */
	(void) numecc();		/* clear ECC count */
	if (bread(startsn,readbackbuf,tracksize) < 0 || numecc()) {
		/*
		 * Hard error or ECC, slow down to inspect sectors individually.
		 * The code here makes several passes over the entire track,
		 * because if the error is marginal, it might not show up on
		 * the first try.
		 *
		 * `badmap' limits the number of complaints to one per sector
		 * when I can't mark the sector as bad (i.e., Addbad()==0).
		 */
		readpass = 8;		/* init pass counter */
		bzero(badmap,sizeof badmap); /* no complaints issued yet */
		do {
			i = 0;		/* init sector # within track */
			do {
				if (badmap[i]) /* if previous pass found it */
					continue; /* ... that was enough */
				errorval = 0;
				if (bread(startsn+i,readbackbuf,st.sectsize) < 0
				     || (errorval = numecc())) {
					/*
					 * Report bad sector, then if `b' flag
					 * is set, bad it and retry the read
					 */
					printf("sn%d: ",startsn+i);
					printf(errorval?"soft (%d)":"hard",errorval);
					printf(" read error\n");
					if (bflag && addbad(startsn+i)) 
						i--;	/* retry */
					else
						badmap[i] = 1;
				}
			} while (++i < st.nsect);
		} while (--readpass > 0);
	}
}

/*
 * Obtain device-specific data and set up `st' structure
 */
get_devdata()
{
	register struct st *s;
	register struct disktab *d;
	struct iogstat gs;

	if (ioctl(dfd,DKIOGSTAT,(char *)&gs) < 0) 
		quit(EX_UNAVAILABLE,"cannot get drive status of %s\n",diskdevice);
	if (!gs.iogs_online) 
		quit(EX_IOERR,"%s: not online\n",diskdevice);
	s = &disktypes[0];
	do {
		if (strcmp(gs.iogs_vname,s->vname) == 0) {
			bcopy((char *)s,(char *)&st,sizeof st);
			goto calc0;
		}
	} while ((++s)->vname[0]);
	/*
	 * Cannot find a description of the disk in my tables,
	 * so try Getdiskbyname() to see if it knows anything
	 */
	s = &st;		/* use defaults */
	bcopy(gs.iogs_vname,s->vname,sizeof s->vname - 1);
	if ((d = getdiskbyname(gs.iogs_vname)) == 0) 
		quit(EX_OSFILE,"cannot get `disktab' information for `%s' disk\n",gs.iogs_vname);
	s->sectsize = d->d_secsize;
	s->nsect = d->d_nsectors;
	s->ntrack = d->d_ntracks;
	s->ncyl = d->d_ncylinders;
	s->nspc = s->nsect * s->ntrack;	/* sectors per cylinder */
calc0:	/*
	 * Sector number of first copy of bad sector table, per DEC Std 144.
	 * This is the first sector of the last track
	 */
	s = &st;
	s->bad0sn = s->ncyl * s->nspc - s->nsect;
}

/*
 * Initialize the in-core copy of the bad sector table:
 *	1.  install pack serial number
 *	2.  mark all bad sector slots as unused (all 1's) 
 */
initbst(packsn)
	int packsn;		/* serial number for pack */
{
	register struct bt_bad *be;

	bst.bt_csn = packsn;
	bst.bt_mbz = 0;
	bst.bt_flag = 0;
	be = &bst.bt_bad[0];
	do {
		be->bt_cyl = short_ones;
		be->bt_trksec = short_ones;
	} while (++be < &bst.bt_bad[MAXNUMBAD]);
	bsi++;
}

/*
 * Write good headers to every sector on the disk, a whole track at a time
 * including the last track where the bad sector file lives.
 */
initheaders()
{
	register daddr_t sn,cyli;

	cyli = st.ntrack * 100;	/* keep user entertained while I work */
	sn = 0;
	do {
		if (wantout) {
			printf("\nLast sector written: %d\n",sn - 1);
			exit(EX_OK);
		}
		(*st.wheader)(sn,(int)st.nsect,WHDR_VALID);
		if (--cyli <= 0) {
			printf("cylinder %d\n", sn / st.nspc + 1);
			cyli = st.ntrack * 100;
		}
	} while ((sn += st.nsect) < st.nspc * st.ncyl);
}

/*
 * Initialize a buffer with the requested pattern
 */
initpattern(bp,size,patno)
	char *bp;	/* buffer to initialize */
	int size;	/* size (bytes) */
	int patno;	/* pattern number */
{
	register unsigned short *pp;
	unsigned short patn;

	pp = (unsigned short *)bp;
	patn = testpattern[patno];
	printf("Write pattern %d = 0x%04x\n",patno,patn);
	size /= sizeof patn;
	do {
		*pp++ = patn;
	} while (--size > 0);
}

/*
 * Return the number of ECC errors corrected since the last call here
 */
numecc()
{
	register long lecc;
	static long necc;
	static int eccnotavail;

	if (eccnotavail) 
		return 0;
	lecc = necc;			/* previous count */
	if (ioctl(dfd,DKIOGETECC,(char *)&necc) < 0) {
		printf("WARNING: ECC COUNTS NOT AVAILABLE, LIMPING ALONG\n");
		eccnotavail++;
		return 0;
	}
	return necc - lecc;
}

/*
 * Printf a diagnostic message and exit
 */
/*VARARGS2*/
quit(exitcode,fmt,args)
	int exitcode;
	char *fmt;
{
	_doprnt(fmt,&args,stdout);	/* tacky, tacky */
	if (exitcode == EX_SOFTWARE) 
		abort();		/* make core image for debugging */
	exit(exitcode);
	/* should *NEVER EVER* get here :-) */
}

/*
 * Read the bad sector table off the pack into core, and validate it
 */
readbst()
{
	register int n;
	register struct bt_bad *e;
	int active;
	daddr_t btabsn, cursn, lastsn;

	btabsn = st.bad0sn;	/* where the first copy of the table is */
	n = 5;			/* 5 copies, per DEC std 144 */
	while (bread(btabsn,(char *)&bst,sizeof bst) < 0) {
		printf("WARNING: CANNOT READ BAD SECTOR TABLE (sn%d)\n",btabsn);
		btabsn += 2;	/* try another copy */
		if (--n <= 0) {
			/*
			 * If I can't read the current bad sector table,
			 * I can't augment it now, can I?
			 */
			printf("\n\
You might have to re-format the *WHOLE* disk with `badm wbi'\n");
			exit(EX_IOERR);
		}
	}
	printf("Bad sector table read from sn%d\n",btabsn);
	/*
	 * Validate the contents per these rules:
	 *	o  Active slots, if any, must be clustered at the
	 *	   beginning of the table, and inactive slots at the end
	 *	o  Cylinder, track, and sector numbers must be within
	 *	   the legal ranges for the type of disk I'm working on
	 *	o  Bad sector entries must be in order of ascending
	 *	   disk addresses
	 */
	e = &bst.bt_bad[0];
	active = 1;			/* in active region of table */
#ifdef lint
	lastsn = 0;			/* avoid lint complaint */
#endif
	do {
		cursn = btsector(e);	/* convert entry to sector number */
		if (cursn == BTINVALID) 
			quit(EX_DATAERR,"Entry %d: illegal disk address, cyl=%x trksec=%x\n",
				e - &bst.bt_bad[0], e->bt_cyl, e->bt_trksec);
		if (cursn == BTEMPTY) 
			active = 0;
		else {
			if (!active ||
			    e > &bst.bt_bad[0] && cursn <= lastsn) 
				quit(EX_DATAERR,"Entry %d: out of order\n",
					e - &bst.bt_bad[0]);
			lastsn = cursn;
		}
	} while (++e < &bst.bt_bad[MAXNUMBAD]);
	bsi++;		/* note that bst is set up */
}

/*
 * Random places on disk
 */
/*ARGSUSED*/
daddr_t
tort0(n)
	daddr_t n;
{
	long random();

	return random() % (st.nspc * st.ncyl);
}

/*
 * Start at opposite ends of disk, then oscillate, closing in on center
 */
daddr_t
tort1(n)
	daddr_t n;
{
	register daddr_t cyl,dist;

	dist = n % st.ncyl >> 1;
	cyl = n & 1 ? st.ncyl - dist - 1 : dist;
	return cyl * st.nspc;
}

/*
 * Slam heads from end to end
 */
daddr_t
tort2(n)
	daddr_t n;
{
	/*
	 * 0x301 is about 1/4 as abusive as 0x001
	 */
	if (n & 0x301) 
		return (st.ncyl - 1) * st.nspc;
	return 0;
}

/*
 * 4 close, large seek, 4 close, large seek ...
 */
daddr_t
tort3(n)
	register daddr_t n;
{
	register daddr_t cyl;
	static daddr_t tab4[4] = { 0, 2, 1, 3 };

	cyl = tab4[n & 3] + (n >> 3) % (st.ncyl - 8 >> 1);
	if (n & 4) 
		cyl += st.ncyl >> 1;
	return cyl * st.nspc;
}

usage()
{
	fprintf(stderr,"Usage:\n\
badm r DISKDEVICE\n\
\tread entire disk sequentially, scanning for errors and ECCs\n\
\t(does not alter disk)\n");
	fprintf(stderr,"\n\
badm t DISKDEVICE\n\
\tread-only torture test for disk arm\n");
	fprintf(stderr,"\n\
badm w[bi] DISKDEVICE [ PACKSN ]\n\
\tw    write and readback test patterns (bashes filesystems!)\n\
\twb   same as `w', and augments bad sector table\n\
\twbi  same as `wb', and clears all headers and bad sector tables first\n\
\t     (decimal pack serial number PACKSN required with `wbi')\n\
");
	fprintf(stderr,"\n\
badm a DISKDEVICE SECTORNO [ SECTORNO ... ]\n\
\tadd specified sectors to the bad sector table\n\
");
	fprintf(stderr,"\n\
DISKDEVICE is the device name and unit number, like `hp1'\n\
SECTORNO is a decimal sector number\n\
*** READ THE CAUTIONS IN THE MANUAL ENTRY FIRST!!! ***\n\
");
	exit(EX_USAGE);
}

char	nohmemmsg[] = "cannot get memory for header I/O\n";

/*
 * Sector header I/O for RM-class MASSBUS disks (incl. Eagle) 
 */
whdr_rm(sn,nb,type)
	daddr_t sn;		/* disk sector number */
	int nb;			/* number of sectors */
	int type;		/* WHDR_xxx */
{
	static struct rmhdrbuf {
		unsigned short hdr1;
		char h2sector;
		char h2track;
		char pad[512];
	} *ebuf;
	register struct rmhdrbuf *e;
	register int i;

	if ((e = ebuf) == 0) {
		e = (struct rmhdrbuf *)malloc(sizeof (struct rmhdrbuf) * (unsigned)st.nsect);
		if (e == 0) 
			quit(EX_OSERR,nohmemmsg);
		ebuf = e;
	}
	i = 0;
	do {
		e->hdr1 = 1 << 12;		/* 16-bit format */
		if (type == WHDR_VALID) 
			e->hdr1 |= 0xC000;	/* good (valid) sector */
		e->hdr1 |= (sn + i) / st.nspc;
		e->h2track = (sn + i) % st.nspc / st.nsect;
		e->h2sector = (sn + i) % st.nsect;
		e++;
	} while (++i < nb);
	if (ioctl(dfd,DKIOCHDR,(char *)0) < 0) {
		perror("DKIOCHDR");
		exit(EX_UNAVAILABLE);
	}
#ifdef HPBUG
	/*
	 * Circumvent hp.c driver bug.  When testing to see if I/O will
	 * tread beyond the end of the disk, hpstrategy() does not take
	 * into account the fact that the sector size during header I/O
	 * is 516 (not 512) bytes.  Thus, the driver rejects some
	 * legitimate requests because it erroneously concludes that
	 * they would result in a transfer beyond the end of the disk.
	 *
	 * The workaround here deducts the number of bytes consumed by
	 * the headers (nb * 4) from the `right' (nb * 516) byte count;
	 * this appeases hp.c, but it also results in a partial write to
	 * the last sector of the bunch.  The partial write is harmless
	 * here, since we care only about the headers and not about
	 * anything else in the sectors.  The only possible problem can
	 * occur if more than 512 bytes are deducted (i.e., nb > 128),
	 * because that would eat into the header of the last sector.
	 */
	if (nb > 128)		/* should never happen :-) */
		quit(EX_SOFTWARE,"\nSOFTWARE BUG: nb (%d) > 128\n",nb);
	if (bwrite(sn, (char *)ebuf, st.sectsize * nb) < 0) 
#else HPBUG
	if (bwrite(sn, (char *)ebuf, (char *)e - (char *)ebuf) < 0) 
#endif HPBUG
		printf("sn%d: HEADER WRITE FAILED, CONTINUING\n",sn);
}

/*
 * Sector header I/O for RP04/5/6 MASSBUS disks
 */
whdr_rp(sn,nb,type)
	daddr_t sn;		/* disk sector number */
	int nb;			/* number of sectors */
	int type;		/* WHDR_xxx */
{
	static struct rphdrbuf {
		unsigned short hdr1;
		char h2sector;
		char h2track;
		unsigned short keyfield1,keyfield2;
		char pad[512];
	} *ebuf;
	register struct rphdrbuf *e;
	register int i;

	if ((e = ebuf) == 0) {
		e = (struct rphdrbuf *)malloc(sizeof (struct rphdrbuf) * (unsigned)st.nsect);
		if (e == 0) 
			quit(EX_OSERR,nohmemmsg);
		ebuf = e;
	}
	i = 0;
	do {
		/*
		 * Bad sector handling on RP04/5/6s is a kludge, since there
		 * is no true hardware support (a la RM series drives).
		 * The convention employed in badm and the kernel (hp.c) to
		 * denote a bad sector is to clear 16-bit format (bit 12) 
		 * in the header.  When reading accessing the sector, the
		 * drive will report "format error", which hp.c assumes to
		 * mean "RP0x bad sector".
		 */
		e->hdr1 = 0;			/* invalid */
		if (type == WHDR_VALID) 
			e->hdr1 = 0x1000;	/* good (valid) sector */
		e->hdr1 |= (sn + i) / st.nspc;
		e->h2track = (sn + i) % st.nspc / st.nsect;
		e->h2sector = (sn + i) % st.nsect;
		e->keyfield1 = 0;		/* what are these for? */
		e->keyfield2 = 0;
		e++;
	} while (++i < nb);
	if (ioctl(dfd,DKIOCHDR,(char *)0) < 0) {
		perror("DKIOCHDR");
		exit(EX_UNAVAILABLE);
	}
#ifdef HPBUG
	/*
	 * Circumvent hp.c driver bug.
	 * See comments in whdr_rm() for explanation of this bug.
	 * Sector size during header I/O for RP06 is 520 bytes
	 * (512 sector data + 8 header).
	 */
	if (nb > 512 / 8)		/* should never happen :-) */
		quit(EX_SOFTWARE,"\nSOFTWARE BUG: nb (%d) > 512 / 8\n",nb);
	if (bwrite(sn, (char *)ebuf, st.sectsize * nb) < 0) 
#else HPBUG
	if (bwrite(sn, (char *)ebuf, (char *)e - (char *)ebuf) < 0) 
#endif HPBUG
		printf("sn%d: HEADER WRITE FAILED, CONTINUING\n",sn);
}

/*ARGSUSED*/
whdr_unknown(sn,nb,type)
	daddr_t sn;		/* disk sector number */
	int nb;			/* number of sectors */
	int type;		/* WHDR_xxx */
{
	quit(EX_UNAVAILABLE,"don't know how to read/write headers on `%s' disks\n",st.vname);
}

/*
 * Write the bad sector table out to the disk
 *
 * Writes 5 copies, in the first 5 even-numbered sectors of the
 * last track of the last cylinder of the disk (per DEC Std 144) 
 *
 * Also informs the driver there's a new bad sector table, because some
 * drivers (e.g., hp.c) keep in-core copies, which are read only at bootup
 * or when the drive is spun up (i.e., when Volume-Valid is reset) 
 */
writebst()
{
	register int i;
	static int noioctl;

	/*
	 * Write 5 copies, per DEC std 144
	 */
	i = 0;
	do {
		if (bwrite(st.bad0sn + i * 2, (char *)&bst, sizeof bst) < 0) 
			printf("ERROR WRITING BAD TABLE %d, CONTINUING\n",i);
	} while (++i < 5);
	/*
	 * Now tell the driver that the bad block table has been updated.
	 * This is necessary because hp.c maintains an in-core copy of
	 * this table (in "hpbad"), and, in order to do the job right,
	 * the in-core copy must be accurate.
	 */
	if (!noioctl && ioctl(dfd,DKIONEWBAD,(char *)0) < 0) {
		noioctl++;
		printf("WARNING: Cannot inform driver of new bad sector table\n");
	}
#ifdef HPBUG
	/*
	 * Perform a gratuitous read operation to force driver to read
	 * the bad sector table into the in-core "hpbad" table.
	 * See explanation in Diskopen() for more details.
	 */
	{
		char gbuf[512];

		(void) bread((daddr_t)0,gbuf,sizeof gbuf);
	}
#endif HPBUG
}
SHAR_EOF
echo shar: extracting badm.8
cat - << \SHAR_EOF > badm.8
.TH BADM 8 10/31/85
.SH NAME
badm \- disk formating and bad sector maintenance
.SH SYNOPSIS
.B badm w
diskdevice
.PP
.B badm wb
diskdevice
.PP
.B badm wbi
diskdevice packsn
.PP
.B badm r
diskdevice
.PP
.B badm t
diskdevice
.PP
.B badm a
diskdevice sectorno [ sectorno ... ]
.SH DESCRIPTION
.I Badm
gives the system administrator control over the bad sector information
on a disk pack.  Some of the salient features are:
.TP 3
\(bu
Comprehensive, thorough formating capability available under
timesharing, eliminating the need to devote an entire Vax
system for a disk format operation
.TP
\(bu
Read-only mode available to assist in locating deficient sectors
on the disk without disturbing filesystems
.TP
\(bu
Capability to add `johnny-come-lately' failing sectors to the bad
sector table without disturbing other data on the disk
.PP
In the following command descriptions, the
.I diskdevice
argument refers to a disk drive by type and unit number (e.g., `hp1'), and
.I sectorno
is a sector number on the disk, expressed as an unsigned integer.
.TP 3
.B w
Write and readback test patterns.  With no options, the
.B w
command writes a test pattern to every track of the disk and reads
each track after writing it.  Any hard errors or soft ECC errors
reported by the drive are also reported by
.IR badm .
If the
.B b
option is given,
.I badm
will also add all sectors with hard or soft errors to the bad
sector table on the disk and mark the headers of the failing
sectors as being bad.  If the
.B i
option is given,
.I badm
makes an initial pass over the disk, writing good headers on all
sectors of the disk and marking all entries in the bad sector table
as unused.  When invoking
.I badm
with the
.B i
option, the decimal serial number
.RI ( packsn )
of the disk must be included on the command line.  The function of
.B badm wbi
may be used in lieu of the traditional
.I format
procedure that the DEC VMS formater performs on new disks.
When invoked with the
.B w
command,
.I badm
will cycle forever through all its test patterns, writing the patterns
to the disk and reading them back.  Typing CTRL/C will exit the program.
.TP
.B r
This function simply reads the entire disk from beginning to end
ad infinitum, reporting soft ECC and hard errors to the user.
It does not modify anything on the disk, and is thus safe to run
at any time.  Type CTRL/C to exit.
.TP
.B t
Torture the disk drive by moving the arm through a series of positions,
ad infinitum.  This function does not alter any data on the disk.
Type CTRL/C to exit.
.TP
.B a
Each
.I sectorno
argument specifies (as a decimal number) a sector to be labeled as `bad'.
.I Badm
rewrites the sector header to indicate that the sector is `bad', adds the
the sector to the disk's bad sector table, and copies the contents of the
specified sector to its replacement sector.  If the contents of the sector
cannot be read,
.I badm
complains and fills the replacement sector with zeros.  This function is
useful for adding known failing sectors to the bad sector list without
incurring the risk and expense of reformating the entire disk.
.RB ( CAUTION:
See operational restrictions below about other disk activity while using
.IR badm .)
.PP
To format a brand new disk, use
.B badm
.BR wbi .
The longer
.I badm
runs, the higher the probability that all of the marginal sectors on
the disk have been classified as being `bad'.  The formating process
begun by
.B badm wbi
can be continued after interruption (CTRL/C, system crash, etc.) by
.B badm
.BR wb .
.PP
When invoked with the
.B w
command,
.I badm
prints a message whenever it begins writing a different test pattern
on the disk:
.PP
.ti +4
Write pattern N = 0xV
.PP
where
.I N
is the pattern number and
.I V
is the test data composing the pattern.  The pattern number,
.IR N ,
will count upward from 0; when it returns to 0, all patterns have been
written to the disk once.
.PP
When formating a disk, it is recommended to let
.I badm
write each of its test patterns on the disk at least once.  This may
take many hours, depending upon the size of the disk, but any time
invested in the formating process will pay off well in the long run.
If it is necessary to have a formated disk quickly, use
.B badm wbi
and type CTRL/C after
.I badm
has begun to write the first test pattern.
.SH OPERATIONAL RESTRICTIONS
It is
.I absolutely imperative
that no other I/O activity be permitted
on a disk that is being modified by the
.BR wb ,
.BR wbi ,
or
.B a
command to
.IR badm .
This means if the disk is a user disk, none of its areas may be mounted.
Also,
.I badm cannot be used at all
to modify a disk that is serving as a system disk; attempts to do so can
result in corruption of the filesystem and/or format information on the disk.
The proper technique for using
.I badm
on a pack that normally serves as a system disk is to boot UNIX from
another system disk and to operate on the target pack as a non-system disk.
.PP
.I Badm
should be used with the same care that one exercises when using
.IR mkfs (8).
.SH DIAGNOSTICS
.TP 5
BAD SECTOR TABLE IS FULL, CANNOT ADD
An attempt to place more than 126 bad sectors in the bad sector file failed.
DEC Standard 144 limits the number of `bad' blocks on a disk to 126.
Try reformating the disk completely
.RB ( badm
.BR wbi );
if this message appears again, the disk has too many bad spots and
should be returned to the vendor for replacement.
.TP
ALREADY IN BAD SECTOR TABLE (ALTERNATE IS BAD!)
.I Badm
detected a failure in a sector that is already marked as being bad.
This usually means that the alternate sector is failing as well; DEC
standard 144 does not deal well with this situation.
.TP
Write pattern N = 0xV
.I Badm
is beginning pass
.I N
over the disk, writing pattern
.I V
in all sectors of the disk.  (This message is informational only,
and indicates no problem.)
.TP
snS: {soft (E), hard} read error
.I Badm
detected a soft ECC (with
.I E
corrections) or a hard read error while reading sector
.IR S .
When operating in
.B w
mode,
.I badm
attempts to classify any sector exhibiting a read error as a `bad' sector.
This message is a normal consequence of the formating process.
.TP
WARNING: ECC COUNTS NOT AVAILABLE, LIMPING ALONG
The UNIX driver for the disk does not support the
.I ioctl
call to return the number of soft ECC correction counts.
The consequence of this is that
.I badm
cannot detect or act upon soft ECC errors on the disk.
Consult a system programmer.
.TP
WARNING: CANNOT READ BAD SECTOR TABLE (snS)
.I Badm
cannot read the bad sector file from sector number
.IR S .
There are five identical copies of the bad sector file; if the first
copy is unreadable,
.I badm
tries to read the other copies until it finds one that it can read.
If none of the copies can be read, the disk should be completely
reformated
.RB ( badm
.BR wbi ).
.TP
WARNING: Cannot inform driver of new bad sector table
The UNIX driver for the disk does not support the
.I ioctl
call to update the in-core copy of the bad block table.
Consult a system programmer.
.TP
Entry N: illegal disk address, cyl=X trksec=X
The bad sector table on the disk contains a disk address that is
not legal.  The entry number (from 0 to 125) is displayed, along
with the contents of the cylinder and track/sector fields.
This can be corrected by completely reformating the disk
.RB ( badm
.BR wbi ).
.TP
Entry N: out of order
Entries in the bad sector table on the disk must be appear in order
of ascending disk addresses.  Entry N in the bad sector table is
out of order.  This can be corrected by completely reformating the disk
.RB ( badm
.BR wbi ).
.PP
There are other, more self-explanatory diagnostics.
.PP
While
.I badm
is running, reports of hard and soft disk ECC errors may appear
at the Vax console.  These messages are normal; they indicate
that bad sectors are being identified and dealt with by
.IR badm .
.SH BUGS
.I Badm
updates the bad sector table of the disk; this is technically a violation
of DEC standard 144, which specifies that this information is recorded at
the time of manufacture.  In practice, however, there seem to be no
operational problems incurred by updating the bad sector table in the field.
.PP
The implementation of the
.B wb
and
.B wbi
functions of
.I badm
requires support from the UNIX kernel in the form of additional
.IR ioctl (2)
services.  These are currently available only for RM05, RP06, RP05,
RP04, and Eagle disks.
.SH SEE ALSO
bad144(8), badsect(8), format(8V)
.PP
DEC Standard 144
.PP
DEC EK-ORM05-UG-002 RM05 Disk Subsystem User Guide
.PP
Emulex SC7851001 SC780 Disk Controller Technical Manual
.SH AUTHOR
.nf
Rick Ace
New York Institute of Technology
Computer Graphics Laboratory
Old Westbury, NY 11568
.fi
SHAR_EOF
echo shar: extracting hp.diffs
cat - << \SHAR_EOF > hp.diffs
Modifications to 4.2bsd vaxmba/hp.c to support badm Rev 2.0.

These changes apply to vaxmba/hp.c bearing the identification
	/*	hp.c	6.2	83/09/25	*/

Comments in brackets have been added to line-number markers to
help you locate the proper places to modify if your hp.c differs.
--------------------------------------------------------------------------
36a37		[add file.h for symbol "FWRITE"]
> #include "../h/file.h"				/* BADM */
207a209		[add new element at end of "struct hpst"]
> 	char	vname[12];	/* BADM vendor's name for device */
209,222c211,224	[add vendor name string to hpst initializers]
< 	{ 32,	5,	32*5,	823,	rm03_sizes,	3, 4 },	/* RM03 */
< 	{ 32,	19,	32*19,	823,	rm05_sizes,	3, 4 },	/* RM05 */
< 	{ 22,	19,	22*19,	815,	rp06_sizes,	3, 4 },	/* RP06 */
< 	{ 31,	14, 	31*14,	559,	rm80_sizes,	3, 4 },	/* RM80 */
< 	{ 22,	19,	22*19,	411,	rp05_sizes,	3, 4 },	/* RP04 */
< 	{ 22,	19,	22*19,	411,	rp05_sizes,	3, 4 },	/* RP05 */
< 	{ 50,	32,	50*32,	630,	rp07_sizes,	7, 8 },	/* RP07 */
< 	{ 1,	1,	1,	1,	0,		0, 0 },	/* ML11A */
< 	{ 1,	1,	1,	1,	0,		0, 0 },	/* ML11B */
< 	{ 32,	40,	32*40,	843,	cdc9775_sizes,	3, 4 },	/* 9775 */
< 	{ 32,	10,	32*10,	823,	cdc9730_sizes,	3, 4 },	/* 9730 */
< 	{ 32,	16,	32*16,	1024,	capricorn_sizes,7, 8 },	/* Capricorn */
< 	{ 48,	20,	48*20,	842,	eagle_sizes,	7, 8 },	/* EAGLE */
< 	{ 32,	19,	32*19,	815,	ampex_sizes,	3, 4 },	/* 9300 */
---
> 	{ 32,	5,	32*5,	823,	rm03_sizes,	3, 4, "rm03" },
> 	{ 32,	19,	32*19,	823,	rm05_sizes,	3, 4, "rm05" },
> 	{ 22,	19,	22*19,	815,	rp06_sizes,	3, 4, "rp06" },
> 	{ 31,	14, 	31*14,	559,	rm80_sizes,	3, 4, "rm80" },
> 	{ 22,	19,	22*19,	411,	rp05_sizes,	3, 4, "rp04" },
> 	{ 22,	19,	22*19,	411,	rp05_sizes,	3, 4, "rp05" },
> 	{ 50,	32,	50*32,	630,	rp07_sizes,	7, 8, "rp07" },
> 	{ 1,	1,	1,	1,	0,		0, 0, "ml11a" },
> 	{ 1,	1,	1,	1,	0,		0, 0, "ml11b" },
> 	{ 32,	40,	32*40,	843,	cdc9775_sizes,	3, 4, "9775" },
> 	{ 32,	10,	32*10,	823,	cdc9730_sizes,	3, 4, "9730" },
> 	{ 32,	16,	32*16,	1024,	capricorn_sizes,7, 8, "capricorn" },
> 	{ 48,	20,	48*20,	842,	eagle_sizes,	7, 8, "eagle" },
> 	{ 32,	19,	32*19,	815,	ampex_sizes,	3, 4, "9300" },
241a244		[add new element at end of "struct hpsoftc"]
> 	long	sc_ecccnt;	/* BADM ECC correction count */
730a734,737	[add automatic variables in hpioctl()]
> 	register int unit = minor(dev) >> 3;		/* BADM */
> 	register struct iogstat *d;			/* BADM */
> 	struct mba_device *mi;				/* BADM */
> 	int ds;						/* BADM */
737a745,767	[add new cases to switch in hpioctl()]
> 	case DKIOGETECC:					/* BADM */
> 		*(long *)data = hpsoftc[unit].sc_ecccnt;	/* BADM */
> 		return 0;					/* BADM */
> 
> 	case DKIOGSTAT:						/* BADM */
> 		mi = hpinfo[unit];				/* BADM */
> 		bzero(data, sizeof (struct iogstat));		/* BADM */
> 		d = (struct iogstat *)data;			/* BADM */
> 		d->iogs_areasize = hpst[mi->mi_type].sizes[minor(dev)&7].nblocks; /* BADM */
> 		bcopy(hpst[mi->mi_type].vname,d->iogs_vname,sizeof d->iogs_vname); /* BADM */
> 		ds = ((struct hpdevice *)mi->mi_drv)->hpds;	/* BADM */
> 		if (ds & HPDS_MOL) 				/* BADM */
> 			d->iogs_online++;			/* BADM */
> 		if (ds & HPDS_WRL) 				/* BADM */
> 			d->iogs_writelock++;			/* BADM */
> 		return 0;					/* BADM */
> 
> 	case DKIONEWBAD:					/* BADM */
> 		if ((flag & FWRITE) == 0) 			/* BADM */
> 			return EBADF;				/* BADM */
> 		hpsoftc[unit].sc_hpinit = 0;			/* BADM */
> 		return 0;					/* BADM */
> 
791a822		[add to hpecc() after statement "bit -= 8;"]
> 			hpsoftc[mi->mi_unit].sc_ecccnt++;	/* BADM */
848c879		[repair bug in hpecc()]
< 	mi->mi_tab.b_errcnt = 0;	/* error has been corrected */
---
> 	mi->mi_tab.b_errcnt--;		/* error has been corrected */
SHAR_EOF
echo shar: extracting dkio.add
cat - << \SHAR_EOF > dkio.add
/*
 * Additions to vax/dkio.h to support "badm" disk formater
 */

#define DKIOGSTAT	_IOR(d, 2, struct iogstat)	/* get device status */
#define DKIOGETECC	_IOR(d, 3, long)		/* disk ECC count */
#define DKIONEWBAD	_IO(d, 4)			/* new bad sec table */

struct iogstat {
	long	iogs_areasize;		/* size of disk area (512-byte units) */
	char	iogs_online;		/* 0 = offline, non-zero = online */
	char	iogs_writelock;		/* non-zero = write locked */
	char	iogs_vname[12];		/* null-terminated vendor device name */
};
SHAR_EOF