xen/pci: Do not register devices with segments >= 0x10000
authorRoger Pau Monne <roger.pau@citrix.com>
Wed, 19 Feb 2025 09:20:55 +0000 (10:20 +0100)
committerJuergen Gross <jgross@suse.com>
Fri, 21 Mar 2025 07:15:26 +0000 (08:15 +0100)
The current hypercall interface for doing PCI device operations always uses
a segment field that has a 16 bit width.  However on Linux there are buses
like VMD that hook up devices into the PCI hierarchy at segment >= 0x10000,
after the maximum possible segment enumerated in ACPI.

Attempting to register or manage those devices with Xen would result in
errors at best, or overlaps with existing devices living on the truncated
equivalent segment values.  Note also that the VMD segment numbers are
arbitrarily assigned by the OS, and hence there would need to be some
negotiation between Xen and the OS to agree on how to enumerate VMD
segments and devices behind them.

Skip notifying Xen about those devices.  Given how VMD bridges can
multiplex interrupts on behalf of devices behind them there's no need for
Xen to be aware of such devices for them to be usable by Linux.

Signed-off-by: Roger Pau Monné <roger.pau@citrix.com>
Acked-by: Juergen Gross <jgross@suse.com>
Message-ID: <20250219092059.90850-2-roger.pau@citrix.com>
Signed-off-by: Juergen Gross <jgross@suse.com>
drivers/xen/pci.c

index 416f231809cb6906cc961c2408dfe0430c4c1a09..bfe07adb3e3a6cb5a9f676b2959ae91f33860289 100644 (file)
@@ -43,6 +43,18 @@ static int xen_add_device(struct device *dev)
                pci_mcfg_reserved = true;
        }
 #endif
+
+       if (pci_domain_nr(pci_dev->bus) >> 16) {
+               /*
+                * The hypercall interface is limited to 16bit PCI segment
+                * values, do not attempt to register devices with Xen in
+                * segments greater or equal than 0x10000.
+                */
+               dev_info(dev,
+                        "not registering with Xen: invalid PCI segment\n");
+               return 0;
+       }
+
        if (pci_seg_supported) {
                DEFINE_RAW_FLEX(struct physdev_pci_device_add, add, optarr, 1);
 
@@ -149,6 +161,16 @@ static int xen_remove_device(struct device *dev)
        int r;
        struct pci_dev *pci_dev = to_pci_dev(dev);
 
+       if (pci_domain_nr(pci_dev->bus) >> 16) {
+               /*
+                * The hypercall interface is limited to 16bit PCI segment
+                * values.
+                */
+               dev_info(dev,
+                        "not unregistering with Xen: invalid PCI segment\n");
+               return 0;
+       }
+
        if (pci_seg_supported) {
                struct physdev_pci_device device = {
                        .seg = pci_domain_nr(pci_dev->bus),
@@ -182,6 +204,16 @@ int xen_reset_device(const struct pci_dev *dev)
                .flags = PCI_DEVICE_RESET_FLR,
        };
 
+       if (pci_domain_nr(dev->bus) >> 16) {
+               /*
+                * The hypercall interface is limited to 16bit PCI segment
+                * values.
+                */
+               dev_info(&dev->dev,
+                        "unable to notify Xen of device reset: invalid PCI segment\n");
+               return 0;
+       }
+
        return HYPERVISOR_physdev_op(PHYSDEVOP_pci_device_reset, &device);
 }
 EXPORT_SYMBOL_GPL(xen_reset_device);