Re: [PATCH] virtio-mmio: read[wl]()/write[wl] are already little-endian
From: "Michael S. Tsirkin" <mst@redhat.com>
Date: 2021-03-14 08:27:48
Also in:
lkml
On Sat, Mar 13, 2021 at 06:10:29PM +0100, Laurent Vivier wrote:
quoted hunk
Le 11/03/2021 à 16:44, Michael S. Tsirkin a écrit :quoted
On Tue, Mar 09, 2021 at 11:43:13PM +0100, Laurent Vivier wrote:quoted
read[wl]()/write[wl] already access memory in little-endian mode.But then they convert it to CPU right? We just convert it back ...In fact the problem is in QEMU. On a big-endian guest, the readw() returns a byte-swapped value, This means QEMU doesn't provide a little-endian value. I found in QEMU virtio_mmio_read() provides a value with byte-swapped bytes. The problem comes from virtio_config_readX() functions that read the value using ldX_p accessors. Is it normal not to use the modern variant here if we are not in legacy mode? I think we should have something like this in virtio_mmio_read (and write):--- a/hw/virtio/virtio-mmio.c +++ b/hw/virtio/virtio-mmio.c@@ -112,15 +112,28 @@ static uint64_t virtio_mmio_read(void *opaque, hwaddr offset, unsigned size) if (offset >= VIRTIO_MMIO_CONFIG) { offset -= VIRTIO_MMIO_CONFIG; - switch (size) { - case 1: - return virtio_config_readb(vdev, offset); - case 2: - return virtio_config_readw(vdev, offset); - case 4: - return virtio_config_readl(vdev, offset); - default: - abort(); + if (proxy->legacy) { + switch (size) { + case 1: + return virtio_config_readb(vdev, offset); + case 2: + return virtio_config_readw(vdev, offset); + case 4: + return virtio_config_readl(vdev, offset); + default: + abort(); + } + } else { + switch (size) { + case 1: + return virtio_config_modern_readb(vdev, offset); + case 2: + return virtio_config_modern_readw(vdev, offset); + case 4: + return virtio_config_modern_readl(vdev, offset); + default: + abort(); + } } } if (size != 4) {
Sounds reasonable ...
And we need the same thing in virtio_pci_config_read() (and write).
We already have it, see below.
And this could explain why it works with virtio-pci and not with virtio-mmio with the big-endian guest: with virtio-pci the bytes are swapped twice (once in virtio-mmio and then in virtio-pci), so they are restored to the initial value, whereas with direct virtio-mmio they are swapped only once. Thanks, Laurent
virtio pci does this: modern accesses use:
virtio_pci_device_read
static uint64_t virtio_pci_device_read(void *opaque, hwaddr addr,
unsigned size)
{
VirtIOPCIProxy *proxy = opaque;
VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
uint64_t val = 0;
if (vdev == NULL) {
return val;
}
switch (size) {
case 1:
val = virtio_config_modern_readb(vdev, addr);
break;
case 2:
val = virtio_config_modern_readw(vdev, addr);
break;
case 4:
val = virtio_config_modern_readl(vdev, addr);
break;
}
return val;
}
while legacy accesses use:
virtio_pci_config_read
static uint64_t virtio_pci_config_read(void *opaque, hwaddr addr,
unsigned size)
{
VirtIOPCIProxy *proxy = opaque;
VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
uint32_t config = VIRTIO_PCI_CONFIG_SIZE(&proxy->pci_dev);
uint64_t val = 0;
if (addr < config) {
return virtio_ioport_read(proxy, addr);
}
addr -= config;
switch (size) {
case 1:
val = virtio_config_readb(vdev, addr);
break;
case 2:
val = virtio_config_readw(vdev, addr);
if (virtio_is_big_endian(vdev)) {
val = bswap16(val);
}
break;
case 4:
val = virtio_config_readl(vdev, addr);
if (virtio_is_big_endian(vdev)) {
val = bswap32(val);
}
break;
}
return val;
}
the naming is configing but there you are.
virtio_pci_config_read is also calling virtio_is_big_endian.
static inline bool virtio_is_big_endian(VirtIODevice *vdev)
{
if (!virtio_vdev_has_feature(vdev, VIRTIO_F_VERSION_1)) {
assert(vdev->device_endian != VIRTIO_DEVICE_ENDIAN_UNKNOWN);
return vdev->device_endian == VIRTIO_DEVICE_ENDIAN_BIG;
}
/* Devices conforming to VIRTIO 1.0 or later are always LE. */
return false;
}
I note that
virtio_is_big_endian is kind of wrong for modern config accesses since it
checks guest feature bits - config accesses are done before features are
acknowledged.
Luckily this function is never called for a modern guest ...
It would be nice to add virtio_legacy_is_big_endian and call that
when we know it's a legacy interface.
--
MST
_______________________________________________
Virtualization mailing list
Virtualization@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/virtualization