git: 926cedd9a071 - main - virtio_mmio: Improve V1 spec conformance
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Wed, 17 Aug 2022 20:16:15 UTC
The branch main has been updated by bryanv:
URL: https://cgit.FreeBSD.org/src/commit/?id=926cedd9a0713c8ed9ff340b09401847b8bfd62c
commit 926cedd9a0713c8ed9ff340b09401847b8bfd62c
Author: Bryan Venteicher <bryanv@FreeBSD.org>
AuthorDate: 2022-08-17 20:15:04 +0000
Commit: Bryan Venteicher <bryanv@FreeBSD.org>
CommitDate: 2022-08-17 20:15:04 +0000
virtio_mmio: Improve V1 spec conformance
Implement the virtio_bus_finalize_features method so the FEATURES_OK
status bit is set. Implement the virtio_bus_config_generation method
to ensure larger than 4-byte reads are consistent.
Reviewed by: cperciva
MFC after: 1 week
Differential Revision: https://reviews.freebsd.org/D36150
---
sys/dev/virtio/mmio/virtio_mmio.c | 87 +++++++++++++++++++++++++++++++++++----
1 file changed, 78 insertions(+), 9 deletions(-)
diff --git a/sys/dev/virtio/mmio/virtio_mmio.c b/sys/dev/virtio/mmio/virtio_mmio.c
index 5e17cf59a84a..e2164eb9e195 100644
--- a/sys/dev/virtio/mmio/virtio_mmio.c
+++ b/sys/dev/virtio/mmio/virtio_mmio.c
@@ -74,6 +74,7 @@ static void vtmmio_child_detached(device_t, device_t);
static int vtmmio_read_ivar(device_t, device_t, int, uintptr_t *);
static int vtmmio_write_ivar(device_t, device_t, int, uintptr_t);
static uint64_t vtmmio_negotiate_features(device_t, uint64_t);
+static int vtmmio_finalize_features(device_t);
static int vtmmio_with_feature(device_t, uint64_t);
static void vtmmio_set_virtqueue(struct vtmmio_softc *sc,
struct virtqueue *vq, uint32_t size);
@@ -85,9 +86,11 @@ static void vtmmio_poll(device_t);
static int vtmmio_reinit(device_t, uint64_t);
static void vtmmio_reinit_complete(device_t);
static void vtmmio_notify_virtqueue(device_t, uint16_t, bus_size_t);
+static int vtmmio_config_generation(device_t);
static uint8_t vtmmio_get_status(device_t);
static void vtmmio_set_status(device_t, uint8_t);
static void vtmmio_read_dev_config(device_t, bus_size_t, void *, int);
+static uint64_t vtmmio_read_dev_config_8(struct vtmmio_softc *, bus_size_t);
static void vtmmio_write_dev_config(device_t, bus_size_t, const void *, int);
static void vtmmio_describe_features(struct vtmmio_softc *, const char *,
uint64_t);
@@ -152,6 +155,7 @@ static device_method_t vtmmio_methods[] = {
/* VirtIO bus interface. */
DEVMETHOD(virtio_bus_negotiate_features, vtmmio_negotiate_features),
+ DEVMETHOD(virtio_bus_finalize_features, vtmmio_finalize_features),
DEVMETHOD(virtio_bus_with_feature, vtmmio_with_feature),
DEVMETHOD(virtio_bus_alloc_virtqueues, vtmmio_alloc_virtqueues),
DEVMETHOD(virtio_bus_setup_intr, vtmmio_setup_intr),
@@ -160,6 +164,7 @@ static device_method_t vtmmio_methods[] = {
DEVMETHOD(virtio_bus_reinit, vtmmio_reinit),
DEVMETHOD(virtio_bus_reinit_complete, vtmmio_reinit_complete),
DEVMETHOD(virtio_bus_notify_vq, vtmmio_notify_virtqueue),
+ DEVMETHOD(virtio_bus_config_generation, vtmmio_config_generation),
DEVMETHOD(virtio_bus_read_device_config, vtmmio_read_dev_config),
DEVMETHOD(virtio_bus_write_device_config, vtmmio_write_dev_config),
@@ -461,6 +466,31 @@ vtmmio_negotiate_features(device_t dev, uint64_t child_features)
return (features);
}
+static int
+vtmmio_finalize_features(device_t dev)
+{
+ struct vtmmio_softc *sc;
+ uint8_t status;
+
+ sc = device_get_softc(dev);
+
+ if (sc->vtmmio_version > 1) {
+ /*
+ * Must re-read the status after setting it to verify the
+ * negotiated features were accepted by the device.
+ */
+ vtmmio_set_status(dev, VIRTIO_CONFIG_S_FEATURES_OK);
+
+ status = vtmmio_get_status(dev);
+ if ((status & VIRTIO_CONFIG_S_FEATURES_OK) == 0) {
+ device_printf(dev, "desired features were not accepted\n");
+ return (ENOTSUP);
+ }
+ }
+
+ return (0);
+}
+
static int
vtmmio_with_feature(device_t dev, uint64_t feature)
{
@@ -540,8 +570,6 @@ vtmmio_alloc_virtqueues(device_t dev, int flags, int nvqs,
vqx = &sc->vtmmio_vqs[idx];
info = &vq_info[idx];
- vtmmio_write_config_4(sc, VIRTIO_MMIO_QUEUE_SEL, idx);
-
vtmmio_select_virtqueue(sc, idx);
size = vtmmio_read_config_4(sc, VIRTIO_MMIO_QUEUE_NUM_MAX);
@@ -605,7 +633,16 @@ vtmmio_reinit(device_t dev, uint64_t features)
vtmmio_set_status(dev, VIRTIO_CONFIG_STATUS_ACK);
vtmmio_set_status(dev, VIRTIO_CONFIG_STATUS_DRIVER);
+ /*
+ * TODO: Check that features are not added as to what was
+ * originally negotiated.
+ */
vtmmio_negotiate_features(dev, features);
+ error = vtmmio_finalize_features(dev);
+ if (error) {
+ device_printf(dev, "cannot finalize features during reinit\n");
+ return (error);
+ }
if (sc->vtmmio_version == 1) {
vtmmio_write_config_4(sc, VIRTIO_MMIO_GUEST_PAGE_SIZE,
@@ -639,6 +676,22 @@ vtmmio_notify_virtqueue(device_t dev, uint16_t queue, bus_size_t offset)
vtmmio_write_config_4(sc, offset, queue);
}
+static int
+vtmmio_config_generation(device_t dev)
+{
+ struct vtmmio_softc *sc;
+ uint32_t gen;
+
+ sc = device_get_softc(dev);
+
+ if (sc->vtmmio_version > 1)
+ gen = vtmmio_read_config_4(sc, VIRTIO_MMIO_CONFIG_GENERATION);
+ else
+ gen = 0;
+
+ return (gen);
+}
+
static uint8_t
vtmmio_get_status(device_t dev)
{
@@ -670,7 +723,6 @@ vtmmio_read_dev_config(device_t dev, bus_size_t offset,
bus_size_t off;
uint8_t *d;
int size;
- uint64_t low32, high32;
sc = device_get_softc(dev);
off = VIRTIO_MMIO_CONFIG + offset;
@@ -707,9 +759,7 @@ vtmmio_read_dev_config(device_t dev, bus_size_t offset,
le32toh(vtmmio_read_config_4(sc, off));
break;
case 8:
- low32 = le32toh(vtmmio_read_config_4(sc, off));
- high32 = le32toh(vtmmio_read_config_4(sc, off + 4));
- *(uint64_t *)dst = (high32 << 32) | low32;
+ *(uint64_t *)dst = vtmmio_read_dev_config_8(sc, off);
break;
default:
panic("%s: invalid length %d\n", __func__, length);
@@ -735,6 +785,24 @@ vtmmio_read_dev_config(device_t dev, bus_size_t offset,
}
}
+static uint64_t
+vtmmio_read_dev_config_8(struct vtmmio_softc *sc, bus_size_t off)
+{
+ device_t dev;
+ int gen;
+ uint32_t val0, val1;
+
+ dev = sc->dev;
+
+ do {
+ gen = vtmmio_config_generation(dev);
+ val0 = le32toh(vtmmio_read_config_4(sc, off));
+ val1 = le32toh(vtmmio_read_config_4(sc, off + 4));
+ } while (gen != vtmmio_config_generation(dev));
+
+ return (((uint64_t) val1 << 32) | val0);
+}
+
static void
vtmmio_write_dev_config(device_t dev, bus_size_t offset,
const void *src, int length)
@@ -888,10 +956,11 @@ vtmmio_free_virtqueues(struct vtmmio_softc *sc)
vqx = &sc->vtmmio_vqs[idx];
vtmmio_select_virtqueue(sc, idx);
- if (sc->vtmmio_version == 1)
- vtmmio_write_config_4(sc, VIRTIO_MMIO_QUEUE_PFN, 0);
- else
+ if (sc->vtmmio_version > 1) {
vtmmio_write_config_4(sc, VIRTIO_MMIO_QUEUE_READY, 0);
+ vtmmio_read_config_4(sc, VIRTIO_MMIO_QUEUE_READY);
+ } else
+ vtmmio_write_config_4(sc, VIRTIO_MMIO_QUEUE_PFN, 0);
virtqueue_free(vqx->vtv_vq);
vqx->vtv_vq = NULL;