Re: KVM and variable-endianness guest CPUs

Victor Kamensky
2014-01-22T05:39:32Z

Hi Guys,

Christoffer and I had a bit heated chat :) on this
subject last night. Christoffer, really appreciate
your time! We did not really reach agreement
during the chat and Christoffer asked me to follow
up on this thread.
Here it goes. Sorry, it is very long email.

I don't believe we can assign any endianity to
mmio.data[] byte array. I believe mmio.data[] and
mmio.len acts just memcpy and that is all. As
memcpy does not imply any endianity of underlying
data mmio.data[] should not either.

Here is my definition:

mmio.data[] is array of bytes that contains memory
bytes in such form, for read case, that if those
bytes are placed in guest memory and guest executes
the same read access instruction with address to this
memory, result would be the same as real h/w device
memory access. Rest of KVM host and hypervisor
part of code should really take care of mmio.data[]
memory so it will be delivered to vcpu registers and
restored by hypervisor part in such way that guest CPU
register value is the same as it would be for real
non-emulated h/w read access (that is emulation part).
The same goes for write access, if guest writes into
memory and those bytes are just copied to emulated
h/w register it would have the same effect as real
mapped h/w register write.

In shorter form, i.e for len=4 access: endianity of integer
at &mmio.data[0] address should match endianity
of emulated h/w device behind phys_addr address,
regardless what is endianity of emulator, KVM host,
hypervisor, and guest

Examples that illustrate my definition
--------------------------------------

1) LE guest (E bit is off in ARM speak) reads integer
(4 bytes) from mapped h/w LE device register -
mmio.data[3] contains MSB, mmio.data[0] contains LSB.

2) BE guest (E bit is on in ARM speak) reads integer
from mapped h/w LE device register - mmio.data[3]
contains MSB, mmio.data[0] contains LSB. Note that
if &mmio.data[0] memory would be placed in guest
address space and instruction restarted with new
address, then it would meet BE guest expectations
- the guest knows that it reads LE h/w so it will byteswap
register before processing it further. This is BE guest ARM
case (regardless of what KVM host endianity is).

3) BE guest reads integer from mapped h/w BE device
register - mmio.data[0] contains MSB, mmio.data[3]
contains LSB. Note that if &mmio.data[0] memory would
be placed in guest address space and instruction
restarted with new address, then it would meet BE
guest expectation - the guest knows that it reads
BE h/w so it will proceed further without any other
work. I guess, it is BE ppc case.

Arguments in favor of memcpy semantics of mmio.data[]
------------------------------------------------------

x) What are possible values of 'len'? Previous discussions
imply that is always powers of 2. Why is that? Maybe
there will be CPU that would need to do 5 bytes mmio
access, or 6 bytes. How do you assign endianity to
such case? 'len' 5 or 6, or any works fine with
memcpy semantics. I admit it is hypothetical case, but
IMHO it tests how clean ABI definition is.

x) Byte array does not have endianity because it
does not have any structure. If one would want to
imply structure why mmio is not defined in such way
so structure reflected in mmio definition?
Something like:

/* KVM_EXIT_MMIO */
struct {
__u64 phys_addr;
union {
__u8 byte;
__u16 hword;
__u32 word;
__u64 dword;
} data;
__u32 len;
__u8 is_write;
} mmio;

where len is really serves as union discriminator and
only allowed len values are 1, 2, 4, 8.
In this case, I agree, endianity of integer types
should be defined. I believe, use of byte array strongly
implies that original intent was to have semantics of
byte stream copy, just like memcpy does.

x) Note there is nothing wrong with user kernel ABI to
use just bytes stream as parameter. There is already
precedents like 'read' and 'write' system calls :).

