On Mon, 19 Jan 2026 at 12:00, Alex Bennée <[email protected]> wrote:
>
> The GIC should always be a little-endian device as big-endian
> behaviour is a function of the current CPU configuration not the
> system as a whole. This allows us to keep the MSI data in plain host
> order rather then potentially truncating with multiple byte swaps of
> different sizes.
>
> Signed-off-by: Alex Bennée <[email protected]>
> ---
>  hw/intc/arm_gicv3_its_common.c | 4 ++--
>  hw/intc/arm_gicv3_its_kvm.c    | 2 +-
>  2 files changed, 3 insertions(+), 3 deletions(-)
>
> diff --git a/hw/intc/arm_gicv3_its_common.c b/hw/intc/arm_gicv3_its_common.c
> index e946e3fb87b..60a5abd8d3e 100644
> --- a/hw/intc/arm_gicv3_its_common.c
> +++ b/hw/intc/arm_gicv3_its_common.c
> @@ -81,7 +81,7 @@ static MemTxResult gicv3_its_trans_write(void *opaque, 
> hwaddr offset,
>      if (offset == 0x0040 && ((size == 2) || (size == 4))) {
>          GICv3ITSState *s = ARM_GICV3_ITS_COMMON(opaque);
>          GICv3ITSCommonClass *c = ARM_GICV3_ITS_COMMON_GET_CLASS(s);
> -        int ret = c->send_msi(s, le64_to_cpu(value), attrs.requester_id);
> +        int ret = c->send_msi(s, value, attrs.requester_id);
>
>          if (ret <= 0) {
>              qemu_log_mask(LOG_GUEST_ERROR,

> diff --git a/hw/intc/arm_gicv3_its_kvm.c b/hw/intc/arm_gicv3_its_kvm.c
> index ae12d41eee1..a8d6d4fb540 100644
> --- a/hw/intc/arm_gicv3_its_kvm.c
> +++ b/hw/intc/arm_gicv3_its_kvm.c
> @@ -58,7 +58,7 @@ static int kvm_its_send_msi(GICv3ITSState *s, uint32_t 
> value, uint16_t devid)
>
>      msi.address_lo = extract64(s->gits_translater_gpa, 0, 32);
>      msi.address_hi = extract64(s->gits_translater_gpa, 32, 32);
> -    msi.data = le32_to_cpu(value);
> +    msi.data = value;
>      msi.flags = KVM_MSI_VALID_DEVID;
>      msi.devid = devid;
>      memset(msi.pad, 0, sizeof(msi.pad));

Having looked at this code a bit more carefully and figured out
that the send_msi method isn't calling core QEMU code but just
one bit of our ITS implementation talking to another, I agree
that these changes are correct:

 * the kernel KVM_SIGNAL_MSI ioctl expects the data field to
   be in host byte order
 * the send_msi() method is entirely up to us how to
   specify, and "host byte order" is the least surprising
   one for a function call
 * write methods of a MemoryRegionOps always get their
   data in host byte order

So we should write this up in the commit message. This is
technically a bug fix (albeit one which only affects anybody
adventurous enough to run KVM on big-endian AArch64 :-))
so we could probably cc: stable too.

I also noticed while looking through the code that we
only seem to implement the send_msi method on the KVM
ITS. This works because TCG passes in its own MemoryRegionOps
for the "translation" MemoryRegion. But that means all
this base class code that calls a send_msi method on
the subclass is used only by KVM. We could tidy this up by:
 * moving gicv3_its_trans_{read,write} from the base class
   to the KVM subclass
 * having gicv3_its_init_mmio() not have fallback code
   for tops == NULL
 * directly call kvm_its_send_msi() instead of indirecting
   through a send_msi method
 * remove the send_msi method pointer entirely

Second side note: kvm_irqchip_send_msi() in accel/kvm/kvm-all.c
also has a rather dodgy looking le32_to_cpu() call in it,
which is probably the inspiration for this one in the Arm code.
Luckily, we never use that function on Arm, and its only
callers are in always-LE-host targets. But all those callsites
seem to assume the msi.data field should be in host order
so this is probably something that somebody who cares about
x86 hosts might like to tidy up.

thanks
-- PMM

Reply via email to