|
|
1.1 root 1: #include "u.h"
2: #include "lib.h"
3: #include "mem.h"
4: #include "dat.h"
5: #include "fns.h"
6: #include "io.h"
7:
8: #define DPRINT if(0)print
9:
10: typedef struct Drive Drive;
11: typedef struct Ident Ident;
12: typedef struct Controller Controller;
13:
14: enum
15: {
16: /* ports */
17: Pbase= 0x1F0,
18: Pdata= 0, /* data port (16 bits) */
19: Perror= 1, /* error port (read) */
20: Pprecomp= 1, /* buffer mode port (write) */
21: Pcount= 2, /* sector count port */
22: Psector= 3, /* sector number port */
23: Pcyllsb= 4, /* least significant byte cylinder # */
24: Pcylmsb= 5, /* most significant byte cylinder # */
25: Pdh= 6, /* drive/head port */
26: Pstatus= 7, /* status port (read) */
27: Sbusy= (1<<7),
28: Sready= (1<<6),
29: Sdrq= (1<<3),
30: Serr= (1<<0),
31: Pcmd= 7, /* cmd port (write) */
32:
33: /* commands */
34: Crecal= 0x10,
35: Cread= 0x20,
36: Cwrite= 0x30,
37: Cident= 0xEC,
38: Cident2= 0xFF, /* pseudo command for post Cident interrupt */
39: Csetbuf= 0xEF,
40:
41: /* file types */
42: Qdir= 0,
43:
44: /* magic bit in drive head register */
45: Dmagic= (1<<5),
46:
47: Timeout= 4, /* seconds to wait for things to complete */
48: };
49:
50: /*
51: * ident sector from drive. this is from ANSI X3.221-1994
52: */
53: struct Ident
54: {
55: ushort config; /* general configuration info */
56: ushort cyls; /* # of cylinders (default) */
57: ushort reserved0;
58: ushort heads; /* # of heads (default) */
59: ushort b2t; /* unformatted bytes/track */
60: ushort b2s; /* unformated bytes/sector */
61: ushort s2t; /* sectors/track (default) */
62: ushort reserved1[3];
63: /* 10 */
64: ushort serial[10]; /* serial number */
65: ushort type; /* buffer type */
66: ushort bsize; /* buffer size/512 */
67: ushort ecc; /* ecc bytes returned by read long */
68: ushort firm[4]; /* firmware revision */
69: ushort model[20]; /* model number */
70: /* 47 */
71: ushort s2i; /* number of sectors/interrupt */
72: ushort dwtf; /* double word transfer flag */
73: ushort capabilities;
74: ushort reserved2;
75: ushort piomode;
76: ushort dmamode;
77: ushort cvalid; /* (cvald&1) if next 4 words are valid */
78: ushort ccyls; /* current # cylinders */
79: ushort cheads; /* current # heads */
80: ushort cs2t; /* current sectors/track */
81: ushort ccap[2]; /* current capacity in sectors */
82: ushort cs2i; /* current number of sectors/interrupt */
83: /* 60 */
84: ushort lbasecs[2]; /* # LBA user addressable sectors */
85: ushort dmasingle;
86: ushort dmadouble;
87: /* 64 */
88: ushort reserved3[64];
89: ushort vendor[32]; /* vendor specific */
90: ushort reserved4[96];
91: };
92:
93: /*
94: * a hard drive
95: */
96: struct Drive
97: {
98: Controller *cp;
99: int drive;
100: int confused; /* needs to be recalibrated (or worse) */
101:
102: Disc;
103: };
104:
105: /*
106: * a controller for 2 drives
107: */
108: struct Controller
109: {
110: int confused; /* needs to be recalibrated (or worse) */
111: int pbase; /* base port */
112:
113: /*
114: * current operation
115: */
116: int cmd; /* current command */
117: char *buf; /* xfer buffer */
118: int tcyl; /* target cylinder */
119: int thead; /* target head */
120: int tsec; /* target sector */
121: int tbyte; /* target byte */
122: int nsecs; /* length of transfer (sectors) */
123: int sofar; /* bytes transferred so far */
124: int status;
125: int error;
126: Drive *dp; /* drive being accessed */
127: };
128:
129: Controller *hardc;
130: Drive *hard;
131:
132: static void hardintr(Ureg*, void*);
133: static long hardxfer(Drive*, Partition*, int, long, long);
134: static int hardident(Drive*);
135: static void hardsetbuf(Drive*, int);
136: static void hardpart(Drive*);
137: static int hardparams(Drive*);
138: static void hardrecal(Drive*);
139: static int hardprobe(Drive*, int, int, int);
140:
141: /*
142: * we assume drives 0 and 1 are on the first controller, 2 and 3 on the
143: * second, etc. Discover drive parameters. BUG! we are only guessing about
144: * the port locations for disks other than 0 and 1.
145: */
146: int
147: hardinit(void)
148: {
149: Drive *dp;
150: Controller *cp;
151: uchar equip;
152: int mask, nhard;
153:
154: equip = nvramread(0x12);
155: if(equip == 0)
156: equip = 0x10; /* the Globalyst 250 lies */
157:
158: hard = ialloc(2 * sizeof(Drive), 0);
159: hardc = ialloc(sizeof(Controller), 0);
160:
161: cp = hardc;
162: cp->buf = ialloc(Maxxfer, 0);
163: cp->cmd = 0;
164: cp->pbase = Pbase;
165: /*
166: * clear any pending intr from drive
167: */
168: inb(cp->pbase+Pstatus);
169: setvec(Hardvec, hardintr, cp);
170:
171: dp = hard;
172: if(equip & 0xf0){
173: dp->drive = 0;
174: dp->online = 0;
175: dp->cp = cp;
176: dp++;
177: }
178: if(equip & 0x0f){
179: dp->drive = 1;
180: dp->online = 0;
181: dp->cp = cp;
182: dp++;
183: }
184: nhard = dp-hard;
185:
186: mask = 0;
187: for(dp = hard; dp < &hard[nhard]; dp++){
188: if(hardparams(dp) == 0){
189: print("hd%d: %d cylinders %d heads %d sectors/track %d bytes\n",
190: dp->drive, dp->cyl, dp->heads, dp->sectors, dp->cap);
191: dp->online = 1;
192: hardpart(dp);
193: hardsetbuf(dp, 1);
194: mask |= 1<<dp->drive;
195: } else
196: dp->online = 0;
197: }
198: return mask;
199: }
200:
201: long
202: hardseek(int dev, long off)
203: {
204: hard[dev].offset = off;
205: return off;
206: }
207:
208: /*
209: * did an interrupt happen?
210: */
211: static void
212: hardwait(Controller *cp)
213: {
214: ulong start;
215: int x;
216:
217: x = spllo();
218: for(start = m->ticks; TK2SEC(m->ticks - start) < Timeout && cp->cmd;)
219: if(cp->cmd == Cident2 && TK2SEC(m->ticks - start) >= 1)
220: break;
221: if(TK2SEC(m->ticks - start) >= Timeout){
222: DPRINT("hardwait timed out %ux\n", inb(cp->pbase+Pstatus));
223: hardintr(0, cp);
224: }
225: splx(x);
226: }
227:
228: Partition*
229: sethardpart(int dev, char *p)
230: {
231: Partition *pp;
232: Drive *dp;
233:
234: dp = &hard[dev];
235: for(pp = dp->p; pp < &dp->p[dp->npart]; pp++)
236: if(strcmp(pp->name, p) == 0){
237: dp->current = pp;
238: return pp;
239: }
240: return 0;
241: }
242:
243: long
244: hardread(int dev, void *a, long n)
245: {
246: Drive *dp;
247: long rv, i;
248: int skip;
249: uchar *aa = a;
250: Partition *pp;
251: Controller *cp;
252:
253: dp = &hard[dev];
254: pp = dp->current;
255: if(pp == 0)
256: return -1;
257: cp = dp->cp;
258:
259: skip = dp->offset % dp->bytes;
260: for(rv = 0; rv < n; rv += i){
261: i = hardxfer(dp, pp, Cread, dp->offset+rv-skip, n-rv+skip);
262: if(i == 0)
263: break;
264: if(i < 0)
265: return -1;
266: i -= skip;
267: if(i > n - rv)
268: i = n - rv;
269: memmove(aa+rv, cp->buf + skip, i);
270: skip = 0;
271: }
272: dp->offset += rv;
273:
274: return rv;
275: }
276:
277: /*
278: * wait for the controller to be ready to accept a command
279: */
280: static int
281: cmdreadywait(Controller *cp)
282: {
283: long start;
284:
285: start = m->ticks;
286: while((inb(cp->pbase+Pstatus) & (Sready|Sbusy)) != Sready)
287: if(TK2MS(m->ticks - start) > Timeout){
288: DPRINT("cmdreadywait failed 0x%lux\n", inb(cp->pbase+Pstatus));
289: return -1;
290: }
291: return 0;
292: }
293:
294: /*
295: * transfer a number of sectors. hardintr will perform all the iterative
296: * parts.
297: */
298: static long
299: hardxfer(Drive *dp, Partition *pp, int cmd, long start, long len)
300: {
301: Controller *cp;
302: long lsec;
303: int loop;
304:
305: if(dp->online == 0){
306: DPRINT("disk not on line\n");
307: return -1;
308: }
309:
310: /*
311: * cut transfer size down to disk buffer size
312: */
313: start = start / dp->bytes;
314: if(len > Maxxfer)
315: len = Maxxfer;
316: len = (len + dp->bytes - 1) / dp->bytes;
317:
318: /*
319: * calculate physical address
320: */
321: cp = dp->cp;
322: lsec = start + pp->start;
323: if(lsec >= pp->end){
324: DPRINT("read past end of partition\n");
325: return 0;
326: }
327: if(dp->lba){
328: cp->tsec = lsec & 0xff;
329: cp->tcyl = (lsec>>8) & 0xffff;
330: cp->thead = (lsec>>24) & 0xf;
331: } else {
332: cp->tcyl = lsec/(dp->sectors*dp->heads);
333: cp->tsec = (lsec % dp->sectors) + 1;
334: cp->thead = (lsec/dp->sectors) % dp->heads;
335: }
336:
337: /*
338: * can't xfer past end of disk
339: */
340: if(lsec+len > pp->end)
341: len = pp->end - lsec;
342: cp->nsecs = len;
343:
344: if(cmdreadywait(cp) < 0)
345: return -1;
346:
347: /*
348: * start the transfer
349: */
350: cp->cmd = cmd;
351: cp->dp = dp;
352: cp->sofar = 0;
353: cp->status = 0;
354: DPRINT("xfer:\ttcyl %d, tsec %d, thead %d\n", cp->tcyl, cp->tsec, cp->thead);
355: DPRINT("\tnsecs %d, sofar %d\n", cp->nsecs, cp->sofar);
356: outb(cp->pbase+Pcount, cp->nsecs);
357: outb(cp->pbase+Psector, cp->tsec);
358: outb(cp->pbase+Pdh, Dmagic | (dp->drive<<4) | (dp->lba<<6) | cp->thead);
359: outb(cp->pbase+Pcyllsb, cp->tcyl);
360: outb(cp->pbase+Pcylmsb, cp->tcyl>>8);
361: outb(cp->pbase+Pcmd, cmd);
362:
363: if(cmd == Cwrite){
364: loop = 0;
365: while((inb(cp->pbase+Pstatus) & Sdrq) == 0)
366: if(++loop > 10000)
367: panic("hardxfer");
368: outss(cp->pbase+Pdata, cp->buf, dp->bytes/2);
369: }
370:
371: hardwait(cp);
372:
373: if(cp->status & Serr){
374: DPRINT("hd%d err: status %lux, err %lux\n",
375: dp-hard, cp->status, cp->error);
376: DPRINT("\ttcyl %d, tsec %d, thead %d\n",
377: cp->tcyl, cp->tsec, cp->thead);
378: DPRINT("\tnsecs %d, sofar %d\n", cp->nsecs, cp->sofar);
379: return -1;
380: }
381:
382: return cp->nsecs*dp->bytes;
383: }
384:
385: /*
386: * set read ahead mode (1 == on, 0 == off)
387: */
388: static void
389: hardsetbuf(Drive *dp, int on)
390: {
391: Controller *cp = dp->cp;
392:
393: if(cmdreadywait(cp) < 0)
394: return;
395:
396: cp->cmd = Csetbuf;
397: /* BUG: precomp varies by hard drive...this is safari-specific? */
398: outb(cp->pbase+Pprecomp, on ? 0xAA : 0x55);
399: outb(cp->pbase+Pdh, Dmagic | (dp->drive<<4));
400: outb(cp->pbase+Pcmd, Csetbuf);
401:
402: hardwait(cp);
403: }
404:
405: /*
406: * get parameters from the drive
407: */
408: static int
409: hardident(Drive *dp)
410: {
411: Controller *cp;
412: Ident *ip;
413:
414: dp->bytes = 512;
415: cp = dp->cp;
416:
417: if(cmdreadywait(cp) < 0)
418: return -1;
419:
420: cp->nsecs = 1;
421: cp->sofar = 0;
422: cp->cmd = Cident;
423: cp->dp = dp;
424: outb(cp->pbase+Pdh, Dmagic | (dp->drive<<4));
425: outb(cp->pbase+Pcmd, Cident);
426:
427: hardwait(cp);
428:
429: if(cp->status & Serr)
430: return -1;
431:
432: hardwait(cp);
433:
434: ip = (Ident*)cp->buf;
435: if(ip->capabilities & (1<<9)){
436: dp->lba = 1;
437: dp->sectors = (ip->lbasecs[0]) | (ip->lbasecs[1]<<16);
438: dp->cap = dp->bytes * dp->sectors;
439: /*print("\nata%d model %s with %d lba sectors\n", dp->drive, id, dp->sectors);/**/
440: } else {
441: dp->lba = 0;
442:
443: /* use default (unformatted) settings */
444: dp->cyl = ip->cyls;
445: dp->heads = ip->heads;
446: dp->sectors = ip->s2t;
447: /*print("\nata%d model %s with default %d cyl %d head %d sec\n", dp->drive,
448: id, dp->cyl, dp->heads, dp->sectors);/**/
449:
450: if(ip->cvalid&(1<<0)){
451: /* use current settings */
452: dp->cyl = ip->ccyls;
453: dp->heads = ip->cheads;
454: dp->sectors = ip->cs2t;
455: /*print("\tchanged to %d cyl %d head %d sec\n", dp->cyl, dp->heads, dp->sectors);/**/
456: }
457: dp->cap = dp->bytes * dp->cyl * dp->heads * dp->sectors;
458: }
459:
460: return 0;
461: }
462:
463: /*
464: * probe the given sector to see if it exists
465: */
466: static int
467: hardprobe(Drive *dp, int cyl, int sec, int head)
468: {
469: Controller *cp;
470:
471: cp = dp->cp;
472: if(cmdreadywait(cp) < 0)
473: return -1;
474:
475: /*
476: * start the transfer
477: */
478: cp->cmd = Cread;
479: cp->dp = dp;
480: cp->sofar = 0;
481: cp->nsecs = 1;
482: cp->status = 0;
483: outb(cp->pbase+Pcount, 1);
484: outb(cp->pbase+Psector, sec+1);
485: outb(cp->pbase+Pdh, Dmagic | head | (dp->lba<<6) | (dp->drive<<4));
486: outb(cp->pbase+Pcyllsb, cyl);
487: outb(cp->pbase+Pcylmsb, cyl>>8);
488: outb(cp->pbase+Pcmd, Cread);
489:
490: hardwait(cp);
491:
492: if(cp->status & Serr)
493: return -1;
494:
495: return 0;
496: }
497:
498: /*
499: * figure out the drive parameters
500: */
501: static int
502: hardparams(Drive *dp)
503: {
504: int i, hi, lo;
505:
506: /*
507: * first try the easy way, ask the drive and make sure it
508: * isn't lying.
509: */
510: dp->bytes = 512;
511: if(hardident(dp) < 0)
512: return -1;
513: if(dp->lba){
514: i = dp->sectors - 1;
515: if(hardprobe(dp, (i>>8)&0xffff, (i&0xff)-1, (i>>24)&0xf) == 0)
516: return 0;
517: } else {
518: if(hardprobe(dp, dp->cyl-1, dp->sectors-1, dp->heads-1) == 0)
519: return 0;
520: }
521:
522: DPRINT("hardparam: cyl %d sectors %d heads %d\n", dp->cyl, dp->sectors, dp->heads);
523: /*
524: * the drive lied, determine parameters by seeing which ones
525: * work to read sectors.
526: */
527: for(i = 0; i < 32; i++)
528: if(hardprobe(dp, 0, 0, i) < 0)
529: break;
530: dp->heads = i;
531: for(i = 0; i < 128; i++)
532: if(hardprobe(dp, 0, i, 0) < 0)
533: break;
534: dp->sectors = i;
535: for(i = 512; ; i += 512)
536: if(hardprobe(dp, i, dp->sectors-1, dp->heads-1) < 0)
537: break;
538: lo = i - 512;
539: hi = i;
540: for(; hi-lo > 1;){
541: i = lo + (hi - lo)/2;
542: if(hardprobe(dp, i, dp->sectors-1, dp->heads-1) < 0)
543: hi = i;
544: else
545: lo = i;
546: }
547: dp->cyl = lo + 1;
548: dp->cap = dp->bytes * dp->cyl * dp->heads * dp->sectors;
549:
550: if(dp->cyl == 0 || dp->heads == 0 || dp->sectors == 0 || dp->cap == 0)
551: return -1;
552:
553: return 0;
554: }
555:
556: /*
557: * read partition table. The partition table is just ascii strings.
558: */
559: #define MAGIC "plan9 partitions"
560: static void
561: hardpart(Drive *dp)
562: {
563: Partition *pp;
564: Controller *cp;
565: char *line[Npart+1];
566: char *field[3];
567: ulong n;
568: int i;
569:
570: cp = dp->cp;
571:
572: /*
573: * we always have a partition for the whole disk
574: * and one for the partition table
575: */
576: pp = &dp->p[0];
577: strcpy(pp->name, "disk");
578: pp->start = 0;
579: pp->end = dp->cap / dp->bytes;
580: pp++;
581: strcpy(pp->name, "partition");
582: pp->start = dp->p[0].end - 1;
583: pp->end = dp->p[0].end;
584: dp->npart = 2;
585:
586: /*
587: * read last sector from disk, null terminate. This used
588: * to be the sector we used for the partition tables.
589: * However, this sector is special on some PC's so we've
590: * started to use the second last sector as the partition
591: * table instead. To avoid reconfiguring all our old systems
592: * we first look to see if there is a valid partition
593: * table in the last sector. If so, we use it. Otherwise
594: * we switch to the second last.
595: */
596: hardxfer(dp, pp, Cread, 0, dp->bytes);
597: cp->buf[dp->bytes-1] = 0;
598: n = getfields(cp->buf, line, Npart+1, '\n');
599: if(n == 0 || strncmp(line[0], MAGIC, sizeof(MAGIC)-1)){
600: dp->p[0].end--;
601: dp->p[1].start--;
602: dp->p[1].end--;
603: hardxfer(dp, pp, Cread, 0, dp->bytes);
604: cp->buf[dp->bytes-1] = 0;
605: n = getfields(cp->buf, line, Npart+1, '\n');
606: }
607:
608: /*
609: * parse partition table.
610: */
611: if(n && strncmp(line[0], MAGIC, sizeof(MAGIC)-1) == 0){
612: for(i = 1; i < n; i++){
613: pp++;
614: if(getfields(line[i], field, 3, ' ') != 3)
615: break;
616: strncpy(pp->name, field[0], NAMELEN);
617: pp->start = strtoul(field[1], 0, 0);
618: pp->end = strtoul(field[2], 0, 0);
619: if(pp->start > pp->end || pp->start >= dp->p[0].end)
620: break;
621: dp->npart++;
622: }
623: }
624: return;
625: }
626:
627: /*
628: * we get an interrupt for every sector transferred
629: */
630: static void
631: hardintr(Ureg*, void *arg)
632: {
633: Controller *cp;
634: Drive *dp;
635: long loop;
636:
637: cp = arg;
638: dp = cp->dp;
639:
640: loop = 0;
641: while((cp->status = inb(cp->pbase+Pstatus)) & Sbusy)
642: if(++loop > 100000){
643: print("hardintr 0x%lux\n", cp->status);
644: break;
645: }
646: switch(cp->cmd){
647: case Cwrite:
648: if(cp->status & Serr){
649: cp->error = inb(cp->pbase+Perror);
650: cp->cmd = 0;
651: return;
652: }
653: cp->sofar++;
654: if(cp->sofar < cp->nsecs){
655: loop = 0;
656: while((inb(cp->pbase+Pstatus) & Sdrq) == 0)
657: if(++loop > 10000){
658: print("hardintr 1");
659: cp->cmd = 0;
660: return;
661: }
662: outss(cp->pbase+Pdata, &cp->buf[cp->sofar*dp->bytes],
663: dp->bytes/2);
664: } else{
665: cp->cmd = 0;
666: }
667: break;
668: case Cread:
669: case Cident:
670: if(cp->status & Serr){
671: cp->cmd = 0;
672: cp->error = inb(cp->pbase+Perror);
673: return;
674: }
675: loop = 0;
676: while((inb(cp->pbase+Pstatus) & Sdrq) == 0)
677: if(++loop > 100000){
678: print("hardintr 2 cmd %ux status %ux",
679: cp->cmd, inb(cp->pbase+Pstatus));
680: cp->cmd = 0;
681: return;
682: }
683: inss(cp->pbase+Pdata, &cp->buf[cp->sofar*dp->bytes],
684: dp->bytes/2);
685: cp->sofar++;
686: if(cp->sofar >= cp->nsecs){
687: if (cp->cmd == Cread)
688: cp->cmd = 0;
689: else
690: cp->cmd = Cident2; /* sometimes we get a second intr */
691: }
692: break;
693: case Csetbuf:
694: case Cident2:
695: cp->cmd = 0;
696: break;
697: default:
698: cp->cmd = 0;
699: break;
700: }
701: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.