1 /* 2 * Copyright (c) 1982, 1988 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 * @(#)up.c 7.9 (Berkeley) 04/04/90 7 */ 8 9 /* 10 * UNIBUS peripheral standalone driver with ECC correction and bad 11 * block forwarding. Also supports header operation and write check 12 * for data and/or header. 13 */ 14 #include "param.h" 15 #include "dkbad.h" 16 #include "disklabel.h" 17 18 #include "../vax/pte.h" 19 20 #include "../vaxuba/upreg.h" 21 #include "../vaxuba/ubareg.h" 22 23 #include "saio.h" 24 #include "savax.h" 25 26 #define RETRIES 27 27 28 #define MAXBADDESC 126 /* max number of bad sectors recorded */ 29 #define SECTSIZ 512 /* sector size in bytes */ 30 #define HDRSIZ 4 /* number of bytes in sector header */ 31 32 #define MAXUNIT 8 33 #define MAXCTLR 1 /* all addresses must be specified */ 34 static u_short upstd[MAXCTLR] = { 0776700 }; 35 static struct disklabel uplabel[MAXNUBA][MAXCTLR][MAXUNIT]; 36 static char lbuf[SECTSIZ]; 37 38 extern struct st upst[]; 39 40 #ifndef SMALL 41 struct dkbad upbad[MAXNUBA][MAXCTLR][MAXUNIT]; /* bad sector table */ 42 #endif 43 int sectsiz; /* real sector size */ 44 45 struct up_softc { 46 char gottype; 47 char type; 48 char debug; 49 # define UPF_BSEDEBUG 01 /* debugging bad sector forwarding */ 50 # define UPF_ECCDEBUG 02 /* debugging ecc correction */ 51 int retries; 52 int ecclim; 53 } up_softc[MAXNUBA][MAXCTLR][MAXUNIT]; 54 55 u_char up_offset[16] = { 56 UPOF_P400, UPOF_M400, UPOF_P400, UPOF_M400, 57 UPOF_P800, UPOF_M800, UPOF_P800, UPOF_M800, 58 UPOF_P1200, UPOF_M1200, UPOF_P1200, UPOF_M1200, 59 0, 0, 0, 0 60 }; 61 62 upopen(io) 63 register struct iob *io; 64 { 65 register struct updevice *upaddr; 66 register struct up_softc *sc; 67 register struct st *st; 68 register struct disklabel *lp; 69 struct disklabel *dlp; 70 register int unit; 71 int error = 0, uba, ctlr; 72 73 if ((u_int)(uba = io->i_adapt) >= nuba) 74 return (EADAPT); 75 if ((u_int)(ctlr = io->i_ctlr) >= MAXCTLR) 76 return (ECTLR); 77 unit = io->i_unit; 78 if ((u_int)unit >= MAXUNIT) 79 return (EUNIT); 80 upaddr = (struct updevice *)ubamem(uba, upstd[ctlr]); 81 upaddr->upcs2 = unit; 82 while ((upaddr->upcs1 & UP_DVA) == 0); 83 sc = &up_softc[uba][ctlr][unit]; 84 lp = &uplabel[uba][ctlr][unit]; 85 if (sc->gottype == 0) { 86 register int i; 87 struct iob tio; 88 89 #ifndef SMALL 90 sc->retries = RETRIES; 91 sc->ecclim = 11; 92 sc->debug = 0; 93 #endif 94 /* Read in the pack label. */ 95 lp->d_nsectors = 32; 96 lp->d_secpercyl = 19*32; 97 tio = *io; 98 tio.i_bn = LABELSECTOR; 99 tio.i_ma = lbuf; 100 tio.i_cc = SECTSIZ; 101 tio.i_flgs |= F_RDDATA; 102 if (upstrategy(&tio, READ) != SECTSIZ) 103 error = ERDLAB; 104 dlp = (struct disklabel *)(lbuf + LABELOFFSET); 105 if (error == 0 && (dlp->d_magic != DISKMAGIC || 106 dlp->d_magic2 != DISKMAGIC)) 107 error = EUNLAB; 108 if (error == 0) 109 *lp = *dlp; 110 else 111 #ifdef COMPAT_42 112 if (upmaptype(unit, upaddr, lp) == 0) 113 #endif 114 return (error); 115 116 #ifndef SMALL 117 /* Read in the bad sector table. */ 118 tio.i_bn = lp->d_secpercyl * lp->d_ncylinders - lp->d_nsectors; 119 tio.i_ma = (char *)&upbad[uba][ctlr][unit]; 120 tio.i_cc = sizeof(struct dkbad); 121 tio.i_flgs |= F_RDDATA; 122 for (i = 0; i < 5; i++) { 123 if (upstrategy(&tio, READ) == sizeof(struct dkbad)) 124 break; 125 tio.i_bn += 2; 126 } 127 if (i == 5) { 128 printf("up: can't read bad sector table\n"); 129 for (i = 0; i < MAXBADDESC; i++) { 130 upbad[uba][ctlr][unit].bt_bad[i].bt_cyl = -1; 131 upbad[uba][ctlr][unit].bt_bad[i].bt_trksec = -1; 132 } 133 } 134 #endif 135 sc->gottype = 1; 136 } 137 if (io->i_part >= lp->d_npartitions || 138 lp->d_partitions[io->i_part].p_size == 0) 139 return (EPART); 140 io->i_boff = lp->d_partitions[io->i_part].p_offset; 141 io->i_flgs &= ~F_TYPEMASK; 142 return (0); 143 } 144 145 upstrategy(io, func) 146 register struct iob *io; 147 int func; 148 { 149 int cn, tn, sn, o; 150 register unit = io->i_unit; 151 register daddr_t bn; 152 int recal, info, waitdry; 153 register struct updevice *upaddr; 154 register struct disklabel *lp; 155 struct up_softc *sc; 156 int error, rv = io->i_cc; 157 #ifndef SMALL 158 int doprintf = 0; 159 #endif 160 161 upaddr = (struct updevice *)ubamem(io->i_adapt, upstd[io->i_ctlr]); 162 sc = &up_softc[io->i_adapt][io->i_ctlr][unit]; 163 lp = &uplabel[io->i_adapt][io->i_ctlr][unit]; 164 sectsiz = SECTSIZ; 165 #ifndef SMALL 166 if (io->i_flgs & (F_HDR|F_HCHECK)) 167 sectsiz += HDRSIZ; 168 #endif 169 upaddr->upcs2 = unit; 170 if ((upaddr->upds & UPDS_VV) == 0) { 171 upaddr->upcs1 = UP_DCLR|UP_GO; 172 upaddr->upcs1 = UP_PRESET|UP_GO; 173 upaddr->upof = UPOF_FMT22; 174 } 175 if ((upaddr->upds & UPDS_DREADY) == 0) { 176 printf("up%d not ready\n", unit); 177 return (-1); 178 } 179 info = ubasetup(io, 1); 180 upaddr->upwc = -io->i_cc / sizeof (short); 181 recal = 0; 182 io->i_errcnt = 0; 183 184 restart: 185 o = io->i_cc + (upaddr->upwc * sizeof (short)); 186 upaddr->upba = info + o; 187 bn = io->i_bn + o / sectsiz; 188 #ifndef SMALL 189 error = 0; 190 if (doprintf && sc->debug & (UPF_ECCDEBUG|UPF_BSEDEBUG)) 191 printf("wc=%d o=%d i_bn=%d bn=%d\n", 192 upaddr->upwc, o, io->i_bn, bn); 193 #endif 194 upwaitdry(upaddr); 195 if (upstart(io, bn, lp) != 0) { 196 rv = -1; 197 goto done; 198 } 199 upwaitrdy(upaddr); 200 /* 201 * If transfer has completed, free UNIBUS 202 * resources and return transfer size. 203 */ 204 if ((upaddr->upds&UPDS_ERR) == 0 && (upaddr->upcs1&UP_TRE) == 0) 205 goto done; 206 bn = io->i_bn + 207 (io->i_cc + upaddr->upwc * sizeof (short)) / sectsiz; 208 if (upaddr->uper1 & (UPER1_DCK|UPER1_ECH)) 209 bn--; 210 cn = bn / lp->d_secpercyl; 211 sn = bn % lp->d_secpercyl; 212 tn = sn / lp->d_nsectors; 213 sn = sn % lp->d_nsectors; 214 #ifndef SMALL 215 if (sc->debug & (UPF_ECCDEBUG|UPF_BSEDEBUG)) { 216 printf("up error: sn%d (cyl,trk,sec)=(%d,%d,%d) ", 217 bn, cn, tn, sn); 218 printf("cs2=%b er1=%b er2=%b wc=%d\n", 219 upaddr->upcs2, UPCS2_BITS, upaddr->uper1, 220 UPER1_BITS, upaddr->uper2, UPER2_BITS, upaddr->upwc); 221 } 222 #endif 223 waitdry = 0; 224 while ((upaddr->upds&UPDS_DRY) == 0 && ++waitdry < sectsiz) 225 DELAY(5); 226 #ifndef SMALL 227 if (upaddr->uper1&UPER1_WLE) { 228 /* 229 * Give up on write locked devices immediately. 230 */ 231 printf("up%d: write locked\n", unit); 232 rv = -1; 233 goto done; 234 } 235 if (upaddr->uper2 & UPER2_BSE) { 236 if ((io->i_flgs&F_NBSF) == 0 && upecc(io, BSE) == 0) 237 goto success; 238 error = EBSE; 239 goto hard; 240 } 241 /* 242 * ECC error. If a soft error, correct it; 243 * if correction is too large, no more retries. 244 */ 245 if ((upaddr->uper1 & (UPER1_DCK|UPER1_ECH|UPER1_HCRC)) == UPER1_DCK) { 246 if (upecc(io, ECC) == 0) 247 goto success; 248 error = EECC; 249 goto hard; 250 } 251 /* 252 * If the error is a header CRC, check if a replacement sector 253 * exists in the bad sector table. 254 */ 255 if ((upaddr->uper1&UPER1_HCRC) && (io->i_flgs&F_NBSF) == 0 && 256 upecc(io, BSE) == 0) 257 goto success; 258 #endif 259 if (++io->i_errcnt > sc->retries) { 260 /* 261 * After 28 retries (16 without offset, and 262 * 12 with offset positioning) give up. 263 */ 264 hard: 265 #ifndef SMALL 266 if (error == 0) { 267 error = EHER; 268 if (upaddr->upcs2 & UPCS2_WCE) 269 error = EWCK; 270 } 271 io->i_error = error; 272 #endif 273 printf("up error: sn%d (cyl,trk,sec)=(%d,%d,%d) ", 274 bn, cn, tn, sn); 275 printf("cs2=%b er1=%b er2=%b\n", 276 upaddr->upcs2, UPCS2_BITS, upaddr->uper1, 277 UPER1_BITS, upaddr->uper2, UPER2_BITS); 278 upaddr->upcs1 = UP_TRE|UP_DCLR|UP_GO; 279 io->i_errblk = bn; 280 if (io->i_errcnt >= 16) { 281 upaddr->upof = UPOF_FMT22; 282 upaddr->upcs1 = UP_RTC|UP_GO; 283 upwaitdry(upaddr); 284 } 285 rv = -1; 286 goto done; 287 } 288 /* 289 * Clear drive error and, every eight attempts, (starting with the 290 * fourth) recalibrate to clear the slate. 291 */ 292 upaddr->upcs1 = UP_TRE|UP_DCLR|UP_GO; 293 if ((io->i_errcnt&07) == 4 ) { 294 upaddr->upcs1 = UP_RECAL|UP_GO; 295 upwaitdry(upaddr); 296 upaddr->updc = cn; 297 upaddr->upcs1 = UP_SEEK|UP_GO; 298 upwaitdry(upaddr); 299 } 300 if (io->i_errcnt >= 16 && (func & READ)) { 301 upaddr->upof = up_offset[io->i_errcnt & 017] | UPOF_FMT22; 302 upaddr->upcs1 = UP_OFFSET|UP_GO; 303 upwaitdry(upaddr); 304 } 305 goto restart; 306 307 success: 308 #define rounddown(x, y) (((x) / (y)) * (y)) 309 upaddr->upwc = rounddown(upaddr->upwc, sectsiz / sizeof (short)); 310 if (upaddr->upwc) { 311 #ifndef SMALL 312 doprintf++; 313 #endif 314 goto restart; 315 } 316 done: 317 ubafree(io, info); 318 /* 319 * If we were offset positioning, 320 * return to centerline. 321 */ 322 if (io->i_errcnt >= 16) { 323 upaddr->upof = UPOF_FMT22; 324 upaddr->upcs1 = UP_RTC|UP_GO; 325 upwaitdry(upaddr); 326 } 327 return (rv); 328 } 329 330 upwaitrdy(upaddr) 331 register struct updevice *upaddr; 332 { 333 do { 334 DELAY(25); 335 } while ((upaddr->upcs1 & UP_RDY) == 0); 336 } 337 338 upwaitdry(upaddr) 339 register struct updevice *upaddr; 340 { 341 while ((upaddr->upds&UPDS_DRY) == 0) 342 DELAY(25); 343 } 344 345 #ifndef SMALL 346 /* 347 * Correct an ECC error, and restart the i/o to complete the transfer (if 348 * necessary). This is quite complicated because the transfer may be going 349 * to an odd memory address base and/or across a page boundary. 350 */ 351 upecc(io, flag) 352 register struct iob *io; 353 int flag; 354 { 355 register i, unit; 356 register struct up_softc *sc; 357 register struct updevice *up; 358 register struct disklabel *lp; 359 caddr_t addr; 360 int bn, twc, npf, mask, cn, tn, sn; 361 daddr_t bbn; 362 363 /* 364 * Npf is the number of sectors transferred 365 * before the sector containing the ECC error; 366 * bn is the current block number. 367 */ 368 unit = io->i_unit; 369 sc = &up_softc[io->i_adapt][io->i_ctlr][unit]; 370 lp = &uplabel[io->i_adapt][io->i_ctlr][unit]; 371 up = (struct updevice *)ubamem(io->i_adapt, upstd[io->i_ctlr]); 372 twc = up->upwc; 373 npf = ((twc * sizeof(short)) + io->i_cc) / sectsiz; 374 if (flag == ECC) 375 npf--; 376 if (sc->debug & UPF_ECCDEBUG) 377 printf("npf=%d mask=0x%x ec1=%d wc=%d\n", 378 npf, up->upec2, up->upec1, twc); 379 bn = io->i_bn + npf; 380 cn = bn / lp->d_secpercyl; 381 sn = bn % lp->d_secpercyl; 382 tn = sn / lp->d_nsectors; 383 sn = sn % lp->d_nsectors; 384 385 /* 386 * ECC correction. 387 */ 388 if (flag == ECC) { 389 int bit, o; 390 391 mask = up->upec2; 392 printf("up%d: soft ecc sn%d\n", unit, bn); 393 for (i = mask, bit = 0; i; i >>= 1) 394 if (i & 1) 395 bit++; 396 if (bit > sc->ecclim) { 397 printf("%d-bit error\n", bit); 398 return (1); 399 } 400 /* 401 * Compute the byte and bit position of 402 * the error. o is the byte offset in 403 * the transfer at which the correction 404 * applied. 405 */ 406 i = up->upec1 - 1; /* -1 makes 0 origin */ 407 bit = i & 07; 408 o = (i & ~07) >> 3; 409 up->upcs1 = UP_TRE|UP_DCLR|UP_GO; 410 /* 411 * Correct while possible bits remain of mask. 412 * Since mask contains 11 bits, we continue while 413 * the bit offset is > -11. Also watch out for 414 * end of this block and the end of the transfer. 415 */ 416 while (o < sectsiz && (npf*sectsiz)+o < io->i_cc && bit > -11) { 417 /* 418 * addr = 419 * (base address of transfer) + 420 * (# sectors transferred before the error) * 421 * (sector size) + 422 * (byte offset to incorrect data) 423 */ 424 addr = io->i_ma + (npf * sectsiz) + o; 425 /* 426 * No data transfer occurs with a write check, 427 * so don't correct the resident copy of data. 428 */ 429 if ((io->i_flgs & (F_CHECK|F_HCHECK)) == 0) { 430 if (sc->debug & UPF_ECCDEBUG) 431 printf("addr=0x%x old=0x%x ", addr, 432 (*addr&0xff)); 433 *addr ^= (mask << bit); 434 if (sc->debug & UPF_ECCDEBUG) 435 printf("new=0x%x\n", (*addr&0xff)); 436 } 437 o++, bit -= 8; 438 } 439 return (0); 440 } 441 442 /* 443 * Bad sector forwarding. 444 */ 445 if (flag == BSE) { 446 /* 447 * If not in bad sector table, 448 * indicate a hard error to caller. 449 */ 450 up->upcs1 = UP_TRE|UP_DCLR|UP_GO; 451 if ((bbn = isbad(&upbad[io->i_adapt][io->i_ctlr][unit], cn, tn, sn)) < 0) 452 return (1); 453 bbn = (lp->d_ncylinders * lp->d_secpercyl) - 454 lp->d_nsectors - 1 - bbn; 455 twc = up->upwc + sectsiz; 456 up->upwc = - (sectsiz / sizeof (short)); 457 if (sc->debug & UPF_BSEDEBUG) 458 printf("revector sn %d to %d\n", sn, bbn); 459 /* 460 * Clear the drive & read the replacement 461 * sector. If this is in the middle of a 462 * transfer, then set up the controller 463 * registers in a normal fashion. 464 * The UNIBUS address need not be changed. 465 */ 466 upwaitrdy(up); 467 if (upstart(io, bbn, lp)) 468 return (1); /* error */ 469 io->i_errcnt = 0; /* success */ 470 upwaitrdy(up); 471 if ((up->upds & UPDS_ERR) || (up->upcs1 & UP_TRE)) { 472 up->upwc = twc - sectsiz; 473 return (1); 474 } 475 } 476 if (twc) 477 up->upwc = twc; 478 return (0); 479 } 480 #endif /* !SMALL */ 481 482 upstart(io, bn, lp) 483 register struct iob *io; 484 daddr_t bn; 485 register struct disklabel *lp; 486 { 487 register struct updevice *upaddr; 488 register struct up_softc *sc; 489 int sn, tn; 490 491 upaddr = (struct updevice *)ubamem(io->i_adapt, upstd[io->i_ctlr]); 492 sc = &up_softc[io->i_adapt][io->i_ctlr][io->i_unit]; 493 sn = bn % lp->d_secpercyl; 494 tn = sn / lp->d_nsectors; 495 sn = sn % lp->d_nsectors; 496 upaddr->updc = bn / lp->d_secpercyl; 497 upaddr->upda = (tn << 8) + sn; 498 switch (io->i_flgs & F_TYPEMASK) { 499 500 case F_RDDATA: 501 upaddr->upcs1 = UP_RCOM|UP_GO; 502 break; 503 504 case F_WRDATA: 505 upaddr->upcs1 = UP_WCOM|UP_GO; 506 break; 507 508 #ifndef SMALL 509 case F_HDR|F_RDDATA: 510 upaddr->upcs1 = UP_RHDR|UP_GO; 511 break; 512 513 case F_HDR|F_WRDATA: 514 upaddr->upcs1 = UP_WHDR|UP_GO; 515 break; 516 517 case F_CHECK|F_WRDATA: 518 case F_CHECK|F_RDDATA: 519 upaddr->upcs1 = UP_WCDATA|UP_GO; 520 break; 521 522 case F_HCHECK|F_WRDATA: 523 case F_HCHECK|F_RDDATA: 524 upaddr->upcs1 = UP_WCHDR|UP_GO; 525 break; 526 #endif 527 528 default: 529 io->i_error = ECMD; 530 io->i_flgs &= ~F_TYPEMASK; 531 return (1); 532 } 533 return (0); 534 } 535 536 #ifndef SMALL 537 /*ARGSUSED*/ 538 upioctl(io, cmd, arg) 539 struct iob *io; 540 int cmd; 541 caddr_t arg; 542 { 543 register struct up_softc *sc; 544 545 sc = &up_softc[io->i_adapt][io->i_ctlr][io->i_unit]; 546 switch(cmd) { 547 case SAIODEBUG: 548 sc->debug = (int)arg; 549 break; 550 case SAIODEVDATA: 551 *(struct disklabel *)arg = 552 uplabel[io->i_adapt][io->i_ctlr][io->i_unit]; 553 break; 554 case SAIOGBADINFO: 555 *(struct dkbad *)arg = 556 upbad[io->i_adapt][io->i_ctlr][io->i_unit]; 557 break; 558 case SAIOECCLIM: 559 sc->ecclim = (int)arg; 560 break; 561 case SAIORETRIES: 562 sc->retries = (int)arg; 563 break; 564 default: 565 return (ECMD); 566 } 567 return (0); 568 } 569 #endif /* !SMALL */ 570