x) Consider case when KVM works with emulated memory mapped
h/w devices where some devices operate in LE mode and others
operate in BE mode. It is defined by semantics of real h/w
device which is it, and should be emulated by emulator and KVM
given all other context. As far as mmio.data[] array concerned, if the
same integer value is read from these devices registers, mmio.data[]
memory should contain integer in opposite endianity for these
two cases, i.e MSB is data[0] in one case and MSB is
data[3] is in another case. It cannot be the same, because
except emulator and guest kernel, all other, like KVM host
and hypervisor, have no clue what endianity of device
actually is - it should treat mmio.data[] in the same way.
But resulting guest target CPU register would need to contain
normal integer value in one case and byteswapped in another,
because guest kernel would use it directly in one case and
byteswap it in another. Byte stream semantics allows to do
that. I don't see how it could happen if you fixate mmio.data[]
endianity in such way that it would contain integer in
the same format for BE and LE emulated device types.

If by this point you agree, that mmio.data[] user-land/kernel
ABI semantics should be just memcpy, stop reading :). If not,
you may would like to take a look at below appendix where I
described in great details endianity of data at different
points along mmio processing code path of existing ARM LE KVM,
and proposed ARM BE KVM. Note appendix, is very long and very
detailed, sorry about that, but I feel that earlier more
digested explanations failed, so it driven me to write out
all details how I see them. If I am wrong, I hope it would be
easier for folks to point in detailed explanation places
where my logic goes bad. Also, I am not sure whether this
mail thread is good place to discuss all details described
in the appendix. Christoffer, please advise whether I should take
that one back on [1]. But I hope this bigger picture may help to
see the mmio.data[] semantics issue in context.

More inline and appendix is at the end.

On 20 January 2014 11:19, Christoffer Dall <christoffer.dall@linaro.org> wrote:
> On Mon, Jan 20, 2014 at 03:22:11PM +0100, Alexander Graf wrote:
>>
>> On 17.01.2014, at 19:52, Peter Maydell <peter.maydell@linaro.org> wrote:
>>
>> > On 17 January 2014 17:53, Peter Maydell <peter.maydell@linaro.org> wrote:
>> >> Specifically, the KVM API says "here's a uint8_t[] byte
>> >> array and a length", and the current QEMU code treats that
>> >> as "this is a byte array written as if the guest CPU
>> >> (a) were in TARGET_WORDS_BIGENDIAN order and (b) wrote its
>> >> I/O access to this buffer rather than to the device".
>> >>
>> >> The KVM API docs don't actually specify the endianness
>> >> semantics of the byte array, but I think that that really
>> >> needs to be nailed down. I can think of a couple of options:
>> >> * always LE
>> >> * always BE
>> >> [these first two are non-starters because they would
>> >> break either x86 or PPC existing code]
>> >> * always the endianness the guest is at the time
>> >> * always some arbitrary endianness based purely on the
>> >> endianness the KVM implementation used historically
>> >> * always the endianness of the host QEMU binary
>> >> * something else?
>> >>
>> >> Any preferences? Current QEMU code basically assumes
>> >> "always the endianness of TARGET_WORDS_BIGENDIAN",
>> >> which is pretty random.
>> >
>> > Having thought a little more about this, my opinion is:
>> >
>> > * we should specify that the byte order of the mmio.data
>> > array is host kernel endianness (ie same endianness
>> > as the QEMU process itself) [this is what it actually
>> > is, I think, for all the cases that work today]

In above please consider two types of mapped emulated
h/w devices: BE and LE they cannot have mmio.data in the
same endianity. Currently in all observable cases LE ARM
and BE PPC devices endianity matches kernel/qemu
endianity but it would break when BE ARM is introduced
or LE PPC or one would start emulating BE devices on LE
ARM.

>> > * we should fix the code path in QEMU for handling
>> > mmio.data which currently has the implicit assumption
>> > that when using KVM TARGET_WORDS_BIGENDIAN is the same
>> > as the QEMU host process endianness (because it's using
>> > load/store functions which swap if TARGET_WORDS_BIGENDIAN
>> > is different from HOST_WORDS_BIGENDIAN)

