iPXE
Data Structures | Defines | Functions
virtio-pci.h File Reference

Go to the source code of this file.

Data Structures

struct  virtio_pci_cap
struct  virtio_pci_notify_cap
struct  virtio_pci_cfg_cap
struct  virtio_pci_common_cfg
struct  virtio_pci_region
struct  virtio_pci_modern_device

Defines

#define VIRTIO_PCI_HOST_FEATURES   0
#define VIRTIO_PCI_GUEST_FEATURES   4
#define VIRTIO_PCI_QUEUE_PFN   8
#define VIRTIO_PCI_QUEUE_NUM   12
#define VIRTIO_PCI_QUEUE_SEL   14
#define VIRTIO_PCI_QUEUE_NOTIFY   16
#define VIRTIO_PCI_STATUS   18
#define VIRTIO_PCI_ISR   19
#define VIRTIO_PCI_ISR_CONFIG   0x2
#define VIRTIO_PCI_CONFIG   20
#define VIRTIO_PCI_ABI_VERSION   0
#define VIRTIO_PCI_CAP_COMMON_CFG   1 /* Common configuration */
#define VIRTIO_PCI_CAP_NOTIFY_CFG   2 /* Notifications */
#define VIRTIO_PCI_CAP_ISR_CFG   3 /* ISR access */
#define VIRTIO_PCI_CAP_DEVICE_CFG   4 /* Device specific configuration */
#define VIRTIO_PCI_CAP_PCI_CFG   5 /* PCI configuration access */
#define __u8   uint8_t
#define __le16   uint16_t
#define __le32   uint32_t
#define __le64   uint64_t
#define VIRTIO_PCI_REGION_TYPE_MASK   0x00000003
#define VIRTIO_PCI_REGION_MEMORY   0x00000001
#define VIRTIO_PCI_REGION_PORT   0x00000002
#define VIRTIO_PCI_REGION_PCI_CONFIG   0x00000003
#define COMMON_OFFSET(field)   offsetof(struct virtio_pci_common_cfg, field)

Functions

static u32 vp_get_features (unsigned int ioaddr)
static void vp_set_features (unsigned int ioaddr, u32 features)
static void vp_get (unsigned int ioaddr, unsigned offset, void *buf, unsigned len)
static u8 vp_get_status (unsigned int ioaddr)
static void vp_set_status (unsigned int ioaddr, u8 status)
static u8 vp_get_isr (unsigned int ioaddr)
static void vp_reset (unsigned int ioaddr)
static void vp_notify (unsigned int ioaddr, int queue_index)
static void vp_del_vq (unsigned int ioaddr, int queue_index)
void vp_free_vq (struct vring_virtqueue *vq)
int vp_find_vq (unsigned int ioaddr, int queue_index, struct vring_virtqueue *vq)
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 void vpm_iowrite64 (struct virtio_pci_modern_device *vdev, struct virtio_pci_region *region, u64 data, size_t offset_lo, size_t 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)
static void vpm_reset (struct virtio_pci_modern_device *vdev)
static u8 vpm_get_status (struct virtio_pci_modern_device *vdev)
static void vpm_add_status (struct virtio_pci_modern_device *vdev, u8 status)
static u64 vpm_get_features (struct virtio_pci_modern_device *vdev)
static void vpm_set_features (struct virtio_pci_modern_device *vdev, u64 features)
static void vpm_get (struct virtio_pci_modern_device *vdev, unsigned offset, void *buf, unsigned len)
static u8 vpm_get_isr (struct virtio_pci_modern_device *vdev)
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)

Define Documentation

#define VIRTIO_PCI_HOST_FEATURES   0

Definition at line 5 of file virtio-pci.h.

Referenced by vp_get_features().

#define VIRTIO_PCI_GUEST_FEATURES   4

Definition at line 8 of file virtio-pci.h.

Referenced by vp_set_features().

#define VIRTIO_PCI_QUEUE_PFN   8

Definition at line 11 of file virtio-pci.h.

Referenced by vp_del_vq(), and vp_find_vq().

#define VIRTIO_PCI_QUEUE_NUM   12

Definition at line 14 of file virtio-pci.h.

Referenced by vp_find_vq().

#define VIRTIO_PCI_QUEUE_SEL   14

Definition at line 17 of file virtio-pci.h.

Referenced by vp_del_vq(), and vp_find_vq().

#define VIRTIO_PCI_QUEUE_NOTIFY   16

Definition at line 20 of file virtio-pci.h.

Referenced by vp_notify().

#define VIRTIO_PCI_STATUS   18

Definition at line 23 of file virtio-pci.h.

Referenced by vp_get_status(), vp_reset(), and vp_set_status().

#define VIRTIO_PCI_ISR   19

