xref: /original-bsd/sys/vax/stand/format.c (revision 50f8aa26)
1 /*
2  * Copyright (c) 1980, 1986 Regents of the University of California.
3  * All rights reserved.  The Berkeley software License Agreement
4  * specifies the terms and conditions for redistribution.
5  */
6 
7 #ifndef lint
8 char copyright[] =
9 "@(#) Copyright (c) 1980, 1986 Regents of the University of California.\n\
10  All rights reserved.\n";
11 #endif not lint
12 
13 #ifndef lint
14 static char sccsid[] = "@(#)format.c	7.1 (Berkeley) 06/05/86";
15 #endif not lint
16 
17 
18 /*
19  * Standalone program to do media checking
20  * and record bad block information on any
21  * disk with the appropriate driver and RM03-style headers.
22  * TODO:
23  *	add new bad sectors to bad-sector table when formatting by track
24  *		(rearranging replacements ala bad144 -a)
25  *	multi-pass format for disks with skip-sector capability
26  */
27 #include "../h/param.h"
28 #include "../h/fs.h"
29 #include "../h/inode.h"
30 #include "../h/dkbad.h"
31 #include "../h/vmmac.h"
32 
33 #include "saio.h"
34 #include "savax.h"
35 
36 #define MAXBADDESC	126		/* size of bad block table */
37 #define CHUNK		48		/* max # of sectors/io operation */
38 #define SECTSIZ		512		/* standard sector size */
39 #define HDRSIZ		4		/* number of bytes in sector header */
40 
41 #define SSERR		0
42 #define BSERR		1
43 
44 #define SSDEV(fd)	(ioctl((fd), SAIOSSDEV, (char *)0) == 0)
45 #define MAXECCBITS	3
46 
47 struct sector {
48 	u_short	header1;
49 	u_short header2;
50 	char	buf[SECTSIZ];
51 };
52 
53 struct	dkbad dkbad;		/* bad sector table */
54 struct	dkbad oldbad;		/* old bad sector table */
55 struct	dkbad sstab;		/* skip sector table */
56 
57 #define	NERRORS		6
58 static char *
59 errornames[NERRORS] = {
60 #define	FE_BSE		0
61 	"Bad sector",
62 #define	FE_WCE		1
63 	"Write check",
64 #define	FE_ECC		2
65 	"Hard ECC",
66 #define	FE_HARD		3
67 	"Other hard",
68 #define	FE_TOTAL	4
69 	"Marked bad",
70 #define	FE_SSE		5
71 	"Skipped",
72 };
73 
74 int	errors[NERRORS];	/* histogram of errors */
75 int	pattern;
76 int	maxeccbits;
77 
78 /*
79  * Purdue/EE severe burnin patterns.
80  */
81 unsigned short ppat[] = {
82 0xf00f, 0xec6d, 0031463,0070707,0133333,0155555,0161616,0143434,
83 0107070,0016161,0034343,0044444,0022222,0111111,0125252, 052525,
84 0125252,0125252,0125252,0125252,0125252,0125252,0125252,0125252,
85 #ifndef	SHORTPASS
86 0125252,0125252,0125252,0125252,0125252,0125252,0125252,0125252,
87  052525, 052525, 052525, 052525, 052525, 052525, 052525, 052525,
88 #endif
89  052525, 052525, 052525, 052525, 052525, 052525, 052525, 052525
90  };
91 
92 #define	NPT	(sizeof (ppat) / sizeof (short))
93 int	maxpass, npat;	/* subscript to ppat[] */
94 int	severe;		/* nz if running "severe" burnin */
95 int	ssdev;			/* device supports skip sectors */
96 int	startcyl, endcyl, starttrack, endtrack;
97 int	nbads;			/* subscript for bads */
98 daddr_t	bads[2*MAXBADDESC]; 	/* Bad blocks accumulated */
99 
100 char	*malloc();
101 int	qcompar();
102 char	*prompt();
103 daddr_t	badsn();
104 extern	int end;
105 
106 main()
107 {
108 	register int sector, sn;
109 	int lastsector, tracksize, rtracksize;
110 	int unit, fd, resid, i, trk, cyl, debug;
111 	struct st st;
112 	struct sector *bp, *cbp;
113 	char *rbp, *rcbp;
114 	int pass;
115 	char *cp;
116 
117 	printf("Disk format/check utility\n\n");
118 
119 again:
120 	nbads = 0;
121 	cp = prompt("Enable debugging (0=none, 1=bse, 2=ecc, 3=bse+ecc)? ");
122 	debug = atoi(cp);
123 	if (debug < 0)
124 		debug = 0;
125 	for (i = 0; i < NERRORS; i++)
126 		errors[i] = 0;
127 	fd = getdevice();
128 	ioctl(fd, SAIODEVDATA, &st);
129 	printf("Device data: #cylinders=%d, #tracks=%d, #sectors=%d\n",
130 	  st.ncyl, st.ntrak, st.nsect);
131 	ssdev = SSDEV(fd);
132 	if (ssdev) {
133 		ioctl(fd, SAIOSSI, (char *)0);	/* set skip sector inhibit */
134 		st.nsect++;
135 		st.nspc += st.ntrak;
136 		printf("(not counting skip-sector replacement)\n");
137 	}
138 	getrange(&st);
139 	if (getpattern())
140 		goto again;
141 	printf("Start formatting...make sure the drive is online\n");
142 	ioctl(fd, SAIONOBAD, (char *)0);
143 	ioctl(fd, SAIORETRIES, (char *)0);
144 	ioctl(fd, SAIOECCLIM, (char *)maxeccbits);
145 	ioctl(fd, SAIODEBUG, (char *)debug);
146 	tracksize = sizeof (struct sector) * st.nsect;
147 	rtracksize = SECTSIZ * st.nsect;
148 	bp = (struct sector *)malloc(tracksize);
149 	rbp = malloc(rtracksize);
150 	pass = 0;
151 	npat = 0;
152 more:
153 	for (; pass < maxpass; pass++) {
154 		if (severe)
155 			printf("Begin pass %d\n", pass);
156 		bufinit(bp, tracksize);
157 		if (severe)
158 			npat++;
159 		/*
160 		 * Begin check, for each track,
161 		 *
162 		 * 1) Write header and test pattern.
163 		 * 2) Read data.  Hardware checks header and data ECC.
164 		 *    Read data (esp on Eagles) is much faster than write check.
165 		 */
166 		sector = ((startcyl * st.ntrak) + starttrack) * st.nsect;
167 		lastsector = ((endcyl * st.ntrak) + endtrack) * st.nsect
168 			+ st.nsect;
169 		for ( ; sector < lastsector; sector += st.nsect) {
170 			cyl = sector / st.nspc;
171 			trk = (sector % st.nspc) / st.nsect;
172 			for (i = 0; i < st.nsect; i++) {
173 				bp[i].header1 =
174 					(u_short) cyl | HDR1_FMT22 | HDR1_OKSCT;
175 				bp[i].header2 = ((u_short)trk << 8) + i;
176 			}
177 			if (sector && (sector % (st.nspc * 100)) == 0)
178 				printf("cylinder %d\n", cyl);
179 			/*
180 			 * Try and write the headers and data patterns into
181 			 * each sector in the track.  Continue until such
182 			 * we're done, or until there's less than a sector's
183 			 * worth of data to transfer.
184 			 *
185 			 * The lseek call is necessary because of
186 			 * the odd sector size (516 bytes)
187 			 */
188 			for (resid = tracksize, cbp = bp, sn = sector;;) {
189 				int cc;
190 
191 				lseek(fd, sn * SECTSIZ, 0);
192 				ioctl(fd, SAIOHDR, (char *)0);
193 				cc = write(fd, cbp, resid);
194 				if (cc == resid)
195 					break;
196 				/*
197 				 * Don't record errors during write,
198 				 * all errors will be found during
199 				 * check performed below.
200 				 */
201 				sn = iob[fd - 3].i_errblk;
202 				cbp += sn - sector;
203 				resid -= (sn - sector) * sizeof (struct sector);
204 				if (resid < sizeof (struct sector))
205 					break;
206 			}
207 			/*
208 			 * Read test patterns.
209 			 * Retry remainder of track on error until
210 			 * we're done, or until there's less than a
211 			 * sector to verify.
212 			 */
213 			for (resid = rtracksize, rcbp = rbp, sn = sector;;) {
214 				int cc, rsn;
215 
216 				lseek(fd, sn * SECTSIZ, 0);
217 				cc = read(fd, rcbp, resid);
218 				if (cc == resid)
219 					break;
220 				sn = iob[fd-3].i_errblk;
221 				if (ssdev) {
222 					rsn = sn - (sn / st.nsect);
223 					printf("data ");
224 				} else
225 					rsn = sn;
226 				printf("sector %d, read error\n\n", rsn);
227 				if (recorderror(fd, sn, &st) < 0 && pass > 0)
228 					goto out;
229 				/* advance past bad sector */
230 				sn++;
231 				resid = rtracksize - ((sn - sector) * SECTSIZ);
232 				rcbp = rbp + ((sn - sector) * SECTSIZ);
233 				if (resid < SECTSIZ)
234 					break;
235 			}
236 		}
237 	}
238 	/*
239 	 * Checking finished.
240 	 */
241 out:
242 	if (severe && maxpass < NPT) {
243 		cp = prompt("More passes? (0 or number) ");
244 		maxpass = atoi(cp);
245 		if (maxpass > 0) {
246 			maxpass += pass;
247 			goto more;
248 		}
249 	}
250 	if (severe && nbads) {
251 		/*
252 		 * Sort bads and insert in bad block table.
253 		 */
254 		qsort(bads, nbads, sizeof (daddr_t), qcompar);
255 		severe = 0;
256 		errno = 0;
257 		for (i = 0; i < nbads; i++)
258 			recorderror(fd, bads[i], &st);
259 		severe++;
260 	}
261 	if (errors[FE_TOTAL] || errors[FE_SSE]) {
262 		/* change the headers of all the bad sectors */
263 		writebb(fd, errors[FE_SSE], &sstab, &st, SSERR);
264 		writebb(fd, errors[FE_TOTAL], &dkbad, &st, BSERR);
265 	}
266 	if (errors[FE_TOTAL] || errors[FE_SSE]) {
267 		printf("Errors:\n");
268 		for (i = 0; i < NERRORS; i++)
269 			printf("%s: %d\n", errornames[i], errors[i]);
270 		printf("Total of %d hard errors revectored\n",
271 			errors[FE_TOTAL] + errors[FE_SSE]);
272 	}
273 	if (endcyl == st.ncyl - 1 &&
274 	    (startcyl < st.ncyl - 1 || starttrack == 0)) {
275 		while (errors[FE_TOTAL] < MAXBADDESC) {
276 			int i = errors[FE_TOTAL]++;
277 
278 			dkbad.bt_bad[i].bt_cyl = -1;
279 			dkbad.bt_bad[i].bt_trksec = -1;
280 		}
281 		printf("\nWriting bad sector table at sector #%d\n",
282 			st.ncyl * st.nspc - st.nsect);
283 		/* place on disk */
284 		for (i = 0; i < 10 && i < st.nsect; i += 2) {
285 			lseek(fd, SECTSIZ * (st.ncyl * st.nspc - st.nsect + i), 0);
286 			write(fd, &dkbad, sizeof (dkbad));
287 		}
288 	} else if (errors[FE_TOTAL]) {
289 		struct bt_bad *bt;
290 
291 		printf("New bad sectors (not added to table):\n");
292 		bt = dkbad.bt_bad;
293 		for (i = 0; i < errors[FE_TOTAL]; i++) {
294 			printf("bn %d (cn=%d, tn=%d, sn=%d)\n", badsn(bt, &st),
295 			    bt->bt_cyl, bt->bt_trksec>>8, bt->bt_trksec&0xff);
296 			bt++;
297 		}
298 	}
299 	printf("Done\n");
300 	ioctl(fd,SAIONOSSI,(char *)0);
301 	close(fd);
302 #ifndef JUSTEXIT
303 	goto again;
304 #endif
305 }
306 
307 qcompar(l1, l2)
308 register daddr_t *l1, *l2;
309 {
310 	if (*l1 < *l2)
311 		return(-1);
312 	if (*l1 == *l2)
313 		return(0);
314 	return(1);
315 }
316 
317 daddr_t
318 badsn(bt, st)
319 	register struct bt_bad *bt;
320 	register struct st *st;
321 {
322 
323 	if (ssdev)
324 	    return ((bt->bt_cyl * st->ntrak + (bt->bt_trksec>>8)) *
325 		(st->nsect - 1) + (bt->bt_trksec&0xff)) - 1;
326 	else
327 	    return ((bt->bt_cyl*st->ntrak + (bt->bt_trksec>>8)) * st->nsect
328 		+ (bt->bt_trksec&0xff));
329 }
330 
331 /*
332  * Mark the bad/skipped sectors.
333  * Bad sectors on skip-sector devices are assumed to be skipped also,
334  * and must be done after the (earlier) first skipped sector.
335  */
336 writebb(fd, nsects, dbad, st, sw)
337 	int nsects, fd;
338 	struct dkbad *dbad;
339 	register struct st *st;
340 {
341 	struct sector bb_buf; /* buffer for one sector plus 4 byte header */
342 	register int i;
343 	int bn, j;
344 	struct bt_bad *btp;
345 
346 	for (i = 0; i < nsects; i++) {
347 		btp = &dbad->bt_bad[i];
348 		if (sw == BSERR) {
349 			bb_buf.header1 = HDR1_FMT22|btp->bt_cyl;
350 			if (ssdev)
351 				bb_buf.header1 |= HDR1_SSF;
352 		} else
353 			bb_buf.header1 =
354 			       btp->bt_cyl | HDR1_FMT22 | HDR1_SSF | HDR1_OKSCT;
355 		bb_buf.header2 = btp->bt_trksec;
356 		bn = st->nspc * btp->bt_cyl +
357 		     st->nsect * (btp->bt_trksec >> 8) +
358 		     (btp->bt_trksec & 0xff);
359 		lseek(fd, bn * SECTSIZ, 0);
360 		ioctl(fd, SAIOHDR, (char *)0);
361 		write(fd, &bb_buf, sizeof (bb_buf));
362 		/*
363 		 * If skip sector, mark all remaining
364 		 * sectors on the track.
365 		 */
366 		if (sw == SSERR) {
367 			for (j = (btp->bt_trksec & 0xff) + 1, bn++;
368 			    j < st->nsect; j++, bn++) {
369 				bb_buf.header2 = j | (btp->bt_trksec & 0xff00);
370 				lseek(fd, bn * SECTSIZ, 0);
371 				ioctl(fd, SAIOHDR, (char *)0);
372 				write(fd, &bb_buf, sizeof (bb_buf));
373 			}
374 		}
375 	}
376 }
377 
378 /*
379  * Record an error, and if there's room, put
380  * it in the appropriate bad sector table.
381  *
382  * If severe burnin store block in a list after making sure
383  * we have not already found it on a prev pass.
384  */
385 recorderror(fd, bn, st)
386 	int fd, bn;
387 	register struct st *st;
388 {
389 	int cn, tn, sn;
390 	register i;
391 
392 
393 	if (severe) {
394 		for (i = 0; i < nbads; i++)
395 			if (bads[i] == bn)
396 				return(0);	/* bn already flagged */
397 		if (nbads >= (ssdev ? 2 * MAXBADDESC : MAXBADDESC)) {
398 			printf("Bad sector table full, format terminating\n");
399 			return(-1);
400 		}
401 		bads[nbads++] = bn;
402 		if (errno < EBSE || errno > EHER)
403 			return(0);
404 		errno -= EBSE;
405 		errors[errno]++;
406 		return(0);
407 	}
408 	if (errno >= EBSE && errno <= EHER) {
409 		errno -= EBSE;
410 		errors[errno]++;
411 	}
412 	cn = bn / st->nspc;
413 	sn = bn % st->nspc;
414 	tn = sn / st->nsect;
415 	sn %= st->nsect;
416 	if (ssdev) {		/* if drive has skip sector capability */
417 		int ss = errors[FE_SSE];
418 
419 		if (errors[FE_SSE] >= MAXBADDESC) {
420 			/* this is bogus, we don't maintain skip sector table */
421 			printf("Too many skip sector errors\n");
422 			return(-1);
423 		}
424 		  /* only one skip sector/track */
425 		if (ss == 0 ||
426 		    tn != (sstab.bt_bad[ss - 1].bt_trksec >> 8) ||
427 		    cn != sstab.bt_bad[ss - 1].bt_cyl) {
428 			/*
429 			 * Don't bother with skipping the extra sector
430 			 * at the end of the track.
431 			 */
432 			if (sn == st->nsect - 1)
433 				return(0);
434 			sstab.bt_bad[ss].bt_cyl = cn;
435 			sstab.bt_bad[ss].bt_trksec = (tn<<8) + sn;
436 			errors[FE_SSE]++;
437 			return(0);
438 		}
439 	}
440 	if (errors[FE_TOTAL] >= MAXBADDESC) {
441 		printf("Too many bad sectors\n");
442 		return(-1);
443 	}
444 	/* record the bad sector address and continue */
445 	dkbad.bt_bad[errors[FE_TOTAL]].bt_cyl = cn;
446 	dkbad.bt_bad[errors[FE_TOTAL]++].bt_trksec = (tn << 8) + sn;
447 	return(0);
448 }
449 
450 /*
451  * Allocate memory on a page-aligned address.
452  * Round allocated chunk to a page multiple to
453  * ease next request.
454  */
455 char *
456 malloc(size)
457 	int size;
458 {
459 	char *result;
460 	static caddr_t last = 0;
461 
462 	if (last == 0)
463 		last = (caddr_t)(((int)&end + 511) & ~0x1ff);
464 	size = (size + 511) & ~0x1ff;
465 	result = (char *)last;
466 	last += size;
467 	return (result);
468 }
469 
470 /*
471  * Prompt and verify a device name from the user.
472  */
473 getdevice()
474 {
475 	register char *cp;
476 	register struct devsw *dp;
477 	int fd;
478 
479 top:
480 	cp = prompt("Device to format? ");
481 	if ((fd = open(cp, 2)) < 0) {
482 		printf("Known devices are: ");
483 		for (dp = devsw; dp->dv_name; dp++)
484 			printf("%s ",dp->dv_name);
485 		printf("\n");
486 		goto top;
487 	}
488 	printf("Formatting drive %c%c%d on adaptor %d: ",
489 		cp[0], cp[1], iob[fd - 3].i_unit % 8, iob[fd - 3].i_unit / 8);
490 	cp = prompt("verify (yes/no)? ");
491 	while (*cp != 'y' && *cp != 'n')
492 		cp = prompt("Huh, yes or no? ");
493 	if (*cp == 'y')
494 		return (fd);
495 	goto top;
496 }
497 
498 /*
499  * Find range of tracks to format.
500  */
501 getrange(st)
502 	struct st *st;
503 {
504 	startcyl = getnum("Starting cylinder", 0, st->ncyl - 1, 0);
505 	starttrack = getnum("Starting track", 0, st->ntrak - 1, 0);
506 	endcyl = getnum("Ending cylinder", 0, st->ncyl - 1, st->ncyl - 1);
507 	endtrack = getnum("Ending track", 0, st->ntrak - 1, st->ntrak - 1);
508 }
509 
510 getnum(s, low, high, dflt)
511 {
512 	char buf[132];
513 	unsigned val;
514 
515 	while (1) {
516 		printf("%s (%d): ", s, dflt);
517 		gets(buf);
518 		if (buf[0] == 0)
519 			return (dflt);
520 		val = atoi(buf);
521 		if (val >= low && val <= high)
522 			return ((int)val);
523 		printf("Value must be in range [%d,%d]\n", low, high);
524 	}
525 }
526 
527 static struct pattern {
528 	long	pa_value;
529 	char	*pa_name;
530 } pat[] = {
531 	{ 0xf00ff00f, 	"RH750 worst case" },
532 	{ 0xec6dec6d,	"media worst case" },
533 	{ 0xa5a5a5a5,	"alternate 1's and 0's" },
534 	{ 0xFFFFFFFF,	"Severe burnin (up to 48 passes)" },
535 	{ 0, 0 },
536 };
537 
538 getpattern()
539 {
540 	register struct pattern *p;
541 	int npatterns;
542 	char *cp;
543 
544 	printf("Available test patterns are:\n");
545 	for (p = pat; p->pa_value; p++)
546 		printf("\t%d - (%x) %s\n", (p - pat) + 1,
547 		  p->pa_value & 0xffff, p->pa_name);
548 	npatterns = p - pat;
549 	cp = prompt("Pattern (one of the above, other to restart)? ");
550 	pattern = atoi(cp) - 1;
551 	if (pattern < 0 || pattern >= npatterns)
552 		return(1);
553 	severe = 0;
554 	maxpass = 1;
555 	if (pat[pattern].pa_value == -1) {
556 		severe = 1;
557 		cp = prompt("How many passes (up to 48)? ");
558 		maxpass = atoi(cp);
559 		if (maxpass > NPT)
560 			maxpass = NPT;
561 	}
562 	maxeccbits = getnum(
563 		"Maximum number of bit errors to allow for soft ECC",
564 		0, 11, MAXECCBITS);
565 	return (0);
566 }
567 
568 struct xsect {
569 	u_short	hd1;
570 	u_short	hd2;
571 	long	buf[128];
572 };
573 
574 /*
575  * Initialize the buffer with the requested pattern.
576  */
577 bufinit(bp, size)
578 	register struct xsect *bp;
579 	int size;
580 {
581 	register struct pattern *pptr;
582 	register long *pp, *last;
583 	register struct xsect *lastbuf;
584 	int patt;
585 
586 	size /= sizeof (struct sector);
587 	lastbuf = bp + size;
588 	if (severe) {
589 		patt = ppat[npat] | ((long)ppat[npat] << 16);
590 		printf("Write pattern 0x%x\n", patt&0xffff);
591 	} else {
592 		pptr = &pat[pattern];
593 		patt = pptr->pa_value;
594 	}
595 	while (bp < lastbuf) {
596 		last = &bp->buf[128];
597 		for (pp = bp->buf; pp < last; pp++)
598 			*pp = patt;
599 		bp++;
600 	}
601 }
602 
603 char *
604 prompt(msg)
605 	char *msg;
606 {
607 	static char buf[132];
608 
609 	printf("%s", msg);
610 	gets(buf);
611 	return (buf);
612 }
613