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