Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F148418553
D29708.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
60 KB
Referenced Files
None
Subscribers
None
D29708.diff
View Options
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
@@ -113,7 +113,11 @@
pci_vt9p_cfgread, /* read virtio config */
NULL, /* write virtio config */
pci_vt9p_neg_features, /* apply negotiated features */
- (1 << 0), /* our capabilities */
+ (1 << 0), /* our capabilities (legacy) */
+ (1 << 0), /* our capabilities (modern) */
+ true, /* Enable legacy */
+ true, /* Enable modern */
+ 2, /* PCI BAR# for modern */
};
@@ -329,7 +333,8 @@
sc->vsc_vq.vq_qsize = VT9P_RINGSZ;
/* initialize config space */
- pci_set_cfgdata16(pi, PCIR_DEVICE, VIRTIO_DEV_9P);
+ pci_set_cfgdata16(pi, PCIR_DEVICE, sc->vsc_vs.vs_vc->vc_en_legacy ?
+ VIRTIO_DEV_9P : vi_get_modern_pci_devid(VIRTIO_ID_9P));
pci_set_cfgdata16(pi, PCIR_VENDOR, VIRTIO_VENDOR);
pci_set_cfgdata8(pi, PCIR_CLASS, PCIC_STORAGE);
pci_set_cfgdata16(pi, PCIR_SUBDEV_0, VIRTIO_ID_9P);
@@ -337,7 +342,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);
return (0);
}
@@ -346,6 +351,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
@@ -216,7 +216,11 @@
pci_vtblk_cfgread, /* read PCI config */
pci_vtblk_cfgwrite, /* write PCI config */
NULL, /* apply negotiated features */
- VTBLK_S_HOSTCAPS, /* our capabilities */
+ VTBLK_S_HOSTCAPS, /* our capabilities (legacy) */
+ VTBLK_S_HOSTCAPS, /* our capabilities (modern) */
+ true, /* Enable legacy */
+ true, /* Enable modern */
+ 2, /* PCI BAR# for modern */
#ifdef BHYVE_SNAPSHOT
pci_vtblk_pause, /* pause blockif threads */
pci_vtblk_resume, /* resume blockif threads */
@@ -485,8 +489,10 @@
}
bcopy(&vtblk_vi_consts, &sc->vbsc_consts, sizeof (vtblk_vi_consts));
- if (blockif_candelete(sc->bc))
- sc->vbsc_consts.vc_hv_caps |= VTBLK_F_DISCARD;
+ if (blockif_candelete(sc->bc)) {
+ sc->vbsc_consts.vc_hv_caps_legacy |= VTBLK_F_DISCARD;
+ sc->vbsc_consts.vc_hv_caps_modern |= VTBLK_F_DISCARD;
+ }
pthread_mutex_init(&sc->vsc_mtx, NULL);
@@ -541,7 +547,8 @@
* have the device, class, and subdev_0 as fields in
* the virtio constants structure.
*/
- pci_set_cfgdata16(pi, PCIR_DEVICE, VIRTIO_DEV_BLOCK);
+ pci_set_cfgdata16(pi, PCIR_DEVICE, sc->vbsc_consts.vc_en_legacy ?
+ VIRTIO_DEV_BLOCK : vi_get_modern_pci_devid(VIRTIO_ID_BLOCK));
pci_set_cfgdata16(pi, PCIR_VENDOR, VIRTIO_VENDOR);
pci_set_cfgdata8(pi, PCIR_CLASS, PCIC_STORAGE);
pci_set_cfgdata16(pi, PCIR_SUBDEV_0, VIRTIO_ID_BLOCK);
@@ -552,7 +559,7 @@
free(sc);
return (1);
}
- vi_set_io_bar(&sc->vbsc_vs, 0);
+ vi_setup_pci_bar(&sc->vbsc_vs);
blockif_register_resize_callback(sc->bc, pci_vtblk_resized, sc);
return (0);
}
@@ -581,6 +588,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
@@ -177,7 +177,11 @@
pci_vtcon_cfgread, /* read virtio config */
pci_vtcon_cfgwrite, /* write virtio config */
pci_vtcon_neg_features, /* apply negotiated features */
- VTCON_S_HOSTCAPS, /* our capabilities */
+ VTCON_S_HOSTCAPS, /* our capabilities (legacy) */
+ VTCON_S_HOSTCAPS, /* our capabilities (modern) */
+ true, /* Enable legacy */
+ true, /* Enable modern */
+ 2, /* PCI BAR# for modern */
};
@@ -709,7 +713,8 @@
}
/* initialize config space */
- pci_set_cfgdata16(pi, PCIR_DEVICE, VIRTIO_DEV_CONSOLE);
+ pci_set_cfgdata16(pi, PCIR_DEVICE, sc->vsc_vs.vs_vc->vc_en_legacy ?
+ VIRTIO_DEV_CONSOLE : vi_get_modern_pci_devid(VIRTIO_ID_CONSOLE));
pci_set_cfgdata16(pi, PCIR_VENDOR, VIRTIO_VENDOR);
pci_set_cfgdata8(pi, PCIR_CLASS, PCIC_SIMPLECOMM);
pci_set_cfgdata16(pi, PCIR_SUBDEV_0, VIRTIO_ID_CONSOLE);
@@ -717,7 +722,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);
/* create control port */
sc->vsc_control_port.vsp_sc = sc;
@@ -753,6 +758,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_input.c
===================================================================
--- usr.sbin/bhyve/pci_virtio_input.c
+++ usr.sbin/bhyve/pci_virtio_input.c
@@ -465,6 +465,9 @@
/* select/subsel changed, query new config on next cfgread */
sc->vsc_config_valid = 0;
+ /* notify the guest the device configuration has been changed */
+ vq_devcfg_changed(&sc->vsc_vs);
+
return (0);
}
@@ -747,7 +750,7 @@
if (vi_intr_init(&sc->vsc_vs, 1, fbsdrun_virtio_msix()))
goto failed;
/* add virtio register */
- vi_set_io_bar(&sc->vsc_vs, 0);
+ vi_setup_pci_bar(&sc->vsc_vs);
return (0);
Index: usr.sbin/bhyve/pci_virtio_net.c
===================================================================
--- usr.sbin/bhyve/pci_virtio_net.c
+++ usr.sbin/bhyve/pci_virtio_net.c
@@ -72,9 +72,12 @@
#define VTNET_MIN_MTU ETHERMIN
#define VTNET_MAX_MTU 65535
-#define VTNET_S_HOSTCAPS \
+#define VTNET_S_HOSTCAPS_LEGACY \
( VIRTIO_NET_F_MAC | VIRTIO_NET_F_STATUS | \
VIRTIO_F_NOTIFY_ON_EMPTY | VIRTIO_RING_F_INDIRECT_DESC)
+#define VTNET_S_HOSTCAPS_MODERN \
+ ( VIRTIO_NET_F_MAC | VIRTIO_NET_F_STATUS | \
+ VIRTIO_RING_F_INDIRECT_DESC)
/*
* PCI config-space "registers"
@@ -153,7 +156,11 @@
pci_vtnet_cfgread, /* read PCI config */
pci_vtnet_cfgwrite, /* write PCI config */
pci_vtnet_neg_features, /* apply negotiated features */
- VTNET_S_HOSTCAPS, /* our capabilities */
+ VTNET_S_HOSTCAPS_LEGACY, /* our capabilities (legacy) */
+ VTNET_S_HOSTCAPS_MODERN, /* our capabilities (modern) */
+ true, /* Enable legacy */
+ true, /* Enable modern */
+ 2, /* PCI BAR# for modern */
#ifdef BHYVE_SNAPSHOT
pci_vtnet_pause, /* pause rx/tx threads */
pci_vtnet_resume, /* resume rx/tx threads */
@@ -612,7 +619,8 @@
free(sc);
return (err);
}
- sc->vsc_consts.vc_hv_caps |= VIRTIO_NET_F_MTU;
+ sc->vsc_consts.vc_hv_caps_legacy |= VIRTIO_NET_F_MTU;
+ sc->vsc_consts.vc_hv_caps_modern |= VIRTIO_NET_F_MTU;
}
sc->vsc_config.mtu = mtu;
@@ -625,7 +633,9 @@
}
}
- sc->vsc_consts.vc_hv_caps |= VIRTIO_NET_F_MRG_RXBUF |
+ sc->vsc_consts.vc_hv_caps_legacy |= VIRTIO_NET_F_MRG_RXBUF |
+ netbe_get_cap(sc->vsc_be);
+ sc->vsc_consts.vc_hv_caps_modern |= VIRTIO_NET_F_MRG_RXBUF |
netbe_get_cap(sc->vsc_be);
/*
@@ -635,7 +645,8 @@
sc->vsc_config.max_virtqueue_pairs = 1;
/* initialize config space */
- pci_set_cfgdata16(pi, PCIR_DEVICE, VIRTIO_DEV_NET);
+ pci_set_cfgdata16(pi, PCIR_DEVICE, sc->vsc_consts.vc_en_legacy ?
+ VIRTIO_DEV_NET : vi_get_modern_pci_devid(VIRTIO_ID_NETWORK));
pci_set_cfgdata16(pi, PCIR_VENDOR, VIRTIO_VENDOR);
pci_set_cfgdata8(pi, PCIR_CLASS, PCIC_NETWORK);
pci_set_cfgdata16(pi, PCIR_SUBDEV_0, VIRTIO_ID_NETWORK);
@@ -653,8 +664,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);
sc->resetting = 0;
@@ -691,6 +702,8 @@
*/
ptr = &sc->vsc_config.mac[offset];
memcpy(ptr, &value, size);
+
+ vq_devcfg_changed(&sc->vsc_vs);
} else {
/* silently ignore other writes */
DPRINTF(("vtnet: write to readonly reg %d", offset));
@@ -808,6 +821,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
@@ -92,7 +92,11 @@
NULL, /* read virtio config */
NULL, /* write virtio config */
NULL, /* apply negotiated features */
- 0, /* our capabilities */
+ 0, /* our capabilities (legacy) */
+ 0, /* our capabilities (modern) */
+ true, /* Enable legacy */
+ true, /* Enable modern */
+ 2, /* PCI BAR# for modern */
};
@@ -187,7 +191,8 @@
sc->vrsc_fd = fd;
/* initialize config space */
- pci_set_cfgdata16(pi, PCIR_DEVICE, VIRTIO_DEV_RANDOM);
+ pci_set_cfgdata16(pi, PCIR_DEVICE, sc->vrsc_vs.vs_vc->vc_en_legacy ?
+ VIRTIO_DEV_RANDOM : VIRTIO_ID_ENTROPY);
pci_set_cfgdata16(pi, PCIR_VENDOR, VIRTIO_VENDOR);
pci_set_cfgdata8(pi, PCIR_CLASS, PCIC_CRYPTO);
pci_set_cfgdata16(pi, PCIR_SUBDEV_0, VIRTIO_ID_ENTROPY);
@@ -195,7 +200,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);
return (0);
}
@@ -204,6 +209,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
@@ -256,7 +256,11 @@
pci_vtscsi_cfgread, /* read virtio config */
pci_vtscsi_cfgwrite, /* write virtio config */
pci_vtscsi_neg_features, /* apply negotiated features */
- 0, /* our capabilities */
+ 0, /* our capabilities (legacy) */
+ 0, /* our capabilities (modern) */
+ true, /* Enable legacy */
+ true, /* Enable modern */
+ 2, /* PCI BAR# for modern */
};
static void *
@@ -717,7 +721,8 @@
}
/* initialize config space */
- pci_set_cfgdata16(pi, PCIR_DEVICE, VIRTIO_DEV_SCSI);
+ pci_set_cfgdata16(pi, PCIR_DEVICE, sc->vss_vs.vs_vc->vc_en_legacy ?
+ VIRTIO_DEV_SCSI : VIRTIO_ID_SCSI);
pci_set_cfgdata16(pi, PCIR_VENDOR, VIRTIO_VENDOR);
pci_set_cfgdata8(pi, PCIR_CLASS, PCIC_STORAGE);
pci_set_cfgdata16(pi, PCIR_SUBDEV_0, VIRTIO_ID_SCSI);
@@ -725,7 +730,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);
return (0);
}
@@ -735,6 +740,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
@@ -201,6 +210,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
@@ -229,22 +240,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) \
@@ -272,7 +307,13 @@
/* called to write config regs */
void (*vc_apply_features)(void *, uint64_t);
/* called to apply negotiated features */
- uint64_t vc_hv_caps; /* hypervisor-provided capabilities */
+ uint64_t vc_hv_caps_legacy;
+ /* hypervisor-provided capabilities (legacy) */
+ uint64_t vc_hv_caps_modern;
+ /* hypervisor-provided capabilities (modern) */
+ bool vc_en_legacy; /* enable legacy */
+ bool vc_en_modern; /* enable modern */
+ char vc_modern_pcibar; /* PCI BAR# for modern */
void (*vc_pause)(void *); /* called to pause device activity */
void (*vc_resume)(void *); /* called to resume device activity */
int (*vc_snapshot)(void *, struct vm_snapshot_meta *);
@@ -298,6 +339,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 *);
@@ -312,19 +354,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?
*/
@@ -343,8 +400,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);
}
/*
@@ -354,15 +410,15 @@
static inline void
vi_interrupt(struct virtio_softc *vs, uint8_t isr, uint16_t msix_idx)
{
+ if (!(vs->vs_status & VIRTIO_CONFIG_STATUS_DRIVER_OK))
+ return;
if (pci_msix_enabled(vs->vs_pi))
pci_generate_msix(vs->vs_pi, msix_idx);
else {
- VS_LOCK(vs);
vs->vs_isr |= isr;
pci_generate_msi(vs->vs_pi, 0);
pci_lintr_assert(vs->vs_pi);
- VS_UNLOCK(vs);
}
}
@@ -377,6 +433,17 @@
vi_interrupt(vs, VIRTIO_PCI_ISR_INTR, vq->vq_msix_idx);
}
+/*
+ * 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_devcfg_changed(struct virtio_softc *vs)
+{
+ vs->vs_flags |= VIRTIO_DEVCFG_CHG;
+ vi_interrupt(vs, VIRTIO_PCI_ISR_CONFIG, vs->vs_msix_cfg_idx);
+}
+
static inline void
vq_kick_enable(struct vqueue_info *vq)
{
@@ -397,6 +464,21 @@
vq->vq_used->flags |= VRING_USED_F_NO_NOTIFY;
}
+static inline uint64_t
+vi_hv_features(struct virtio_softc *vs, bool modern)
+{
+ return (modern ? vs->vs_vc->vc_hv_caps_modern | VIRTIO_F_VERSION_1 :
+ vs->vs_vc->vc_hv_caps_legacy);
+}
+
+static inline uint16_t
+vi_get_modern_pci_devid(uint16_t vdid)
+{
+ return (vdid + VIRTIO_PCI_DEVICEID_MODERN_MIN);
+}
+
+#define VIRTIO_LEGACY_BAR 0 /* BAR # to host virtio legacy cfg regs */
+
struct iovec;
/*
@@ -414,8 +496,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);
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);
@@ -426,6 +508,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 both modern and legacy are supported (i.e. transitional device), "barnum"
+ * MUST NOT be 0.
+ */
+void
+vi_setup_pci_bar(struct virtio_softc *vs)
+{
+ struct virtio_consts *vc;
+
+ vc = vs->vs_vc;
+
+ if (vc->vc_en_legacy)
+ vi_legacy_set_io_bar(vs, 0);
+ if (vc->vc_en_modern) {
+ assert(!vc->vc_en_legacy || vc->vc_modern_pcibar != 0);
+ vi_modern_setup_mem_bar(vs, vc->vc_modern_pcibar);
+ }
+}
+
/*
* 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,9 +521,9 @@
}
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 &
+ } else if ((vs->vs_negotiated_caps &
VIRTIO_RING_F_INDIRECT_DESC) == 0) {
EPRINTLN(
"%s: descriptor has forbidden INDIRECT flag, "
@@ -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 = ®stbl[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) {
/*
@@ -612,7 +812,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 */
@@ -629,14 +829,17 @@
switch (offset) {
case VIRTIO_PCI_HOST_FEATURES:
- value = vc->vc_hv_caps;
+ /* Caps for legacy PCI configuration layout is only 32bit */
+ value = vi_hv_features(vs, false);
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 ?
@@ -655,7 +858,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;
@@ -666,23 +869,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;
@@ -691,27 +893,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) {
/*
@@ -727,11 +914,11 @@
else
error = 0;
if (!error)
- goto done;
+ 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 */
@@ -748,12 +935,12 @@
"%s: write to bad offset/size %jd/%d",
name, (uintmax_t)offset, size);
}
- goto done;
+ return;
}
switch (offset) {
case VIRTIO_PCI_GUEST_FEATURES:
- vs->vs_negotiated_caps = value & vc->vc_hv_caps;
+ vs->vs_negotiated_caps = vi_hv_features(vs, false);
if (vc->vc_apply_features)
(*vc->vc_apply_features)(DEV_SOFTC(vs),
vs->vs_negotiated_caps);
@@ -761,7 +948,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:
/*
@@ -775,7 +962,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)
@@ -802,61 +989,823 @@
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 = vi_hv_features(vs, true);
+ if (vs->vs_flags & VIRTIO_DFSELECT_HI)
+ value >>= 32;
+ else
+ value & 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));
-
- return (0);
-}
+ name = vc->vc_name;
+ value &= mask;
-static int
-vi_pci_snapshot_softc(struct virtio_softc *vs, struct vm_snapshot_meta *meta)
-{
- int ret;
+ 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;
+ }
- SNAPSHOT_VAR_OR_LEAVE(vs->vs_flags, meta, ret, done);
+ 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:
+ value &= vi_hv_features(vs, true);
+ if (vs->vs_flags & VIRTIO_GFSELECT_HI)
+ vs->vs_negotiated_caps = (vs->vs_negotiated_caps & 0xffffffff) |
+ (value << 32);
+ else
+ 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 != NULL)
+ (*vc->vc_cfgwrite)(DEV_SOFTC(vs), offset, size, value);
+}
+
+/*
+ * 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)) {
+ return (-1);
+ }
+
+ 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 = -1ull;
+ 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);
SNAPSHOT_VAR_OR_LEAVE(vs->vs_curq, meta, ret, done);
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);
@@ -869,7 +1818,10 @@
SNAPSHOT_VAR_CMP_OR_LEAVE(vc->vc_nvq, meta, ret, done);
SNAPSHOT_VAR_CMP_OR_LEAVE(vc->vc_cfgsize, meta, ret, done);
- SNAPSHOT_VAR_CMP_OR_LEAVE(vc->vc_hv_caps, meta, ret, done);
+ SNAPSHOT_VAR_CMP_OR_LEAVE(vc->vc_hv_caps_legacy, meta, ret, done);
+ SNAPSHOT_VAR_CMP_OR_LEAVE(vc->vc_hv_caps_modern, meta, ret, done);
+ SNAPSHOT_VAR_CMP_OR_LEAVE(vc->vc_en_legacy, meta, ret, done);
+ SNAPSHOT_VAR_CMP_OR_LEAVE(vc->vc_en_modern, meta, ret, done);
done:
return (ret);
@@ -899,7 +1851,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
Details
Attached
Mime Type
text/plain
Expires
Wed, Mar 18, 5:43 PM (7 h, 45 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
29918415
Default Alt Text
D29708.diff (60 KB)
Attached To
Mode
D29708: bhyve: Add virtio modern support handling
Attached
Detach File
Event Timeline
Log In to Comment