]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/vmx/ide.c
grep: error if sbrk fails
[plan9front.git] / sys / src / cmd / vmx / ide.c
1 #include <u.h>
2 #include <libc.h>
3 #include <thread.h>
4 #include "dat.h"
5 #include "fns.h"
6
7 typedef struct IDE IDE;
8 typedef struct IDEIO IDEIO;
9
10 struct IDEIO {
11         QLock;
12         Rendez;
13         u8int rbuf[8192];
14         u8int wbuf[1024];
15         u16int rbufrp, rbufwp;
16         u16int wbufrp, wbufwp;
17         vlong addr;
18         int cnt;
19         u8int err, scratched, wr;
20         enum {
21                 IIOIDLE,
22                 IIOBUSY,
23         } state;
24 };
25
26 struct IDE {
27         IDEIO io;
28         enum {
29                 IDEPRESENT = 1,
30                 IDEKEEPFEAT = 2,
31                 IDEPIOREAD = 4,
32                 IDEPIOWRITE = 8,
33         } flags;
34         u8int stat, err, irq;
35         u8int ctrl , feat;
36         u8int sec, cnt;
37         u16int cyl;
38         u8int head;
39         int fd;
40         vlong size;
41         int pcyl, phead, psec;
42         int lcyl, lhead, lsec;
43 } ide[4];
44
45 uchar ideint13[4*12];
46 int idediskno;
47 enum {
48         /* ctrl */
49         IDESRST = 4,
50         IDENIEN = 2,
51         /* stat */
52         IDEBUSY = 0x80,
53         IDEDRDY = 0x40,
54         IDEDSC = 0x10,
55         IDEDRQ = 0x08,
56         IDEERR = 0x01,
57         /* error */
58         IDEUNC = 0x40,
59         IDEIDNF = 0x10,
60         IDEABRT = 0x04,
61 };
62
63 static void
64 idereset(IDE *d)
65 {
66         d->ctrl &= IDESRST;
67         if((d->flags & IDEPRESENT) != 0){
68                 qlock(&d->io);
69                 while(d->io.state != IIOIDLE){
70                         d->io.scratched = 1;
71                         rwakeup(&d->io);
72                         rsleep(&d->io);
73                 }
74                 d->io.rbufrp = d->io.rbufwp = 0;
75                 d->io.scratched = 0;
76                 qunlock(&d->io);
77                 d->stat = IDEDRDY | IDEDSC;
78                 d->err = 1;
79                 d->flags &= IDEPRESENT | IDEKEEPFEAT;
80                 d->sec = d->cnt = 1;
81                 d->cyl = 0;
82                 d->head = 0xa0;
83                 d->feat = 0;
84         }
85 }
86
87 static void
88 ideirq(IDE *d, int n)
89 {
90         IDE *s;
91
92         if(n >= 0)
93                 d->irq = n;
94         s = (d - ide & ~1) + (d->head >> 4 & 1) + ide;
95         irqline(14 + (d - ide)/2, s->irq && (s->ctrl & IDENIEN) == 0);
96 }
97
98 static vlong
99 getlba(IDE *d)
100 {
101         int he;
102
103         if((d->head & 0x40) != 0)
104                 return d->sec | d->cyl << 8 | (d->head & 0xf) << 16;
105         if(d->sec == 0 || d->sec > d->lsec)
106                 return -1;
107         he = d->head & 0xf;
108         if(d->cyl >= d->lcyl || he >= d->lhead)
109                         return -1;
110         return d->sec - 1 + (he + d->cyl * d->lhead) * d->lsec;
111
112 }
113
114 static void
115 idegoio(IDE *d, int wr)
116 {
117         vlong addr;
118         
119         addr = getlba(d);
120         if(addr < 0){
121                 vmerror("ide%d: access to invalid sector address (access to CHS=(%#.4ux,%#ux,%#.2ux); geometry is (%#.4ux,%#ux,%#.2ux)", d-ide, d->cyl, d->head&0xf, d->sec, d->lcyl, d->lhead, d->lsec);
122                 postexc("#bp", NOERRC);
123                 d->stat = IDEDRDY | IDEDSC | IDEDRQ | IDEERR;
124                 d->err = IDEIDNF;
125                 ideirq(d, 1);
126                 return;
127         }
128         if(wr){
129                 d->stat = IDEDRDY | IDEDRQ | IDEDSC;
130                 d->flags |= IDEPIOWRITE;
131         }else{
132                 d->stat = IDEDRDY | IDEBUSY | IDEDSC;
133                 d->flags |= IDEPIOREAD;
134         }
135         qlock(&d->io);
136         while(d->io.state != IIOIDLE)
137                 rsleep(&d->io);
138         d->io.addr = addr;
139         d->io.cnt = (d->cnt - 1 & 0xff) + 1;
140         d->io.err = 0;
141         d->io.wr = wr;
142         d->io.state = IIOBUSY;
143         rwakeup(&d->io);
144         qunlock(&d->io);
145 }
146
147 static void
148 ideincaddr(IDE *d)
149 {
150         if((d->head & 0x40) != 0){
151                 if(d->sec++ == 255 && d->cyl++ == 65535)
152                         d->head = d->head + 1 & 0xf | d->head & 0xf0;
153         }else{
154                 if(d->sec++ == d->lsec){
155                         d->sec = 1;
156                         if((d->head & 0xf) == d->lhead-1){
157                                 d->head &= 0xf0;
158                                 if(d->cyl++ == d->lcyl)
159                                         d->cyl = 0;
160                         }else
161                                 d->head++;
162                 }
163         }
164 }
165
166 static void
167 idesecrend(IDE *d)
168 {
169         if((d->flags & IDEPIOREAD) == 0){
170                 d->stat &= ~IDEDRQ;
171                 return;
172         }
173         ideincaddr(d);
174         if(d->io.rbufwp == (u16int)(d->io.rbufrp + sizeof(d->io.rbuf) - 512))
175                 rwakeup(&d->io);
176         if(--d->cnt == 0){
177                 d->stat &= ~(IDEBUSY|IDEDRQ);
178                 d->flags &= ~IDEPIOREAD;
179         }else if(d->io.rbufrp == d->io.rbufwp){
180                 if(d->io.err != 0){
181                         d->stat = d->stat | IDEERR;
182                         d->err = d->io.err;
183                         ideirq(d, 1);
184                 }else
185                         d->stat = d->stat & ~IDEDRQ | IDEBUSY;
186         }else
187                 ideirq(d, 1);
188 }
189
190 static void
191 idesecrdone(void *dp)
192 {
193         IDE *d;
194         
195         d = dp;
196         qlock(&d->io);
197         d->stat = d->stat & ~IDEBUSY | IDEDRQ;
198         if(d->io.err != 0){
199                 d->stat |= IDEERR;
200                 d->err = d->io.err;
201         }
202         ideirq(d, 1);
203         qunlock(&d->io);
204 }
205
206 static void
207 idesecwend(IDE *d)
208 {
209         ideincaddr(d);
210         d->cnt--;
211         if((u16int)(d->io.wbufwp - 512) == d->io.wbufrp)
212                 rwakeup(&d->io);
213         if(d->io.wbufwp == (u16int)(d->io.wbufrp + sizeof(d->io.wbuf))){
214                 d->stat = d->stat & ~IDEDRQ | IDEBUSY;
215         }else{
216                 if(d->cnt == 0)
217                         d->stat = d->stat & ~(IDEDRQ|IDEBUSY);
218                 ideirq(d, 1);
219         }
220 }
221
222 static void
223 idesecwdone(void *dp)
224 {
225         IDE *d;
226         
227         d = dp;
228         qlock(&d->io);
229         if(d->cnt == 0)
230                 d->stat = d->stat & ~(IDEDRQ|IDEBUSY);
231         else
232                 d->stat = d->stat & ~IDEBUSY | IDEDRQ;
233         ideirq(d, 1);
234         qunlock(&d->io);
235 }
236
237 typedef struct Sector Sector;
238 struct Sector {
239         uchar data[512];
240         vlong addr;
241         Sector *next;
242 };
243 Sector *sectors;
244
245 static int
246 getsector(vlong a, uchar *p)
247 {
248         Sector *s;
249         
250         for(s = sectors; s != nil; s = s->next)
251                 if(s->addr == a){
252                         vmdebug("reading updated sector %lld", a);
253                         memmove(p, s->data, 512);
254                         return 0;
255                 }
256         return -1;
257 }
258
259 static void
260 putsector(vlong a, uchar *dp)
261 {
262         Sector *s, **p;
263         
264         for(p = &sectors; s = *p, s != nil; p = &s->next)
265                 if(s->addr == a){
266                         memmove(s->data, dp, 512);
267                         return;
268                 }
269         s = emalloc(sizeof(Sector));
270         s->addr = a;
271         memmove(s->data, dp, 512);
272         *p = s;
273 }
274
275 static void
276 ideioproc(void *dp)
277 {
278         IDE *d;
279         IDEIO *io;
280         vlong a;
281         uchar *p;
282         int i, n;
283         
284         d = dp;
285         io = &d->io;
286         threadsetname("ide");
287         for(;;){
288                 qlock(io);
289                 io->state = IIOIDLE;
290                 rwakeup(io);
291                 while(io->state == IIOIDLE)
292                         rsleep(io);
293                 a = io->addr;
294                 n = io->cnt;
295                 qunlock(io);
296                 
297                 if(io->wr){
298                         for(i = 0; i < n; i++){
299                                 qlock(io);
300                                 while(!io->scratched && io->wbufrp == (io->wbufwp & ~511))
301                                         rsleep(io);
302                                 if(io->scratched){
303                                         qunlock(io);
304                                         break;
305                                 }
306                                 p = io->wbuf + (io->wbufrp & sizeof(io->wbuf) - 1);
307                                 qunlock(io);
308                                 putsector(a+i, p);
309                                 qlock(io);
310                                 if(io->wbufwp == (u16int)(io->wbufrp + sizeof(io->wbuf)))
311                                         sendnotif(idesecwdone, d);
312                                 io->wbufrp += 512;
313                                 qunlock(io);
314                         }
315                 }else{
316                         for(i = 0; i < n; i++){
317                                 qlock(io);
318                                 while(!io->scratched && io->rbufwp == (u16int)((io->rbufrp & ~511) + sizeof(io->rbuf)))
319                                         rsleep(io);
320                                 if(io->scratched){
321                                         qunlock(io);
322                                         break;
323                                 }
324                                 p = io->rbuf + (io->rbufwp & sizeof(io->rbuf) - 1);
325                                 qunlock(io);
326                                 werrstr("eof");
327                                 if(getsector(a+i, p) < 0 && pread(d->fd, p, 512, (a+i)*512) < 512){
328                                         vmerror("ide%d: read: %r", d - ide);
329                                         qlock(io);
330                                         io->err = IDEUNC;
331                                         qunlock(io);
332                                         sendnotif(idesecrdone, d);
333                                         break;
334                                 }
335                                 qlock(io);
336                                 if(io->rbufrp == io->rbufwp)
337                                         sendnotif(idesecrdone, d);
338                                 io->rbufwp += 512;
339                                 qunlock(io);
340                         }
341                 }
342         }
343 }
344
345 static void
346 idecmd(IDE *d, u8int cmd)
347 {
348         u8int *p;
349         vlong vl;
350
351         if(cmd == 0)
352                 return;
353         switch(cmd){
354         case 0x90:
355                 break;
356         default:
357                 if((d->flags & IDEPRESENT) == 0){
358                         vmerror("ide%d: command %#ux issued to absent drive", d-ide, cmd);
359                         return;
360                 }
361         }
362         if(cmd >> 4 == 1 || cmd >> 4 == 7){
363                 /* relibrate / seek */
364                 d->stat = IDEDRDY|IDEDSC;
365                 ideirq(d, 1);
366                 return;
367         }
368         switch(cmd){
369         case 0x20: case 0x21: /* read (pio) */
370                 idegoio(d, 0);
371                 break;
372         case 0x30: case 0x31: /* write (pio) */
373                 idegoio(d, 1);
374                 break;
375         case 0x40: case 0x41: /* read verify */
376                 while(--d->cnt != 0)
377                         ideincaddr(d);
378                 d->stat = IDEDRDY|IDEDSC;
379                 ideirq(d, 1);
380                 break;
381         case 0x90: /* diagnostics */
382                 d = (d - ide & ~1) + ide;
383                 d[0].err = 0;
384                 d[1].err = 0;
385                 if((d->flags & IDEPRESENT) != 0){
386                         d->stat = IDEDRDY|IDEDSC;
387                         ideirq(d, 1);
388                 }
389                 break;
390         case 0x91: /* set translation mode */
391                 d->lhead = (d->head & 0xf) + 1;
392                 d->lsec = d->cnt;
393                 if(d->cnt != 0){
394                         vl = d->size / (d->lhead * d->lsec);
395                         d->lcyl = vl >= 65535 ? 65535 : vl;
396                 }
397                 d->stat = IDEDRDY|IDEDSC;
398                 ideirq(d, 1);
399                 break;
400         case 0xec: /* identify */
401                 qlock(&d->io);
402                 while(d->io.state != IIOIDLE)
403                         rsleep(&d->io);
404                 p = d->io.rbuf + (d->io.rbufwp & sizeof(d->io.rbuf) - 1);
405                 d->io.rbufwp += 512;
406                 memset(p, 0, 512);
407                 strcpy((char*)p+20, "13149562358579393248");
408                 strcpy((char*)p+46, ".2.781  ");
409                 sprint((char*)p+54, "%-40s", "jhidks s");
410                 PUT16(p, 0, 0x40);
411                 PUT16(p, 2, d->pcyl);
412                 PUT16(p, 6, d->phead);
413                 PUT16(p, 8, d->psec << 9);
414                 PUT16(p, 10, 512);
415                 PUT16(p, 12, d->psec);
416                 PUT16(p, 98, 0x200);
417                 if(d->lsec != 0){
418                         PUT16(d, 106, 1);
419                         PUT16(d, 108, d->lcyl);
420                         PUT16(d, 110, d->lhead);
421                         PUT16(d, 112, d->lsec);
422                         PUT32(d, 114, d->lcyl * d->lhead * d->lsec);
423                 }
424                 PUT32(p, 120, d->size >= (u32int)-1 ? -1 : d->size);
425                 PUT16(p, 160, 7);
426                 qunlock(&d->io);
427                 d->stat = IDEDRDY|IDEDSC|IDEDRQ;
428                 ideirq(d, 1);
429                 break;
430         case 0xef: /* set feature */
431                 switch(d->feat){
432                 case 1: case 0x81: /* enable/disable 8-bit transfers */
433                 case 2: case 0x82: /* enable/disable cache */
434                         break;
435                 case 0x66: d->flags |= IDEKEEPFEAT; break; /* retain settings */
436                 case 0xcc: d->flags &= ~IDEKEEPFEAT; break; /* revert to default on reset */
437                 default:
438                         vmerror("ide%d: unknown feature %#ux", d-ide, d->feat);
439                         d->stat = IDEDRDY|IDEDSC|IDEERR;
440                         d->err = IDEABRT;
441                         return;
442                 }
443                 d->stat = IDEDRDY|IDEDSC;
444                 break;
445         default:
446                 vmerror("ide%d: unknown command %#ux", d-ide, cmd);
447                 d->stat = IDEDRDY|IDEDSC|IDEERR;
448                 d->err = IDEABRT;
449         }
450 }
451
452 u32int
453 ideio(int isin, u16int port, u32int val, int sz, void *)
454 {
455         IDE *d, *e;
456         u32int rc;
457
458         d = &ide[2 * ((port & 0x80) == 0)];
459         d += d->head >> 4 & 1;
460         e = (d - ide ^ 1) + ide;
461         if((port|0x80) != 0x1f0){
462                 if(sz != 1)
463                         vmerror("ide: access to port %#x with incorrect size %d", port, sz);
464                 val = (u8int) val;
465         }
466         if(isin && (d->flags & IDEPRESENT) == 0)
467                 return 0;
468         switch(isin << 16 | port | 0x80){
469         case 0x001f0:
470                 if((d->flags & IDEPIOWRITE) == 0)
471                         return 0;
472                 qlock(&d->io);
473                 PUT16(d->io.wbuf, d->io.wbufwp & sizeof(d->io.wbuf) - 1, (u16int)val);
474                 d->io.wbufwp += 2;
475                 if((d->io.wbufwp & 511) == 0)
476                         idesecwend(d);
477                 qunlock(&d->io);
478                 if(sz == 4)
479                         ideio(0, port, val >> 16, 2, nil);
480                 return 0;
481         case 0x001f1: d->feat = e->feat = val; return 0;
482         case 0x001f2: d->cnt = e->cnt = val; return 0;
483         case 0x001f3: d->sec = e->sec = val; return 0;
484         case 0x001f4: d->cyl = d->cyl & 0xff00 | val; e->cyl = e->cyl & 0xff00 | val; return 0;
485         case 0x001f5: d->cyl = d->cyl & 0xff | val << 8; e->cyl = e->cyl & 0xff | val << 8; return 0;
486         case 0x001f6: d->head = e->head = val | 0xa0; return 0;
487         case 0x001f7: idecmd(d, val); return 0;
488         case 0x003f6:
489                 d->ctrl = e->ctrl = val;
490                 if((val & IDESRST) != 0){
491                         idereset(d);
492                         idereset(e);
493                 }
494                 ideirq(d, -1);
495                 return 0;
496
497         case 0x101f0:
498                 qlock(&d->io);
499                 if(d->io.rbufrp != d->io.rbufwp){
500                         rc = GET16(d->io.rbuf, d->io.rbufrp & sizeof(d->io.rbuf)-1);
501                         d->io.rbufrp += 2;
502                         if((d->io.rbufrp & 511) == 0)
503                                 idesecrend(d);
504                 }else
505                         rc = 0;
506                 qunlock(&d->io);
507                 if(sz == 4)
508                         rc |= ideio(1, port, 0, 2, nil) << 16;
509                 return rc;
510         case 0x101f1: return d->err;
511         case 0x101f2: return d->cnt;
512         case 0x101f3: return d->sec;
513         case 0x101f4: return (u8int)d->cyl;
514         case 0x101f5: return d->cyl >> 8;
515         case 0x101f6: return d->head;
516         case 0x101f7:
517                 ideirq(d, 0);
518         case 0x103f6:
519                 if((d->ctrl & IDESRST) != 0)
520                         return IDEBUSY;
521                 rc = d->stat;
522                 /* stupid hack to work around different expectations of how DRQ behaves on error */
523                 if((d->stat & IDEERR) != 0)
524                         d->stat &= ~IDEDRQ;
525                 return rc;
526         default: return iowhine(isin, port, val, sz, "ide");
527         }
528 }
529
530 static int
531 idegeom(vlong vsz, int *cp, int *hp, int *sp)
532 {
533         int sz, c, h, s, t;
534         int max;
535         
536         if(vsz >= 63*255*1023){
537                 *cp = 1023;
538                 *hp = 255;
539                 *sp = 63;
540                 return 0;
541         }
542         sz = vsz;
543         max = 0;
544         for(s = 63; s >= 1; s--)
545                 for(h = 1; h <= 16; h++){
546                         c = sz / (h * s);
547                         if(c >= 1023) c = 1023;
548                         t = c * h * s;
549                         if(t > max){
550                                 max = t;
551                                 *cp = c;
552                                 *hp = h;
553                                 *sp = s;
554                         }
555                 }
556         return max == 0 ? -1 : 0;
557 }
558
559 int
560 mkideblk(char *fn)
561 {
562         int fd;
563         IDE *d;
564         uchar *p;
565         
566         if(idediskno >= 4){
567                 werrstr("too many ide disks");
568                 return -1;
569         }
570         d = &ide[idediskno];
571         d->io.Rendez.l = &d->io;
572         fd = open(fn, ORDWR);
573         if(fd < 0)
574                 return -1;
575         d->size = seek(fd, 0, 2) >> 9;
576         if(idegeom(d->size, &d->pcyl, &d->phead, &d->psec) < 0){
577                 werrstr("disk file too small");
578                 return -1;
579         }
580         if(idediskno < 2){
581                 cmos[0x12] |= 0xf << (1-idediskno) * 4;
582                 cmos[0x19 + idediskno] = 47;
583         }
584         p = ideint13 + idediskno * 12;
585         PUT16(p, 0, 0x80 | idediskno);
586         PUT32(p, 2, d->pcyl);
587         PUT16(p, 6, d->psec);
588         PUT16(p, 8, d->phead);
589         PUT16(p, 10, 1);
590         d->flags |= IDEPRESENT;
591         d->fd = fd;
592         idereset(&ide[idediskno]);
593         idediskno++;
594         proccreate(ideioproc, d, 8192);
595         return 0;
596
597 }