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