diff options
author | Tom Rini | 2022-06-08 11:15:28 -0400 |
---|---|---|
committer | Tom Rini | 2022-06-08 11:15:28 -0400 |
commit | c0e63bf46848d573b3ef86d5796f8f993c316ed6 (patch) | |
tree | 7967ae792497f0618b8ec3e2c22a0721cbcdae29 | |
parent | ed1cbbe2afe4d4c7c25316db4c2e15c4c579fc4e (diff) | |
parent | d036104a02995efe416dd5ada503408ae37b56a5 (diff) |
Merge branch '2022-06-08-virtio-harden-and-test-vring' into next
To quote the author:
Make the virtio ring code resilient against corruption of the buffers
shared with the device.
It follows the example of Linux by keeping a private copy of the
descriptors and metadata for state tracking and only ever writing to the
descriptors that are shared with the device. I was able to test these
hardening steps in the sandbox by simulating device writes to the
queues.
-rw-r--r-- | drivers/virtio/virtio_ring.c | 96 | ||||
-rw-r--r-- | drivers/virtio/virtio_rng.c | 3 | ||||
-rw-r--r-- | drivers/virtio/virtio_sandbox.c | 4 | ||||
-rw-r--r-- | include/virtio_ring.h | 12 | ||||
-rw-r--r-- | test/dm/Makefile | 6 | ||||
-rw-r--r-- | test/dm/virtio.c | 99 | ||||
-rw-r--r-- | test/dm/virtio_device.c | 195 | ||||
-rw-r--r-- | test/dm/virtio_rng.c | 52 |
8 files changed, 330 insertions, 137 deletions
diff --git a/drivers/virtio/virtio_ring.c b/drivers/virtio/virtio_ring.c index 7f1cbc59329..f71bab78477 100644 --- a/drivers/virtio/virtio_ring.c +++ b/drivers/virtio/virtio_ring.c @@ -16,21 +16,40 @@ #include <linux/bug.h> #include <linux/compat.h> +static unsigned int virtqueue_attach_desc(struct virtqueue *vq, unsigned int i, + struct virtio_sg *sg, u16 flags) +{ + struct vring_desc_shadow *desc_shadow = &vq->vring_desc_shadow[i]; + struct vring_desc *desc = &vq->vring.desc[i]; + + /* Update the shadow descriptor. */ + desc_shadow->addr = (u64)(uintptr_t)sg->addr; + desc_shadow->len = sg->length; + desc_shadow->flags = flags; + + /* Update the shared descriptor to match the shadow. */ + desc->addr = cpu_to_virtio64(vq->vdev, desc_shadow->addr); + desc->len = cpu_to_virtio32(vq->vdev, desc_shadow->len); + desc->flags = cpu_to_virtio16(vq->vdev, desc_shadow->flags); + desc->next = cpu_to_virtio16(vq->vdev, desc_shadow->next); + + return desc_shadow->next; +} + int virtqueue_add(struct virtqueue *vq, struct virtio_sg *sgs[], unsigned int out_sgs, unsigned int in_sgs) { struct vring_desc *desc; - unsigned int total_sg = out_sgs + in_sgs; - unsigned int i, n, avail, descs_used, uninitialized_var(prev); + unsigned int descs_used = out_sgs + in_sgs; + unsigned int i, n, avail, uninitialized_var(prev); int head; - WARN_ON(total_sg == 0); + WARN_ON(descs_used == 0); head = vq->free_head; desc = vq->vring.desc; i = head; - descs_used = total_sg; if (vq->num_free < descs_used) { debug("Can't add buf len %i - avail = %i\n", @@ -45,30 +64,17 @@ int virtqueue_add(struct virtqueue *vq, struct virtio_sg *sgs[], return -ENOSPC; } - for (n = 0; n < out_sgs; n++) { - struct virtio_sg *sg = sgs[n]; - - desc[i].flags = cpu_to_virtio16(vq->vdev, VRING_DESC_F_NEXT); - desc[i].addr = cpu_to_virtio64(vq->vdev, (u64)(size_t)sg->addr); - desc[i].len = cpu_to_virtio32(vq->vdev, sg->length); - - prev = i; - i = virtio16_to_cpu(vq->vdev, desc[i].next); - } - for (; n < (out_sgs + in_sgs); n++) { - struct virtio_sg *sg = sgs[n]; - - desc[i].flags = cpu_to_virtio16(vq->vdev, VRING_DESC_F_NEXT | - VRING_DESC_F_WRITE); - desc[i].addr = cpu_to_virtio64(vq->vdev, - (u64)(uintptr_t)sg->addr); - desc[i].len = cpu_to_virtio32(vq->vdev, sg->length); + for (n = 0; n < descs_used; n++) { + u16 flags = VRING_DESC_F_NEXT; + if (n >= out_sgs) + flags |= VRING_DESC_F_WRITE; prev = i; - i = virtio16_to_cpu(vq->vdev, desc[i].next); + i = virtqueue_attach_desc(vq, i, sgs[n], flags); } /* Last one doesn't continue */ - desc[prev].flags &= cpu_to_virtio16(vq->vdev, ~VRING_DESC_F_NEXT); + vq->vring_desc_shadow[prev].flags &= ~VRING_DESC_F_NEXT; + desc[prev].flags = cpu_to_virtio16(vq->vdev, vq->vring_desc_shadow[prev].flags); /* We're using some buffers from the free list. */ vq->num_free -= descs_used; @@ -76,6 +82,9 @@ int virtqueue_add(struct virtqueue *vq, struct virtio_sg *sgs[], /* Update free pointer */ vq->free_head = i; + /* Mark the descriptor as the head of a chain. */ + vq->vring_desc_shadow[head].chain_head = true; + /* * Put entry in available array (but don't update avail->idx * until they do sync). @@ -137,17 +146,19 @@ void virtqueue_kick(struct virtqueue *vq) static void detach_buf(struct virtqueue *vq, unsigned int head) { unsigned int i; - __virtio16 nextflag = cpu_to_virtio16(vq->vdev, VRING_DESC_F_NEXT); + + /* Unmark the descriptor as the head of a chain. */ + vq->vring_desc_shadow[head].chain_head = false; /* Put back on free list: unmap first-level descriptors and find end */ i = head; - while (vq->vring.desc[i].flags & nextflag) { - i = virtio16_to_cpu(vq->vdev, vq->vring.desc[i].next); + while (vq->vring_desc_shadow[i].flags & VRING_DESC_F_NEXT) { + i = vq->vring_desc_shadow[i].next; vq->num_free++; } - vq->vring.desc[i].next = cpu_to_virtio16(vq->vdev, vq->free_head); + vq->vring_desc_shadow[i].next = vq->free_head; vq->free_head = head; /* Plus final descriptor */ @@ -189,6 +200,12 @@ void *virtqueue_get_buf(struct virtqueue *vq, unsigned int *len) return NULL; } + if (unlikely(!vq->vring_desc_shadow[i].chain_head)) { + printf("(%s.%d): id %u is not a head\n", + vq->vdev->name, vq->index, i); + return NULL; + } + detach_buf(vq, i); vq->last_used_idx++; /* @@ -200,8 +217,7 @@ void *virtqueue_get_buf(struct virtqueue *vq, unsigned int *len) virtio_store_mb(&vring_used_event(&vq->vring), cpu_to_virtio16(vq->vdev, vq->last_used_idx)); - return (void *)(uintptr_t)virtio64_to_cpu(vq->vdev, - vq->vring.desc[i].addr); + return (void *)(uintptr_t)vq->vring_desc_shadow[i].addr; } static struct virtqueue *__vring_new_virtqueue(unsigned int index, @@ -210,6 +226,7 @@ static struct virtqueue *__vring_new_virtqueue(unsigned int index, { unsigned int i; struct virtqueue *vq; + struct vring_desc_shadow *vring_desc_shadow; struct virtio_dev_priv *uc_priv = dev_get_uclass_priv(udev); struct udevice *vdev = uc_priv->vdev; @@ -217,10 +234,17 @@ static struct virtqueue *__vring_new_virtqueue(unsigned int index, if (!vq) return NULL; + vring_desc_shadow = calloc(vring.num, sizeof(struct vring_desc_shadow)); + if (!vring_desc_shadow) { + free(vq); + return NULL; + } + vq->vdev = vdev; vq->index = index; vq->num_free = vring.num; vq->vring = vring; + vq->vring_desc_shadow = vring_desc_shadow; vq->last_used_idx = 0; vq->avail_flags_shadow = 0; vq->avail_idx_shadow = 0; @@ -238,7 +262,7 @@ static struct virtqueue *__vring_new_virtqueue(unsigned int index, /* Put everything in free lists */ vq->free_head = 0; for (i = 0; i < vring.num - 1; i++) - vq->vring.desc[i].next = cpu_to_virtio16(vdev, i + 1); + vq->vring_desc_shadow[i].next = i + 1; return vq; } @@ -291,6 +315,7 @@ struct virtqueue *vring_create_virtqueue(unsigned int index, unsigned int num, void vring_del_virtqueue(struct virtqueue *vq) { free(vq->vring.desc); + free(vq->vring_desc_shadow); list_del(&vq->list); free(vq); } @@ -336,11 +361,12 @@ void virtqueue_dump(struct virtqueue *vq) printf("\tlast_used_idx %u, avail_flags_shadow %u, avail_idx_shadow %u\n", vq->last_used_idx, vq->avail_flags_shadow, vq->avail_idx_shadow); - printf("Descriptor dump:\n"); + printf("Shadow descriptor dump:\n"); for (i = 0; i < vq->vring.num; i++) { - printf("\tdesc[%u] = { 0x%llx, len %u, flags %u, next %u }\n", - i, vq->vring.desc[i].addr, vq->vring.desc[i].len, - vq->vring.desc[i].flags, vq->vring.desc[i].next); + struct vring_desc_shadow *desc = &vq->vring_desc_shadow[i]; + + printf("\tdesc_shadow[%u] = { 0x%llx, len %u, flags %u, next %u }\n", + i, desc->addr, desc->len, desc->flags, desc->next); } printf("Avail ring dump:\n"); diff --git a/drivers/virtio/virtio_rng.c b/drivers/virtio/virtio_rng.c index 9314c0a03ed..b85545c2ee5 100644 --- a/drivers/virtio/virtio_rng.c +++ b/drivers/virtio/virtio_rng.c @@ -41,6 +41,9 @@ static int virtio_rng_read(struct udevice *dev, void *data, size_t len) while (!virtqueue_get_buf(priv->rng_vq, &rsize)) ; + if (rsize > sg.length) + return -EIO; + memcpy(ptr, buf, rsize); len -= rsize; ptr += rsize; diff --git a/drivers/virtio/virtio_sandbox.c b/drivers/virtio/virtio_sandbox.c index aafb7beb949..5484ae3a1a0 100644 --- a/drivers/virtio/virtio_sandbox.c +++ b/drivers/virtio/virtio_sandbox.c @@ -160,8 +160,8 @@ static int virtio_sandbox_probe(struct udevice *udev) struct virtio_dev_priv *uc_priv = dev_get_uclass_priv(udev); /* fake some information for testing */ - priv->device_features = VIRTIO_F_VERSION_1; - uc_priv->device = VIRTIO_ID_BLOCK; + priv->device_features = BIT_ULL(VIRTIO_F_VERSION_1); + uc_priv->device = VIRTIO_ID_RNG; uc_priv->vendor = ('u' << 24) | ('b' << 16) | ('o' << 8) | 't'; return 0; diff --git a/include/virtio_ring.h b/include/virtio_ring.h index 6fc0593b14b..c77c212cffd 100644 --- a/include/virtio_ring.h +++ b/include/virtio_ring.h @@ -55,6 +55,16 @@ struct vring_desc { __virtio16 next; }; +/* Shadow of struct vring_desc in guest byte order. */ +struct vring_desc_shadow { + u64 addr; + u32 len; + u16 flags; + u16 next; + /* Metadata about the descriptor. */ + bool chain_head; +}; + struct vring_avail { __virtio16 flags; __virtio16 idx; @@ -89,6 +99,7 @@ struct vring { * @index: the zero-based ordinal number for this queue * @num_free: number of elements we expect to be able to fit * @vring: actual memory layout for this queue + * @vring_desc_shadow: guest-only copy of descriptors * @event: host publishes avail event idx * @free_head: head of free buffer list * @num_added: number we've added since last sync @@ -102,6 +113,7 @@ struct virtqueue { unsigned int index; unsigned int num_free; struct vring vring; + struct vring_desc_shadow *vring_desc_shadow; bool event; unsigned int free_head; unsigned int num_added; diff --git a/test/dm/Makefile b/test/dm/Makefile index f0a7c97e3d1..caea52f4e2a 100644 --- a/test/dm/Makefile +++ b/test/dm/Makefile @@ -107,7 +107,11 @@ obj-$(CONFIG_TEE) += tee.o obj-$(CONFIG_TIMER) += timer.o obj-$(CONFIG_DM_USB) += usb.o obj-$(CONFIG_DM_VIDEO) += video.o -obj-$(CONFIG_VIRTIO_SANDBOX) += virtio.o +ifeq ($(CONFIG_VIRTIO_SANDBOX),y) +obj-y += virtio.o +obj-$(CONFIG_VIRTIO_RNG) += virtio_device.o +obj-$(CONFIG_VIRTIO_RNG) += virtio_rng.o +endif ifeq ($(CONFIG_WDT_GPIO)$(CONFIG_WDT_SANDBOX),yy) obj-y += wdt.o endif diff --git a/test/dm/virtio.c b/test/dm/virtio.c index 9a7e658cceb..3e108cdc35d 100644 --- a/test/dm/virtio.c +++ b/test/dm/virtio.c @@ -7,7 +7,6 @@ #include <dm.h> #include <virtio_types.h> #include <virtio.h> -#include <virtio_ring.h> #include <dm/device-internal.h> #include <dm/root.h> #include <dm/test.h> @@ -15,78 +14,6 @@ #include <test/test.h> #include <test/ut.h> -/* Basic test of the virtio uclass */ -static int dm_test_virtio_base(struct unit_test_state *uts) -{ - struct udevice *bus, *dev; - u8 status; - - /* check probe success */ - ut_assertok(uclass_first_device(UCLASS_VIRTIO, &bus)); - ut_assertnonnull(bus); - - /* check the child virtio-blk device is bound */ - ut_assertok(device_find_first_child(bus, &dev)); - ut_assertnonnull(dev); - ut_assertok(strcmp(dev->name, "virtio-blk#0")); - - /* check driver status */ - ut_assertok(virtio_get_status(dev, &status)); - ut_asserteq(VIRTIO_CONFIG_S_ACKNOWLEDGE, status); - - return 0; -} -DM_TEST(dm_test_virtio_base, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); - -/* Test all of the virtio uclass ops */ -static int dm_test_virtio_all_ops(struct unit_test_state *uts) -{ - struct udevice *bus, *dev; - struct virtio_dev_priv *uc_priv; - uint offset = 0, len = 0, nvqs = 1; - void *buffer = NULL; - u8 status; - u32 counter; - u64 features; - struct virtqueue *vqs[2]; - - /* check probe success */ - ut_assertok(uclass_first_device(UCLASS_VIRTIO, &bus)); - ut_assertnonnull(bus); - - /* check the child virtio-blk device is bound */ - ut_assertok(device_find_first_child(bus, &dev)); - ut_assertnonnull(dev); - - /* - * fake the virtio device probe by filling in uc_priv->vdev - * which is used by virtio_find_vqs/virtio_del_vqs. - */ - uc_priv = dev_get_uclass_priv(bus); - ut_assertnonnull(uc_priv); - uc_priv->vdev = dev; - - /* test virtio_xxx APIs */ - ut_assertok(virtio_get_config(dev, offset, buffer, len)); - ut_assertok(virtio_set_config(dev, offset, buffer, len)); - ut_asserteq(-ENOSYS, virtio_generation(dev, &counter)); - ut_assertok(virtio_set_status(dev, VIRTIO_CONFIG_S_DRIVER_OK)); - ut_assertok(virtio_get_status(dev, &status)); - ut_asserteq(VIRTIO_CONFIG_S_DRIVER_OK, status); - ut_assertok(virtio_reset(dev)); - ut_assertok(virtio_get_status(dev, &status)); - ut_asserteq(0, status); - ut_assertok(virtio_get_features(dev, &features)); - ut_asserteq(VIRTIO_F_VERSION_1, features); - ut_assertok(virtio_set_features(dev)); - ut_assertok(virtio_find_vqs(dev, nvqs, vqs)); - ut_assertok(virtio_del_vqs(dev)); - ut_assertok(virtio_notify(dev, vqs[0])); - - return 0; -} -DM_TEST(dm_test_virtio_all_ops, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); - /* Test of the virtio driver that does not have required driver ops */ static int dm_test_virtio_missing_ops(struct unit_test_state *uts) { @@ -104,29 +31,3 @@ static int dm_test_virtio_missing_ops(struct unit_test_state *uts) return 0; } DM_TEST(dm_test_virtio_missing_ops, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); - -/* Test removal of virtio device driver */ -static int dm_test_virtio_remove(struct unit_test_state *uts) -{ - struct udevice *bus, *dev; - - /* check probe success */ - ut_assertok(uclass_first_device(UCLASS_VIRTIO, &bus)); - ut_assertnonnull(bus); - - /* check the child virtio-blk device is bound */ - ut_assertok(device_find_first_child(bus, &dev)); - ut_assertnonnull(dev); - - /* set driver status to VIRTIO_CONFIG_S_DRIVER_OK */ - ut_assertok(virtio_set_status(dev, VIRTIO_CONFIG_S_DRIVER_OK)); - - /* check the device can be successfully removed */ - dev_or_flags(dev, DM_FLAG_ACTIVATED); - ut_asserteq(-EKEYREJECTED, device_remove(bus, DM_REMOVE_ACTIVE_ALL)); - - ut_asserteq(false, device_active(dev)); - - return 0; -} -DM_TEST(dm_test_virtio_remove, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); diff --git a/test/dm/virtio_device.c b/test/dm/virtio_device.c new file mode 100644 index 00000000000..d0195e6bf09 --- /dev/null +++ b/test/dm/virtio_device.c @@ -0,0 +1,195 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2018, Bin Meng <bmeng.cn@gmail.com> + */ + +#include <common.h> +#include <dm.h> +#include <virtio_types.h> +#include <virtio.h> +#include <virtio_ring.h> +#include <dm/device-internal.h> +#include <dm/root.h> +#include <dm/test.h> +#include <dm/uclass-internal.h> +#include <test/test.h> +#include <test/ut.h> + +/* Basic test of the virtio uclass */ +static int dm_test_virtio_base(struct unit_test_state *uts) +{ + struct udevice *bus, *dev; + u8 status; + + /* check probe success */ + ut_assertok(uclass_first_device(UCLASS_VIRTIO, &bus)); + ut_assertnonnull(bus); + + /* check the child virtio-rng device is bound */ + ut_assertok(device_find_first_child(bus, &dev)); + ut_assertnonnull(dev); + ut_asserteq_str("virtio-rng#0", dev->name); + + /* check driver status */ + ut_assertok(virtio_get_status(dev, &status)); + ut_asserteq(VIRTIO_CONFIG_S_ACKNOWLEDGE, status); + + /* probe the virtio-rng driver */ + ut_assertok(device_probe(dev)); + + /* check the device was reset and the driver picked up the device */ + ut_assertok(virtio_get_status(dev, &status)); + ut_asserteq(VIRTIO_CONFIG_S_DRIVER | + VIRTIO_CONFIG_S_DRIVER_OK | + VIRTIO_CONFIG_S_FEATURES_OK, status); + + return 0; +} +DM_TEST(dm_test_virtio_base, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Test all of the virtio uclass ops */ +static int dm_test_virtio_all_ops(struct unit_test_state *uts) +{ + struct udevice *bus, *dev; + struct virtio_dev_priv *uc_priv; + uint offset = 0, len = 0, nvqs = 1; + void *buffer = NULL; + u8 status; + u32 counter; + u64 features; + struct virtqueue *vqs[2]; + + /* check probe success */ + ut_assertok(uclass_first_device(UCLASS_VIRTIO, &bus)); + ut_assertnonnull(bus); + + /* check the child virtio-rng device is bound */ + ut_assertok(device_find_first_child(bus, &dev)); + ut_assertnonnull(dev); + + /* + * fake the virtio device probe by filling in uc_priv->vdev + * which is used by virtio_find_vqs/virtio_del_vqs. + */ + uc_priv = dev_get_uclass_priv(bus); + ut_assertnonnull(uc_priv); + uc_priv->vdev = dev; + + /* test virtio_xxx APIs */ + ut_assertok(virtio_get_config(dev, offset, buffer, len)); + ut_assertok(virtio_set_config(dev, offset, buffer, len)); + ut_asserteq(-ENOSYS, virtio_generation(dev, &counter)); + ut_assertok(virtio_set_status(dev, VIRTIO_CONFIG_S_DRIVER_OK)); + ut_assertok(virtio_get_status(dev, &status)); + ut_asserteq(VIRTIO_CONFIG_S_DRIVER_OK, status); + ut_assertok(virtio_reset(dev)); + ut_assertok(virtio_get_status(dev, &status)); + ut_asserteq(0, status); + ut_assertok(virtio_get_features(dev, &features)); + ut_asserteq_64(BIT_ULL(VIRTIO_F_VERSION_1), features); + ut_assertok(virtio_set_features(dev)); + ut_assertok(virtio_find_vqs(dev, nvqs, vqs)); + ut_assertok(virtio_notify(dev, vqs[0])); + ut_assertok(virtio_del_vqs(dev)); + + return 0; +} +DM_TEST(dm_test_virtio_all_ops, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Test removal of virtio device driver */ +static int dm_test_virtio_remove(struct unit_test_state *uts) +{ + struct udevice *bus, *dev; + + /* check probe success */ + ut_assertok(uclass_first_device(UCLASS_VIRTIO, &bus)); + ut_assertnonnull(bus); + + /* check the child virtio-rng device is bound */ + ut_assertok(device_find_first_child(bus, &dev)); + ut_assertnonnull(dev); + + /* set driver status to VIRTIO_CONFIG_S_DRIVER_OK */ + ut_assertok(virtio_set_status(dev, VIRTIO_CONFIG_S_DRIVER_OK)); + + /* check the device can be successfully removed */ + dev_or_flags(dev, DM_FLAG_ACTIVATED); + ut_asserteq(-EKEYREJECTED, device_remove(bus, DM_REMOVE_ACTIVE_ALL)); + + ut_asserteq(false, device_active(dev)); + + return 0; +} +DM_TEST(dm_test_virtio_remove, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Test all of the virtio ring */ +static int dm_test_virtio_ring(struct unit_test_state *uts) +{ + struct udevice *bus, *dev; + struct virtio_dev_priv *uc_priv; + struct virtqueue *vq; + struct virtio_sg sg[2]; + struct virtio_sg *sgs[2]; + unsigned int len; + u8 buffer[2][32]; + + /* check probe success */ + ut_assertok(uclass_first_device(UCLASS_VIRTIO, &bus)); + ut_assertnonnull(bus); + + /* check the child virtio-blk device is bound */ + ut_assertok(device_find_first_child(bus, &dev)); + ut_assertnonnull(dev); + + /* + * fake the virtio device probe by filling in uc_priv->vdev + * which is used by virtio_find_vqs/virtio_del_vqs. + */ + uc_priv = dev_get_uclass_priv(bus); + ut_assertnonnull(uc_priv); + uc_priv->vdev = dev; + + /* prepare the scatter-gather buffer */ + sg[0].addr = buffer[0]; + sg[0].length = sizeof(buffer[0]); + sg[1].addr = buffer[1]; + sg[1].length = sizeof(buffer[1]); + sgs[0] = &sg[0]; + sgs[1] = &sg[1]; + + /* read a buffer and report written size from device */ + ut_assertok(virtio_find_vqs(dev, 1, &vq)); + ut_assertok(virtqueue_add(vq, sgs, 0, 1)); + vq->vring.used->idx = 1; + vq->vring.used->ring[0].id = 0; + vq->vring.used->ring[0].len = 0x53355885; + ut_asserteq_ptr(buffer, virtqueue_get_buf(vq, &len)); + ut_asserteq(0x53355885, len); + ut_assertok(virtio_del_vqs(dev)); + + /* rejects used descriptors that aren't a chain head */ + ut_assertok(virtio_find_vqs(dev, 1, &vq)); + ut_assertok(virtqueue_add(vq, sgs, 0, 2)); + vq->vring.used->idx = 1; + vq->vring.used->ring[0].id = 1; + vq->vring.used->ring[0].len = 0x53355885; + ut_assertnull(virtqueue_get_buf(vq, &len)); + ut_assertok(virtio_del_vqs(dev)); + + /* device changes to descriptor are ignored */ + ut_assertok(virtio_find_vqs(dev, 1, &vq)); + ut_assertok(virtqueue_add(vq, sgs, 0, 1)); + vq->vring.desc[0].addr = cpu_to_virtio64(dev, 0xbadbad11); + vq->vring.desc[0].len = cpu_to_virtio32(dev, 0x11badbad); + vq->vring.desc[0].flags = cpu_to_virtio16(dev, VRING_DESC_F_NEXT); + vq->vring.desc[0].next = cpu_to_virtio16(dev, U16_MAX); + vq->vring.used->idx = 1; + vq->vring.used->ring[0].id = 0; + vq->vring.used->ring[0].len = 6; + ut_asserteq_ptr(buffer, virtqueue_get_buf(vq, &len)); + ut_asserteq(6, len); + ut_assertok(virtio_del_vqs(dev)); + + return 0; +} +DM_TEST(dm_test_virtio_ring, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); diff --git a/test/dm/virtio_rng.c b/test/dm/virtio_rng.c new file mode 100644 index 00000000000..ff5646b4e11 --- /dev/null +++ b/test/dm/virtio_rng.c @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2022 Google, Inc. + * Written by Andrew Scull <ascull@google.com> + */ + +#include <common.h> +#include <dm.h> +#include <virtio_types.h> +#include <virtio.h> +#include <virtio_ring.h> +#include <dm/device-internal.h> +#include <dm/test.h> +#include <rng.h> +#include <test/test.h> +#include <test/ut.h> + +/* This is a brittle means of getting access to the virtqueue */ +struct virtio_rng_priv { + struct virtqueue *rng_vq; +}; + +/* Test the virtio-rng driver validates the used size */ +static int dm_test_virtio_rng_check_len(struct unit_test_state *uts) +{ + struct udevice *bus, *dev; + struct virtio_rng_priv *priv; + u8 buffer[16]; + + /* check probe success */ + ut_assertok(uclass_first_device(UCLASS_VIRTIO, &bus)); + ut_assertnonnull(bus); + + /* check the child virtio-rng device is bound */ + ut_assertok(device_find_first_child(bus, &dev)); + ut_assertnonnull(dev); + + /* probe the virtio-rng driver */ + ut_assertok(device_probe(dev)); + + /* simulate the device returning the buffer with too much data */ + priv = dev_get_priv(dev); + priv->rng_vq->vring.used->idx = 1; + priv->rng_vq->vring.used->ring[0].id = 0; + priv->rng_vq->vring.used->ring[0].len = U32_MAX; + + /* check the driver gracefully handles the error */ + ut_asserteq(-EIO, dm_rng_read(dev, buffer, sizeof(buffer))); + + return 0; +} +DM_TEST(dm_test_virtio_rng_check_len, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); |