1: /* 2: * SCCS id @(#)rm.c 2.1 (Berkeley) 9/1/83 3: */ 4: 5: /* 6: * RJM02/RWM03 disk driver 7: * 8: * For simplicity we use hpreg.h instead of an rmreg.h. The bits 9: * are the same. Only the register names have been changed to 10: * protect the innocent. 11: */ 12: 13: #include "rm.h" 14: #if NRM > 0 15: #include "param.h" 16: #include <sys/systm.h> 17: #include <sys/buf.h> 18: #include <sys/conf.h> 19: #include <sys/dir.h> 20: #include <sys/user.h> 21: #include <sys/hpreg.h> 22: #ifndef INTRLVE 23: #include <sys/inline.h> 24: #endif 25: #ifdef RM_DUMP 26: #include <sys/seg.h> 27: #endif 28: #include <sys/uba.h> 29: 30: extern struct hpdevice *RMADDR; 31: extern struct size rm_sizes[]; 32: 33: int rm_offset[] = 34: { 35: HPOF_P400, HPOF_M400, HPOF_P400, HPOF_M400, 36: HPOF_P800, HPOF_M800, HPOF_P800, HPOF_M800, 37: HPOF_P1200, HPOF_M1200, HPOF_P1200, HPOF_M1200, 38: 0, 0, 0, 0 39: }; 40: 41: #define RM_NSECT 32 42: #define RM_NTRAC 5 43: #define RM_SDIST 2 44: #define RM_RDIST 6 45: 46: struct buf rmtab; 47: #ifdef UCB_DBUFS 48: struct buf rrmbuf[NRM]; 49: #else 50: struct buf rrmbuf; 51: #endif 52: #if NRM > 1 53: struct buf rmutab[NRM]; 54: #endif 55: 56: #ifdef INTRLVE 57: extern daddr_t dkblock(); 58: #endif 59: #if NRM > 1 60: int rmcc[NRM]; /* Current cylinder */ 61: #endif 62: 63: void 64: rmroot() 65: { 66: rmattach(RMADDR, 0); 67: } 68: 69: rmattach(addr, unit) 70: register struct hpdevice *addr; 71: { 72: if (unit != 0) 73: return(0); 74: if ((addr != (struct hpdevice *) NULL) && (fioword(addr) != -1)) { 75: RMADDR = addr; 76: #if PDP11 == 70 || PDP11 == GENERIC 77: if (fioword(&(addr->hpbae)) != -1) 78: rmtab.b_flags |= B_RH70; 79: #endif 80: return(1); 81: } 82: RMADDR = (struct hpdevice *) NULL; 83: return(0); 84: } 85: 86: rmstrategy(bp) 87: register struct buf *bp; 88: { 89: register struct buf *dp; 90: register unit; 91: long bn; 92: 93: unit = minor(bp->b_dev) & 077; 94: if (unit >= (NRM << 3) || (RMADDR == (struct hpdevice *) NULL)) { 95: bp->b_error = ENXIO; 96: goto errexit; 97: } 98: if (bp->b_blkno < 0 || 99: (bn = dkblock(bp)) + (long) ((bp->b_bcount + 511) >> 9) 100: > rm_sizes[unit & 07].nblocks) { 101: bp->b_error = EINVAL; 102: errexit: 103: bp->b_flags |= B_ERROR; 104: iodone(bp); 105: return; 106: } 107: #ifdef UNIBUS_MAP 108: if ((rmtab.b_flags & B_RH70) == 0) 109: mapalloc(bp); 110: #endif 111: bp->b_cylin = bn / (RM_NSECT * RM_NTRAC) + rm_sizes[unit & 07].cyloff; 112: unit = dkunit(bp); 113: #if NRM > 1 114: dp = &rmutab[unit]; 115: #else 116: dp = &rmtab; 117: #endif 118: (void) _spl5(); 119: disksort(dp, bp); 120: if (dp->b_active == 0) { 121: #if NRM > 1 122: rmustart(unit); 123: if (rmtab.b_active == 0) 124: rmstart(); 125: #else 126: rmstart(); 127: #endif 128: } 129: (void) _spl0(); 130: } 131: 132: #if NRM > 1 133: rmustart(unit) 134: register unit; 135: { 136: register struct hpdevice *rmaddr = RMADDR; 137: register struct buf *dp; 138: struct buf *bp; 139: daddr_t bn; 140: int sn, cn, csn; 141: 142: rmaddr->hpcs2.w = unit; 143: rmaddr->hpcs1.c[0] = HP_IE; 144: rmaddr->hpas = 1 << unit; 145: 146: if (unit >= NRM) 147: return; 148: #ifdef RM_DKN 149: dk_busy &= ~(1 << (unit + RM_DKN)); 150: #endif 151: dp = &rmutab[unit]; 152: if ((bp=dp->b_actf) == NULL) 153: return; 154: /* 155: * If we have already positioned this drive, 156: * then just put it on the ready queue. 157: */ 158: if (dp->b_active) 159: goto done; 160: dp->b_active++; 161: /* 162: * If drive has just come up, 163: * set up the pack. 164: */ 165: if ((rmaddr->hpds & HPDS_VV) == 0) { 166: /* SHOULD WARN SYSTEM THAT THIS HAPPENED */ 167: rmaddr->hpcs1.c[0] = HP_IE | HP_PRESET | HP_GO; 168: rmaddr->hpof = HPOF_FMT22; 169: } 170: /* 171: * If drive is offline, forget about positioning. 172: */ 173: if ((rmaddr->hpds & (HPDS_DPR | HPDS_MOL)) != (HPDS_DPR | HPDS_MOL)) 174: goto done; 175: 176: bn = dkblock(bp); 177: cn = bp->b_cylin; 178: sn = bn % (RM_NSECT * RM_NTRAC); 179: sn = (sn + RM_NSECT - RM_SDIST) % RM_NSECT; 180: 181: if (rmcc[unit] != cn) 182: goto search; 183: csn = (rmaddr->hpla >> 6) - sn + RM_SDIST - 1; 184: if (csn < 0) 185: csn += RM_NSECT; 186: if (csn > RM_NSECT - RM_RDIST) 187: goto done; 188: 189: search: 190: rmaddr->hpdc = cn; 191: rmaddr->hpda = sn; 192: rmaddr->hpcs1.c[0] = HP_IE | HP_SEARCH | HP_GO; 193: rmcc[unit] = cn; 194: /* 195: * Mark unit busy for iostat. 196: */ 197: #ifdef RM_DKN 198: unit += RM_DKN; 199: dk_busy |= 1 << unit; 200: dk_numb[unit]++; 201: #endif 202: return; 203: 204: done: 205: /* 206: * Device is ready to go. 207: * Put it on the ready queue for the controller. 208: */ 209: dp->b_forw = NULL; 210: if (rmtab.b_actf == NULL) 211: rmtab.b_actf = dp; 212: else 213: rmtab.b_actl->b_forw = dp; 214: rmtab.b_actl = dp; 215: } 216: #endif 217: 218: /* 219: * Start up a transfer on a drive. 220: */ 221: rmstart() 222: { 223: register struct hpdevice *rmaddr = RMADDR; 224: register struct buf *bp; 225: struct buf *dp; 226: register unit; 227: daddr_t bn; 228: int dn, sn, tn, cn; 229: 230: loop: 231: /* 232: * Pull a request off the controller queue. 233: */ 234: #if NRM == 1 235: dp = &rmtab; 236: if ((bp = dp->b_actf) == NULL) 237: return; 238: #else 239: if ((dp = rmtab.b_actf) == NULL) 240: return; 241: if ((bp = dp->b_actf) == NULL) { 242: rmtab.b_actf = dp->b_forw; 243: goto loop; 244: } 245: #endif 246: /* 247: * Mark controller busy and 248: * determine destination of this request. 249: */ 250: rmtab.b_active++; 251: unit = minor(bp->b_dev) & 077; 252: dn = dkunit(bp); 253: bn = dkblock(bp); 254: cn = bn / (RM_NSECT * RM_NTRAC) + rm_sizes[unit & 07].cyloff; 255: sn = bn % (RM_NSECT * RM_NTRAC); 256: tn = sn / RM_NSECT; 257: sn = sn % RM_NSECT; 258: 259: /* 260: * Select drive. 261: */ 262: rmaddr->hpcs2.w = dn; 263: #if NRM == 1 264: /* 265: * If drive has just come up, 266: * set up the pack. 267: */ 268: if ((rmaddr->hpds & HPDS_VV) == 0) { 269: /* SHOULD WARN SYSTEM THAT THIS HAPPENED */ 270: rmaddr->hpcs1.c[0] = HP_IE | HP_PRESET | HP_GO; 271: rmaddr->hpof = HPOF_FMT22; 272: } 273: #endif 274: /* 275: * Check that it is ready and online. 276: */ 277: if ((rmaddr->hpds & (HPDS_DPR | HPDS_MOL)) != (HPDS_DPR | HPDS_MOL)) { 278: rmtab.b_active = 0; 279: rmtab.b_errcnt = 0; 280: dp->b_actf = bp->av_forw; 281: bp->b_flags |= B_ERROR; 282: iodone(bp); 283: goto loop; 284: } 285: if (rmtab.b_errcnt >= 16 && (bp->b_flags & B_READ)) { 286: rmaddr->hpof = rm_offset[rmtab.b_errcnt & 017] | HPOF_FMT22; 287: rmaddr->hpcs1.w = HP_OFFSET | HP_GO; 288: while ((rmaddr->hpds & (HPDS_PIP | HPDS_DRY)) != HPDS_DRY) 289: ; 290: } 291: rmaddr->hpdc = cn; 292: rmaddr->hpda = (tn << 8) + sn; 293: rmaddr->hpba = bp->b_un.b_addr; 294: #if PDP11 == 70 || PDP11 == GENERIC 295: if (rmtab.b_flags & B_RH70) 296: rmaddr->hpbae = bp->b_xmem; 297: #endif 298: rmaddr->hpwc = -(bp->b_bcount >> 1); 299: /* 300: * Warning: unit is being used as a temporary. 301: */ 302: unit = ((bp->b_xmem & 3) << 8) | HP_IE | HP_GO; 303: if (bp->b_flags & B_READ) 304: unit |= HP_RCOM; 305: else 306: unit |= HP_WCOM; 307: rmaddr->hpcs1.w = unit; 308: 309: #ifdef RM_DKN 310: dk_busy |= 1 << (RM_DKN + NRM); 311: dk_numb[RM_DKN + NRM]++; 312: dk_wds[RM_DKN + NRM] += bp->b_bcount >> 6; 313: #endif 314: } 315: 316: /* 317: * Handle a disk interrupt. 318: */ 319: rmintr() 320: { 321: register struct hpdevice *rmaddr = RMADDR; 322: register struct buf *bp, *dp; 323: short unit; 324: int as, i, j; 325: 326: as = rmaddr->hpas & 0377; 327: if (rmtab.b_active) { 328: #ifdef RM_DKN 329: dk_busy &= ~(1 << (RM_DKN + NRM)); 330: #endif 331: /* 332: * Get device and block structures. Select the drive. 333: */ 334: #if NRM > 1 335: dp = rmtab.b_actf; 336: #else 337: dp = &rmtab; 338: #endif 339: bp = dp->b_actf; 340: unit = dkunit(bp); 341: rmaddr->hpcs2.c[0] = unit; 342: /* 343: * Check for and process errors. 344: */ 345: if (rmaddr->hpcs1.w & HP_TRE) { /* error bit */ 346: while ((rmaddr->hpds & HPDS_DRY) == 0) 347: ; 348: if (rmaddr->hper1 & HPER1_WLE) { 349: /* 350: * Give up on write locked devices 351: * immediately. 352: */ 353: printf("rm%d: write locked\n", unit); 354: bp->b_flags |= B_ERROR; 355: } else { 356: /* 357: * After 28 retries (16 without offset and 358: * 12 with offset positioning), give up. 359: */ 360: if (++rmtab.b_errcnt > 28) { 361: bp->b_flags |= B_ERROR; 362: #ifdef UCB_DEVERR 363: harderr(bp, "rm"); 364: printf("cs2=%b er1=%b\n", rmaddr->hpcs2.w, 365: HPCS2_BITS, rmaddr->hper1, HPER1_BITS); 366: #else 367: deverror(bp, rmaddr->hpcs2.w, rmaddr->hper1); 368: #endif 369: } else 370: rmtab.b_active = 0; 371: } 372: #ifdef UCB_ECC 373: /* 374: * If soft ecc, correct it (continuing 375: * by returning if necessary). 376: * Otherwise, fall through and retry the transfer. 377: */ 378: if((rmaddr->hper1 & (HPER1_DCK|HPER1_ECH)) == HPER1_DCK) 379: if (rmecc(bp)) 380: return; 381: #endif 382: rmaddr->hpcs1.w = HP_TRE | HP_IE | HP_DCLR | HP_GO; 383: if ((rmtab.b_errcnt & 07) == 4) { 384: rmaddr->hpcs1.w = HP_RECAL | HP_IE | HP_GO; 385: while ((rmaddr->hpds & (HPDS_PIP | HPDS_DRY)) != HPDS_DRY) 386: ; 387: } 388: #if NRM > 1 389: rmcc[unit] = -1; 390: #endif 391: } 392: if (rmtab.b_active) { 393: if (rmtab.b_errcnt) { 394: rmaddr->hpcs1.w = HP_RTC | HP_GO; 395: while ((rmaddr->hpds & (HPDS_PIP | HPDS_DRY)) != HPDS_DRY) 396: ; 397: } 398: rmtab.b_errcnt = 0; 399: #if NRM > 1 400: rmtab.b_active = 0; 401: rmtab.b_actf = dp->b_forw; 402: rmcc[unit] = bp->b_cylin; 403: #endif 404: dp->b_active = 0; 405: dp->b_actf = bp->b_actf; 406: bp->b_resid = - (rmaddr->hpwc << 1); 407: iodone(bp); 408: rmaddr->hpcs1.w = HP_IE; 409: #if NRM > 1 410: if (dp->b_actf) 411: rmustart(unit); 412: #endif 413: } 414: #if NRM > 1 415: as &= ~(1 << unit); 416: #endif 417: } else { 418: if (as == 0) 419: rmaddr->hpcs1.w = HP_IE; 420: rmaddr->hpcs1.c[1] = HP_TRE >> 8; 421: } 422: #if NRM > 1 423: for(unit = 0; unit < NRM; unit++) 424: if (as & (1 << unit)) 425: rmustart(unit); 426: #endif 427: rmstart(); 428: } 429: 430: rmread(dev) 431: dev_t dev; 432: { 433: #ifdef UCB_DBUFS 434: register int unit = (minor(dev) >> 3) & 07; 435: 436: if (unit >= NRM) 437: u.u_error = ENXIO; 438: else 439: physio(rmstrategy, &rrmbuf[unit], dev, B_READ); 440: #else 441: physio(rmstrategy, &rrmbuf, dev, B_READ); 442: #endif 443: } 444: 445: rmwrite(dev) 446: dev_t dev; 447: { 448: #ifdef UCB_DBUFS 449: register int unit = (minor(dev) >> 3) & 07; 450: 451: if (unit >= NRM) 452: u.u_error = ENXIO; 453: else 454: physio(rmstrategy, &rrmbuf[unit], dev, B_WRITE); 455: #else 456: physio(rmstrategy, &rrmbuf, dev, B_WRITE); 457: #endif 458: } 459: 460: #ifdef UCB_ECC 461: #define exadr(x,y) (((long)(x) << 16) | (unsigned)(y)) 462: 463: /* 464: * Correct an ECC error and restart the i/o to complete 465: * the transfer if necessary. This is quite complicated because 466: * the correction may be going to an odd memory address base 467: * and the transfer may cross a sector boundary. 468: */ 469: rmecc(bp) 470: register struct buf *bp; 471: { 472: register struct hpdevice *rmaddr = RMADDR; 473: register unsigned byte; 474: ubadr_t bb, addr; 475: long wrong; 476: int bit, wc; 477: unsigned ndone, npx; 478: int ocmd; 479: int cn, tn, sn; 480: daddr_t bn; 481: #ifdef UNIBUS_MAP 482: struct ubmap *ubp; 483: #endif 484: 485: /* 486: * ndone is #bytes including the error 487: * which is assumed to be in the last disk page transferred. 488: */ 489: wc = rmaddr->hpwc; 490: ndone = (wc * NBPW) + bp->b_bcount; 491: npx = ndone / PGSIZE; 492: printf("rm%d%c: soft ecc bn %D\n", 493: dkunit(bp), 'a' + (minor(bp->b_dev) & 07), 494: bp->b_blkno + (npx - 1)); 495: wrong = rmaddr->hpec2; 496: if (wrong == 0) { 497: rmaddr->hpof = HPOF_FMT22; 498: rmaddr->hpcs1.w |= HP_IE; 499: return (0); 500: } 501: 502: /* 503: * Compute the byte/bit position of the err 504: * within the last disk page transferred. 505: * Hpec1 is origin-1. 506: */ 507: byte = rmaddr->hpec1 - 1; 508: bit = byte & 07; 509: byte >>= 3; 510: byte += ndone - PGSIZE; 511: bb = exadr(bp->b_xmem, bp->b_un.b_addr); 512: wrong <<= bit; 513: 514: /* 515: * Correct until mask is zero or until end of transfer, 516: * whichever comes first. 517: */ 518: while (byte < bp->b_bcount && wrong != 0) { 519: addr = bb + byte; 520: #ifdef UNIBUS_MAP 521: if (bp->b_flags & (B_MAP|B_UBAREMAP)) { 522: /* 523: * Simulate UNIBUS map if UNIBUS transfer 524: */ 525: ubp = UBMAP + ((addr >> 13) & 037); 526: addr = exadr(ubp->ub_hi, ubp->ub_lo) + (addr & 017777); 527: } 528: #endif 529: putmemc(addr, getmemc(addr) ^ (int) wrong); 530: byte++; 531: wrong >>= 8; 532: } 533: 534: rmtab.b_active++; 535: if (wc == 0) 536: return (0); 537: 538: /* 539: * Have to continue the transfer. Clear the drive 540: * and compute the position where the transfer is to continue. 541: * We have completed npx sectors of the transfer already. 542: */ 543: ocmd = (rmaddr->hpcs1.w & ~HP_RDY) | HP_IE | HP_GO; 544: rmaddr->hpcs2.w = dkunit(bp); 545: rmaddr->hpcs1.w = HP_TRE | HP_DCLR | HP_GO; 546: 547: bn = dkblock(bp); 548: cn = bp->b_cylin - bn / (RM_NSECT * RM_NTRAC); 549: bn += npx; 550: addr = bb + ndone; 551: 552: cn += bn / (RM_NSECT * RM_NTRAC); 553: sn = bn % (RM_NSECT * RM_NTRAC); 554: tn = sn / RM_NSECT; 555: sn %= RM_NSECT; 556: 557: rmaddr->hpdc = cn; 558: rmaddr->hpda = (tn << 8) + sn; 559: rmaddr->hpwc = ((int)(ndone - bp->b_bcount)) / NBPW; 560: rmaddr->hpba = (int) addr; 561: #if PDP11 == 70 || PDP11 == GENERIC 562: if (rmtab.b_flags & B_RH70) 563: rmaddr->hpbae = (int) (addr >> 16); 564: #endif 565: rmaddr->hpcs1.w = ocmd; 566: return (1); 567: } 568: #endif UCB_ECC 569: 570: #if defined(RM_DUMP) && defined(UCB_AUTOBOOT) 571: /* 572: * Dump routine for RM02/RM03. 573: * Dumps from dumplo to end of memory/end of disk section for minor(dev). 574: * It uses the UNIBUS map to dump all of memory if there is a UNIBUS map 575: * and this isn't an RM03. This depends on UNIBUS_MAP being defined. 576: * If there is no UNIBUS map, it will work with any definitions. 577: */ 578: 579: #ifdef UNIBUS_MAP 580: #define DBSIZE (UBPAGE/PGSIZE) /* unit of transfer, one UBPAGE */ 581: #else 582: #define DBSIZE 16 /* unit of transfer, same number */ 583: #endif 584: 585: rmdump(dev) 586: dev_t dev; 587: { 588: register struct hpdevice *rmaddr = RMADDR; 589: daddr_t bn, dumpsize; 590: long paddr; 591: register sn; 592: register count; 593: #ifdef UNIBUS_MAP 594: extern bool_t ubmap; 595: register struct ubmap *ubp; 596: #endif 597: 598: if ((bdevsw[major(dev)].d_strategy != rmstrategy) /* paranoia */ 599: || ((dev=minor(dev)) > (NRM << 3))) 600: return(EINVAL); 601: dumpsize = rm_sizes[dev & 07].nblocks; 602: if ((dumplo < 0) || (dumplo >= dumpsize)) 603: return(EINVAL); 604: dumpsize -= dumplo; 605: 606: rmaddr->hpcs2.w = dev >> 3; 607: if ((rmaddr->hpds & HPDS_VV) == 0) { 608: rmaddr->hpcs1.w = HP_DCLR | HP_GO; 609: rmaddr->hpcs1.w = HP_PRESET | HP_GO; 610: rmaddr->hpof = HPOF_FMT22; 611: } 612: if ((rmaddr->hpds & (HPDS_DPR | HPDS_MOL)) != (HPDS_DPR | HPDS_MOL)) 613: return(EFAULT); 614: dev &= 07; 615: #ifdef UNIBUS_MAP 616: ubp = &UBMAP[0]; 617: #endif 618: for (paddr = 0L; dumpsize > 0; dumpsize -= count) { 619: count = dumpsize>DBSIZE? DBSIZE: dumpsize; 620: bn = dumplo + (paddr >> PGSHIFT); 621: rmaddr->hpdc = bn / (RM_NSECT*RM_NTRAC) + rm_sizes[dev].cyloff; 622: sn = bn % (RM_NSECT * RM_NTRAC); 623: rmaddr->hpda = ((sn / RM_NSECT) << 8) | (sn % RM_NSECT); 624: rmaddr->hpwc = -(count << (PGSHIFT - 1)); 625: /* 626: * If UNIBUS_MAP exists, use 627: * the map, unless on an 11/70 with RM03. 628: */ 629: #ifdef UNIBUS_MAP 630: if (ubmap && ((rmtab.b_flags & B_RH70) == 0)) { 631: ubp->ub_lo = loint(paddr); 632: ubp->ub_hi = hiint(paddr); 633: rmaddr->hpba = 0; 634: rmaddr->hpcs1.w = HP_WCOM | HP_GO; 635: } 636: else 637: #endif 638: { 639: /* 640: * Non-UNIBUS map, or 11/70 RM03 (MASSBUS) 641: */ 642: rmaddr->hpba = loint(paddr); 643: #if PDP11 == 70 || PDP11 == GENERIC 644: if (rmtab.b_flags & B_RH70) 645: rmaddr->hpbae = hiint(paddr); 646: #endif 647: rmaddr->hpcs1.w = HP_WCOM | HP_GO | ((paddr >> 8) & (03 << 8)); 648: } 649: while (rmaddr->hpcs1.w & HP_GO) 650: ; 651: if (rmaddr->hpcs1.w & HP_TRE) { 652: if (rmaddr->hpcs2.w & HPCS2_NEM) 653: return(0); /* made it to end of memory */ 654: return(EIO); 655: } 656: paddr += (DBSIZE << PGSHIFT); 657: } 658: return(0); /* filled disk minor dev */ 659: } 660: #endif RM_DUMP 661: #endif NRM