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

From: Leon Romanovsky

Date: Wed Aug 12 2026 - 15:48:42 EST


From: Leon Romanovsky <leonro@xxxxxxxxxx>

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 <leonro@xxxxxxxxxx>
---
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