I do not follow above. Maybe I am missing bigger context.
What is CPU under discussion in above? On ARM V7 system
when LE device is accessed as integer &mmio.data[0] address
would contain integer is in LE format, ie mmio.data[0] is LSB.

Here is gdb session of LE qemu running on V7 LE kernel and
TC1 LE guest. Guest kernel accesses sys_cfgstat register which is
arm_sysctl registers with offset of 0xa8. Note.arm_sysct is memory
mapped LE device.
Please check run->mmio structure after read
(cpu_physical_memory_rw) completes it is in 4 bytes integer in
LE format mmio.data[0] is LSB and is equal to 1
(s->syscfgstat value):

(gdb) bt
#0 arm_sysctl_read (opaque=0x95a600, offset=168, size=4) at
/home/root/20131219/qemu-be/hw/misc/arm_sysctl.c:127
#1 0x0023b9b4 in memory_region_read_accessor (mr=0x95b8e0,
addr=<optimized out>, value=0xb5c0dc18, size=4, shift=0,
mask=4294967295)
at /home/root/20131219/qemu-be/memory.c:407
#2 0x0023aba4 in access_with_adjusted_size (addr=4294967295,
value=0xb5c0dc18, value@entry=0xb5c0dc10, size=size@entry=4,
access_size_min=1,
access_size_max=2357596, access=access@entry=0x23b96c
<memory_region_read_accessor>, mr=mr@entry=0x95b8e0) at
/home/root/20131219/qemu-be/memory.c:477
#3 0x0023f95c in memory_region_dispatch_read1 (size=4, addr=168,
mr=0x95b8e0) at /home/root/20131219/qemu-be/memory.c:944
#4 memory_region_dispatch_read (size=4, pval=0xb5c0dc68, addr=168,
mr=0x95b8e0) at /home/root/20131219/qemu-be/memory.c:966
#5 io_mem_read (mr=mr@entry=0x95b8e0, addr=<optimized out>,
pval=pval@entry=0xb5c0dc68, size=size@entry=4) at
/home/root/20131219/qemu-be/memory.c:1743
#6 0x001abd38 in address_space_rw (as=as@entry=0x8102d8
<address_space_memory>, addr=469827752, buf=buf@entry=0xb6fd6028 "",
len=4, is_write=false,
is_write@entry=true) at /home/root/20131219/qemu-be/exec.c:2025
#7 0x001abf90 in cpu_physical_memory_rw (addr=<optimized out>,
buf=buf@entry=0xb6fd6028 "", len=<optimized out>, is_write=0)
at /home/root/20131219/qemu-be/exec.c:2070
#8 0x00239e00 in kvm_cpu_exec (cpu=cpu@entry=0x8758f8) at
/home/root/20131219/qemu-be/kvm-all.c:1701
#9 0x001a3f78 in qemu_kvm_cpu_thread_fn (arg=0x8758f8) at
/home/root/20131219/qemu-be/cpus.c:874
#10 0xb6cae06c in start_thread (arg=0xb5c0e310) at pthread_create.c:314
#11 0xb69f5070 in ?? () at
../ports/sysdeps/unix/sysv/linux/arm/clone.S:97 from /lib/libc.so.6
#12 0xb69f5070 in ?? () at
../ports/sysdeps/unix/sysv/linux/arm/clone.S:97 from /lib/libc.so.6
Backtrace stopped: previous frame identical to this frame (corrupt stack?)
(gdb) p /x s->sys_cfgstat
$25 = 0x1
(gdb) finish
Run till exit from #0 arm_sysctl_read (opaque=0x95a600, offset=168,
size=4) at /home/root/20131219/qemu-be/hw/misc/arm_sysctl.c:127
memory_region_read_accessor (mr=0x95b8e0, addr=<optimized out>,
value=0xb5c0dc18, size=4, shift=0, mask=4294967295) at
/home/root/20131219/qemu-be/memory.c:408
408 trace_memory_region_ops_read(mr, addr, tmp, size);
Value returned is $26 = 1
(gdb) enable 2
(gdb) cont
Continuing.

