This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

Virtual interrupt deactivation in gicv2 cannot work normally

I am writing a virtual machine for aarch64. My system is using gicv2. Now I am dealing with virutal interrupt. I have implemented a virtual gicd and mapped gicv to guest gicc. When I booted linux, I found the interrupt only triggered once . After debugging, I suppose there are some problems with deactive virtual interrupt.  The debug process is as follows:

The processing of virtual interrupts is as follows: Using the virtual timer interrupt (interrupt number 27) as an example. Upon receiving the interrupt, the system traps from the guest to the Hypervisor (EL2). The Hypervisor then performs operations on the Virtual Generic Interrupt Controller (GICH) to inject the interrupt. In this case, the value of my GICH_LR register is 0x9a006c1b, indicating that this is a hardware interrupt because its HW, bit [31] is set to 1, and the pINTID, bits [19:10] is 27.

After injecting this interrupt into the virtual machine, I added logging in Linux and found that the interrupt was executed successfully, and the EOI register was written. Since I directly mapped GICV to the guest as GICC, the guest should write to GICV_EOIR in this context. Theoretically, triggering the EOI for the virtual interrupt should lead to a maintenance interrupt, but it does not. I observed that the value of the GICH_MISR register is also 0.

For debugging purposes, I modified the Linux code. After writing EOI to the interrupt, I manually set the GICC_EOIR register by trapping through HVC to EL2. After making this modification, the system can boot successfully. To rule out the possibility that the guest did not successfully write to GICV_EOIR, I attempted trapping to the hypervisor without writing to the GICC_EOIR register, instead writing to GICV_EOIR, but this also proved unsuccessful.

I currently have two questions:

1. Why doesn't the writing to GICV_EOIR trigger a maintenance interrupt while my GICH_HCR is properly set to 0b101? The related GICH_HCR documentation:

  • LRENPIE, bit [2]:List Register Entry Not Present Interrupt Enable.
  • En, bit [0]:Enable. Global enable bit for the virtual CPU interface.

2. According to the documentation,

After the virtual machine completes processing the corresponding virtual interrupt, it writes to the GICV_EOIR or GICV_AEOIR to deactivate the interrupt. This deactivates both the virtual interrupt and the corresponding physical interrupt, provided that both of the following conditions are true:

  • the GICV_CTLR.EOImode bit is set to 0
  • the GICH_LRn.HW bit is set to 1.

I have verified that the value of GICV_CTLR is 1, EOImode is not set, and I monitored the corresponding GICH_LR values. During guest's asserting interrupt, processing interrupt, and deactivating interrupt, only the state value changes from 01 (pending) to 10 (active) and finally to 00 (deactivated), while the 31st bit HW remains 1. Why doesn't the guest writing to GICV_EOIR result in the interrupt being deactivated?

  • My linux kernel version is 6.2.15 and I was use qemu to emulating a real aarch64 machine, which means I run my hypervisor in qemu. The qemu version is QEMU emulator version 7.2.5.

  • The List Register state does seem to be getting updated as you'd expect (Pending -> Active -> Invalid), so that bit is working.

    I think you might be trying to generate the wrong maintenance interrupt.  LRENPIE is generated is there is a deactivate (write to GICV_EIOR or GICV_DIR) and the specified INTID does not match one of the List Registers.  In your case, the INTID does match in a List Register so EOICount is not incremented and the Maintenance interrupt isn't generated.  You could set UIE instead, but that would go off at the point the VM ack'ed the interrupt, not when it deactivated the interrupt.

    So the questions seems to be why isn't the virtual deactivate also deactivating the physical interrupt.  The obvious causes were would GICH_LR.HW being 0 or GICV_CTLR.EOImode being 1, but you've checked those.  As the interrupt is a PPI, is VM running on the same physical core as the pPPI was originally generated on?