PCI: s390: Fix use-after-free of PCI resources with per-function hotplug
[platform/kernel/linux-starfive.git] / drivers / pci / pci-acpi.c
index a46fec7..1698205 100644 (file)
@@ -976,24 +976,41 @@ bool acpi_pci_power_manageable(struct pci_dev *dev)
 bool acpi_pci_bridge_d3(struct pci_dev *dev)
 {
        struct pci_dev *rpdev;
-       struct acpi_device *adev;
-       acpi_status status;
-       unsigned long long state;
+       struct acpi_device *adev, *rpadev;
        const union acpi_object *obj;
 
        if (acpi_pci_disabled || !dev->is_hotplug_bridge)
                return false;
 
-       /* Assume D3 support if the bridge is power-manageable by ACPI. */
-       if (acpi_pci_power_manageable(dev))
-               return true;
+       adev = ACPI_COMPANION(&dev->dev);
+       if (adev) {
+               /*
+                * If the bridge has _S0W, whether or not it can go into D3
+                * depends on what is returned by that object.  In particular,
+                * if the power state returned by _S0W is D2 or shallower,
+                * entering D3 should not be allowed.
+                */
+               if (acpi_dev_power_state_for_wake(adev) <= ACPI_STATE_D2)
+                       return false;
+
+               /*
+                * Otherwise, assume that the bridge can enter D3 so long as it
+                * is power-manageable via ACPI.
+                */
+               if (acpi_device_power_manageable(adev))
+                       return true;
+       }
 
        rpdev = pcie_find_root_port(dev);
        if (!rpdev)
                return false;
 
-       adev = ACPI_COMPANION(&rpdev->dev);
-       if (!adev)
+       if (rpdev == dev)
+               rpadev = adev;
+       else
+               rpadev = ACPI_COMPANION(&rpdev->dev);
+
+       if (!rpadev)
                return false;
 
        /*
@@ -1001,15 +1018,15 @@ bool acpi_pci_bridge_d3(struct pci_dev *dev)
         * doesn't supply a wakeup GPE via _PRW, it cannot signal hotplug
         * events from low-power states including D3hot and D3cold.
         */
-       if (!adev->wakeup.flags.valid)
+       if (!rpadev->wakeup.flags.valid)
                return false;
 
        /*
-        * If the Root Port cannot wake itself from D3hot or D3cold, we
-        * can't use D3.
+        * In the bridge-below-a-Root-Port case, evaluate _S0W for the Root Port
+        * to verify whether or not it can signal wakeup from D3.
         */
-       status = acpi_evaluate_integer(adev->handle, "_S0W", NULL, &state);
-       if (ACPI_SUCCESS(status) && state < ACPI_STATE_D3_HOT)
+       if (rpadev != adev &&
+           acpi_dev_power_state_for_wake(rpadev) <= ACPI_STATE_D2)
                return false;
 
        /*
@@ -1018,7 +1035,7 @@ bool acpi_pci_bridge_d3(struct pci_dev *dev)
         * bridges *below* that Root Port can also signal hotplug events
         * while in D3.
         */
-       if (!acpi_dev_get_property(adev, "HotPlugSupportInD3",
+       if (!acpi_dev_get_property(rpadev, "HotPlugSupportInD3",
                                   ACPI_TYPE_INTEGER, &obj) &&
            obj->integer.value == 1)
                return true;