Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F159663932
D57364.id178996.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
21 KB
Referenced Files
None
Subscribers
None
D57364.id178996.diff
View Options
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
Details
Attached
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)
Attached To
Mode
D57364: bhyve: Implement virtio 1.0
Attached
Detach File
Event Timeline
Log In to Comment