virtio_ring: Maintain a shadow copy of descriptors
The shared descriptors should only be written by the guest driver, however, the device is still able to overwrite and corrupt them. Maintain a private shadow copy of the descriptors for the driver to use for state tracking, removing the need to read from the shared descriptors. Signed-off-by: Andrew Scull <ascull@google.com> Reviewed-by: Simon Glass <sjg@chromium.org>
This commit is contained in:
parent
b0952977c9
commit
10a1453636
@ -19,13 +19,21 @@
|
||||
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];
|
||||
|
||||
desc->addr = cpu_to_virtio64(vq->vdev, (u64)(uintptr_t)sg->addr);
|
||||
desc->len = cpu_to_virtio32(vq->vdev, sg->length);
|
||||
desc->flags = cpu_to_virtio16(vq->vdev, flags);
|
||||
/* Update the shadow descriptor. */
|
||||
desc_shadow->addr = (u64)(uintptr_t)sg->addr;
|
||||
desc_shadow->len = sg->length;
|
||||
desc_shadow->flags = flags;
|
||||
|
||||
return virtio16_to_cpu(vq->vdev, desc->next);
|
||||
/* 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[],
|
||||
@ -65,7 +73,8 @@ int virtqueue_add(struct virtqueue *vq, struct virtio_sg *sgs[],
|
||||
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;
|
||||
@ -134,17 +143,16 @@ 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);
|
||||
|
||||
/* 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 */
|
||||
@ -197,8 +205,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,
|
||||
@ -207,6 +214,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;
|
||||
|
||||
@ -214,10 +222,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;
|
||||
@ -235,7 +250,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;
|
||||
}
|
||||
@ -288,6 +303,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);
|
||||
}
|
||||
@ -333,11 +349,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");
|
||||
|
@ -55,6 +55,14 @@ 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;
|
||||
};
|
||||
|
||||
struct vring_avail {
|
||||
__virtio16 flags;
|
||||
__virtio16 idx;
|
||||
@ -89,6 +97,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 +111,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;
|
||||
|
Loading…
Reference in New Issue
Block a user