<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv=Content-Type content="text/html; charset=utf8">
<title>/usr/web/sources/contrib/cinap_lenrek/sdvirtio.c - Plan 9 from Bell Labs</title>
<!-- THIS FILE IS AUTOMATICALLY GENERATED. -->
<!-- EDIT sources.tr INSTEAD. -->
</meta>
</head>
<body>
<p style="margin-top: 0; margin-bottom: 0.17in"></p>
<p style="line-height: 1.2em; margin-left: 1.00in; text-indent: 0.00in; margin-right: 1.00in; margin-top: 0; margin-bottom: 0; text-align: center;">
<span style="font-size: 10pt"><a href="/plan9/">Plan 9 from Bell Labs</a>&rsquo;s /usr/web/sources/contrib/cinap_lenrek/sdvirtio.c</span></p>
<p style="margin-top: 0; margin-bottom: 0.17in"></p>
<p style="margin-top: 0; margin-bottom: 0.17in"></p>
<center><font size=-1>
Copyright © 2009 Alcatel-Lucent.<br />
Distributed under the
<a href="/plan9/license.html">Lucent Public License version 1.02</a>.
<br />
<a href="/plan9/download.html">Download the Plan 9 distribution.</a>
</font>
</center>
<p style="margin-top: 0; margin-bottom: 0.17in"></p>
<table width="100%" cellspacing=0 border=0><tr><td align="center">
<table cellspacing=0 cellpadding=5 bgcolor="#eeeeff"><tr><td align="left">
<pre>
<!-- END HEADER -->
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "io.h"
#include "ureg.h"
#include "../port/error.h"

#include "../port/sd.h"

typedef struct Vring Vring;
typedef struct Vdesc Vdesc;
typedef struct Vused Vused;
typedef struct Vqueue Vqueue;
typedef struct Vdev Vdev;

/* status flags */
enum {
       Acknowledge = 1,
       Driver = 2,
       DriverOk = 4,
       Failed = 0x80,
};

/* virtio ports */
enum {
       Devfeat = 0,
       Drvfeat = 4,
       Qaddr = 8,
       Qsize = 12,
       Qselect = 14,
       Qnotify = 16,
       Status = 18,
       Isr = 19,

       Devspec = 20,
};

/* descriptor flags */
enum {
       Next = 1,
       Write = 2,
       Indirect = 4,
};

struct Vring
{
       u16int  flags;
       u16int  idx;
};

struct Vdesc
{
       u64int  addr;
       u32int  len;
       u16int  flags;
       u16int  next;
};

struct Vused
{
       u32int  id;
       u32int  len;
};

struct Vqueue
{
       Lock;
       int     size;

       int     free;
       int     nfree;

       Vdesc   *desc;

       Vring   *avail;
       u16int  *availent;
       u16int  *availevent;

       Vring   *used;
       Vused   *usedent;
       u16int  *usedevent;
       u16int  lastused;

       void    *rock[];
};

struct Vdev
{
       int     typ;

       Pcidev  *pci;

       ulong   port;
       ulong   feat;

       int     nqueue;
       Vqueue  *queue[16];

       Vdev    *next;
};

static Vqueue*
mkvqueue(int size)
{
       Vqueue *q;
       uchar *p;
       int i;

       q = malloc(sizeof(*q) + sizeof(void*)*size);
       p = mallocalign(
               PGROUND(sizeof(Vdesc)*size +
                       sizeof(Vring) +
                       sizeof(u16int)*size +
                       sizeof(u16int)) +
               PGROUND(sizeof(Vring) +
                       sizeof(Vused)*size +
                       sizeof(u16int)),
               BY2PG, 0, 0);
       if(p == nil || q == nil){
               print("virtio: no memory for Vqueue\n");
               free(p);
               free(q);
               return nil;
       }

       q-&gt;desc = (void*)p;
       p += sizeof(Vdesc)*size;
       q-&gt;avail = (void*)p;
       p += sizeof(Vring);
       q-&gt;availent = (void*)p;
       p += sizeof(u16int)*size;
       q-&gt;availevent = (void*)p;
       p += sizeof(u16int);

       p = (uchar*)PGROUND((ulong)p);
       q-&gt;used = (void*)p;
       p += sizeof(Vring);
       q-&gt;usedent = (void*)p;
       p += sizeof(Vused)*size;
       q-&gt;usedevent = (void*)p;

       q-&gt;free = -1;
       q-&gt;nfree = q-&gt;size = size;
       for(i=0; i&lt;size; i++){
               q-&gt;desc[i].next = q-&gt;free;
               q-&gt;free = i;
       }

       return q;
}