Definition at line 28 of file virtio-pci.h.

Referenced by vp_get_isr(), and vp_reset().

#define VIRTIO_PCI_ISR_CONFIG   0x2

Definition at line 31 of file virtio-pci.h.

#define VIRTIO_PCI_CONFIG   20

Definition at line 35 of file virtio-pci.h.

Referenced by vp_get().

#define VIRTIO_PCI_ABI_VERSION   0

Definition at line 38 of file virtio-pci.h.

#define VIRTIO_PCI_CAP_COMMON_CFG   1 /* Common configuration */

Definition at line 41 of file virtio-pci.h.

Referenced by virtnet_probe_modern().

#define VIRTIO_PCI_CAP_NOTIFY_CFG   2 /* Notifications */

Definition at line 42 of file virtio-pci.h.

Referenced by virtnet_probe_modern().

#define VIRTIO_PCI_CAP_ISR_CFG   3 /* ISR access */

Definition at line 43 of file virtio-pci.h.

Referenced by virtnet_probe_modern().

#define VIRTIO_PCI_CAP_DEVICE_CFG   4 /* Device specific configuration */

Definition at line 44 of file virtio-pci.h.

Referenced by virtnet_probe_modern().

#define VIRTIO_PCI_CAP_PCI_CFG   5 /* PCI configuration access */

Definition at line 45 of file virtio-pci.h.

Referenced by virtnet_probe_modern().

#define __u8   uint8_t

Definition at line 47 of file virtio-pci.h.

#define __le16   uint16_t

Definition at line 48 of file virtio-pci.h.

#define __le32   uint32_t

Definition at line 49 of file virtio-pci.h.

#define __le64   uint64_t

Definition at line 50 of file virtio-pci.h.

#define VIRTIO_PCI_REGION_TYPE_MASK   0x00000003
#define VIRTIO_PCI_REGION_MEMORY   0x00000001
#define VIRTIO_PCI_REGION_PORT   0x00000002
#define VIRTIO_PCI_REGION_PCI_CONFIG   0x00000003
#define COMMON_OFFSET (   field)    offsetof(struct virtio_pci_common_cfg, field)

Function Documentation

static u32 vp_get_features ( unsigned int  ioaddr) [inline, static]

Definition at line 138 of file virtio-pci.h.

References inl(), and VIRTIO_PCI_HOST_FEATURES.

Referenced by virtnet_open_legacy(), and virtnet_probe_legacy().

static void vp_set_features ( unsigned int  ioaddr,
u32  features 
) [inline, static]

Definition at line 143 of file virtio-pci.h.

References outl(), and VIRTIO_PCI_GUEST_FEATURES.

Referenced by virtnet_open_legacy().

static void vp_get ( unsigned int  ioaddr,
unsigned  offset,
void *  buf,
unsigned  len 
) [inline, static]

Definition at line 148 of file virtio-pci.h.

References inb(), len, and VIRTIO_PCI_CONFIG.

Referenced by virtnet_probe_legacy().

{
   u8 *ptr = buf;
   unsigned i;

   for (i = 0; i < len; i++)
           ptr[i] = inb(ioaddr + VIRTIO_PCI_CONFIG + offset + i);
}
static u8 vp_get_status ( unsigned int  ioaddr) [inline, static]

Definition at line 158 of file virtio-pci.h.

References inb(), and VIRTIO_PCI_STATUS.

{
   return inb(ioaddr + VIRTIO_PCI_STATUS);
}
static void vp_set_status ( unsigned int  ioaddr,
u8  status 
) [inline, static]

Definition at line 163 of file virtio-pci.h.

References outb(), and VIRTIO_PCI_STATUS.

Referenced by virtnet_open_legacy().

{
   if (status == 0)        /* reset */
           return;
   outb(status, ioaddr + VIRTIO_PCI_STATUS);
}
static u8 vp_get_isr ( unsigned int  ioaddr) [inline, static]

Definition at line 170 of file virtio-pci.h.

References inb(), and VIRTIO_PCI_ISR.

Referenced by virtnet_poll().

{
   return inb(ioaddr + VIRTIO_PCI_ISR);
}
static void vp_reset ( unsigned int  ioaddr) [inline, static]
static void vp_notify ( unsigned int  ioaddr,
int  queue_index 
) [inline, static]

Definition at line 181 of file virtio-pci.h.

References outw(), and VIRTIO_PCI_QUEUE_NOTIFY.

Referenced by vring_kick().

{
   outw(queue_index, ioaddr + VIRTIO_PCI_QUEUE_NOTIFY);
}
static void vp_del_vq ( unsigned int  ioaddr,
int  queue_index 
) [inline, static]

Definition at line 186 of file virtio-pci.h.

