On Mon, Apr 11, 2016 at 11:26:58AM +0200, Ladi Prosek wrote: > This commit adds support for driving virtio 1.0 PCI devices. > In addition to various helpers, a number of vpm_ functions are > introduced to be used instead of their legacy vp_ counterparts > when accessing virtio 1.0 (aka modern) devices. > > Signed-off-by: Ladi Prosek <lpro...@redhat.com> > --- > src/drivers/bus/virtio-pci.c | 353 > ++++++++++++++++++++++++++++++++++++++++- > src/drivers/bus/virtio-ring.c | 17 +- > src/drivers/net/virtio-net.c | 5 +- > src/include/ipxe/errfile.h | 1 + > src/include/ipxe/virtio-pci.h | 147 +++++++++++++++++ > src/include/ipxe/virtio-ring.h | 6 +- > 6 files changed, 517 insertions(+), 12 deletions(-) > > diff --git a/src/drivers/bus/virtio-pci.c b/src/drivers/bus/virtio-pci.c > index fbef067..d25db65 100644 > --- a/src/drivers/bus/virtio-pci.c > +++ b/src/drivers/bus/virtio-pci.c > @@ -11,10 +11,15 @@ > * > */ > > +#include "errno.h" > +#include "byteswap.h" > #include "etherboot.h" > #include "ipxe/io.h" > -#include "ipxe/virtio-ring.h" > +#include "ipxe/iomap.h" > +#include "ipxe/pci.h" > +#include "ipxe/reboot.h" > #include "ipxe/virtio-pci.h" > +#include "ipxe/virtio-ring.h" > > int vp_find_vq(unsigned int ioaddr, int queue_index, > struct vring_virtqueue *vq) > @@ -30,19 +35,19 @@ int vp_find_vq(unsigned int ioaddr, int queue_index, > > num = inw(ioaddr + VIRTIO_PCI_QUEUE_NUM); > if (!num) { > - printf("ERROR: queue size is 0\n"); > + DBG("VIRTIO-PCI ERROR: queue size is 0\n"); > return -1; > } > > if (num > MAX_QUEUE_NUM) { > - printf("ERROR: queue size %d > %d\n", num, MAX_QUEUE_NUM); > + DBG("VIRTIO-PCI ERROR: queue size %d > %d\n", num, MAX_QUEUE_NUM); > return -1; > } > > /* check if the queue is already active */ > > if (inl(ioaddr + VIRTIO_PCI_QUEUE_PFN)) { > - printf("ERROR: queue already active\n"); > + DBG("VIRTIO-PCI ERROR: queue already active\n"); > return -1; > } > > @@ -62,3 +67,343 @@ int vp_find_vq(unsigned int ioaddr, int queue_index, > > return num; > } > + > +#define CFG_POS(vdev, field) \ > + (vdev->cfg_cap_pos + offsetof(struct virtio_pci_cfg_cap, field)) > + > +static void prep_pci_cfg_cap(struct virtio_pci_modern_device *vdev, > + struct virtio_pci_region *region, > + size_t offset, u32 length) > +{ > + pci_write_config_byte(vdev->pci, CFG_POS(vdev, cap.bar), region->bar); > + pci_write_config_dword(vdev->pci, CFG_POS(vdev, cap.length), length); > + pci_write_config_dword(vdev->pci, CFG_POS(vdev, cap.offset), > + (uint32_t)(region->base + offset)); > +} > + > +void vpm_iowrite8(struct virtio_pci_modern_device *vdev, > + struct virtio_pci_region *region, u8 data, size_t offset) > +{ > + switch (region->flags & VIRTIO_PCI_REGION_TYPE_MASK) { > + case VIRTIO_PCI_REGION_MEMORY: > + writeb(data, region->base + offset); > + break; > + case VIRTIO_PCI_REGION_PORT: > + outb(data, region->base + offset); > + break; > + case VIRTIO_PCI_REGION_PCI_CONFIG: > + prep_pci_cfg_cap(vdev, region, offset, 1); > + pci_write_config_byte(vdev->pci, CFG_POS(vdev, pci_cfg_data), data); > + break; > + default: > + assert(0); > + break; > + } > +} > + > +void vpm_iowrite16(struct virtio_pci_modern_device *vdev, > + struct virtio_pci_region *region, u16 data, size_t offset) > +{ > + data = cpu_to_le16(data); > + switch (region->flags & VIRTIO_PCI_REGION_TYPE_MASK) { > + case VIRTIO_PCI_REGION_MEMORY: > + writew(data, region->base + offset); > + break; > + case VIRTIO_PCI_REGION_PORT: > + outw(data, region->base + offset); > + break; > + case VIRTIO_PCI_REGION_PCI_CONFIG: > + prep_pci_cfg_cap(vdev, region, offset, 2); > + pci_write_config_word(vdev->pci, CFG_POS(vdev, pci_cfg_data), data); > + break; > + default: > + assert(0); > + break; > + } > +} > + > +void vpm_iowrite32(struct virtio_pci_modern_device *vdev, > + struct virtio_pci_region *region, u32 data, size_t offset) > +{ > + data = cpu_to_le32(data); > + switch (region->flags & VIRTIO_PCI_REGION_TYPE_MASK) { > + case VIRTIO_PCI_REGION_MEMORY: > + writel(data, region->base + offset); > + break; > + case VIRTIO_PCI_REGION_PORT: > + outl(data, region->base + offset); > + break; > + case VIRTIO_PCI_REGION_PCI_CONFIG: > + prep_pci_cfg_cap(vdev, region, offset, 4); > + pci_write_config_dword(vdev->pci, CFG_POS(vdev, pci_cfg_data), data); > + break; > + default: > + assert(0); > + break; > + } > +} > + > +u8 vpm_ioread8(struct virtio_pci_modern_device *vdev, > + struct virtio_pci_region *region, size_t offset) > +{ > + uint8_t data; > + switch (region->flags & VIRTIO_PCI_REGION_TYPE_MASK) { > + case VIRTIO_PCI_REGION_MEMORY: > + data = readb(region->base + offset); > + break; > + case VIRTIO_PCI_REGION_PORT: > + data = inb(region->base + offset); > + break; > + case VIRTIO_PCI_REGION_PCI_CONFIG: > + prep_pci_cfg_cap(vdev, region, offset, 1); > + pci_read_config_byte(vdev->pci, CFG_POS(vdev, pci_cfg_data), &data); > + break; > + default: > + assert(0); > + data = 0; > + break; > + } > + return data; > +} > + > +u16 vpm_ioread16(struct virtio_pci_modern_device *vdev, > + struct virtio_pci_region *region, size_t offset) > +{ > + uint16_t data; > + switch (region->flags & VIRTIO_PCI_REGION_TYPE_MASK) { > + case VIRTIO_PCI_REGION_MEMORY: > + data = readw(region->base + offset); > + break; > + case VIRTIO_PCI_REGION_PORT: > + data = inw(region->base + offset); > + break; > + case VIRTIO_PCI_REGION_PCI_CONFIG: > + prep_pci_cfg_cap(vdev, region, offset, 2); > + pci_read_config_word(vdev->pci, CFG_POS(vdev, pci_cfg_data), &data); > + break; > + default: > + assert(0); > + data = 0; > + break; > + } > + return le16_to_cpu(data); > +} > + > +u32 vpm_ioread32(struct virtio_pci_modern_device *vdev, > + struct virtio_pci_region *region, size_t offset) > +{ > + uint32_t data; > + switch (region->flags & VIRTIO_PCI_REGION_TYPE_MASK) { > + case VIRTIO_PCI_REGION_MEMORY: > + data = readw(region->base + offset); > + break; > + case VIRTIO_PCI_REGION_PORT: > + data = inw(region->base + offset); > + break; > + case VIRTIO_PCI_REGION_PCI_CONFIG: > + prep_pci_cfg_cap(vdev, region, offset, 4); > + pci_read_config_dword(vdev->pci, CFG_POS(vdev, pci_cfg_data), &data); > + break; > + default: > + assert(0); > + data = 0; > + break; > + } > + return le32_to_cpu(data); > +} > + > +int virtio_pci_find_capability(struct pci_device *pci, uint8_t cfg_type) > +{ > + int pos; > + uint8_t type, bar; > + > + for (pos = pci_find_capability(pci, PCI_CAP_ID_VNDR); > + pos > 0; > + pos = pci_find_next_capability(pci, pos, PCI_CAP_ID_VNDR)) { > + > + pci_read_config_byte(pci, pos + offsetof(struct virtio_pci_cap, > + cfg_type), &type); > + pci_read_config_byte(pci, pos + offsetof(struct virtio_pci_cap, > + bar), &bar); > + > + /* Ignore structures with reserved BAR values */ > + if (bar > 0x5) { > + continue; > + } > + > + if (type == cfg_type) { > + return pos; > + } > + } > + return 0; > +} > + > +int virtio_pci_map_capability(struct pci_device *pci, int cap, size_t minlen, > + u32 align, u32 start, u32 size, > + struct virtio_pci_region *region) > +{ > + u8 bar; > + u32 offset, length, base_raw; > + unsigned long base; > + > + pci_read_config_byte(pci, cap + offsetof(struct virtio_pci_cap, bar), > &bar); > + pci_read_config_dword(pci, cap + offsetof(struct virtio_pci_cap, offset), > + &offset); > + pci_read_config_dword(pci, cap + offsetof(struct virtio_pci_cap, length), > + &length); > + > + if (length <= start) { > + DBG("VIRTIO-PCI bad capability len %u (>%u expected)\n", length, > start); > + return -EINVAL; > + } > + if (length - start < minlen) { > + DBG("VIRTIO-PCI bad capability len %u (>=%zu expected)\n", length, > minlen); > + return -EINVAL; > + } > + length -= start; > + if (start + offset < offset) { > + DBG("VIRTIO-PCI map wrap-around %u+%u\n", start, offset); > + return -EINVAL; > + } > + offset += start; > + if (offset & (align - 1)) { > + DBG("VIRTIO-PCI offset %u not aligned to %u\n", offset, align); > + return -EINVAL; > + } > + if (length > size) { > + length = size; > + } > + > + if (minlen + offset < minlen || > + minlen + offset > pci_bar_size(pci, PCI_BASE_ADDRESS(bar))) { > + DBG("VIRTIO-PCI map virtio %zu@%u out of range on bar %i length > %lu\n", > + minlen, offset, > + bar, (unsigned long)pci_bar_size(pci, PCI_BASE_ADDRESS(bar))); > + return -EINVAL; > + } > + > + region->base = NULL; > + region->length = length; > + region->bar = bar; > + > + base = pci_bar_start(pci, PCI_BASE_ADDRESS(bar)); > + if (base) { > + pci_read_config_dword(pci, PCI_BASE_ADDRESS(bar), &base_raw); > + > + if (base_raw & PCI_BASE_ADDRESS_SPACE_IO) { > + /* Region accessed using port I/O */ > + region->base = (void *)(base + offset); > + region->flags = VIRTIO_PCI_REGION_PORT; > + } else { > + /* Region mapped into memory space */ > + region->base = ioremap(base + offset, length); > + region->flags = VIRTIO_PCI_REGION_MEMORY; > + } > + } > + if (!region->base) { > + /* Region accessed via PCI config space window */ > + region->base = (void *)offset; > + region->flags = VIRTIO_PCI_REGION_PCI_CONFIG; > + } > + return 0; > +} > + > +void virtio_pci_unmap_capability(struct virtio_pci_region *region) > +{ > + unsigned region_type = region->flags & VIRTIO_PCI_REGION_TYPE_MASK; > + if (region_type == VIRTIO_PCI_REGION_MEMORY) { > + iounmap(region->base); > + } > +} > + > +void vpm_notify(struct virtio_pci_modern_device *vdev, > + struct vring_virtqueue *vq) > +{ > + vpm_iowrite16(vdev, &vq->notification, (u16)vq->queue_index, 0); > +} > + > +int vpm_find_vqs(struct virtio_pci_modern_device *vdev, > + unsigned nvqs, struct vring_virtqueue *vqs) > +{ > + unsigned i; > + struct vring_virtqueue *vq; > + u16 size, off; > + u32 notify_offset_multiplier; > + int err; > + > + if (nvqs > vpm_ioread16(vdev, &vdev->common, COMMON_OFFSET(num_queues))) > { > + return -ENOENT; > + } > + > + /* Read notify_off_multiplier from config space. */ > + pci_read_config_dword(vdev->pci, > + vdev->notify_cap_pos + offsetof(struct virtio_pci_notify_cap, > + notify_off_multiplier), > + ¬ify_offset_multiplier); > + > + for (i = 0; i < nvqs; i++) { > + /* Select the queue we're interested in */ > + vpm_iowrite16(vdev, &vdev->common, (u16)i, > COMMON_OFFSET(queue_select)); > + > + /* Check if queue is either not available or already active. */ > + size = vpm_ioread16(vdev, &vdev->common, COMMON_OFFSET(queue_size)); > + /* QEMU has a bug where queues don't revert to inactive on device > + * reset. Skip checking the queue_enable field until it is fixed.
Should be OK now, right? Maybe we can drop it. Can be a patch on top. > + */ > + if (!size /*|| vpm_ioread16(vdev, &vdev->common.queue_enable)*/) > + return -ENOENT; > + > + if (size & (size - 1)) { > + DBG("VIRTIO-PCI %p: bad queue size %u", vdev, size); > + return -EINVAL; > + } > + > + vq = &vqs[i]; > + vq->queue_index = i; > + > + /* get offset of notification word for this vq */ > + off = vpm_ioread16(vdev, &vdev->common, > COMMON_OFFSET(queue_notify_off)); > + vq->vring.num = size; > + > + vring_init(&vq->vring, size, (unsigned char *)vq->queue); > + > + /* activate the queue */ > + vpm_iowrite16(vdev, &vdev->common, size, COMMON_OFFSET(queue_size)); > + > + vpm_iowrite64(vdev, &vdev->common, virt_to_phys(vq->vring.desc), > + COMMON_OFFSET(queue_desc_lo), > + COMMON_OFFSET(queue_desc_hi)); > + vpm_iowrite64(vdev, &vdev->common, virt_to_phys(vq->vring.avail), > + COMMON_OFFSET(queue_avail_lo), > + COMMON_OFFSET(queue_avail_hi)); > + vpm_iowrite64(vdev, &vdev->common, virt_to_phys(vq->vring.used), > + COMMON_OFFSET(queue_used_lo), > + COMMON_OFFSET(queue_used_hi)); > + > + err = virtio_pci_map_capability(vdev->pci, > + vdev->notify_cap_pos, 2, 2, > + off * notify_offset_multiplier, 2, > + &vq->notification); > + if (err) { > + goto err_map_notify; > + } > + } > + > + /* Select and activate all queues. Has to be done last: once we do > + * this, there's no way to go back except reset. > + */ > + for (i = 0; i < nvqs; i++) { > + vq = &vqs[i]; > + vpm_iowrite16(vdev, &vdev->common, (u16)vq->queue_index, > + COMMON_OFFSET(queue_select)); > + vpm_iowrite16(vdev, &vdev->common, 1, COMMON_OFFSET(queue_enable)); > + } > + return 0; > + > +err_map_notify: > + /* Undo the virtio_pci_map_capability calls. */ > + while (i-- > 0) { > + virtio_pci_unmap_capability(&vqs[i].notification); > + } > + return err; > +} > diff --git a/src/drivers/bus/virtio-ring.c b/src/drivers/bus/virtio-ring.c > index e55b6d0..98e787e 100644 > --- a/src/drivers/bus/virtio-ring.c > +++ b/src/drivers/bus/virtio-ring.c > @@ -18,8 +18,8 @@ FILE_LICENCE ( GPL2_OR_LATER ); > > #include "etherboot.h" > #include "ipxe/io.h" > -#include "ipxe/virtio-ring.h" > #include "ipxe/virtio-pci.h" > +#include "ipxe/virtio-ring.h" > > #define BUG() do { \ > printf("BUG: failure at %s:%d/%s()!\n", \ > @@ -122,7 +122,8 @@ void vring_add_buf(struct vring_virtqueue *vq, > wmb(); > } > > -void vring_kick(unsigned int ioaddr, struct vring_virtqueue *vq, int > num_added) > +void vring_kick(struct virtio_pci_modern_device *vdev, unsigned int ioaddr, > + struct vring_virtqueue *vq, int num_added) > { > struct vring *vr = &vq->vring; > > @@ -130,7 +131,13 @@ void vring_kick(unsigned int ioaddr, struct > vring_virtqueue *vq, int num_added) > vr->avail->idx += num_added; > > mb(); > - if (!(vr->used->flags & VRING_USED_F_NO_NOTIFY)) > - vp_notify(ioaddr, vq->queue_index); > + if (!(vr->used->flags & VRING_USED_F_NO_NOTIFY)) { > + if (vdev) { > + /* virtio 1.0 */ > + vpm_notify(vdev, vq); > + } else { > + /* legacy virtio */ > + vp_notify(ioaddr, vq->queue_index); > + } > + } > } > - > diff --git a/src/drivers/net/virtio-net.c b/src/drivers/net/virtio-net.c > index 533ccb0..446bbd6 100644 > --- a/src/drivers/net/virtio-net.c > +++ b/src/drivers/net/virtio-net.c > @@ -24,14 +24,15 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); > > #include <errno.h> > #include <stdlib.h> > +#include <unistd.h> > #include <ipxe/list.h> > #include <ipxe/iobuf.h> > #include <ipxe/netdevice.h> > #include <ipxe/pci.h> > #include <ipxe/if_ether.h> > #include <ipxe/ethernet.h> > -#include <ipxe/virtio-ring.h> > #include <ipxe/virtio-pci.h> > +#include <ipxe/virtio-ring.h> > #include "virtio-net.h" > > /* > @@ -135,7 +136,7 @@ static void virtnet_enqueue_iob ( struct net_device > *netdev, > virtnet, iobuf, vq_idx ); > > vring_add_buf ( vq, list, out, in, iobuf, 0 ); > - vring_kick ( virtnet->ioaddr, vq, 1 ); > + vring_kick ( NULL, virtnet->ioaddr, vq, 1 ); > } > > /** Try to keep rx virtqueue filled with iobufs > diff --git a/src/include/ipxe/errfile.h b/src/include/ipxe/errfile.h > index 338ebdd..c7c34cc 100644 > --- a/src/include/ipxe/errfile.h > +++ b/src/include/ipxe/errfile.h > @@ -350,6 +350,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); > #define ERRFILE_efi_usb ( ERRFILE_OTHER | 0x004b0000 ) > #define ERRFILE_efi_fbcon ( ERRFILE_OTHER | 0x004c0000 ) > #define ERRFILE_efi_local ( ERRFILE_OTHER | 0x004d0000 ) > +#define ERRFILE_virtio_pci ( ERRFILE_OTHER | 0x004e0000 ) > > /** @} */ > > diff --git a/src/include/ipxe/virtio-pci.h b/src/include/ipxe/virtio-pci.h > index 8076f20..c7452c8 100644 > --- a/src/include/ipxe/virtio-pci.h > +++ b/src/include/ipxe/virtio-pci.h > @@ -97,6 +97,44 @@ struct virtio_pci_common_cfg { > __le32 queue_used_hi; /* read-write */ > }; > > +/* Virtio 1.0 PCI region descriptor. We support memory mapped I/O, port I/O, > + * and PCI config space access via the cfg PCI capability as a fallback. */ > +struct virtio_pci_region { > + void *base; > + size_t length; > + u8 bar; > + > +/* How to interpret the base field */ > +#define VIRTIO_PCI_REGION_TYPE_MASK 0x00000003 > +/* The base field is a memory address */ > +#define VIRTIO_PCI_REGION_MEMORY 0x00000000 > +/* The base field is a port address */ > +#define VIRTIO_PCI_REGION_PORT 0x00000001 > +/* The base field is an offset within the PCI bar */ > +#define VIRTIO_PCI_REGION_PCI_CONFIG 0x00000002 > + unsigned flags; > +}; > + > +/* Virtio 1.0 device state */ > +struct virtio_pci_modern_device { > + struct pci_device *pci; > + > + /* VIRTIO_PCI_CAP_PCI_CFG position */ > + int cfg_cap_pos; > + > + /* VIRTIO_PCI_CAP_COMMON_CFG data */ > + struct virtio_pci_region common; > + > + /* VIRTIO_PCI_CAP_DEVICE_CFG data */ > + struct virtio_pci_region device; > + > + /* VIRTIO_PCI_CAP_ISR_CFG data */ > + struct virtio_pci_region isr; > + > + /* VIRTIO_PCI_CAP_NOTIFY_CFG data */ > + int notify_cap_pos; > +}; > + > static inline u32 vp_get_features(unsigned int ioaddr) > { > return inl(ioaddr + VIRTIO_PCI_HOST_FEATURES); > @@ -156,6 +194,115 @@ static inline void vp_del_vq(unsigned int ioaddr, int > queue_index) > outl(0, ioaddr + VIRTIO_PCI_QUEUE_PFN); > } > > +struct vring_virtqueue; > + > int vp_find_vq(unsigned int ioaddr, int queue_index, > struct vring_virtqueue *vq); > + > +/* Virtio 1.0 I/O routines abstract away the three possible HW access > + * mechanisms - memory, port I/O, and PCI cfg space access. Also built-in > + * are endianness conversions - to LE on write and from LE on read. */ > + > +void vpm_iowrite8(struct virtio_pci_modern_device *vdev, > + struct virtio_pci_region *region, u8 data, size_t offset); > + > +void vpm_iowrite16(struct virtio_pci_modern_device *vdev, > + struct virtio_pci_region *region, u16 data, size_t > offset); > + > +void vpm_iowrite32(struct virtio_pci_modern_device *vdev, > + struct virtio_pci_region *region, u32 data, size_t > offset); > + > +static inline void vpm_iowrite64(struct virtio_pci_modern_device *vdev, > + struct virtio_pci_region *region, > + u64 data, size_t offset_lo, size_t > offset_hi) > +{ > + vpm_iowrite32(vdev, region, (u32)data, offset_lo); > + vpm_iowrite32(vdev, region, data >> 32, offset_hi); > +} > + > +u8 vpm_ioread8(struct virtio_pci_modern_device *vdev, > + struct virtio_pci_region *region, size_t offset); > + > +u16 vpm_ioread16(struct virtio_pci_modern_device *vdev, > + struct virtio_pci_region *region, size_t offset); > + > +u32 vpm_ioread32(struct virtio_pci_modern_device *vdev, > + struct virtio_pci_region *region, size_t offset); > + > +/* Virtio 1.0 device manipulation routines */ > + > +#define COMMON_OFFSET(field) offsetof(struct virtio_pci_common_cfg, field) > + > +static inline void vpm_reset(struct virtio_pci_modern_device *vdev) > +{ > + vpm_iowrite8(vdev, &vdev->common, 0, COMMON_OFFSET(device_status)); > + while (vpm_ioread8(vdev, &vdev->common, COMMON_OFFSET(device_status))) > + mdelay(1); > +} > + > +static inline u8 vpm_get_status(struct virtio_pci_modern_device *vdev) > +{ > + return vpm_ioread8(vdev, &vdev->common, COMMON_OFFSET(device_status)); > +} > + > +static inline void vpm_add_status(struct virtio_pci_modern_device *vdev, > + u8 status) > +{ > + u8 curr_status = vpm_ioread8(vdev, &vdev->common, > COMMON_OFFSET(device_status)); > + vpm_iowrite8(vdev, &vdev->common, > + curr_status | status, COMMON_OFFSET(device_status)); > +} > + > +static inline u64 vpm_get_features(struct virtio_pci_modern_device *vdev) > +{ > + u32 features_lo, features_hi; > + > + vpm_iowrite32(vdev, &vdev->common, 0, > COMMON_OFFSET(device_feature_select)); > + features_lo = vpm_ioread32(vdev, &vdev->common, > COMMON_OFFSET(device_feature)); > + vpm_iowrite32(vdev, &vdev->common, 1, > COMMON_OFFSET(device_feature_select)); > + features_hi = vpm_ioread32(vdev, &vdev->common, > COMMON_OFFSET(device_feature)); > + > + return ((u64)features_hi << 32) | features_lo; > +} > + > +static inline void vpm_set_features(struct virtio_pci_modern_device *vdev, > + u64 features) > +{ > + u32 features_lo = (u32)features; > + u32 features_hi = features >> 32; > + > + vpm_iowrite32(vdev, &vdev->common, 0, > COMMON_OFFSET(guest_feature_select)); > + vpm_iowrite32(vdev, &vdev->common, features_lo, > COMMON_OFFSET(guest_feature)); > + vpm_iowrite32(vdev, &vdev->common, 1, > COMMON_OFFSET(guest_feature_select)); > + vpm_iowrite32(vdev, &vdev->common, features_hi, > COMMON_OFFSET(guest_feature)); > +} > + > +static inline void vpm_get(struct virtio_pci_modern_device *vdev, > + unsigned offset, void *buf, unsigned len) > +{ > + u8 *ptr = buf; > + unsigned i; > + > + for (i = 0; i < len; i++) > + ptr[i] = vpm_ioread8(vdev, &vdev->device, offset + i); > +} > + > +static inline u8 vpm_get_isr(struct virtio_pci_modern_device *vdev) > +{ > + return vpm_ioread8(vdev, &vdev->isr, 0); > +} > + > +void vpm_notify(struct virtio_pci_modern_device *vdev, > + struct vring_virtqueue *vq); > + > +int vpm_find_vqs(struct virtio_pci_modern_device *vdev, > + unsigned nvqs, struct vring_virtqueue *vqs); > + > +int virtio_pci_find_capability(struct pci_device *pci, uint8_t cfg_type); > + > +int virtio_pci_map_capability(struct pci_device *pci, int cap, size_t minlen, > + u32 align, u32 start, u32 size, > + struct virtio_pci_region *region); > + > +void virtio_pci_unmap_capability(struct virtio_pci_region *region); > #endif /* _VIRTIO_PCI_H_ */ > diff --git a/src/include/ipxe/virtio-ring.h b/src/include/ipxe/virtio-ring.h > index e44d13c..6ba550b 100644 > --- a/src/include/ipxe/virtio-ring.h > +++ b/src/include/ipxe/virtio-ring.h > @@ -1,6 +1,8 @@ > #ifndef _VIRTIO_RING_H_ > # define _VIRTIO_RING_H_ > > +#include <ipxe/virtio-pci.h> > + > /* Status byte for guest to report progress, and synchronize features. */ > /* We have seen device and processed generic fields (VIRTIO_CONFIG_F_VIRTIO) > */ > #define VIRTIO_CONFIG_S_ACKNOWLEDGE 1 > @@ -79,6 +81,7 @@ struct vring_virtqueue { > void *vdata[MAX_QUEUE_NUM]; > /* PCI */ > int queue_index; > + struct virtio_pci_region notification; > }; > > struct vring_list { > @@ -142,6 +145,7 @@ void *vring_get_buf(struct vring_virtqueue *vq, unsigned > int *len); > void vring_add_buf(struct vring_virtqueue *vq, struct vring_list list[], > unsigned int out, unsigned int in, > void *index, int num_added); > -void vring_kick(unsigned int ioaddr, struct vring_virtqueue *vq, int > num_added); > +void vring_kick(struct virtio_pci_modern_device *vdev, unsigned int ioaddr, > + struct vring_virtqueue *vq, int num_added); > > #endif /* _VIRTIO_RING_H_ */ > -- > 2.5.5 _______________________________________________ ipxe-devel mailing list ipxe-devel@lists.ipxe.org https://lists.ipxe.org/mailman/listinfo.cgi/ipxe-devel