diff options
author | Andre Przywara <andre.przywara@arm.com> | 2023-11-07 16:09:00 +0000 |
---|---|---|
committer | Tom Rini <trini@konsulko.com> | 2023-11-17 11:58:26 -0500 |
commit | 45c4b276f0a4d67e06c94de3d0a5dadf4bee530a (patch) | |
tree | 5378af6bbb11115d1eb807df683ba0efac866043 | |
parent | 741d1e9d3f368908e3cd1861ddd707e81e1fd576 (diff) | |
download | u-boot-45c4b276f0a4d67e06c94de3d0a5dadf4bee530a.zip u-boot-45c4b276f0a4d67e06c94de3d0a5dadf4bee530a.tar.gz u-boot-45c4b276f0a4d67e06c94de3d0a5dadf4bee530a.tar.bz2 |
virtio: rng: gracefully handle 0 byte returns
According to the virtio v1.x "entropy device" specification, a virtio-rng
device is supposed to always return at least one byte of entropy.
However the virtio v0.9 spec does not mention such a requirement.
The Arm Fixed Virtual Platform (FVP) implementation of virtio-rng always
returns 8 bytes less of entropy than requested. If 8 bytes or less are
requested, it will return 0 bytes.
This behaviour makes U-Boot's virtio_rng_read() implementation go into an
endless loop, hanging the system.
Work around this problem by always requesting 8 bytes more than needed,
but only if a previous call to virtqueue_get_buf() returned 0 bytes.
This should never trigger on a v1.x spec compliant implementation, but
fixes the hang on the Arm FVP.
Signed-off-by: Andre Przywara <andre.przywara@arm.com>
Reported-by: Peter Hoyes <peter.hoyes@arm.com>
-rw-r--r-- | drivers/virtio/virtio_rng.c | 9 |
1 files changed, 7 insertions, 2 deletions
diff --git a/drivers/virtio/virtio_rng.c b/drivers/virtio/virtio_rng.c index b85545c..786359a 100644 --- a/drivers/virtio/virtio_rng.c +++ b/drivers/virtio/virtio_rng.c @@ -20,7 +20,7 @@ struct virtio_rng_priv { static int virtio_rng_read(struct udevice *dev, void *data, size_t len) { int ret; - unsigned int rsize; + unsigned int rsize = 1; unsigned char buf[BUFFER_SIZE] __aligned(4); unsigned char *ptr = data; struct virtio_sg sg; @@ -29,7 +29,12 @@ static int virtio_rng_read(struct udevice *dev, void *data, size_t len) while (len) { sg.addr = buf; - sg.length = min(len, sizeof(buf)); + /* + * Work around implementations which always return 8 bytes + * less than requested, down to 0 bytes, which would + * cause an endless loop otherwise. + */ + sg.length = min(rsize ? len : len + 8, sizeof(buf)); sgs[0] = &sg; ret = virtqueue_add(priv->rng_vq, sgs, 0, 1); |