* [PATCH] virtio-mmio: read[wl]()/write[wl] are already little-endian
@ 2021-03-09 22:43 Laurent Vivier
2021-03-10 3:08 ` kernel test robot
2021-03-11 15:44 ` Michael S. Tsirkin
0 siblings, 2 replies; 6+ messages in thread
From: Laurent Vivier @ 2021-03-09 22:43 UTC (permalink / raw)
To: linux-kernel
Cc: virtualization, Michael S. Tsirkin, Jason Wang, Laurent Vivier
read[wl]()/write[wl] already access memory in little-endian mode.
No need to convert the value with cpu_to_leXX()/leXX_to_cpu()
Signed-off-by: Laurent Vivier <laurent@vivier.eu>
---
drivers/virtio/virtio_mmio.c | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/drivers/virtio/virtio_mmio.c b/drivers/virtio/virtio_mmio.c
index a286d22b6551..3f6a5588f77d 100644
--- a/drivers/virtio/virtio_mmio.c
+++ b/drivers/virtio/virtio_mmio.c
@@ -168,17 +168,17 @@ static void vm_get(struct virtio_device *vdev, unsigned offset,
memcpy(buf, &b, sizeof b);
break;
case 2:
- w = cpu_to_le16(readw(base + offset));
+ w = readw(base + offset);
memcpy(buf, &w, sizeof w);
break;
case 4:
- l = cpu_to_le32(readl(base + offset));
+ l = readl(base + offset);
memcpy(buf, &l, sizeof l);
break;
case 8:
- l = cpu_to_le32(readl(base + offset));
+ l = readl(base + offset);
memcpy(buf, &l, sizeof l);
- l = cpu_to_le32(ioread32(base + offset + sizeof l));
+ l = ioread32(base + offset + sizeof l);
memcpy(buf + sizeof l, &l, sizeof l);
break;
default:
@@ -212,17 +212,17 @@ static void vm_set(struct virtio_device *vdev, unsigned offset,
break;
case 2:
memcpy(&w, buf, sizeof w);
- writew(le16_to_cpu(w), base + offset);
+ writew(w, base + offset);
break;
case 4:
memcpy(&l, buf, sizeof l);
- writel(le32_to_cpu(l), base + offset);
+ writel(l, base + offset);
break;
case 8:
memcpy(&l, buf, sizeof l);
- writel(le32_to_cpu(l), base + offset);
+ writel(l, base + offset);
memcpy(&l, buf + sizeof l, sizeof l);
- writel(le32_to_cpu(l), base + offset + sizeof l);
+ writel(l, base + offset + sizeof l);
break;
default:
BUG();
--
2.29.2
^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH] virtio-mmio: read[wl]()/write[wl] are already little-endian
2021-03-09 22:43 [PATCH] virtio-mmio: read[wl]()/write[wl] are already little-endian Laurent Vivier
@ 2021-03-10 3:08 ` kernel test robot
2021-03-11 15:44 ` Michael S. Tsirkin
1 sibling, 0 replies; 6+ messages in thread
From: kernel test robot @ 2021-03-10 3:08 UTC (permalink / raw)
To: Laurent Vivier, linux-kernel
Cc: kbuild-all, virtualization, Michael S. Tsirkin, Jason Wang,
Laurent Vivier
[-- Attachment #1: Type: text/plain, Size: 5687 bytes --]
Hi Laurent,
Thank you for the patch! Perhaps something to improve:
[auto build test WARNING on linux/master]
[also build test WARNING on linus/master v5.12-rc2 next-20210309]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]
url: https://github.com/0day-ci/linux/commits/Laurent-Vivier/virtio-mmio-read-wl-write-wl-are-already-little-endian/20210310-064527
base: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git 144c79ef33536b4ecb4951e07dbc1f2b7fa99d32
config: x86_64-randconfig-s022-20210310 (attached as .config)
compiler: gcc-9 (Debian 9.3.0-22) 9.3.0
reproduce:
# apt-get install sparse
# sparse version: v0.6.3-262-g5e674421-dirty
# https://github.com/0day-ci/linux/commit/1fd3d4da486545f554eb33663c6afe068bbcbcf8
git remote add linux-review https://github.com/0day-ci/linux
git fetch --no-tags linux-review Laurent-Vivier/virtio-mmio-read-wl-write-wl-are-already-little-endian/20210310-064527
git checkout 1fd3d4da486545f554eb33663c6afe068bbcbcf8
# save the attached .config to linux build tree
make W=1 C=1 CF='-fdiagnostic-prefix -D__CHECK_ENDIAN__' ARCH=x86_64
If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>
"sparse warnings: (new ones prefixed by >>)"
drivers/virtio/virtio_mmio.c:171:19: sparse: sparse: incorrect type in assignment (different base types) @@ expected restricted __le16 [usertype] w @@ got unsigned short @@
drivers/virtio/virtio_mmio.c:171:19: sparse: expected restricted __le16 [usertype] w
drivers/virtio/virtio_mmio.c:171:19: sparse: got unsigned short
drivers/virtio/virtio_mmio.c:175:19: sparse: sparse: incorrect type in assignment (different base types) @@ expected restricted __le32 [usertype] l @@ got unsigned int @@
drivers/virtio/virtio_mmio.c:175:19: sparse: expected restricted __le32 [usertype] l
drivers/virtio/virtio_mmio.c:175:19: sparse: got unsigned int
drivers/virtio/virtio_mmio.c:179:19: sparse: sparse: incorrect type in assignment (different base types) @@ expected restricted __le32 [addressable] [usertype] l @@ got unsigned int @@
drivers/virtio/virtio_mmio.c:179:19: sparse: expected restricted __le32 [addressable] [usertype] l
drivers/virtio/virtio_mmio.c:179:19: sparse: got unsigned int
drivers/virtio/virtio_mmio.c:181:19: sparse: sparse: incorrect type in assignment (different base types) @@ expected restricted __le32 [addressable] [usertype] l @@ got unsigned int @@
drivers/virtio/virtio_mmio.c:181:19: sparse: expected restricted __le32 [addressable] [usertype] l
drivers/virtio/virtio_mmio.c:181:19: sparse: got unsigned int
>> drivers/virtio/virtio_mmio.c:215:24: sparse: sparse: incorrect type in argument 1 (different base types) @@ expected unsigned short val @@ got restricted __le16 [addressable] [usertype] w @@
drivers/virtio/virtio_mmio.c:215:24: sparse: expected unsigned short val
drivers/virtio/virtio_mmio.c:215:24: sparse: got restricted __le16 [addressable] [usertype] w
>> drivers/virtio/virtio_mmio.c:219:24: sparse: sparse: incorrect type in argument 1 (different base types) @@ expected unsigned int val @@ got restricted __le32 [addressable] [usertype] l @@
drivers/virtio/virtio_mmio.c:219:24: sparse: expected unsigned int val
drivers/virtio/virtio_mmio.c:219:24: sparse: got restricted __le32 [addressable] [usertype] l
drivers/virtio/virtio_mmio.c:223:24: sparse: sparse: incorrect type in argument 1 (different base types) @@ expected unsigned int val @@ got restricted __le32 [addressable] [usertype] l @@
drivers/virtio/virtio_mmio.c:223:24: sparse: expected unsigned int val
drivers/virtio/virtio_mmio.c:223:24: sparse: got restricted __le32 [addressable] [usertype] l
drivers/virtio/virtio_mmio.c:225:24: sparse: sparse: incorrect type in argument 1 (different base types) @@ expected unsigned int val @@ got restricted __le32 [addressable] [usertype] l @@
drivers/virtio/virtio_mmio.c:225:24: sparse: expected unsigned int val
drivers/virtio/virtio_mmio.c:225:24: sparse: got restricted __le32 [addressable] [usertype] l
vim +215 drivers/virtio/virtio_mmio.c
188
189 static void vm_set(struct virtio_device *vdev, unsigned offset,
190 const void *buf, unsigned len)
191 {
192 struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev);
193 void __iomem *base = vm_dev->base + VIRTIO_MMIO_CONFIG;
194 u8 b;
195 __le16 w;
196 __le32 l;
197
198 if (vm_dev->version == 1) {
199 const u8 *ptr = buf;
200 int i;
201
202 for (i = 0; i < len; i++)
203 writeb(ptr[i], base + offset + i);
204
205 return;
206 }
207
208 switch (len) {
209 case 1:
210 memcpy(&b, buf, sizeof b);
211 writeb(b, base + offset);
212 break;
213 case 2:
214 memcpy(&w, buf, sizeof w);
> 215 writew(w, base + offset);
216 break;
217 case 4:
218 memcpy(&l, buf, sizeof l);
> 219 writel(l, base + offset);
220 break;
221 case 8:
222 memcpy(&l, buf, sizeof l);
223 writel(l, base + offset);
224 memcpy(&l, buf + sizeof l, sizeof l);
225 writel(l, base + offset + sizeof l);
226 break;
227 default:
228 BUG();
229 }
230 }
231
---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org
[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 34366 bytes --]
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH] virtio-mmio: read[wl]()/write[wl] are already little-endian
2021-03-09 22:43 [PATCH] virtio-mmio: read[wl]()/write[wl] are already little-endian Laurent Vivier
2021-03-10 3:08 ` kernel test robot
@ 2021-03-11 15:44 ` Michael S. Tsirkin
2021-03-11 15:55 ` Laurent Vivier
2021-03-13 17:10 ` Laurent Vivier
1 sibling, 2 replies; 6+ messages in thread
From: Michael S. Tsirkin @ 2021-03-11 15:44 UTC (permalink / raw)
To: Laurent Vivier; +Cc: linux-kernel, virtualization, Jason Wang
On Tue, Mar 09, 2021 at 11:43:13PM +0100, Laurent Vivier wrote:
> read[wl]()/write[wl] already access memory in little-endian mode.
But then they convert it to CPU right? We just convert it back ...
> No need to convert the value with cpu_to_leXX()/leXX_to_cpu()
>
> Signed-off-by: Laurent Vivier <laurent@vivier.eu>
> ---
> drivers/virtio/virtio_mmio.c | 16 ++++++++--------
> 1 file changed, 8 insertions(+), 8 deletions(-)
>
> diff --git a/drivers/virtio/virtio_mmio.c b/drivers/virtio/virtio_mmio.c
> index a286d22b6551..3f6a5588f77d 100644
> --- a/drivers/virtio/virtio_mmio.c
> +++ b/drivers/virtio/virtio_mmio.c
> @@ -168,17 +168,17 @@ static void vm_get(struct virtio_device *vdev, unsigned offset,
> memcpy(buf, &b, sizeof b);
> break;
> case 2:
> - w = cpu_to_le16(readw(base + offset));
> + w = readw(base + offset);
> memcpy(buf, &w, sizeof w);
> break;
> case 4:
> - l = cpu_to_le32(readl(base + offset));
> + l = readl(base + offset);
> memcpy(buf, &l, sizeof l);
> break;
> case 8:
> - l = cpu_to_le32(readl(base + offset));
> + l = readl(base + offset);
> memcpy(buf, &l, sizeof l);
> - l = cpu_to_le32(ioread32(base + offset + sizeof l));
> + l = ioread32(base + offset + sizeof l);
> memcpy(buf + sizeof l, &l, sizeof l);
> break;
> default:
> @@ -212,17 +212,17 @@ static void vm_set(struct virtio_device *vdev, unsigned offset,
> break;
> case 2:
> memcpy(&w, buf, sizeof w);
> - writew(le16_to_cpu(w), base + offset);
> + writew(w, base + offset);
> break;
> case 4:
> memcpy(&l, buf, sizeof l);
> - writel(le32_to_cpu(l), base + offset);
> + writel(l, base + offset);
> break;
> case 8:
> memcpy(&l, buf, sizeof l);
> - writel(le32_to_cpu(l), base + offset);
> + writel(l, base + offset);
> memcpy(&l, buf + sizeof l, sizeof l);
> - writel(le32_to_cpu(l), base + offset + sizeof l);
> + writel(l, base + offset + sizeof l);
> break;
> default:
> BUG();
> --
> 2.29.2
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH] virtio-mmio: read[wl]()/write[wl] are already little-endian
2021-03-11 15:44 ` Michael S. Tsirkin
@ 2021-03-11 15:55 ` Laurent Vivier
2021-03-13 17:10 ` Laurent Vivier
1 sibling, 0 replies; 6+ messages in thread
From: Laurent Vivier @ 2021-03-11 15:55 UTC (permalink / raw)
To: Michael S. Tsirkin; +Cc: linux-kernel, virtualization, Jason Wang
Le 11/03/2021 à 16:44, Michael S. Tsirkin a écrit :
> On Tue, Mar 09, 2021 at 11:43:13PM +0100, Laurent Vivier wrote:
>> read[wl]()/write[wl] already access memory in little-endian mode.
>
> But then they convert it to CPU right? We just convert it back ...
Yes, you're right.
But there's a real problem with a big-endian guest using directly virtio-mmio without using virtio-pci.
It seems there is one too many little-endian conversion somewhere, and this has not been detected
before because there is no big-endian guest that uses virtio-mmio directly, and with little-endian
guests the conversion is a no-op. I'm going to have a closer look to the code path... perhaps the
problem is in QEMU not in the kernel.
Thanks,
Laurent
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH] virtio-mmio: read[wl]()/write[wl] are already little-endian
2021-03-11 15:44 ` Michael S. Tsirkin
2021-03-11 15:55 ` Laurent Vivier
@ 2021-03-13 17:10 ` Laurent Vivier
2021-03-14 8:26 ` Michael S. Tsirkin
1 sibling, 1 reply; 6+ messages in thread
From: Laurent Vivier @ 2021-03-13 17:10 UTC (permalink / raw)
To: Michael S. Tsirkin; +Cc: linux-kernel, virtualization, Jason Wang
Le 11/03/2021 à 16:44, Michael S. Tsirkin a écrit :
> On Tue, Mar 09, 2021 at 11:43:13PM +0100, Laurent Vivier wrote:
>> 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) {
And we need the same thing in virtio_pci_config_read() (and write).
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
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH] virtio-mmio: read[wl]()/write[wl] are already little-endian
2021-03-13 17:10 ` Laurent Vivier
@ 2021-03-14 8:26 ` Michael S. Tsirkin
0 siblings, 0 replies; 6+ messages in thread
From: Michael S. Tsirkin @ 2021-03-14 8:26 UTC (permalink / raw)
To: Laurent Vivier; +Cc: linux-kernel, virtualization, Jason Wang
On Sat, Mar 13, 2021 at 06:10:29PM +0100, Laurent Vivier wrote:
> Le 11/03/2021 à 16:44, Michael S. Tsirkin a écrit :
> > On Tue, Mar 09, 2021 at 11:43:13PM +0100, Laurent Vivier wrote:
> >> 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
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2021-03-14 8:27 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-03-09 22:43 [PATCH] virtio-mmio: read[wl]()/write[wl] are already little-endian Laurent Vivier
2021-03-10 3:08 ` kernel test robot
2021-03-11 15:44 ` Michael S. Tsirkin
2021-03-11 15:55 ` Laurent Vivier
2021-03-13 17:10 ` Laurent Vivier
2021-03-14 8:26 ` Michael S. Tsirkin
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).