summaryrefslogtreecommitdiff
path: root/tools/lguest/lguest.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/lguest/lguest.c')
-rw-r--r--tools/lguest/lguest.c140
1 files changed, 131 insertions, 9 deletions
diff --git a/tools/lguest/lguest.c b/tools/lguest/lguest.c
index b00263f5febb..10a72b810127 100644
--- a/tools/lguest/lguest.c
+++ b/tools/lguest/lguest.c
@@ -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,