mirror of https://gitee.com/openkylin/qemu.git
libqos: access VIRTIO 1.0 vring in little-endian
VIRTIO 1.0 uses little-endian for the vring. Legacy VIRTIO uses guest endianness. Adjust the code to handle both. Note that qvirtio_readq() is not defined because it has no users. All the other accessors are really needed. Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com> Reviewed-by: Thomas Huth <thuth@redhat.com> Message-Id: <20191023100425.12168-10-stefanha@redhat.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
This commit is contained in:
parent
c0f79698ed
commit
bccd82b407
|
@ -157,6 +157,7 @@ static QVirtQueue *qvirtio_mmio_virtqueue_setup(QVirtioDevice *d,
|
|||
uint64_t addr;
|
||||
|
||||
vq = g_malloc0(sizeof(*vq));
|
||||
vq->vdev = d;
|
||||
qvirtio_mmio_queue_select(d, index);
|
||||
qtest_writel(dev->qts, dev->addr + QVIRTIO_MMIO_QUEUE_ALIGN, dev->page_size);
|
||||
|
||||
|
|
|
@ -217,6 +217,7 @@ static QVirtQueue *qvirtio_pci_virtqueue_setup(QVirtioDevice *d,
|
|||
feat = qvirtio_pci_get_guest_features(d);
|
||||
|
||||
qvirtio_pci_queue_select(d, index);
|
||||
vqpci->vq.vdev = d;
|
||||
vqpci->vq.index = index;
|
||||
vqpci->vq.size = qvirtio_pci_get_queue_size(d);
|
||||
vqpci->vq.free_head = 0;
|
||||
|
|
|
@ -8,11 +8,68 @@
|
|||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/bswap.h"
|
||||
#include "libqtest.h"
|
||||
#include "libqos/virtio.h"
|
||||
#include "standard-headers/linux/virtio_config.h"
|
||||
#include "standard-headers/linux/virtio_ring.h"
|
||||
|
||||
/*
|
||||
* qtest_readX/writeX() functions transfer host endian from/to guest endian.
|
||||
* This works great for Legacy VIRTIO devices where we need guest endian
|
||||
* accesses. For VIRTIO 1.0 the vring is little-endian so the automatic guest
|
||||
* endianness conversion is not wanted.
|
||||
*
|
||||
* The following qvirtio_readX/writeX() functions handle Legacy and VIRTIO 1.0
|
||||
* accesses seamlessly.
|
||||
*/
|
||||
static uint16_t qvirtio_readw(QVirtioDevice *d, QTestState *qts, uint64_t addr)
|
||||
{
|
||||
uint16_t val = qtest_readw(qts, addr);
|
||||
|
||||
if (d->features & (1ull << VIRTIO_F_VERSION_1) && qtest_big_endian(qts)) {
|
||||
val = bswap16(val);
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
static uint32_t qvirtio_readl(QVirtioDevice *d, QTestState *qts, uint64_t addr)
|
||||
{
|
||||
uint32_t val = qtest_readl(qts, addr);
|
||||
|
||||
if (d->features & (1ull << VIRTIO_F_VERSION_1) && qtest_big_endian(qts)) {
|
||||
val = bswap32(val);
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
static void qvirtio_writew(QVirtioDevice *d, QTestState *qts,
|
||||
uint64_t addr, uint16_t val)
|
||||
{
|
||||
if (d->features & (1ull << VIRTIO_F_VERSION_1) && qtest_big_endian(qts)) {
|
||||
val = bswap16(val);
|
||||
}
|
||||
qtest_writew(qts, addr, val);
|
||||
}
|
||||
|
||||
static void qvirtio_writel(QVirtioDevice *d, QTestState *qts,
|
||||
uint64_t addr, uint32_t val)
|
||||
{
|
||||
if (d->features & (1ull << VIRTIO_F_VERSION_1) && qtest_big_endian(qts)) {
|
||||
val = bswap32(val);
|
||||
}
|
||||
qtest_writel(qts, addr, val);
|
||||
}
|
||||
|
||||
static void qvirtio_writeq(QVirtioDevice *d, QTestState *qts,
|
||||
uint64_t addr, uint64_t val)
|
||||
{
|
||||
if (d->features & (1ull << VIRTIO_F_VERSION_1) && qtest_big_endian(qts)) {
|
||||
val = bswap64(val);
|
||||
}
|
||||
qtest_writeq(qts, addr, val);
|
||||
}
|
||||
|
||||
uint8_t qvirtio_config_readb(QVirtioDevice *d, uint64_t addr)
|
||||
{
|
||||
g_assert_true(d->features_negotiated);
|
||||
|
@ -189,23 +246,23 @@ void qvring_init(QTestState *qts, const QGuestAllocator *alloc, QVirtQueue *vq,
|
|||
|
||||
for (i = 0; i < vq->size - 1; i++) {
|
||||
/* vq->desc[i].addr */
|
||||
qtest_writeq(qts, vq->desc + (16 * i), 0);
|
||||
qvirtio_writeq(vq->vdev, qts, vq->desc + (16 * i), 0);
|
||||
/* vq->desc[i].next */
|
||||
qtest_writew(qts, vq->desc + (16 * i) + 14, i + 1);
|
||||
qvirtio_writew(vq->vdev, qts, vq->desc + (16 * i) + 14, i + 1);
|
||||
}
|
||||
|
||||
/* vq->avail->flags */
|
||||
qtest_writew(qts, vq->avail, 0);
|
||||
qvirtio_writew(vq->vdev, qts, vq->avail, 0);
|
||||
/* vq->avail->idx */
|
||||
qtest_writew(qts, vq->avail + 2, 0);
|
||||
qvirtio_writew(vq->vdev, qts, vq->avail + 2, 0);
|
||||
/* vq->avail->used_event */
|
||||
qtest_writew(qts, vq->avail + 4 + (2 * vq->size), 0);
|
||||
qvirtio_writew(vq->vdev, qts, vq->avail + 4 + (2 * vq->size), 0);
|
||||
|
||||
/* vq->used->flags */
|
||||
qtest_writew(qts, vq->used, 0);
|
||||
qvirtio_writew(vq->vdev, qts, vq->used, 0);
|
||||
/* vq->used->avail_event */
|
||||
qtest_writew(qts, vq->used + 2 + sizeof(struct vring_used_elem) * vq->size,
|
||||
0);
|
||||
qvirtio_writew(vq->vdev, qts, vq->used + 2 +
|
||||
sizeof(struct vring_used_elem) * vq->size, 0);
|
||||
}
|
||||
|
||||
QVRingIndirectDesc *qvring_indirect_desc_setup(QTestState *qs, QVirtioDevice *d,
|
||||
|
@ -221,35 +278,39 @@ QVRingIndirectDesc *qvring_indirect_desc_setup(QTestState *qs, QVirtioDevice *d,
|
|||
|
||||
for (i = 0; i < elem - 1; ++i) {
|
||||
/* indirect->desc[i].addr */
|
||||
qtest_writeq(qs, indirect->desc + (16 * i), 0);
|
||||
qvirtio_writeq(d, qs, indirect->desc + (16 * i), 0);
|
||||
/* indirect->desc[i].flags */
|
||||
qtest_writew(qs, indirect->desc + (16 * i) + 12, VRING_DESC_F_NEXT);
|
||||
qvirtio_writew(d, qs, indirect->desc + (16 * i) + 12,
|
||||
VRING_DESC_F_NEXT);
|
||||
/* indirect->desc[i].next */
|
||||
qtest_writew(qs, indirect->desc + (16 * i) + 14, i + 1);
|
||||
qvirtio_writew(d, qs, indirect->desc + (16 * i) + 14, i + 1);
|
||||
}
|
||||
|
||||
return indirect;
|
||||
}
|
||||
|
||||
void qvring_indirect_desc_add(QTestState *qts, QVRingIndirectDesc *indirect,
|
||||
void qvring_indirect_desc_add(QVirtioDevice *d, QTestState *qts,
|
||||
QVRingIndirectDesc *indirect,
|
||||
uint64_t data, uint32_t len, bool write)
|
||||
{
|
||||
uint16_t flags;
|
||||
|
||||
g_assert_cmpint(indirect->index, <, indirect->elem);
|
||||
|
||||
flags = qtest_readw(qts, indirect->desc + (16 * indirect->index) + 12);
|
||||
flags = qvirtio_readw(d, qts, indirect->desc +
|
||||
(16 * indirect->index) + 12);
|
||||
|
||||
if (write) {
|
||||
flags |= VRING_DESC_F_WRITE;
|
||||
}
|
||||
|
||||
/* indirect->desc[indirect->index].addr */
|
||||
qtest_writeq(qts, indirect->desc + (16 * indirect->index), data);
|
||||
qvirtio_writeq(d, qts, indirect->desc + (16 * indirect->index), data);
|
||||
/* indirect->desc[indirect->index].len */
|
||||
qtest_writel(qts, indirect->desc + (16 * indirect->index) + 8, len);
|
||||
qvirtio_writel(d, qts, indirect->desc + (16 * indirect->index) + 8, len);
|
||||
/* indirect->desc[indirect->index].flags */
|
||||
qtest_writew(qts, indirect->desc + (16 * indirect->index) + 12, flags);
|
||||
qvirtio_writew(d, qts, indirect->desc + (16 * indirect->index) + 12,
|
||||
flags);
|
||||
|
||||
indirect->index++;
|
||||
}
|
||||
|
@ -269,11 +330,11 @@ uint32_t qvirtqueue_add(QTestState *qts, QVirtQueue *vq, uint64_t data,
|
|||
}
|
||||
|
||||
/* vq->desc[vq->free_head].addr */
|
||||
qtest_writeq(qts, vq->desc + (16 * vq->free_head), data);
|
||||
qvirtio_writeq(vq->vdev, qts, vq->desc + (16 * vq->free_head), data);
|
||||
/* vq->desc[vq->free_head].len */
|
||||
qtest_writel(qts, vq->desc + (16 * vq->free_head) + 8, len);
|
||||
qvirtio_writel(vq->vdev, qts, vq->desc + (16 * vq->free_head) + 8, len);
|
||||
/* vq->desc[vq->free_head].flags */
|
||||
qtest_writew(qts, vq->desc + (16 * vq->free_head) + 12, flags);
|
||||
qvirtio_writew(vq->vdev, qts, vq->desc + (16 * vq->free_head) + 12, flags);
|
||||
|
||||
return vq->free_head++; /* Return and increase, in this order */
|
||||
}
|
||||
|
@ -288,12 +349,13 @@ uint32_t qvirtqueue_add_indirect(QTestState *qts, QVirtQueue *vq,
|
|||
vq->num_free--;
|
||||
|
||||
/* vq->desc[vq->free_head].addr */
|
||||
qtest_writeq(qts, vq->desc + (16 * vq->free_head), indirect->desc);
|
||||
qvirtio_writeq(vq->vdev, qts, vq->desc + (16 * vq->free_head),
|
||||
indirect->desc);
|
||||
/* vq->desc[vq->free_head].len */
|
||||
qtest_writel(qts, vq->desc + (16 * vq->free_head) + 8,
|
||||
qvirtio_writel(vq->vdev, qts, vq->desc + (16 * vq->free_head) + 8,
|
||||
sizeof(struct vring_desc) * indirect->elem);
|
||||
/* vq->desc[vq->free_head].flags */
|
||||
qtest_writew(qts, vq->desc + (16 * vq->free_head) + 12,
|
||||
qvirtio_writew(vq->vdev, qts, vq->desc + (16 * vq->free_head) + 12,
|
||||
VRING_DESC_F_INDIRECT);
|
||||
|
||||
return vq->free_head++; /* Return and increase, in this order */
|
||||
|
@ -303,20 +365,20 @@ void qvirtqueue_kick(QTestState *qts, QVirtioDevice *d, QVirtQueue *vq,
|
|||
uint32_t free_head)
|
||||
{
|
||||
/* vq->avail->idx */
|
||||
uint16_t idx = qtest_readw(qts, vq->avail + 2);
|
||||
uint16_t idx = qvirtio_readw(d, qts, vq->avail + 2);
|
||||
/* vq->used->flags */
|
||||
uint16_t flags;
|
||||
/* vq->used->avail_event */
|
||||
uint16_t avail_event;
|
||||
|
||||
/* vq->avail->ring[idx % vq->size] */
|
||||
qtest_writew(qts, vq->avail + 4 + (2 * (idx % vq->size)), free_head);
|
||||
qvirtio_writew(d, qts, vq->avail + 4 + (2 * (idx % vq->size)), free_head);
|
||||
/* vq->avail->idx */
|
||||
qtest_writew(qts, vq->avail + 2, idx + 1);
|
||||
qvirtio_writew(d, qts, vq->avail + 2, idx + 1);
|
||||
|
||||
/* Must read after idx is updated */
|
||||
flags = qtest_readw(qts, vq->avail);
|
||||
avail_event = qtest_readw(qts, vq->used + 4 +
|
||||
flags = qvirtio_readw(d, qts, vq->avail);
|
||||
avail_event = qvirtio_readw(d, qts, vq->used + 4 +
|
||||
sizeof(struct vring_used_elem) * vq->size);
|
||||
|
||||
/* < 1 because we add elements to avail queue one by one */
|
||||
|
@ -342,7 +404,8 @@ bool qvirtqueue_get_buf(QTestState *qts, QVirtQueue *vq, uint32_t *desc_idx,
|
|||
uint16_t idx;
|
||||
uint64_t elem_addr, addr;
|
||||
|
||||
idx = qtest_readw(qts, vq->used + offsetof(struct vring_used, idx));
|
||||
idx = qvirtio_readw(vq->vdev, qts,
|
||||
vq->used + offsetof(struct vring_used, idx));
|
||||
if (idx == vq->last_used_idx) {
|
||||
return false;
|
||||
}
|
||||
|
@ -354,12 +417,12 @@ bool qvirtqueue_get_buf(QTestState *qts, QVirtQueue *vq, uint32_t *desc_idx,
|
|||
|
||||
if (desc_idx) {
|
||||
addr = elem_addr + offsetof(struct vring_used_elem, id);
|
||||
*desc_idx = qtest_readl(qts, addr);
|
||||
*desc_idx = qvirtio_readl(vq->vdev, qts, addr);
|
||||
}
|
||||
|
||||
if (len) {
|
||||
addr = elem_addr + offsetof(struct vring_used_elem, len);
|
||||
*len = qtest_readw(qts, addr);
|
||||
*len = qvirtio_readw(vq->vdev, qts, addr);
|
||||
}
|
||||
|
||||
vq->last_used_idx++;
|
||||
|
@ -371,7 +434,7 @@ void qvirtqueue_set_used_event(QTestState *qts, QVirtQueue *vq, uint16_t idx)
|
|||
g_assert(vq->event);
|
||||
|
||||
/* vq->avail->used_event */
|
||||
qtest_writew(qts, vq->avail + 4 + (2 * vq->size), idx);
|
||||
qvirtio_writew(vq->vdev, qts, vq->avail + 4 + (2 * vq->size), idx);
|
||||
}
|
||||
|
||||
void qvirtio_start_device(QVirtioDevice *vdev)
|
||||
|
|
|
@ -27,6 +27,7 @@ typedef struct QVirtioDevice {
|
|||
} QVirtioDevice;
|
||||
|
||||
typedef struct QVirtQueue {
|
||||
QVirtioDevice *vdev;
|
||||
uint64_t desc; /* This points to an array of struct vring_desc */
|
||||
uint64_t avail; /* This points to a struct vring_avail */
|
||||
uint64_t used; /* This points to a struct vring_used */
|
||||
|
@ -135,7 +136,8 @@ void qvring_init(QTestState *qts, const QGuestAllocator *alloc, QVirtQueue *vq,
|
|||
QVRingIndirectDesc *qvring_indirect_desc_setup(QTestState *qs, QVirtioDevice *d,
|
||||
QGuestAllocator *alloc,
|
||||
uint16_t elem);
|
||||
void qvring_indirect_desc_add(QTestState *qts, QVRingIndirectDesc *indirect,
|
||||
void qvring_indirect_desc_add(QVirtioDevice *d, QTestState *qts,
|
||||
QVRingIndirectDesc *indirect,
|
||||
uint64_t data, uint32_t len, bool write);
|
||||
uint32_t qvirtqueue_add(QTestState *qts, QVirtQueue *vq, uint64_t data,
|
||||
uint32_t len, bool write, bool next);
|
||||
|
|
|
@ -388,8 +388,8 @@ static void indirect(void *obj, void *u_data, QGuestAllocator *t_alloc)
|
|||
g_free(req.data);
|
||||
|
||||
indirect = qvring_indirect_desc_setup(qts, dev, t_alloc, 2);
|
||||
qvring_indirect_desc_add(qts, indirect, req_addr, 528, false);
|
||||
qvring_indirect_desc_add(qts, indirect, req_addr + 528, 1, true);
|
||||
qvring_indirect_desc_add(dev, qts, indirect, req_addr, 528, false);
|
||||
qvring_indirect_desc_add(dev, qts, indirect, req_addr + 528, 1, true);
|
||||
free_head = qvirtqueue_add_indirect(qts, vq, indirect);
|
||||
qvirtqueue_kick(qts, dev, vq, free_head);
|
||||
|
||||
|
@ -413,8 +413,8 @@ static void indirect(void *obj, void *u_data, QGuestAllocator *t_alloc)
|
|||
g_free(req.data);
|
||||
|
||||
indirect = qvring_indirect_desc_setup(qts, dev, t_alloc, 2);
|
||||
qvring_indirect_desc_add(qts, indirect, req_addr, 16, false);
|
||||
qvring_indirect_desc_add(qts, indirect, req_addr + 16, 513, true);
|
||||
qvring_indirect_desc_add(dev, qts, indirect, req_addr, 16, false);
|
||||
qvring_indirect_desc_add(dev, qts, indirect, req_addr + 16, 513, true);
|
||||
free_head = qvirtqueue_add_indirect(qts, vq, indirect);
|
||||
qvirtqueue_kick(qts, dev, vq, free_head);
|
||||
|
||||
|
|
Loading…
Reference in New Issue