tools/lguest: insert device references from the 1.0 spec (4.1 Virtio Over PCI)
[linux-2.6-block.git] / tools / lguest / lguest.c
index b00263f5febbbfbc223182821e1b8518ef50eb08..10a72b810127271bde28921059a6228ca41ec390 100644 (file)
@@ -673,7 +673,13 @@ static void trigger_irq(struct virtqueue *vq)
                return;
        }
 
-       /* Set isr to 1 (queue interrupt pending) */
+       /*
+        * 4.1.4.5.1:
+        *
+        *  If MSI-X capability is disabled, the device MUST set the Queue
+        *  Interrupt bit in ISR status before sending a virtqueue notification
+        *  to the driver.
+        */
        vq->dev->mmio->isr = 0x1;
 
        /* Send the Guest an interrupt tell them we used something up. */
@@ -1304,11 +1310,19 @@ static bool pci_data_iowrite(u16 port, u32 mask, u32 val)
        } else if (&d->config_words[reg] == &d->config.cfg_access.pci_cfg_data) {
                u32 write_mask;
 
+               /*
+                * 4.1.4.7.1:
+                *
+                *  Upon detecting driver write access to pci_cfg_data, the
+                *  device MUST execute a write access at offset cap.offset at
+                *  BAR selected by cap.bar using the first cap.length bytes
+                *  from pci_cfg_data.
+                */
+
                /* Must be bar 0 */
                if (!valid_bar_access(d, &d->config.cfg_access))
                        return false;
 
-               /* First copy what they wrote into the window */
                iowrite(portoff, val, mask, &d->config.cfg_access.pci_cfg_data);
 
                /*
@@ -1346,6 +1360,14 @@ static void pci_data_ioread(u16 port, u32 mask, u32 *val)
        if (&d->config_words[reg] == &d->config.cfg_access.pci_cfg_data) {
                u32 read_mask;
 
+               /*
+                * 4.1.4.7.1:
+                *
+                *  Upon detecting driver read access to pci_cfg_data, the
+                *  device MUST execute a read access of length cap.length at
+                *  offset cap.offset at BAR selected by cap.bar and store the
+                *  first cap.length bytes in pci_cfg_data.
+                */
                /* Must be bar 0 */
                if (!valid_bar_access(d, &d->config.cfg_access))
                        errx(1, "Invalid cfg_access to bar%u, offset %u len %u",
@@ -1704,6 +1726,13 @@ static void emulate_mmio_write(struct device *d, u32 off, u32 val, u32 mask)
 
        switch (off) {
        case offsetof(struct virtio_pci_mmio, cfg.device_feature_select):
+               /*
+                * 4.1.4.3.1:
+                *
+                * The device MUST present the feature bits it is offering in
+                * device_feature, starting at bit device_feature_select ∗ 32
+                * for any device_feature_select written by the driver
+                */
                if (val == 0)
                        d->mmio->cfg.device_feature = d->features;
                else if (val == 1)
@@ -1731,12 +1760,23 @@ static void emulate_mmio_write(struct device *d, u32 off, u32 val, u32 mask)
                goto write_through32;
        case offsetof(struct virtio_pci_mmio, cfg.device_status):
                verbose("%s: device status -> %#x\n", d->name, val);
+               /*
+                * 4.1.4.3.1:
+                * 
+                *  The device MUST reset when 0 is written to device_status,
+                *  and present a 0 in device_status once that is done.
+                */
                if (val == 0)
                        reset_device(d);
                goto write_through8;
        case offsetof(struct virtio_pci_mmio, cfg.queue_select):
                vq = vq_by_num(d, val);
-               /* Out of range?  Return size 0 */
+               /*
+                * 4.1.4.3.1:
+                *
+                *  The device MUST present a 0 in queue_size if the virtqueue
+                *  corresponding to the current queue_select is unavailable.
+                */
                if (!vq) {
                        d->mmio->cfg.queue_size = 0;
                        goto write_through16;
@@ -1841,6 +1881,17 @@ static u32 emulate_mmio_read(struct device *d, u32 off, u32 mask)
                goto read_through16;
        case offsetof(struct virtio_pci_mmio, cfg.device_status):
        case offsetof(struct virtio_pci_mmio, cfg.config_generation):
+               /*
+                * 4.1.4.3.1:
+                *
+                *  The device MUST present a changed config_generation after
+                *  the driver has read a device-specific configuration value
+                *  which has changed since any part of the device-specific
+                *  configuration was last read.
+                *
+                * This is simple: none of our devices change config, so this
+                * is always 0.
+                */
                goto read_through8;
        case offsetof(struct virtio_pci_mmio, notify):
                goto read_through16;
@@ -1848,8 +1899,12 @@ static u32 emulate_mmio_read(struct device *d, u32 off, u32 mask)
                if (mask != 0xFF)
                        errx(1, "%s: non-8-bit read from offset %u (%#x)",
                             d->name, off, getreg(eip));
-               /* Read resets the isr */
                isr = d->mmio->isr;
+               /*
+                * 4.1.4.5.1:
+                *
+                *  The device MUST reset ISR status to 0 on driver read. 
+                */
                d->mmio->isr = 0;
                return isr;
        case offsetof(struct virtio_pci_mmio, padding):
@@ -2008,10 +2063,25 @@ static void set_device_config(struct device *dev, const void *conf, size_t len)
        dev->mmio = realloc(dev->mmio, dev->mmio_size);
        memcpy(dev->mmio + 1, conf, len);
 
+       /*
+        * 4.1.4.6:
+        *
+        *  The device MUST present at least one VIRTIO_PCI_CAP_DEVICE_CFG
+        *  capability for any device type which has a device-specific
+        *  configuration.
+        */
        /* Hook up device cfg */
        dev->config.cfg_access.cap.cap_next
                = offsetof(struct pci_config, device);
 
+       /*
+        * 4.1.4.6.1:
+        *
+        *  The offset for the device-specific configuration MUST be 4-byte
+        *  aligned.
+        */
+       assert(dev->config.cfg_access.cap.cap_next % 4 == 0);
+
        /* Fix up device cfg field length. */
        dev->config.device.length = len;
 
@@ -2041,7 +2111,12 @@ static void init_pci_config(struct pci_config *pci, u16 type,
 {
        size_t bar_offset, bar_len;
 
-       /* Save typing: most thing are happy being zero. */
+       /*
+        * 4.1.4.4.1:
+        *
+        *  The device MUST either present notify_off_multiplier as an even
+        *  power of 2, or present notify_off_multiplier as 0.
+        */
        memset(pci, 0, sizeof(*pci));
 
        /* 4.1.2.1: Devices MUST have the PCI Vendor ID 0x1AF4 */
@@ -2058,14 +2133,18 @@ static void init_pci_config(struct pci_config *pci, u16 type,
        pci->subclass = subclass;
 
        /*
-        * 4.1.2.1 Non-transitional devices SHOULD have a PCI Revision
-        * ID of 1 or higher
+        * 4.1.2.1:
+        *
+        *  Non-transitional devices SHOULD have a PCI Revision ID of 1 or
+        *  higher
         */
        pci->revid = 1;
 
        /*
-        * 4.1.2.1 Non-transitional devices SHOULD have a PCI
-        * Subsystem Device ID of 0x40 or higher.
+        * 4.1.2.1:
+        *
+        *  Non-transitional devices SHOULD have a PCI Subsystem Device ID of
+        *  0x40 or higher.
         */
        pci->subsystem_device_id = 0x40;
 
@@ -2077,17 +2156,48 @@ static void init_pci_config(struct pci_config *pci, u16 type,
        pci->status = (1 << 4);
 
        /* Link them in. */
+       /*
+        * 4.1.4.3.1:
+        *
+        *  The device MUST present at least one common configuration
+        *  capability.
+        */
        pci->capabilities = offsetof(struct pci_config, common);
 
+       /* 4.1.4.3.1 ... offset MUST be 4-byte aligned. */
+       assert(pci->capabilities % 4 == 0);
+
        bar_offset = offsetof(struct virtio_pci_mmio, cfg);
        bar_len = sizeof(((struct virtio_pci_mmio *)0)->cfg);
        init_cap(&pci->common, sizeof(pci->common), VIRTIO_PCI_CAP_COMMON_CFG,
                 bar_offset, bar_len,
                 offsetof(struct pci_config, notify));
 
+       /*
+        * 4.1.4.4.1:
+        *
+        *  The device MUST present at least one notification capability.
+        */
        bar_offset += bar_len;
        bar_len = sizeof(((struct virtio_pci_mmio *)0)->notify);
+
+       /*
+        * 4.1.4.4.1:
+        *
+        *  The cap.offset MUST be 2-byte aligned.
+        */
+       assert(pci->common.cap_next % 2 == 0);
+
        /* FIXME: Use a non-zero notify_off, for per-queue notification? */
+       /*
+        * 4.1.4.4.1:
+        *
+        *  The value cap.length presented by the device MUST be at least 2 and
+        *  MUST be large enough to support queue notification offsets for all
+        *  supported queues in all possible configurations.
+        */
+       assert(bar_len >= 2);
+
        init_cap(&pci->notify.cap, sizeof(pci->notify),
                 VIRTIO_PCI_CAP_NOTIFY_CFG,
                 bar_offset, bar_len,
@@ -2095,11 +2205,23 @@ static void init_pci_config(struct pci_config *pci, u16 type,
 
        bar_offset += bar_len;
        bar_len = sizeof(((struct virtio_pci_mmio *)0)->isr);
+       /*
+        * 4.1.4.5.1:
+        *
+        *  The device MUST present at least one VIRTIO_PCI_CAP_ISR_CFG
+        *  capability.
+        */
        init_cap(&pci->isr, sizeof(pci->isr),
                 VIRTIO_PCI_CAP_ISR_CFG,
                 bar_offset, bar_len,
                 offsetof(struct pci_config, cfg_access));
 
+       /*
+        * 4.1.4.7.1:
+        *
+        * The device MUST present at least one VIRTIO_PCI_CAP_PCI_CFG
+        * capability.
+        */
        /* This doesn't have any presence in the BAR */
        init_cap(&pci->cfg_access.cap, sizeof(pci->cfg_access),
                 VIRTIO_PCI_CAP_PCI_CFG,