]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/vmx/virtio.c
vmx: reset virtio queue state on device reset
[plan9front.git] / sys / src / cmd / vmx / virtio.c
1 #include <u.h>
2 #include <libc.h>
3 #include <thread.h>
4 #include "dat.h"
5 #include "fns.h"
6
7 #include <ip.h>         /* parseether() */
8 #include <libsec.h>     /* genrandom() */
9
10 typedef struct VIODev VIODev;
11 typedef struct VIOQueue VIOQueue;
12 typedef struct VIOBuf VIOBuf;
13 typedef struct VIONetDev VIONetDev;
14 typedef struct VIOBlkDev VIOBlkDev;
15
16 enum {
17         BUFCHAIN = 1,
18         BUFWR = 2,
19         
20         USEDNOIRQ = 1,
21         
22         DRIVEROK = 4, /* devstat */
23 };
24
25 struct VIOBuf {
26         u32int flags;
27         VIOQueue *qu;
28         void *p;
29         u64int addr;
30         u32int len;
31         u32int idx;
32         VIOBuf *next, *head;
33         u32int rptr, wptr;
34 };
35
36 struct VIOQueue {
37         QLock;
38         Rendez;
39         VIODev *d;
40         u8int (*desc)[16], *avail, *used;
41         u16int size;
42         u32int addr;
43         u16int availidx, usedidx;
44         void (*notify)(VIOQueue*);
45         int livebuf;
46         Rendez livebufrend;
47 };
48
49 struct VIONetDev {
50         int readfd, writefd;
51         u8int mac[6];
52         enum {
53                 VNETPROMISC = 1,
54                 VNETALLMULTI = 2,
55                 VNETALLUNI = 4,
56                 VNETNOMULTI = 8,
57                 VNETNOUNI = 16,
58                 VNETNOBCAST = 32,
59                 
60                 VNETHEADER = 1<<31,
61         } flags;
62         u64int macbloom, multibloom;
63 };
64
65 struct VIOBlkDev {
66         int fd;
67         uvlong size;
68 };
69
70 struct VIODev {
71         PCIDev *pci;
72         u32int devfeat, guestfeat;
73         u16int qsel;
74         u8int devstat, isrstat;
75         VIOQueue *qu;
76         int nqu, allocqu;
77         u32int (*io)(int, u16int, u32int, int, VIODev *);
78         void (*reset)(VIODev *);
79         union {
80                 VIONetDev net;
81                 VIOBlkDev blk;
82         };
83 };
84
85 static void
86 vioirq_(void *arg)
87 {
88         VIODev *d;
89         int val;
90         
91         d = ((void**)arg)[0];
92         val = (uintptr)((void**)arg)[1];
93         if(val != 0)
94                 d->isrstat |= val;
95         else
96                 d->isrstat = 0;
97         pciirq(d->pci, d->isrstat);
98         free(arg);
99 }
100
101 static void
102 vioirq(VIODev *d, int val)
103 {
104         void **v;
105         
106         assert(d != nil);
107         v = emalloc(sizeof(void*)*2);
108         v[0] = d;
109         v[1] = (void *) val;
110         sendnotif(vioirq_, v);
111 }
112
113 static void *
114 checkdesc(VIOQueue *q, int i)
115 {
116         if(i >= q->size){
117                 vmerror("virtio device %#x: invalid next pointer %d in queue (size %d), ignoring descriptor", q->d->pci->bdf, i, q->size);
118                 return nil;
119         }
120         return q->desc[i];
121 }
122
123 VIOBuf *
124 viogetbuf(VIOQueue *q, int wait)
125 {
126         u16int gidx;
127         VIOBuf *b, *rb, **bp;
128         void *dp;
129         
130         qlock(q);
131 waitloop:
132         while((q->d->devstat & DRIVEROK) == 0 || q->desc == nil || (gidx = GET16(q->avail, 2), gidx == q->availidx)){
133                 if(!wait){
134                         qunlock(q);
135                         return nil;
136                 }
137                 rsleep(q);
138         }
139         dp = checkdesc(q, GET16(q->avail, 4 + 2 * (q->availidx % q->size)));
140         rb = nil;
141         bp = &rb;
142         for(;;){
143                 b = emalloc(sizeof(VIOBuf));
144                 b->qu = q;
145                 b->idx = (u8int(*)[16])dp - q->desc;
146                 b->addr = GET64(dp, 0);
147                 b->len = GET32(dp, 8);
148                 b->flags = GET16(dp, 12);
149                 b->p = gptr(b->addr, b->len);
150                 if(b->p == nil){
151                         vmerror("virtio device %#x: invalid buffer pointer %#p in queue, ignoring descriptor", q->d->pci->bdf, (void*)b->addr);
152                         free(b);
153                         break;
154                 }
155                 *bp = b;
156                 b->head = rb;
157                 bp = &b->next;
158                 if((b->flags & BUFCHAIN) == 0) break;
159                 dp = checkdesc(q, GET16(dp, 14));
160                 if(dp == nil) break;
161         }
162         q->availidx++;
163         if(rb == nil) goto waitloop;
164         q->livebuf++;
165         qunlock(q);
166         return rb;
167 }
168
169 void
170 vioputbuf(VIOBuf *b)
171 {
172         VIOBuf *bn;
173         VIOQueue *q;
174         u8int *p;
175         
176         if(b == nil) return;
177         q = b->qu;
178         qlock(q);
179         if((q->d->devstat & DRIVEROK) == 0){
180                 qunlock(q);
181                 goto end;
182         }
183         if(q->used == nil)
184                 vmerror("virtio device %#x: address was set to an invalid value while holding buffer", q->d->pci->bdf);
185         else{
186                 p = q->used + 4 + 8 * (q->usedidx % q->size);
187                 PUT32(p, 4, b->wptr);
188                 PUT32(p, 0, b->idx);
189                 PUT16(q->used, 2, ++q->usedidx);
190         }
191         if(--q->livebuf <= 0)
192                 rwakeup(&q->livebufrend);
193         qunlock(q);
194         if(q->avail != nil && (GET16(q->avail, 0) & USEDNOIRQ) == 0)
195                 vioirq(q->d, 1);
196 end:
197         while(b != nil){
198                 bn = b->next;
199                 free(b);
200                 b = bn;
201         }
202 }
203
204 ulong
205 vioqread(VIOBuf *b, void *v, ulong n)
206 {
207         VIOBuf *c;
208         u32int p;
209         int rc;
210         ulong m;
211         
212         p = b->rptr;
213         c = b;
214         rc = 0;
215         for(;;){
216                 if(rc >= n) return rc;
217                 for(;;){
218                         if(c == nil) return rc;
219                         if((c->flags & BUFWR) == 0){
220                                 if(p < c->len) break;
221                                 p -= c->len;
222                         }
223                         c = c->next;
224                 }
225                 m = c->len - p;
226                 if(m > n - rc) m = n - rc;
227                 memmove(v, (u8int*)c->p + p, m);
228                 p += m, rc += m;
229                 v = (u8int*)v + m;
230                 b->rptr += m;
231         }
232 }
233
234 ulong
235 vioqwrite(VIOBuf *b, void *v, ulong n)
236 {
237         VIOBuf *c;
238         u32int p;
239         int rc;
240         ulong m;
241         
242         p = b->wptr;
243         c = b;
244         rc = 0;
245         for(;;){
246                 if(rc >= n) return rc;
247                 for(;;){
248                         if(c == nil) return rc;
249                         if((c->flags & BUFWR) != 0){
250                                 if(p < c->len) break;
251                                 p -= c->len;
252                         }
253                         c = c->next;
254                 }
255                 m = c->len - p;
256                 if(m > n - rc) m = n - rc;
257                 memmove((u8int*)c->p + p, v, m);
258                 p += m, rc += m;
259                 v = (u8int*)v + m;
260                 b->wptr += m;
261         }
262 }
263
264 ulong
265 vioqrem(VIOBuf *b, int wr)
266 {
267         VIOBuf *c;
268         u32int p;
269         ulong rc;
270         
271         p = wr ? b->wptr : b->rptr;
272         for(c = b;; c = c->next){
273                 if(c == nil) return 0;
274                 if(((c->flags & BUFWR) != 0) == wr){
275                         if(p < c->len) break;
276                         p -= c->len;
277                 }
278         }
279         rc = c->len - p;
280         for(c = c->next; c != nil; c = c->next)
281                 if(((c->flags & BUFWR) != 0) == wr)
282                         rc += c->len;
283         return rc;
284 }
285
286 static void
287 vioqaddrset(VIOQueue *q, u64int addr)
288 {
289         void *p;
290         int sz1, sz;
291
292         addr <<= 12;
293         sz1 = -(-(18 * q->size + 4) & -4096);
294         sz = sz1 + (-(-(8 * q->size + 6) & -4096));
295         p = gptr(addr, sz);
296         if(p == nil)
297                 vmerror("virtio device %#x: attempt to set queue to invalid address %#p", q->d->pci->bdf, (void *) addr);
298         qlock(q);
299         q->addr = addr;
300         if(p == nil){
301                 q->desc = nil;
302                 q->avail = nil;
303                 q->used = nil;
304         }else{
305                 q->desc = p;
306                 q->avail = (u8int*)p + 16 * q->size;
307                 q->used = (u8int*)p + sz1;
308                 rwakeupall(q);
309         }
310         qunlock(q);
311 }
312
313 static void
314 vioqreset(VIOQueue *q)
315 {
316         q->desc = nil;
317         q->avail = nil;
318         q->used = nil;
319         q->addr = 0;
320         q->availidx = 0;
321         q->usedidx = 0;
322 }
323
324 static void
325 viodevstatset(VIODev *v, u32int val)
326 {
327         int i;
328
329         v->devstat = val;
330         if(val == 0){
331                 if(v->reset != nil)
332                         v->reset(v);
333                 v->guestfeat = 0;
334                 vioirq(v, 0);
335                 for(i = 0; i < v->nqu; i++){
336                         qlock(&v->qu[i]);
337                         while(v->qu[i].livebuf > 0)
338                                 rsleep(&v->qu[i].livebufrend);
339                         vioqreset(&v->qu[i]);
340                         qunlock(&v->qu[i]);
341                 }
342         }else{
343                 for(i = 0; i < v->nqu; i++)
344                         v->qu[i].notify(&v->qu[i]);
345         }
346 }
347
348 u32int
349 vioio(int isin, u16int port, u32int val, int sz, void *vp)
350 {
351         VIODev *v;
352         int rc;
353         static char whinebuf[32];
354         
355         v = vp;
356         switch(isin << 16 | port){
357         case 0x4: v->guestfeat = val; return 0;
358         case 0x8: if(v->qsel < v->nqu) vioqaddrset(&v->qu[v->qsel], val); return 0;
359         case 0xe: v->qsel = val; return 0;
360         case 0x10: if(val < v->nqu) v->qu[val].notify(&v->qu[val]); return 0;
361         case 0x12: viodevstatset(v, val); return 0;
362         case 0x10000: return v->devfeat;
363         case 0x10004: return v->guestfeat;
364         case 0x10008: return v->qsel >= v->nqu ? 0 : v->qu[v->qsel].addr;
365         case 0x1000c: return v->qsel >= v->nqu ? 0 : v->qu[v->qsel].size;
366         case 0x1000e: return v->qsel;
367         case 0x10010: return 0;
368         case 0x10012: return v->devstat;
369         case 0x10013: rc = v->isrstat; vioirq(v, 0); return rc;
370         }
371         if(port >= 20 && v->io != nil)
372                 return v->io(isin, port - 20, val, sz, v);
373         snprint(whinebuf, sizeof(whinebuf), "virtio device %6x", v->pci->bdf);
374         return iowhine(isin, port, val, sz, whinebuf);
375 }
376
377 VIODev *
378 mkviodev(u16int devid, u32int pciclass, u32int subid, int queues)
379 {
380         VIODev *d;
381         
382         d = emalloc(sizeof(VIODev));
383         d->pci = mkpcidev(allocbdf(), devid << 16 | 0x1AF4, pciclass << 8, 1);
384         d->pci->subid = subid << 16;
385         mkpcibar(d->pci, BARIO, 0, 256, vioio, d);
386         d->qu = emalloc(queues * sizeof(VIOQueue));
387         d->allocqu = queues;
388         return d;
389 }
390
391 static void
392 viowakeup(VIOQueue *q)
393 {
394         qlock(q);
395         rwakeupall(q);
396         qunlock(q);
397 }
398
399 VIOQueue *
400 mkvioqueue(VIODev *d, int sz, void (*fn)(VIOQueue*))
401 {
402         VIOQueue *q;
403
404         assert(sz > 0 && sz <= 32768 && (sz & sz - 1) == 0 && fn != nil && d->nqu < d->allocqu);
405         q = d->qu + d->nqu++;
406         q->Rendez.l = q;
407         q->livebufrend.l = q;
408         q->size = sz;
409         q->d = d;
410         q->notify = fn;
411         vioqreset(q);
412         return q;
413 }
414
415 int
416 bloomhash(u8int *mac)
417 {
418         int x;
419
420         x = mac[0];
421         x ^= mac[0] >> 6 ^ mac[1] << 2;
422         x ^= mac[1] >> 4 ^ mac[2] << 4;
423         x ^= mac[2] >> 2;
424         x ^= mac[3];
425         x ^= mac[3] >> 6 ^ mac[4] << 2;
426         x ^= mac[4] >> 4 ^ mac[5] << 4;
427         x ^= mac[5] >> 2;
428         return x & 63;
429 }
430
431 int
432 viomacok(VIODev *d, u8int *mac)
433 {
434         static u8int bcast[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
435
436         if((d->net.flags & VNETPROMISC) != 0) return 1;
437         if((mac[0] & 1) == 0){
438                 if((d->net.flags & (VNETNOUNI|VNETALLUNI)) != 0)
439                         return (d->net.flags & VNETNOUNI) == 0;
440                 if(memcmp(mac, d->net.mac, 6) == 0) return 1;
441                 if(d->net.macbloom == 0) return 0;
442                 return d->net.macbloom >> bloomhash(mac) & 1;
443         }else if(memcmp(mac, bcast, 6) == 0)
444                 return (d->net.flags & VNETNOBCAST) == 0;
445         else{
446                 if((d->net.flags & (VNETNOMULTI|VNETALLMULTI)) != 0)
447                         return (d->net.flags & VNETNOMULTI) == 0;
448                 if(d->net.multibloom == 0) return 0;
449                 return d->net.multibloom >> bloomhash(mac) & 1;
450         }
451 }
452
453 void
454 vionetrproc(void *vp)
455 {
456         VIODev *v;
457         VIOQueue *q;
458         VIOBuf *vb;
459         uchar rxhead[10];
460         uchar rxbuf[1600];
461         int rc;
462         
463         threadsetname("vionetrproc");
464         v = vp;
465         q = &v->qu[0];
466         memset(rxhead, 0, sizeof(rxhead));
467         for(;;){
468                 rc = read(v->net.readfd, rxbuf, sizeof(rxbuf));
469                 if(rc == 0){
470                         vmerror("read(vionetrproc): eof");
471                         threadexits("read: eof");
472                 }
473                 if(rc < 0){
474                         vmerror("read(vionetrproc): %r");
475                         threadexits("read: %r");
476                 }
477                 if(rc < 14){
478                         vmerror("vionetrproc: short packet received (len=%d)", rc);
479                         continue;
480                 }
481                 if(!viomacok(v, rxbuf))
482                         continue;
483                 vb = viogetbuf(q, 1);
484                 if(vb == nil){
485                         vmerror("viogetbuf: %r");
486                         continue;
487                 }
488                 vioqwrite(vb, rxhead, sizeof(rxhead));
489                 vioqwrite(vb, rxbuf, rc);
490                 vioputbuf(vb);
491         }
492 }
493
494 void
495 vionetwproc(void *vp)
496 {
497         VIODev *v;
498         VIOQueue *q;
499         VIOBuf *vb;
500         uchar txhead[10];
501         uchar txbuf[1610];
502         int rc, len;
503         uvlong ns;
504         
505         threadsetname("vionetwproc");
506         v = vp;
507         q = &v->qu[1];
508         for(;;){
509                 vb = viogetbuf(q, 1);
510                 if(vb == nil){
511                         vmerror("viogetbuf: %r");
512                         threadexits("viogetbuf: %r");
513                 }
514                 vioqread(vb, txhead, sizeof(txhead));
515                 len = vioqread(vb, txbuf+10, sizeof(txbuf)-10);
516                 if(len == sizeof(txbuf)-10){
517                         vmerror("virtio net: ignoring excessively long packet");
518                         vioputbuf(vb);
519                         continue;
520                 }
521                 if(len < 14){
522                         /* openbsd ends up sending lots of zero length packets sometimes */
523                         if(len != 0)
524                                 vmerror("virtio net: ignoring short packet (length=%d)", len);
525                         vioputbuf(vb);
526                         continue;
527                 }else if(len < 60){ /* openbsd doesn't seem to know about ethernet minimum packet lengths either */
528                         memset(txbuf + 10 + len, 0, 60 - len);
529                         len = 60;
530                 }
531                 if((v->net.flags & VNETHEADER) != 0){
532                         txbuf[0] = len  >> 8;
533                         txbuf[1] = len;
534                         ns = nanosec();
535                         txbuf[2] = ns >> 56;
536                         txbuf[3] = ns >> 48;
537                         txbuf[4] = ns >> 40;
538                         txbuf[5] = ns >> 32;
539                         txbuf[6] = ns >> 24;
540                         txbuf[7] = ns >> 16;
541                         txbuf[8] = ns >> 8;
542                         txbuf[9] = ns;
543                         rc = write(v->net.writefd, txbuf, len + 10);
544                 }else
545                         rc = write(v->net.writefd, txbuf + 10, len);
546                 vioputbuf(vb);
547                 if(rc < 0){
548                         vmerror("write(vionetwproc): %r");
549                         continue;
550                 }
551                 if(rc < len){
552                         vmerror("write(vionetwproc): incomplete write (%d < %d)", rc, len);
553                         continue;
554                 }
555         }
556 }
557
558 u32int
559 vionetio(int isin, u16int port, u32int val, int sz, VIODev *v)
560 {
561         switch(isin << 16 | port){
562         case 0x10000: case 0x10001: case 0x10002: case 0x10003:
563                 return GET32(v->net.mac, 0) >> (port & 3) * 8;
564         case 0x10004: case 0x10005: case 0x10006: case 0x10007:
565                 return (GET16(v->net.mac, 4) | 1 << 16) >> (port & 3) * 8;
566         }
567         return iowhine(isin, port, val, sz, "virtio net");
568 }
569
570 int
571 vionettables(VIODev *d, VIOBuf *b)
572 {
573         u8int buf[4];
574         u8int mac[6];
575         u64int bloom[2];
576         int i, l;
577         
578         bloom[0] = 0;
579         bloom[1] = 0;
580         for(i = 0; i < 2; i++){
581                 if(vioqread(b, buf, 4) < 4)
582                         return 1;
583                 l = GET32(buf, 0);
584                 while(l--){
585                         if(vioqread(b, mac, 6) < 6)
586                                 return 1;
587                         bloom[i] |= 1ULL<<bloomhash(mac);
588                 }
589         }
590         d->net.macbloom = bloom[0];
591         d->net.multibloom = bloom[1];
592         return 0;
593 }
594
595 void
596 vionetcmd(VIOQueue *q)
597 {
598         VIODev *d;
599         VIOBuf *b;
600         u8int cmd[2], buf[6];
601         u8int ack;
602         int fl;
603
604         d = q->d;
605         for(; b = viogetbuf(q, 0), b != nil; vioputbuf(b)){
606                 if(vioqread(b, cmd, 2) < 2){
607                         ack = 1;
608                         vioqwrite(b, &ack, 1);
609                         continue;
610                 }
611                 ack = 0;
612                 switch(cmd[0] << 8 | cmd[1]){
613                 case 0x0000: fl = VNETPROMISC; goto flag;
614                 case 0x0001: fl = VNETALLMULTI; goto flag;
615                 case 0x0002: fl = VNETALLUNI; goto flag;
616                 case 0x0003: fl = VNETNOMULTI; goto flag;
617                 case 0x0004: fl = VNETNOUNI; goto flag;
618                 case 0x0005: fl = VNETNOBCAST; goto flag;
619                 flag:
620                         if(vioqread(b, buf, 1) < 1) ack = 1;
621                         else if(buf[0] == 1) d->net.flags |= fl;
622                         else if(buf[0] == 0) d->net.flags &= ~fl;
623                         else ack = 1;
624                         break;
625                 case 0x0100: /* MAC_TABLE_SET */
626                         ack = vionettables(d, b);
627                         break;
628                 case 0x0101: /* MAC_ADDR_SET */
629                         if(vioqread(b, buf, 6) < 6) ack = 1;
630                         else memmove(d->net.mac, buf, 6);
631                         break;
632                 default:
633                         ack = 1;
634                 }
635                 vioqwrite(b, &ack, 1);
636         }
637 }
638
639 void
640 vionetreset(VIODev *d)
641 {
642         d->net.flags &= VNETHEADER;
643         d->net.macbloom = 0;
644         d->net.multibloom = 0;
645 }
646
647 int
648 mkvionet(char *net)
649 {
650         int fd, cfd;
651         VIODev *d;
652         char *ea;
653         int flags;
654         enum { VNETFILE = 1 };
655
656         ea = nil;
657         flags = 0;
658         for(;;){
659                 if(strncmp(net, "hdr!", 4) == 0){
660                         net += 4;
661                         flags |= VNETHEADER;
662                 }else if(strncmp(net, "file!", 5) == 0){
663                         net += 5;
664                         flags |= VNETFILE;
665                 }else if(strncmp(net, "ea:", 3) == 0){
666                         net = strchr(ea = net+3, '!');
667                         if(net++ == nil){
668                                 werrstr("missing: !");
669                                 return -1;
670                         }
671                 }else
672                         break;
673         }
674         if((flags & VNETFILE) != 0){
675                 flags &= ~VNETFILE;
676                 fd = open(net, ORDWR);
677                 if(fd < 0) return -1;
678         }else{
679                 fd = dial(netmkaddr("-1", net, nil), nil, nil, &cfd);
680                 if(fd < 0) return -1;
681                 if(cfd >= 0) {
682                         write(cfd, "promiscuous", 11);
683                         write(cfd, "bridge", 6);
684                 }
685         }
686         
687         d = mkviodev(0x1000, 0x020000, 1, 3);
688         mkvioqueue(d, 1024, viowakeup);
689         mkvioqueue(d, 1024, viowakeup);
690         mkvioqueue(d, 32, vionetcmd);
691         if(ea == nil || parseether(d->net.mac, ea)){
692                 genrandom(d->net.mac, 6);
693                 d->net.mac[0] = d->net.mac[0] & ~1 | 2;
694         }
695         d->net.flags = flags;
696         d->devfeat = 1<<5|1<<16|1<<17|1<<18|1<<20;
697         d->io = vionetio;
698         d->reset = vionetreset;
699         d->net.readfd = d->net.writefd = fd;
700         proccreate(vionetrproc, d, 8192);
701         proccreate(vionetwproc, d, 8192);
702         return 0;
703 }
704
705 u32int
706 vioblkio(int isin, u16int port, u32int val, int sz, VIODev *v)
707 {
708         switch(isin << 16 | port){
709         case 0x10000: case 0x10001: case 0x10002: case 0x10003:
710                 return (u32int)v->blk.size >> (port & 3) * 8;
711         case 0x10004: case 0x10005: case 0x10006: case 0x10007:
712                 return (u32int)(v->blk.size >> 32) >> (port & 3) * 8;
713         }
714         return iowhine(isin, port, val, sz, "virtio blk");
715 }
716
717 void
718 vioblkproc(void *vp)
719 {
720         VIODev *v;
721         VIOQueue *q;
722         VIOBuf *b;
723         u8int cmd[16];
724         u8int ack;
725         char buf[8192];
726         uvlong addr;
727         int rc, n, m;
728         
729         threadsetname("vioblkproc");
730         v = vp;
731         q = &v->qu[0];
732         for(;;){
733                 b = viogetbuf(q, 1);
734                 if(b == nil){
735                         vmerror("vioblkproc: viogetbuf: %r");
736                         threadexits("vioblkproc: viogetbuf: %r");
737                 }
738                 ack = 0;
739                 if(vioqread(b, cmd, sizeof(cmd)) < sizeof(cmd)) goto nope;
740                 addr = GET64(cmd, 8);
741                 switch(GET32(cmd, 0)){
742                 case 0:
743                         n = vioqrem(b, 1) - 1;
744                         if(n < 0 || addr * 512 + n > v->blk.size * 512){
745                                 ack = 1;
746                                 break;
747                         }
748                         seek(v->blk.fd, addr << 9, 0);
749                         for(; n > 0; n -= rc){
750                                 rc = sizeof(buf);
751                                 if(n < rc) rc = n;
752                                 rc = read(v->blk.fd, buf, rc);
753                                 if(rc < 0) vmerror("read(vioblkproc): %r");
754                                 if(rc <= 0){
755                                         ack = 1;
756                                         break;
757                                 }
758                                 vioqwrite(b, buf, rc);
759                         }
760                         break;
761                 case 1:
762                         n = vioqrem(b, 0);
763                         if(addr * 512 + n > v->blk.size * 512){
764                                 ack = 1;
765                                 break;
766                         }
767                         seek(v->blk.fd, addr << 9, 0);
768                         for(; n > 0; n -= rc){
769                                 m = vioqread(b, buf, sizeof(buf));
770                                 rc = write(v->blk.fd, buf, m);
771                                 if(rc < 0) vmerror("write(vioblkproc): %r");
772                                 if(rc < m){
773                                         ack = 1;
774                                         break;
775                                 }
776                         }
777                         break;
778                 default:
779                 nope:
780                         ack = 2;
781                 }
782                 vioqwrite(b, &ack, 1);
783                 vioputbuf(b);
784         }
785 }
786
787 int
788 mkvioblk(char *fn)
789 {
790         int fd;
791         VIODev *d;
792         
793         fd = open(fn, ORDWR);
794         if(fd < 0) return -1;
795         d = mkviodev(0x1000, 0x018000, 2, 1);
796         mkvioqueue(d, 32, viowakeup);
797         d->io = vioblkio;
798         d->blk.fd = fd;
799         d->blk.size = seek(fd, 0, 2) >> 9;
800         proccreate(vioblkproc, d, 16384);
801         return 0;
802 }