From: Dexuan Cui <[email protected]> Sent: Monday, May 4, 2026 5:49 PM
> 
> If vmbus_reserve_fb() in the kdump/kexec kernel fails to properly reserve
> the framebuffer MMIO range (which is below 4GB) due to a Gen2 VM's
> screen.lfb_base being zero [1], there is an MMIO conflict between the
> drivers hyperv-drm and pci-hyperv: when the driver pci-hyperv's
> hv_pci_allocate_bridge_windows() calls vmbus_allocate_mmio() to get a
> 32-bit MMIO range, it may get an MMIO range that overlaps with the
> framebuffer MMIO range, and later hv_pci_enter_d0() fails with an
> error message "PCI Pass-through VSP failed D0 Entry with status" since
> the host thinks that PCI devices must not use MMIO space that the
> host has assigned to the framebuffer.
> 
> This is especially an issue if pci-hyperv is built-in and hyperv-drm is
> built as a module. Consequently, the kdump/kexec kernel fails to detect
> PCI devices via pci-hyperv, and may fail to mount the root file system,
> which may reside in a NVMe disk. The issue described here has existed
> for SR-IOV VF NICs since day one of the pci-hyperv driver, and has been
> worked around on x64 when possible. With the recent introduction of
> ARM64 VMs that boot from NVMe, there is no workaround, so we need a
> formal fix.
> 
> On Gen2 VMs, if the screen.lfb_base is 0 in the kdump/kexec kernel [1],
> fall back to the low MMIO base, which should be equal to the framebuffer
> MMIO base [2] (the statement is true according to my testing on x64
> Windows Server 2016, and on x64 and ARM64 Windows Server 2025 and on
> Azure. I checked with the Hyper-V team and they said the statement should
> continue to be true for Gen2 VMs). In the first kernel, screen.lfb_base
> is not 0; if the user specifies a very high resolution, it's not enough
> to only reserve 8MB: in this case, reserve half of the space below 4GB,
> but cap the reservation to 128MB, which is the required framebuffer size
> of the highest resolution 7680*4320 supported by Hyper-V.
> 
> While at it, fix the comparison "end > VTPM_BASE_ADDRESS" by changing
> the > to >=. Here the 'end' is an inclusive end (typically, it's
> 0xFFFF_FFFF for the low MMIO range).
> 
> Note: vmbus_reserve_fb() now also reserves an MMIO range at the beginning
> of the low MMIO range on CVMs, which have no framebuffers (the
> 'screen.lfb_base' in vmbus_reserve_fb() is 0 for CVMs), just in case the
> host might treat the beginning of the low MMIO range specially [4]. BTW,
> the OpenHCL kernel is not affected by the change, because that kernel
> boots with DeviceTree rather than ACPI (so vmbus_reserve_fb() won't run
> there), and there is no framebuffer device for that kernel.
> 
> Note: normally Gen1 VMs don't have the MMIO conflict issue because the
> framebuffer MMIO range (which is hardcoded to base=4GB-128MB and
> size=64MB for Gen1 VMs by the host) is always reported via the legacy PCI
> graphics device's BAR, so the kdump/kexec kernel can reserve the 64MB
> MMIO range; however, if the VM is configured to use a very high resolution
> and the required framebuffer size exceeds 64MB (AFAIK, in practice, this
> isn't a typical configuration by users), the hyperv-drm driver may need to
> allocate an MMIO range above 4GB and change the framebuffer MMIO location
> to the allocated MMIO range -- in this case, there can still be issues [3]
> which can't be easily fixed: any possible affected Gen1 users would have
> to use a resolution whose framebuffer size is <= 64MB, or switch to Gen2
> VMs.
> 
> [1]
> https://lore.kernel.org/all/SA1PR21MB692176C1BC53BFC9EAE5CF8EBF51A@SA1PR2
> 1MB6921.namprd21.prod.outlook.com/
> [2]
> https://lore.kernel.org/all/SA1PR21MB69218F955B62DFF62E3E88D2BF222@SA1PR2
> 1MB6921.namprd21.prod.outlook.com/
> [3]
> https://lore.kernel.org/all/SA1PR21MB69213486F821CA5A2C793C81BF342@SA1PR
> 21MB6921.namprd21.prod.outlook.com/
> [4]
> https://lore.kernel.org/all/SN6PR02MB415726B17D5A6027CD1717E8D4342@SN6P
> R02MB4157.namprd02.prod.outlook.com/
> 
> Fixes: 4daace0d8ce8 ("PCI: hv: Add paravirtual PCI front-end for Microsoft 
> Hyper-V VMs")
> CC: [email protected]
> Signed-off-by: Dexuan Cui <[email protected]>
> ---
> 
> Changes since v1 
> (https://lore.kernel.org/all/[email protected]/):
>   Fixed a typo in the subject: s/logc/logic/.
> 
>   In the commit message, better explained fb_mmio_base is equal to
>   low_mmio_base for Gen2 VMs.
> 
>   Addressed Michael Kelley's comments:
> 
>     In the commit message:
>          Changed the "kdump" to "kdump/kexec" since the described
>          issue is applicable to both kdump and kexec.
> 
>          Provided more detail about the MMIO conflict.
> 
>          Described an scenario where Gen1 VMs can also be affected.
> 
>     Added a pr_warn() in vmbus_reserve_fb() in case the 'start' is 0.
> 
>     Dropped the CVM check in vmbus_reserve(), meaning vmbus_reserve_fb()
>     also reserves MMIO for CVMs.
> 
>   Changed "low_mmio_base >= SZ_4G" to "upper_32_bits(low_mmio_base)"
>   to avoid a compilation warning for the i386 build.
> 
>   Changed "0x%pa" to "%pa", because %pa already adds a "0x" prefix.
> 
> 
> Hi Krister, Matthew, sorry -- I'm not adding your Tested-by's since
> the code changed, though the change is small. If the v2 looks good
> to Michael, please test the patch again.
> 
> Hi Hardik, I'm not adding your Reviewed-by since the patch changed.
> Please review the v2.
> 
>  drivers/hv/vmbus_drv.c | 29 ++++++++++++++++++++++++++---
>  1 file changed, 26 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c
> index f0d0803d1e16..d73ac5c8dd04 100644
> --- a/drivers/hv/vmbus_drv.c
> +++ b/drivers/hv/vmbus_drv.c
> @@ -2327,8 +2327,8 @@ static acpi_status vmbus_walk_resources(struct 
> acpi_resource *res, void *ctx)
>               return AE_NO_MEMORY;
> 
>       /* If this range overlaps the virtual TPM, truncate it. */
> -     if (end > VTPM_BASE_ADDRESS && start < VTPM_BASE_ADDRESS)
> -             end = VTPM_BASE_ADDRESS;
> +     if (end >= VTPM_BASE_ADDRESS && start < VTPM_BASE_ADDRESS)
> +             end = VTPM_BASE_ADDRESS - 1;
> 
>       new_res->name = "hyperv mmio";
>       new_res->flags = IORESOURCE_MEM;
> @@ -2395,6 +2395,7 @@ static void vmbus_mmio_remove(void)
>  static void __maybe_unused vmbus_reserve_fb(void)
>  {
>       resource_size_t start = 0, size;
> +     resource_size_t low_mmio_base;
>       struct pci_dev *pdev;
> 
>       if (efi_enabled(EFI_BOOT)) {
> @@ -2402,6 +2403,24 @@ static void __maybe_unused vmbus_reserve_fb(void)
>               if (IS_ENABLED(CONFIG_SYSFB)) {
>                       start = sysfb_primary_display.screen.lfb_base;
>                       size = max_t(__u32, 
> sysfb_primary_display.screen.lfb_size, 0x800000);
> +
> +                     low_mmio_base = hyperv_mmio->start;
> +                     if (!low_mmio_base || upper_32_bits(low_mmio_base) ||
> +                         (start && start < low_mmio_base)) {
> +                             pr_warn("Unexpected low mmio base %pa\n", 
> &low_mmio_base);
> +                     } else {
> +                             /*
> +                              * If the kdump kernel's lfb_base is 0,

Nit:  The case of lfb_base is 0 applies to kexec and kdump kernels, and also to
CVMs.

> +                              * fall back to the low mmio base.
> +                              */
> +                             if (!start)
> +                                     start = low_mmio_base;
> +                             /*
> +                              * Reserve half of the space below 4GB for high
> +                              * resolutions, but cap the reservation to 
> 128MB.
> +                              */
> +                             size = min((SZ_4G - start) / 2, SZ_128M);
> +                     }
>               }
>       } else {
>               /* Gen1 VM: get FB base from PCI */
> @@ -2422,8 +2441,10 @@ static void __maybe_unused vmbus_reserve_fb(void)
>               pci_dev_put(pdev);
>       }
> 
> -     if (!start)
> +     if (!start) {
> +             pr_warn("Unexpected framebuffer mmio base of zero\n");
>               return;
> +     }
> 
>       /*
>        * Make a claim for the frame buffer in the resource tree under the
> @@ -2433,6 +2454,8 @@ static void __maybe_unused vmbus_reserve_fb(void)
>        */
>       for (; !fb_mmio && (size >= 0x100000); size >>= 1)
>               fb_mmio = __request_region(hyperv_mmio, start, size, 
> fb_mmio_name, 0);
> +
> +     pr_info("hv_mmio=%pR,%pR fb=%pR\n", hyperv_mmio, hyperv_mmio->sibling, 
> fb_mmio);
>  }

Modulo my nit about the comment,

Reviewed-by: Michael Kelley <[email protected]>

Reply via email to