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>
This commit is contained in:
@@ -20,7 +20,7 @@ struct virtio_rng_priv {
|
|||||||
static int virtio_rng_read(struct udevice *dev, void *data, size_t len)
|
static int virtio_rng_read(struct udevice *dev, void *data, size_t len)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
unsigned int rsize;
|
unsigned int rsize = 1;
|
||||||
unsigned char buf[BUFFER_SIZE] __aligned(4);
|
unsigned char buf[BUFFER_SIZE] __aligned(4);
|
||||||
unsigned char *ptr = data;
|
unsigned char *ptr = data;
|
||||||
struct virtio_sg sg;
|
struct virtio_sg sg;
|
||||||
@@ -29,7 +29,12 @@ static int virtio_rng_read(struct udevice *dev, void *data, size_t len)
|
|||||||
|
|
||||||
while (len) {
|
while (len) {
|
||||||
sg.addr = buf;
|
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;
|
sgs[0] = &sg;
|
||||||
|
|
||||||
ret = virtqueue_add(priv->rng_vq, sgs, 0, 1);
|
ret = virtqueue_add(priv->rng_vq, sgs, 0, 1);
|
||||||
|
|||||||
Reference in New Issue
Block a user