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