static Vdev*
viopnpdevs(int typ)
{
       Vdev *vd, *h, *t;
       Pcidev *p;
       int n, i;

       h = t = nil;
       for(p = nil; p = pcimatch(p, 0, 0);){
               if(p-&gt;vid != 0x1AF4)
                       continue;
               if((p-&gt;did &lt; 0x1000) || (p-&gt;did &gt;= 0x1040))
                       continue;
               if(p-&gt;rid != 0)
                       continue;
               if(pcicfgr16(p, 0x2E) != typ)
                       continue;
               if((vd = malloc(sizeof(*vd))) == nil){
                       print("virtio: no memory for Vdev\n");
                       break;
               }
               vd-&gt;port = p-&gt;mem[0].bar &amp; ~0x1;
               if(ioalloc(vd-&gt;port, p-&gt;mem[0].size, 0, "virtio") &lt; 0){
                       print("virtio: port %lux in use\n", vd-&gt;port);
                       free(vd);
                       continue;
               }
               vd-&gt;typ = typ;
               vd-&gt;pci = p;

               /* reset */
               outb(vd-&gt;port+Status, 0);

               vd-&gt;feat = inl(vd-&gt;port+Devfeat);
               outb(vd-&gt;port+Status, Acknowledge|Driver);
               for(i=0; i&lt;nelem(vd-&gt;queue); i++){
                       outs(vd-&gt;port+Qselect, i);
                       n = ins(vd-&gt;port+Qsize);
                       if(n == 0 || (n &amp; (n-1)) != 0)
                               break;
                       if((vd-&gt;queue[i] = mkvqueue(n)) == nil)
                               break;
                       coherence();
                       outl(vd-&gt;port+Qaddr, PADDR(vd-&gt;queue[i]-&gt;desc)/BY2PG);
               }
               vd-&gt;nqueue = i;

               if(h == nil)
                       h = vd;
               else
                       t-&gt;next = vd;
               t = vd;
       }

       return h;
}

struct Rock {
       int done;
       Rendez *sleep;
};

static void
viointerrupt(Ureg *, void *arg)
{
       int id, free, m;
       struct Rock *r;
       Vqueue *q;
       Vdev *vd;

       vd = arg;
       if(inb(vd-&gt;port+Isr) &amp; 1){
               q = vd-&gt;queue[0];
               m = q-&gt;size-1;

               ilock(q);
               while((q-&gt;lastused ^ q-&gt;used-&gt;idx) &amp; m){
                       id = q-&gt;usedent[q-&gt;lastused++ &amp; m].id;
                       if(r = q-&gt;rock[id]){
                               q-&gt;rock[id] = nil;
                               r-&gt;done = 1;
                               wakeup(r-&gt;sleep);
                       }
                       do {
                               free = id;
                               id = q-&gt;desc[free].next;
                               q-&gt;desc[free].next = q-&gt;free;
                               q-&gt;free = free;
                               q-&gt;nfree++;
                       } while(q-&gt;desc[free].flags &amp; Next);
               }
               iunlock(q);
       }
}

static int
viodone(void *arg)
{
       return ((struct Rock*)arg)-&gt;done;
}

static int
vioreq(Vdev *vd, int typ, void *a, long count, long secsize, uvlong lba)
{
       struct Rock rock;
       int free, head;
       Vqueue *q;
       Vdesc *d;

       u8int status;
       struct Vioreqhdr {
               u32int  typ;
               u32int  prio;
               u64int  lba;
       } req;

       status = 0;
       req.typ = typ;
       req.prio = 0;
       req.lba = lba;

       rock.done = 0;
       rock.sleep = &amp;up-&gt;sleep;

       q = vd-&gt;queue[0];
       ilock(q);
       while(q-&gt;nfree &lt; 3){
               iunlock(q);

               if(!waserror())
                       tsleep(&amp;up-&gt;sleep, return0, 0, 500);
               poperror();

               ilock(q);
       }

       head = free = q-&gt;free;

       d = &amp;q-&gt;desc[free]; free = d-&gt;next;
       d-&gt;addr = PADDR(&amp;req);
       d-&gt;len = sizeof(req);
       d-&gt;flags = Next;

       d = &amp;q-&gt;desc[free]; free = d-&gt;next;
       d-&gt;addr = PADDR(a);
       d-&gt;len = secsize*count;
       d-&gt;flags = typ ? Next : (Write|Next);

       d = &amp;q-&gt;desc[free]; free = d-&gt;next;
       d-&gt;addr = PADDR(&amp;status);
       d-&gt;len = sizeof(status);
       d-&gt;flags = Write;

       q-&gt;free = free;
       q-&gt;nfree -= 3;

       q-&gt;rock[head] = &amp;rock;

       coherence();
       q-&gt;availent[q-&gt;avail-&gt;idx++ &amp; (q-&gt;size-1)] = head;
       coherence();
       outs(vd-&gt;port+Qnotify, 0);
       iunlock(q);

       while(!rock.done){
               while(waserror())
                       ;
               tsleep(rock.sleep, viodone, &amp;rock, 1000);
               poperror();

               if(!rock.done)
                       viointerrupt(nil, vd);
       }

       return status;
}

