[PATCH RFC 3/7] PCI/P2PDMA: Authorize HMAT-described cross-host-bridge P2P

Leon Romanovsky <[email protected]> Wed, 12 Aug 2026 22:47:40 +0300
Newsgroups gmane.linux.acpi.devel,gmane.linux.kernel,gmane.linux.kernel.pci
Message-ID <[email protected]>
From: Leon Romanovsky <[email protected]>

PCI P2PDMA rejects cross-host-bridge transactions unless the platform
matches hard-coded CPU or host-bridge knowledge. That excludes capable
systems, requires recurring kernel whitelist updates, and cannot represent
firmware-qualified directional paths.

Accept a reachable ordered HMAT path from requester to completer as
platform authorization. Missing entries, unreachable entries, and UIO-only
entries do not authorize ordinary DMA. The existing safety boundary remains
in force when firmware does not describe a usable ordered path.

Signed-off-by: Leon Romanovsky <[email protected]>
---
 drivers/pci/p2pdma.c | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 54 insertions(+), 1 deletion(-)

diff --git a/drivers/pci/p2pdma.c b/drivers/pci/p2pdma.c
index 34929bc6efb6..dd300d2e9887 100644
--- a/drivers/pci/p2pdma.c
+++ b/drivers/pci/p2pdma.c
@@ -9,6 +9,7 @@
  */
 
 #define pr_fmt(fmt) "pci-p2pdma: " fmt
+#include <linux/acpi.h>
 #include <linux/ctype.h>
 #include <linux/dma-map-ops.h>
 #include <linux/pci-p2pdma.h>
@@ -733,6 +734,57 @@ static bool host_bridge_whitelist(struct pci_dev *a, struct pci_dev *b,
 	return false;
 }
 
+#ifdef CONFIG_ACPI
+static int pci_host_bridge_pxm(struct pci_dev *pdev)
+{
+	struct pci_host_bridge *host = pci_find_host_bridge(pdev->bus);
+	struct acpi_device *adev;
+	const char *uid_str;
+	u32 uid;
+
+	adev = to_acpi_device_node(host->dev.fwnode);
+	if (!adev)
+		return -ENODEV;
+
+	uid_str = acpi_device_uid(adev);
+	if (!uid_str || kstrtou32(uid_str, 0, &uid))
+		return -ENODEV;
+
+	return acpi_get_genport_proximity_domain(uid);
+}
+#else
+static int pci_host_bridge_pxm(struct pci_dev *pdev)
+{
+	return -ENODEV;
+}
+#endif
+
+/*
+ * Check whether platform firmware describes, via an HMAT PCIe P2P Latency and
+ * Bandwidth Information Structure, a reachable ordered (non-UIO) P2P path from
+ * the client's host bridge to the provider's host bridge. When it does, the
+ * platform vouches for cross-host-bridge P2P even if the root complex is not
+ * in the static whitelist.
+ *
+ * The client is the requester of the P2P transactions and the provider is the
+ * completer, so the client's host bridge is the HMAT initiator and the
+ * provider's host bridge is the HMAT target. UIO-only paths are described by
+ * firmware but are not (yet) used to authorize ordered DMA.
+ */
+static bool host_bridge_hmat_p2p(struct pci_dev *provider, struct pci_dev *client)
+{
+	struct access_coordinate coord;
+	int init_pxm, targ_pxm;
+
+	init_pxm = pci_host_bridge_pxm(client);
+	targ_pxm = pci_host_bridge_pxm(provider);
+	if (init_pxm < 0 || targ_pxm < 0)
+		return false;
+
+	return acpi_get_p2p_coordinates(init_pxm, targ_pxm, HMAT_P2P_NON_UIO,
+					&coord) == 0;
+}
+
 static unsigned long map_types_idx(struct pci_dev *client)
 {
 	return (pci_domain_nr(client->bus) << 16) | pci_dev_id(client);
@@ -890,9 +942,10 @@ calc_map_type_and_dist(struct pci_dev *provider, struct pci_dev *client,
 
 map_through_host_bridge:
 	if (!cpu_supports_p2pdma() &&
+	    !host_bridge_hmat_p2p(provider, client) &&
 	    !host_bridge_whitelist(provider, client, verbose)) {
 		if (verbose)
-			pci_warn(client, "cannot be used for peer-to-peer DMA as the client and provider (%s) do not share an upstream bridge or whitelisted host bridge\n",
+			pci_warn(client, "cannot be used for peer-to-peer DMA as the client and provider (%s) do not share an upstream bridge, whitelisted host bridge, or HMAT-described P2P path\n",
 				 pci_name(provider));
 		map_type = PCI_P2PDMA_MAP_NOT_SUPPORTED;
 	}

-- 
2.55.0