diff options
-rw-r--r-- | tools/lguest/lguest.c | 140 |
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, |