[PATCH 12/19] hw/net/xilinx_ethlite: Only expect big-endian accesses

Philippe Mathieu-Daudé posted 19 patches 2 weeks, 3 days ago
There is a newer version of this series
[PATCH 12/19] hw/net/xilinx_ethlite: Only expect big-endian accesses
Posted by Philippe Mathieu-Daudé 2 weeks, 3 days ago
The Xilinx 'ethlite' device was added in commit b43848a100
("xilinx: Add ethlite emulation"), being only built back
then for a big-endian MicroBlaze target (see commit 72b675caac
"microblaze: Hook into the build-system").

I/O endianness access was then clarified in commit d48751ed4f
("xilinx-ethlite: Simplify byteswapping to/from brams"). Here
the 'fix' was to use tswap32(). Since the machine was built as
big-endian target, tswap32() use means the fix was for a little
endian host. While the datasheet (reference added in file header)
is not precise about it, we interpret such change as the device
expects accesses in big-endian order. Besides, this is what other
Xilinx/MicroBlaze devices use (see the 3 previous commits).

Correct the MemoryRegionOps endianness. Add a 'access-little-endian'
property, so if the bus access expect little-endian order we swap
the values. Replace the tswap32() calls accordingly.

Set the property on the single machine using this device.

Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org>
---
 hw/microblaze/petalogix_s3adsp1800_mmu.c |  1 +
 hw/net/xilinx_ethlite.c                  | 20 ++++++++++++++++----
 2 files changed, 17 insertions(+), 4 deletions(-)

diff --git a/hw/microblaze/petalogix_s3adsp1800_mmu.c b/hw/microblaze/petalogix_s3adsp1800_mmu.c
index 8110be83715..8407dbee12a 100644
--- a/hw/microblaze/petalogix_s3adsp1800_mmu.c
+++ b/hw/microblaze/petalogix_s3adsp1800_mmu.c
@@ -123,6 +123,7 @@ petalogix_s3adsp1800_init(MachineState *machine)
     qemu_configure_nic_device(dev, true, NULL);
     qdev_prop_set_uint32(dev, "tx-ping-pong", 0);
     qdev_prop_set_uint32(dev, "rx-ping-pong", 0);
+    qdev_prop_set_bit(dev, "access-little-endian", !TARGET_BIG_ENDIAN);
     sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
     sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, ETHLITE_BASEADDR);
     sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq[ETHLITE_IRQ]);
diff --git a/hw/net/xilinx_ethlite.c b/hw/net/xilinx_ethlite.c
index ede7c172748..44ef11ebf89 100644
--- a/hw/net/xilinx_ethlite.c
+++ b/hw/net/xilinx_ethlite.c
@@ -3,6 +3,9 @@
  *
  * Copyright (c) 2009 Edgar E. Iglesias.
  *
+ * DS580: https://docs.amd.com/v/u/en-US/xps_ethernetlite
+ * LogiCORE IP XPS Ethernet Lite Media Access Controller
+ *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * of this software and associated documentation files (the "Software"), to deal
  * in the Software without restriction, including without limitation the rights
@@ -25,7 +28,6 @@
 #include "qemu/osdep.h"
 #include "qemu/module.h"
 #include "qom/object.h"
-#include "exec/tswap.h"
 #include "hw/sysbus.h"
 #include "hw/irq.h"
 #include "hw/qdev-properties.h"
@@ -65,6 +67,7 @@ struct xlx_ethlite
     NICState *nic;
     NICConf conf;
 
+    bool access_little_endian;
     uint32_t c_tx_pingpong;
     uint32_t c_rx_pingpong;
     unsigned int txbuf;
@@ -103,9 +106,12 @@ eth_read(void *opaque, hwaddr addr, unsigned int size)
             break;
 
         default:
-            r = tswap32(s->regs[addr]);
+            r = s->regs[addr];
             break;
     }
+    if (s->access_little_endian) {
+        bswap32s(&r);
+    }
     return r;
 }
 
