Re: [PATCH v4] PCI: Relabel JHL6540 on Lenovo X1 Carbon 7,8

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Wed, Jun 26, 2024 at 4:05 AM Mika Westerberg
<mika.westerberg@xxxxxxxxxxxxxxx> wrote:
> I will be on vacation starting next week for the whole July. The patch
> is kind of "pseudo-code" in that sense that it probably needs some
> additional work, cleanup, maybe drop the serial number checks and so on.
> You are free to use it as you see fit, or submit upstream as proper
> patch if nobody objects.

I cleaned it up, but I think I'd like to run it by you before
submitting it, as you are the author and also some of my cleanups
ended up being a bit more involved than I anticipated.

For cleanup, I did the following:

1) I ended up moving the changes from pcie_set_pcie_untrusted to
pci_set_removable for multiple reasons:

- The downstream bug I ran into happened because of the "removable" attribute.

- There seems to be a reason why both removable and untrusted exist
despite both having the same logic. pci_fixup_early is run after
pcie_set_pcie_untrusted, but before pci_set_removable. It seems like
this was done on purpose so that downstream security policies can use
quirks to set specific internal, fixed devices as untrusted.

- The way you wrote it makes the attributes removable = untrusted,
which wasn't the case before, and undos the pci_fixup_early quirks
logic.

- If you want to make sure that these non-tunneled discrete
thunderbolt chips are labeled as trusted, we may have to duplicate
this logic in both functions (which seems to be already the case
anyways in their current state).
I just don't fully know what the "untrusted" attribute entails, so I
am erring on the more conservative side of only making changes I fully
understand.

2) I changed this comment into code:

> +/* root->external_facing is true, parent != NULL */

3) I edited legacy comments to reflect what the code does now. I also
changed your comments to reflect how I changed the code, but for the
most part I kept your words in as they were really clear.

4) I removed the serial checks as you suggested

> If nothing has happened when I come back, I can pick up the work if I
> still remember this ;-)

I did my best to clean up! I'm unsure if you will want me to duplicate
this logic to pcie_set_pcie_untrusted, so just let me know if I should
fix that, and I'll send it to the kernel! (I'm assuming with the
Co-developed-by, and the Signed-off-by lines, to properly attribute
you?)

I hope you had a nice vacation! Both you and Lukas Wurner have been so
helpful and attentive.

The cleaned up patch is below:


diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index 43159965e09e9..fc3ef2cf66d58 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -1613,24 +1613,161 @@ static void set_pcie_untrusted(struct pci_dev *dev)
                dev->untrusted = true;
 }

