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