Breakpoint 2, kvm_cpu_exec (cpu=cpu@entry=0x8758f8) at
/home/root/20131219/qemu-be/kvm-all.c:1660
1660 kvm_arch_pre_run(cpu, run);
(gdb) bt
#0 kvm_cpu_exec (cpu=cpu@entry=0x8758f8) at
/home/root/20131219/qemu-be/kvm-all.c:1660
#1 0x001a3f78 in qemu_kvm_cpu_thread_fn (arg=0x8758f8) at
/home/root/20131219/qemu-be/cpus.c:874
#2 0xb6cae06c in start_thread (arg=0xb5c0e310) at pthread_create.c:314
#3 0xb69f5070 in ?? () at
../ports/sysdeps/unix/sysv/linux/arm/clone.S:97 from /lib/libc.so.6
#4 0xb69f5070 in ?? () at
../ports/sysdeps/unix/sysv/linux/arm/clone.S:97 from /lib/libc.so.6
Backtrace stopped: previous frame identical to this frame (corrupt stack?)
(gdb) p /x run->mmio
$27 = {phys_addr = 0x1c0100a8, data = {0x1, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0}, len = 0x4, is_write = 0x0}

Also please look at adjust_endianness function and
struct MemoryRegion 'endianness' field. IMHO in qemu it
works quite nicely already. MemoryRegion 'read' and 'write'
callbacks return/get data in native format adjust_endianness
function checks whether emulated device endianness matches
emulator endianness and if it is different it does byteswap
according to size. As in above example arm_sysctl_ops memory
region should be marked as DEVICE_LITTLE_ENDIAN when it
returns s->sys_cfgstat value LE qemu sees that endianity
matches and it does not byteswap of result, so integer at
&mmio.data[0] address is in LE form. When qemu would
run in BE mode on BE kernel, it would see that endianity
mismatches and it will byteswap s->sys_cfgstat native value
(BE), so mmio.data would contain integer in LE format again.

Note in currently committed code arm_sysctl_ops endianity
is DEVICE_NATIVE_ENDIAN, which is wrong - real vexpress
arm_sysctl device always gives/receives data in LE format regardless
of current CPSR E bit value, so it cannot be marked as NATIVE.
LE and BE kernels always read it as LE device; BE kernel follows
with byteswap. It was OK while we just run qemu in LE, but it
should be fixed to be LITTLE_ENDIAN for BE qemu work correctly
... and actually that device and few other ARM specific devices
endianity change to LITTLE_ENDIAN was the only change in qemu
to make BE KVM to work.

>>
>> Yes, I fully agree :).
>>
> Great, I'll prepare a patch for the KVM API documentation.
>
> -Christoffer
> _______________________________________________
> kvmarm mailing list
> kvmarm@lists.cs.columbia.edu
> https://lists.cs.columbia.edu/cucslists/listinfo/kvmarm

Thanks,
Victor

[1] http://lists.infradead.org/pipermail/linux-arm-kernel/2014-January/thread.html#223186

Appendix
Data path endianity in ARM KVM mmio
===================================

This writeup considers several scenarios and tracks endianity
of data how it travels from emulator to guest CPU register, in
case of ARM KVM. It starts with currently committed code for LE
KVM host case and further discusses proposed BE KVM host
arrangement.

Just to restrict discussion writeup considers code path of
integer (4 bytes) read from h/w mapped emulated device memory.
Writeup considers endianity of essential places involved in such
code path.

For all cases when endianity is defined, it is assumed that
values under consideration are in memory (opposite to be in
register that does not have endianity). I.e even if function
variable could be actually allocated in CPU register writeup
will reference to it as it is in memory, just to keep
discussion clean, except for final guest CPU register.