static long
viobio(SDunit *u, int, int write, void *a, long count, uvlong lba)
{
       long ss, cc, max, ret;
       Vdev *vd;

       max = 32;
       ss = u-&gt;secsize;
       vd = u-&gt;dev-&gt;ctlr;

       ret = 0;
       while(count &gt; 0){
               if((cc = count) &gt; max)
                       cc = max;
               if(vioreq(vd, write != 0, (uchar*)a + ret, cc, ss, lba) != 0)
                       error(Eio);
               ret += cc*ss;
               count -= cc;
               lba += cc;
       }

       return ret;
}

static int
viorio(SDreq *r)
{
       int i, count, rw;
       uvlong lba;
       SDunit *u;

       u = r-&gt;unit;
       if(r-&gt;cmd[0] == 0x35 || r-&gt;cmd[0] == 0x91){
               if(vioreq(u-&gt;dev-&gt;ctlr, 4, nil, 0, 0, 0) != 0)
                       return sdsetsense(r, SDcheck, 3, 0xc, 2);
               return sdsetsense(r, SDok, 0, 0, 0);
       }
       if((i = sdfakescsi(r)) != SDnostatus)
               return r-&gt;status = i;
       if((i = sdfakescsirw(r, &amp;lba, &amp;count, &amp;rw)) != SDnostatus)
               return i;
       r-&gt;rlen = viobio(u, r-&gt;lun, rw == SDwrite, r-&gt;data, count, lba);
       return r-&gt;status = SDok;
}

static int
vioonline(SDunit *u)
{
       uvlong cap;
       Vdev *vd;

       vd = u-&gt;dev-&gt;ctlr;
       cap = inl(vd-&gt;port+Devspec+4);
       cap &lt;&lt;= 32;
       cap |= inl(vd-&gt;port+Devspec);
       if(u-&gt;sectors != cap){
               u-&gt;sectors = cap;
               u-&gt;secsize = 512;
               return 2;
       }
       return 1;
}

static int
vioverify(SDunit *)
{
       return 1;
}

SDifc sdvirtioifc;

static SDev*
viopnp(void)
{
       SDev *s, *h, *t;
       Vdev *vd;
       int id;

       id = 'F';
       h = t = nil;
       for(vd =  viopnpdevs(2); vd; vd = vd-&gt;next){
               if(vd-&gt;nqueue != 1)
                       continue;

               intrenable(vd-&gt;pci-&gt;intl, viointerrupt, vd, vd-&gt;pci-&gt;tbdf, "virtio");
               outb(vd-&gt;port+Status, inb(vd-&gt;port+Status) | DriverOk);

               if((s = malloc(sizeof(*s))) == nil)
                       break;
               s-&gt;ctlr = vd;
               s-&gt;idno = id++;
               s-&gt;ifc = &amp;sdvirtioifc;
               s-&gt;nunit = 1;
               if(h)
                       t-&gt;next = s;
               else
                       h = s;
               t = s;
       }

       return h;
}

SDifc sdvirtioifc = {
       "virtio",                       /* name */

       viopnp,                         /* pnp */
       nil,                            /* legacy */
       nil,                            /* enable */
       nil,                            /* disable */

       vioverify,                      /* verify */
       vioonline,                      /* online */
       viorio,                         /* rio */
       nil,                            /* rctl */
       nil,                            /* wctl */

       viobio,                         /* bio */
       nil,                            /* probe */
       nil,                            /* clear */
       nil,                            /* rtopctl */
       nil,                            /* wtopctl */
};
<!-- BEGIN TAIL -->
</pre>
</td></tr></table>
</td></tr></table>
<p style="margin-top: 0; margin-bottom: 0.17in"></p>
<p style="line-height: 1.2em; margin-left: 1.00in; text-indent: 0.00in; margin-right: 1.00in; margin-top: 0; margin-bottom: 0; text-align: center;">
<span style="font-size: 10pt"></span></p>
<p style="margin-top: 0; margin-bottom: 0.50in"></p>
<p style="margin-top: 0; margin-bottom: 0.33in"></p>
<center><table border="0"><tr>
<td valign="middle"><a href="http://www.alcatel-lucent.com/"><img border="0" src="/plan9/img/logo_ft.gif" alt="Bell Labs" />
</a></td>
<td valign="middle"><a href="http://www.opensource.org"><img border="0" alt="OSI certified" src="/plan9/img/osi-certified-60x50.gif" />
</a></td>
<td><img style="padding-right: 45px;" alt="Powered by Plan 9" src="/plan9/img/power36.gif" />
</td>
</tr></table></center>
<p style="margin-top: 0; margin-bottom: 0.17in"></p>
<center>
<span style="font-size: 10pt">(<a href="/plan9/">Return to Plan 9 Home Page</a>)</span>
</center>
<p style="margin-top: 0; margin-bottom: 0.17in"></p>
<center><font size=-1>
<span style="font-size: 10pt"><a href="http://www.lucent.com/copyright.html">Copyright</a></span>
<span style="font-size: 10pt">© 2009 Alcatel-Lucent.</span>
<span style="font-size: 10pt">All Rights Reserved.</span>
<br />
<span style="font-size: 10pt">Comments to</span>
<span style="font-size: 10pt"><a href="mailto:[email protected]">[email protected]</a>.</span>
</font></center>
</body>
</html>