@@ -117,6 +123,10 @@ eth_write(void *opaque, hwaddr addr,
     unsigned int base = 0;
     uint32_t value = val64;
 
+    if (s->access_little_endian) {
+        bswap32s(&value);
+    }
+
     addr >>= 2;
     switch (addr) 
     {
@@ -161,7 +171,7 @@ eth_write(void *opaque, hwaddr addr,
             break;
 
         default:
-            s->regs[addr] = tswap32(value);
+            s->regs[addr] = value;
             break;
     }
 }
@@ -169,7 +179,7 @@ eth_write(void *opaque, hwaddr addr,
 static const MemoryRegionOps eth_ops = {
     .read = eth_read,
     .write = eth_write,
-    .endianness = DEVICE_NATIVE_ENDIAN,
+    .endianness = DEVICE_BIG_ENDIAN,
     .impl = {
         .min_access_size = 4,
         .max_access_size = 4,
@@ -256,6 +266,8 @@ static void xilinx_ethlite_init(Object *obj)
 }
 
 static Property xilinx_ethlite_properties[] = {
+    DEFINE_PROP_BOOL("access-little-endian", struct xlx_ethlite,
+                     access_little_endian, false),
     DEFINE_PROP_UINT32("tx-ping-pong", struct xlx_ethlite, c_tx_pingpong, 1),
     DEFINE_PROP_UINT32("rx-ping-pong", struct xlx_ethlite, c_rx_pingpong, 1),
     DEFINE_NIC_PROPERTIES(struct xlx_ethlite, conf),
-- 
2.45.2


Re: [PATCH 12/19] hw/net/xilinx_ethlite: Only expect big-endian accesses
Posted by Edgar E. Iglesias 2 weeks, 3 days ago
On Tue, Nov 05, 2024 at 02:04:24PM +0100, Philippe Mathieu-Daudé wrote:
> The Xilinx 'ethlite' device was added in commit b43848a100
> ("xilinx: Add ethlite emulation"), being only built back
> then for a big-endian MicroBlaze target (see commit 72b675caac
> "microblaze: Hook into the build-system").
> 
> I/O endianness access was then clarified in commit d48751ed4f
> ("xilinx-ethlite: Simplify byteswapping to/from brams"). Here
> the 'fix' was to use tswap32(). Since the machine was built as
> big-endian target, tswap32() use means the fix was for a little
> endian host. While the datasheet (reference added in file header)
> is not precise about it, we interpret such change as the device
> expects accesses in big-endian order. Besides, this is what other
> Xilinx/MicroBlaze devices use (see the 3 previous commits).
> 
> Correct the MemoryRegionOps endianness. Add a 'access-little-endian'
> property, so if the bus access expect little-endian order we swap
> the values. Replace the tswap32() calls accordingly.
> 
> Set the property on the single machine using this device.


I think you're partially correct but not fully. This buffer area is
really a RAM and has no endianess. Problem is back then I don't think
I was a ware of a way to map RAM memory sub regions so we hacked in
byteswaps to swap from host (which usually was little endian) to
big endian. This is because register accesses from CPU to device model
are kept in host endianess. I think the right way to solve this issue
is to map a RAM memory region to represent the BRAM.

Cheers,
Edgar


> 
> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org>
> ---
>  hw/microblaze/petalogix_s3adsp1800_mmu.c |  1 +
>  hw/net/xilinx_ethlite.c                  | 20 ++++++++++++++++----
>  2 files changed, 17 insertions(+), 4 deletions(-)
> 
> diff --git a/hw/microblaze/petalogix_s3adsp1800_mmu.c b/hw/microblaze/petalogix_s3adsp1800_mmu.c
> index 8110be83715..8407dbee12a 100644
> --- a/hw/microblaze/petalogix_s3adsp1800_mmu.c
> +++ b/hw/microblaze/petalogix_s3adsp1800_mmu.c
> @@ -123,6 +123,7 @@ petalogix_s3adsp1800_init(MachineState *machine)
>      qemu_configure_nic_device(dev, true, NULL);
>      qdev_prop_set_uint32(dev, "tx-ping-pong", 0);
>      qdev_prop_set_uint32(dev, "rx-ping-pong", 0);
> +    qdev_prop_set_bit(dev, "access-little-endian", !TARGET_BIG_ENDIAN);
>      sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
>      sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, ETHLITE_BASEADDR);
>      sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq[ETHLITE_IRQ]);
> diff --git a/hw/net/xilinx_ethlite.c b/hw/net/xilinx_ethlite.c
> index ede7c172748..44ef11ebf89 100644
> --- a/hw/net/xilinx_ethlite.c
> +++ b/hw/net/xilinx_ethlite.c
> @@ -3,6 +3,9 @@
>   *
>   * Copyright (c) 2009 Edgar E. Iglesias.
>   *
> + * DS580: https://docs.amd.com/v/u/en-US/xps_ethernetlite
> + * LogiCORE IP XPS Ethernet Lite Media Access Controller
> + *
>   * Permission is hereby granted, free of charge, to any person obtaining a copy
>   * of this software and associated documentation files (the "Software"), to deal
>   * in the Software without restriction, including without limitation the rights
> @@ -25,7 +28,6 @@
>  #include "qemu/osdep.h"
>  #include "qemu/module.h"
>  #include "qom/object.h"
> -#include "exec/tswap.h"
>  #include "hw/sysbus.h"
>  #include "hw/irq.h"
>  #include "hw/qdev-properties.h"
> @@ -65,6 +67,7 @@ struct xlx_ethlite
>      NICState *nic;
>      NICConf conf;
>  
> +    bool access_little_endian;
>      uint32_t c_tx_pingpong;
>      uint32_t c_rx_pingpong;
>      unsigned int txbuf;
> @@ -103,9 +106,12 @@ eth_read(void *opaque, hwaddr addr, unsigned int size)
>              break;
>  
>          default:
> -            r = tswap32(s->regs[addr]);
> +            r = s->regs[addr];
>              break;
>      }
> +    if (s->access_little_endian) {
> +        bswap32s(&r);
> +    }
>      return r;
>  }
>  
> @@ -117,6 +123,10 @@ eth_write(void *opaque, hwaddr addr,
>      unsigned int base = 0;
>      uint32_t value = val64;
>  
> +    if (s->access_little_endian) {
> +        bswap32s(&value);
> +    }
> +
>      addr >>= 2;
>      switch (addr) 
>      {
> @@ -161,7 +171,7 @@ eth_write(void *opaque, hwaddr addr,
>              break;
>  
>          default:
> -            s->regs[addr] = tswap32(value);
> +            s->regs[addr] = value;
>              break;
>      }
>  }
> @@ -169,7 +179,7 @@ eth_write(void *opaque, hwaddr addr,
>  static const MemoryRegionOps eth_ops = {
>      .read = eth_read,
>      .write = eth_write,
> -    .endianness = DEVICE_NATIVE_ENDIAN,
> +    .endianness = DEVICE_BIG_ENDIAN,
>      .impl = {
>          .min_access_size = 4,
>          .max_access_size = 4,
> @@ -256,6 +266,8 @@ static void xilinx_ethlite_init(Object *obj)
>  }
>  
>  static Property xilinx_ethlite_properties[] = {
> +    DEFINE_PROP_BOOL("access-little-endian", struct xlx_ethlite,
> +                     access_little_endian, false),
>      DEFINE_PROP_UINT32("tx-ping-pong", struct xlx_ethlite, c_tx_pingpong, 1),
>      DEFINE_PROP_UINT32("rx-ping-pong", struct xlx_ethlite, c_rx_pingpong, 1),
>      DEFINE_NIC_PROPERTIES(struct xlx_ethlite, conf),
> -- 
> 2.45.2
> 
Re: [PATCH 12/19] hw/net/xilinx_ethlite: Only expect big-endian accesses
Posted by Paolo Bonzini 2 weeks, 3 days ago
On 11/5/24 14:04, Philippe Mathieu-Daudé wrote:
> The Xilinx 'ethlite' device was added in commit b43848a100
> ("xilinx: Add ethlite emulation"), being only built back
> then for a big-endian MicroBlaze target (see commit 72b675caac
> "microblaze: Hook into the build-system").
> 
> I/O endianness access was then clarified in commit d48751ed4f
> ("xilinx-ethlite: Simplify byteswapping to/from brams"). Here
> the 'fix' was to use tswap32(). Since the machine was built as
> big-endian target, tswap32() use means the fix was for a little
> endian host. While the datasheet (reference added in file header)
> is not precise about it, we interpret such change as the device
> expects accesses in big-endian order. Besides, this is what other
> Xilinx/MicroBlaze devices use (see the 3 previous commits).
> 
> Correct the MemoryRegionOps endianness. Add a 'access-little-endian'
> property, so if the bus access expect little-endian order we swap
> the values. Replace the tswap32() calls accordingly.
> 
> Set the property on the single machine using this device.

I don't understand.  This machine type is little-endian only and 
expecting inverted accesses, isn't it?  Therefore, all that you need is

> -    .endianness = DEVICE_NATIVE_ENDIAN,
> +    .endianness = DEVICE_BIG_ENDIAN,

And removing the tswap altogether.  The big-endian petalogix board will 
start getting "correct" values (not swapped anymore).  That's a feature, 
not a bug.

Paolo
Re: [PATCH 12/19] hw/net/xilinx_ethlite: Only expect big-endian accesses
Posted by Philippe Mathieu-Daudé 2 weeks, 3 days ago
On 5/11/24 14:18, Paolo Bonzini wrote:
> On 11/5/24 14:04, Philippe Mathieu-Daudé wrote:
>> The Xilinx 'ethlite' device was added in commit b43848a100
>> ("xilinx: Add ethlite emulation"), being only built back
>> then for a big-endian MicroBlaze target (see commit 72b675caac
>> "microblaze: Hook into the build-system").
>>
>> I/O endianness access was then clarified in commit d48751ed4f
>> ("xilinx-ethlite: Simplify byteswapping to/from brams"). Here
>> the 'fix' was to use tswap32(). Since the machine was built as
>> big-endian target, tswap32() use means the fix was for a little
>> endian host. While the datasheet (reference added in file header)
>> is not precise about it, we interpret such change as the device
>> expects accesses in big-endian order. Besides, this is what other
>> Xilinx/MicroBlaze devices use (see the 3 previous commits).
>>
>> Correct the MemoryRegionOps endianness. Add a 'access-little-endian'
>> property, so if the bus access expect little-endian order we swap
>> the values. Replace the tswap32() calls accordingly.
>>
>> Set the property on the single machine using this device.
> 
> I don't understand.  This machine type is little-endian only and 
> expecting inverted accesses, isn't it?  Therefore, all that you need is
> 
>> -    .endianness = DEVICE_NATIVE_ENDIAN,
>> +    .endianness = DEVICE_BIG_ENDIAN,
> 
> And removing the tswap altogether.  The big-endian petalogix board will 
> start getting "correct" values (not swapped anymore).  That's a feature, 
> not a bug.

The feature is memory.c swapping MemoryRegionOps depending on the
*qemu-system binary* target endianness.

We assumed most guest vCPUs run with the same endianness of the binary.

Now we want to swap wrt the vCPU, not the binary. So indeed this patch
effectively undo the memory.c swapping (feature).

I suppose the better way is to modify memory.c, possibly passing MemOp
all over. For HW accel where vCPU endianness is forced to host one,
this would become a no-op. Lot of rework in perspective.
Re: [PATCH 12/19] hw/net/xilinx_ethlite: Only expect big-endian accesses
Posted by Paolo Bonzini 2 weeks, 3 days ago
On 11/6/24 00:29, Philippe Mathieu-Daudé wrote:
> We assumed most guest vCPUs run with the same endianness of the binary.
> 
> Now we want to swap wrt the vCPU, not the binary. So indeed this patch
> effectively undo the memory.c swapping (feature).
> 
> I suppose the better way is to modify memory.c, possibly passing MemOp
> all over. For HW accel where vCPU endianness is forced to host one,
> this would become a no-op. Lot of rework in perspective.

It should be much easier than that.  First of all, this is when memory.c
swaps according to DEVICE_*_ENDIAN:

guest      \   host    little-endian         big-endian
little-endian          BIG                   LITTLE, NATIVE
big-endian             BIG, NATIVE           LITTLE

tswap swaps in the two cases marked "NATIVE" (same as DEVICE_NATIVE_ENDIAN).
ldl_le_p swaps in the two cases marked "LITTLE" (same as DEVICE_LITTLE_ENDIAN).
ldl_be_p swaps in the two cases marked "BIG" (same as DEVICE_BIG_ENDIAN).


First of all, current code does different things for RAM vs. the other
registers.  After your patch it's the same, which seems fishy.

Anyway let's focus on RAM for now.  Current code (unconditional tswap +
DEVICE_NATIVE_ENDIAN) always performs an even number of swaps:

guest      \   host    little-endian         big-endian
little-endian          none                  tswap+memory.c
big-endian             tswap+memory.c        none

That's what Edgar says - it's just RAM.



So with your patch the behavior becomes:

guest      \   host    little-endian         big-endian
little-endian          bswap+memory.c        bswap
big-endian             memory.c              none

Behavior changes in the cross-endianness case.  LE-on-LE remains the same.
It seems to break BE hosts since petalogix is a qemu-system-microblazeel board.

If this reasoning is correct, together with DEVICE_BIG_ENDIAN you need
cpu_to_be32 instead of tswap:

guest      \   host    little-endian         big-endian
little-endian          cpu_to_be32+memory.c  none
big-endian             cpu_to_be32+memory.c  none

  

However, things are different for the R_RX* and R_TX* cases.

Before:

guest      \   host    little-endian         big-endian
little-endian          none                  memory.c
big-endian             memory.c              none


Your patch here keeps the same evenness of swaps, even if who
swaps changes:

guest      \   host    little-endian         big-endian
little-endian          bswap+memory.c        bswap
big-endian             memory.c              none

Is this just a change in migration format for the RAM ara?  Then I
guess your patch works, though I'd prefer Richard's suggestion of
flipping the endianness in the MemoryRegionOps.


However, since you said the board is LE-only, maybe the following
also works and seems simpler?

1) use DEVICE_LITTLE_ENDIAN (i.e. always the same perspective as
qemu-microblazeel)

2) use cpu_to_le32 for RAM and nothing for the other registers


But again, maybe I'm completely wrong.

Paolo
Re: [PATCH 12/19] hw/net/xilinx_ethlite: Only expect big-endian accesses
Posted by Richard Henderson 2 weeks, 3 days ago
On 11/5/24 13:04, Philippe Mathieu-Daudé wrote:
> The Xilinx 'ethlite' device was added in commit b43848a100
> ("xilinx: Add ethlite emulation"), being only built back
> then for a big-endian MicroBlaze target (see commit 72b675caac
> "microblaze: Hook into the build-system").
> 
> I/O endianness access was then clarified in commit d48751ed4f
> ("xilinx-ethlite: Simplify byteswapping to/from brams"). Here
> the 'fix' was to use tswap32(). Since the machine was built as
> big-endian target, tswap32() use means the fix was for a little
> endian host. While the datasheet (reference added in file header)
> is not precise about it, we interpret such change as the device
> expects accesses in big-endian order. Besides, this is what other
> Xilinx/MicroBlaze devices use (see the 3 previous commits).
> 
> Correct the MemoryRegionOps endianness. Add a 'access-little-endian'
> property, so if the bus access expect little-endian order we swap
> the values. Replace the tswap32() calls accordingly.
> 
> Set the property on the single machine using this device.
> 
> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org>
> ---
>   hw/microblaze/petalogix_s3adsp1800_mmu.c |  1 +
>   hw/net/xilinx_ethlite.c                  | 20 ++++++++++++++++----
>   2 files changed, 17 insertions(+), 4 deletions(-)
> 
> diff --git a/hw/microblaze/petalogix_s3adsp1800_mmu.c b/hw/microblaze/petalogix_s3adsp1800_mmu.c
> index 8110be83715..8407dbee12a 100644
> --- a/hw/microblaze/petalogix_s3adsp1800_mmu.c
> +++ b/hw/microblaze/petalogix_s3adsp1800_mmu.c
> @@ -123,6 +123,7 @@ petalogix_s3adsp1800_init(MachineState *machine)
>       qemu_configure_nic_device(dev, true, NULL);
>       qdev_prop_set_uint32(dev, "tx-ping-pong", 0);
>       qdev_prop_set_uint32(dev, "rx-ping-pong", 0);
> +    qdev_prop_set_bit(dev, "access-little-endian", !TARGET_BIG_ENDIAN);
>       sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
>       sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, ETHLITE_BASEADDR);
>       sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq[ETHLITE_IRQ]);
> diff --git a/hw/net/xilinx_ethlite.c b/hw/net/xilinx_ethlite.c
> index ede7c172748..44ef11ebf89 100644
> --- a/hw/net/xilinx_ethlite.c
> +++ b/hw/net/xilinx_ethlite.c
> @@ -3,6 +3,9 @@
>    *
>    * Copyright (c) 2009 Edgar E. Iglesias.
>    *
> + * DS580: https://docs.amd.com/v/u/en-US/xps_ethernetlite
> + * LogiCORE IP XPS Ethernet Lite Media Access Controller
> + *
>    * Permission is hereby granted, free of charge, to any person obtaining a copy
>    * of this software and associated documentation files (the "Software"), to deal
>    * in the Software without restriction, including without limitation the rights
> @@ -25,7 +28,6 @@
>   #include "qemu/osdep.h"
>   #include "qemu/module.h"
>   #include "qom/object.h"
> -#include "exec/tswap.h"
>   #include "hw/sysbus.h"
>   #include "hw/irq.h"
>   #include "hw/qdev-properties.h"
> @@ -65,6 +67,7 @@ struct xlx_ethlite
>       NICState *nic;
>       NICConf conf;
>   
> +    bool access_little_endian;
>       uint32_t c_tx_pingpong;
>       uint32_t c_rx_pingpong;
>       unsigned int txbuf;
> @@ -103,9 +106,12 @@ eth_read(void *opaque, hwaddr addr, unsigned int size)
>               break;
>   
>           default:
> -            r = tswap32(s->regs[addr]);
> +            r = s->regs[addr];
>               break;
>       }
> +    if (s->access_little_endian) {
> +        bswap32s(&r);
> +    }
>       return r;
>   }
>   
> @@ -117,6 +123,10 @@ eth_write(void *opaque, hwaddr addr,
>       unsigned int base = 0;
>       uint32_t value = val64;
>   
> +    if (s->access_little_endian) {
> +        bswap32s(&value);
> +    }
> +
>       addr >>= 2;
>       switch (addr)
>       {
> @@ -161,7 +171,7 @@ eth_write(void *opaque, hwaddr addr,
>               break;
>   
>           default:
> -            s->regs[addr] = tswap32(value);
> +            s->regs[addr] = value;
>               break;
>       }
>   }
> @@ -169,7 +179,7 @@ eth_write(void *opaque, hwaddr addr,
>   static const MemoryRegionOps eth_ops = {
>       .read = eth_read,
>       .write = eth_write,
> -    .endianness = DEVICE_NATIVE_ENDIAN,
> +    .endianness = DEVICE_BIG_ENDIAN,
>       .impl = {
>           .min_access_size = 4,
>           .max_access_size = 4,
> @@ -256,6 +266,8 @@ static void xilinx_ethlite_init(Object *obj)
>   }
>   
>   static Property xilinx_ethlite_properties[] = {
> +    DEFINE_PROP_BOOL("access-little-endian", struct xlx_ethlite,
> +                     access_little_endian, false),

I'm not a fan of performing any swapping within device code.
The memory subsystem should do all of it.

A better solution is two tables, eth_ops_{be,le}, which differ only in the setting of 
.endianness.  Handle the property by registering the correct MemoryRegionOps during init.


r~

Re: [PATCH 12/19] hw/net/xilinx_ethlite: Only expect big-endian accesses
Posted by Philippe Mathieu-Daudé 2 weeks, 2 days ago
On 5/11/24 13:30, Richard Henderson wrote:
> On 11/5/24 13:04, Philippe Mathieu-Daudé wrote:
>> The Xilinx 'ethlite' device was added in commit b43848a100
>> ("xilinx: Add ethlite emulation"), being only built back
>> then for a big-endian MicroBlaze target (see commit 72b675caac
>> "microblaze: Hook into the build-system").
>>
>> I/O endianness access was then clarified in commit d48751ed4f
>> ("xilinx-ethlite: Simplify byteswapping to/from brams"). Here
>> the 'fix' was to use tswap32(). Since the machine was built as
>> big-endian target, tswap32() use means the fix was for a little
>> endian host. While the datasheet (reference added in file header)
>> is not precise about it, we interpret such change as the device
>> expects accesses in big-endian order. Besides, this is what other
>> Xilinx/MicroBlaze devices use (see the 3 previous commits).
>>
>> Correct the MemoryRegionOps endianness. Add a 'access-little-endian'
>> property, so if the bus access expect little-endian order we swap
>> the values. Replace the tswap32() calls accordingly.
>>
>> Set the property on the single machine using this device.
>>
>> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org>
>> ---
>>   hw/microblaze/petalogix_s3adsp1800_mmu.c |  1 +
>>   hw/net/xilinx_ethlite.c                  | 20 ++++++++++++++++----
>>   2 files changed, 17 insertions(+), 4 deletions(-)
>>
>> diff --git a/hw/microblaze/petalogix_s3adsp1800_mmu.c 
>> b/hw/microblaze/petalogix_s3adsp1800_mmu.c
>> index 8110be83715..8407dbee12a 100644
>> --- a/hw/microblaze/petalogix_s3adsp1800_mmu.c
>> +++ b/hw/microblaze/petalogix_s3adsp1800_mmu.c
>> @@ -123,6 +123,7 @@ petalogix_s3adsp1800_init(MachineState *machine)
>>       qemu_configure_nic_device(dev, true, NULL);
>>       qdev_prop_set_uint32(dev, "tx-ping-pong", 0);
>>       qdev_prop_set_uint32(dev, "rx-ping-pong", 0);
>> +    qdev_prop_set_bit(dev, "access-little-endian", !TARGET_BIG_ENDIAN);
>>       sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
>>       sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, ETHLITE_BASEADDR);
>>       sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq[ETHLITE_IRQ]);
>> diff --git a/hw/net/xilinx_ethlite.c b/hw/net/xilinx_ethlite.c
>> index ede7c172748..44ef11ebf89 100644
>> --- a/hw/net/xilinx_ethlite.c
>> +++ b/hw/net/xilinx_ethlite.c
>> @@ -3,6 +3,9 @@
>>    *
>>    * Copyright (c) 2009 Edgar E. Iglesias.
>>    *
>> + * DS580: https://docs.amd.com/v/u/en-US/xps_ethernetlite
>> + * LogiCORE IP XPS Ethernet Lite Media Access Controller
>> + *
>>    * Permission is hereby granted, free of charge, to any person 
>> obtaining a copy
>>    * of this software and associated documentation files (the 
>> "Software"), to deal
>>    * in the Software without restriction, including without limitation 
>> the rights
>> @@ -25,7 +28,6 @@
>>   #include "qemu/osdep.h"
>>   #include "qemu/module.h"
>>   #include "qom/object.h"
>> -#include "exec/tswap.h"
>>   #include "hw/sysbus.h"
>>   #include "hw/irq.h"
>>   #include "hw/qdev-properties.h"
>> @@ -65,6 +67,7 @@ struct xlx_ethlite
>>       NICState *nic;
>>       NICConf conf;
>> +    bool access_little_endian;
>>       uint32_t c_tx_pingpong;
>>       uint32_t c_rx_pingpong;
>>       unsigned int txbuf;
>> @@ -103,9 +106,12 @@ eth_read(void *opaque, hwaddr addr, unsigned int 
>> size)
>>               break;
>>           default:
>> -            r = tswap32(s->regs[addr]);
>> +            r = s->regs[addr];
>>               break;
>>       }
>> +    if (s->access_little_endian) {
>> +        bswap32s(&r);
>> +    }
>>       return r;
>>   }
>> @@ -117,6 +123,10 @@ eth_write(void *opaque, hwaddr addr,
>>       unsigned int base = 0;
>>       uint32_t value = val64;
>> +    if (s->access_little_endian) {
>> +        bswap32s(&value);
>> +    }
>> +
>>       addr >>= 2;
>>       switch (addr)
>>       {
>> @@ -161,7 +171,7 @@ eth_write(void *opaque, hwaddr addr,
>>               break;
>>           default:
>> -            s->regs[addr] = tswap32(value);
>> +            s->regs[addr] = value;
>>               break;
>>       }
>>   }
>> @@ -169,7 +179,7 @@ eth_write(void *opaque, hwaddr addr,
>>   static const MemoryRegionOps eth_ops = {
>>       .read = eth_read,
>>       .write = eth_write,
>> -    .endianness = DEVICE_NATIVE_ENDIAN,
>> +    .endianness = DEVICE_BIG_ENDIAN,
>>       .impl = {
>>           .min_access_size = 4,
>>           .max_access_size = 4,
>> @@ -256,6 +266,8 @@ static void xilinx_ethlite_init(Object *obj)
>>   }
>>   static Property xilinx_ethlite_properties[] = {
>> +    DEFINE_PROP_BOOL("access-little-endian", struct xlx_ethlite,
>> +                     access_little_endian, false),
> 
> I'm not a fan of performing any swapping within device code.
> The memory subsystem should do all of it.
> 
> A better solution is two tables, eth_ops_{be,le}, which differ only in 
> the setting of .endianness.  Handle the property by registering the 
> correct MemoryRegionOps during init.

Squashing this on top to have the two tables, but we still need to
swap back the effect of memory.c swapping for the binary, so I don't
think this is what you want:

-- >8 --
diff --git a/hw/net/xilinx_ethlite.c b/hw/net/xilinx_ethlite.c
index 44ef11ebf89..d58757eb919 100644
--- a/hw/net/xilinx_ethlite.c
+++ b/hw/net/xilinx_ethlite.c
@@ -67,6 +67,7 @@ struct xlx_ethlite
      NICState *nic;
      NICConf conf;

+    bool model_little_endian;
      bool access_little_endian;
      uint32_t c_tx_pingpong;
      uint32_t c_rx_pingpong;
@@ -109,7 +110,7 @@ eth_read(void *opaque, hwaddr addr, unsigned int size)
              r = s->regs[addr];
              break;
      }
-    if (s->access_little_endian) {
+    if (s->access_little_endian ^ s->model_little_endian) {
          bswap32s(&r);
      }
      return r;
@@ -123,7 +124,7 @@ eth_write(void *opaque, hwaddr addr,
      unsigned int base = 0;
      uint32_t value = val64;

-    if (s->access_little_endian) {
+    if (s->access_little_endian ^ s->model_little_endian) {
          bswap32s(&value);
      }

@@ -176,7 +177,7 @@ eth_write(void *opaque, hwaddr addr,
      }
  }

-static const MemoryRegionOps eth_ops = {
+static const MemoryRegionOps eth_ops_be = {
      .read = eth_read,
      .write = eth_write,
      .endianness = DEVICE_BIG_ENDIAN,
@@ -190,6 +191,20 @@ static const MemoryRegionOps eth_ops = {
      },
  };

+static const MemoryRegionOps eth_ops_le = {
+    .read = eth_read,
+    .write = eth_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .impl = {
+        .min_access_size = 4,
+        .max_access_size = 4,
+    },
+    .valid = {
+        .min_access_size = 4,
+        .max_access_size = 4,
+    },
+};
+
  static bool eth_can_rx(NetClientState *nc)
  {
      struct xlx_ethlite *s = qemu_get_nic_opaque(nc);
@@ -247,6 +262,11 @@ static void xilinx_ethlite_realize(DeviceState 
*dev, Error **errp)
  {
      struct xlx_ethlite *s = XILINX_ETHLITE(dev);

+    memory_region_init_io(&s->mmio, OBJECT(dev),
+                          s->model_little_endian ? &eth_ops_le : 
&eth_ops_be, s,
+                          "xlnx.xps-ethernetlite", R_MAX * 4);
+    sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->mmio);
+
      qemu_macaddr_default_if_unset(&s->conf.macaddr);
      s->nic = qemu_new_nic(&net_xilinx_ethlite_info, &s->conf,
                            object_get_typename(OBJECT(dev)), dev->id,
@@ -259,15 +279,13 @@ static void xilinx_ethlite_init(Object *obj)
      struct xlx_ethlite *s = XILINX_ETHLITE(obj);

      sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq);
-
-    memory_region_init_io(&s->mmio, obj, &eth_ops, s,
-                          "xlnx.xps-ethernetlite", R_MAX * 4);
-    sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio);
  }

  static Property xilinx_ethlite_properties[] = {
      DEFINE_PROP_BOOL("access-little-endian", struct xlx_ethlite,
                       access_little_endian, false),
+    DEFINE_PROP_BOOL("model-little-endian", struct xlx_ethlite,
+                     model_little_endian, false),
      DEFINE_PROP_UINT32("tx-ping-pong", struct xlx_ethlite, 
c_tx_pingpong, 1),
      DEFINE_PROP_UINT32("rx-ping-pong", struct xlx_ethlite, 
c_rx_pingpong, 1),
      DEFINE_NIC_PROPERTIES(struct xlx_ethlite, conf),

---