Let's consider the following places along data path from
emulator to guest CPU register:

1) emulator code that holds integer value to be read, assume
it would be global 'int emulated_hw_device_val' variable.
Normally in emulator it is held in native endian format - i.e
it is CPSR E bit is the same as kernel CPSR E bit. Just for
discussion sake assume that this h/w device registers
holds 5 as its value.

2) KVM_EXIT_MMIO part of 'struct kvm_run' structure, i.e
mmio.data byte array. Byte array does not have endianity,
but for this discussion it would track endianity of integer
at &mmio.data[0] address

3) 'data' variable type of 'unsigned long' in
kvm_handle_mmio_return function before vcpu_data_host_to_guest
call. KVM host mmio_read_buf function is used to fill this
variable from mmio.data buffer. mmio_read_buf actually
acts as memcpy from mmio.data buffer address,
just taking access size in account.

4) the same 'data' variable as above, but after
vcpu_data_host_to_guest function call, just before it is copied
to vcpu_reg target register location. Note
vcpu_data_host_to_guest function may byteswap value of 'data'
depending on current KVM host endianity and value of
guest CPSR E bit.

5) guest CPU spilled register array, location of target register
i.e integer at vcpu_reg(vcpu, vcpu->arch.mmio_decode.rt) address

6) finally guest CPU register filled from vcpu_reg just before
guest resume execution of trapped emulated instruction. Note
it is done by hypervisor part of code and hypervisor EE bit is
the same as KVM host CPSR E bit.

Note again, KVM host, emulator, and hypervisor part of code (guest
CPU registers save and restore code) always run in the same
endianity. Endianity of accessed emulated devices and endianity
of guest varies independently of KVM host endianity.

Below sections consider all permutations of all possible cases,
it maybe quite boring to read. I've created summary table at
the end, you can jump to the table, after reading few cases.
But if you have objections and you see things happen differently
please comment inline of the use cases steps.

LE KVM host
===========

Use case 1
----------

Emulated h/w device gives data in LE form; emulator and KVM
host endianity is LE (host CPSR E bit is off); guest compiled
in LE mode; and guest does access with CPSR E bit off

1) 'emulated_hw_device_val' emulator variable is LE
2) &mmio.data[0] holds integer in LE format, matches device
endianity
3) 'data' is LE
4) 'data' is LE (since guest CPSR E bit is off no byteswap)
5) integer at 'vcpu_reg(vcpu, vcpu->arch.mmio_decode.rt)' is LE
6) final guest target CPU register contains 5 (0x00000005)

guest resumes execution ... Let's say after 'ldr r1, [r0]'
instruction, where r0 holds address of devices, it knows
that it reads LE mapped h/w so no addition processing is
needed

Use case 2
----------

Emulated h/w device gives data in LE form; emulator and KVM
host endianity is LE (host CPSR E bit is off); guest compiled
in BE mode; and guest does access with CPSR E bit on

1) 'emulated_hw_device_val' emulator variable is LE
2) &mmio.data[0] holds integer in LE format; matches device
endianity
3) 'data' is LE
4) 'data' is BE (since guest CPSR E bit is on, vcpu_data_host_to_guest
will do byteswap: cpu_to_be)
5) integer at 'vcpu_reg(vcpu, vcpu->arch.mmio_decode.rt)' is BE
6) final guest target CPU register contains 0x05000000

guest resumes execution after 'ldr r1, [r0]', guest kernel
knows that it runs in BE mode (E bit on), it knows that it reads
LE device memory, it needs to byteswap r1 before further
processing so it does 'rev r1, r1' and proceed with result

Use case 3
----------

Emulated h/w device gives data in BE form; emulator and KVM
host endianity is LE (host CPSR E bit is off); guest compiled
in LE mode; and guest does access with CPSR E bit off

