Page MenuHomeFreeBSD

D57364.id178996.diff
No OneTemporary

D57364.id178996.diff

diff --git a/usr.sbin/bhyve/virtio.h b/usr.sbin/bhyve/virtio.h
--- a/usr.sbin/bhyve/virtio.h
+++ b/usr.sbin/bhyve/virtio.h
@@ -33,7 +33,11 @@
#include <dev/virtio/virtio.h>
#include <dev/virtio/virtio_ring.h>
+#include <dev/virtio/virtio_config.h>
#include <dev/virtio/pci/virtio_pci_var.h>
+#include <dev/virtio/pci/virtio_pci_modern_var.h>
+
+#include <stdbool.h>
/*
* These are derived from several virtio specifications.
@@ -174,6 +178,8 @@
/*
* PCI revision IDs
*/
+#define VIRTIO_REV_LEGACY 0
+#define VIRTIO_REV_MODERN 1 /* transitional device, supports both */
#define VIRTIO_REV_INPUT 1
/*
@@ -186,6 +192,25 @@
*/
#define VIRTIO_SUBDEV_INPUT 0x1100
+/*
+ * Modern VirtIO MMIO BAR number and region offsets within it.
+ *
+ * BAR 0: legacy I/O
+ * BAR 1: MSI-X table (when MSI-X is enabled)
+ * BAR 2: modern MMIO (common cfg, ISR, device cfg, notify)
+ */
+#define VIRTIO_MODERN_BAR 2
+
+#define VTMOD_COMMON_CFG_OFFSET 0x0000
+#define VTMOD_COMMON_CFG_LEN 56 /* sizeof(virtio_pci_common_cfg) */
+#define VTMOD_ISR_CFG_OFFSET 0x0100
+#define VTMOD_ISR_CFG_LEN 4
+#define VTMOD_DEV_CFG_OFFSET 0x0200
+#define VTMOD_DEV_CFG_MAX_LEN 0x0800
+#define VTMOD_NOTIFY_OFFSET 0x1000
+#define VTMOD_NOTIFY_MULT 4 /* notify_off_multiplier */
+#define VTMOD_BAR_SIZE 0x2000 /* 8 KB total */
+
/* From section 2.3, "Virtqueue Configuration", of the virtio specification */
static inline int
vring_size_aligned(u_int qsz)
@@ -230,18 +255,24 @@
#define VIRTIO_USE_MSIX 0x01
#define VIRTIO_EVENT_IDX 0x02 /* use the event-index values */
#define VIRTIO_BROKED 0x08 /* ??? */
+#define VIRTIO_MODERN 0x10 /* device exposes modern MMIO BAR */
struct virtio_softc {
struct virtio_consts *vs_vc; /* constants (see below) */
int vs_flags; /* VIRTIO_* flags from above */
pthread_mutex_t *vs_mtx; /* POSIX mutex, if any */
struct pci_devinst *vs_pi; /* PCI device instance */
- uint32_t vs_negotiated_caps; /* negotiated capabilities */
+ uint64_t vs_negotiated_caps; /* negotiated capabilities (64-bit) */
struct vqueue_info *vs_queues; /* one per vc_nvq */
int vs_curq; /* current queue */
uint8_t vs_status; /* value from last status write */
uint8_t vs_isr; /* ISR flags, if not MSI-X */
uint16_t vs_msix_cfg_idx; /* MSI-X vector for config event */
+
+ /* Modern VirtIO 1.0 fields */
+ uint32_t vs_dev_feature_sel; /* device_feature_select (0 or 1) */
+ uint32_t vs_drv_feature_sel; /* driver_feature_select (0 or 1) */
+ uint8_t vs_config_gen; /* config_generation counter */
};
#define VS_LOCK(vs) \
@@ -293,10 +324,11 @@
* (but more easily) computable, and this time we'll compute them:
* they're just XX_ring[N].
*/
-#define VQ_ALLOC 0x01 /* set once we have a pfn */
+#define VQ_ALLOC 0x01 /* set once we have a pfn or modern addrs */
#define VQ_BROKED 0x02 /* ??? */
struct vqueue_info {
uint16_t vq_qsize; /* size of this queue (a power of 2) */
+ uint16_t vq_max_qsize; /* device max queue size (modern: driver may reduce) */
void (*vq_notify)(void *, struct vqueue_info *);
/* called instead of vc_notify, if not NULL */
@@ -304,12 +336,18 @@
uint16_t vq_num; /* we're the num'th queue in the softc */
uint16_t vq_flags; /* flags (see above) */
+ bool vq_enabled; /* modern: queue has been enabled by driver */
uint16_t vq_last_avail; /* a recent value of vq_avail->idx */
uint16_t vq_next_used; /* index of the next used slot to be filled */
uint16_t vq_save_used; /* saved vq_used->idx; see vq_endchains */
uint16_t vq_msix_idx; /* MSI-X index, or VIRTIO_MSI_NO_VECTOR */
- uint32_t vq_pfn; /* PFN of virt queue (not shifted!) */
+ uint32_t vq_pfn; /* legacy: PFN of virt queue (not shifted!) */
+
+ /* Modern: split virtqueue 64-bit guest-physical addresses */
+ uint64_t vq_desc_addr; /* descriptor table GPA */
+ uint64_t vq_avail_addr; /* available ring GPA */
+ uint64_t vq_used_addr; /* used ring GPA */
struct vring_desc *vq_desc; /* descriptor array */
struct vring_avail *vq_avail; /* the "avail" ring */
@@ -413,6 +451,8 @@
int vi_intr_init(struct virtio_softc *vs, int barnum, int use_msix);
void vi_reset_dev(struct virtio_softc *);
void vi_set_io_bar(struct virtio_softc *, int);
+int vi_set_modern_bar(struct virtio_softc *vs, bool allow_device_cfg);
+void vi_config_changed(struct virtio_softc *vs);
int vq_getchain(struct vqueue_info *vq, struct iovec *iov, int niov,
struct vi_req *reqp);
diff --git a/usr.sbin/bhyve/virtio.c b/usr.sbin/bhyve/virtio.c
--- a/usr.sbin/bhyve/virtio.c
+++ b/usr.sbin/bhyve/virtio.c
@@ -32,8 +32,11 @@
#include <machine/atomic.h>
+#include <dev/pci/pcireg.h>
#include <dev/virtio/pci/virtio_pci_legacy_var.h>
+#include <dev/virtio/pci/virtio_pci_modern_var.h>
+#include <stdbool.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
@@ -81,6 +84,7 @@
for (i = 0; i < vc->vc_nvq; i++) {
queues[i].vq_vs = vs;
queues[i].vq_num = i;
+ queues[i].vq_max_qsize = queues[i].vq_qsize;
}
}
@@ -105,13 +109,22 @@
nvq = vs->vs_vc->vc_nvq;
for (vq = vs->vs_queues, i = 0; i < nvq; vq++, i++) {
vq->vq_flags = 0;
+ vq->vq_enabled = false;
vq->vq_last_avail = 0;
vq->vq_next_used = 0;
vq->vq_save_used = 0;
vq->vq_pfn = 0;
+ vq->vq_desc_addr = 0;
+ vq->vq_avail_addr = 0;
+ vq->vq_used_addr = 0;
vq->vq_msix_idx = VIRTIO_MSI_NO_VECTOR;
+ /* Restore queue size to device maximum */
+ if (vq->vq_max_qsize != 0)
+ vq->vq_qsize = vq->vq_max_qsize;
}
vs->vs_negotiated_caps = 0;
+ vs->vs_dev_feature_sel = 0;
+ vs->vs_drv_feature_sel = 0;
vs->vs_curq = 0;
/* vs->vs_status = 0; -- redundant */
if (vs->vs_isr)
@@ -136,6 +149,504 @@
pci_emul_alloc_bar(vs->vs_pi, barnum, PCIBAR_IO, size);
}
+/*
+ * Set up modern VirtIO MMIO BAR (BAR 2) and add the required PCI vendor
+ * capabilities pointing into it. Must be called after vi_intr_init().
+ * If allow_device_cfg is false, VIRTIO_PCI_CAP_DEVICE_CFG is omitted
+ * (for devices with no device-specific config space).
+ */
+int
+vi_set_modern_bar(struct virtio_softc *vs, bool allow_device_cfg)
+{
+ struct pci_devinst *pi = vs->vs_pi;
+ struct virtio_consts *vc = vs->vs_vc;
+ struct virtio_pci_cap cap;
+ struct virtio_pci_notify_cap ncap;
+ struct virtio_pci_cfg_cap ccap;
+ uint32_t notify_len;
+ int error;
+
+ notify_len = (uint32_t)vc->vc_nvq * VTMOD_NOTIFY_MULT;
+ if (notify_len < 4)
+ notify_len = 4;
+
+ error = pci_emul_alloc_bar(pi, VIRTIO_MODERN_BAR, PCIBAR_MEM32,
+ VTMOD_BAR_SIZE);
+ if (error != 0)
+ return (error);
+
+ /* VIRTIO_PCI_CAP_COMMON_CFG */
+ bzero(&cap, sizeof(cap));
+ cap.cap_vndr = PCIY_VENDOR;
+ cap.cap_len = sizeof(cap);
+ cap.cfg_type = VIRTIO_PCI_CAP_COMMON_CFG;
+ cap.bar = VIRTIO_MODERN_BAR;
+ cap.offset = VTMOD_COMMON_CFG_OFFSET;
+ cap.length = VTMOD_COMMON_CFG_LEN;
+ error = pci_emul_add_capability(pi, (u_char *)&cap, sizeof(cap));
+ if (error != 0)
+ return (error);
+
+ /* VIRTIO_PCI_CAP_NOTIFY_CFG */
+ bzero(&ncap, sizeof(ncap));
+ ncap.cap.cap_vndr = PCIY_VENDOR;
+ ncap.cap.cap_len = sizeof(ncap);
+ ncap.cap.cfg_type = VIRTIO_PCI_CAP_NOTIFY_CFG;
+ ncap.cap.bar = VIRTIO_MODERN_BAR;
+ ncap.cap.offset = VTMOD_NOTIFY_OFFSET;
+ ncap.cap.length = notify_len;
+ ncap.notify_off_multiplier = VTMOD_NOTIFY_MULT;
+ error = pci_emul_add_capability(pi, (u_char *)&ncap, sizeof(ncap));
+ if (error != 0)
+ return (error);
+
+ /* VIRTIO_PCI_CAP_ISR_CFG */
+ bzero(&cap, sizeof(cap));
+ cap.cap_vndr = PCIY_VENDOR;
+ cap.cap_len = sizeof(cap);
+ cap.cfg_type = VIRTIO_PCI_CAP_ISR_CFG;
+ cap.bar = VIRTIO_MODERN_BAR;
+ cap.offset = VTMOD_ISR_CFG_OFFSET;
+ cap.length = VTMOD_ISR_CFG_LEN;
+ error = pci_emul_add_capability(pi, (u_char *)&cap, sizeof(cap));
+ if (error != 0)
+ return (error);
+
+ /* VIRTIO_PCI_CAP_DEVICE_CFG (only if device has config space) */
+ if (allow_device_cfg && vc->vc_cfgsize > 0) {
+ bzero(&cap, sizeof(cap));
+ cap.cap_vndr = PCIY_VENDOR;
+ cap.cap_len = sizeof(cap);
+ cap.cfg_type = VIRTIO_PCI_CAP_DEVICE_CFG;
+ cap.bar = VIRTIO_MODERN_BAR;
+ cap.offset = VTMOD_DEV_CFG_OFFSET;
+ cap.length = (uint32_t)vc->vc_cfgsize;
+ error = pci_emul_add_capability(pi, (u_char *)&cap,
+ sizeof(cap));
+ if (error != 0)
+ return (error);
+ }
+
+ /* VIRTIO_PCI_CAP_PCI_CFG (required by spec; provides alternative BAR access) */
+ bzero(&ccap, sizeof(ccap));
+ ccap.cap.cap_vndr = PCIY_VENDOR;
+ ccap.cap.cap_len = sizeof(ccap);
+ ccap.cap.cfg_type = VIRTIO_PCI_CAP_PCI_CFG;
+ ccap.cap.bar = 0;
+ ccap.cap.offset = 0;
+ ccap.cap.length = 0;
+ error = pci_emul_add_capability(pi, (u_char *)&ccap, sizeof(ccap));
+ if (error != 0)
+ return (error);
+
+ vs->vs_flags |= VIRTIO_MODERN;
+ return (0);
+}
+
+/*
+ * Initialize a virtqueue from 64-bit modern addresses (called when the
+ * driver sets queue_enable = 1 in common_cfg).
+ */
+static void
+vi_vq_init_modern(struct virtio_softc *vs, struct vqueue_info *vq)
+{
+ struct vmctx *ctx = vs->vs_pi->pi_vmctx;
+ size_t desc_size, avail_size, used_size;
+
+ desc_size = (size_t)vq->vq_qsize * sizeof(struct vring_desc);
+ avail_size = (2 + (size_t)vq->vq_qsize + 1) * sizeof(uint16_t);
+ used_size = (2 + 2 * (size_t)vq->vq_qsize + 1) * sizeof(uint16_t);
+
+ vq->vq_desc = paddr_guest2host(ctx, vq->vq_desc_addr, desc_size);
+ vq->vq_avail = paddr_guest2host(ctx, vq->vq_avail_addr, avail_size);
+ vq->vq_used = paddr_guest2host(ctx, vq->vq_used_addr, used_size);
+
+ vq->vq_flags = VQ_ALLOC;
+ vq->vq_last_avail = 0;
+ vq->vq_next_used = 0;
+ vq->vq_save_used = 0;
+}
+
+/*
+ * Signal a device configuration change to the driver. Increments
+ * config_generation so the driver's consistency loop detects the change,
+ * then delivers the config interrupt.
+ */
+void
+vi_config_changed(struct virtio_softc *vs)
+{
+ VS_LOCK(vs);
+ vs->vs_config_gen++;
+ VS_UNLOCK(vs);
+ vi_interrupt(vs, VIRTIO_PCI_ISR_CONFIG, vs->vs_msix_cfg_idx);
+}
+
+/*
+ * Read handler for the common_cfg region of the modern MMIO BAR.
+ * off is the byte offset within common_cfg (relative to VTMOD_COMMON_CFG_OFFSET).
+ */
+static uint64_t
+vi_common_cfg_read(struct virtio_softc *vs, uint32_t off)
+{
+ struct virtio_consts *vc = vs->vs_vc;
+ struct vqueue_info *vq;
+ uint64_t value = 0;
+
+ switch (off) {
+ case VIRTIO_PCI_COMMON_DFSELECT:
+ value = vs->vs_dev_feature_sel;
+ break;
+ case VIRTIO_PCI_COMMON_DF:
+ if (vs->vs_dev_feature_sel == 0)
+ value = (uint32_t)vc->vc_hv_caps;
+ else if (vs->vs_dev_feature_sel == 1)
+ value = (uint32_t)(vc->vc_hv_caps >> 32);
+ break;
+ case VIRTIO_PCI_COMMON_GFSELECT:
+ value = vs->vs_drv_feature_sel;
+ break;
+ case VIRTIO_PCI_COMMON_GF:
+ if (vs->vs_drv_feature_sel == 0)
+ value = (uint32_t)vs->vs_negotiated_caps;
+ else if (vs->vs_drv_feature_sel == 1)
+ value = (uint32_t)(vs->vs_negotiated_caps >> 32);
+ break;
+ case VIRTIO_PCI_COMMON_MSIX:
+ value = vs->vs_msix_cfg_idx;
+ break;
+ case VIRTIO_PCI_COMMON_NUMQ:
+ value = (uint16_t)vc->vc_nvq;
+ break;
+ case VIRTIO_PCI_COMMON_STATUS:
+ value = vs->vs_status;
+ break;
+ case VIRTIO_PCI_COMMON_CFGGENERATION:
+ value = vs->vs_config_gen;
+ break;
+ case VIRTIO_PCI_COMMON_Q_SELECT:
+ value = (uint16_t)vs->vs_curq;
+ break;
+ case VIRTIO_PCI_COMMON_Q_SIZE:
+ value = vs->vs_curq < vc->vc_nvq ?
+ vs->vs_queues[vs->vs_curq].vq_qsize :
+ 0;
+ break;
+ case VIRTIO_PCI_COMMON_Q_MSIX:
+ value = vs->vs_curq < vc->vc_nvq ?
+ vs->vs_queues[vs->vs_curq].vq_msix_idx :
+ VIRTIO_MSI_NO_VECTOR;
+ break;
+ case VIRTIO_PCI_COMMON_Q_ENABLE:
+ value = (vs->vs_curq < vc->vc_nvq) ?
+ vs->vs_queues[vs->vs_curq].vq_enabled :
+ 0;
+ break;
+ case VIRTIO_PCI_COMMON_Q_NOFF:
+ /* queue_notify_off = queue index; multiplier = VTMOD_NOTIFY_MULT */
+ value = (vs->vs_curq < vc->vc_nvq) ? (uint16_t)vs->vs_curq : 0;
+ break;
+ case VIRTIO_PCI_COMMON_Q_DESCLO:
+ if (vs->vs_curq < vc->vc_nvq) {
+ vq = &vs->vs_queues[vs->vs_curq];
+ value = (uint32_t)vq->vq_desc_addr;
+ }
+ break;
+ case VIRTIO_PCI_COMMON_Q_DESCHI:
+ if (vs->vs_curq < vc->vc_nvq) {
+ vq = &vs->vs_queues[vs->vs_curq];
+ value = (uint32_t)(vq->vq_desc_addr >> 32);
+ }
+ break;
+ case VIRTIO_PCI_COMMON_Q_AVAILLO:
+ if (vs->vs_curq < vc->vc_nvq) {
+ vq = &vs->vs_queues[vs->vs_curq];
+ value = (uint32_t)vq->vq_avail_addr;
+ }
+ break;
+ case VIRTIO_PCI_COMMON_Q_AVAILHI:
+ if (vs->vs_curq < vc->vc_nvq) {
+ vq = &vs->vs_queues[vs->vs_curq];
+ value = (uint32_t)(vq->vq_avail_addr >> 32);
+ }
+ break;
+ case VIRTIO_PCI_COMMON_Q_USEDLO:
+ if (vs->vs_curq < vc->vc_nvq) {
+ vq = &vs->vs_queues[vs->vs_curq];
+ value = (uint32_t)vq->vq_used_addr;
+ }
+ break;
+ case VIRTIO_PCI_COMMON_Q_USEDHI:
+ if (vs->vs_curq < vc->vc_nvq) {
+ vq = &vs->vs_queues[vs->vs_curq];
+ value = (uint32_t)(vq->vq_used_addr >> 32);
+ }
+ break;
+ }
+ return (value);
+}
+
+/*
+ * Write handler for the common_cfg region of the modern MMIO BAR.
+ * off is the byte offset within common_cfg (relative to VTMOD_COMMON_CFG_OFFSET).
+ */
+static void
+vi_common_cfg_write(struct virtio_softc *vs, uint32_t off, uint64_t value)
+{
+ struct virtio_consts *vc = vs->vs_vc;
+ struct vqueue_info *vq;
+ uint64_t mask, bits;
+
+ switch (off) {
+ case VIRTIO_PCI_COMMON_DFSELECT:
+ vs->vs_dev_feature_sel = (uint32_t)value;
+ break;
+ case VIRTIO_PCI_COMMON_DF:
+ /* device_feature is read-only */
+ break;
+ case VIRTIO_PCI_COMMON_GFSELECT:
+ vs->vs_drv_feature_sel = (uint32_t)value;
+ break;
+ case VIRTIO_PCI_COMMON_GF:
+ if (vs->vs_drv_feature_sel <= 1) {
+ mask = (uint64_t)0xFFFFFFFF
+ << (vs->vs_drv_feature_sel * 32);
+ bits = ((uint64_t)(uint32_t)value)
+ << (vs->vs_drv_feature_sel * 32);
+ vs->vs_negotiated_caps = (vs->vs_negotiated_caps &
+ ~mask) |
+ (bits & vc->vc_hv_caps);
+ }
+ break;
+ case VIRTIO_PCI_COMMON_MSIX:
+ vs->vs_msix_cfg_idx = (uint16_t)value;
+ break;
+ case VIRTIO_PCI_COMMON_NUMQ:
+ /* num_queues is read-only */
+ break;
+ case VIRTIO_PCI_COMMON_STATUS: {
+ uint8_t old_status = vs->vs_status;
+ vs->vs_status = (uint8_t)value;
+ if (vs->vs_status == 0) {
+ (*vc->vc_reset)(DEV_SOFTC(vs));
+ vs->vs_config_gen++;
+ } else if ((vs->vs_status & VIRTIO_CONFIG_S_FEATURES_OK) != 0 &&
+ (old_status & VIRTIO_CONFIG_S_FEATURES_OK) == 0) {
+ if (vc->vc_apply_features != NULL)
+ (*vc->vc_apply_features)(DEV_SOFTC(vs),
+ vs->vs_negotiated_caps);
+ }
+ break;
+ }
+ case VIRTIO_PCI_COMMON_CFGGENERATION:
+ /* config_generation is read-only */
+ break;
+ case VIRTIO_PCI_COMMON_Q_SELECT:
+ vs->vs_curq = (int)(uint16_t)value;
+ break;
+ case VIRTIO_PCI_COMMON_Q_SIZE:
+ if (vs->vs_curq < vc->vc_nvq) {
+ vq = &vs->vs_queues[vs->vs_curq];
+ if ((uint16_t)value <= vq->vq_max_qsize &&
+ (uint16_t)value > 0)
+ vq->vq_qsize = (uint16_t)value;
+ }
+ break;
+ case VIRTIO_PCI_COMMON_Q_MSIX:
+ if (vs->vs_curq < vc->vc_nvq)
+ vs->vs_queues[vs->vs_curq].vq_msix_idx = (uint16_t)
+ value;
+ break;
+ case VIRTIO_PCI_COMMON_Q_ENABLE:
+ if (vs->vs_curq >= vc->vc_nvq)
+ break;
+ vq = &vs->vs_queues[vs->vs_curq];
+ if ((uint16_t)value == 1) {
+ vq->vq_enabled = true;
+ if (vq->vq_desc_addr != 0 && vq->vq_avail_addr != 0 &&
+ vq->vq_used_addr != 0)
+ vi_vq_init_modern(vs, vq);
+ } else {
+ vq->vq_enabled = false;
+ vq->vq_flags &= ~VQ_ALLOC;
+ }
+ break;
+ case VIRTIO_PCI_COMMON_Q_NOFF:
+ /* queue_notify_off is read-only */
+ break;
+ case VIRTIO_PCI_COMMON_Q_DESCLO:
+ if (vs->vs_curq < vc->vc_nvq) {
+ vq = &vs->vs_queues[vs->vs_curq];
+ vq->vq_desc_addr = (vq->vq_desc_addr &
+ 0xFFFFFFFF00000000ULL) |
+ (uint64_t)(uint32_t)value;
+ }
+ break;
+ case VIRTIO_PCI_COMMON_Q_DESCHI:
+ if (vs->vs_curq < vc->vc_nvq) {
+ vq = &vs->vs_queues[vs->vs_curq];
+ vq->vq_desc_addr = (vq->vq_desc_addr &
+ 0x00000000FFFFFFFFULL) |
+ ((uint64_t)(uint32_t)value << 32);
+ }
+ break;
+ case VIRTIO_PCI_COMMON_Q_AVAILLO:
+ if (vs->vs_curq < vc->vc_nvq) {
+ vq = &vs->vs_queues[vs->vs_curq];
+ vq->vq_avail_addr = (vq->vq_avail_addr &
+ 0xFFFFFFFF00000000ULL) |
+ (uint64_t)(uint32_t)value;
+ }
+ break;
+ case VIRTIO_PCI_COMMON_Q_AVAILHI:
+ if (vs->vs_curq < vc->vc_nvq) {
+ vq = &vs->vs_queues[vs->vs_curq];
+ vq->vq_avail_addr = (vq->vq_avail_addr &
+ 0x00000000FFFFFFFFULL) |
+ ((uint64_t)(uint32_t)value << 32);
+ }
+ break;
+ case VIRTIO_PCI_COMMON_Q_USEDLO:
+ if (vs->vs_curq < vc->vc_nvq) {
+ vq = &vs->vs_queues[vs->vs_curq];
+ vq->vq_used_addr = (vq->vq_used_addr &
+ 0xFFFFFFFF00000000ULL) |
+ (uint64_t)(uint32_t)value;
+ }
+ break;
+ case VIRTIO_PCI_COMMON_Q_USEDHI:
+ if (vs->vs_curq < vc->vc_nvq) {
+ vq = &vs->vs_queues[vs->vs_curq];
+ vq->vq_used_addr = (vq->vq_used_addr &
+ 0x00000000FFFFFFFFULL) |
+ ((uint64_t)(uint32_t)value << 32);
+ }
+ break;
+ }
+}
+
+/*
+ * Read handler for the ISR region of the modern MMIO BAR.
+ * Reading clears the ISR and de-asserts the legacy interrupt line.
+ */
+static uint64_t
+vi_isr_cfg_read(struct virtio_softc *vs)
+{
+ uint64_t value = vs->vs_isr;
+
+ vs->vs_isr = 0;
+ if (value)
+ pci_lintr_deassert(vs->vs_pi);
+ return (value);
+}
+
+/*
+ * Read handler for the device-specific config region of the modern MMIO BAR.
+ * off is the byte offset within the device config (relative to VTMOD_DEV_CFG_OFFSET).
+ */
+static uint64_t
+vi_dev_cfg_read(struct virtio_softc *vs, uint32_t off, int size)
+{
+ struct virtio_consts *vc = vs->vs_vc;
+ uint32_t val32 = 0;
+
+ if (vc->vc_cfgread != NULL)
+ (*vc->vc_cfgread)(DEV_SOFTC(vs), off, size, &val32);
+ return (val32);
+}
+
+/*
+ * Write handler for the device-specific config region of the modern MMIO BAR.
+ * off is the byte offset within the device config (relative to VTMOD_DEV_CFG_OFFSET).
+ */
+static void
+vi_dev_cfg_write(struct virtio_softc *vs, uint32_t off, int size,
+ uint64_t value)
+{
+ struct virtio_consts *vc = vs->vs_vc;
+
+ if (vc->vc_cfgwrite != NULL)
+ (*vc->vc_cfgwrite)(DEV_SOFTC(vs), off, size, (uint32_t)value);
+}
+
+/*
+ * Write handler for the notify region of the modern MMIO BAR.
+ * off is the byte offset within the notify region (relative to VTMOD_NOTIFY_OFFSET).
+ */
+static void
+vi_notify_cfg_write(struct virtio_softc *vs, uint32_t off)
+{
+ struct virtio_consts *vc = vs->vs_vc;
+ struct vqueue_info *vq;
+ uint32_t qidx;
+
+ qidx = off / VTMOD_NOTIFY_MULT;
+ if (qidx < (uint32_t)vc->vc_nvq) {
+ vq = &vs->vs_queues[qidx];
+ if (vq->vq_notify != NULL)
+ (*vq->vq_notify)(DEV_SOFTC(vs), vq);
+ else if (vc->vc_qnotify != NULL)
+ (*vc->vc_qnotify)(DEV_SOFTC(vs), vq);
+ }
+}
+
+/*
+ * Read handler for the modern MMIO BAR (BAR 2).
+ */
+static uint64_t
+vi_modern_bar_read(struct virtio_softc *vs, uint64_t offset, int size)
+{
+ struct virtio_consts *vc = vs->vs_vc;
+ uint64_t value = 0;
+
+ if (offset >= VTMOD_COMMON_CFG_OFFSET &&
+ offset < VTMOD_COMMON_CFG_OFFSET + VTMOD_COMMON_CFG_LEN)
+ value = vi_common_cfg_read(vs,
+ (uint32_t)(offset - VTMOD_COMMON_CFG_OFFSET));
+ else if (offset >= VTMOD_ISR_CFG_OFFSET &&
+ offset < VTMOD_ISR_CFG_OFFSET + VTMOD_ISR_CFG_LEN)
+ value = vi_isr_cfg_read(vs);
+ else if (vc->vc_cfgsize > 0 && offset >= VTMOD_DEV_CFG_OFFSET &&
+ offset < VTMOD_DEV_CFG_OFFSET + vc->vc_cfgsize)
+ value = vi_dev_cfg_read(vs,
+ (uint32_t)(offset - VTMOD_DEV_CFG_OFFSET), size);
+ /* Notify region reads return 0 (write-only doorbell) */
+
+ if (size == 1)
+ value &= 0xFF;
+ else if (size == 2)
+ value &= 0xFFFF;
+ else if (size == 4)
+ value &= 0xFFFFFFFF;
+ return (value);
+}
+
+/*
+ * Write handler for the modern MMIO BAR (BAR 2).
+ */
+static void
+vi_modern_bar_write(struct virtio_softc *vs, uint64_t offset, int size,
+ uint64_t value)
+{
+ struct virtio_consts *vc = vs->vs_vc;
+
+ if (offset >= VTMOD_COMMON_CFG_OFFSET &&
+ offset < VTMOD_COMMON_CFG_OFFSET + VTMOD_COMMON_CFG_LEN)
+ vi_common_cfg_write(vs,
+ (uint32_t)(offset - VTMOD_COMMON_CFG_OFFSET), value);
+ else if (offset >= VTMOD_ISR_CFG_OFFSET &&
+ offset < VTMOD_ISR_CFG_OFFSET + VTMOD_ISR_CFG_LEN)
+ /* ISR region is read-only; writes are ignored */;
+ else if (vc->vc_cfgsize > 0 && offset >= VTMOD_DEV_CFG_OFFSET &&
+ offset < VTMOD_DEV_CFG_OFFSET + vc->vc_cfgsize)
+ vi_dev_cfg_write(vs, (uint32_t)(offset - VTMOD_DEV_CFG_OFFSET),
+ size, value);
+ else if (offset >= VTMOD_NOTIFY_OFFSET &&
+ offset <
+ VTMOD_NOTIFY_OFFSET + (uint32_t)vc->vc_nvq * VTMOD_NOTIFY_MULT)
+ vi_notify_cfg_write(vs,
+ (uint32_t)(offset - VTMOD_NOTIFY_OFFSET));
+}
+
/*
* Initialize MSI-X vector capabilities if we're to use MSI-X,
* or MSI capabilities if not.
@@ -580,8 +1091,21 @@
}
}
- /* XXX probably should do something better than just assert() */
- assert(baridx == 0);
+ /* Dispatch modern MMIO BAR reads */
+ if (baridx == VIRTIO_MODERN_BAR &&
+ (vs->vs_flags & VIRTIO_MODERN) != 0) {
+ uint64_t val;
+ if (vs->vs_mtx)
+ pthread_mutex_lock(vs->vs_mtx);
+ val = vi_modern_bar_read(vs, offset, size);
+ if (vs->vs_mtx)
+ pthread_mutex_unlock(vs->vs_mtx);
+ return (val);
+ }
+
+ /* Legacy BAR 0 */
+ if (baridx != 0)
+ return (size == 1 ? 0xff : size == 2 ? 0xffff : 0xffffffff);
if (vs->vs_mtx)
pthread_mutex_lock(vs->vs_mtx);
@@ -701,8 +1225,20 @@
}
}
- /* XXX probably should do something better than just assert() */
- assert(baridx == 0);
+ /* Dispatch modern MMIO BAR writes */
+ if (baridx == VIRTIO_MODERN_BAR &&
+ (vs->vs_flags & VIRTIO_MODERN) != 0) {
+ if (vs->vs_mtx)
+ pthread_mutex_lock(vs->vs_mtx);
+ vi_modern_bar_write(vs, offset, size, value);
+ if (vs->vs_mtx)
+ pthread_mutex_unlock(vs->vs_mtx);
+ return;
+ }
+
+ /* Legacy BAR 0 */
+ if (baridx != 0)
+ return;
if (vs->vs_mtx)
pthread_mutex_lock(vs->vs_mtx);
@@ -755,7 +1291,10 @@
switch (offset) {
case VIRTIO_PCI_GUEST_FEATURES:
- vs->vs_negotiated_caps = value & vc->vc_hv_caps;
+ /* Legacy: only lower 32 bits */
+ vs->vs_negotiated_caps = (vs->vs_negotiated_caps &
+ 0xFFFFFFFF00000000ULL) |
+ ((uint64_t)(uint32_t)value & vc->vc_hv_caps);
if (vc->vc_apply_features)
(*vc->vc_apply_features)(DEV_SOFTC(vs),
vs->vs_negotiated_caps);

File Metadata

Mime Type
text/plain
Expires
Wed, Jun 17, 7:56 PM (6 h, 14 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
33656246
Default Alt Text
D57364.id178996.diff (21 KB)

Event Timeline