rev |
line source |
pascal@698
|
1 Index: qemu-0.9.1/Makefile.target
|
pascal@698
|
2 ===================================================================
|
pascal@698
|
3 --- qemu-0.9.1.orig/Makefile.target 2008-01-06 19:38:41.000000000 +0000
|
pascal@698
|
4 +++ qemu-0.9.1/Makefile.target 2008-02-07 13:36:23.000000000 +0000
|
pascal@698
|
5 @@ -436,6 +436,9 @@
|
pascal@698
|
6 VL_OBJS += pcnet.o
|
pascal@698
|
7 VL_OBJS += rtl8139.o
|
pascal@698
|
8
|
pascal@698
|
9 +# virtio devices
|
pascal@698
|
10 +VL_OBJS += virtio.o
|
pascal@698
|
11 +
|
pascal@698
|
12 ifeq ($(TARGET_BASE_ARCH), i386)
|
pascal@698
|
13 # Hardware support
|
pascal@698
|
14 VL_OBJS+= ide.o pckbd.o ps2.o vga.o $(SOUND_HW) dma.o
|
pascal@698
|
15 Index: qemu-0.9.1/hw/virtio.c
|
pascal@698
|
16 ===================================================================
|
pascal@698
|
17 --- /dev/null 1970-01-01 00:00:00.000000000 +0000
|
pascal@698
|
18 +++ qemu-0.9.1/hw/virtio.c 2008-02-07 13:36:23.000000000 +0000
|
pascal@698
|
19 @@ -0,0 +1,422 @@
|
pascal@698
|
20 +/*
|
pascal@698
|
21 + * Virtio Support
|
pascal@698
|
22 + *
|
pascal@698
|
23 + * Copyright IBM, Corp. 2007
|
pascal@698
|
24 + *
|
pascal@698
|
25 + * Authors:
|
pascal@698
|
26 + * Anthony Liguori <address@hidden>
|
pascal@698
|
27 + *
|
pascal@698
|
28 + * This work is licensed under the terms of the GNU GPL, version 2. See
|
pascal@698
|
29 + * the COPYING file in the top-level directory.
|
pascal@698
|
30 + *
|
pascal@698
|
31 + */
|
pascal@698
|
32 +
|
pascal@698
|
33 +#include <inttypes.h>
|
pascal@698
|
34 +#include <err.h>
|
pascal@698
|
35 +
|
pascal@698
|
36 +#include "virtio.h"
|
pascal@698
|
37 +#include "sysemu.h"
|
pascal@698
|
38 +
|
pascal@698
|
39 +/* from Linux's linux/virtio_pci.h */
|
pascal@698
|
40 +
|
pascal@698
|
41 +/* A 32-bit r/o bitmask of the features supported by the host */
|
pascal@698
|
42 +#define VIRTIO_PCI_HOST_FEATURES 0
|
pascal@698
|
43 +
|
pascal@698
|
44 +/* A 32-bit r/w bitmask of features activated by the guest */
|
pascal@698
|
45 +#define VIRTIO_PCI_GUEST_FEATURES 4
|
pascal@698
|
46 +
|
pascal@698
|
47 +/* A 32-bit r/w PFN for the currently selected queue */
|
pascal@698
|
48 +#define VIRTIO_PCI_QUEUE_PFN 8
|
pascal@698
|
49 +
|
pascal@698
|
50 +/* A 16-bit r/o queue size for the currently selected queue */
|
pascal@698
|
51 +#define VIRTIO_PCI_QUEUE_NUM 12
|
pascal@698
|
52 +
|
pascal@698
|
53 +/* A 16-bit r/w queue selector */
|
pascal@698
|
54 +#define VIRTIO_PCI_QUEUE_SEL 14
|
pascal@698
|
55 +
|
pascal@698
|
56 +/* A 16-bit r/w queue notifier */
|
pascal@698
|
57 +#define VIRTIO_PCI_QUEUE_NOTIFY 16
|
pascal@698
|
58 +
|
pascal@698
|
59 +/* An 8-bit device status register. */
|
pascal@698
|
60 +#define VIRTIO_PCI_STATUS 18
|
pascal@698
|
61 +
|
pascal@698
|
62 +/* An 8-bit r/o interrupt status register. Reading the value will return the
|
pascal@698
|
63 + * current contents of the ISR and will also clear it. This is effectively
|
pascal@698
|
64 + * a read-and-acknowledge. */
|
pascal@698
|
65 +#define VIRTIO_PCI_ISR 19
|
pascal@698
|
66 +
|
pascal@698
|
67 +#define VIRTIO_PCI_CONFIG 20
|
pascal@698
|
68 +
|
pascal@698
|
69 +/* QEMU doesn't strictly need write barriers since everything runs in
|
pascal@698
|
70 + * lock-step. We'll leave the calls to wmb() in though to make it obvious for
|
pascal@698
|
71 + * KVM or if kqemu gets SMP support.
|
pascal@698
|
72 + */
|
pascal@698
|
73 +#define wmb() do { } while (0)
|
pascal@698
|
74 +
|
pascal@698
|
75 +/* virt queue functions */
|
pascal@698
|
76 +
|
pascal@698
|
77 +static void virtqueue_init(VirtQueue *vq, void *p)
|
pascal@698
|
78 +{
|
pascal@698
|
79 + vq->vring.desc = p;
|
pascal@698
|
80 + vq->vring.avail = p + vq->vring.num * sizeof(VRingDesc);
|
pascal@698
|
81 + vq->vring.used = (void *)TARGET_PAGE_ALIGN((unsigned long)&vq->vring.avail->ring[vq->vring.num]);
|
pascal@698
|
82 +}
|
pascal@698
|
83 +
|
pascal@698
|
84 +static unsigned virtqueue_next_desc(VirtQueue *vq, unsigned int i)
|
pascal@698
|
85 +{
|
pascal@698
|
86 + unsigned int next;
|
pascal@698
|
87 +
|
pascal@698
|
88 + /* If this descriptor says it doesn't chain, we're done. */
|
pascal@698
|
89 + if (!(vq->vring.desc[i].flags & VRING_DESC_F_NEXT))
|
pascal@698
|
90 + return vq->vring.num;
|
pascal@698
|
91 +
|
pascal@698
|
92 + /* Check they're not leading us off end of descriptors. */
|
pascal@698
|
93 + next = vq->vring.desc[i].next;
|
pascal@698
|
94 + /* Make sure compiler knows to grab that: we don't want it changing! */
|
pascal@698
|
95 + wmb();
|
pascal@698
|
96 +
|
pascal@698
|
97 + if (next >= vq->vring.num)
|
pascal@698
|
98 + errx(1, "Desc next is %u", next);
|
pascal@698
|
99 +
|
pascal@698
|
100 + return next;
|
pascal@698
|
101 +}
|
pascal@698
|
102 +
|
pascal@698
|
103 +void virtqueue_push(VirtQueue *vq, const VirtQueueElement *elem,
|
pascal@698
|
104 + unsigned int len)
|
pascal@698
|
105 +{
|
pascal@698
|
106 + VRingUsedElem *used;
|
pascal@698
|
107 +
|
pascal@698
|
108 + /* Get a pointer to the next entry in the used ring. */
|
pascal@698
|
109 + used = &vq->vring.used->ring[vq->vring.used->idx % vq->vring.num];
|
pascal@698
|
110 + used->id = elem->index;
|
pascal@698
|
111 + used->len = len;
|
pascal@698
|
112 + /* Make sure buffer is written before we update index. */
|
pascal@698
|
113 + wmb();
|
pascal@698
|
114 + vq->vring.used->idx++;
|
pascal@698
|
115 +}
|
pascal@698
|
116 +
|
pascal@698
|
117 +int virtqueue_pop(VirtQueue *vq, VirtQueueElement *elem)
|
pascal@698
|
118 +{
|
pascal@698
|
119 + unsigned int i, head;
|
pascal@698
|
120 + unsigned int position;
|
pascal@698
|
121 +
|
pascal@698
|
122 + /* Check it isn't doing very strange things with descriptor numbers. */
|
pascal@698
|
123 + if ((uint16_t)(vq->vring.avail->idx - vq->last_avail_idx) > vq->vring.num)
|
pascal@698
|
124 + errx(1, "Guest moved used index from %u to %u",
|
pascal@698
|
125 + vq->last_avail_idx, vq->vring.avail->idx);
|
pascal@698
|
126 +
|
pascal@698
|
127 + /* If there's nothing new since last we looked, return invalid. */
|
pascal@698
|
128 + if (vq->vring.avail->idx == vq->last_avail_idx)
|
pascal@698
|
129 + return 0;
|
pascal@698
|
130 +
|
pascal@698
|
131 + /* Grab the next descriptor number they're advertising, and increment
|
pascal@698
|
132 + * the index we've seen. */
|
pascal@698
|
133 + head = vq->vring.avail->ring[vq->last_avail_idx++ % vq->vring.num];
|
pascal@698
|
134 +
|
pascal@698
|
135 + /* If their number is silly, that's a fatal mistake. */
|
pascal@698
|
136 + if (head >= vq->vring.num)
|
pascal@698
|
137 + errx(1, "Guest says index %u is available", head);
|
pascal@698
|
138 +
|
pascal@698
|
139 + /* When we start there are none of either input nor output. */
|
pascal@698
|
140 + position = elem->out_num = elem->in_num = 0;
|
pascal@698
|
141 +
|
pascal@698
|
142 + i = head;
|
pascal@698
|
143 + do {
|
pascal@698
|
144 + struct iovec *sg;
|
pascal@698
|
145 +
|
pascal@698
|
146 + if ((vq->vring.desc[i].addr + vq->vring.desc[i].len) > ram_size)
|
pascal@698
|
147 + errx(1, "Guest sent invalid pointer");
|
pascal@698
|
148 +
|
pascal@698
|
149 + if (vq->vring.desc[i].flags & VRING_DESC_F_WRITE)
|
pascal@698
|
150 + sg = &elem->in_sg[elem->in_num++];
|
pascal@698
|
151 + else
|
pascal@698
|
152 + sg = &elem->out_sg[elem->out_num++];
|
pascal@698
|
153 +
|
pascal@698
|
154 + /* Grab the first descriptor, and check it's OK. */
|
pascal@698
|
155 + sg->iov_len = vq->vring.desc[i].len;
|
pascal@698
|
156 + sg->iov_base = phys_ram_base + vq->vring.desc[i].addr;
|
pascal@698
|
157 +
|
pascal@698
|
158 + /* If we've got too many, that implies a descriptor loop. */
|
pascal@698
|
159 + if ((elem->in_num + elem->out_num) > vq->vring.num)
|
pascal@698
|
160 + errx(1, "Looped descriptor");
|
pascal@698
|
161 + } while ((i = virtqueue_next_desc(vq, i)) != vq->vring.num);
|
pascal@698
|
162 +
|
pascal@698
|
163 + elem->index = head;
|
pascal@698
|
164 +
|
pascal@698
|
165 + return elem->in_num + elem->out_num;
|
pascal@698
|
166 +}
|
pascal@698
|
167 +
|
pascal@698
|
168 +/* virtio device */
|
pascal@698
|
169 +
|
pascal@698
|
170 +static VirtIODevice *to_virtio_device(PCIDevice *pci_dev)
|
pascal@698
|
171 +{
|
pascal@698
|
172 + return (VirtIODevice *)pci_dev;
|
pascal@698
|
173 +}
|
pascal@698
|
174 +
|
pascal@698
|
175 +static void virtio_update_irq(VirtIODevice *vdev)
|
pascal@698
|
176 +{
|
pascal@698
|
177 + qemu_set_irq(vdev->pci_dev.irq[0], vdev->isr & 1);
|
pascal@698
|
178 +}
|
pascal@698
|
179 +
|
pascal@698
|
180 +static void virtio_ioport_write(void *opaque, uint32_t addr, uint32_t val)
|
pascal@698
|
181 +{
|
pascal@698
|
182 + VirtIODevice *vdev = to_virtio_device(opaque);
|
pascal@698
|
183 + ram_addr_t pa;
|
pascal@698
|
184 +
|
pascal@698
|
185 + addr -= vdev->addr;
|
pascal@698
|
186 +
|
pascal@698
|
187 + switch (addr) {
|
pascal@698
|
188 + case VIRTIO_PCI_GUEST_FEATURES:
|
pascal@698
|
189 + if (vdev->set_features)
|
pascal@698
|
190 + vdev->set_features(vdev, val);
|
pascal@698
|
191 + vdev->features = val;
|
pascal@698
|
192 + break;
|
pascal@698
|
193 + case VIRTIO_PCI_QUEUE_PFN:
|
pascal@698
|
194 + pa = (ram_addr_t)val << TARGET_PAGE_BITS;
|
pascal@698
|
195 + vdev->vq[vdev->queue_sel].pfn = val;
|
pascal@698
|
196 + if (pa == 0) {
|
pascal@698
|
197 + vdev->vq[vdev->queue_sel].vring.desc = NULL;
|
pascal@698
|
198 + vdev->vq[vdev->queue_sel].vring.avail = NULL;
|
pascal@698
|
199 + vdev->vq[vdev->queue_sel].vring.used = NULL;
|
pascal@698
|
200 + } else if (pa < (ram_size - TARGET_PAGE_SIZE)) {
|
pascal@698
|
201 + virtqueue_init(&vdev->vq[vdev->queue_sel], phys_ram_base + pa);
|
pascal@698
|
202 + /* FIXME if pa == 0, deal with device tear down */
|
pascal@698
|
203 + }
|
pascal@698
|
204 + break;
|
pascal@698
|
205 + case VIRTIO_PCI_QUEUE_SEL:
|
pascal@698
|
206 + if (val < VIRTIO_PCI_QUEUE_MAX)
|
pascal@698
|
207 + vdev->queue_sel = val;
|
pascal@698
|
208 + break;
|
pascal@698
|
209 + case VIRTIO_PCI_QUEUE_NOTIFY:
|
pascal@698
|
210 + if (val < VIRTIO_PCI_QUEUE_MAX && vdev->vq[val].vring.desc)
|
pascal@698
|
211 + vdev->vq[val].handle_output(vdev, &vdev->vq[val]);
|
pascal@698
|
212 + break;
|
pascal@698
|
213 + case VIRTIO_PCI_STATUS:
|
pascal@698
|
214 + vdev->status = val & 0xFF;
|
pascal@698
|
215 + break;
|
pascal@698
|
216 + }
|
pascal@698
|
217 +}
|
pascal@698
|
218 +
|
pascal@698
|
219 +static uint32_t virtio_ioport_read(void *opaque, uint32_t addr)
|
pascal@698
|
220 +{
|
pascal@698
|
221 + VirtIODevice *vdev = to_virtio_device(opaque);
|
pascal@698
|
222 + uint32_t ret = 0xFFFFFFFF;
|
pascal@698
|
223 +
|
pascal@698
|
224 + addr -= vdev->addr;
|
pascal@698
|
225 +
|
pascal@698
|
226 + switch (addr) {
|
pascal@698
|
227 + case VIRTIO_PCI_HOST_FEATURES:
|
pascal@698
|
228 + ret = vdev->get_features(vdev);
|
pascal@698
|
229 + break;
|
pascal@698
|
230 + case VIRTIO_PCI_GUEST_FEATURES:
|
pascal@698
|
231 + ret = vdev->features;
|
pascal@698
|
232 + break;
|
pascal@698
|
233 + case VIRTIO_PCI_QUEUE_PFN:
|
pascal@698
|
234 + ret = vdev->vq[vdev->queue_sel].pfn;
|
pascal@698
|
235 + break;
|
pascal@698
|
236 + case VIRTIO_PCI_QUEUE_NUM:
|
pascal@698
|
237 + ret = vdev->vq[vdev->queue_sel].vring.num;
|
pascal@698
|
238 + break;
|
pascal@698
|
239 + case VIRTIO_PCI_QUEUE_SEL:
|
pascal@698
|
240 + ret = vdev->queue_sel;
|
pascal@698
|
241 + break;
|
pascal@698
|
242 + case VIRTIO_PCI_STATUS:
|
pascal@698
|
243 + ret = vdev->status;
|
pascal@698
|
244 + break;
|
pascal@698
|
245 + case VIRTIO_PCI_ISR:
|
pascal@698
|
246 + /* reading from the ISR also clears it. */
|
pascal@698
|
247 + ret = vdev->isr;
|
pascal@698
|
248 + vdev->isr = 0;
|
pascal@698
|
249 + virtio_update_irq(vdev);
|
pascal@698
|
250 + break;
|
pascal@698
|
251 + default:
|
pascal@698
|
252 + break;
|
pascal@698
|
253 + }
|
pascal@698
|
254 +
|
pascal@698
|
255 + return ret;
|
pascal@698
|
256 +}
|
pascal@698
|
257 +
|
pascal@698
|
258 +static uint32_t virtio_config_readb(void *opaque, uint32_t addr)
|
pascal@698
|
259 +{
|
pascal@698
|
260 + VirtIODevice *vdev = opaque;
|
pascal@698
|
261 + uint8_t val;
|
pascal@698
|
262 +
|
pascal@698
|
263 + addr -= vdev->addr + VIRTIO_PCI_CONFIG;
|
pascal@698
|
264 + if (addr > (vdev->config_len - sizeof(val)))
|
pascal@698
|
265 + return (uint32_t)-1;
|
pascal@698
|
266 +
|
pascal@698
|
267 + memcpy(&val, vdev->config + addr, sizeof(val));
|
pascal@698
|
268 + return val;
|
pascal@698
|
269 +}
|
pascal@698
|
270 +
|
pascal@698
|
271 +static uint32_t virtio_config_readw(void *opaque, uint32_t addr)
|
pascal@698
|
272 +{
|
pascal@698
|
273 + VirtIODevice *vdev = opaque;
|
pascal@698
|
274 + uint16_t val;
|
pascal@698
|
275 +
|
pascal@698
|
276 + addr -= vdev->addr + VIRTIO_PCI_CONFIG;
|
pascal@698
|
277 + if (addr > (vdev->config_len - sizeof(val)))
|
pascal@698
|
278 + return (uint32_t)-1;
|
pascal@698
|
279 +
|
pascal@698
|
280 + memcpy(&val, vdev->config + addr, sizeof(val));
|
pascal@698
|
281 + return val;
|
pascal@698
|
282 +}
|
pascal@698
|
283 +
|
pascal@698
|
284 +static uint32_t virtio_config_readl(void *opaque, uint32_t addr)
|
pascal@698
|
285 +{
|
pascal@698
|
286 + VirtIODevice *vdev = opaque;
|
pascal@698
|
287 + uint32_t val;
|
pascal@698
|
288 +
|
pascal@698
|
289 + addr -= vdev->addr + VIRTIO_PCI_CONFIG;
|
pascal@698
|
290 + if (addr > (vdev->config_len - sizeof(val)))
|
pascal@698
|
291 + return (uint32_t)-1;
|
pascal@698
|
292 +
|
pascal@698
|
293 + memcpy(&val, vdev->config + addr, sizeof(val));
|
pascal@698
|
294 + return val;
|
pascal@698
|
295 +}
|
pascal@698
|
296 +
|
pascal@698
|
297 +static void virtio_config_writeb(void *opaque, uint32_t addr, uint32_t data)
|
pascal@698
|
298 +{
|
pascal@698
|
299 + VirtIODevice *vdev = opaque;
|
pascal@698
|
300 + uint8_t val = data;
|
pascal@698
|
301 +
|
pascal@698
|
302 + addr -= vdev->addr + VIRTIO_PCI_CONFIG;
|
pascal@698
|
303 + if (addr > (vdev->config_len - sizeof(val)))
|
pascal@698
|
304 + return;
|
pascal@698
|
305 +
|
pascal@698
|
306 + memcpy(vdev->config + addr, &val, sizeof(val));
|
pascal@698
|
307 +}
|
pascal@698
|
308 +
|
pascal@698
|
309 +static void virtio_config_writew(void *opaque, uint32_t addr, uint32_t data)
|
pascal@698
|
310 +{
|
pascal@698
|
311 + VirtIODevice *vdev = opaque;
|
pascal@698
|
312 + uint16_t val = data;
|
pascal@698
|
313 +
|
pascal@698
|
314 + addr -= vdev->addr + VIRTIO_PCI_CONFIG;
|
pascal@698
|
315 + if (addr > (vdev->config_len - sizeof(val)))
|
pascal@698
|
316 + return;
|
pascal@698
|
317 +
|
pascal@698
|
318 + memcpy(vdev->config + addr, &val, sizeof(val));
|
pascal@698
|
319 +}
|
pascal@698
|
320 +
|
pascal@698
|
321 +static void virtio_config_writel(void *opaque, uint32_t addr, uint32_t data)
|
pascal@698
|
322 +{
|
pascal@698
|
323 + VirtIODevice *vdev = opaque;
|
pascal@698
|
324 + uint32_t val = data;
|
pascal@698
|
325 +
|
pascal@698
|
326 + addr -= vdev->addr + VIRTIO_PCI_CONFIG;
|
pascal@698
|
327 + if (addr > (vdev->config_len - sizeof(val)))
|
pascal@698
|
328 + return;
|
pascal@698
|
329 +
|
pascal@698
|
330 + memcpy(vdev->config + addr, &val, sizeof(val));
|
pascal@698
|
331 +}
|
pascal@698
|
332 +
|
pascal@698
|
333 +static void virtio_map(PCIDevice *pci_dev, int region_num,
|
pascal@698
|
334 + uint32_t addr, uint32_t size, int type)
|
pascal@698
|
335 +{
|
pascal@698
|
336 + VirtIODevice *vdev = to_virtio_device(pci_dev);
|
pascal@698
|
337 + int i;
|
pascal@698
|
338 +
|
pascal@698
|
339 + vdev->addr = addr;
|
pascal@698
|
340 + for (i = 0; i < 3; i++) {
|
pascal@698
|
341 + register_ioport_write(addr, 20, 1 << i, virtio_ioport_write, vdev);
|
pascal@698
|
342 + register_ioport_read(addr, 20, 1 << i, virtio_ioport_read, vdev);
|
pascal@698
|
343 + }
|
pascal@698
|
344 +
|
pascal@698
|
345 + if (vdev->config_len) {
|
pascal@698
|
346 + register_ioport_write(addr + 20, vdev->config_len, 1,
|
pascal@698
|
347 + virtio_config_writeb, vdev);
|
pascal@698
|
348 + register_ioport_write(addr + 20, vdev->config_len, 2,
|
pascal@698
|
349 + virtio_config_writew, vdev);
|
pascal@698
|
350 + register_ioport_write(addr + 20, vdev->config_len, 4,
|
pascal@698
|
351 + virtio_config_writel, vdev);
|
pascal@698
|
352 + register_ioport_read(addr + 20, vdev->config_len, 1,
|
pascal@698
|
353 + virtio_config_readb, vdev);
|
pascal@698
|
354 + register_ioport_read(addr + 20, vdev->config_len, 2,
|
pascal@698
|
355 + virtio_config_readw, vdev);
|
pascal@698
|
356 + register_ioport_read(addr + 20, vdev->config_len, 4,
|
pascal@698
|
357 + virtio_config_readl, vdev);
|
pascal@698
|
358 +
|
pascal@698
|
359 + vdev->update_config(vdev, vdev->config);
|
pascal@698
|
360 + }
|
pascal@698
|
361 +}
|
pascal@698
|
362 +
|
pascal@698
|
363 +VirtQueue *virtio_add_queue(VirtIODevice *vdev, int queue_size,
|
pascal@698
|
364 + void (*handle_output)(VirtIODevice *, VirtQueue *))
|
pascal@698
|
365 +{
|
pascal@698
|
366 + int i;
|
pascal@698
|
367 +
|
pascal@698
|
368 + for (i = 0; i < VIRTIO_PCI_QUEUE_MAX; i++) {
|
pascal@698
|
369 + if (vdev->vq[i].vring.num == 0)
|
pascal@698
|
370 + break;
|
pascal@698
|
371 + }
|
pascal@698
|
372 +
|
pascal@698
|
373 + if (i == VIRTIO_PCI_QUEUE_MAX || queue_size > VIRTQUEUE_MAX_SIZE)
|
pascal@698
|
374 + abort();
|
pascal@698
|
375 +
|
pascal@698
|
376 + vdev->vq[i].vring.num = queue_size;
|
pascal@698
|
377 + vdev->vq[i].handle_output = handle_output;
|
pascal@698
|
378 + vdev->vq[i].index = i;
|
pascal@698
|
379 +
|
pascal@698
|
380 + return &vdev->vq[i];
|
pascal@698
|
381 +}
|
pascal@698
|
382 +
|
pascal@698
|
383 +void virtio_notify(VirtIODevice *vdev, VirtQueue *vq)
|
pascal@698
|
384 +{
|
pascal@698
|
385 + if (vq->vring.avail->flags & VRING_AVAIL_F_NO_INTERRUPT)
|
pascal@698
|
386 + return;
|
pascal@698
|
387 +
|
pascal@698
|
388 + vdev->isr = 1;
|
pascal@698
|
389 + virtio_update_irq(vdev);
|
pascal@698
|
390 +}
|
pascal@698
|
391 +
|
pascal@698
|
392 +VirtIODevice *virtio_init_pci(PCIBus *bus, const char *name,
|
pascal@698
|
393 + uint16_t vendor, uint16_t device,
|
pascal@698
|
394 + uint16_t subvendor, uint16_t subdevice,
|
pascal@698
|
395 + uint8_t class_code, uint8_t subclass_code,
|
pascal@698
|
396 + uint8_t pif, size_t config_size,
|
pascal@698
|
397 + size_t struct_size)
|
pascal@698
|
398 +{
|
pascal@698
|
399 + VirtIODevice *vdev;
|
pascal@698
|
400 + PCIDevice *pci_dev;
|
pascal@698
|
401 + uint8_t *config;
|
pascal@698
|
402 +
|
pascal@698
|
403 + pci_dev = pci_register_device(bus, name, struct_size,
|
pascal@698
|
404 + -1, NULL, NULL);
|
pascal@698
|
405 + vdev = to_virtio_device(pci_dev);
|
pascal@698
|
406 +
|
pascal@698
|
407 + vdev->status = 0;
|
pascal@698
|
408 + vdev->isr = 0;
|
pascal@698
|
409 + vdev->queue_sel = 0;
|
pascal@698
|
410 + memset(vdev->vq, 0, sizeof(vdev->vq));
|
pascal@698
|
411 +
|
pascal@698
|
412 + config = pci_dev->config;
|
pascal@698
|
413 + config[0x00] = vendor & 0xFF;
|
pascal@698
|
414 + config[0x01] = (vendor >> 8) & 0xFF;
|
pascal@698
|
415 + config[0x02] = device & 0xFF;
|
pascal@698
|
416 + config[0x03] = (device >> 8) & 0xFF;
|
pascal@698
|
417 +
|
pascal@698
|
418 + config[0x09] = pif;
|
pascal@698
|
419 + config[0x0a] = subclass_code;
|
pascal@698
|
420 + config[0x0b] = class_code;
|
pascal@698
|
421 + config[0x0e] = 0x00;
|
pascal@698
|
422 +
|
pascal@698
|
423 + config[0x2c] = subvendor & 0xFF;
|
pascal@698
|
424 + config[0x2d] = (subvendor >> 8) & 0xFF;
|
pascal@698
|
425 + config[0x2e] = subdevice & 0xFF;
|
pascal@698
|
426 + config[0x2f] = (subdevice >> 8) & 0xFF;
|
pascal@698
|
427 +
|
pascal@698
|
428 + config[0x3d] = 1;
|
pascal@698
|
429 +
|
pascal@698
|
430 + vdev->name = name;
|
pascal@698
|
431 + vdev->config_len = config_size;
|
pascal@698
|
432 + if (vdev->config_len)
|
pascal@698
|
433 + vdev->config = qemu_mallocz(config_size);
|
pascal@698
|
434 + else
|
pascal@698
|
435 + vdev->config = NULL;
|
pascal@698
|
436 +
|
pascal@698
|
437 + pci_register_io_region(pci_dev, 0, 20 + config_size, PCI_ADDRESS_SPACE_IO,
|
pascal@698
|
438 + virtio_map);
|
pascal@698
|
439 +
|
pascal@698
|
440 + return vdev;
|
pascal@698
|
441 +}
|
pascal@698
|
442 Index: qemu-0.9.1/hw/virtio.h
|
pascal@698
|
443 ===================================================================
|
pascal@698
|
444 --- /dev/null 1970-01-01 00:00:00.000000000 +0000
|
pascal@698
|
445 +++ qemu-0.9.1/hw/virtio.h 2008-02-07 13:36:23.000000000 +0000
|
pascal@698
|
446 @@ -0,0 +1,143 @@
|
pascal@698
|
447 +/*
|
pascal@698
|
448 + * Virtio Support
|
pascal@698
|
449 + *
|
pascal@698
|
450 + * Copyright IBM, Corp. 2007
|
pascal@698
|
451 + *
|
pascal@698
|
452 + * Authors:
|
pascal@698
|
453 + * Anthony Liguori <address@hidden>
|
pascal@698
|
454 + *
|
pascal@698
|
455 + * This work is licensed under the terms of the GNU GPL, version 2. See
|
pascal@698
|
456 + * the COPYING file in the top-level directory.
|
pascal@698
|
457 + *
|
pascal@698
|
458 + */
|
pascal@698
|
459 +
|
pascal@698
|
460 +#ifndef _QEMU_VIRTIO_H
|
pascal@698
|
461 +#define _QEMU_VIRTIO_H
|
pascal@698
|
462 +
|
pascal@698
|
463 +#include <sys/uio.h>
|
pascal@698
|
464 +#include "hw.h"
|
pascal@698
|
465 +#include "pci.h"
|
pascal@698
|
466 +
|
pascal@698
|
467 +/* from Linux's linux/virtio_config.h */
|
pascal@698
|
468 +
|
pascal@698
|
469 +/* Status byte for guest to report progress, and synchronize features. */
|
pascal@698
|
470 +/* We have seen device and processed generic fields (VIRTIO_CONFIG_F_VIRTIO) */
|
pascal@698
|
471 +#define VIRTIO_CONFIG_S_ACKNOWLEDGE 1
|
pascal@698
|
472 +/* We have found a driver for the device. */
|
pascal@698
|
473 +#define VIRTIO_CONFIG_S_DRIVER 2
|
pascal@698
|
474 +/* Driver has used its parts of the config, and is happy */
|
pascal@698
|
475 +#define VIRTIO_CONFIG_S_DRIVER_OK 4
|
pascal@698
|
476 +/* We've given up on this device. */
|
pascal@698
|
477 +#define VIRTIO_CONFIG_S_FAILED 0x80
|
pascal@698
|
478 +
|
pascal@698
|
479 +/* from Linux's linux/virtio_ring.h */
|
pascal@698
|
480 +
|
pascal@698
|
481 +/* This marks a buffer as continuing via the next field. */
|
pascal@698
|
482 +#define VRING_DESC_F_NEXT 1
|
pascal@698
|
483 +/* This marks a buffer as write-only (otherwise read-only). */
|
pascal@698
|
484 +#define VRING_DESC_F_WRITE 2
|
pascal@698
|
485 +
|
pascal@698
|
486 +/* This means don't notify other side when buffer added. */
|
pascal@698
|
487 +#define VRING_USED_F_NO_NOTIFY 1
|
pascal@698
|
488 +/* This means don't interrupt guest when buffer consumed. */
|
pascal@698
|
489 +#define VRING_AVAIL_F_NO_INTERRUPT 1
|
pascal@698
|
490 +
|
pascal@698
|
491 +typedef struct VirtQueue VirtQueue;
|
pascal@698
|
492 +typedef struct VirtIODevice VirtIODevice;
|
pascal@698
|
493 +
|
pascal@698
|
494 +typedef struct VRingDesc
|
pascal@698
|
495 +{
|
pascal@698
|
496 + uint64_t addr;
|
pascal@698
|
497 + uint32_t len;
|
pascal@698
|
498 + uint16_t flags;
|
pascal@698
|
499 + uint16_t next;
|
pascal@698
|
500 +} VRingDesc;
|
pascal@698
|
501 +
|
pascal@698
|
502 +typedef struct VRingAvail
|
pascal@698
|
503 +{
|
pascal@698
|
504 + uint16_t flags;
|
pascal@698
|
505 + uint16_t idx;
|
pascal@698
|
506 + uint16_t ring[0];
|
pascal@698
|
507 +} VRingAvail;
|
pascal@698
|
508 +
|
pascal@698
|
509 +typedef struct VRingUsedElem
|
pascal@698
|
510 +{
|
pascal@698
|
511 + uint32_t id;
|
pascal@698
|
512 + uint32_t len;
|
pascal@698
|
513 +} VRingUsedElem;
|
pascal@698
|
514 +
|
pascal@698
|
515 +typedef struct VRingUsed
|
pascal@698
|
516 +{
|
pascal@698
|
517 + uint16_t flags;
|
pascal@698
|
518 + uint16_t idx;
|
pascal@698
|
519 + VRingUsedElem ring[0];
|
pascal@698
|
520 +} VRingUsed;
|
pascal@698
|
521 +
|
pascal@698
|
522 +typedef struct VRing
|
pascal@698
|
523 +{
|
pascal@698
|
524 + unsigned int num;
|
pascal@698
|
525 + VRingDesc *desc;
|
pascal@698
|
526 + VRingAvail *avail;
|
pascal@698
|
527 + VRingUsed *used;
|
pascal@698
|
528 +} VRing;
|
pascal@698
|
529 +
|
pascal@698
|
530 +struct VirtQueue
|
pascal@698
|
531 +{
|
pascal@698
|
532 + VRing vring;
|
pascal@698
|
533 + uint32_t pfn;
|
pascal@698
|
534 + uint16_t last_avail_idx;
|
pascal@698
|
535 + void (*handle_output)(VirtIODevice *vdev, VirtQueue *vq);
|
pascal@698
|
536 + int index;
|
pascal@698
|
537 +};
|
pascal@698
|
538 +
|
pascal@698
|
539 +#define VIRTQUEUE_MAX_SIZE 1024
|
pascal@698
|
540 +
|
pascal@698
|
541 +typedef struct VirtQueueElement
|
pascal@698
|
542 +{
|
pascal@698
|
543 + unsigned int index;
|
pascal@698
|
544 + unsigned int out_num;
|
pascal@698
|
545 + unsigned int in_num;
|
pascal@698
|
546 + struct iovec in_sg[VIRTQUEUE_MAX_SIZE];
|
pascal@698
|
547 + struct iovec out_sg[VIRTQUEUE_MAX_SIZE];
|
pascal@698
|
548 +} VirtQueueElement;
|
pascal@698
|
549 +
|
pascal@698
|
550 +#define VIRTIO_PCI_QUEUE_MAX 16
|
pascal@698
|
551 +
|
pascal@698
|
552 +struct VirtIODevice
|
pascal@698
|
553 +{
|
pascal@698
|
554 + PCIDevice pci_dev;
|
pascal@698
|
555 + const char *name;
|
pascal@698
|
556 + uint32_t addr;
|
pascal@698
|
557 + uint16_t vendor;
|
pascal@698
|
558 + uint16_t device;
|
pascal@698
|
559 + uint8_t status;
|
pascal@698
|
560 + uint8_t isr;
|
pascal@698
|
561 + uint16_t queue_sel;
|
pascal@698
|
562 + uint32_t features;
|
pascal@698
|
563 + size_t config_len;
|
pascal@698
|
564 + void *config;
|
pascal@698
|
565 + uint32_t (*get_features)(VirtIODevice *vdev);
|
pascal@698
|
566 + void (*set_features)(VirtIODevice *vdev, uint32_t val);
|
pascal@698
|
567 + void (*update_config)(VirtIODevice *vdev, uint8_t *config);
|
pascal@698
|
568 + VirtQueue vq[VIRTIO_PCI_QUEUE_MAX];
|
pascal@698
|
569 +};
|
pascal@698
|
570 +
|
pascal@698
|
571 +VirtIODevice *virtio_init_pci(PCIBus *bus, const char *name,
|
pascal@698
|
572 + uint16_t vendor, uint16_t device,
|
pascal@698
|
573 + uint16_t subvendor, uint16_t subdevice,
|
pascal@698
|
574 + uint8_t class_code, uint8_t subclass_code,
|
pascal@698
|
575 + uint8_t pif, size_t config_size,
|
pascal@698
|
576 + size_t struct_size);
|
pascal@698
|
577 +
|
pascal@698
|
578 +VirtQueue *virtio_add_queue(VirtIODevice *vdev, int queue_size,
|
pascal@698
|
579 + void (*handle_output)(VirtIODevice *,
|
pascal@698
|
580 + VirtQueue *));
|
pascal@698
|
581 +
|
pascal@698
|
582 +void virtqueue_push(VirtQueue *vq, const VirtQueueElement *elem,
|
pascal@698
|
583 + unsigned int len);
|
pascal@698
|
584 +
|
pascal@698
|
585 +int virtqueue_pop(VirtQueue *vq, VirtQueueElement *elem);
|
pascal@698
|
586 +
|
pascal@698
|
587 +void virtio_notify(VirtIODevice *vdev, VirtQueue *vq);
|
pascal@698
|
588 +
|
pascal@698
|
589 +#endif
|
pascal@698
|
590 Index: qemu-0.9.1/Makefile.target
|
pascal@698
|
591 ===================================================================
|
pascal@698
|
592 --- qemu-0.9.1.orig/Makefile.target 2008-02-07 13:36:23.000000000 +0000
|
pascal@698
|
593 +++ qemu-0.9.1/Makefile.target 2008-02-07 13:36:37.000000000 +0000
|
pascal@698
|
594 @@ -437,7 +437,7 @@
|
pascal@698
|
595 VL_OBJS += rtl8139.o
|
pascal@698
|
596
|
pascal@698
|
597 # virtio devices
|
pascal@698
|
598 -VL_OBJS += virtio.o
|
pascal@698
|
599 +VL_OBJS += virtio.o virtio-net.o
|
pascal@698
|
600
|
pascal@698
|
601 ifeq ($(TARGET_BASE_ARCH), i386)
|
pascal@698
|
602 # Hardware support
|
pascal@698
|
603 Index: qemu-0.9.1/hw/pc.h
|
pascal@698
|
604 ===================================================================
|
pascal@698
|
605 --- qemu-0.9.1.orig/hw/pc.h 2008-01-06 19:38:42.000000000 +0000
|
pascal@698
|
606 +++ qemu-0.9.1/hw/pc.h 2008-02-07 13:36:37.000000000 +0000
|
pascal@698
|
607 @@ -142,4 +142,9 @@
|
pascal@698
|
608
|
pascal@698
|
609 void isa_ne2000_init(int base, qemu_irq irq, NICInfo *nd);
|
pascal@698
|
610
|
pascal@698
|
611 +/* virtio-net.c */
|
pascal@698
|
612 +
|
pascal@698
|
613 +void *virtio_net_init(PCIBus *bus, NICInfo *nd, int devfn);
|
pascal@698
|
614 +
|
pascal@698
|
615 +
|
pascal@698
|
616 #endif
|
pascal@698
|
617 Index: qemu-0.9.1/hw/pci.c
|
pascal@698
|
618 ===================================================================
|
pascal@698
|
619 --- qemu-0.9.1.orig/hw/pci.c 2008-01-06 19:38:42.000000000 +0000
|
pascal@698
|
620 +++ qemu-0.9.1/hw/pci.c 2008-02-07 13:36:37.000000000 +0000
|
pascal@698
|
621 @@ -25,6 +25,7 @@
|
pascal@698
|
622 #include "pci.h"
|
pascal@698
|
623 #include "console.h"
|
pascal@698
|
624 #include "net.h"
|
pascal@698
|
625 +#include "pc.h"
|
pascal@698
|
626
|
pascal@698
|
627 //#define DEBUG_PCI
|
pascal@698
|
628
|
pascal@698
|
629 @@ -638,9 +639,11 @@
|
pascal@698
|
630 pci_rtl8139_init(bus, nd, devfn);
|
pascal@698
|
631 } else if (strcmp(nd->model, "pcnet") == 0) {
|
pascal@698
|
632 pci_pcnet_init(bus, nd, devfn);
|
pascal@698
|
633 + } else if (strcmp(nd->model, "virtio") == 0) {
|
pascal@698
|
634 + virtio_net_init(bus, nd, devfn);
|
pascal@698
|
635 } else if (strcmp(nd->model, "?") == 0) {
|
pascal@698
|
636 fprintf(stderr, "qemu: Supported PCI NICs: i82551 i82557b i82559er"
|
pascal@698
|
637 - " ne2k_pci pcnet rtl8139\n");
|
pascal@698
|
638 + " ne2k_pci pcnet rtl8139 virtio\n");
|
pascal@698
|
639 exit (1);
|
pascal@698
|
640 } else {
|
pascal@698
|
641 fprintf(stderr, "qemu: Unsupported NIC: %s\n", nd->model);
|
pascal@698
|
642 Index: qemu-0.9.1/hw/virtio-net.c
|
pascal@698
|
643 ===================================================================
|
pascal@698
|
644 --- /dev/null 1970-01-01 00:00:00.000000000 +0000
|
pascal@698
|
645 +++ qemu-0.9.1/hw/virtio-net.c 2008-02-07 13:36:37.000000000 +0000
|
pascal@698
|
646 @@ -0,0 +1,178 @@
|
pascal@698
|
647 +/*
|
pascal@698
|
648 + * Virtio Network Device
|
pascal@698
|
649 + *
|
pascal@698
|
650 + * Copyright IBM, Corp. 2007
|
pascal@698
|
651 + *
|
pascal@698
|
652 + * Authors:
|
pascal@698
|
653 + * Anthony Liguori <address@hidden>
|
pascal@698
|
654 + *
|
pascal@698
|
655 + * This work is licensed under the terms of the GNU GPL, version 2. See
|
pascal@698
|
656 + * the COPYING file in the top-level directory.
|
pascal@698
|
657 + *
|
pascal@698
|
658 + */
|
pascal@698
|
659 +
|
pascal@698
|
660 +#include "virtio.h"
|
pascal@698
|
661 +#include "net.h"
|
pascal@698
|
662 +#include "pc.h"
|
pascal@698
|
663 +
|
pascal@698
|
664 +/* from Linux's virtio_net.h */
|
pascal@698
|
665 +
|
pascal@698
|
666 +/* The ID for virtio_net */
|
pascal@698
|
667 +#define VIRTIO_ID_NET 1
|
pascal@698
|
668 +
|
pascal@698
|
669 +/* The feature bitmap for virtio net */
|
pascal@698
|
670 +#define VIRTIO_NET_F_NO_CSUM 0
|
pascal@698
|
671 +#define VIRTIO_NET_F_TSO4 1
|
pascal@698
|
672 +#define VIRTIO_NET_F_UFO 2
|
pascal@698
|
673 +#define VIRTIO_NET_F_TSO4_ECN 3
|
pascal@698
|
674 +#define VIRTIO_NET_F_TSO6 4
|
pascal@698
|
675 +#define VIRTIO_NET_F_MAC 5
|
pascal@698
|
676 +
|
pascal@698
|
677 +/* The config defining mac address (6 bytes) */
|
pascal@698
|
678 +struct virtio_net_config
|
pascal@698
|
679 +{
|
pascal@698
|
680 + uint8_t mac[6];
|
pascal@698
|
681 +} __attribute__((packed));
|
pascal@698
|
682 +
|
pascal@698
|
683 +/* This is the first element of the scatter-gather list. If you don't
|
pascal@698
|
684 + * specify GSO or CSUM features, you can simply ignore the header. */
|
pascal@698
|
685 +struct virtio_net_hdr
|
pascal@698
|
686 +{
|
pascal@698
|
687 +#define VIRTIO_NET_HDR_F_NEEDS_CSUM 1 // Use csum_start, csum_offset
|
pascal@698
|
688 + uint8_t flags;
|
pascal@698
|
689 +#define VIRTIO_NET_HDR_GSO_NONE 0 // Not a GSO frame
|
pascal@698
|
690 +#define VIRTIO_NET_HDR_GSO_TCPV4 1 // GSO frame, IPv4 TCP (TSO)
|
pascal@698
|
691 +/* FIXME: Do we need this? If they said they can handle ECN, do they care? */
|
pascal@698
|
692 +#define VIRTIO_NET_HDR_GSO_TCPV4_ECN 2 // GSO frame, IPv4 TCP w/ ECN
|
pascal@698
|
693 +#define VIRTIO_NET_HDR_GSO_UDP 3 // GSO frame, IPv4 UDP (UFO)
|
pascal@698
|
694 +#define VIRTIO_NET_HDR_GSO_TCPV6 4 // GSO frame, IPv6 TCP
|
pascal@698
|
695 + uint8_t gso_type;
|
pascal@698
|
696 + uint16_t gso_size;
|
pascal@698
|
697 + uint16_t csum_start;
|
pascal@698
|
698 + uint16_t csum_offset;
|
pascal@698
|
699 +};
|
pascal@698
|
700 +
|
pascal@698
|
701 +typedef struct VirtIONet
|
pascal@698
|
702 +{
|
pascal@698
|
703 + VirtIODevice vdev;
|
pascal@698
|
704 + uint8_t mac[6];
|
pascal@698
|
705 + VirtQueue *rx_vq;
|
pascal@698
|
706 + VirtQueue *tx_vq;
|
pascal@698
|
707 + VLANClientState *vc;
|
pascal@698
|
708 + int can_receive;
|
pascal@698
|
709 +} VirtIONet;
|
pascal@698
|
710 +
|
pascal@698
|
711 +static VirtIONet *to_virtio_net(VirtIODevice *vdev)
|
pascal@698
|
712 +{
|
pascal@698
|
713 + return (VirtIONet *)vdev;
|
pascal@698
|
714 +}
|
pascal@698
|
715 +
|
pascal@698
|
716 +static void virtio_net_update_config(VirtIODevice *vdev, uint8_t *config)
|
pascal@698
|
717 +{
|
pascal@698
|
718 + VirtIONet *n = to_virtio_net(vdev);
|
pascal@698
|
719 + struct virtio_net_config netcfg;
|
pascal@698
|
720 +
|
pascal@698
|
721 + memcpy(netcfg.mac, n->mac, 6);
|
pascal@698
|
722 + memcpy(config, &netcfg, sizeof(netcfg));
|
pascal@698
|
723 +}
|
pascal@698
|
724 +
|
pascal@698
|
725 +static uint32_t virtio_net_get_features(VirtIODevice *vdev)
|
pascal@698
|
726 +{
|
pascal@698
|
727 + return (1 << VIRTIO_NET_F_MAC);
|
pascal@698
|
728 +}
|
pascal@698
|
729 +
|
pascal@698
|
730 +/* RX */
|
pascal@698
|
731 +
|
pascal@698
|
732 +static void virtio_net_handle_rx(VirtIODevice *vdev, VirtQueue *vq)
|
pascal@698
|
733 +{
|
pascal@698
|
734 + VirtIONet *n = to_virtio_net(vdev);
|
pascal@698
|
735 + n->can_receive = 1;
|
pascal@698
|
736 +}
|
pascal@698
|
737 +
|
pascal@698
|
738 +static int virtio_net_can_receive(void *opaque)
|
pascal@698
|
739 +{
|
pascal@698
|
740 + VirtIONet *n = opaque;
|
pascal@698
|
741 +
|
pascal@698
|
742 + return (n->vdev.status & VIRTIO_CONFIG_S_DRIVER_OK) && n->can_receive;
|
pascal@698
|
743 +}
|
pascal@698
|
744 +
|
pascal@698
|
745 +static void virtio_net_receive(void *opaque, const uint8_t *buf, int size)
|
pascal@698
|
746 +{
|
pascal@698
|
747 + VirtIONet *n = opaque;
|
pascal@698
|
748 + VirtQueueElement elem;
|
pascal@698
|
749 + struct virtio_net_hdr *hdr;
|
pascal@698
|
750 + int offset, i;
|
pascal@698
|
751 +
|
pascal@698
|
752 + /* FIXME: the drivers really need to set their status better */
|
pascal@698
|
753 + if (n->rx_vq->vring.avail == NULL) {
|
pascal@698
|
754 + n->can_receive = 0;
|
pascal@698
|
755 + return;
|
pascal@698
|
756 + }
|
pascal@698
|
757 +
|
pascal@698
|
758 + if (virtqueue_pop(n->rx_vq, &elem) == 0) {
|
pascal@698
|
759 + /* wait until the guest adds some rx bufs */
|
pascal@698
|
760 + n->can_receive = 0;
|
pascal@698
|
761 + return;
|
pascal@698
|
762 + }
|
pascal@698
|
763 +
|
pascal@698
|
764 + hdr = (void *)elem.in_sg[0].iov_base;
|
pascal@698
|
765 + hdr->flags = 0;
|
pascal@698
|
766 + hdr->gso_type = VIRTIO_NET_HDR_GSO_NONE;
|
pascal@698
|
767 +
|
pascal@698
|
768 + /* copy in packet. ugh */
|
pascal@698
|
769 + offset = 0;
|
pascal@698
|
770 + i = 1;
|
pascal@698
|
771 + while (offset < size && i < elem.in_num) {
|
pascal@698
|
772 + int len = MIN(elem.in_sg[i].iov_len, size - offset);
|
pascal@698
|
773 + memcpy(elem.in_sg[i].iov_base, buf + offset, len);
|
pascal@698
|
774 + offset += len;
|
pascal@698
|
775 + i++;
|
pascal@698
|
776 + }
|
pascal@698
|
777 +
|
pascal@698
|
778 + /* signal other side */
|
pascal@698
|
779 + virtqueue_push(n->rx_vq, &elem, sizeof(*hdr) + offset);
|
pascal@698
|
780 + virtio_notify(&n->vdev, n->rx_vq);
|
pascal@698
|
781 +}
|
pascal@698
|
782 +
|
pascal@698
|
783 +/* TX */
|
pascal@698
|
784 +static void virtio_net_handle_tx(VirtIODevice *vdev, VirtQueue *vq)
|
pascal@698
|
785 +{
|
pascal@698
|
786 + VirtIONet *n = to_virtio_net(vdev);
|
pascal@698
|
787 + VirtQueueElement elem;
|
pascal@698
|
788 +
|
pascal@698
|
789 + while (virtqueue_pop(vq, &elem)) {
|
pascal@698
|
790 + int i;
|
pascal@698
|
791 + size_t len = 0;
|
pascal@698
|
792 +
|
pascal@698
|
793 + /* ignore the header for now */
|
pascal@698
|
794 + for (i = 1; i < elem.out_num; i++) {
|
pascal@698
|
795 + qemu_send_packet(n->vc, elem.out_sg[i].iov_base,
|
pascal@698
|
796 + elem.out_sg[i].iov_len);
|
pascal@698
|
797 + len += elem.out_sg[i].iov_len;
|
pascal@698
|
798 + }
|
pascal@698
|
799 +
|
pascal@698
|
800 + virtqueue_push(vq, &elem, sizeof(struct virtio_net_hdr) + len);
|
pascal@698
|
801 + virtio_notify(&n->vdev, vq);
|
pascal@698
|
802 + }
|
pascal@698
|
803 +}
|
pascal@698
|
804 +
|
pascal@698
|
805 +void *virtio_net_init(PCIBus *bus, NICInfo *nd, int devfn)
|
pascal@698
|
806 +{
|
pascal@698
|
807 + VirtIONet *n;
|
pascal@698
|
808 +
|
pascal@698
|
809 + n = (VirtIONet *)virtio_init_pci(bus, "virtio-net", 6900, 0x1000,
|
pascal@698
|
810 + 0, VIRTIO_ID_NET,
|
pascal@698
|
811 + 0x02, 0x00, 0x00,
|
pascal@698
|
812 + 6, sizeof(VirtIONet));
|
pascal@698
|
813 +
|
pascal@698
|
814 + n->vdev.update_config = virtio_net_update_config;
|
pascal@698
|
815 + n->vdev.get_features = virtio_net_get_features;
|
pascal@698
|
816 + n->rx_vq = virtio_add_queue(&n->vdev, 512, virtio_net_handle_rx);
|
pascal@698
|
817 + n->tx_vq = virtio_add_queue(&n->vdev, 128, virtio_net_handle_tx);
|
pascal@698
|
818 + n->can_receive = 0;
|
pascal@698
|
819 + memcpy(n->mac, nd->macaddr, 6);
|
pascal@698
|
820 + n->vc = qemu_new_vlan_client(nd->vlan, virtio_net_receive,
|
pascal@698
|
821 + virtio_net_can_receive, n);
|
pascal@698
|
822 +
|
pascal@698
|
823 + return &n->vdev;
|
pascal@698
|
824 +}
|
pascal@698
|
825 Index: qemu-0.9.1/Makefile.target
|
pascal@698
|
826 ===================================================================
|
pascal@698
|
827 --- qemu-0.9.1.orig/Makefile.target 2008-02-07 13:36:37.000000000 +0000
|
pascal@698
|
828 +++ qemu-0.9.1/Makefile.target 2008-02-07 13:38:53.000000000 +0000
|
pascal@698
|
829 @@ -437,7 +437,7 @@
|
pascal@698
|
830 VL_OBJS += rtl8139.o
|
pascal@698
|
831
|
pascal@698
|
832 # virtio devices
|
pascal@698
|
833 -VL_OBJS += virtio.o virtio-net.o
|
pascal@698
|
834 +VL_OBJS += virtio.o virtio-net.o virtio-blk.o
|
pascal@698
|
835
|
pascal@698
|
836 ifeq ($(TARGET_BASE_ARCH), i386)
|
pascal@698
|
837 # Hardware support
|
pascal@698
|
838 Index: qemu-0.9.1/hw/pc.c
|
pascal@698
|
839 ===================================================================
|
pascal@698
|
840 --- qemu-0.9.1.orig/hw/pc.c 2008-01-06 19:38:42.000000000 +0000
|
pascal@698
|
841 +++ qemu-0.9.1/hw/pc.c 2008-02-07 13:38:53.000000000 +0000
|
pascal@698
|
842 @@ -1008,6 +1008,18 @@
|
pascal@698
|
843 }
|
pascal@698
|
844 }
|
pascal@698
|
845 }
|
pascal@698
|
846 +
|
pascal@698
|
847 + /* Add virtio block devices */
|
pascal@698
|
848 + if (pci_enabled) {
|
pascal@698
|
849 + int index;
|
pascal@698
|
850 + int unit_id = 0;
|
pascal@698
|
851 +
|
pascal@698
|
852 + while ((index = drive_get_index(IF_VIRTIO, 0, unit_id)) != -1) {
|
pascal@698
|
853 + virtio_blk_init(pci_bus, 0x5002, 0x2258,
|
pascal@698
|
854 + drives_table[index].bdrv);
|
pascal@698
|
855 + unit_id++;
|
pascal@698
|
856 + }
|
pascal@698
|
857 + }
|
pascal@698
|
858 }
|
pascal@698
|
859
|
pascal@698
|
860 static void pc_init_pci(int ram_size, int vga_ram_size,
|
pascal@698
|
861 Index: qemu-0.9.1/hw/pc.h
|
pascal@698
|
862 ===================================================================
|
pascal@698
|
863 --- qemu-0.9.1.orig/hw/pc.h 2008-02-07 13:36:37.000000000 +0000
|
pascal@698
|
864 +++ qemu-0.9.1/hw/pc.h 2008-02-07 13:38:53.000000000 +0000
|
pascal@698
|
865 @@ -147,4 +147,8 @@
|
pascal@698
|
866 void *virtio_net_init(PCIBus *bus, NICInfo *nd, int devfn);
|
pascal@698
|
867
|
pascal@698
|
868
|
pascal@698
|
869 +/* virtio-blk.h */
|
pascal@698
|
870 +void *virtio_blk_init(PCIBus *bus, uint16_t vendor, uint16_t device,
|
pascal@698
|
871 + BlockDriverState *bs);
|
pascal@698
|
872 +
|
pascal@698
|
873 #endif
|
pascal@698
|
874 Index: qemu-0.9.1/hw/virtio-blk.c
|
pascal@698
|
875 ===================================================================
|
pascal@698
|
876 --- /dev/null 1970-01-01 00:00:00.000000000 +0000
|
pascal@698
|
877 +++ qemu-0.9.1/hw/virtio-blk.c 2008-02-07 13:38:53.000000000 +0000
|
pascal@698
|
878 @@ -0,0 +1,163 @@
|
pascal@698
|
879 +/*
|
pascal@698
|
880 + * Virtio Block Device
|
pascal@698
|
881 + *
|
pascal@698
|
882 + * Copyright IBM, Corp. 2007
|
pascal@698
|
883 + *
|
pascal@698
|
884 + * Authors:
|
pascal@698
|
885 + * Anthony Liguori <address@hidden>
|
pascal@698
|
886 + *
|
pascal@698
|
887 + * This work is licensed under the terms of the GNU GPL, version 2. See
|
pascal@698
|
888 + * the COPYING file in the top-level directory.
|
pascal@698
|
889 + *
|
pascal@698
|
890 + */
|
pascal@698
|
891 +
|
pascal@698
|
892 +#include "virtio.h"
|
pascal@698
|
893 +#include "block.h"
|
pascal@698
|
894 +#include "pc.h"
|
pascal@698
|
895 +
|
pascal@698
|
896 +/* from Linux's linux/virtio_blk.h */
|
pascal@698
|
897 +
|
pascal@698
|
898 +/* The ID for virtio_block */
|
pascal@698
|
899 +#define VIRTIO_ID_BLOCK 2
|
pascal@698
|
900 +
|
pascal@698
|
901 +/* Feature bits */
|
pascal@698
|
902 +#define VIRTIO_BLK_F_BARRIER 0 /* Does host support barriers? */
|
pascal@698
|
903 +#define VIRTIO_BLK_F_SIZE_MAX 1 /* Indicates maximum segment size */
|
pascal@698
|
904 +#define VIRTIO_BLK_F_SEG_MAX 2 /* Indicates maximum # of segments */
|
pascal@698
|
905 +
|
pascal@698
|
906 +struct virtio_blk_config
|
pascal@698
|
907 +{
|
pascal@698
|
908 + uint64_t capacity;
|
pascal@698
|
909 + uint32_t size_max;
|
pascal@698
|
910 + uint32_t seg_max;
|
pascal@698
|
911 +};
|
pascal@698
|
912 +
|
pascal@698
|
913 +/* These two define direction. */
|
pascal@698
|
914 +#define VIRTIO_BLK_T_IN 0
|
pascal@698
|
915 +#define VIRTIO_BLK_T_OUT 1
|
pascal@698
|
916 +
|
pascal@698
|
917 +/* This bit says it's a scsi command, not an actual read or write. */
|
pascal@698
|
918 +#define VIRTIO_BLK_T_SCSI_CMD 2
|
pascal@698
|
919 +
|
pascal@698
|
920 +/* Barrier before this op. */
|
pascal@698
|
921 +#define VIRTIO_BLK_T_BARRIER 0x80000000
|
pascal@698
|
922 +
|
pascal@698
|
923 +/* This is the first element of the read scatter-gather list. */
|
pascal@698
|
924 +struct virtio_blk_outhdr
|
pascal@698
|
925 +{
|
pascal@698
|
926 + /* VIRTIO_BLK_T* */
|
pascal@698
|
927 + uint32_t type;
|
pascal@698
|
928 + /* io priority. */
|
pascal@698
|
929 + uint32_t ioprio;
|
pascal@698
|
930 + /* Sector (ie. 512 byte offset) */
|
pascal@698
|
931 + uint64_t sector;
|
pascal@698
|
932 + /* Where to put reply. */
|
pascal@698
|
933 + uint64_t id;
|
pascal@698
|
934 +};
|
pascal@698
|
935 +
|
pascal@698
|
936 +#define VIRTIO_BLK_S_OK 0
|
pascal@698
|
937 +#define VIRTIO_BLK_S_IOERR 1
|
pascal@698
|
938 +#define VIRTIO_BLK_S_UNSUPP 2
|
pascal@698
|
939 +
|
pascal@698
|
940 +/* This is the first element of the write scatter-gather list */
|
pascal@698
|
941 +struct virtio_blk_inhdr
|
pascal@698
|
942 +{
|
pascal@698
|
943 + unsigned char status;
|
pascal@698
|
944 +};
|
pascal@698
|
945 +
|
pascal@698
|
946 +typedef struct VirtIOBlock
|
pascal@698
|
947 +{
|
pascal@698
|
948 + VirtIODevice vdev;
|
pascal@698
|
949 + BlockDriverState *bs;
|
pascal@698
|
950 +} VirtIOBlock;
|
pascal@698
|
951 +
|
pascal@698
|
952 +static VirtIOBlock *to_virtio_blk(VirtIODevice *vdev)
|
pascal@698
|
953 +{
|
pascal@698
|
954 + return (VirtIOBlock *)vdev;
|
pascal@698
|
955 +}
|
pascal@698
|
956 +
|
pascal@698
|
957 +static void virtio_blk_handle_output(VirtIODevice *vdev, VirtQueue *vq)
|
pascal@698
|
958 +{
|
pascal@698
|
959 + VirtIOBlock *s = to_virtio_blk(vdev);
|
pascal@698
|
960 + VirtQueueElement elem;
|
pascal@698
|
961 + unsigned int count;
|
pascal@698
|
962 +
|
pascal@698
|
963 + while ((count = virtqueue_pop(vq, &elem)) != 0) {
|
pascal@698
|
964 + struct virtio_blk_inhdr *in;
|
pascal@698
|
965 + struct virtio_blk_outhdr *out;
|
pascal@698
|
966 + unsigned int wlen;
|
pascal@698
|
967 + off_t off;
|
pascal@698
|
968 + int i;
|
pascal@698
|
969 +
|
pascal@698
|
970 + out = (void *)elem.out_sg[0].iov_base;
|
pascal@698
|
971 + in = (void *)elem.in_sg[elem.in_num - 1].iov_base;
|
pascal@698
|
972 + off = out->sector;
|
pascal@698
|
973 +
|
pascal@698
|
974 + if (out->type & VIRTIO_BLK_T_SCSI_CMD) {
|
pascal@698
|
975 + wlen = sizeof(*in);
|
pascal@698
|
976 + in->status = VIRTIO_BLK_S_UNSUPP;
|
pascal@698
|
977 + } else if (out->type & VIRTIO_BLK_T_OUT) {
|
pascal@698
|
978 + wlen = sizeof(*in);
|
pascal@698
|
979 +
|
pascal@698
|
980 + for (i = 1; i < elem.out_num; i++) {
|
pascal@698
|
981 + bdrv_write(s->bs, off,
|
pascal@698
|
982 + elem.out_sg[i].iov_base,
|
pascal@698
|
983 + elem.out_sg[i].iov_len / 512);
|
pascal@698
|
984 + off += elem.out_sg[i].iov_len / 512;
|
pascal@698
|
985 + }
|
pascal@698
|
986 +
|
pascal@698
|
987 + in->status = VIRTIO_BLK_S_OK;
|
pascal@698
|
988 + } else {
|
pascal@698
|
989 + wlen = sizeof(*in);
|
pascal@698
|
990 +
|
pascal@698
|
991 + for (i = 0; i < elem.in_num - 1; i++) {
|
pascal@698
|
992 + bdrv_read(s->bs, off,
|
pascal@698
|
993 + elem.in_sg[i].iov_base,
|
pascal@698
|
994 + elem.in_sg[i].iov_len / 512);
|
pascal@698
|
995 + off += elem.in_sg[i].iov_len / 512;
|
pascal@698
|
996 + wlen += elem.in_sg[i].iov_len;
|
pascal@698
|
997 + }
|
pascal@698
|
998 +
|
pascal@698
|
999 + in->status = VIRTIO_BLK_S_OK;
|
pascal@698
|
1000 + }
|
pascal@698
|
1001 +
|
pascal@698
|
1002 + virtqueue_push(vq, &elem, wlen);
|
pascal@698
|
1003 + virtio_notify(vdev, vq);
|
pascal@698
|
1004 + }
|
pascal@698
|
1005 +}
|
pascal@698
|
1006 +
|
pascal@698
|
1007 +static void virtio_blk_update_config(VirtIODevice *vdev, uint8_t *config)
|
pascal@698
|
1008 +{
|
pascal@698
|
1009 + VirtIOBlock *s = to_virtio_blk(vdev);
|
pascal@698
|
1010 + struct virtio_blk_config blkcfg;
|
pascal@698
|
1011 + int64_t capacity;
|
pascal@698
|
1012 +
|
pascal@698
|
1013 + bdrv_get_geometry(s->bs, &capacity);
|
pascal@698
|
1014 + blkcfg.capacity = capacity;
|
pascal@698
|
1015 + blkcfg.seg_max = 128 - 2;
|
pascal@698
|
1016 + memcpy(config, &blkcfg, sizeof(blkcfg));
|
pascal@698
|
1017 +}
|
pascal@698
|
1018 +
|
pascal@698
|
1019 +static uint32_t virtio_blk_get_features(VirtIODevice *vdev)
|
pascal@698
|
1020 +{
|
pascal@698
|
1021 + return (1 << VIRTIO_BLK_F_SEG_MAX);
|
pascal@698
|
1022 +}
|
pascal@698
|
1023 +
|
pascal@698
|
1024 +void *virtio_blk_init(PCIBus *bus, uint16_t vendor, uint16_t device,
|
pascal@698
|
1025 + BlockDriverState *bs)
|
pascal@698
|
1026 +{
|
pascal@698
|
1027 + VirtIOBlock *s;
|
pascal@698
|
1028 +
|
pascal@698
|
1029 + s = (VirtIOBlock *)virtio_init_pci(bus, "virtio-blk", 6900, 0x1001,
|
pascal@698
|
1030 + 0, VIRTIO_ID_BLOCK,
|
pascal@698
|
1031 + 0x01, 0x80, 0x00,
|
pascal@698
|
1032 + 16, sizeof(VirtIOBlock));
|
pascal@698
|
1033 +
|
pascal@698
|
1034 + s->vdev.update_config = virtio_blk_update_config;
|
pascal@698
|
1035 + s->vdev.get_features = virtio_blk_get_features;
|
pascal@698
|
1036 + s->bs = bs;
|
pascal@698
|
1037 +
|
pascal@698
|
1038 + virtio_add_queue(&s->vdev, 128, virtio_blk_handle_output);
|
pascal@698
|
1039 +
|
pascal@698
|
1040 + return &s->vdev;
|
pascal@698
|
1041 +}
|
pascal@698
|
1042 Index: qemu-0.9.1/sysemu.h
|
pascal@698
|
1043 ===================================================================
|
pascal@698
|
1044 --- qemu-0.9.1.orig/sysemu.h 2008-01-06 19:38:42.000000000 +0000
|
pascal@698
|
1045 +++ qemu-0.9.1/sysemu.h 2008-02-07 13:38:53.000000000 +0000
|
pascal@698
|
1046 @@ -117,7 +117,7 @@
|
pascal@698
|
1047 #endif
|
pascal@698
|
1048
|
pascal@698
|
1049 typedef enum {
|
pascal@698
|
1050 - IF_IDE, IF_SCSI, IF_FLOPPY, IF_PFLASH, IF_MTD, IF_SD
|
pascal@698
|
1051 + IF_IDE, IF_SCSI, IF_FLOPPY, IF_PFLASH, IF_MTD, IF_SD, IF_VIRTIO
|
pascal@698
|
1052 } BlockInterfaceType;
|
pascal@698
|
1053
|
pascal@698
|
1054 typedef struct DriveInfo {
|
pascal@698
|
1055 Index: qemu-0.9.1/vl.c
|
pascal@698
|
1056 ===================================================================
|
pascal@698
|
1057 --- qemu-0.9.1.orig/vl.c 2008-01-06 19:38:42.000000000 +0000
|
pascal@698
|
1058 +++ qemu-0.9.1/vl.c 2008-02-07 13:40:52.000000000 +0000
|
pascal@698
|
1059 @@ -4953,6 +4953,9 @@
|
pascal@698
|
1060 } else if (!strcmp(buf, "sd")) {
|
pascal@698
|
1061 type = IF_SD;
|
pascal@698
|
1062 max_devs = 0;
|
pascal@698
|
1063 + } else if (!strcmp(buf, "virtio")) {
|
pascal@698
|
1064 + type = IF_VIRTIO;
|
pascal@698
|
1065 + max_devs = 0;
|
pascal@698
|
1066 } else {
|
pascal@698
|
1067 fprintf(stderr, "qemu: '%s' unsupported bus type '%s'\n", str, buf);
|
pascal@698
|
1068 return -1;
|
pascal@698
|
1069 @@ -5141,6 +5144,7 @@
|
pascal@698
|
1070 break;
|
pascal@698
|
1071 case IF_PFLASH:
|
pascal@698
|
1072 case IF_MTD:
|
pascal@698
|
1073 + case IF_VIRTIO:
|
pascal@698
|
1074 break;
|
pascal@698
|
1075 }
|
pascal@698
|
1076 if (!file[0])
|