diff --git a/devicemodel/hw/pci/virtio/virtio.c b/devicemodel/hw/pci/virtio/virtio.c index e715e9bc5..745f1dbf4 100644 --- a/devicemodel/hw/pci/virtio/virtio.c +++ b/devicemodel/hw/pci/virtio/virtio.c @@ -130,6 +130,7 @@ virtio_set_io_bar(struct virtio_base *base, int barnum) */ size = VIRTIO_CR_CFG1 + base->vops->cfgsize; pci_emul_alloc_bar(base->dev, barnum, PCIBAR_IO, size); + base->legacy_pio_bar_idx = barnum; } /* @@ -561,7 +562,7 @@ virtio_pci_read(struct vmctx *ctx, int vcpu, struct pci_vdev *dev, } /* XXX probably should do something better than just assert() */ - assert(baridx == 0); + assert(baridx == base->legacy_pio_bar_idx); if (base->mtx) pthread_mutex_lock(base->mtx); @@ -683,7 +684,7 @@ virtio_pci_write(struct vmctx *ctx, int vcpu, struct pci_vdev *dev, } /* XXX probably should do something better than just assert() */ - assert(baridx == 0); + assert(baridx == base->legacy_pio_bar_idx); if (base->mtx) pthread_mutex_lock(base->mtx); @@ -798,3 +799,134 @@ done: if (base->mtx) pthread_mutex_unlock(base->mtx); } + +/* + * Set virtio modern MMIO BAR (usually 4) to map the 4 capabilities. + */ +static int +virtio_set_modern_mmio_bar(struct virtio_base *base, int barnum) +{ + struct virtio_ops *vops; + int rc; + struct virtio_pci_cap cap = { + .cap_vndr = PCIY_VENDOR, + .cap_next = 0, + .cap_len = sizeof(cap), + .bar = barnum, + }; + struct virtio_pci_notify_cap notify = { + .cap.cap_vndr = PCIY_VENDOR, + .cap.cap_next = 0, + .cap.cap_len = sizeof(notify), + .cap.cfg_type = VIRTIO_PCI_CAP_NOTIFY_CFG, + .cap.bar = barnum, + .cap.offset = VIRTIO_CAP_NOTIFY_OFFSET, + .cap.length = VIRTIO_CAP_NOTIFY_SIZE, + .notify_off_multiplier = VIRTIO_MODERN_NOTIFY_OFF_MULT, + }; + struct virtio_pci_cfg_cap cfg = { + .cap.cap_vndr = PCIY_VENDOR, + .cap.cap_next = 0, + .cap.cap_len = sizeof(cfg), + .cap.cfg_type = VIRTIO_PCI_CAP_PCI_CFG, + }; + + vops = base->vops; + + if (vops->cfgsize > VIRTIO_CAP_DEVICE_SIZE) { + fprintf(stderr, + "%s: cfgsize %lu > max %d\r\n", + vops->name, vops->cfgsize, VIRTIO_CAP_DEVICE_SIZE); + return -1; + } + + /* common configuration capability */ + cap.cfg_type = VIRTIO_PCI_CAP_COMMON_CFG; + cap.offset = VIRTIO_CAP_COMMON_OFFSET; + cap.length = VIRTIO_CAP_COMMON_SIZE; + rc = pci_emul_add_capability(base->dev, (u_char *)&cap, sizeof(cap)); + assert(rc == 0); + + /* isr status capability */ + cap.cfg_type = VIRTIO_PCI_CAP_ISR_CFG; + cap.offset = VIRTIO_CAP_ISR_OFFSET; + cap.length = VIRTIO_CAP_ISR_SIZE; + rc = pci_emul_add_capability(base->dev, (u_char *)&cap, sizeof(cap)); + assert(rc == 0); + + /* device specific configuration capability */ + cap.cfg_type = VIRTIO_PCI_CAP_DEVICE_CFG; + cap.offset = VIRTIO_CAP_DEVICE_OFFSET; + cap.length = VIRTIO_CAP_DEVICE_SIZE; + rc = pci_emul_add_capability(base->dev, (u_char *)&cap, sizeof(cap)); + assert(rc == 0); + + /* notification capability */ + rc = pci_emul_add_capability(base->dev, (u_char *)¬ify, + sizeof(notify)); + assert(rc == 0); + + /* pci alternative configuration access capability */ + rc = pci_emul_add_capability(base->dev, (u_char *)&cfg, sizeof(cfg)); + assert(rc == 0); + + /* allocate and register modern memory bar */ + rc = pci_emul_alloc_bar(base->dev, barnum, PCIBAR_MEM64, + VIRTIO_MODERN_MEM_BAR_SIZE); + assert(rc == 0); + + base->modern_mmio_bar_idx = barnum; + return 0; +} + +/* + * Set virtio modern PIO BAR (usually 2) to map notify capability. + */ +static int +virtio_set_modern_pio_bar(struct virtio_base *base, int barnum) +{ + int rc; + struct virtio_pci_notify_cap notify_pio = { + .cap.cap_vndr = PCIY_VENDOR, + .cap.cap_next = 0, + .cap.cap_len = sizeof(notify_pio), + .cap.cfg_type = VIRTIO_PCI_CAP_NOTIFY_CFG, + .cap.bar = barnum, + .cap.offset = 0, + .cap.length = 4, + .notify_off_multiplier = 0, + }; + + /* notification capability */ + rc = pci_emul_add_capability(base->dev, (u_char *)¬ify_pio, + sizeof(notify_pio)); + assert(rc == 0); + + /* allocate and register modern pio bar */ + rc = pci_emul_alloc_bar(base->dev, barnum, PCIBAR_IO, 4); + assert(rc == 0); + + base->modern_pio_bar_idx = barnum; + return 0; +} + +int +virtio_set_modern_bar(struct virtio_base *base, bool use_notify_pio) +{ + struct virtio_ops *vops; + int rc = 0; + + vops = base->vops; + + if (!vops || (vops->hv_caps & VIRTIO_F_VERSION_1) == 0) + return -1; + + if (use_notify_pio) + rc = virtio_set_modern_pio_bar(base, + VIRTIO_MODERN_PIO_BAR_IDX); + if (!rc) + rc = virtio_set_modern_mmio_bar(base, + VIRTIO_MODERN_MMIO_BAR_IDX); + + return rc; +} diff --git a/devicemodel/include/virtio.h b/devicemodel/include/virtio.h index 33a03b7d3..ea455ceb3 100644 --- a/devicemodel/include/virtio.h +++ b/devicemodel/include/virtio.h @@ -839,6 +839,21 @@ uint64_t virtio_pci_read(struct vmctx *ctx, int vcpu, struct pci_vdev *dev, */ void virtio_pci_write(struct vmctx *ctx, int vcpu, struct pci_vdev *dev, int baridx, uint64_t offset, int size, uint64_t value); + +/** + * @brief Set modern BAR (usually 4) to map PCI config registers. + * + * Set modern MMIO BAR (usually 4) to map virtio 1.0 capabilities and optional + * set modern PIO BAR (usually 2) to map notify capability. This interface is + * only valid for modern virtio. + * + * @param base Pointer to struct virtio_base. + * @param use_notify_pio Whether use pio for notify capability. + * + * @return 0 on success and non-zero on fail. + */ +int virtio_set_modern_bar(struct virtio_base *base, bool use_notify_pio); + /** * @} */