wok annotate qemu/stuff/virtio.u @ rev 893

tazlito: include tazlitobox in the package
author Christophe Lincoln <pankso@slitaz.org>
date Tue Jun 10 01:24:16 2008 +0200 (2008-06-10)
parents
children
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])