References outl(), outw(), VIRTIO_PCI_QUEUE_PFN, and VIRTIO_PCI_QUEUE_SEL.

{
   /* select the queue */

   outw(queue_index, ioaddr + VIRTIO_PCI_QUEUE_SEL);

   /* deactivate the queue */

   outl(0, ioaddr + VIRTIO_PCI_QUEUE_PFN);
}
void vp_free_vq ( struct vring_virtqueue vq)

Definition at line 40 of file virtio-pci.c.

References free, NULL, vring_virtqueue::queue, and vring_virtqueue::vdata.

Referenced by virtnet_free_virtqueues().

{
    if (vq->queue) {
        free(vq->queue);
        vq->queue = NULL;
        vq->vdata = NULL;
    }
}
int vp_find_vq ( unsigned int  ioaddr,
int  queue_index,
struct vring_virtqueue vq 
)

Definition at line 49 of file virtio-pci.c.

References DBG, vring::desc, inl(), inw(), num, outl(), outw(), PAGE_SHIFT, vring_virtqueue::queue, vring_virtqueue::queue_index, rc, virt_to_phys(), VIRTIO_PCI_QUEUE_NUM, VIRTIO_PCI_QUEUE_PFN, VIRTIO_PCI_QUEUE_SEL, vp_alloc_vq(), vring_virtqueue::vring, and vring_init().

Referenced by virtnet_open_legacy().

{
   struct vring * vr = &vq->vring;
   u16 num;
   int rc;

   /* select the queue */

   outw(queue_index, ioaddr + VIRTIO_PCI_QUEUE_SEL);

   /* check if the queue is available */

   num = inw(ioaddr + VIRTIO_PCI_QUEUE_NUM);
   if (!num) {
           DBG("VIRTIO-PCI ERROR: queue size is 0\n");
           return -1;
   }

   /* check if the queue is already active */

   if (inl(ioaddr + VIRTIO_PCI_QUEUE_PFN)) {
           DBG("VIRTIO-PCI ERROR: queue already active\n");
           return -1;
   }

   vq->queue_index = queue_index;

   /* initialize the queue */
   rc = vp_alloc_vq(vq, num);
   if (rc) {
           DBG("VIRTIO-PCI ERROR: failed to allocate queue memory\n");
           return rc;
   }
   vring_init(vr, num, vq->queue);

   /* activate the queue
    *
    * NOTE: vr->desc is initialized by vring_init()
    */

   outl((unsigned long)virt_to_phys(vr->desc) >> PAGE_SHIFT,
        ioaddr + VIRTIO_PCI_QUEUE_PFN);

   return num;
}
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 void vpm_iowrite64 ( struct virtio_pci_modern_device vdev,
struct virtio_pci_region region,
u64  data,
size_t  offset_lo,
size_t  offset_hi 
) [inline, static]

Definition at line 217 of file virtio-pci.h.

References vpm_iowrite32().

Referenced by vpm_find_vqs().

{
    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 
)
static void vpm_reset ( struct virtio_pci_modern_device vdev) [inline, static]

Definition at line 238 of file virtio-pci.h.

References virtio_pci_modern_device::common, COMMON_OFFSET, mdelay(), vpm_ioread8(), and vpm_iowrite8().

Referenced by virtnet_close(), and virtnet_probe_modern().

{
    vpm_iowrite8(vdev, &vdev->common, 0, COMMON_OFFSET(device_status));
    while (vpm_ioread8(vdev, &vdev->common, COMMON_OFFSET(device_status)))
        mdelay(1);
}
static u8 vpm_get_status ( struct virtio_pci_modern_device vdev) [inline, static]

Definition at line 245 of file virtio-pci.h.

References virtio_pci_modern_device::common, COMMON_OFFSET, and vpm_ioread8().

Referenced by virtnet_open_modern().

{
    return vpm_ioread8(vdev, &vdev->common, COMMON_OFFSET(device_status));
}
static void vpm_add_status ( struct virtio_pci_modern_device vdev,
u8  status 
) [inline, static]

Definition at line 250 of file virtio-pci.h.

References virtio_pci_modern_device::common, COMMON_OFFSET, vpm_ioread8(), and vpm_iowrite8().

Referenced by virtnet_open_modern(), and virtnet_probe_modern().

