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