|
|
1.1 ! root 1: #include "u.h" ! 2: #include "../port/lib.h" ! 3: #include "mem.h" ! 4: #include "dat.h" ! 5: #include "fns.h" ! 6: #include "io.h" ! 7: #include "../port/error.h" ! 8: ! 9: #include "devtab.h" ! 10: ! 11: #define DPRINT if(0)print ! 12: ! 13: typedef struct Drive Drive; ! 14: typedef struct Ident Ident; ! 15: typedef struct Controller Controller; ! 16: typedef struct Partition Partition; ! 17: typedef struct Repl Repl; ! 18: ! 19: enum ! 20: { ! 21: /* ports */ ! 22: Pbase0= 0x1F0, ! 23: Pbase1= 0x170, ! 24: Pdata= 0, /* data port (16 bits) */ ! 25: Perror= 1, /* error port (read) */ ! 26: Pprecomp= 1, /* buffer mode port (write) */ ! 27: Pcount= 2, /* sector count port */ ! 28: Psector= 3, /* sector number port */ ! 29: Pcyllsb= 4, /* least significant byte cylinder # */ ! 30: Pcylmsb= 5, /* most significant byte cylinder # */ ! 31: Pdh= 6, /* drive/head port */ ! 32: Pstatus= 7, /* status port (read) */ ! 33: Sbusy= (1<<7), ! 34: Sready= (1<<6), ! 35: Sdrq= (1<<3), ! 36: Serr= (1<<0), ! 37: Pcmd= 7, /* cmd port (write) */ ! 38: ! 39: /* commands */ ! 40: Crecal= 0x10, ! 41: Cread= 0x20, ! 42: Cwrite= 0x30, ! 43: Cident= 0xEC, ! 44: Cident2= 0xFF, /* pseudo command for post Cident interrupt */ ! 45: Csetbuf= 0xEF, ! 46: Cinitparam= 0x91, ! 47: ! 48: /* conner specific commands */ ! 49: Cstandby= 0xE2, ! 50: Cidle= 0xE1, ! 51: Cpowerdown= 0xE3, ! 52: ! 53: /* disk states */ ! 54: Sspinning, ! 55: Sstandby, ! 56: Sidle, ! 57: Spowerdown, ! 58: ! 59: /* something we have to or into the drive/head reg */ ! 60: DHmagic= 0xA0, ! 61: ! 62: /* file types */ ! 63: Qdir= 0, ! 64: ! 65: Maxxfer= BY2PG, /* maximum transfer size/cmd */ ! 66: Npart= 8+2, /* 8 sub partitions, disk, and partition */ ! 67: Nrepl= 64, /* maximum replacement blocks */ ! 68: ! 69: Hardtimeout= 4000, /* disk access timeout */ ! 70: }; ! 71: #define PART(x) ((x)&0xF) ! 72: #define DRIVE(x) (((x)>>4)&0x7) ! 73: #define MKQID(d,p) (((d)<<4) | (p)) ! 74: ! 75: struct Partition ! 76: { ! 77: ulong start; ! 78: ulong end; ! 79: char name[NAMELEN+1]; ! 80: }; ! 81: ! 82: struct Repl ! 83: { ! 84: Partition *p; ! 85: int nrepl; ! 86: ulong blk[Nrepl]; ! 87: }; ! 88: ! 89: #define PARTMAGIC "plan9 partitions" ! 90: #define REPLMAGIC "block replacements" ! 91: ! 92: /* ! 93: * an ata drive ! 94: */ ! 95: struct Drive ! 96: { ! 97: QLock; ! 98: ! 99: Controller *cp; ! 100: int drive; ! 101: int confused; /* needs to be recalibrated (or worse) */ ! 102: int online; ! 103: int npart; /* number of real partitions */ ! 104: Partition p[Npart]; ! 105: Repl repl; ! 106: ulong usetime; ! 107: int state; ! 108: char vol[NAMELEN]; ! 109: ! 110: ulong cap; /* total bytes */ ! 111: int bytes; /* bytes/sector */ ! 112: int sectors; /* sectors/track */ ! 113: int heads; /* heads/cyl */ ! 114: long cyl; /* cylinders/drive */ ! 115: ! 116: char lba; /* true if drive has logical block addressing */ ! 117: char multi; /* non-zero if drive does multiple block xfers */ ! 118: }; ! 119: ! 120: /* ! 121: * a controller for 2 drives ! 122: */ ! 123: struct Controller ! 124: { ! 125: QLock; /* exclusive access to the controller */ ! 126: ! 127: Lock reglock; /* exclusive access to the registers */ ! 128: ! 129: int confused; /* needs to be recalibrated (or worse) */ ! 130: int pbase; /* base port */ ! 131: ! 132: /* ! 133: * current operation ! 134: */ ! 135: int cmd; /* current command */ ! 136: int lastcmd; /* debugging info */ ! 137: Rendez r; /* wait here for command termination */ ! 138: char *buf; /* xfer buffer */ ! 139: int nsecs; /* length of transfer (sectors) */ ! 140: int sofar; /* sectors transferred so far */ ! 141: int status; ! 142: int error; ! 143: Drive *dp; /* drive being accessed */ ! 144: }; ! 145: ! 146: Controller *atac; ! 147: Drive *ata; ! 148: static int spindowntime; ! 149: ! 150: static void ataintr(Ureg*, void*); ! 151: static long ataxfer(Drive*, Partition*, int, long, long, char*); ! 152: static void ataident(Drive*); ! 153: static void atasetbuf(Drive*, int); ! 154: static void ataparams(Drive*); ! 155: static void atapart(Drive*); ! 156: static int ataprobe(Drive*, int, int, int); ! 157: ! 158: static int ! 159: atagen(Chan *c, Dirtab *tab, long ntab, long s, Dir *dirp) ! 160: { ! 161: Qid qid; ! 162: int drive; ! 163: char name[NAMELEN+4]; ! 164: Drive *dp; ! 165: Partition *pp; ! 166: ulong l; ! 167: ! 168: USED(tab, ntab); ! 169: qid.vers = 0; ! 170: drive = s/Npart; ! 171: s = s % Npart; ! 172: if(drive >= conf.nhard) ! 173: return -1; ! 174: dp = &ata[drive]; ! 175: ! 176: if(dp->online == 0 || s >= dp->npart) ! 177: return 0; ! 178: ! 179: pp = &dp->p[s]; ! 180: sprint(name, "%s%s", dp->vol, pp->name); ! 181: name[NAMELEN] = 0; ! 182: qid.path = MKQID(drive, s); ! 183: l = (pp->end - pp->start) * dp->bytes; ! 184: devdir(c, qid, name, l, eve, 0660, dirp); ! 185: return 1; ! 186: } ! 187: ! 188: void ! 189: atareset(void) ! 190: { ! 191: Drive *dp; ! 192: Controller *cp; ! 193: uchar equip; ! 194: char *p; ! 195: ! 196: equip = nvramread(0x12); ! 197: if(equip == 0) ! 198: equip = 0x10; /* the Globalyst 250 lies */ ! 199: ! 200: ata = xalloc(2 * sizeof(Drive)); ! 201: atac = xalloc(sizeof(Controller)); ! 202: ! 203: cp = atac; ! 204: cp->buf = 0; ! 205: cp->lastcmd = cp->cmd; ! 206: cp->cmd = 0; ! 207: cp->pbase = Pbase0; ! 208: setvec(ATAvec0, ataintr, 0); ! 209: ! 210: dp = ata; ! 211: if(equip & 0xf0){ ! 212: dp->drive = 0; ! 213: dp->online = 0; ! 214: dp->cp = cp; ! 215: dp++; ! 216: } ! 217: if((equip & 0x0f)){ ! 218: dp->drive = 1; ! 219: dp->online = 0; ! 220: dp->cp = cp; ! 221: dp++; ! 222: } ! 223: conf.nhard = dp - ata; ! 224: ! 225: if(conf.nhard && (p = getconf("spindowntime"))) ! 226: spindowntime = atoi(p); ! 227: } ! 228: ! 229: void ! 230: atainit(void) ! 231: { ! 232: } ! 233: ! 234: /* ! 235: * Get the characteristics of each drive. Mark unresponsive ones ! 236: * off line. ! 237: */ ! 238: Chan* ! 239: ataattach(char *spec) ! 240: { ! 241: Drive *dp; ! 242: ! 243: for(dp = ata; dp < &ata[conf.nhard]; dp++){ ! 244: if(waserror()){ ! 245: dp->online = 0; ! 246: qunlock(dp); ! 247: continue; ! 248: } ! 249: qlock(dp); ! 250: if(!dp->online){ ! 251: /* ! 252: * Make sure ataclock() doesn't ! 253: * interfere. ! 254: */ ! 255: dp->usetime = m->ticks; ! 256: ataparams(dp); ! 257: dp->online = 1; ! 258: atasetbuf(dp, 1); ! 259: } ! 260: ! 261: /* ! 262: * read Plan 9 partition table ! 263: */ ! 264: atapart(dp); ! 265: qunlock(dp); ! 266: poperror(); ! 267: } ! 268: return devattach('H', spec); ! 269: } ! 270: ! 271: Chan* ! 272: ataclone(Chan *c, Chan *nc) ! 273: { ! 274: return devclone(c, nc); ! 275: } ! 276: ! 277: int ! 278: atawalk(Chan *c, char *name) ! 279: { ! 280: return devwalk(c, name, 0, 0, atagen); ! 281: } ! 282: ! 283: void ! 284: atastat(Chan *c, char *dp) ! 285: { ! 286: devstat(c, dp, 0, 0, atagen); ! 287: } ! 288: ! 289: Chan* ! 290: ataopen(Chan *c, int omode) ! 291: { ! 292: return devopen(c, omode, 0, 0, atagen); ! 293: } ! 294: ! 295: void ! 296: atacreate(Chan *c, char *name, int omode, ulong perm) ! 297: { ! 298: USED(c, name, omode, perm); ! 299: error(Eperm); ! 300: } ! 301: ! 302: void ! 303: ataclose(Chan *c) ! 304: { ! 305: Drive *d; ! 306: Partition *p; ! 307: ! 308: if(c->mode != OWRITE && c->mode != ORDWR) ! 309: return; ! 310: ! 311: d = &ata[DRIVE(c->qid.path)]; ! 312: p = &d->p[PART(c->qid.path)]; ! 313: if(strcmp(p->name, "partition") != 0) ! 314: return; ! 315: ! 316: if(waserror()){ ! 317: qunlock(d); ! 318: nexterror(); ! 319: } ! 320: qlock(d); ! 321: atapart(d); ! 322: qunlock(d); ! 323: poperror(); ! 324: } ! 325: ! 326: void ! 327: ataremove(Chan *c) ! 328: { ! 329: USED(c); ! 330: error(Eperm); ! 331: } ! 332: ! 333: void ! 334: atawstat(Chan *c, char *dp) ! 335: { ! 336: USED(c, dp); ! 337: error(Eperm); ! 338: } ! 339: ! 340: long ! 341: ataread(Chan *c, void *a, long n, ulong offset) ! 342: { ! 343: Drive *dp; ! 344: long rv, i; ! 345: int skip; ! 346: uchar *aa = a; ! 347: Partition *pp; ! 348: char *buf; ! 349: ! 350: if(c->qid.path == CHDIR) ! 351: return devdirread(c, a, n, 0, 0, atagen); ! 352: ! 353: buf = smalloc(Maxxfer); ! 354: if(waserror()){ ! 355: free(buf); ! 356: nexterror(); ! 357: } ! 358: ! 359: dp = &ata[DRIVE(c->qid.path)]; ! 360: pp = &dp->p[PART(c->qid.path)]; ! 361: ! 362: skip = offset % dp->bytes; ! 363: for(rv = 0; rv < n; rv += i){ ! 364: i = ataxfer(dp, pp, Cread, offset+rv-skip, n-rv+skip, buf); ! 365: if(i == 0) ! 366: break; ! 367: i -= skip; ! 368: if(i > n - rv) ! 369: i = n - rv; ! 370: memmove(aa+rv, buf + skip, i); ! 371: skip = 0; ! 372: } ! 373: ! 374: free(buf); ! 375: poperror(); ! 376: ! 377: return rv; ! 378: } ! 379: ! 380: long ! 381: atawrite(Chan *c, void *a, long n, ulong offset) ! 382: { ! 383: Drive *dp; ! 384: long rv, i, partial; ! 385: uchar *aa = a; ! 386: Partition *pp; ! 387: char *buf; ! 388: ! 389: if(c->qid.path == CHDIR) ! 390: error(Eisdir); ! 391: ! 392: dp = &ata[DRIVE(c->qid.path)]; ! 393: pp = &dp->p[PART(c->qid.path)]; ! 394: buf = smalloc(Maxxfer); ! 395: if(waserror()){ ! 396: free(buf); ! 397: nexterror(); ! 398: } ! 399: ! 400: /* ! 401: * if not starting on a sector boundary, ! 402: * read in the first sector before writing ! 403: * it out. ! 404: */ ! 405: partial = offset % dp->bytes; ! 406: if(partial){ ! 407: ataxfer(dp, pp, Cread, offset-partial, dp->bytes, buf); ! 408: if(partial+n > dp->bytes) ! 409: rv = dp->bytes - partial; ! 410: else ! 411: rv = n; ! 412: memmove(buf+partial, aa, rv); ! 413: ataxfer(dp, pp, Cwrite, offset-partial, dp->bytes, buf); ! 414: } else ! 415: rv = 0; ! 416: ! 417: /* ! 418: * write out the full sectors ! 419: */ ! 420: partial = (n - rv) % dp->bytes; ! 421: n -= partial; ! 422: for(; rv < n; rv += i){ ! 423: i = n - rv; ! 424: if(i > Maxxfer) ! 425: i = Maxxfer; ! 426: memmove(buf, aa+rv, i); ! 427: i = ataxfer(dp, pp, Cwrite, offset+rv, i, buf); ! 428: if(i == 0) ! 429: break; ! 430: } ! 431: ! 432: /* ! 433: * if not ending on a sector boundary, ! 434: * read in the last sector before writing ! 435: * it out. ! 436: */ ! 437: if(partial){ ! 438: ataxfer(dp, pp, Cread, offset+rv, dp->bytes, buf); ! 439: memmove(buf, aa+rv, partial); ! 440: ataxfer(dp, pp, Cwrite, offset+rv, dp->bytes, buf); ! 441: rv += partial; ! 442: } ! 443: ! 444: free(buf); ! 445: poperror(); ! 446: ! 447: return rv; ! 448: } ! 449: ! 450: /* ! 451: * did an interrupt happen? ! 452: */ ! 453: static int ! 454: cmddone(void *a) ! 455: { ! 456: Controller *cp = a; ! 457: ! 458: return cp->cmd == 0; ! 459: } ! 460: ! 461: /* ! 462: * Wait for the controller to be ready to accept a command. ! 463: * This is protected from intereference by ataclock() by ! 464: * setting dp->usetime before it is called. ! 465: */ ! 466: static void ! 467: cmdreadywait(Drive *dp) ! 468: { ! 469: long start; ! 470: int period; ! 471: Controller *cp = dp->cp; ! 472: ! 473: /* give it 2 seconds to spin down and up */ ! 474: if(dp->state == Sspinning) ! 475: period = 10; ! 476: else ! 477: period = 2000; ! 478: ! 479: start = m->ticks; ! 480: while((inb(cp->pbase+Pstatus) & (Sready|Sbusy)) != Sready) ! 481: if(TK2MS(m->ticks - start) > period){ ! 482: DPRINT("cmdreadywait failed\n"); ! 483: error(Eio); ! 484: } ! 485: } ! 486: ! 487: static void ! 488: atarepl(Drive *dp, long bblk) ! 489: { ! 490: int i; ! 491: ! 492: if(dp->repl.p == 0) ! 493: return; ! 494: for(i = 0; i < dp->repl.nrepl; i++){ ! 495: if(dp->repl.blk[i] == bblk) ! 496: DPRINT("found bblk %ld at offset %ld\n", bblk, i); ! 497: } ! 498: } ! 499: ! 500: static void ! 501: atasleep(Controller *cp) ! 502: { ! 503: tsleep(&cp->r, cmddone, cp, Hardtimeout); ! 504: if(cp->cmd && cp->cmd != Cident2){ ! 505: DPRINT("hard drive timeout\n"); ! 506: error("ata drive timeout"); ! 507: } ! 508: } ! 509: ! 510: ! 511: /* ! 512: * transfer a number of sectors. ataintr will perform all the iterative ! 513: * parts. ! 514: */ ! 515: static long ! 516: ataxfer(Drive *dp, Partition *pp, int cmd, long start, long len, char *buf) ! 517: { ! 518: Controller *cp; ! 519: long lblk; ! 520: int cyl, sec, head; ! 521: int loop, stat; ! 522: ! 523: if(dp->online == 0) ! 524: error(Eio); ! 525: ! 526: /* ! 527: * cut transfer size down to disk buffer size ! 528: */ ! 529: start = start / dp->bytes; ! 530: if(len > Maxxfer) ! 531: len = Maxxfer; ! 532: len = (len + dp->bytes - 1) / dp->bytes; ! 533: if(len == 0) ! 534: return 0; ! 535: ! 536: /* ! 537: * calculate physical address ! 538: */ ! 539: lblk = start + pp->start; ! 540: if(lblk >= pp->end) ! 541: return 0; ! 542: if(lblk+len > pp->end) ! 543: len = pp->end - lblk; ! 544: if(dp->lba){ ! 545: sec = lblk & 0xff; ! 546: cyl = (lblk>>8) & 0xffff; ! 547: head = (lblk>>24) & 0xf; ! 548: } else { ! 549: cyl = lblk/(dp->sectors*dp->heads); ! 550: sec = (lblk % dp->sectors) + 1; ! 551: head = ((lblk/dp->sectors) % dp->heads); ! 552: } ! 553: ! 554: cp = dp->cp; ! 555: qlock(cp); ! 556: if(waserror()){ ! 557: cp->buf = 0; ! 558: qunlock(cp); ! 559: nexterror(); ! 560: } ! 561: ! 562: /* ! 563: * Make sure hardclock() doesn't ! 564: * interfere. ! 565: */ ! 566: dp->usetime = m->ticks; ! 567: cmdreadywait(dp); ! 568: ! 569: ilock(&cp->reglock); ! 570: cp->sofar = 0; ! 571: cp->buf = buf; ! 572: cp->nsecs = len; ! 573: cp->cmd = cmd; ! 574: cp->dp = dp; ! 575: cp->status = 0; ! 576: ! 577: outb(cp->pbase+Pcount, cp->nsecs); ! 578: outb(cp->pbase+Psector, sec); ! 579: outb(cp->pbase+Pdh, DHmagic | (dp->drive<<4) | (dp->lba<<6) | head); ! 580: outb(cp->pbase+Pcyllsb, cyl); ! 581: outb(cp->pbase+Pcylmsb, cyl>>8); ! 582: outb(cp->pbase+Pcmd, cmd); ! 583: ! 584: if(cmd == Cwrite){ ! 585: loop = 0; ! 586: while((stat = inb(cp->pbase+Pstatus) & (Serr|Sdrq)) == 0) ! 587: if(++loop > 10000) ! 588: panic("ataxfer"); ! 589: outss(cp->pbase+Pdata, cp->buf, dp->bytes/2); ! 590: } else ! 591: stat = 0; ! 592: iunlock(&cp->reglock); ! 593: ! 594: if(stat & Serr) ! 595: error(Eio); ! 596: ! 597: /* ! 598: * wait for command to complete. if we get a note, ! 599: * remember it but keep waiting to let the disk finish ! 600: * the current command. ! 601: */ ! 602: loop = 0; ! 603: while(waserror()){ ! 604: DPRINT("interrupted ataxfer\n"); ! 605: if(loop++ > 10){ ! 606: print("ata disk error\n"); ! 607: nexterror(); ! 608: } ! 609: } ! 610: atasleep(cp); ! 611: dp->state = Sspinning; ! 612: dp->usetime = m->ticks; ! 613: poperror(); ! 614: if(loop) ! 615: nexterror(); ! 616: ! 617: if(cp->status & Serr){ ! 618: DPRINT("hd%d err: lblk %ld status %lux, err %lux\n", ! 619: dp-ata, lblk, cp->status, cp->error); ! 620: DPRINT("\tcyl %d, sec %d, head %d\n", cyl, sec, head); ! 621: DPRINT("\tnsecs %d, sofar %d\n", cp->nsecs, cp->sofar); ! 622: atarepl(dp, lblk+cp->sofar); ! 623: error(Eio); ! 624: } ! 625: cp->buf = 0; ! 626: len = cp->sofar*dp->bytes; ! 627: qunlock(cp); ! 628: poperror(); ! 629: ! 630: return len; ! 631: } ! 632: ! 633: /* ! 634: * set read ahead mode ! 635: */ ! 636: static void ! 637: atasetbuf(Drive *dp, int on) ! 638: { ! 639: Controller *cp = dp->cp; ! 640: ! 641: qlock(cp); ! 642: if(waserror()){ ! 643: qunlock(cp); ! 644: nexterror(); ! 645: } ! 646: ! 647: cmdreadywait(dp); ! 648: ! 649: ilock(&cp->reglock); ! 650: cp->cmd = Csetbuf; ! 651: outb(cp->pbase+Pprecomp, on ? 0xAA : 0x55); /* read look ahead */ ! 652: outb(cp->pbase+Pdh, DHmagic | (dp->drive<<4)); ! 653: outb(cp->pbase+Pcmd, Csetbuf); ! 654: iunlock(&cp->reglock); ! 655: ! 656: atasleep(cp); ! 657: ! 658: /* if(cp->status & Serr) ! 659: DPRINT("hd%d setbuf err: status %lux, err %lux\n", ! 660: dp-ata, cp->status, cp->error);/**/ ! 661: ! 662: poperror(); ! 663: qunlock(cp); ! 664: } ! 665: ! 666: /* ! 667: * ident sector from drive. this is from ANSI X3.221-1994 ! 668: */ ! 669: struct Ident ! 670: { ! 671: ushort config; /* general configuration info */ ! 672: ushort cyls; /* # of cylinders (default) */ ! 673: ushort reserved0; ! 674: ushort heads; /* # of heads (default) */ ! 675: ushort b2t; /* unformatted bytes/track */ ! 676: ushort b2s; /* unformated bytes/sector */ ! 677: ushort s2t; /* sectors/track (default) */ ! 678: ushort reserved1[3]; ! 679: /* 10 */ ! 680: ushort serial[10]; /* serial number */ ! 681: ushort type; /* buffer type */ ! 682: ushort bsize; /* buffer size/512 */ ! 683: ushort ecc; /* ecc bytes returned by read long */ ! 684: ushort firm[4]; /* firmware revision */ ! 685: ushort model[20]; /* model number */ ! 686: /* 47 */ ! 687: ushort s2i; /* number of sectors/interrupt */ ! 688: ushort dwtf; /* double word transfer flag */ ! 689: ushort capabilities; ! 690: ushort reserved2; ! 691: ushort piomode; ! 692: ushort dmamode; ! 693: ushort cvalid; /* (cvald&1) if next 4 words are valid */ ! 694: ushort ccyls; /* current # cylinders */ ! 695: ushort cheads; /* current # heads */ ! 696: ushort cs2t; /* current sectors/track */ ! 697: ushort ccap[2]; /* current capacity in sectors */ ! 698: ushort cs2i; /* current number of sectors/interrupt */ ! 699: /* 60 */ ! 700: ushort lbasecs[2]; /* # LBA user addressable sectors */ ! 701: ushort dmasingle; ! 702: ushort dmadouble; ! 703: /* 64 */ ! 704: ushort reserved3[64]; ! 705: ushort vendor[32]; /* vendor specific */ ! 706: ushort reserved4[96]; ! 707: }; ! 708: ! 709: /* ! 710: * get parameters from the drive ! 711: */ ! 712: static void ! 713: ataident(Drive *dp) ! 714: { ! 715: Controller *cp; ! 716: char *buf; ! 717: Ident *ip; ! 718: char id[21]; ! 719: ! 720: cp = dp->cp; ! 721: buf = smalloc(Maxxfer); ! 722: qlock(cp); ! 723: if(waserror()){ ! 724: qunlock(cp); ! 725: nexterror(); ! 726: } ! 727: ! 728: cmdreadywait(dp); ! 729: ! 730: ilock(&cp->reglock); ! 731: cp->nsecs = 1; ! 732: cp->sofar = 0; ! 733: cp->cmd = Cident; ! 734: cp->dp = dp; ! 735: cp->buf = buf; ! 736: outb(cp->pbase+Pdh, DHmagic | (dp->drive<<4)); ! 737: outb(cp->pbase+Pcmd, Cident); ! 738: iunlock(&cp->reglock); ! 739: ! 740: atasleep(cp); ! 741: ! 742: if(cp->status & Serr){ ! 743: DPRINT("bad disk ident status\n"); ! 744: error(Eio); ! 745: } ! 746: ip = (Ident*)buf; ! 747: ! 748: /* ! 749: * this function appears to respond with an extra interrupt after ! 750: * the ident information is read, except on the safari. The following ! 751: * delay gives this extra interrupt a chance to happen while we are quiet. ! 752: * Otherwise, the interrupt may come during a subsequent read or write, ! 753: * causing a panic and much confusion. ! 754: */ ! 755: if (cp->cmd == Cident2) ! 756: tsleep(&cp->r, return0, 0, Hardtimeout); ! 757: ! 758: memmove(id, ip->model, sizeof(id)-1); ! 759: id[sizeof(id)-1] = 0; ! 760: ! 761: if(ip->capabilities & (1<<9)){ ! 762: dp->lba = 1; ! 763: dp->sectors = (ip->lbasecs[0]) | (ip->lbasecs[1]<<16); ! 764: dp->cap = dp->bytes * dp->sectors; ! 765: /*print("\nata%d model %s with %d lba sectors\n", dp->drive, id, dp->sectors);/**/ ! 766: } else { ! 767: dp->lba = 0; ! 768: ! 769: /* use default (unformatted) settings */ ! 770: dp->cyl = ip->cyls; ! 771: dp->heads = ip->heads; ! 772: dp->sectors = ip->s2t; ! 773: /*print("\nata%d model %s with default %d cyl %d head %d sec\n", dp->drive, ! 774: id, dp->cyl, dp->heads, dp->sectors);/**/ ! 775: ! 776: if(ip->cvalid&(1<<0)){ ! 777: /* use current settings */ ! 778: dp->cyl = ip->ccyls; ! 779: dp->heads = ip->cheads; ! 780: dp->sectors = ip->cs2t; ! 781: /*print("\tchanged to %d cyl %d head %d sec\n", dp->cyl, dp->heads, dp->sectors);/**/ ! 782: } ! 783: dp->cap = dp->bytes * dp->cyl * dp->heads * dp->sectors; ! 784: } ! 785: cp->lastcmd = cp->cmd; ! 786: cp->cmd = 0; ! 787: cp->buf = 0; ! 788: free(buf); ! 789: poperror(); ! 790: qunlock(cp); ! 791: } ! 792: ! 793: /* ! 794: * probe the given sector to see if it exists ! 795: */ ! 796: static int ! 797: ataprobe(Drive *dp, int cyl, int sec, int head) ! 798: { ! 799: Controller *cp; ! 800: char *buf; ! 801: int rv; ! 802: ! 803: cp = dp->cp; ! 804: buf = smalloc(Maxxfer); ! 805: qlock(cp); ! 806: if(waserror()){ ! 807: free(buf); ! 808: qunlock(cp); ! 809: nexterror(); ! 810: } ! 811: ! 812: cmdreadywait(dp); ! 813: ! 814: ilock(&cp->reglock); ! 815: cp->cmd = Cread; ! 816: cp->dp = dp; ! 817: cp->status = 0; ! 818: cp->nsecs = 1; ! 819: cp->sofar = 0; ! 820: ! 821: outb(cp->pbase+Pcount, 1); ! 822: outb(cp->pbase+Psector, sec+1); ! 823: outb(cp->pbase+Pdh, DHmagic | head | (dp->lba<<6) | (dp->drive<<4)); ! 824: outb(cp->pbase+Pcyllsb, cyl); ! 825: outb(cp->pbase+Pcylmsb, cyl>>8); ! 826: outb(cp->pbase+Pcmd, Cread); ! 827: iunlock(&cp->reglock); ! 828: ! 829: atasleep(cp); ! 830: ! 831: if(cp->status & Serr) ! 832: rv = -1; ! 833: else ! 834: rv = 0; ! 835: ! 836: cp->buf = 0; ! 837: free(buf); ! 838: poperror(); ! 839: qunlock(cp); ! 840: return rv; ! 841: } ! 842: ! 843: /* ! 844: * figure out the drive parameters ! 845: */ ! 846: static void ! 847: ataparams(Drive *dp) ! 848: { ! 849: int i, hi, lo; ! 850: ! 851: /* ! 852: * first try the easy way, ask the drive and make sure it ! 853: * isn't lying. ! 854: */ ! 855: dp->bytes = 512; ! 856: ataident(dp); ! 857: if(dp->lba){ ! 858: i = dp->sectors - 1; ! 859: if(ataprobe(dp, (i>>8)&0xffff, (i&0xff)-1, (i>>24)&0xf) == 0) ! 860: return; ! 861: } else { ! 862: if(ataprobe(dp, dp->cyl-1, dp->sectors-1, dp->heads-1) == 0) ! 863: return; ! 864: } ! 865: ! 866: /* ! 867: * the drive lied, determine parameters by seeing which ones ! 868: * work to read sectors. ! 869: */ ! 870: dp->lba = 0; ! 871: for(i = 0; i < 32; i++) ! 872: if(ataprobe(dp, 0, 0, i) < 0) ! 873: break; ! 874: dp->heads = i; ! 875: for(i = 0; i < 128; i++) ! 876: if(ataprobe(dp, 0, i, 0) < 0) ! 877: break; ! 878: dp->sectors = i; ! 879: for(i = 512; ; i += 512) ! 880: if(ataprobe(dp, i, dp->sectors-1, dp->heads-1) < 0) ! 881: break; ! 882: lo = i - 512; ! 883: hi = i; ! 884: for(; hi-lo > 1;){ ! 885: i = lo + (hi - lo)/2; ! 886: if(ataprobe(dp, i, dp->sectors-1, dp->heads-1) < 0) ! 887: hi = i; ! 888: else ! 889: lo = i; ! 890: } ! 891: dp->cyl = lo + 1; ! 892: dp->cap = dp->bytes * dp->cyl * dp->heads * dp->sectors; ! 893: } ! 894: ! 895: /* ! 896: * Read block replacement table. ! 897: * The table is just ascii block numbers. ! 898: */ ! 899: static void ! 900: atareplinit(Drive *dp) ! 901: { ! 902: char *line[Nrepl+1]; ! 903: char *field[1]; ! 904: ulong n; ! 905: int i; ! 906: char *buf; ! 907: ! 908: /* ! 909: * check the partition is big enough ! 910: */ ! 911: if(dp->repl.p->end - dp->repl.p->start < Nrepl+1){ ! 912: dp->repl.p = 0; ! 913: return; ! 914: } ! 915: ! 916: buf = smalloc(Maxxfer); ! 917: if(waserror()){ ! 918: free(buf); ! 919: nexterror(); ! 920: } ! 921: ! 922: /* ! 923: * read replacement table from disk, null terminate ! 924: */ ! 925: ataxfer(dp, dp->repl.p, Cread, 0, dp->bytes, buf); ! 926: buf[dp->bytes-1] = 0; ! 927: ! 928: /* ! 929: * parse replacement table. ! 930: */ ! 931: n = getfields(buf, line, Nrepl+1, "\n"); ! 932: if(strncmp(line[0], REPLMAGIC, sizeof(REPLMAGIC)-1)){ ! 933: dp->repl.p = 0; ! 934: } else { ! 935: for(dp->repl.nrepl = 0, i = 1; i < n; i++, dp->repl.nrepl++){ ! 936: if(getfields(line[i], field, 1, " ") != 1) ! 937: break; ! 938: dp->repl.blk[dp->repl.nrepl] = strtoul(field[0], 0, 0); ! 939: if(dp->repl.blk[dp->repl.nrepl] <= 0) ! 940: break; ! 941: } ! 942: } ! 943: free(buf); ! 944: poperror(); ! 945: } ! 946: ! 947: /* ! 948: * read partition table. The partition table is just ascii strings. ! 949: */ ! 950: static void ! 951: atapart(Drive *dp) ! 952: { ! 953: Partition *pp; ! 954: char *line[Npart+1]; ! 955: char *field[3]; ! 956: ulong n; ! 957: int i; ! 958: char *buf; ! 959: ! 960: sprint(dp->vol, "hd%d", dp - ata); ! 961: ! 962: /* ! 963: * we always have a partition for the whole disk ! 964: * and one for the partition table ! 965: */ ! 966: pp = &dp->p[0]; ! 967: strcpy(pp->name, "disk"); ! 968: pp->start = 0; ! 969: pp->end = dp->cap / dp->bytes; ! 970: pp++; ! 971: strcpy(pp->name, "partition"); ! 972: pp->start = dp->p[0].end - 1; ! 973: pp->end = dp->p[0].end; ! 974: dp->npart = 2; ! 975: ! 976: /* ! 977: * initialise the bad-block replacement info ! 978: */ ! 979: dp->repl.p = 0; ! 980: ! 981: buf = smalloc(Maxxfer); ! 982: if(waserror()){ ! 983: free(buf); ! 984: nexterror(); ! 985: } ! 986: ! 987: /* ! 988: * read last sector from disk, null terminate. This used ! 989: * to be the sector we used for the partition tables. ! 990: * However, this sector is special on some PC's so we've ! 991: * started to use the second last sector as the partition ! 992: * table instead. To avoid reconfiguring all our old systems ! 993: * we first look to see if there is a valid partition ! 994: * table in the last sector. If so, we use it. Otherwise ! 995: * we switch to the second last. ! 996: */ ! 997: ataxfer(dp, pp, Cread, 0, dp->bytes, buf); ! 998: buf[dp->bytes-1] = 0; ! 999: n = getfields(buf, line, Npart+1, "\n"); ! 1000: if(n == 0 || strncmp(line[0], PARTMAGIC, sizeof(PARTMAGIC)-1)){ ! 1001: dp->p[0].end--; ! 1002: dp->p[1].start--; ! 1003: dp->p[1].end--; ! 1004: ataxfer(dp, pp, Cread, 0, dp->bytes, buf); ! 1005: buf[dp->bytes-1] = 0; ! 1006: n = getfields(buf, line, Npart+1, "\n"); ! 1007: } ! 1008: ! 1009: /* ! 1010: * parse partition table. ! 1011: */ ! 1012: if(n && strncmp(line[0], PARTMAGIC, sizeof(PARTMAGIC)-1) == 0){ ! 1013: for(i = 1; i < n; i++){ ! 1014: pp++; ! 1015: switch(getfields(line[i], field, 3, " ")) { ! 1016: case 2: ! 1017: if(strcmp(field[0], "unit") == 0) ! 1018: strncpy(dp->vol, field[1], NAMELEN); ! 1019: break; ! 1020: case 3: ! 1021: strncpy(pp->name, field[0], NAMELEN); ! 1022: if(strncmp(pp->name, "repl", NAMELEN) == 0) ! 1023: dp->repl.p = pp; ! 1024: pp->start = strtoul(field[1], 0, 0); ! 1025: pp->end = strtoul(field[2], 0, 0); ! 1026: if(pp->start > pp->end || pp->start >= dp->p[0].end) ! 1027: break; ! 1028: dp->npart++; ! 1029: } ! 1030: } ! 1031: } ! 1032: free(buf); ! 1033: poperror(); ! 1034: ! 1035: if(dp->repl.p) ! 1036: atareplinit(dp); ! 1037: } ! 1038: ! 1039: enum ! 1040: { ! 1041: Maxloop= 10000, ! 1042: }; ! 1043: ! 1044: /* ! 1045: * we get an interrupt for every sector transferred ! 1046: */ ! 1047: static void ! 1048: ataintr(Ureg *ur, void *arg) ! 1049: { ! 1050: Controller *cp; ! 1051: Drive *dp; ! 1052: long loop; ! 1053: char *addr; ! 1054: ! 1055: USED(ur, arg); ! 1056: ! 1057: /* ! 1058: * BUG!! if there is ever more than one controller, we need a way to ! 1059: * distinguish which interrupted (use arg). ! 1060: */ ! 1061: cp = &atac[0]; ! 1062: dp = cp->dp; ! 1063: ! 1064: ilock(&cp->reglock); ! 1065: ! 1066: loop = 0; ! 1067: while((cp->status = inb(cp->pbase+Pstatus)) & Sbusy){ ! 1068: if(++loop > Maxloop) { ! 1069: DPRINT("cmd=%lux status=%lux\n", ! 1070: cp->cmd, inb(cp->pbase+Pstatus)); ! 1071: panic("ataintr: wait busy"); ! 1072: } ! 1073: } ! 1074: ! 1075: switch(cp->cmd){ ! 1076: case Cwrite: ! 1077: if(cp->status & Serr){ ! 1078: cp->lastcmd = cp->cmd; ! 1079: cp->cmd = 0; ! 1080: cp->error = inb(cp->pbase+Perror); ! 1081: wakeup(&cp->r); ! 1082: break; ! 1083: } ! 1084: cp->sofar++; ! 1085: if(cp->sofar < cp->nsecs){ ! 1086: loop = 0; ! 1087: while(((cp->status = inb(cp->pbase+Pstatus)) & Sdrq) == 0) ! 1088: if(++loop > Maxloop) { ! 1089: DPRINT("cmd=%lux status=%lux\n", ! 1090: cp->cmd, inb(cp->pbase+Pstatus)); ! 1091: panic("ataintr: write"); ! 1092: } ! 1093: addr = cp->buf; ! 1094: if(addr){ ! 1095: addr += cp->sofar*dp->bytes; ! 1096: outss(cp->pbase+Pdata, addr, dp->bytes/2); ! 1097: } ! 1098: } else{ ! 1099: cp->lastcmd = cp->cmd; ! 1100: cp->cmd = 0; ! 1101: wakeup(&cp->r); ! 1102: } ! 1103: break; ! 1104: case Cread: ! 1105: case Cident: ! 1106: loop = 0; ! 1107: while((cp->status & (Serr|Sdrq)) == 0){ ! 1108: if(++loop > Maxloop) { ! 1109: DPRINT("cmd=%lux status=%lux\n", ! 1110: cp->cmd, inb(cp->pbase+Pstatus)); ! 1111: panic("ataintr: read/ident"); ! 1112: } ! 1113: cp->status = inb(cp->pbase+Pstatus); ! 1114: } ! 1115: if(cp->status & Serr){ ! 1116: cp->lastcmd = cp->cmd; ! 1117: cp->cmd = 0; ! 1118: cp->error = inb(cp->pbase+Perror); ! 1119: wakeup(&cp->r); ! 1120: break; ! 1121: } ! 1122: addr = cp->buf; ! 1123: if(addr){ ! 1124: addr += cp->sofar*dp->bytes; ! 1125: inss(cp->pbase+Pdata, addr, dp->bytes/2); ! 1126: } ! 1127: cp->sofar++; ! 1128: if(cp->sofar > cp->nsecs) ! 1129: print("ataintr %d %d\n", cp->sofar, cp->nsecs); ! 1130: if(cp->sofar >= cp->nsecs){ ! 1131: cp->lastcmd = cp->cmd; ! 1132: if (cp->cmd == Cread) ! 1133: cp->cmd = 0; ! 1134: else ! 1135: cp->cmd = Cident2; ! 1136: wakeup(&cp->r); ! 1137: } ! 1138: break; ! 1139: case Cinitparam: ! 1140: case Csetbuf: ! 1141: case Cidle: ! 1142: case Cstandby: ! 1143: case Cpowerdown: ! 1144: cp->lastcmd = cp->cmd; ! 1145: cp->cmd = 0; ! 1146: wakeup(&cp->r); ! 1147: break; ! 1148: case Cident2: ! 1149: cp->lastcmd = cp->cmd; ! 1150: cp->cmd = 0; ! 1151: break; ! 1152: default: ! 1153: print("weird disk interrupt, cmd=%.2ux, lastcmd= %.2ux status=%.2ux\n", ! 1154: cp->cmd, cp->lastcmd, cp->status); ! 1155: break; ! 1156: } ! 1157: ! 1158: iunlock(&cp->reglock); ! 1159: } ! 1160: ! 1161: void ! 1162: hardclock(void) ! 1163: { ! 1164: int drive; ! 1165: Drive *dp; ! 1166: Controller *cp; ! 1167: int diff; ! 1168: ! 1169: if(spindowntime <= 0) ! 1170: return; ! 1171: ! 1172: for(drive = 0; drive < conf.nhard; drive++){ ! 1173: dp = &ata[drive]; ! 1174: cp = dp->cp; ! 1175: ! 1176: diff = TK2SEC(m->ticks - dp->usetime); ! 1177: if((dp->state == Sspinning) && (diff >= spindowntime)){ ! 1178: ilock(&cp->reglock); ! 1179: cp->cmd = Cstandby; ! 1180: outb(cp->pbase+Pcount, 0); ! 1181: outb(cp->pbase+Pdh, DHmagic | (dp->drive<<4) | 0); ! 1182: outb(cp->pbase+Pcmd, cp->cmd); ! 1183: iunlock(&cp->reglock); ! 1184: dp->state = Sstandby; ! 1185: } ! 1186: } ! 1187: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.