hw/intc/arm_gic: GICV_HPPIR drops the virtual SGI source CPUID that GICV_IAR reports

Host environment

  • Operating system: macOS 14.8.7 (build 23J520)

  • OS/kernel version: Darwin 23.6.0 arm64

  • Architecture: ARM (aarch64, Apple silicon)

  • QEMU flavor: qemu-system-aarch64

  • QEMU version: 11.1.50 (v11.1.0-907-ga925240509), built from git master

  • QEMU command line:

    qemu-system-aarch64 -M virt,gic-version=2,virtualization=on \
      -cpu cortex-a57 -smp 2 -accel qtest -display none -qtest stdio

Emulated/Virtualized environment

  • Operating system: none, qtest only
  • OS/kernel version: n/a
  • Architecture: ARM (GICv2 virtual CPU interface)

Description of problem

Disclosure: AI-assisted tooling helped prepare this report. I ran and verified the tests below.

For SGIs, bits [12:10] of IAR and HPPIR hold the source CPUID. On the virtual CPU interface the two registers disagree: GICV_IAR returns the CPUID from the List Register, GICV_HPPIR zeroes it. Same interrupt, two different sources.

With List Register 0 holding a pending SGI, INTID 5 from CPUID 1 (GICH_LR0 = 0x12000405):

readl 0x08040018   # GICV_HPPIR -> 0x00000005   (CPUID lost)
readl 0x0804000c   # GICV_IAR   -> 0x00000405   (CPUID present)

Both should read 0x405.

Arm IHI 0048B.b defines the contract: 5.3.8 (GICV_IAR), 5.5.4 (the List Register CPUID field) and above all 5.5.7, which specifies the virtual HPPIR as reporting the same interrupt the next IAR read would return.

Steps to reproduce

  1. Build qemu-system-aarch64 from master.

  2. Run it with no kernel, disk or guest code, feeding the qtest commands on stdin. QEMU exits at EOF:

    qemu-system-aarch64 -M virt,gic-version=2,virtualization=on \
      -cpu cortex-a57 -smp 2 -accel qtest -display none -qtest stdio
    writel 0x08040000 0x00000001   # GICV_CTLR: enable the virtual CPU interface
    writel 0x08040004 0x000000ff   # GICV_PMR:  allow all priorities
    writel 0x08030000 0x00000001   # GICH_HCR:  enable the virtual interface
    writel 0x08030100 0x12000405   # GICH_LR0:  pending SGI, INTID 5, CPUID 1
    readl  0x08040018              # GICV_HPPIR
    readl  0x0804000c              # GICV_IAR
  3. GICV_HPPIR reads 0x5, GICV_IAR reads 0x405.

Additional information

Varying only the LR CPUID field, measured with -smp 8 so every CPUID below is a real source CPU:

GICH_LR0 LR CPUID GICV_HPPIR GICV_IAR agree
0x12000005 0 0x5 0x5 yes
0x12000405 1 0x5 0x405 no
0x12001c05 7 0x5 0x1c05 no

This is generic GICv2 code, not board code, so any machine with a GICv2 virtual CPU interface is affected. Reproduced above on virt; also on -M raspi4b under TCG with a bare-metal EL2 payload, where the EL1 guest sees 0x005 instead of 0x405.

Reproduced on master a9252405 (2026-09-02) and on 11.0.2.

Edited by Karl Mehltretter