1) 'emulated_hw_device_val' emulator variable is LE
2) &mmio.data[0] holds integer in BE format; emulator byteswaps
it because it knows that device endianity is opposite to native,
and it should match device endianity
3) 'data' is BE
4) 'data' is BE (since guest CPSR E bit is off no byteswap)
5) integer at 'vcpu_reg(vcpu, vcpu->arch.mmio_decode.rt)' is BE
6) final guest target CPU register contains 0x05000000

guest resumes execution after 'ldr r1, [r0]', guest kernel
knows that it runs in LE mode (E bit off), it knows that it
reads BE device memory, it need to byteswap r1 before further
processing so it does 'rev r1, r1' and proceeds with result

Use case 4
----------

Emulated h/w device gives data in BE form; emulator and KVM
host endianity is LE (host CPSR E bit is off); guest compiled
in BE mode; and guest does access with CPSR E bit on

1) 'emulated_hw_device_val' emulator variable is LE
2) &mmio.data[0] holds integer in BE format; emulator byteswaps
it because it knows that device endianity is opposite to native,
and should match device endianity
3) 'data' is BE
4) 'data' is LE (since guest CPSR E bit is on, vcpu_data_host_to_guest
will do byteswap: cpu_to_be)
5) integer at 'vcpu_reg(vcpu, vcpu->arch.mmio_decode.rt)' is LE
6) final guest target CPU register contains 5 (0x00000005)

guest resumes execution after 'ldr r1, [r0]', guest kernel
knows that it runs in BE mode, it knows that it reads BE device
memory, so it does not need to do anything before further
processing.

Above uses cases that is exactly what we have now after Marc's
commit to support BE guest on LE KVM host. Further use
cases describe how it would work with BE KVM patches I proposed.
It is understood that it is subject of further discussion.

BE KVM host
===========

Use case 5
----------

Emulated h/w device gives data in LE form; emulator and KVM
host endianity is BE (host CPSR E bit is on); guest compiled
in BE mode; and guest does access with CPSR E bit on

1) 'emulated_hw_device_val' emulator variable is BE
2) &mmio.data[0] holds integer in LE format; emulator byteswaps
it because it knows that device endianity is opposite to native;
matches device endianity
3) 'data' is LE
4) 'data' is LE (since guest CPSR E bit is on, BE KVM host kernel
does *not* do byteswap: cpu_to_be no effect in BE host kernel)
5) integer at 'vcpu_reg(vcpu, vcpu->arch.mmio_decode.rt)' is LE
6) final guest target CPU register contains 0x05000000 because
hypervisor runs in BE mode, so load of LE integer will be
byteswapped value in register

guest resumes execution after 'ldr r1, [r0]', guest kernel
knows that it runs in BE mode, it knows that it reads LE device
memory, it need to byteswap r1 before further processing so it
does 'rev r1, r1' and proceeds with result

Use case 6
----------

Emulated h/w device gives data in LE form; emulator and KVM
host endianity is BE (host CPSR E bit is on); guest compiled
in LE mode; and guest does access with CPSR E bit off

1) 'emulated_hw_device_val' emulator variable is BE
2) &mmio.data[0] holds integer in LE format; emulator byteswaps
it because it knows that device endianity is opposite to native;
matches device endianity
3) 'data' is LE
4) 'data' is BE (since guest CPSR E bit is off, BE KVM host kernel
does byteswap: cpu_to_le)
5) integer at 'vcpu_reg(vcpu, vcpu->arch.mmio_decode.rt)' is BE
6) final guest target CPU register contains 5 (0x00000005) because
hypervisor runs in BE mode, so load of BE integer will be OK

guest resumes execution after 'ldr r1, [r0]', guest kernel
knows that it runs in LE mode, it knows that it reads LE device
memory, so it does not need to do anything else it just proceeds

Use case 7
----------

Emulated h/w device gives data in BE form; emulator and KVM
host endianity is BE (host CPSR E bit is on); guest compiled
in BE mode; and guest does access with CPSR E bit on

