tools/lguest: insert device references from the 1.0 spec (4.1 Virtio Over PCI)

There are some (optional) parts we don't implement, but this quotes all
the device requirements from the spec (csd 03, but it should be the same
across all released versions).

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
Rusty Russell 2015-02-13 17:13:41 +10:30
parent b2ce1ea442
commit 8dc425ffdd
1 changed files with 131 additions and 9 deletions

View 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,