{
    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 u64 vpm_get_features ( struct virtio_pci_modern_device vdev) [inline, static]

Definition at line 258 of file virtio-pci.h.

References virtio_pci_modern_device::common, COMMON_OFFSET, vpm_ioread32(), and vpm_iowrite32().

Referenced by virtnet_open_modern(), and virtnet_probe_modern().

{
    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 void vpm_set_features ( struct virtio_pci_modern_device vdev,
u64  features 
) [inline, static]

Definition at line 270 of file virtio-pci.h.

References virtio_pci_modern_device::common, COMMON_OFFSET, and vpm_iowrite32().

Referenced by virtnet_open_modern().

{
    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 void vpm_get ( struct virtio_pci_modern_device vdev,
unsigned  offset,
void *  buf,
unsigned  len 
) [inline, static]

Definition at line 282 of file virtio-pci.h.

References virtio_pci_modern_device::device, len, and vpm_ioread8().

Referenced by virtnet_probe_modern().

{
    u8 *ptr = buf;
    unsigned i;

    for (i = 0; i < len; i++)
        ptr[i] = vpm_ioread8(vdev, &vdev->device, offset + i);
}
static u8 vpm_get_isr ( struct virtio_pci_modern_device vdev) [inline, static]

Definition at line 292 of file virtio-pci.h.

References virtio_pci_modern_device::isr, and vpm_ioread8().

Referenced by virtnet_poll().

{
    return vpm_ioread8(vdev, &vdev->isr, 0);
}
void vpm_notify ( struct virtio_pci_modern_device vdev,
struct vring_virtqueue vq 
)

Definition at line 344 of file virtio-pci.c.

References vring_virtqueue::notification, vring_virtqueue::queue_index, and vpm_iowrite16().

Referenced by vring_kick().

{
    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 
)

Definition at line 350 of file virtio-pci.c.

References vring::avail, virtio_pci_modern_device::common, COMMON_OFFSET, DBG, vring::desc, EINVAL, ENOENT, MAX_QUEUE_NUM, vring_virtqueue::notification, virtio_pci_modern_device::notify_cap_pos, offsetof, virtio_pci_modern_device::pci, pci_read_config_dword(), vring_virtqueue::queue, vring_virtqueue::queue_index, size, vring::used, virt_to_phys(), virtio_pci_map_capability(), vp_alloc_vq(), vpm_ioread16(), vpm_iowrite16(), vpm_iowrite64(), vring_virtqueue::vring, and vring_init().

Referenced by virtnet_open_modern().

{
    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),
        &notify_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.
         */
        if (!size /*|| vpm_ioread16(vdev, &vdev->common.queue_enable)*/)
            return -ENOENT;

        if (size & (size - 1)) {
            DBG("VIRTIO-PCI %p: bad queue size %d\n", vdev, size);
            return -EINVAL;
        }

        if (size > MAX_QUEUE_NUM) {
            /* iPXE networking tends to be not perf critical so there's no
             * need to accept large queue sizes.
             */
            size = MAX_QUEUE_NUM;
        }

        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));

        err = vp_alloc_vq(vq, size);
        if (err) {
            DBG("VIRTIO-PCI %p: failed to allocate queue memory\n", vdev);
            return err;
        }
        vring_init(&vq->vring, size, 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) {
            return err;
        }
    }

    /* 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;
}
int virtio_pci_find_capability ( struct pci_device pci,
uint8_t  cfg_type 
)

Definition at line 240 of file virtio-pci.c.

References offsetof, PCI_CAP_ID_VNDR, pci_find_capability(), pci_find_next_capability(), pci_read_config_byte(), and type.

Referenced by virtnet_probe_modern().

{
    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 
)

Definition at line 266 of file virtio-pci.c.

References virtio_pci_region::bar, virtio_pci_region::base, base, DBG, EINVAL, virtio_pci_region::flags, ioremap(), length, virtio_pci_region::length, NULL, offset, offsetof, pci_bar_size(), pci_bar_start(), PCI_BASE_ADDRESS, PCI_BASE_ADDRESS_SPACE_IO, pci_read_config_byte(), pci_read_config_dword(), size, start, VIRTIO_PCI_REGION_MEMORY, VIRTIO_PCI_REGION_PCI_CONFIG, and VIRTIO_PCI_REGION_PORT.

Referenced by virtnet_probe_modern(), and vpm_find_vqs().

{
    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 %d (>%d expected)\n", length, start);
        return -EINVAL;
    }
    if (length - start < minlen) {
        DBG("VIRTIO-PCI bad capability len %d (>=%zd expected)\n", length, minlen);
        return -EINVAL;
    }
    length -= start;
    if (start + offset < offset) {
        DBG("VIRTIO-PCI map wrap-around %d+%d\n", start, offset);
        return -EINVAL;
    }
    offset += start;
    if (offset & (align - 1)) {
        DBG("VIRTIO-PCI offset %d not aligned to %d\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 %zd@%d out of range on bar %i length %ld\n",
            minlen, offset,
            bar, 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 *)(intptr_t)offset;
        region->flags = VIRTIO_PCI_REGION_PCI_CONFIG;
    }
    return 0;
}