+/*
+ * Checks if the PCIe switch that contains pdev is directly under
+ * the specified bridge.
+ */
+static bool pcie_switch_directly_under(struct pci_dev *bridge,
+                                      struct pci_dev *parent,
+                                      struct pci_dev *pdev)
+{
+       /*
+        * If the device has a PCIe type, that means it is part of a PCIe
+        * switch.
+        */
+       switch (pci_pcie_type(pdev)) {
+       case PCI_EXP_TYPE_UPSTREAM:
+               if (parent == bridge)
+                       return true;
+               break;
+
+       case PCI_EXP_TYPE_DOWNSTREAM:
+               if (pci_pcie_type(parent) == PCI_EXP_TYPE_UPSTREAM) {
+                       parent = pci_upstream_bridge(parent);
+                       if (parent == bridge)
+                               return true;
+               }
+               break;
+
+       case PCI_EXP_TYPE_ENDPOINT:
+               if (pci_pcie_type(parent) == PCI_EXP_TYPE_DOWNSTREAM) {
+                       parent = pci_upstream_bridge(parent);
+                       if (parent && pci_pcie_type(parent) ==
PCI_EXP_TYPE_UPSTREAM) {
+                               parent = pci_upstream_bridge(parent);
+                               if (parent == bridge)
+                                       return true;
+                       }
+               }
+               break;
+       }
+
+       return false;
+}
+
+static bool pcie_has_usb4_host_interface(struct pci_dev *pdev)
+{
+       struct fwnode_handle *fwnode;
+
+       /*
+        * For USB4 the tunneled PCIe root or downstream ports are marked with
+        * the "usb4-host-interface" property so we look for that first. This
+        * should cover the most cases.
+        */
+       fwnode = fwnode_find_reference(dev_fwnode(&pdev->dev),
+                                      "usb4-host-interface", 0);
+       if (!IS_ERR(fwnode)) {
+               fwnode_handle_put(fwnode);
+               return true;
+       }
+
+       /*
+        * Any integrated Thunderbolt 3/4 PCIe root ports from Intel
+        * before Alder Lake do not have the above device property so we
+        * use their PCI IDs instead. All these are tunneled. This list
+        * is not expected to grow.
+        */
+       if (pdev->vendor == PCI_VENDOR_ID_INTEL) {
+               switch (pdev->device) {
+               /* Ice Lake Thunderbolt 3 PCIe Root Ports */
+               case 0x8a1d:
+               case 0x8a1f:
+               case 0x8a21:
+               case 0x8a23:
+               /* Tiger Lake-LP Thunderbolt 4 PCIe Root Ports */
+               case 0x9a23:
+               case 0x9a25:
+               case 0x9a27:
+               case 0x9a29:
+               /* Tiger Lake-H Thunderbolt 4 PCIe Root Ports */
+               case 0x9a2b:
+               case 0x9a2d:
+               case 0x9a2f:
+               case 0x9a31:
+                       return true;
+               }
+       }
+
+       return false;
+}
+
+static bool pcie_is_tunneled(struct pci_dev *root, struct pci_dev *parent,
+                            struct pci_dev *pdev)
+{
+       /* Return least trusted outcome if params are invalid */
+       if (!(root && root->external_facing && parent))
+               return true;
+
+       /* Anything directly behind a "usb4-host-interface" is tunneled */
+       if (pcie_has_usb4_host_interface(parent))
+               return true;
+
+       /*
+        * Check if this is a discrete Thunderbolt/USB4 controller that is
+        * directly behind a PCIe Root Port marked as "ExternalFacingPort".
+        * These are not behind a PCIe tunnel.
+        */
+       if (pcie_switch_directly_under(root, parent, pdev))
+               return false;
+
+       return true;
+}
+
 static void pci_set_removable(struct pci_dev *dev)
 {
-       struct pci_dev *parent = pci_upstream_bridge(dev);
+       struct pci_dev *parent, *root;
+
+       parent = pci_upstream_bridge(dev);

        /*
-        * We (only) consider everything downstream from an external_facing
-        * device to be removable by the user. We're mainly concerned with
-        * consumer platforms with user accessible thunderbolt ports that are
-        * vulnerable to DMA attacks, and we expect those ports to be marked by
-        * the firmware as external_facing. Devices in traditional hotplug
-        * slots can technically be removed, but the expectation is that unless
-        * the port is marked with external_facing, such devices are less
-        * accessible to user / may not be removed by end user, and thus not
-        * exposed as "removable" to userspace.
+        * We're mainly concerned with consumer platforms with user accessible
+        * thunderbolt ports that are vulnerable to DMA attacks.
+        * We expect those ports to be marked by the firmware as
external_facing.
+        * Devices outside external_facing ports are labeled as removable, with
+        * the exception of discrete thunderbolt chips within the chassis.
+        *
+        * Devices in traditional hotplug slots can technically be removed,
+        * but the expectation is that unless the port is marked with
+        * external_facing, such devices are less accessible to user / may not
+        * be removed by end user, and thus not exposed as "removable" to
+        * userspace.
         */
-       if (parent &&
-           (parent->external_facing || dev_is_removable(&parent->dev)))
+       if (!parent)
+               return;
+
+       if (dev_is_removable(&parent->dev))
                dev_set_removable(&dev->dev, DEVICE_REMOVABLE);
+
+       root = pcie_find_root_port(dev);
+
+       if (root && root->external_facing) {
+               /*
+                * All devices behind a PCIe root port labeled as
+                * "ExternalFacingPort" are tunneled by definition,
+                * with the exception of discrete Thunderbolt/USB4
+                * controllers that add Thunderbolt capabilities
+                * to CPUs that lack integrated Thunderbolt.
+                * They are identified because by definition, they
+                * aren't tunneled.
+                *
+                * Those discrete Thunderbolt/USB4 controllers are
+                * not removable. Only their downstream facing ports
+                * are actually something that are exposed to the
+                * wild so we only mark devices tunneled behind those
+                * as removable.
+                */
+               if (pcie_is_tunneled(root, parent, dev))
+                       dev_set_removable(&dev->dev, DEVICE_REMOVABLE);
+       }
 }

 /**





[Index of Archives]     [DMA Engine]     [Linux Coverity]     [Linux USB]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Greybus]

  Powered by Linux