On 2026-09-02 17:17:58 Wed, Narayana Murty N wrote:
> An EEH PE hot or fundamental reset involves a synchronous kernel ioctl
> (VFIO_EEH_PE_RESET_HOT / VFIO_EEH_PE_RESET_FUNDAMENTAL) that asserts
> then de-asserts a PCI reset signal to the endpoint.  During this window
> the device's BARs are inaccessible, but QEMU may still have those BARs
> memory-mapped for direct guest access.  A guest MMIO fault that arrives
> while the hardware is in reset can cause an unexpected host kernel page
> fault or an indeterminate read value.
> 
> Fix this by disabling the BAR mmap windows for every VFIO PCI device
> under the PHB before issuing the PE reset ioctl, and re-enabling them
> after VFIO_EEH_PE_CONFIGURE succeeds.
> 
> A new spapr_phb_vfio_eeh_post_configure() bus walker calls
> vfio_region_mmaps_set_enabled(..., true) on the configure success
> path inside spapr_phb_vfio_eeh_configure().  On failure the mmaps
> remain disabled; the next EEH reset attempt will call pre_reset
> again.
> 
> Signed-off-by: Narayana Murty N <[email protected]>
> ---
>  hw/ppc/spapr_pci_vfio.c | 68 +++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 68 insertions(+)
> 
> diff --git a/hw/ppc/spapr_pci_vfio.c b/hw/ppc/spapr_pci_vfio.c
> index c233822d14..1cf058cbde 100644
> --- a/hw/ppc/spapr_pci_vfio.c
> +++ b/hw/ppc/spapr_pci_vfio.c
> @@ -252,17 +252,28 @@ int spapr_phb_vfio_eeh_get_state(SpaprPhbState *sphb, 
> int *state)
>   * pci_host_config_write_common() so that the VFIO config-write handler calls
>   * vfio_msix_disable(), cleanly releasing vectors and KVM irqfd routes while
>   * leaving the shadow intact.
> + *
> + * After disabling interrupts, disable BAR mmap windows so that the host
> + * kernel PE reset ioctl does not race with QEMU direct-mapped guest 
> accesses.
> + * The timer that would ordinarily re-enable mmaps after an INTx quiet period
> + * is cancelled here; mmaps are restored after VFIO_EEH_PE_CONFIGURE succeeds
> + * in spapr_phb_vfio_eeh_configure().
>   */
>  static void spapr_phb_vfio_eeh_prepare_dev(PCIBus *bus,
>                                             PCIDevice *pdev,
>                                             void *opaque)
>  {
> +    VFIOPCIDevice *vdev;
>      uint16_t flags;
> +    int i;
>  
>      if (!object_dynamic_cast(OBJECT(pdev), TYPE_VFIO_PCI_DEVICE)) {
>          return;
>      }
>  
> +    vdev = VFIO_PCI_DEVICE(pdev);
> +
> +    /* Step 1: disable MSI-X without wiping the shadow table (see above). */
>      if (msix_enabled(pdev)) {
>          flags = pci_get_word(pdev->config + pdev->msix_cap + PCI_MSIX_FLAGS);
>          flags &= ~PCI_MSIX_FLAGS_ENABLE;
> @@ -270,6 +281,19 @@ static void spapr_phb_vfio_eeh_prepare_dev(PCIBus *bus,
>                                       pdev->msix_cap + PCI_MSIX_FLAGS,
>                                       pci_config_size(pdev), flags, 2);
>      }
> +
> +    /*
> +     * Step 2: cancel any pending INTx mmap re-enable timer.  The timer is
> +     * only allocated when PCI_INTERRUPT_PIN is non-zero, so guard the call.
> +     */
> +    if (vdev->intx.mmap_timer) {
> +        timer_del(vdev->intx.mmap_timer);
> +    }
> +
> +    /* Step 3: disable BAR mmaps last, after interrupt teardown. */
> +    for (i = 0; i < PCI_ROM_SLOT; i++) {
> +        vfio_region_mmaps_set_enabled(&vdev->bars[i].region, false);
> +    }

Maybe you can add a small helper in something like
vfio_eeh_pci_pre_reset(pdev) in hw/vfio/pci.c to include step 2 and 3 ?
That way VFIOPCIDevice internals can be hidden under the helper.

>  }
>  
>  static void spapr_phb_vfio_eeh_prepare_bus(PCIBus *bus, void *opaque)
> @@ -286,6 +310,43 @@ static void spapr_phb_vfio_eeh_pre_reset(SpaprPhbState 
> *sphb)
>      pci_for_each_bus(phb->bus, spapr_phb_vfio_eeh_prepare_bus, NULL);
>  }
>  
> +/*
> + * Re-enable BAR mmap windows for a single VFIO PCI device after a successful
> + * EEH PE configure.  Called only on the configure success path; on failure 
> the
> + * mmaps remain disabled until the next hot/fundamental reset attempt.
> + */
> +static void spapr_phb_vfio_eeh_post_configure_dev(PCIBus *bus,
> +                                                   PCIDevice *pdev,
> +                                                   void *opaque)
> +{
> +    VFIOPCIDevice *vdev;
> +    int i;
> +
> +    if (!object_dynamic_cast(OBJECT(pdev), TYPE_VFIO_PCI_DEVICE)) {
> +        return;
> +    }
> +
> +    vdev = VFIO_PCI_DEVICE(pdev);
> +
> +    for (i = 0; i < PCI_ROM_SLOT; i++) {
> +        vfio_region_mmaps_set_enabled(&vdev->bars[i].region, true);
> +    }

Same here.

Also, what about the mmap timer ? Does that get re-armed when INTx is
re-initialsed during EEH recovery and hence we ignore here ?

> +}
> +
> +static void spapr_phb_vfio_eeh_post_configure_bus(PCIBus *bus, void *opaque)
> +{
> +    pci_for_each_device_under_bus(bus,
> +                                  spapr_phb_vfio_eeh_post_configure_dev,
> +                                  NULL);
> +}
> +
> +static void spapr_phb_vfio_eeh_post_configure(SpaprPhbState *sphb)
> +{
> +    PCIHostState *phb = PCI_HOST_BRIDGE(sphb);
> +
> +    pci_for_each_bus(phb->bus, spapr_phb_vfio_eeh_post_configure_bus, NULL);
> +}
> +
>  int spapr_phb_vfio_eeh_reset(SpaprPhbState *sphb, int option)
>  {
>      uint32_t op;
> @@ -293,6 +354,11 @@ int spapr_phb_vfio_eeh_reset(SpaprPhbState *sphb, int 
> option)
>  
>      switch (option) {
>      case RTAS_SLOT_RESET_DEACTIVATE:
> +        /*
> +         * Deactivate does not perform a full PE reset; BAR mmaps were 
> already
> +         * disabled by the preceding HOT or FUNDAMENTAL reset call and must 
> not
> +         * be re-enabled here.
> +         */
>          op = VFIO_EEH_PE_RESET_DEACTIVATE;
>          break;
>      case RTAS_SLOT_RESET_HOT:
> @@ -324,6 +390,8 @@ int spapr_phb_vfio_eeh_configure(SpaprPhbState *sphb)
>          return RTAS_OUT_PARAM_ERROR;
>      }
>  
> +    spapr_phb_vfio_eeh_post_configure(sphb);
> +
>      return RTAS_OUT_SUCCESS;
>  }


Thanks,
-Mahesh.

-- 
Mahesh J Salgaonkar

Reply via email to