4.3BSD release version
[unix-history] / usr / src / sys / vax / stand / format.c
CommitLineData
da7c5cc6 1/*
0880b18e 2 * Copyright (c) 1980, 1986 Regents of the University of California.
da7c5cc6
KM
3 * All rights reserved. The Berkeley software License Agreement
4 * specifies the terms and conditions for redistribution.
5 */
6
7#ifndef lint
8char copyright[] =
0880b18e 9"@(#) Copyright (c) 1980, 1986 Regents of the University of California.\n\
da7c5cc6
KM
10 All rights reserved.\n";
11#endif not lint
71da8ead 12
da7c5cc6 13#ifndef lint
0880b18e 14static char sccsid[] = "@(#)format.c 7.1 (Berkeley) %G%";
da7c5cc6 15#endif not lint
001cf9d7 16
f5e63974 17
001cf9d7
SL
18/*
19 * Standalone program to do media checking
20 * and record bad block information on any
0f3feeae 21 * disk with the appropriate driver and RM03-style headers.
f5e63974
MK
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
001cf9d7
SL
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
f834e141
MK
44#define SSDEV(fd) (ioctl((fd), SAIOSSDEV, (char *)0) == 0)
45#define MAXECCBITS 3
001cf9d7
SL
46
47struct sector {
48 u_short header1;
49 u_short header2;
50 char buf[SECTSIZ];
51};
52
53struct dkbad dkbad; /* bad sector table */
f834e141 54struct dkbad oldbad; /* old bad sector table */
001cf9d7
SL
55struct dkbad sstab; /* skip sector table */
56
57#define NERRORS 6
c68cea4f
SL
58static char *
59errornames[NERRORS] = {
0f3feeae 60#define FE_BSE 0
001cf9d7 61 "Bad sector",
0f3feeae
MK
62#define FE_WCE 1
63 "Write check",
001cf9d7 64#define FE_ECC 2
f834e141 65 "Hard ECC",
001cf9d7
SL
66#define FE_HARD 3
67 "Other hard",
68#define FE_TOTAL 4
f834e141 69 "Marked bad",
001cf9d7 70#define FE_SSE 5
f834e141 71 "Skipped",
001cf9d7
SL
72};
73
74int errors[NERRORS]; /* histogram of errors */
c68cea4f 75int pattern;
f834e141 76int maxeccbits;
001cf9d7 77
71da8ead
MK
78/*
79 * Purdue/EE severe burnin patterns.
80 */
81unsigned short ppat[] = {
fb9e17ff 820xf00f, 0xec6d, 0031463,0070707,0133333,0155555,0161616,0143434,
71da8ead
MK
830107070,0016161,0034343,0044444,0022222,0111111,0125252, 052525,
840125252,0125252,0125252,0125252,0125252,0125252,0125252,0125252,
85#ifndef SHORTPASS
860125252,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))
fb9e17ff 93int maxpass, npat; /* subscript to ppat[] */
71da8ead 94int severe; /* nz if running "severe" burnin */
f834e141 95int ssdev; /* device supports skip sectors */
f5e63974 96int startcyl, endcyl, starttrack, endtrack;
f834e141
MK
97int nbads; /* subscript for bads */
98daddr_t bads[2*MAXBADDESC]; /* Bad blocks accumulated */
71da8ead 99
001cf9d7 100char *malloc();
71da8ead 101int qcompar();
001cf9d7 102char *prompt();
f5e63974 103daddr_t badsn();
001cf9d7
SL
104extern int end;
105
106main()
107{
108 register int sector, sn;
71da8ead 109 int lastsector, tracksize, rtracksize;
001cf9d7
SL
110 int unit, fd, resid, i, trk, cyl, debug;
111 struct st st;
112 struct sector *bp, *cbp;
71da8ead 113 char *rbp, *rcbp;
fb9e17ff 114 int pass;
001cf9d7
SL
115 char *cp;
116
117 printf("Disk format/check utility\n\n");
118
119again:
71da8ead
MK
120 nbads = 0;
121 cp = prompt("Enable debugging (0=none, 1=bse, 2=ecc, 3=bse+ecc)? ");
001cf9d7
SL
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);
f834e141
MK
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 }
f5e63974 138 getrange(&st);
c68cea4f 139 if (getpattern())
001cf9d7
SL
140 goto again;
141 printf("Start formatting...make sure the drive is online\n");
142 ioctl(fd, SAIONOBAD, (char *)0);
f834e141
MK
143 ioctl(fd, SAIORETRIES, (char *)0);
144 ioctl(fd, SAIOECCLIM, (char *)maxeccbits);
001cf9d7 145 ioctl(fd, SAIODEBUG, (char *)debug);
001cf9d7 146 tracksize = sizeof (struct sector) * st.nsect;
71da8ead 147 rtracksize = SECTSIZ * st.nsect;
001cf9d7 148 bp = (struct sector *)malloc(tracksize);
71da8ead 149 rbp = malloc(rtracksize);
fb9e17ff
MK
150 pass = 0;
151 npat = 0;
152more:
153 for (; pass < maxpass; pass++) {
71da8ead
MK
154 if (severe)
155 printf("Begin pass %d\n", pass);
156 bufinit(bp, tracksize);
157 if (severe)
158 npat++;
001cf9d7 159 /*
71da8ead 160 * Begin check, for each track,
001cf9d7 161 *
71da8ead
MK
162 * 1) Write header and test pattern.
163 * 2) Read data. Hardware checks header and data ECC.
fb9e17ff 164 * Read data (esp on Eagles) is much faster than write check.
001cf9d7 165 */
f5e63974
MK
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) {
71da8ead
MK
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);
001cf9d7 179 /*
71da8ead
MK
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)
001cf9d7 187 */
71da8ead
MK
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
f834e141 199 * check performed below.
71da8ead
MK
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;;) {
f834e141 214 int cc, rsn;
71da8ead
MK
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;
f834e141
MK
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);
71da8ead
MK
227 if (recorderror(fd, sn, &st) < 0 && pass > 0)
228 goto out;
229 /* advance past bad sector */
230 sn++;
f834e141
MK
231 resid = rtracksize - ((sn - sector) * SECTSIZ);
232 rcbp = rbp + ((sn - sector) * SECTSIZ);
71da8ead
MK
233 if (resid < SECTSIZ)
234 break;
235 }
001cf9d7
SL
236 }
237 }
238 /*
239 * Checking finished.
240 */
71da8ead 241out:
fb9e17ff
MK
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 }
71da8ead
MK
250 if (severe && nbads) {
251 /*
252 * Sort bads and insert in bad block table.
253 */
f834e141 254 qsort(bads, nbads, sizeof (daddr_t), qcompar);
71da8ead 255 severe = 0;
fb9e17ff
MK
256 errno = 0;
257 for (i = 0; i < nbads; i++)
71da8ead 258 recorderror(fd, bads[i], &st);
71da8ead
MK
259 severe++;
260 }
001cf9d7 261 if (errors[FE_TOTAL] || errors[FE_SSE]) {
001cf9d7
SL
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 }
f834e141
MK
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 }
f5e63974
MK
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]++;
001cf9d7 277
f5e63974
MK
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++) {
f834e141 294 printf("bn %d (cn=%d, tn=%d, sn=%d)\n", badsn(bt, &st),
f5e63974
MK
295 bt->bt_cyl, bt->bt_trksec>>8, bt->bt_trksec&0xff);
296 bt++;
297 }
001cf9d7
SL
298 }
299 printf("Done\n");
300 ioctl(fd,SAIONOSSI,(char *)0);
301 close(fd);
302#ifndef JUSTEXIT
303 goto again;
304#endif
305}
306
71da8ead 307qcompar(l1, l2)
f834e141 308register daddr_t *l1, *l2;
71da8ead
MK
309{
310 if (*l1 < *l2)
311 return(-1);
312 if (*l1 == *l2)
313 return(0);
314 return(1);
315}
316
f5e63974
MK
317daddr_t
318badsn(bt, st)
319 register struct bt_bad *bt;
320 register struct st *st;
321{
f834e141
MK
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
f5e63974
MK
328 + (bt->bt_trksec&0xff));
329}
330
001cf9d7 331/*
f5e63974 332 * Mark the bad/skipped sectors.
f834e141
MK
333 * Bad sectors on skip-sector devices are assumed to be skipped also,
334 * and must be done after the (earlier) first skipped sector.
001cf9d7
SL
335 */
336writebb(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;
f834e141 350 if (ssdev)
001cf9d7
SL
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) +
0f3feeae 358 (btp->bt_trksec & 0xff);
001cf9d7
SL
359 lseek(fd, bn * SECTSIZ, 0);
360 ioctl(fd, SAIOHDR, (char *)0);
361 write(fd, &bb_buf, sizeof (bb_buf));
001cf9d7
SL
362 /*
363 * If skip sector, mark all remaining
364 * sectors on the track.
365 */
f834e141
MK
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 }
001cf9d7
SL
374 }
375 }
376}
377
378/*
379 * Record an error, and if there's room, put
380 * it in the appropriate bad sector table.
71da8ead
MK
381 *
382 * If severe burnin store block in a list after making sure
383 * we have not already found it on a prev pass.
001cf9d7
SL
384 */
385recorderror(fd, bn, st)
386 int fd, bn;
387 register struct st *st;
388{
f5e63974 389 int cn, tn, sn;
71da8ead
MK
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 */
f834e141 397 if (nbads >= (ssdev ? 2 * MAXBADDESC : MAXBADDESC)) {
f5e63974 398 printf("Bad sector table full, format terminating\n");
71da8ead
MK
399 return(-1);
400 }
401 bads[nbads++] = bn;
fb9e17ff
MK
402 if (errno < EBSE || errno > EHER)
403 return(0);
404 errno -= EBSE;
405 errors[errno]++;
71da8ead
MK
406 return(0);
407 }
fb9e17ff 408 if (errno >= EBSE && errno <= EHER) {
fb9e17ff
MK
409 errno -= EBSE;
410 errors[errno]++;
001cf9d7 411 }
001cf9d7
SL
412 cn = bn / st->nspc;
413 sn = bn % st->nspc;
414 tn = sn / st->nsect;
415 sn %= st->nsect;
f834e141 416 if (ssdev) { /* if drive has skip sector capability */
f5e63974 417 int ss = errors[FE_SSE];
001cf9d7 418
f5e63974
MK
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) {
f834e141
MK
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);
001cf9d7
SL
434 sstab.bt_bad[ss].bt_cyl = cn;
435 sstab.bt_bad[ss].bt_trksec = (tn<<8) + sn;
f5e63974
MK
436 errors[FE_SSE]++;
437 return(0);
001cf9d7 438 }
f5e63974
MK
439 }
440 if (errors[FE_TOTAL] >= MAXBADDESC) {
441 printf("Too many bad sectors\n");
442 return(-1);
001cf9d7
SL
443 }
444 /* record the bad sector address and continue */
0f3feeae 445 dkbad.bt_bad[errors[FE_TOTAL]].bt_cyl = cn;
001cf9d7 446 dkbad.bt_bad[errors[FE_TOTAL]++].bt_trksec = (tn << 8) + sn;
71da8ead 447 return(0);
001cf9d7
SL
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 */
455char *
456malloc(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 */
473getdevice()
474{
475 register char *cp;
476 register struct devsw *dp;
477 int fd;
478
479top:
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 }
71da8ead
MK
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);
001cf9d7
SL
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
f5e63974
MK
498/*
499 * Find range of tracks to format.
500 */
501getrange(st)
502 struct st *st;
503{
504 startcyl = getnum("Starting cylinder", 0, st->ncyl - 1, 0);
f5e63974 505 starttrack = getnum("Starting track", 0, st->ntrak - 1, 0);
f834e141 506 endcyl = getnum("Ending cylinder", 0, st->ncyl - 1, st->ncyl - 1);
f5e63974
MK
507 endtrack = getnum("Ending track", 0, st->ntrak - 1, st->ntrak - 1);
508}
509
510getnum(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
c68cea4f
SL
527static 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" },
fb9e17ff 534 { 0xFFFFFFFF, "Severe burnin (up to 48 passes)" },
c68cea4f
SL
535 { 0, 0 },
536};
537
538getpattern()
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;
fb9e17ff
MK
551 if (pattern < 0 || pattern >= npatterns)
552 return(1);
71da8ead 553 severe = 0;
fb9e17ff
MK
554 maxpass = 1;
555 if (pat[pattern].pa_value == -1) {
71da8ead 556 severe = 1;
fb9e17ff
MK
557 cp = prompt("How many passes (up to 48)? ");
558 maxpass = atoi(cp);
559 if (maxpass > NPT)
560 maxpass = NPT;
561 }
f834e141
MK
562 maxeccbits = getnum(
563 "Maximum number of bit errors to allow for soft ECC",
564 0, 11, MAXECCBITS);
fb9e17ff 565 return (0);
c68cea4f
SL
566}
567
568struct 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 */
577bufinit(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;
71da8ead 584 int patt;
c68cea4f
SL
585
586 size /= sizeof (struct sector);
587 lastbuf = bp + size;
71da8ead
MK
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 }
c68cea4f
SL
595 while (bp < lastbuf) {
596 last = &bp->buf[128];
597 for (pp = bp->buf; pp < last; pp++)
71da8ead 598 *pp = patt;
c68cea4f
SL
599 bp++;
600 }
601}
602
001cf9d7
SL
603char *
604prompt(msg)
605 char *msg;
606{
607 static char buf[132];
608
609 printf("%s", msg);
610 gets(buf);
611 return (buf);
612}