1) 'emulated_hw_device_val' emulator variable is BE
2) &mmio.data[0] holds integer in BE format; matches device
endianity
3) 'data' is BE
4) 'data' is BE (since guest CPSR E bit is on, BE KVM host kernel
does *not* do byteswap: cpu_to_be no effect in BE host kernel)
5) integer at 'vcpu_reg(vcpu, vcpu->arch.mmio_decode.rt)' is BE
6) final guest target CPU register contains 5 (0x00000005) because
hypervisor runs in BE mode, so load of BE integer will be OK

guest resumes execution after 'ldr r1, [r0]', guest kernel
knows that it runs in BE mode, it knows that it reads BE device
memory, so it does not need to do anything else it just proceeds

Use case 8
----------

Emulated h/w device gives data in BE form; emulator and KVM
host endianity is BE (host CPSR E bit is on); guest compiled
in LE mode; and guest does access with CPSR E bit off

1) 'emulated_hw_device_val' emulator variable is BE
2) &mmio.data[0] holds integer in BE format; matches device
endianity
3) 'data' is BE
4) 'data' is LE (since guest CPSR E bit is off, BE KVM host kernel
does byteswap: cpu_to_le)
5) integer at 'vcpu_reg(vcpu, vcpu->arch.mmio_decode.rt)' is LE
6) final guest target CPU register contains 0x05000000 because
hypervisor runs in BE mode, so load of LE integer will be
byteswapped value in register

guest resumes execution after 'ldr r1, [r0]', guest kernel
knows that it runs in LE mode, it knows that it reads BE device
memory, it need to byteswap r1 before further processing so it
does 'rev r1, r1' and proceeds with result

Note that with BE kernel we actually have some initial portion
of assembler code that is executed with CPSR bit off and it reads
LE h/w - i.e it falls into use case 1.

Summary Table (please use fixed font to see it correctly)
========================================

--------------------------------------------------------------
| Use Case # | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
--------------------------------------------------------------
| KVM Host, | LE | LE | LE | LE | BE | BE | BE | BE |
| Emulator, | | | | | | | | |
| Hypervisor | | | | | | | | |
| Endianity | | | | | | | | |
--------------------------------------------------------------
| Device | LE | LE | BE | BE | LE | LE | BE | BE |
| Endianity | | | | | | | | |
--------------------------------------------------------------
| Guest | LE | BE | LE | BE | BE | LE | BE | LE |
| Access | | | | | | | | |
| Endianity | | | | | | | | |
--------------------------------------------------------------
| Step 1) | LE | LE | LE | LE | BE | BE | BE | BE |
--------------------------------------------------------------
| Step 2) | LE | LE | BE | BE | LE | LE | BE | BE |
--------------------------------------------------------------
| Step 3) | LE | LE | BE | BE | LE | LE | BE | BE |
--------------------------------------------------------------
| Step 4) | LE | BE | BE | LE | LE | BE | BE | LE |
--------------------------------------------------------------
| Step 5) | LE | BE | BE | LE | LE | BE | BE | LE |
--------------------------------------------------------------
| Final Reg | no | yes | yes | no | yes | no | no | yes |
| value | | | | | | | | |
| byteswapped| | | | | | | | |
--------------------------------------------------------------
| Guest | no | yes | yes | no | yes | no | no | yes |
| Follows | | | | | | | | |
| with rev | | | | | | | | |
--------------------------------------------------------------

Few objservations
=================

x) Note above table is symmetric wrt to BE<->LE change:
1<-->7
2<-->8
3<-->5
4<-->6

x) &mmio.data[0] address always holds integer in the same
format as emulated device endianity

x) During step 4) when vcpu_data_host_to_guest function
is used, if guest E bit value different, but everything else
is the same, opposite result are produced (1&2, 3&4, 5&6,
7&8)

If you reached to this end :), again, thank you very much for
reading it!

- Victor