Page MenuHomeFreeBSD

D29708.1789457270.diff
No OneTemporary

Size
51 KB
Referenced Files
None
Subscribers
None

D29708.1789457270.diff

Index: usr.sbin/bhyve/pci_emul.h
===================================================================
--- usr.sbin/bhyve/pci_emul.h
+++ usr.sbin/bhyve/pci_emul.h
@@ -228,6 +228,8 @@
void pci_callback(void);
int pci_emul_alloc_bar(struct pci_devinst *pdi, int idx,
enum pcibar_type type, uint64_t size);
+int pci_emul_add_capability(struct pci_devinst *pi, u_char *capdata, int caplen,
+ int *capoffp);
int pci_emul_add_msicap(struct pci_devinst *pi, int msgnum);
int pci_emul_add_pciecap(struct pci_devinst *pi, int pcie_device_type);
void pci_emul_capwrite(struct pci_devinst *pi, int offset, int bytes,
Index: usr.sbin/bhyve/pci_emul.c
===================================================================
--- usr.sbin/bhyve/pci_emul.c
+++ usr.sbin/bhyve/pci_emul.c
@@ -727,8 +727,9 @@
}
#define CAP_START_OFFSET 0x40
-static int
-pci_emul_add_capability(struct pci_devinst *pi, u_char *capdata, int caplen)
+int
+pci_emul_add_capability(struct pci_devinst *pi, u_char *capdata, int caplen,
+ int *capoffp)
{
int i, capoff, reallen;
uint16_t sts;
@@ -763,6 +764,9 @@
pi->pi_prevcap = capoff;
pi->pi_capend = capoff + reallen - 1;
+
+ if (capoffp != NULL)
+ *capoffp = capoff;
return (0);
}
@@ -839,7 +843,8 @@
pci_populate_msicap(&msicap, msgnum, 0);
- return (pci_emul_add_capability(pi, (u_char *)&msicap, sizeof(msicap)));
+ return (pci_emul_add_capability(pi, (u_char *)&msicap, sizeof(msicap),
+ NULL));
}
static void
@@ -914,7 +919,7 @@
tab_size + pi->pi_msix.pba_size);
return (pci_emul_add_capability(pi, (u_char *)&msixcap,
- sizeof(msixcap)));
+ sizeof(msixcap), NULL));
}
static void
@@ -1014,7 +1019,8 @@
pciecap.link_status = 0x11; /* gen1, x1 */
}
- err = pci_emul_add_capability(pi, (u_char *)&pciecap, sizeof(pciecap));
+ err = pci_emul_add_capability(pi, (u_char *)&pciecap, sizeof(pciecap),
+ NULL);
return (err);
}
Index: usr.sbin/bhyve/pci_virtio_9p.c
===================================================================
--- usr.sbin/bhyve/pci_virtio_9p.c
+++ usr.sbin/bhyve/pci_virtio_9p.c
@@ -337,7 +337,7 @@
if (vi_intr_init(&sc->vsc_vs, 1, fbsdrun_virtio_msix()))
return (1);
- vi_set_io_bar(&sc->vsc_vs, 0);
+ vi_setup_pci_bar(&sc->vsc_vs, VIRTIO_LEGACY_BAR);
return (0);
}
@@ -346,6 +346,8 @@
.pe_emu = "virtio-9p",
.pe_legacy_config = pci_vt9p_legacy_config,
.pe_init = pci_vt9p_init,
+ .pe_cfgwrite = vi_pci_cfgwrite,
+ .pe_cfgread = vi_pci_cfgread,
.pe_barwrite = vi_pci_write,
.pe_barread = vi_pci_read
};
Index: usr.sbin/bhyve/pci_virtio_block.c
===================================================================
--- usr.sbin/bhyve/pci_virtio_block.c
+++ usr.sbin/bhyve/pci_virtio_block.c
@@ -540,7 +540,7 @@
free(sc);
return (1);
}
- vi_set_io_bar(&sc->vbsc_vs, 0);
+ vi_setup_pci_bar(&sc->vbsc_vs, VIRTIO_LEGACY_BAR);
return (0);
}
@@ -568,6 +568,8 @@
.pe_emu = "virtio-blk",
.pe_init = pci_vtblk_init,
.pe_legacy_config = blockif_legacy_config,
+ .pe_cfgwrite = vi_pci_cfgwrite,
+ .pe_cfgread = vi_pci_cfgread,
.pe_barwrite = vi_pci_write,
.pe_barread = vi_pci_read,
#ifdef BHYVE_SNAPSHOT
Index: usr.sbin/bhyve/pci_virtio_console.c
===================================================================
--- usr.sbin/bhyve/pci_virtio_console.c
+++ usr.sbin/bhyve/pci_virtio_console.c
@@ -717,7 +717,7 @@
if (vi_intr_init(&sc->vsc_vs, 1, fbsdrun_virtio_msix()))
return (1);
- vi_set_io_bar(&sc->vsc_vs, 0);
+ vi_setup_pci_bar(&sc->vsc_vs, VIRTIO_LEGACY_BAR);
/* create control port */
sc->vsc_control_port.vsp_sc = sc;
@@ -753,6 +753,8 @@
struct pci_devemu pci_de_vcon = {
.pe_emu = "virtio-console",
.pe_init = pci_vtcon_init,
+ .pe_cfgwrite = vi_pci_cfgwrite,
+ .pe_cfgread = vi_pci_cfgread,
.pe_barwrite = vi_pci_write,
.pe_barread = vi_pci_read
};
Index: usr.sbin/bhyve/pci_virtio_net.c
===================================================================
--- usr.sbin/bhyve/pci_virtio_net.c
+++ usr.sbin/bhyve/pci_virtio_net.c
@@ -653,8 +653,8 @@
return (1);
}
- /* use BAR 0 to map config regs in IO space */
- vi_set_io_bar(&sc->vsc_vs, 0);
+ /* Virtio-legacy: use BAR 0 to map config regs in IO space */
+ vi_setup_pci_bar(&sc->vsc_vs, VIRTIO_LEGACY_BAR);
sc->resetting = 0;
@@ -808,6 +808,8 @@
.pe_emu = "virtio-net",
.pe_init = pci_vtnet_init,
.pe_legacy_config = netbe_legacy_config,
+ .pe_cfgwrite = vi_pci_cfgwrite,
+ .pe_cfgread = vi_pci_cfgread,
.pe_barwrite = vi_pci_write,
.pe_barread = vi_pci_read,
#ifdef BHYVE_SNAPSHOT
Index: usr.sbin/bhyve/pci_virtio_rnd.c
===================================================================
--- usr.sbin/bhyve/pci_virtio_rnd.c
+++ usr.sbin/bhyve/pci_virtio_rnd.c
@@ -195,7 +195,7 @@
if (vi_intr_init(&sc->vrsc_vs, 1, fbsdrun_virtio_msix()))
return (1);
- vi_set_io_bar(&sc->vrsc_vs, 0);
+ vi_setup_pci_bar(&sc->vrsc_vs, VIRTIO_LEGACY_BAR);
return (0);
}
@@ -204,6 +204,8 @@
struct pci_devemu pci_de_vrnd = {
.pe_emu = "virtio-rnd",
.pe_init = pci_vtrnd_init,
+ .pe_cfgwrite = vi_pci_cfgwrite,
+ .pe_cfgread = vi_pci_cfgread,
.pe_barwrite = vi_pci_write,
.pe_barread = vi_pci_read,
#ifdef BHYVE_SNAPSHOT
Index: usr.sbin/bhyve/pci_virtio_scsi.c
===================================================================
--- usr.sbin/bhyve/pci_virtio_scsi.c
+++ usr.sbin/bhyve/pci_virtio_scsi.c
@@ -722,7 +722,7 @@
if (vi_intr_init(&sc->vss_vs, 1, fbsdrun_virtio_msix()))
return (1);
- vi_set_io_bar(&sc->vss_vs, 0);
+ vi_setup_pci_bar(&sc->vss_vs, VIRTIO_LEGACY_BAR);
return (0);
}
@@ -732,6 +732,8 @@
.pe_emu = "virtio-scsi",
.pe_init = pci_vtscsi_init,
.pe_legacy_config = pci_vtscsi_legacy_config,
+ .pe_cfgwrite = vi_pci_cfgwrite,
+ .pe_cfgread = vi_pci_cfgread,
.pe_barwrite = vi_pci_write,
.pe_barread = vi_pci_read
};
Index: usr.sbin/bhyve/virtio.h
===================================================================
--- usr.sbin/bhyve/virtio.h
+++ usr.sbin/bhyve/virtio.h
@@ -3,6 +3,10 @@
*
* Copyright (c) 2013 Chris Torek <torek @ torek net>
* All rights reserved.
+ * Copyright (c) 2021 The FreeBSD Foundation
+ *
+ * Portions of this software were developed by Ka Ho Ng
+ * under sponsorship of the FreeBSD Foundation.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -38,14 +42,17 @@
#include <dev/virtio/pci/virtio_pci_var.h>
/*
- * These are derived from several virtio specifications.
- *
- * Some useful links:
+ * Virtio legacy supports are derived from several specifications below:
* https://github.com/rustyrussell/virtio-spec
* http://people.redhat.com/pbonzini/virtio-spec.pdf
+ *
+ * Virtio modern supports is authored with the reference below:
+ * https://docs.oasis-open.org/virtio/virtio/v1.1/cs01/virtio-v1.1-cs01.pdf
*/
/*
+ * Virtio legacy:
+ *
* A virtual device has zero or more "virtual queues" (virtqueue).
* Each virtqueue uses at least two 4096-byte pages, laid out thus:
*
@@ -130,6 +137,8 @@
#define VRING_ALIGN 4096
/*
+ * Virtio legacy:
+ *
* The address of any given virtual queue is determined by a single
* Page Frame Number register. The guest writes the PFN into the
* PCI config space. However, a device that has two or more
@@ -185,6 +194,8 @@
struct vm_snapshot_meta;
/*
+ * Virtio legacy:
+ *
* A virtual device, with some number (possibly 0) of virtual
* queues and some size (possibly 0) of configuration-space
* registers private to the device. The virtio_softc should come
@@ -213,22 +224,46 @@
*
* The BROKED flag ("this thing done gone and broked") is for future
* use.
+ *
+ *
+ * Virtio modern:
+ *
+ * The DFSELECT_HI flag indicates the device should return the high-part of
+ * the hypervisor-provided capabilities. The GFSELECT_HI flag indicates the
+ * device should return the high-part of the guest driver's capabilities.
*/
#define VIRTIO_USE_MSIX 0x01
#define VIRTIO_EVENT_IDX 0x02 /* use the event-index values */
+#define VIRTIO_DEVCFG_CHG 0x04 /* Device configuration changed */
#define VIRTIO_BROKED 0x08 /* ??? */
+#define VIRTIO_DFSELECT_HI 0x10 /* return high-part of host cap */
+#define VIRTIO_GFSELECT_HI 0x20 /* return high-part of guest cap */
+
+struct virtio_pci_cfg {
+ int c_captype;
+ int c_baridx;
+ uint32_t c_offset;
+ uint32_t c_size;
+ int c_capoff;
+ int c_caplen;
+};
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 */
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 */
+ uint8_t vs_devcfg_gen; /* Generation of device config space */
+ struct virtio_pci_cfg vs_cfgs[5]; /* Configurations */
+ struct virtio_pci_cfg *vs_pcicfg; /* PCI configuration access
+ cap */
+ int vs_ncfgs; /* Number of PCI configurations */
};
#define VS_LOCK(vs) \
@@ -282,6 +317,7 @@
*/
#define VQ_ALLOC 0x01 /* set once we have a pfn */
#define VQ_BROKED 0x02 /* ??? */
+#define VQ_ENABLED 0x04 /* set if the queue was enabled */
struct vqueue_info {
uint16_t vq_qsize; /* size of this queue (a power of 2) */
void (*vq_notify)(void *, struct vqueue_info *);
@@ -296,19 +332,34 @@
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!) */
+ uint64_t vq_desc_gpa; /* PA of virtqueue descriptors ring */
+ uint64_t vq_avail_gpa; /* PA of virtqueue avail ring */
+ uint64_t vq_used_gpa; /* PA of virtqueue used ring */
volatile struct vring_desc *vq_desc; /* descriptor array */
volatile struct vring_avail *vq_avail; /* the "avail" ring */
volatile struct vring_used *vq_used; /* the "used" ring */
};
-/* as noted above, these are sort of backwards, name-wise */
+/*
+ * As noted above, these are sort of backwards, name-wise.
+ *
+ * Endian helpers must be used when using the following macros.
+ */
#define VQ_AVAIL_EVENT_IDX(vq) \
(*(volatile uint16_t *)&(vq)->vq_used->ring[(vq)->vq_qsize])
#define VQ_USED_EVENT_IDX(vq) \
((vq)->vq_avail->ring[(vq)->vq_qsize])
+/*
+ * Return true if the virtio device is running in modern mode
+ */
+static inline bool
+vi_is_modern(struct virtio_softc *vs)
+{
+ return (vs->vs_negotiated_caps & VIRTIO_F_VERSION_1) != 0;
+}
+
/*
* Is this ring ready for I/O?
*/
@@ -327,8 +378,7 @@
vq_has_descs(struct vqueue_info *vq)
{
- return (vq_ring_ready(vq) && vq->vq_last_avail !=
- vq->vq_avail->idx);
+ return (vq_ring_ready(vq) && vq->vq_last_avail != vq->vq_avail->idx);
}
/*
@@ -338,6 +388,8 @@
static inline void
vq_interrupt(struct virtio_softc *vs, struct vqueue_info *vq)
{
+ if (!(vs->vs_status & VIRTIO_CONFIG_STATUS_DRIVER_OK))
+ return;
if (pci_msix_enabled(vs->vs_pi))
pci_generate_msix(vs->vs_pi, vq->vq_msix_idx);
@@ -350,6 +402,27 @@
}
}
+/*
+ * Deliver an interrupt to guest on device-specific configuration changes
+ * (if possible, or a generic MSI interrupt if not using MSI-X).
+ */
+static inline void
+vq_interrupt_devcfg(struct virtio_softc *vs)
+{
+ if (!(vs->vs_status & VIRTIO_CONFIG_STATUS_DRIVER_OK))
+ return;
+
+ if (pci_msix_enabled(vs->vs_pi))
+ pci_generate_msix(vs->vs_pi, vs->vs_msix_cfg_idx);
+ else {
+ VS_LOCK(vs);
+ vs->vs_isr |= VIRTIO_PCI_ISR_CONFIG;
+ pci_generate_msi(vs->vs_pi, 0);
+ pci_lintr_assert(vs->vs_pi);
+ VS_UNLOCK(vs);
+ }
+}
+
static inline void
vq_kick_enable(struct vqueue_info *vq)
{
@@ -370,6 +443,8 @@
vq->vq_used->flags |= VRING_USED_F_NO_NOTIFY;
}
+#define VIRTIO_LEGACY_BAR 0 /* BAR # to host virtio legacy cfg regs */
+
struct iovec;
/*
@@ -387,8 +462,8 @@
void *dev_softc, struct pci_devinst *pi,
struct vqueue_info *queues);
int vi_intr_init(struct virtio_softc *vs, int barnum, int use_msix);
+void vi_setup_pci_bar(struct virtio_softc *vs, int barnum);
void vi_reset_dev(struct virtio_softc *);
-void vi_set_io_bar(struct virtio_softc *, int);
int vq_getchain(struct vqueue_info *vq, struct iovec *iov, int niov,
struct vi_req *reqp);
@@ -399,6 +474,10 @@
void vq_relchain(struct vqueue_info *vq, uint16_t idx, uint32_t iolen);
void vq_endchains(struct vqueue_info *vq, int used_all_avail);
+int vi_pci_cfgread(struct vmctx *ctx, int vcpu, struct pci_devinst *pi,
+ int offset, int bytes, uint32_t *retval);
+int vi_pci_cfgwrite(struct vmctx *ctx, int vcpu, struct pci_devinst *pi,
+ int offset, int bytes, uint32_t val);
uint64_t vi_pci_read(struct vmctx *ctx, int vcpu, struct pci_devinst *pi,
int baridx, uint64_t offset, int size);
void vi_pci_write(struct vmctx *ctx, int vcpu, struct pci_devinst *pi,
Index: usr.sbin/bhyve/virtio.c
===================================================================
--- usr.sbin/bhyve/virtio.c
+++ usr.sbin/bhyve/virtio.c
@@ -4,6 +4,10 @@
* Copyright (c) 2013 Chris Torek <torek @ torek net>
* All rights reserved.
* Copyright (c) 2019 Joyent, Inc.
+ * Copyright (c) 2021 The FreeBSD Foundation
+ *
+ * Portions of this software were developed by Ka Ho Ng
+ * under sponsorship of the FreeBSD Foundation.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -37,7 +41,9 @@
#include <machine/vmm_snapshot.h>
#include <dev/virtio/pci/virtio_pci_legacy_var.h>
+#include <dev/virtio/pci/virtio_pci_modern_var.h>
+#include <inttypes.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
@@ -52,8 +58,14 @@
/*
* Functions for dealing with generalized "virtual devices" as
* defined by <https://www.google.com/#output=search&q=virtio+spec>
+ *
+ * The reference for the implementation of virtio modern is on
+ * <https://docs.oasis-open.org/virtio/virtio/v1.1/csprd01/virtio-v1.1-csprd01.html>
*/
+/* XXX Make this configurable? */
+#define VQ_NOTIFY_OFF_MULTIPLIER PAGE_SIZE
+
/*
* In case we decide to relax the "virtio softc comes at the
* front of virtio-based device softc" constraint, let's use
@@ -61,6 +73,11 @@
*/
#define DEV_SOFTC(vs) ((void *)(vs))
+static uint64_t vi_modern_pci_read(struct virtio_softc *vs, int vcpu,
+ int baridx, uint64_t offset, int size);
+static void vi_modern_pci_write(struct virtio_softc *vs, int vcpu,
+ int baridx, uint64_t offset, int size, uint64_t value);
+
/*
* Link a virtio_softc to its constants, the device softc, and
* the PCI emulation.
@@ -109,7 +126,7 @@
vq->vq_last_avail = 0;
vq->vq_next_used = 0;
vq->vq_save_used = 0;
- vq->vq_pfn = 0;
+ vq->vq_desc_gpa = vq->vq_avail_gpa = vq->vq_used_gpa = 0;
vq->vq_msix_idx = VIRTIO_MSI_NO_VECTOR;
}
vs->vs_negotiated_caps = 0;
@@ -122,10 +139,10 @@
}
/*
- * Set I/O BAR (usually 0) to map PCI config registers.
+ * Set I/O BAR (usually 0) to map legacy PCI config registers.
*/
-void
-vi_set_io_bar(struct virtio_softc *vs, int barnum)
+static void
+vi_legacy_set_io_bar(struct virtio_softc *vs, int barnum)
{
size_t size;
@@ -137,6 +154,153 @@
pci_emul_alloc_bar(vs->vs_pi, barnum, PCIBAR_IO, size);
}
+/*
+ * Add modern configuration structure capability
+ */
+static inline int
+vi_modern_add_cfg(struct virtio_softc *vs, struct virtio_pci_cap *cap,
+ int barnum, uint32_t off, uint32_t length, uint8_t caplen,
+ uint8_t cfgtype)
+{
+ int capoff;
+
+ cap->cap_vndr = PCIY_VENDOR;
+ cap->cap_len = caplen;
+ cap->cfg_type = cfgtype;
+ cap->bar = barnum;
+ cap->offset = off;
+ cap->length = length;
+ if (pci_emul_add_capability(vs->vs_pi, (u_char *)cap, caplen,
+ &capoff) != 0)
+ return (-1);
+
+ vs->vs_cfgs[vs->vs_ncfgs].c_captype = cfgtype;
+ vs->vs_cfgs[vs->vs_ncfgs].c_baridx = cap->bar;
+ vs->vs_cfgs[vs->vs_ncfgs].c_offset = cap->offset;
+ vs->vs_cfgs[vs->vs_ncfgs].c_size = cap->length;
+ vs->vs_cfgs[vs->vs_ncfgs].c_capoff = capoff;
+ vs->vs_cfgs[vs->vs_ncfgs++].c_caplen = caplen;
+ return (0);
+}
+
+/*
+ * Add COMMON_CFG configuration structure capability
+ */
+static void
+vi_modern_add_common_cfg(struct virtio_softc *vs, int barnum, uint32_t *offp)
+{
+ struct virtio_pci_cap cap;
+ uint32_t cfglen;
+
+ cfglen = roundup2(sizeof(struct virtio_pci_common_cfg), PAGE_SIZE);
+
+ memset(&cap, 0, sizeof(cap));
+ vi_modern_add_cfg(vs, &cap, barnum, *offp, cfglen, sizeof(cap),
+ VIRTIO_PCI_CAP_COMMON_CFG);
+ *offp += cfglen;
+}
+
+/*
+ * Add NOTIFY_CFG configuration structure capability
+ */
+static void
+vi_modern_add_notify_cfg(struct virtio_softc *vs, int barnum, uint32_t *offp)
+{
+ struct virtio_pci_notify_cap cap;
+ uint32_t cfglen;
+
+ cfglen = roundup2(VQ_NOTIFY_OFF_MULTIPLIER * vs->vs_vc->vc_nvq, PAGE_SIZE);
+
+ memset(&cap, 0, sizeof(cap));
+ cap.notify_off_multiplier = VQ_NOTIFY_OFF_MULTIPLIER;
+ vi_modern_add_cfg(vs, &cap.cap, barnum, *offp, cfglen, sizeof(cap),
+ VIRTIO_PCI_CAP_NOTIFY_CFG);
+ *offp += cfglen;
+}
+
+/*
+ * Add ISR_CFG configuration structure capability
+ */
+static void
+vi_modern_add_isr_cfg(struct virtio_softc *vs, int barnum, uint32_t *offp)
+{
+ struct virtio_pci_cap cap;
+
+ memset(&cap, 0, sizeof(cap));
+ vi_modern_add_cfg(vs, &cap, barnum, *offp, PAGE_SIZE, sizeof(cap),
+ VIRTIO_PCI_CAP_ISR_CFG);
+ *offp += PAGE_SIZE;
+}
+
+/*
+ * Add DEV_CFG configuration structure capability
+ */
+static void
+vi_modern_add_dev_cfg(struct virtio_softc *vs, int barnum, uint32_t *offp)
+{
+ struct virtio_pci_cap cap;
+
+ memset(&cap, 0, sizeof(cap));
+ vi_modern_add_cfg(vs, &cap, barnum, *offp, PAGE_SIZE, sizeof(cap),
+ VIRTIO_PCI_CAP_DEVICE_CFG);
+ *offp += PAGE_SIZE;
+}
+
+/*
+ * Add PCI_CFG configuration structure capability
+ */
+static void
+vi_modern_add_pci_cfg(struct virtio_softc *vs)
+{
+ struct virtio_pci_cfg_cap cap;
+
+ memset(&cap, 0, sizeof(cap));
+ memset(cap.pci_cfg_data, 0xff, sizeof(cap.pci_cfg_data));
+ if (vi_modern_add_cfg(vs, &cap.cap, 0, 0, 0,
+ sizeof(cap), VIRTIO_PCI_CAP_PCI_CFG) != 0)
+ return;
+ vs->vs_pcicfg = &vs->vs_cfgs[vs->vs_ncfgs - 1];
+}
+
+/*
+ * Set up Virtio modern device pci configuration space
+ */
+static void
+vi_modern_setup_mem_bar(struct virtio_softc *vs, int barnum)
+{
+ uint32_t size;
+
+ size = 0;
+
+ vi_modern_add_common_cfg(vs, barnum, &size);
+ vi_modern_add_notify_cfg(vs, barnum, &size);
+ vi_modern_add_dev_cfg(vs, barnum, &size);
+ vi_modern_add_isr_cfg(vs, barnum, &size);
+ vi_modern_add_pci_cfg(vs);
+ pci_emul_alloc_bar(vs->vs_pi, barnum, PCIBAR_MEM64, size);
+}
+
+/*
+ * Set up Virtio device pci configuration space.
+ *
+ * If modern is supported, "barnum" MUST NOT be 0.
+ */
+void
+vi_setup_pci_bar(struct virtio_softc *vs, int barnum)
+{
+ struct virtio_consts *vc;
+
+ vc = vs->vs_vc;
+
+ if ((vc->vc_hv_caps & VIRTIO_F_VERSION_1) == 0) {
+ vi_legacy_set_io_bar(vs, barnum);
+ return;
+ }
+ assert(barnum != 0);
+ vi_modern_setup_mem_bar(vs, barnum);
+ vi_legacy_set_io_bar(vs, 0);
+}
+
/*
* Initialize MSI-X vector capabilities if we're to use MSI-X,
* or MSI capabilities if not.
@@ -170,12 +334,11 @@
}
/*
- * Initialize the currently-selected virtio queue (vs->vs_curq).
- * The guest just gave us a page frame number, from which we can
- * calculate the addresses of the queue.
+ * Initialize the currently-selected virtio queue (vs->vs_curq)
+ * for virtio modern device only
*/
-void
-vi_vq_init(struct virtio_softc *vs, uint32_t pfn)
+static void
+vi_vq_init(struct virtio_softc *vs)
{
struct vqueue_info *vq;
uint64_t phys;
@@ -183,23 +346,22 @@
char *base;
vq = &vs->vs_queues[vs->vs_curq];
- vq->vq_pfn = pfn;
- phys = (uint64_t)pfn << VRING_PFN;
- size = vring_size_aligned(vq->vq_qsize);
- base = paddr_guest2host(vs->vs_pi->pi_vmctx, phys, size);
- /* First page(s) are descriptors... */
+ phys = vq->vq_desc_gpa;
+ size = vq->vq_qsize * sizeof(struct vring_desc);
+ base = paddr_guest2host(vs->vs_pi->pi_vmctx, phys, size);
vq->vq_desc = (struct vring_desc *)base;
- base += vq->vq_qsize * sizeof(struct vring_desc);
- /* ... immediately followed by "avail" ring (entirely uint16_t's) */
+ phys = vq->vq_avail_gpa;
+ size = sizeof(struct vring_avail) + sizeof(uint16_t) +
+ vq->vq_qsize * sizeof(uint16_t);
+ base = paddr_guest2host(vs->vs_pi->pi_vmctx, phys, size);
vq->vq_avail = (struct vring_avail *)base;
- base += (2 + vq->vq_qsize + 1) * sizeof(uint16_t);
- /* Then it's rounded up to the next page... */
- base = (char *)roundup2((uintptr_t)base, VRING_ALIGN);
-
- /* ... and the last page(s) are the used ring. */
+ phys = vq->vq_used_gpa;
+ size = sizeof(struct vring_used) + sizeof(uint16_t) +
+ vq->vq_qsize * sizeof(struct vring_used_elem);
+ base = paddr_guest2host(vs->vs_pi->pi_vmctx, phys, size);
vq->vq_used = (struct vring_used *)base;
/* Mark queue as allocated, and start at 0 when we use it. */
@@ -209,14 +371,47 @@
vq->vq_save_used = 0;
}
+/*
+ * Initialize the currently-selected virtio queue (vs->vs_curq).
+ * The guest just gave us a page frame number, from which we can
+ * calculate the addresses of the queue.
+ */
+static void
+vi_legacy_vq_init(struct virtio_softc *vs, uint32_t pfn)
+{
+ struct vqueue_info *vq;
+ uint64_t phys;
+
+ vq = &vs->vs_queues[vs->vs_curq];
+ phys = (uint64_t)pfn << VRING_PFN;
+
+ /* First page(s) are descriptors... */
+ vq->vq_desc_gpa = phys;
+ phys += vq->vq_qsize * sizeof(struct vring_desc);
+ /* ... immediately followed by "avail" ring (entirely uint16_t's) */
+ vq->vq_avail_gpa = phys;
+ phys += sizeof(struct vring_avail) + sizeof(uint16_t) +
+ vq->vq_qsize * sizeof(uint16_t);
+ /* Then it's rounded up to the next page... */
+ phys = roundup2(phys, VRING_ALIGN);
+ /* ... and the last page(s) are the used ring. */
+ vq->vq_used_gpa = phys;
+
+ vi_vq_init(vs);
+}
+
+
/*
* Helper inline for vq_getchain(): record the i'th "real"
* descriptor.
*/
static inline void
-_vq_record(int i, volatile struct vring_desc *vd,
- struct vmctx *ctx, struct iovec *iov, int n_iov,
- struct vi_req *reqp) {
+_vq_record(struct virtio_softc *vs, int i, volatile struct vring_desc *vd,
+ struct iovec *iov, int n_iov, struct vi_req *reqp)
+{
+ struct vmctx *ctx;
+
+ ctx = vs->vs_pi->pi_vmctx;
if (i >= n_iov)
return;
@@ -326,7 +521,7 @@
}
vdir = &vq->vq_desc[next];
if ((vdir->flags & VRING_DESC_F_INDIRECT) == 0) {
- _vq_record(i, vdir, ctx, iov, niov, &req);
+ _vq_record(vs, i, vdir, iov, niov, &req);
i++;
} else if ((vs->vs_vc->vc_hv_caps &
VIRTIO_RING_F_INDIRECT_DESC) == 0) {
@@ -363,7 +558,7 @@
name);
return (-1);
}
- _vq_record(i, vp, ctx, iov, niov, &req);
+ _vq_record(vs, i, vp, iov, niov, &req);
if (++i > VQ_MAX_DESCRIPTORS)
goto loopy;
if ((vp->flags & VRING_DESC_F_NEXT) == 0)
@@ -519,7 +714,7 @@
uint8_t cr_size; /* size (bytes) */
uint8_t cr_ro; /* true => reg is read only */
const char *cr_name; /* name of reg */
-} config_regs[] = {
+} legacy_cfg_regs[] = {
{ VIRTIO_PCI_HOST_FEATURES, 4, 1, "HOST_FEATURES" },
{ VIRTIO_PCI_GUEST_FEATURES, 4, 0, "GUEST_FEATURES" },
{ VIRTIO_PCI_QUEUE_PFN, 4, 0, "QUEUE_PFN" },
@@ -530,18 +725,38 @@
{ VIRTIO_PCI_ISR, 1, 0, "ISR" },
{ VIRTIO_MSI_CONFIG_VECTOR, 2, 0, "CONFIG_VECTOR" },
{ VIRTIO_MSI_QUEUE_VECTOR, 2, 0, "QUEUE_VECTOR" },
+}, common_cfg_regs[] = {
+ { VIRTIO_PCI_COMMON_DFSELECT, 4, 0, "DFSELECT" },
+ { VIRTIO_PCI_COMMON_DF, 4, 1, "DF" },
+ { VIRTIO_PCI_COMMON_GFSELECT, 4, 0, "GFSELECT" },
+ { VIRTIO_PCI_COMMON_GF, 4, 0, "GF" },
+ { VIRTIO_PCI_COMMON_MSIX, 2, 0, "MSIX" },
+ { VIRTIO_PCI_COMMON_NUMQ, 2, 1, "NUMQ" },
+ { VIRTIO_PCI_COMMON_STATUS, 1, 0, "STATUS" },
+ { VIRTIO_PCI_COMMON_CFGGENERATION, 1, 1, "CFGGENERATION" },
+ { VIRTIO_PCI_COMMON_Q_SELECT, 2, 0, "Q_SELECT" },
+ { VIRTIO_PCI_COMMON_Q_SIZE, 2, 0, "Q_SIZE" },
+ { VIRTIO_PCI_COMMON_Q_MSIX, 2, 0, "Q_MSIX" },
+ { VIRTIO_PCI_COMMON_Q_ENABLE, 2, 0, "Q_ENABLE" },
+ { VIRTIO_PCI_COMMON_Q_NOFF, 2, 1, "Q_NOFF" },
+ { VIRTIO_PCI_COMMON_Q_DESCLO, 4, 0, "Q_DESCLO" },
+ { VIRTIO_PCI_COMMON_Q_DESCHI, 4, 0, "Q_DESCHI" },
+ { VIRTIO_PCI_COMMON_Q_AVAILLO, 4, 0, "Q_AVAILLO" },
+ { VIRTIO_PCI_COMMON_Q_AVAILHI, 4, 0, "Q_AVAILHI" },
+ { VIRTIO_PCI_COMMON_Q_USEDLO, 4, 0, "Q_USEDLO" },
+ { VIRTIO_PCI_COMMON_Q_USEDHI, 4, 0, "Q_USEDHI" },
};
static inline struct config_reg *
-vi_find_cr(int offset) {
+vi_find_cr(struct config_reg *regstbl, size_t n, int offset) {
u_int hi, lo, mid;
struct config_reg *cr;
lo = 0;
- hi = sizeof(config_regs) / sizeof(*config_regs) - 1;
+ hi = n - 1;
while (hi >= lo) {
mid = (hi + lo) >> 1;
- cr = &config_regs[mid];
+ cr = &regstbl[mid];
if (cr->cr_offset == offset)
return (cr);
if (cr->cr_offset < offset)
@@ -553,16 +768,15 @@
}
/*
- * Handle pci config space reads.
- * If it's to the MSI-X info, do that.
- * If it's part of the virtio standard stuff, do that.
- * Otherwise dispatch to the actual driver.
+ * Handle legacy pci config space writes.
+ *
+ * If it's part of the legacy virtio config structure, do that.
+ * Otherwise dispatch to the actual device backend's config read
+ * callback.
*/
-uint64_t
-vi_pci_read(struct vmctx *ctx, int vcpu, struct pci_devinst *pi,
- int baridx, uint64_t offset, int size)
+static uint64_t
+vi_legacy_pci_read(struct virtio_softc *vs, int vcpu, uint64_t offset, int size)
{
- struct virtio_softc *vs = pi->pi_arg;
struct virtio_consts *vc;
struct config_reg *cr;
uint64_t virtio_config_size, max;
@@ -571,27 +785,13 @@
uint32_t value;
int error;
- if (vs->vs_flags & VIRTIO_USE_MSIX) {
- if (baridx == pci_msix_table_bar(pi) ||
- baridx == pci_msix_pba_bar(pi)) {
- return (pci_emul_msix_tread(pi, offset, size));
- }
- }
-
- /* XXX probably should do something better than just assert() */
- assert(baridx == 0);
-
- if (vs->vs_mtx)
- pthread_mutex_lock(vs->vs_mtx);
+ /* Checked by caller */
+ assert(size == 1 || size == 2 || size == 4);
vc = vs->vs_vc;
name = vc->vc_name;
value = size == 1 ? 0xff : size == 2 ? 0xffff : 0xffffffff;
-
- if (size != 1 && size != 2 && size != 4)
- goto bad;
-
- virtio_config_size = VIRTIO_PCI_CONFIG_OFF(pci_msix_enabled(pi));
+ virtio_config_size = VIRTIO_PCI_CONFIG_OFF(pci_msix_enabled(vs->vs_pi));
if (offset >= virtio_config_size) {
/*
@@ -609,7 +809,7 @@
}
bad:
- cr = vi_find_cr(offset);
+ cr = vi_find_cr(legacy_cfg_regs, nitems(legacy_cfg_regs), offset);
if (cr == NULL || cr->cr_size != size) {
if (cr != NULL) {
/* offset must be OK, so size must be bad */
@@ -626,14 +826,17 @@
switch (offset) {
case VIRTIO_PCI_HOST_FEATURES:
+ /* Caps for legacy PCI configuration layout is only 32bit */
value = vc->vc_hv_caps;
break;
case VIRTIO_PCI_GUEST_FEATURES:
value = vs->vs_negotiated_caps;
break;
case VIRTIO_PCI_QUEUE_PFN:
- if (vs->vs_curq < vc->vc_nvq)
- value = vs->vs_queues[vs->vs_curq].vq_pfn;
+ if ((vs->vs_negotiated_caps & VIRTIO_F_VERSION_1) == 0 &&
+ vs->vs_curq < vc->vc_nvq)
+ value = vs->vs_queues[vs->vs_curq].vq_desc_gpa >>
+ VRING_PFN;
break;
case VIRTIO_PCI_QUEUE_NUM:
value = vs->vs_curq < vc->vc_nvq ?
@@ -652,7 +855,7 @@
value = vs->vs_isr;
vs->vs_isr = 0; /* a read clears this flag */
if (value)
- pci_lintr_deassert(pi);
+ pci_lintr_deassert(vs->vs_pi);
break;
case VIRTIO_MSI_CONFIG_VECTOR:
value = vs->vs_msix_cfg_idx;
@@ -663,23 +866,22 @@
VIRTIO_MSI_NO_VECTOR;
break;
}
+
done:
- if (vs->vs_mtx)
- pthread_mutex_unlock(vs->vs_mtx);
return (value);
}
/*
- * Handle pci config space writes.
- * If it's to the MSI-X info, do that.
- * If it's part of the virtio standard stuff, do that.
- * Otherwise dispatch to the actual driver.
+ * Handle legacy pci config space writes.
+ *
+ * If it's part of the legacy virtio config structure, do that.
+ * Otherwise dispatch to the actual device backend's config write
+ * callback.
*/
-void
-vi_pci_write(struct vmctx *ctx, int vcpu, struct pci_devinst *pi,
- int baridx, uint64_t offset, int size, uint64_t value)
+static void
+vi_legacy_pci_write(struct virtio_softc *vs, int vcpu,
+ uint64_t offset, int size, uint64_t value)
{
- struct virtio_softc *vs = pi->pi_arg;
struct vqueue_info *vq;
struct virtio_consts *vc;
struct config_reg *cr;
@@ -688,27 +890,12 @@
uint32_t newoff;
int error;
- if (vs->vs_flags & VIRTIO_USE_MSIX) {
- if (baridx == pci_msix_table_bar(pi) ||
- baridx == pci_msix_pba_bar(pi)) {
- pci_emul_msix_twrite(pi, offset, size, value);
- return;
- }
- }
-
- /* XXX probably should do something better than just assert() */
- assert(baridx == 0);
-
- if (vs->vs_mtx)
- pthread_mutex_lock(vs->vs_mtx);
+ /* Checked by caller */
+ assert(size == 1 || size == 2 || size == 4);
vc = vs->vs_vc;
name = vc->vc_name;
-
- if (size != 1 && size != 2 && size != 4)
- goto bad;
-
- virtio_config_size = VIRTIO_PCI_CONFIG_OFF(pci_msix_enabled(pi));
+ virtio_config_size = VIRTIO_PCI_CONFIG_OFF(pci_msix_enabled(vs->vs_pi));
if (offset >= virtio_config_size) {
/*
@@ -720,12 +907,14 @@
if (newoff + size > max)
goto bad;
error = (*vc->vc_cfgwrite)(DEV_SOFTC(vs), newoff, size, value);
- if (!error)
- goto done;
+ if (!error) {
+ vs->vs_flags |= VIRTIO_DEVCFG_CHG;
+ return;
+ }
}
bad:
- cr = vi_find_cr(offset);
+ cr = vi_find_cr(legacy_cfg_regs, nitems(legacy_cfg_regs), offset);
if (cr == NULL || cr->cr_size != size || cr->cr_ro) {
if (cr != NULL) {
/* offset must be OK, wrong size and/or reg is R/O */
@@ -742,7 +931,7 @@
"%s: write to bad offset/size %jd/%d",
name, (uintmax_t)offset, size);
}
- goto done;
+ return;
}
switch (offset) {
@@ -755,7 +944,7 @@
case VIRTIO_PCI_QUEUE_PFN:
if (vs->vs_curq >= vc->vc_nvq)
goto bad_qindex;
- vi_vq_init(vs, value);
+ vi_legacy_vq_init(vs, value);
break;
case VIRTIO_PCI_QUEUE_SEL:
/*
@@ -769,7 +958,7 @@
if (value >= vc->vc_nvq) {
EPRINTLN("%s: queue %d notify out of range",
name, (int)value);
- goto done;
+ break;
}
vq = &vs->vs_queues[value];
if (vq->vq_notify)
@@ -796,54 +985,804 @@
vq->vq_msix_idx = value;
break;
}
- goto done;
+
+ return;
bad_qindex:
EPRINTLN(
"%s: write config reg %s: curq %d >= max %d",
name, cr->cr_name, vs->vs_curq, vc->vc_nvq);
-done:
- if (vs->vs_mtx)
- pthread_mutex_unlock(vs->vs_mtx);
}
-#ifdef BHYVE_SNAPSHOT
-int
-vi_pci_pause(struct vmctx *ctx, struct pci_devinst *pi)
+/*
+ * Virtio modern:
+ * Handle pci config space reads to common config structure.
+ */
+static uint64_t
+vi_pci_common_cfg_read(struct virtio_softc *vs, int vcpu, uint64_t offset,
+ int size)
{
- struct virtio_softc *vs;
+ uint64_t mask = size == 1 ? 0xff : size == 2 ? 0xffff : 0xffffffff;
+ uint64_t value = -1;
struct virtio_consts *vc;
+ struct vqueue_info *vq;
+ struct config_reg *cr;
+ const char *name;
- vs = pi->pi_arg;
- vc = vs->vs_vc;
+ /* Checked by caller */
+ assert(size == 1 || size == 2 || size == 4);
vc = vs->vs_vc;
- assert(vc->vc_pause != NULL);
- (*vc->vc_pause)(DEV_SOFTC(vs));
+ name = vc->vc_name;
- return (0);
+ cr = vi_find_cr(common_cfg_regs, nitems(common_cfg_regs), offset);
+ if (cr == NULL || cr->cr_size != size) {
+ /* Strict alignment and access size checking */
+ if (cr != NULL) {
+ EPRINTLN(
+ "%s: read from %s: bad size %d",
+ name, cr->cr_name, size);
+ } else {
+ EPRINTLN(
+ "%s: read from bad offset/size %jd/%d",
+ name, (uintmax_t)offset, size);
+ }
+ goto done;
+ }
+
+ switch (offset) {
+ case VIRTIO_PCI_COMMON_DFSELECT:
+ if (vs->vs_flags & VIRTIO_DFSELECT_HI)
+ value = 1;
+ else
+ value = 0;
+ break;
+ case VIRTIO_PCI_COMMON_DF:
+ value = (vs->vs_flags & VIRTIO_DFSELECT_HI) ?
+ (vc->vc_hv_caps >> 32) :
+ (vc->vc_hv_caps & 0xffffffff);
+ break;
+ case VIRTIO_PCI_COMMON_GFSELECT:
+ if (vs->vs_flags & VIRTIO_GFSELECT_HI)
+ value = 1;
+ else
+ value = 0;
+ break;
+ case VIRTIO_PCI_COMMON_GF:
+ value = (vs->vs_flags & VIRTIO_GFSELECT_HI) ?
+ (vs->vs_negotiated_caps >> 32) :
+ (vs->vs_negotiated_caps & 0xffffffff);
+ break;
+ case VIRTIO_PCI_COMMON_MSIX:
+ value = vs->vs_msix_cfg_idx;
+ break;
+ case VIRTIO_PCI_COMMON_NUMQ:
+ value = vc->vc_nvq;
+ break;
+ case VIRTIO_PCI_COMMON_STATUS:
+ value = vs->vs_status;
+ break;
+ case VIRTIO_PCI_COMMON_CFGGENERATION:
+ if (vs->vs_flags & VIRTIO_DEVCFG_CHG) {
+ vs->vs_devcfg_gen++;
+ vs->vs_flags &= ~VIRTIO_DEVCFG_CHG;
+ }
+ value = vs->vs_devcfg_gen;
+ break;
+ case VIRTIO_PCI_COMMON_Q_SELECT:
+ value = 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:
+ if (vs->vs_curq < vc->vc_nvq) {
+ vq = &vs->vs_queues[vs->vs_curq];
+ value = vq->vq_msix_idx;
+ }
+ break;
+ case VIRTIO_PCI_COMMON_Q_ENABLE:
+ value = vs->vs_curq < vc->vc_nvq ?
+ !!(vs->vs_queues[vs->vs_curq].vq_flags & VQ_ENABLED) : 0;
+ break;
+ case VIRTIO_PCI_COMMON_Q_NOFF:
+ /* queue_notify_off is equal to qid for now */
+ value = vs->vs_curq;
+ break;
+ case VIRTIO_PCI_COMMON_Q_DESCLO:
+ if (vs->vs_curq < vc->vc_nvq)
+ value = vs->vs_queues[vs->vs_curq].vq_desc_gpa &
+ 0xffffffff;
+ break;
+ case VIRTIO_PCI_COMMON_Q_DESCHI:
+ if (vs->vs_curq < vc->vc_nvq)
+ value = vs->vs_queues[vs->vs_curq].vq_desc_gpa >> 32;
+ break;
+ case VIRTIO_PCI_COMMON_Q_AVAILLO:
+ if (vs->vs_curq < vc->vc_nvq)
+ value = vs->vs_queues[vs->vs_curq].vq_avail_gpa &
+ 0xffffffff;
+ break;
+ case VIRTIO_PCI_COMMON_Q_AVAILHI:
+ if (vs->vs_curq < vc->vc_nvq)
+ value = vs->vs_queues[vs->vs_curq].vq_avail_gpa >> 32;
+ break;
+ case VIRTIO_PCI_COMMON_Q_USEDLO:
+ if (vs->vs_curq < vc->vc_nvq)
+ value = vs->vs_queues[vs->vs_curq].vq_used_gpa &
+ 0xffffffff;
+ break;
+ case VIRTIO_PCI_COMMON_Q_USEDHI:
+ if (vs->vs_curq < vc->vc_nvq)
+ value = vs->vs_queues[vs->vs_curq].vq_used_gpa >> 32;
+ break;
+ }
+
+done:
+ value &= mask;
+ return (value);
}
-int
-vi_pci_resume(struct vmctx *ctx, struct pci_devinst *pi)
+/*
+ * Virtio modern:
+ * Handle pci config space writes to common config structure.
+ */
+static void
+vi_pci_common_cfg_write(struct virtio_softc *vs, int vcpu,
+ uint64_t offset, int size, uint64_t value)
{
- struct virtio_softc *vs;
+ uint64_t mask = size == 1 ? 0xff : size == 2 ? 0xffff : 0xffffffff;
struct virtio_consts *vc;
+ struct vqueue_info *vq;
+ struct config_reg *cr;
+ const char *name;
- vs = pi->pi_arg;
- vc = vs->vs_vc;
+ /* Checked by caller */
+ assert(size == 1 || size == 2 || size == 4);
vc = vs->vs_vc;
- assert(vc->vc_resume != NULL);
- (*vc->vc_resume)(DEV_SOFTC(vs));
+ name = vc->vc_name;
+ value &= mask;
- return (0);
-}
+ cr = vi_find_cr(common_cfg_regs, nitems(common_cfg_regs), offset);
+ if (cr == NULL || cr->cr_size != size) {
+ /* Strict alignment and access size checking */
+ if (cr != NULL) {
+ EPRINTLN(
+ "%s: read from %s: bad size %d",
+ name, cr->cr_name, size);
+ } else {
+ EPRINTLN(
+ "%s: read from bad offset/size %jd/%d",
+ name, (uintmax_t)offset, size);
+ }
+ return;
+ }
-static int
-vi_pci_snapshot_softc(struct virtio_softc *vs, struct vm_snapshot_meta *meta)
-{
- int ret;
+ switch (offset) {
+ case VIRTIO_PCI_COMMON_DFSELECT:
+ if (value == 1)
+ vs->vs_flags |= VIRTIO_DFSELECT_HI;
+ else if (value == 0)
+ vs->vs_flags &= ~VIRTIO_DFSELECT_HI;
+ else {
+ EPRINTLN(
+ "%s: writing bad value to device_feature_select",
+ name);
+ goto bad_write;
+ }
+ break;
+ case VIRTIO_PCI_COMMON_GFSELECT:
+ if (value == 1)
+ vs->vs_flags |= VIRTIO_GFSELECT_HI;
+ else if (value == 0)
+ vs->vs_flags &= ~VIRTIO_GFSELECT_HI;
+ else {
+ EPRINTLN(
+ "%s: writing bad value to driver_feature_select",
+ name);
+ goto bad_write;
+ }
+ break;
+ case VIRTIO_PCI_COMMON_GF:
+ if (vs->vs_flags & VIRTIO_GFSELECT_HI) {
+ value &= vs->vs_vc->vc_hv_caps;
+ vs->vs_negotiated_caps = (vs->vs_negotiated_caps & 0xffffffff) |
+ (value << 32);
+ } else {
+ value &= vs->vs_vc->vc_hv_caps;
+ vs->vs_negotiated_caps =
+ (vs->vs_negotiated_caps & 0xffffffff00000000) | value;
+ }
+ break;
+ case VIRTIO_PCI_COMMON_MSIX:
+ vs->vs_msix_cfg_idx = value;
+ break;
+ case VIRTIO_PCI_COMMON_STATUS:
+ if (value == 0) {
+ (*vc->vc_reset)(DEV_SOFTC(vs));
+ vs->vs_status = value;
+ break;
+ }
+ if (!(vs->vs_status & VIRTIO_CONFIG_S_FEATURES_OK) &&
+ value & VIRTIO_CONFIG_S_FEATURES_OK) {
+ if (vc->vc_apply_features)
+ (*vc->vc_apply_features)(DEV_SOFTC(vs),
+ vs->vs_negotiated_caps);
+ }
+ vs->vs_status = value;
+ break;
+ case VIRTIO_PCI_COMMON_Q_SELECT:
+ if (value >= vc->vc_nvq) {
+ EPRINTLN("%s: queue select %d out of range",
+ name, (int)value);
+ goto bad_write;
+ }
+ vs->vs_curq = value;
+ break;
+ case VIRTIO_PCI_COMMON_Q_SIZE:
+ /* XXX: Check power of 2 */
+ if (vs->vs_curq >= vc->vc_nvq) {
+ EPRINTLN("%s: setting queue size for %d out of range",
+ name, vs->vs_curq);
+ goto bad_write;
+ }
+ vq = &vs->vs_queues[vs->vs_curq];
+ vq->vq_qsize = value;
+ break;
+ case VIRTIO_PCI_COMMON_Q_MSIX:
+ if (vs->vs_curq >= vc->vc_nvq) {
+ EPRINTLN(
+ "%s: setting msix vector of queue %d out of range",
+ name, vs->vs_curq);
+ goto bad_write;
+ }
+ vq = &vs->vs_queues[vs->vs_curq];
+ vq->vq_msix_idx = value;
+ break;
+ case VIRTIO_PCI_COMMON_Q_ENABLE:
+ if (vs->vs_curq >= vc->vc_nvq) {
+ EPRINTLN("%s: enabling queue %d out of range", name,
+ vs->vs_curq);
+ goto bad_write;
+ }
+ vq = &vs->vs_queues[vs->vs_curq];
+ if (!(vq->vq_flags & VQ_ENABLED) && value == 1) {
+ vi_vq_init(vs);
+ vq->vq_flags |= VQ_ENABLED;
+ } else if (!value)
+ vq->vq_flags &= ~VQ_ENABLED;
+ break;
+ case VIRTIO_PCI_COMMON_Q_DESCLO:
+ if (vs->vs_curq >= vc->vc_nvq) {
+ EPRINTLN(
+ "%s: setting desc ring of queue %d out of range",
+ name, vs->vs_curq);
+ goto bad_write;
+ }
+ vq = &vs->vs_queues[vs->vs_curq];
+ vq->vq_desc_gpa =
+ (vq->vq_desc_gpa & 0xffffffff00000000) | value;
+ break;
+ case VIRTIO_PCI_COMMON_Q_DESCHI:
+ if (vs->vs_curq >= vc->vc_nvq) {
+ EPRINTLN(
+ "%s: setting desc ring of queue %d out of range",
+ name, vs->vs_curq);
+ goto bad_write;
+ }
+ vq = &vs->vs_queues[vs->vs_curq];
+ vq->vq_desc_gpa =
+ (vq->vq_desc_gpa & 0xffffffff) | (value << 32);
+ break;
+ case VIRTIO_PCI_COMMON_Q_AVAILLO:
+ if (vs->vs_curq >= vc->vc_nvq) {
+ EPRINTLN(
+ "%s: setting avail ring of queue %d out of range",
+ name, vs->vs_curq);
+ goto bad_write;
+ }
+ vq = &vs->vs_queues[vs->vs_curq];
+ vq->vq_avail_gpa =
+ (vq->vq_avail_gpa & 0xffffffff00000000) | value;
+ break;
+ case VIRTIO_PCI_COMMON_Q_AVAILHI:
+ if (vs->vs_curq >= vc->vc_nvq) {
+ EPRINTLN(
+ "%s: setting avail ring of queue %d out of range",
+ name, vs->vs_curq);
+ goto bad_write;
+ }
+ vq = &vs->vs_queues[vs->vs_curq];
+ vq->vq_avail_gpa =
+ (vq->vq_avail_gpa & 0xffffffff) | (value << 32);
+ break;
+ case VIRTIO_PCI_COMMON_Q_USEDLO:
+ if (vs->vs_curq >= vc->vc_nvq) {
+ EPRINTLN(
+ "%s: setting used ring of queue %d out of range",
+ name, vs->vs_curq);
+ goto bad_write;
+ }
+ vq = &vs->vs_queues[vs->vs_curq];
+ vq->vq_used_gpa =
+ (vq->vq_used_gpa & 0xffffffff00000000) | value;
+ break;
+ case VIRTIO_PCI_COMMON_Q_USEDHI:
+ if (vs->vs_curq >= vc->vc_nvq) {
+ EPRINTLN(
+ "%s: setting used ring of queue %d out of range",
+ name, vs->vs_curq);
+ goto bad_write;
+ }
+ vq = &vs->vs_queues[vs->vs_curq];
+ vq->vq_used_gpa =
+ (vq->vq_used_gpa & 0xffffffff) | (value << 32);
+ break;
+ default:
+ EPRINTLN("%s: write to bad offset/size %jd/%d", name,
+ (uintmax_t)offset, size);
+ goto bad_write;
+ }
+
+ return;
+
+bad_write:
+ return;
+}
+
+/*
+ * Virtio modern:
+ * Handle pci mmio/pio reads to notification structure.
+ *
+ * Reading the structure always returns zero.
+ */
+static uint64_t
+vi_pci_notify_cfg_read(struct virtio_softc *vs, int vcpu, uint64_t offset,
+ int size)
+{
+ return (0);
+}
+
+/*
+ * Virtio modern:
+ * Handle pci mmio/pio writes to notification structure.
+ *
+ * VIRTIO_F_NOTIFICATION_DATA is not presented yet, so only
+ * consider the case writing vq index into the registers.
+ */
+static void
+vi_pci_notify_cfg_write(struct virtio_softc *vs, int vcpu, uint64_t offset,
+ int size, uint64_t value)
+{
+ struct virtio_consts *vc;
+ struct vqueue_info *vq;
+ unsigned int qid;
+ const char *name;
+
+ vc = vs->vs_vc;
+ name = vc->vc_name;
+ qid = value;
+
+ if (size != 2) {
+ EPRINTLN("%s: bad access at offset %" PRIu64,
+ name, offset);
+ return;
+ }
+
+ if (!(vs->vs_status & VIRTIO_CONFIG_STATUS_DRIVER_OK))
+ return;
+
+ /* queue_notify_off is equal to qid for now */
+ if (offset != qid * VQ_NOTIFY_OFF_MULTIPLIER) {
+ EPRINTLN(
+ "%s: queue %u notify does not have matching offset at %" PRIu64,
+ name, qid, offset);
+ return;
+ }
+
+ if (qid >= vc->vc_nvq) {
+ EPRINTLN("%s: queue %u notify out of range", name, qid);
+ return;
+ }
+
+ vq = &vs->vs_queues[qid];
+ if (!(vq->vq_flags & VQ_ENABLED))
+ return;
+ if (vq->vq_notify)
+ (*vq->vq_notify)(DEV_SOFTC(vs), vq);
+ else if (vc->vc_qnotify)
+ (*vc->vc_qnotify)(DEV_SOFTC(vs), vq);
+ else
+ EPRINTLN(
+ "%s: qnotify queue %u: missing vq/vc notify", name, qid);
+}
+
+/*
+ * Virtio modern:
+ * Handle pci mmio/pio reads to ISR structure.
+ *
+ * The ISR structure has a relaxed requirement on alignment.
+ */
+static uint64_t
+vi_pci_isr_cfg_read(struct virtio_softc *vs, int vcpu, uint64_t offset,
+ int size)
+{
+ uint64_t value;
+
+ if (offset == 0)
+ value = vs->vs_isr;
+ else
+ value = 0;
+
+ vs->vs_isr = 0;
+ return (value);
+}
+
+/*
+ * Virtio modern:
+ * pci mmio/pio writes to ISR structure are disallowed.
+ */
+static void
+vi_pci_isr_cfg_write(struct virtio_softc *vs, int vcpu, uint64_t offset,
+ int size, uint64_t value)
+{
+ const char *name = vs->vs_vc->vc_name;
+
+ EPRINTLN("%s: invalid write into isr cfg", name);
+}
+
+/*
+ * Virtio modern:
+ * Handle pci mmio/pio reads to device-specific config structure.
+ */
+static uint64_t
+vi_pci_dev_cfg_read(struct virtio_softc *vs, int vcpu,
+ uint64_t offset, int size)
+{
+ uint64_t max;
+ struct virtio_consts *vc;
+ uint32_t value;
+
+ vc = vs->vs_vc;
+ value = size == 1 ? 0xff : size == 2 ? 0xffff : 0xffffffff;
+
+ max = vc->vc_cfgsize ? vc->vc_cfgsize : 0x100000000;
+ if (offset + size > max)
+ return (value);
+
+ (*vc->vc_cfgread)(DEV_SOFTC(vs), offset, size, &value);
+ return (value);
+}
+
+/*
+ * Virtio modern:
+ * Handle pci mmio/pio writes to device-specific config structure.
+ */
+static void
+vi_pci_dev_cfg_write(struct virtio_softc *vs, int vcpu, uint64_t offset,
+ int size, uint64_t value)
+{
+ uint64_t mask = size == 1 ? 0xff : size == 2 ? 0xffff : 0xffffffff;
+ struct virtio_consts *vc;
+ uint64_t max;
+
+ value = value & mask;
+
+ max = vc->vc_cfgsize ? vc->vc_cfgsize : 0x100000000;
+ if (offset + size > max)
+ return;
+ if ((*vc->vc_cfgwrite)(DEV_SOFTC(vs), offset, size, value) == 0)
+ vs->vs_flags |= VIRTIO_DEVCFG_CHG;
+}
+
+/*
+ * Check if pci config space access should be redirected or dropped.
+ */
+static bool
+vi_pci_should_redirect(struct virtio_softc *vs, int offset, int size)
+{
+ int i;
+
+ for (i = 0; i < vs->vs_ncfgs; i++) {
+ if (offset + size > vs->vs_cfgs[i].c_capoff &&
+ (offset < vs->vs_cfgs[i].c_capoff +
+ vs->vs_cfgs[i].c_caplen))
+ return (false);
+ }
+ return (true);
+}
+
+/*
+ * Virtio modern:
+ * Handle reads to pci config access capability.
+ */
+int
+vi_pci_cfgread(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, int offset,
+ int bytes, uint32_t *retval)
+{
+ uint32_t mask = bytes == 1 ? 0xff : bytes == 2 ? 0xffff : 0xffffffff;
+ struct virtio_softc *vs = pi->pi_arg;
+ uint32_t baroff, barlen;
+ int baridx;
+
+ if (vs->vs_pcicfg == NULL ||
+ (offset != vs->vs_pcicfg->c_capoff +
+ __offsetof(struct virtio_pci_cfg_cap, pci_cfg_data)) ||
+ (bytes != 1 && bytes != 2 && bytes != 4)) {
+ if (vi_pci_should_redirect(vs, offset, bytes))
+ /* Dispatch unrelated reads to pci emulation */
+ return (-1);
+ /* Dropped */
+ *retval = ~0 & mask;
+ return (0);
+ }
+
+ if (vs->vs_mtx)
+ pthread_mutex_lock(vs->vs_mtx);
+
+ baridx = pci_get_cfgdata8(pi,
+ offset + __offsetof(struct virtio_pci_cap, bar));
+ baroff = pci_get_cfgdata32(pi,
+ offset + __offsetof(struct virtio_pci_cap, offset));
+ barlen = pci_get_cfgdata32(pi,
+ offset + __offsetof(struct virtio_pci_cap, length));
+ if (baridx > PCIR_MAX_BAR_0) {
+ *retval = ~0 & mask;
+ goto done;
+ }
+ *retval = vi_modern_pci_read(vs, vcpu, baridx, baroff, barlen);
+
+done:
+ if (vs->vs_mtx)
+ pthread_mutex_unlock(vs->vs_mtx);
+ return (0);
+}
+
+/*
+ * Virtio modern:
+ * Handle writes to pci config access capability.
+ */
+int
+vi_pci_cfgwrite(struct vmctx *ctx, int vcpu, struct pci_devinst *pi,
+ int offset, int bytes, uint32_t val)
+{
+ struct virtio_softc *vs = pi->pi_arg;
+ uint32_t baroff, barlen;
+ int baridx;
+
+ if (vs->vs_pcicfg == NULL ||
+ (offset != vs->vs_pcicfg->c_capoff +
+ __offsetof(struct virtio_pci_cfg_cap, pci_cfg_data)) ||
+ (bytes != 1 && bytes != 2 && bytes != 4)) {
+ if (vi_pci_should_redirect(vs, offset, bytes))
+ /* Dispatch unrelated writes to pci emulation */
+ return (-1);
+ /* Dropped */
+ return (0);
+ }
+
+ if (vs->vs_mtx)
+ pthread_mutex_lock(vs->vs_mtx);
+
+ baridx = pci_get_cfgdata8(pi,
+ offset + __offsetof(struct virtio_pci_cap, bar));
+ baroff = pci_get_cfgdata32(pi,
+ offset + __offsetof(struct virtio_pci_cap, offset));
+ barlen = pci_get_cfgdata32(pi,
+ offset + __offsetof(struct virtio_pci_cap, length));
+ if (baridx > PCIR_MAX_BAR_0)
+ goto done;
+ vi_modern_pci_write(vs, vcpu, baridx, baroff, barlen, val);
+
+done:
+ if (vs->vs_mtx)
+ pthread_mutex_unlock(vs->vs_mtx);
+ return (0);
+}
+
+/*
+ * Handle pci config space reads to virtio-related structures
+ */
+static uint64_t
+vi_modern_pci_read(struct virtio_softc *vs, int vcpu,
+ int baridx, uint64_t offset, int size)
+{
+ uint64_t value;
+ int i;
+
+ for (i = 0; i < vs->vs_ncfgs; i++) {
+ if ((vs->vs_cfgs[i].c_captype == VIRTIO_PCI_CAP_PCI_CFG) ||
+ (baridx != vs->vs_cfgs[i].c_baridx) ||
+ (offset < vs->vs_cfgs[i].c_offset) ||
+ (offset + size > vs->vs_cfgs[i].c_offset +
+ vs->vs_cfgs[i].c_size))
+ continue;
+
+ offset -= vs->vs_cfgs[i].c_offset;
+
+ switch (vs->vs_cfgs[i].c_captype) {
+ case VIRTIO_PCI_CAP_COMMON_CFG:
+ value = vi_pci_common_cfg_read(vs, vcpu, offset, size);
+ break;
+ case VIRTIO_PCI_CAP_NOTIFY_CFG:
+ value = vi_pci_notify_cfg_read(vs, vcpu, offset, size);
+ break;
+ case VIRTIO_PCI_CAP_ISR_CFG:
+ value = vi_pci_isr_cfg_read(vs, vcpu, offset, size);
+ break;
+ case VIRTIO_PCI_CAP_DEVICE_CFG:
+ value = vi_pci_dev_cfg_read(vs, vcpu, offset, size);
+ break;
+ }
+ break;
+ }
+
+ return (value);
+}
+
+/*
+ * Handle pci config space reads to virtio-related structures
+ */
+static void
+vi_modern_pci_write(struct virtio_softc *vs, int vcpu,
+ int baridx, uint64_t offset, int size, uint64_t value)
+{
+ int i;
+
+ for (i = 0; i < vs->vs_ncfgs; i++) {
+ if ((baridx != vs->vs_cfgs[i].c_baridx) ||
+ (offset < vs->vs_cfgs[i].c_offset) ||
+ (offset + size > vs->vs_cfgs[i].c_offset +
+ vs->vs_cfgs[i].c_size))
+ continue;
+
+ offset -= vs->vs_cfgs[i].c_offset;
+
+ switch (vs->vs_cfgs[i].c_captype) {
+ case VIRTIO_PCI_CAP_COMMON_CFG:
+ vi_pci_common_cfg_write(vs, vcpu, offset, size, value);
+ break;
+ case VIRTIO_PCI_CAP_NOTIFY_CFG:
+ vi_pci_notify_cfg_write(vs, vcpu, offset, size,
+ value);
+ break;
+ case VIRTIO_PCI_CAP_ISR_CFG:
+ vi_pci_isr_cfg_write(vs, vcpu, offset, size, value);
+ break;
+ case VIRTIO_PCI_CAP_DEVICE_CFG:
+ vi_pci_dev_cfg_write(vs, vcpu, offset, size, value);
+ break;
+ }
+ break;
+ }
+}
+
+/*
+ * Handle virtio bar reads.
+ *
+ * If it's to the MSI-X info, dispatch the reads to the msix handling code.
+ * Otherwise, dispatch the reads to virtio device code.
+ */
+uint64_t
+vi_pci_read(struct vmctx *ctx, int vcpu, struct pci_devinst *pi,
+ int baridx, uint64_t offset, int size)
+{
+ struct virtio_softc *vs = pi->pi_arg;
+ struct virtio_consts *vc;
+ uint64_t value;
+
+ if (vs->vs_flags & VIRTIO_USE_MSIX) {
+ if (baridx == pci_msix_table_bar(pi) ||
+ baridx == pci_msix_pba_bar(pi)) {
+ return (pci_emul_msix_tread(pi, offset, size));
+ }
+ }
+
+ if (vs->vs_mtx)
+ pthread_mutex_lock(vs->vs_mtx);
+
+ vc = vs->vs_vc;
+ value = size == 1 ? 0xff : size == 2 ? 0xffff : 0xffffffff;
+
+ if (size != 1 && size != 2 && size != 4)
+ goto done;
+
+ if (!baridx) {
+ value = vi_legacy_pci_read(vs, vcpu, offset, size);
+ goto done;
+ }
+
+ value = vi_modern_pci_read(vs, vcpu, baridx, offset, size);
+
+done:
+ if (vs->vs_mtx)
+ pthread_mutex_unlock(vs->vs_mtx);
+ return (value);
+}
+
+/*
+ * Handle virtio bar writes.
+ *
+ * If it's to the MSI-X info, dispatch the writes to the msix handling code.
+ * Otherwise, dispatch the writes to virtio device code.
+ */
+void
+vi_pci_write(struct vmctx *ctx, int vcpu, struct pci_devinst *pi,
+ int baridx, uint64_t offset, int size, uint64_t value)
+{
+ struct virtio_softc *vs = pi->pi_arg;
+ struct virtio_consts *vc;
+
+ vc = vs->vs_vc;
+
+ if (vs->vs_flags & VIRTIO_USE_MSIX) {
+ if (baridx == pci_msix_table_bar(pi) ||
+ baridx == pci_msix_pba_bar(pi)) {
+ pci_emul_msix_twrite(pi, offset, size, value);
+ return;
+ }
+ }
+
+ if (vs->vs_mtx)
+ pthread_mutex_lock(vs->vs_mtx);
+
+ if (size != 1 && size != 2 && size != 4)
+ goto done;
+
+ if (!baridx) {
+ vi_legacy_pci_write(vs, vcpu, offset, size, value);
+ goto done;
+ }
+
+ vi_modern_pci_write(vs, vcpu, baridx, offset, size, value);
+
+done:
+ if (vs->vs_mtx)
+ pthread_mutex_unlock(vs->vs_mtx);
+}
+
+#ifdef BHYVE_SNAPSHOT
+int
+vi_pci_pause(struct vmctx *ctx, struct pci_devinst *pi)
+{
+ struct virtio_softc *vs;
+ struct virtio_consts *vc;
+
+ vs = pi->pi_arg;
+ vc = vs->vs_vc;
+
+ vc = vs->vs_vc;
+ assert(vc->vc_pause != NULL);
+ (*vc->vc_pause)(DEV_SOFTC(vs));
+
+ return (0);
+}
+
+int
+vi_pci_resume(struct vmctx *ctx, struct pci_devinst *pi)
+{
+ struct virtio_softc *vs;
+ struct virtio_consts *vc;
+
+ vs = pi->pi_arg;
+ vc = vs->vs_vc;
+
+ vc = vs->vs_vc;
+ assert(vc->vc_resume != NULL);
+ (*vc->vc_resume)(DEV_SOFTC(vs));
+
+ return (0);
+}
+
+static int
+vi_pci_snapshot_softc(struct virtio_softc *vs, struct vm_snapshot_meta *meta)
+{
+ int ret, i;
+ int pcicfg_idx;
+
+ pcicfg_idx = -1;
SNAPSHOT_VAR_OR_LEAVE(vs->vs_flags, meta, ret, done);
SNAPSHOT_VAR_OR_LEAVE(vs->vs_negotiated_caps, meta, ret, done);
@@ -851,6 +1790,23 @@
SNAPSHOT_VAR_OR_LEAVE(vs->vs_status, meta, ret, done);
SNAPSHOT_VAR_OR_LEAVE(vs->vs_isr, meta, ret, done);
SNAPSHOT_VAR_OR_LEAVE(vs->vs_msix_cfg_idx, meta, ret, done);
+ SNAPSHOT_VAR_OR_LEAVE(vs->vs_devcfg_gen, meta, ret, done);
+ SNAPSHOT_VAR_OR_LEAVE(vs->vs_ncfgs, meta, ret, done);
+ for (i = 0; i < vs->vs_ncfgs; i++) {
+ SNAPSHOT_VAR_OR_LEAVE(vs->vs_cfgs[i].c_captype, meta, ret,
+ done);
+ SNAPSHOT_VAR_OR_LEAVE(vs->vs_cfgs[i].c_baridx, meta, ret, done);
+ SNAPSHOT_VAR_OR_LEAVE(vs->vs_cfgs[i].c_offset, meta, ret, done);
+ SNAPSHOT_VAR_OR_LEAVE(vs->vs_cfgs[i].c_size, meta, ret, done);
+ SNAPSHOT_VAR_OR_LEAVE(vs->vs_cfgs[i].c_capoff, meta, ret, done);
+ SNAPSHOT_VAR_OR_LEAVE(vs->vs_cfgs[i].c_caplen, meta, ret, done);
+ if (vs->vs_cfgs[i].c_captype == VIRTIO_PCI_CAP_PCI_CFG)
+ pcicfg_idx = i;
+ }
+ if (meta->op == VM_SNAPSHOT_RESTORE) {
+ if (pcicfg_idx != -1)
+ vs->vs_pcicfg = &vs->vs_cfgs[pcicfg_idx];
+ }
done:
return (ret);
@@ -893,7 +1849,9 @@
SNAPSHOT_VAR_OR_LEAVE(vq->vq_save_used, meta, ret, done);
SNAPSHOT_VAR_OR_LEAVE(vq->vq_msix_idx, meta, ret, done);
- SNAPSHOT_VAR_OR_LEAVE(vq->vq_pfn, meta, ret, done);
+ SNAPSHOT_VAR_OR_LEAVE(vq->vq_desc_gpa, meta, ret, done);
+ SNAPSHOT_VAR_OR_LEAVE(vq->vq_avail_gpa, meta, ret, done);
+ SNAPSHOT_VAR_OR_LEAVE(vq->vq_used_gpa, meta, ret, done);
addr_size = vq->vq_qsize * sizeof(struct vring_desc);
SNAPSHOT_GUEST2HOST_ADDR_OR_LEAVE(vq->vq_desc, addr_size,

File Metadata

Mime Type
text/plain
Expires
Tue, Sep 15, 7:27 AM (12 h, 17 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
29644768
Default Alt Text
D29708.1789457270.diff (51 KB)

Event Timeline