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
main()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
qcompar(l1,l2)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
badsn(bt,lp)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 */
writebb(fd,nsects,dbad,lp,sw)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 */
recorderror(fd,bn,lp)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 *
malloc(size)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 */
getdevice()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 */
getrange(lp)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
getnum(s,low,high,dflt)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
getpattern()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 */
bufinit(bp,size)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 *
prompt(msg)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