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