[PATCH v4 07/10] PCI/IDE: Add IDE establishment helpers

Dan Williams posted 10 patches 2 months, 3 weeks ago
[PATCH v4 07/10] PCI/IDE: Add IDE establishment helpers
Posted by Dan Williams 2 months, 3 weeks ago
There are two components to establishing an encrypted link, provisioning
the stream in Partner Port config-space, and programming the keys into
the link layer via IDE_KM (IDE Key Management). This new library,
drivers/pci/ide.c, enables the former. IDE_KM, via a TSM low-level
driver, is saved for later.

With the platform TSM implementations of SEV-TIO and TDX Connect in mind
this library abstracts small differences in those implementations. For
example, TDX Connect handles Root Port register setup while SEV-TIO
expects System Software to update the Root Port registers. This is the
rationale for fine-grained 'setup' + 'enable' verbs.

The other design detail for TSM-coordinated IDE establishment is that
the TSM may manage allocation of Stream IDs, this is why the Stream ID
value is passed in to pci_ide_stream_setup().

The flow is:

pci_ide_stream_alloc()
  Allocate a Selective IDE Stream Register Block in each Partner Port
  (Endpoint + Root Port), and reserve a host bridge / platform stream
  slot. Gather Partner Port specific stream settings like Requester ID.
pci_ide_stream_register()
  Publish the stream in sysfs after allocating a Stream ID. In the TSM
  case the TSM allocates the Stream ID for the Partner Port pair.
pci_ide_stream_setup()
  Program the stream settings to a Partner Port. Caller is responsible
  for optionally calling this for the Root Port as well if the TSM
  implementation requires it.
pci_ide_stream_enable()
  Try to run the stream after IDE_KM.

In support of system administrators auditing where platform, Root Port,
and Endpoint IDE stream resources are being spent, the allocated stream
is reflected as a symlink from the host bridge to the endpoint with the
name:

    stream%d.%d.%d

Where the tuple of integers reflects the allocated platform, Root Port,
and Endpoint stream index (Selective IDE Stream Register Block) values.

Thanks to Wu Hao for a draft implementation of this infrastructure.

Cc: Bjorn Helgaas <bhelgaas@google.com>
Cc: Lukas Wunner <lukas@wunner.de>
Cc: Samuel Ortiz <sameo@rivosinc.com>
Co-developed-by: Alexey Kardashevskiy <aik@amd.com>
Signed-off-by: Alexey Kardashevskiy <aik@amd.com>
Co-developed-by: Yilun Xu <yilun.xu@linux.intel.com>
Signed-off-by: Yilun Xu <yilun.xu@linux.intel.com>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
---
 .../ABI/testing/sysfs-devices-pci-host-bridge |  16 +
 drivers/pci/ide.c                             | 422 ++++++++++++++++++
 include/linux/pci-ide.h                       |  70 +++
 include/linux/pci.h                           |   6 +
 4 files changed, 514 insertions(+)
 create mode 100644 include/linux/pci-ide.h

diff --git a/Documentation/ABI/testing/sysfs-devices-pci-host-bridge b/Documentation/ABI/testing/sysfs-devices-pci-host-bridge
index 8c3a652799f1..c67d7c30efa0 100644
--- a/Documentation/ABI/testing/sysfs-devices-pci-host-bridge
+++ b/Documentation/ABI/testing/sysfs-devices-pci-host-bridge
@@ -17,3 +17,19 @@ Description:
 		PNP0A08 (/sys/devices/LNXSYSTM:00/LNXSYBUS:00/PNP0A08:00). See
 		/sys/devices/pciDDDD:BB entry for details about the DDDD:BB
 		format.
+
+What:		pciDDDD:BB/streamH.R.E
+Contact:	linux-pci@vger.kernel.org
+Description:
+		(RO) When a platform has established a secure connection, PCIe
+		IDE, between two Partner Ports, this symlink appears. The
+		primary function is to account the stream slot / resources
+		consumed in each of the (H)ost bridge, (R)oot Port and
+		(E)ndpoint that will be freed when invoking the tsm/disconnect
+		flow. The link points to the endpoint PCI device in the
+		Selective IDE Stream. "R" and "E" represent the assigned
+		Selective IDE Stream Register Block in the Root Port and
+		Endpoint, and "H" represents a platform specific pool of stream
+		resources shared by the Root Ports in a host bridge. See
+		/sys/devices/pciDDDD:BB entry for details about the DDDD:BB
+		format.
diff --git a/drivers/pci/ide.c b/drivers/pci/ide.c
index e15937cdb2a4..cdc773a8b381 100644
--- a/drivers/pci/ide.c
+++ b/drivers/pci/ide.c
@@ -5,6 +5,8 @@
 
 #define dev_fmt(fmt) "PCI/IDE: " fmt
 #include <linux/pci.h>
+#include <linux/sysfs.h>
+#include <linux/pci-ide.h>
 #include <linux/bitfield.h>
 #include "pci.h"
 
@@ -24,6 +26,13 @@ static int __sel_ide_offset(u16 ide_cap, u8 nr_link_ide, u8 stream_index,
 	return offset;
 }
 
+static int sel_ide_offset(struct pci_dev *pdev,
+			  struct pci_ide_partner *settings)
+{
+	return __sel_ide_offset(pdev->ide_cap, pdev->nr_link_ide,
+				settings->stream_index, pdev->nr_ide_mem);
+}
+
 void pci_ide_init(struct pci_dev *pdev)
 {
 	u8 nr_link_ide, nr_ide_mem, nr_streams;
@@ -89,5 +98,418 @@ void pci_ide_init(struct pci_dev *pdev)
 
 	pdev->ide_cap = ide_cap;
 	pdev->nr_link_ide = nr_link_ide;
+	pdev->nr_sel_ide = nr_streams;
 	pdev->nr_ide_mem = nr_ide_mem;
 }
+
+struct stream_index {
+	unsigned long *map;
+	u8 max, stream_index;
+};
+
+static void free_stream_index(struct stream_index *stream)
+{
+	clear_bit_unlock(stream->stream_index, stream->map);
+}
+
+DEFINE_FREE(free_stream, struct stream_index *, if (_T) free_stream_index(_T))
+static struct stream_index *alloc_stream_index(unsigned long *map, u8 max,
+					       struct stream_index *stream)
+{
+	if (!max)
+		return NULL;
+
+	do {
+		u8 stream_index = find_first_zero_bit(map, max);
+
+		if (stream_index == max)
+			return NULL;
+		if (!test_and_set_bit_lock(stream_index, map)) {
+			*stream = (struct stream_index) {
+				.map = map,
+				.max = max,
+				.stream_index = stream_index,
+			};
+			return stream;
+		}
+		/* collided with another stream acquisition */
+	} while (1);
+}
+
+/**
+ * pci_ide_stream_alloc() - Reserve stream indices and probe for settings
+ * @pdev: IDE capable PCIe Endpoint Physical Function
+ *
+ * Retrieve the Requester ID range of @pdev for programming its Root
+ * Port IDE RID Association registers, and conversely retrieve the
+ * Requester ID of the Root Port for programming @pdev's IDE RID
+ * Association registers.
+ *
+ * Allocate a Selective IDE Stream Register Block instance per port.
+ *
+ * Allocate a platform stream resource from the associated host bridge.
+ * Retrieve stream association parameters for Requester ID range and
+ * address range restrictions for the stream.
+ */
+struct pci_ide *pci_ide_stream_alloc(struct pci_dev *pdev)
+{
+	/* EP, RP, + HB Stream allocation */
+	struct stream_index __stream[PCI_IDE_HB + 1];
+	struct pci_host_bridge *hb;
+	struct pci_dev *rp;
+	int num_vf, rid_end;
+
+	if (!pci_is_pcie(pdev))
+		return NULL;
+
+	if (pci_pcie_type(pdev) != PCI_EXP_TYPE_ENDPOINT)
+		return NULL;
+
+	if (!pdev->ide_cap)
+		return NULL;
+
+	/*
+	 * Catch buggy PCI platform initialization (missing
+	 * pci_ide_init_nr_streams())
+	 */
+	hb = pci_find_host_bridge(pdev->bus);
+	if (WARN_ON_ONCE(!hb->nr_ide_streams))
+		return NULL;
+
+	struct pci_ide *ide __free(kfree) = kzalloc(sizeof(*ide), GFP_KERNEL);
+	if (!ide)
+		return NULL;
+
+	struct stream_index *hb_stream __free(free_stream) = alloc_stream_index(
+		hb->ide_stream_map, hb->nr_ide_streams, &__stream[PCI_IDE_HB]);
+	if (!hb_stream)
+		return NULL;
+
+	rp = pcie_find_root_port(pdev);
+	struct stream_index *rp_stream __free(free_stream) = alloc_stream_index(
+		rp->ide_stream_map, rp->nr_sel_ide, &__stream[PCI_IDE_RP]);
+	if (!rp_stream)
+		return NULL;
+
+	struct stream_index *ep_stream __free(free_stream) = alloc_stream_index(
+		pdev->ide_stream_map, pdev->nr_sel_ide, &__stream[PCI_IDE_EP]);
+	if (!ep_stream)
+		return NULL;
+
+	/* for SR-IOV case, cover all VFs */
+	num_vf = pci_num_vf(pdev);
+	if (num_vf)
+		rid_end = PCI_DEVID(pci_iov_virtfn_bus(pdev, num_vf),
+				    pci_iov_virtfn_devfn(pdev, num_vf));
+	else
+		rid_end = pci_dev_id(pdev);
+
+	*ide = (struct pci_ide) {
+		.pdev = pdev,
+		.partner = {
+			[PCI_IDE_EP] = {
+				.rid_start = pci_dev_id(rp),
+				.rid_end = pci_dev_id(rp),
+				.stream_index = no_free_ptr(ep_stream)->stream_index,
+			},
+			[PCI_IDE_RP] = {
+				.rid_start = pci_dev_id(pdev),
+				.rid_end = rid_end,
+				.stream_index = no_free_ptr(rp_stream)->stream_index,
+			},
+		},
+		.host_bridge_stream = no_free_ptr(hb_stream)->stream_index,
+		.stream_id = -1,
+	};
+
+	return_ptr(ide);
+}
+EXPORT_SYMBOL_GPL(pci_ide_stream_alloc);
+
+/**
+ * pci_ide_stream_free() - unwind pci_ide_stream_alloc()
+ * @ide: idle IDE settings descriptor
+ *
+ * Free all of the stream index (register block) allocations acquired by
+ * pci_ide_stream_alloc(). The stream represented by @ide is assumed to
+ * be unregistered and not instantiated in any device.
+ */
+void pci_ide_stream_free(struct pci_ide *ide)
+{
+	struct pci_dev *pdev = ide->pdev;
+	struct pci_dev *rp = pcie_find_root_port(pdev);
+	struct pci_host_bridge *hb = pci_find_host_bridge(pdev->bus);
+
+	clear_bit_unlock(ide->partner[PCI_IDE_EP].stream_index,
+			 pdev->ide_stream_map);
+	clear_bit_unlock(ide->partner[PCI_IDE_RP].stream_index,
+			 rp->ide_stream_map);
+	clear_bit_unlock(ide->host_bridge_stream, hb->ide_stream_map);
+	kfree(ide);
+}
+EXPORT_SYMBOL_GPL(pci_ide_stream_free);
+
+/**
+ * pci_ide_stream_release() - unwind and release an @ide context
+ * @ide: partially or fully registered IDE settings descriptor
+ *
+ * In support of automatic cleanup of IDE setup routines perform IDE
+ * teardown in expected reverse order of setup and with respect to which
+ * aspects of IDE setup have successfully completed.
+ *
+ * Be careful that setup order mirrors this shutdown order. Otherwise,
+ * open code releasing the IDE context.
+ */
+void pci_ide_stream_release(struct pci_ide *ide)
+{
+	struct pci_dev *pdev = ide->pdev;
+	struct pci_dev *rp = pcie_find_root_port(pdev);
+
+	if (ide->partner[PCI_IDE_RP].enable)
+		pci_ide_stream_disable(rp, ide);
+
+	if (ide->partner[PCI_IDE_EP].enable)
+		pci_ide_stream_disable(pdev, ide);
+
+	if (ide->partner[PCI_IDE_RP].setup)
+		pci_ide_stream_teardown(rp, ide);
+
+	if (ide->partner[PCI_IDE_EP].setup)
+		pci_ide_stream_teardown(pdev, ide);
+
+	if (ide->name)
+		pci_ide_stream_unregister(ide);
+
+	pci_ide_stream_free(ide);
+}
+EXPORT_SYMBOL_GPL(pci_ide_stream_release);
+
+/**
+ * pci_ide_stream_register() - Prepare to activate an IDE Stream
+ * @ide: IDE settings descriptor
+ *
+ * After a Stream ID has been acquired for @ide, record the presence of
+ * the stream in sysfs. The expectation is that @ide is immutable while
+ * registered.
+ */
+int pci_ide_stream_register(struct pci_ide *ide)
+{
+	struct pci_dev *pdev = ide->pdev;
+	struct pci_host_bridge *hb = pci_find_host_bridge(pdev->bus);
+	u8 ep_stream, rp_stream;
+	int rc;
+
+	if (ide->stream_id < 0 || ide->stream_id > U8_MAX) {
+		pci_err(pdev, "Setup fail: Invalid Stream ID: %d\n", ide->stream_id);
+		return -ENXIO;
+	}
+
+	ep_stream = ide->partner[PCI_IDE_EP].stream_index;
+	rp_stream = ide->partner[PCI_IDE_RP].stream_index;
+	const char *name __free(kfree) = kasprintf(GFP_KERNEL, "stream%d.%d.%d",
+						   ide->host_bridge_stream,
+						   rp_stream, ep_stream);
+	if (!name)
+		return -ENOMEM;
+
+	rc = sysfs_create_link(&hb->dev.kobj, &pdev->dev.kobj, name);
+	if (rc)
+		return rc;
+
+	ide->name = no_free_ptr(name);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(pci_ide_stream_register);
+
+/**
+ * pci_ide_stream_unregister() - unwind pci_ide_stream_register()
+ * @ide: idle IDE settings descriptor
+ *
+ * In preparation for freeing @ide, remove sysfs enumeration for the
+ * stream.
+ */
+void pci_ide_stream_unregister(struct pci_ide *ide)
+{
+	struct pci_dev *pdev = ide->pdev;
+	struct pci_host_bridge *hb = pci_find_host_bridge(pdev->bus);
+
+	sysfs_remove_link(&hb->dev.kobj, ide->name);
+	kfree(ide->name);
+	ide->name = NULL;
+}
+EXPORT_SYMBOL_GPL(pci_ide_stream_unregister);
+
+int pci_ide_domain(struct pci_dev *pdev)
+{
+	if (pdev->fm_enabled)
+		return pci_domain_nr(pdev->bus);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(pci_ide_domain);
+
+struct pci_ide_partner *pci_ide_to_settings(struct pci_dev *pdev, struct pci_ide *ide)
+{
+	if (!pci_is_pcie(pdev)) {
+		pci_warn_once(pdev, "not a PCIe device\n");
+		return NULL;
+	}
+
+	switch (pci_pcie_type(pdev)) {
+	case PCI_EXP_TYPE_ENDPOINT:
+		if (pdev != ide->pdev) {
+			pci_warn_once(pdev, "setup expected Endpoint: %s\n", pci_name(ide->pdev));
+			return NULL;
+		}
+		return &ide->partner[PCI_IDE_EP];
+	case PCI_EXP_TYPE_ROOT_PORT: {
+		struct pci_dev *rp = pcie_find_root_port(ide->pdev);
+
+		if (pdev != rp) {
+			pci_warn_once(pdev, "setup expected Root Port: %s\n",
+				      pci_name(rp));
+			return NULL;
+		}
+		return &ide->partner[PCI_IDE_RP];
+	}
+	default:
+		pci_warn_once(pdev, "invalid device type\n");
+		return NULL;
+	}
+}
+EXPORT_SYMBOL_GPL(pci_ide_to_settings);
+
+static void set_ide_sel_ctl(struct pci_dev *pdev, struct pci_ide *ide, int pos,
+			    bool enable)
+{
+	u32 val = FIELD_PREP(PCI_IDE_SEL_CTL_ID_MASK, ide->stream_id) |
+		  FIELD_PREP(PCI_IDE_SEL_CTL_DEFAULT, 1) |
+		  FIELD_PREP(PCI_IDE_SEL_CTL_CFG_EN, pdev->ide_cfg) |
+		  FIELD_PREP(PCI_IDE_SEL_CTL_TEE_LIMITED, pdev->ide_tee_limit) |
+		  FIELD_PREP(PCI_IDE_SEL_CTL_EN, enable);
+
+	pci_write_config_dword(pdev, pos + PCI_IDE_SEL_CTL, val);
+}
+
+/**
+ * pci_ide_stream_setup() - program settings to Selective IDE Stream registers
+ * @pdev: PCIe device object for either a Root Port or Endpoint Partner Port
+ * @ide: registered IDE settings descriptor
+ *
+ * When @pdev is a PCI_EXP_TYPE_ENDPOINT then the PCI_IDE_EP partner
+ * settings are written to @pdev's Selective IDE Stream register block,
+ * and when @pdev is a PCI_EXP_TYPE_ROOT_PORT, the PCI_IDE_RP settings
+ * are selected.
+ */
+void pci_ide_stream_setup(struct pci_dev *pdev, struct pci_ide *ide)
+{
+	struct pci_ide_partner *settings = pci_ide_to_settings(pdev, ide);
+	int pos;
+	u32 val;
+
+	if (!settings)
+		return;
+
+	pos = sel_ide_offset(pdev, settings);
+
+	val = FIELD_PREP(PCI_IDE_SEL_RID_1_LIMIT_MASK, settings->rid_end);
+	pci_write_config_dword(pdev, pos + PCI_IDE_SEL_RID_1, val);
+
+	val = FIELD_PREP(PCI_IDE_SEL_RID_2_VALID, 1) |
+	      FIELD_PREP(PCI_IDE_SEL_RID_2_BASE_MASK, settings->rid_start) |
+	      FIELD_PREP(PCI_IDE_SEL_RID_2_SEG_MASK, pci_ide_domain(pdev));
+
+	pci_write_config_dword(pdev, pos + PCI_IDE_SEL_RID_2, val);
+
+	/*
+	 * Setup control register early for devices that expect
+	 * stream_id is set during key programming.
+	 */
+	set_ide_sel_ctl(pdev, ide, pos, false);
+	settings->setup = 1;
+}
+EXPORT_SYMBOL_GPL(pci_ide_stream_setup);
+
+/**
+ * pci_ide_stream_teardown() - disable the stream and clear all settings
+ * @pdev: PCIe device object for either a Root Port or Endpoint Partner Port
+ * @ide: registered IDE settings descriptor
+ *
+ * For stream destruction, zero all registers that may have been written
+ * by pci_ide_stream_setup(). Consider pci_ide_stream_disable() to leave
+ * settings in place while temporarily disabling the stream.
+ */
+void pci_ide_stream_teardown(struct pci_dev *pdev, struct pci_ide *ide)
+{
+	struct pci_ide_partner *settings = pci_ide_to_settings(pdev, ide);
+	int pos;
+
+	if (!settings)
+		return;
+
+	pos = sel_ide_offset(pdev, settings);
+
+	pci_write_config_dword(pdev, pos + PCI_IDE_SEL_CTL, 0);
+	pci_write_config_dword(pdev, pos + PCI_IDE_SEL_RID_2, 0);
+	pci_write_config_dword(pdev, pos + PCI_IDE_SEL_RID_1, 0);
+	settings->setup = 0;
+}
+EXPORT_SYMBOL_GPL(pci_ide_stream_teardown);
+
+/**
+ * pci_ide_stream_enable() - try to enable a Selective IDE Stream
+ * @pdev: PCIe device object for either a Root Port or Endpoint Partner Port
+ * @ide: registered and setup IDE settings descriptor
+ *
+ * Activate the stream by writing to the Selective IDE Stream Control
+ * Register, report whether the state successfully transitioned to
+ * secure mode. Note that the state may go "insecure" at any point after
+ * this check, but that is handled via asynchronous error reporting.
+ */
+int pci_ide_stream_enable(struct pci_dev *pdev, struct pci_ide *ide)
+{
+	struct pci_ide_partner *settings = pci_ide_to_settings(pdev, ide);
+	int pos;
+	u32 val;
+
+	if (!settings)
+		return -ENXIO;
+
+	pos = sel_ide_offset(pdev, settings);
+
+	set_ide_sel_ctl(pdev, ide, pos, true);
+
+	pci_read_config_dword(pdev, pos + PCI_IDE_SEL_STS, &val);
+	if (FIELD_GET(PCI_IDE_SEL_STS_STATE_MASK, val) !=
+	    PCI_IDE_SEL_STS_STATE_SECURE) {
+		set_ide_sel_ctl(pdev, ide, pos, false);
+		return -ENXIO;
+	}
+
+	settings->enable = 1;
+	return 0;
+}
+EXPORT_SYMBOL_GPL(pci_ide_stream_enable);
+
+/**
+ * pci_ide_stream_disable() - disable a Selective IDE Stream
+ * @pdev: PCIe device object for either a Root Port or Endpoint Partner Port
+ * @ide: registered and setup IDE settings descriptor
+ *
+ * Clear the Selective IDE Stream Control Register, but leave all other
+ * registers untouched.
+ */
+void pci_ide_stream_disable(struct pci_dev *pdev, struct pci_ide *ide)
+{
+	struct pci_ide_partner *settings = pci_ide_to_settings(pdev, ide);
+	int pos;
+
+	if (!settings)
+		return;
+
+	pos = sel_ide_offset(pdev, settings);
+
+	pci_write_config_dword(pdev, pos + PCI_IDE_SEL_CTL, 0);
+	settings->enable = 0;
+}
+EXPORT_SYMBOL_GPL(pci_ide_stream_disable);
diff --git a/include/linux/pci-ide.h b/include/linux/pci-ide.h
new file mode 100644
index 000000000000..89c1ef0de841
--- /dev/null
+++ b/include/linux/pci-ide.h
@@ -0,0 +1,70 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright(c) 2024 Intel Corporation. All rights reserved. */
+
+/* PCIe 6.2 section 6.33 Integrity & Data Encryption (IDE) */
+
+#ifndef __PCI_IDE_H__
+#define __PCI_IDE_H__
+
+enum pci_ide_partner_select {
+	PCI_IDE_EP,
+	PCI_IDE_RP,
+	PCI_IDE_PARTNER_MAX,
+	/*
+	 * In addition to the resources in each partner port the
+	 * platform / host-bridge additionally has a Stream ID pool that
+	 * it shares across root ports. Let pci_ide_stream_alloc() use
+	 * the alloc_stream_index() helper as endpoints and root ports.
+	 */
+	PCI_IDE_HB = PCI_IDE_PARTNER_MAX,
+};
+
+/**
+ * struct pci_ide_partner - Per port pair Selective IDE Stream settings
+ * @rid_start: Partner Port Requester ID range start
+ * @rid_start: Partner Port Requester ID range end
+ * @stream_index: Selective IDE Stream Register Block selection
+ * @setup: flag to track whether to run pci_ide_stream_teardown for this parnter slot
+ * @enable: flag whether to run pci_ide_stream_disable for this parnter slot
+ */
+struct pci_ide_partner {
+	u16 rid_start;
+	u16 rid_end;
+	u8 stream_index;
+	unsigned int setup:1;
+	unsigned int enable:1;
+};
+
+/**
+ * struct pci_ide - PCIe Selective IDE Stream descriptor
+ * @pdev: PCIe Endpoint in the pci_ide_partner pair
+ * @partner: Per-partner settings
+ * @host_bridge_stream: track platform Stream ID
+ * @stream_id: unique Stream ID (within Partner Port pairing)
+ * @name: name of the established Selective IDE Stream in sysfs
+ *
+ * Negative @stream_id values indicate "uninitialized" on the
+ * expectation that with TSM established IDE the TSM owns the stream_id
+ * allocation.
+ */
+struct pci_ide {
+	struct pci_dev *pdev;
+	struct pci_ide_partner partner[PCI_IDE_PARTNER_MAX];
+	u8 host_bridge_stream;
+	int stream_id;
+	const char *name;
+};
+
+int pci_ide_domain(struct pci_dev *pdev);
+struct pci_ide_partner *pci_ide_to_settings(struct pci_dev *pdev, struct pci_ide *ide);
+struct pci_ide *pci_ide_stream_alloc(struct pci_dev *pdev);
+void pci_ide_stream_free(struct pci_ide *ide);
+int  pci_ide_stream_register(struct pci_ide *ide);
+void pci_ide_stream_unregister(struct pci_ide *ide);
+void pci_ide_stream_setup(struct pci_dev *pdev, struct pci_ide *ide);
+void pci_ide_stream_teardown(struct pci_dev *pdev, struct pci_ide *ide);
+int pci_ide_stream_enable(struct pci_dev *pdev, struct pci_ide *ide);
+void pci_ide_stream_disable(struct pci_dev *pdev, struct pci_ide *ide);
+void pci_ide_stream_release(struct pci_ide *ide);
+DEFINE_FREE(pci_ide_stream_release, struct pci_ide *, if (_T) pci_ide_stream_release(_T))
+#endif /* __PCI_IDE_H__ */
diff --git a/include/linux/pci.h b/include/linux/pci.h
index a7353df51fea..cc83ae274601 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -538,6 +538,8 @@ struct pci_dev {
 	u16		ide_cap;	/* Link Integrity & Data Encryption */
 	u8		nr_ide_mem;	/* Address association resources for streams */
 	u8		nr_link_ide;	/* Link Stream count (Selective Stream offset) */
+	u8		nr_sel_ide;	/* Selective Stream count (register block allocator) */
+	DECLARE_BITMAP(ide_stream_map, CONFIG_PCI_IDE_STREAM_MAX);
 	unsigned int	ide_cfg:1;	/* Config cycles over IDE */
 	unsigned int	ide_tee_limit:1; /* Disallow T=0 traffic over IDE */
 #endif
@@ -607,6 +609,10 @@ struct pci_host_bridge {
 	int		domain_nr;
 	struct list_head windows;	/* resource_entry */
 	struct list_head dma_ranges;	/* dma ranges resource list */
+#ifdef CONFIG_PCI_IDE
+	u8 nr_ide_streams;		/* Track available vs in-use streams */
+	DECLARE_BITMAP(ide_stream_map, CONFIG_PCI_IDE_STREAM_MAX);
+#endif
 	u8 (*swizzle_irq)(struct pci_dev *, u8 *); /* Platform IRQ swizzler */
 	int (*map_irq)(const struct pci_dev *, u8, u8);
 	void (*release_fn)(struct pci_host_bridge *);
-- 
2.50.1
Re: [PATCH v4 07/10] PCI/IDE: Add IDE establishment helpers
Posted by Arto Merilainen 1 month, 4 weeks ago
On 17.7.2025 21.33, Dan Williams wrote:
> +static void set_ide_sel_ctl(struct pci_dev *pdev, struct pci_ide *ide, int pos,
> +			    bool enable)
> +{
> +	u32 val = FIELD_PREP(PCI_IDE_SEL_CTL_ID_MASK, ide->stream_id) |
> +		  FIELD_PREP(PCI_IDE_SEL_CTL_DEFAULT, 1) |

If I recall correctly, setting the DEFAULT bit is allowed only for one 
SEL_SID instance at a time. If we consider the root port, wouldn't this 
prevent having multiple IDE capable devices under the same RP?

> +		  FIELD_PREP(PCI_IDE_SEL_CTL_CFG_EN, pdev->ide_cfg) |
> +		  FIELD_PREP(PCI_IDE_SEL_CTL_TEE_LIMITED, pdev->ide_tee_limit) |
> +		  FIELD_PREP(PCI_IDE_SEL_CTL_EN, enable);
> +
> +	pci_write_config_dword(pdev, pos + PCI_IDE_SEL_CTL, val);
> +}
> +
> +/**
> + * pci_ide_stream_setup() - program settings to Selective IDE Stream registers
> + * @pdev: PCIe device object for either a Root Port or Endpoint Partner Port
> + * @ide: registered IDE settings descriptor
> + *
> + * When @pdev is a PCI_EXP_TYPE_ENDPOINT then the PCI_IDE_EP partner
> + * settings are written to @pdev's Selective IDE Stream register block,
> + * and when @pdev is a PCI_EXP_TYPE_ROOT_PORT, the PCI_IDE_RP settings
> + * are selected.
> + */
> +void pci_ide_stream_setup(struct pci_dev *pdev, struct pci_ide *ide)
> +{
> +	struct pci_ide_partner *settings = pci_ide_to_settings(pdev, ide);
> +	int pos;
> +	u32 val;
> +
> +	if (!settings)
> +		return;
> +
> +	pos = sel_ide_offset(pdev, settings);
> +
> +	val = FIELD_PREP(PCI_IDE_SEL_RID_1_LIMIT_MASK, settings->rid_end);
> +	pci_write_config_dword(pdev, pos + PCI_IDE_SEL_RID_1, val);
> +
> +	val = FIELD_PREP(PCI_IDE_SEL_RID_2_VALID, 1) |
> +	      FIELD_PREP(PCI_IDE_SEL_RID_2_BASE_MASK, settings->rid_start) |
> +	      FIELD_PREP(PCI_IDE_SEL_RID_2_SEG_MASK, pci_ide_domain(pdev));
> +
> +	pci_write_config_dword(pdev, pos + PCI_IDE_SEL_RID_2, val);
> +
> +	/*
> +	 * Setup control register early for devices that expect
> +	 * stream_id is set during key programming.
> +	 */
> +	set_ide_sel_ctl(pdev, ide, pos, false);
> +	settings->setup = 1;
> +}
> +EXPORT_SYMBOL_GPL(pci_ide_stream_setup);

The first revision of this patch had address association register 
programming but it has since been removed. Could you comment if there is 
a reason for this change?

Some background: This might be problematic for ARM CCA. I recall seeing 
a comment stating that the address association register programming can 
be skipped on some architectures (e.g., apparently AMD uses a separate 
table that contains the StreamID) but on ARM CCA the StreamID 
association AFAIK happens through these registers.

- R2
Re: [PATCH v4 07/10] PCI/IDE: Add IDE establishment helpers
Posted by dan.j.williams@intel.com 1 month, 4 weeks ago
Arto Merilainen wrote:
> On 17.7.2025 21.33, Dan Williams wrote:
> > +static void set_ide_sel_ctl(struct pci_dev *pdev, struct pci_ide *ide, int pos,
> > +			    bool enable)
> > +{
> > +	u32 val = FIELD_PREP(PCI_IDE_SEL_CTL_ID_MASK, ide->stream_id) |
> > +		  FIELD_PREP(PCI_IDE_SEL_CTL_DEFAULT, 1) |
> 
> If I recall correctly, setting the DEFAULT bit is allowed only for one 
> SEL_SID instance at a time. If we consider the root port, wouldn't this 
> prevent having multiple IDE capable devices under the same RP?

True, I'll drop this from the next version. We can circle back to this
when ATS is considered, but that is not in scope for initial enabling.

> > +		  FIELD_PREP(PCI_IDE_SEL_CTL_CFG_EN, pdev->ide_cfg) |
> > +		  FIELD_PREP(PCI_IDE_SEL_CTL_TEE_LIMITED, pdev->ide_tee_limit) |
> > +		  FIELD_PREP(PCI_IDE_SEL_CTL_EN, enable);
> > +
> > +	pci_write_config_dword(pdev, pos + PCI_IDE_SEL_CTL, val);
> > +}
> > +
> > +/**
> > + * pci_ide_stream_setup() - program settings to Selective IDE Stream registers
> > + * @pdev: PCIe device object for either a Root Port or Endpoint Partner Port
> > + * @ide: registered IDE settings descriptor
> > + *
> > + * When @pdev is a PCI_EXP_TYPE_ENDPOINT then the PCI_IDE_EP partner
> > + * settings are written to @pdev's Selective IDE Stream register block,
> > + * and when @pdev is a PCI_EXP_TYPE_ROOT_PORT, the PCI_IDE_RP settings
> > + * are selected.
> > + */
> > +void pci_ide_stream_setup(struct pci_dev *pdev, struct pci_ide *ide)
> > +{
> > +	struct pci_ide_partner *settings = pci_ide_to_settings(pdev, ide);
> > +	int pos;
> > +	u32 val;
> > +
> > +	if (!settings)
> > +		return;
> > +
> > +	pos = sel_ide_offset(pdev, settings);
> > +
> > +	val = FIELD_PREP(PCI_IDE_SEL_RID_1_LIMIT_MASK, settings->rid_end);
> > +	pci_write_config_dword(pdev, pos + PCI_IDE_SEL_RID_1, val);
> > +
> > +	val = FIELD_PREP(PCI_IDE_SEL_RID_2_VALID, 1) |
> > +	      FIELD_PREP(PCI_IDE_SEL_RID_2_BASE_MASK, settings->rid_start) |
> > +	      FIELD_PREP(PCI_IDE_SEL_RID_2_SEG_MASK, pci_ide_domain(pdev));
> > +
> > +	pci_write_config_dword(pdev, pos + PCI_IDE_SEL_RID_2, val);
> > +
> > +	/*
> > +	 * Setup control register early for devices that expect
> > +	 * stream_id is set during key programming.
> > +	 */
> > +	set_ide_sel_ctl(pdev, ide, pos, false);
> > +	settings->setup = 1;
> > +}
> > +EXPORT_SYMBOL_GPL(pci_ide_stream_setup);
> 
> The first revision of this patch had address association register 
> programming but it has since been removed. Could you comment if there is 
> a reason for this change?

We chatted about it around this point in the original review thread [1].
tl;dr SEV-TIO and TDX Connect did not see a strict need for it. However,
the expectation was always to circle back and revive it if it turned out
later to be required.

[1]: http://lore.kernel.org/67bcf19bd1c7a_1c530f29449@dwillia2-xfh.jf.intel.com.notmuch

> Some background: This might be problematic for ARM CCA. I recall seeing 
> a comment stating that the address association register programming can 
> be skipped on some architectures (e.g., apparently AMD uses a separate 
> table that contains the StreamID) but on ARM CCA the StreamID 
> association AFAIK happens through these registers.

Can you confirm and perhaps work with Aneesh to propose an incremental
patch to add that support back? It might be something that we let the
low level TSM driver control. Like an additional address association
object that can be attached to 'struct pci_ide' by the low level TSM
driver.

The messy part is sparse device MMIO layout vs limited association
blocks and this is where SEV-TIO and TDX Connect have other mechanisms
to do that stream-id association.
Re: [PATCH v4 07/10] PCI/IDE: Add IDE establishment helpers
Posted by Arto Merilainen 1 month, 3 weeks ago
On 8.8.2025 20.26, dan.j.williams@intel.com wrote:
> Arto Merilainen wrote:
>> The first revision of this patch had address association register
>> programming but it has since been removed. Could you comment if there is
>> a reason for this change?
> 
> We chatted about it around this point in the original review thread [1].
> tl;dr SEV-TIO and TDX Connect did not see a strict need for it. However,
> the expectation was always to circle back and revive it if it turned out
> later to be required.

Thank you for the reference. I suppose it is ok to rely on the default 
streams on the first iteration, and add a follow-up patch in the ARM CCA 
device assignment support series in case it is the only architecture 
that depends on them.

> 
>> Some background: This might be problematic for ARM CCA. I recall seeing
>> a comment stating that the address association register programming can
>> be skipped on some architectures (e.g., apparently AMD uses a separate
>> table that contains the StreamID) but on ARM CCA the StreamID
>> association AFAIK happens through these registers.
> 
> Can you confirm and perhaps work with Aneesh to propose an incremental
> patch to add that support back? It might be something that we let the
> low level TSM driver control. Like an additional address association
> object that can be attached to 'struct pci_ide' by the low level TSM
> driver.

Aneesh, could you perhaps extend the IDE driver by adding the RP address 
association register programming in the next revision of the DA support 
series?

I think the EP side programming won't be relevant until we get to the 
P2P use-cases.

> 
> The messy part is sparse device MMIO layout vs limited association
> blocks and this is where SEV-TIO and TDX Connect have other mechanisms
> to do that stream-id association.

Despite the potential sparsity, I think there needs to be only three 
address association register blocks per SEL_IDE block: The routing is 
based on the type-1 configuration space header which defines only three 
ranges (32bit BAR, 64bit BAR, IO). When enabling IDE between an RP and 
an EP, the SEL_IDE address association registers in the RP can be 
programmed with the same ranges used in the type-1 header in the switch 
upstream from the EP.

That said, if the RP implements less than three address association 
registers per SEL_SID, this scheme won't work.

(I vaguely recall that the PCIe spec might forbid IORd/IOWr TLPs when 
selective IDE streams are used so the limit might in fact be two instead 
three...)

- R2
Re: [PATCH v4 07/10] PCI/IDE: Add IDE establishment helpers
Posted by Aneesh Kumar K.V 1 month, 1 week ago
Arto Merilainen <amerilainen@nvidia.com> writes:

> On 8.8.2025 20.26, dan.j.williams@intel.com wrote:
>> Arto Merilainen wrote:
>>> The first revision of this patch had address association register
>>> programming but it has since been removed. Could you comment if there is
>>> a reason for this change?
>> 
>> We chatted about it around this point in the original review thread [1].
>> tl;dr SEV-TIO and TDX Connect did not see a strict need for it. However,
>> the expectation was always to circle back and revive it if it turned out
>> later to be required.
>
> Thank you for the reference. I suppose it is ok to rely on the default 
> streams on the first iteration, and add a follow-up patch in the ARM CCA 
> device assignment support series in case it is the only architecture 
> that depends on them.
>
>> 
>>> Some background: This might be problematic for ARM CCA. I recall seeing
>>> a comment stating that the address association register programming can
>>> be skipped on some architectures (e.g., apparently AMD uses a separate
>>> table that contains the StreamID) but on ARM CCA the StreamID
>>> association AFAIK happens through these registers.
>> 
>> Can you confirm and perhaps work with Aneesh to propose an incremental
>> patch to add that support back? It might be something that we let the
>> low level TSM driver control. Like an additional address association
>> object that can be attached to 'struct pci_ide' by the low level TSM
>> driver.
>
> Aneesh, could you perhaps extend the IDE driver by adding the RP address 
> association register programming in the next revision of the DA support 
> series?
>

Sure, I can add that change as part of next update. 

>
> I think the EP side programming won't be relevant until we get to the 
> P2P use-cases.
>
>> 
>> The messy part is sparse device MMIO layout vs limited association
>> blocks and this is where SEV-TIO and TDX Connect have other mechanisms
>> to do that stream-id association.
>
> Despite the potential sparsity, I think there needs to be only three 
> address association register blocks per SEL_IDE block: The routing is 
> based on the type-1 configuration space header which defines only three 
> ranges (32bit BAR, 64bit BAR, IO). When enabling IDE between an RP and 
> an EP, the SEL_IDE address association registers in the RP can be 
> programmed with the same ranges used in the type-1 header in the switch 
> upstream from the EP.
>
> That said, if the RP implements less than three address association 
> registers per SEL_SID, this scheme won't work.
>
> (I vaguely recall that the PCIe spec might forbid IORd/IOWr TLPs when 
> selective IDE streams are used so the limit might in fact be two instead 
> three...)
>
> - R2
Re: [PATCH v4 07/10] PCI/IDE: Add IDE establishment helpers
Posted by Aneesh Kumar K.V 3 weeks, 4 days ago
Aneesh Kumar K.V <aneesh.kumar@kernel.org> writes:

> Arto Merilainen <amerilainen@nvidia.com> writes:
>
>> On 8.8.2025 20.26, dan.j.williams@intel.com wrote:
>>> Arto Merilainen wrote:
>>>> The first revision of this patch had address association register
>>>> programming but it has since been removed. Could you comment if there is
>>>> a reason for this change?
>>> 
>>> We chatted about it around this point in the original review thread [1].
>>> tl;dr SEV-TIO and TDX Connect did not see a strict need for it. However,
>>> the expectation was always to circle back and revive it if it turned out
>>> later to be required.
>>
>> Thank you for the reference. I suppose it is ok to rely on the default 
>> streams on the first iteration, and add a follow-up patch in the ARM CCA 
>> device assignment support series in case it is the only architecture 
>> that depends on them.
>>
>>> 
>>>> Some background: This might be problematic for ARM CCA. I recall seeing
>>>> a comment stating that the address association register programming can
>>>> be skipped on some architectures (e.g., apparently AMD uses a separate
>>>> table that contains the StreamID) but on ARM CCA the StreamID
>>>> association AFAIK happens through these registers.
>>> 
>>> Can you confirm and perhaps work with Aneesh to propose an incremental
>>> patch to add that support back? It might be something that we let the
>>> low level TSM driver control. Like an additional address association
>>> object that can be attached to 'struct pci_ide' by the low level TSM
>>> driver.
>>
>> Aneesh, could you perhaps extend the IDE driver by adding the RP address 
>> association register programming in the next revision of the DA support 
>> series?
>>
>
> Sure, I can add that change as part of next update. 
>

This is the change I am adding

 drivers/pci/ide.c                        | 128 ++++++++++++++++++++++-
 drivers/virt/coco/arm-cca-host/arm-cca.c |  13 +++
 include/linux/pci-ide.h                  |   7 ++
 3 files changed, 147 insertions(+), 1 deletion(-)

diff --git a/drivers/pci/ide.c b/drivers/pci/ide.c
index 3f772979eacb..23d1712ba97a 100644
--- a/drivers/pci/ide.c
+++ b/drivers/pci/ide.c
@@ -101,7 +101,7 @@ void pci_ide_init(struct pci_dev *pdev)
 	pdev->ide_cap = ide_cap;
 	pdev->nr_link_ide = nr_link_ide;
 	pdev->nr_sel_ide = nr_streams;
-	pdev->nr_ide_mem = nr_ide_mem;
+	pdev->nr_ide_mem = min(nr_ide_mem, PCI_IDE_AASOC_REG_MAX);
 }
 
 struct stream_index {
@@ -213,11 +213,13 @@ struct pci_ide *pci_ide_stream_alloc(struct pci_dev *pdev)
 				.rid_start = pci_dev_id(rp),
 				.rid_end = pci_dev_id(rp),
 				.stream_index = no_free_ptr(ep_stream)->stream_index,
+				.nr_mem = 0,
 			},
 			[PCI_IDE_RP] = {
 				.rid_start = pci_dev_id(pdev),
 				.rid_end = rid_end,
 				.stream_index = no_free_ptr(rp_stream)->stream_index,
+				.nr_mem = 0,
 			},
 		},
 		.host_bridge_stream = no_free_ptr(hb_stream)->stream_index,
@@ -228,6 +230,109 @@ struct pci_ide *pci_ide_stream_alloc(struct pci_dev *pdev)
 }
 EXPORT_SYMBOL_GPL(pci_ide_stream_alloc);
 
+static int add_range_merge_overlap(struct range *range, int az, int nr_range,
+				   u64 start, u64 end)
+{
+	int i;
+
+	if (start >= end)
+		return nr_range;
+
+	/* get new start/end: */
+	for (i = 0; i < nr_range; i++) {
+
+		if (!range[i].end)
+			continue;
+
+		/* Try to add to the end */
+		if (range[i].end + 1 == start) {
+			range[i].end = end;
+			return nr_range;
+		}
+
+		/* Try to add to the start */
+		if (range[i].start == end + 1) {
+			range[i].start = start;
+			return nr_range;
+		}
+	}
+
+	/* Need to add it: */
+	return add_range(range, az, nr_range, start, end);
+}
+
+int pci_ide_add_address_assoc_block(struct pci_dev *pdev,
+				    struct pci_ide *ide,
+				    u64 start, u64 end)
+{
+	struct pci_ide_partner *partner;
+
+	if (!pci_is_pcie(pdev)) {
+		pci_warn_once(pdev, "not a PCIe device\n");
+		return -EINVAL;
+	}
+
+	switch (pci_pcie_type(pdev)) {
+	case PCI_EXP_TYPE_ENDPOINT:
+
+		if (pdev != ide->pdev)
+			return -EINVAL;
+		partner = &ide->partner[PCI_IDE_RP];
+		break;
+	default:
+		pci_warn_once(pdev, "invalid device type\n");
+		return -EINVAL;
+	}
+
+	if (partner->nr_mem >= pdev->nr_ide_mem)
+		return -ENOMEM;
+
+	partner->nr_mem = add_range_merge_overlap(partner->mem,
+					   PCI_IDE_AASOC_REG_MAX, partner->nr_mem,
+					   start, end);
+	return 0;
+}
+
+
+int pci_ide_merge_address_assoc_block(struct pci_dev *pdev,
+				      struct pci_ide *ide, u64 start, u64 end)
+{
+	struct pci_ide_partner *partner;
+
+	if (!pci_is_pcie(pdev)) {
+		pci_warn_once(pdev, "not a PCIe device\n");
+		return -EINVAL;
+	}
+
+	switch (pci_pcie_type(pdev)) {
+	case PCI_EXP_TYPE_ENDPOINT:
+
+		if (pdev != ide->pdev)
+			return -EINVAL;
+		partner = &ide->partner[PCI_IDE_RP];
+		break;
+	default:
+		pci_warn_once(pdev, "invalid device type\n");
+		return -EINVAL;
+	}
+
+	for (int i = 0; i < PCI_IDE_AASOC_REG_MAX; i++) {
+		struct range *r = &partner->mem[i];
+
+		if (r->start < start)
+			start = r->start;
+		if (r->end > end)
+			end = r->end;
+		r->start = 0;
+		r->end = 0;
+	}
+	partner->mem[0].start = start;
+	partner->mem[0].end = end;
+	partner->nr_mem = 1;
+
+	return 0;
+}
+
 /**
  * pci_ide_stream_free() - unwind pci_ide_stream_alloc()
  * @ide: idle IDE settings descriptor
@@ -424,6 +529,21 @@ void pci_ide_stream_setup(struct pci_dev *pdev, struct pci_ide *ide)
 
 	pci_write_config_dword(pdev, pos + PCI_IDE_SEL_RID_2, val);
 
+	for (int i = 0; i < settings->nr_mem; i++) {
+		val = FIELD_PREP(PCI_IDE_SEL_ADDR_1_VALID, 1) |
+			FIELD_PREP(PCI_IDE_SEL_ADDR_1_BASE_LOW,
+				   lower_32_bits(settings->mem[i].start)) |
+			FIELD_PREP(PCI_IDE_SEL_ADDR_1_LIMIT_LOW,
+				   lower_32_bits(settings->mem[i].end));
+		pci_write_config_dword(pdev, pos + PCI_IDE_SEL_ADDR_1(i), val);
+
+		val = upper_32_bits(settings->mem[i].end);
+		pci_write_config_dword(pdev, pos + PCI_IDE_SEL_ADDR_2(i), val);
+
+		val = upper_32_bits(settings->mem[i].start);
+		pci_write_config_dword(pdev, pos + PCI_IDE_SEL_ADDR_3(i), val);
+	}
+
 	/*
 	 * Setup control register early for devices that expect
 	 * stream_id is set during key programming.
@@ -453,6 +573,12 @@ void pci_ide_stream_teardown(struct pci_dev *pdev, struct pci_ide *ide)
 	pos = sel_ide_offset(pdev, settings);
 
 	pci_write_config_dword(pdev, pos + PCI_IDE_SEL_CTL, 0);
+	for (int i = settings->nr_mem - 1; i >= 0; i--) {
+		pci_write_config_dword(pdev, pos + PCI_IDE_SEL_ADDR_3(i), 0);
+		pci_write_config_dword(pdev, pos + PCI_IDE_SEL_ADDR_2(i), 0);
+		pci_write_config_dword(pdev, pos + PCI_IDE_SEL_ADDR_1(i), 0);
+	}
+
 	pci_write_config_dword(pdev, pos + PCI_IDE_SEL_RID_2, 0);
 	pci_write_config_dword(pdev, pos + PCI_IDE_SEL_RID_1, 0);
 	settings->setup = 0;
diff --git a/drivers/virt/coco/arm-cca-host/arm-cca.c b/drivers/virt/coco/arm-cca-host/arm-cca.c
index c9717698af56..28993f9277e4 100644
--- a/drivers/virt/coco/arm-cca-host/arm-cca.c
+++ b/drivers/virt/coco/arm-cca-host/arm-cca.c
@@ -137,6 +137,7 @@ static int cca_tsm_connect(struct pci_dev *pdev)
 {
 	struct pci_dev *rp = pcie_find_root_port(pdev);
 	struct cca_host_pf0_dsc *dsc_pf0;
+	struct resource *res;
 	struct pci_ide *ide;
 	int rc, stream_id;
 
@@ -163,9 +164,21 @@ static int cca_tsm_connect(struct pci_dev *pdev)
 	if (rc)
 		goto err_stream;
 
+	/*
+	 * Try to use the available address assoc register blocks.
+	 * If we fail with ENOMEM, create one block covering the entire
+	 * address range. (Should work for arm64)
+	 */
+	pci_dev_for_each_resource(pdev, res) {
+		rc = pci_ide_add_address_assoc_block(pdev, ide, res->start, res->end);
+		if (rc == -ENOMEM)
+			pci_ide_merge_address_assoc_block(pdev, ide, res->start, res->end);
+	}
+
 	pci_ide_stream_setup(pdev, ide);
 	pci_ide_stream_setup(rp, ide);
 
+
 	rc = tsm_ide_stream_register(ide);
 	if (rc)
 		goto err_tsm;
diff --git a/include/linux/pci-ide.h b/include/linux/pci-ide.h
index c3838d11af88..3d4f7f462a8d 100644
--- a/include/linux/pci-ide.h
+++ b/include/linux/pci-ide.h
@@ -19,6 +19,7 @@ enum pci_ide_partner_select {
 	PCI_IDE_HB = PCI_IDE_PARTNER_MAX,
 };
 
+#define PCI_IDE_AASOC_REG_MAX	6
 /**
  * struct pci_ide_partner - Per port pair Selective IDE Stream settings
  * @rid_start: Partner Port Requester ID range start
@@ -34,6 +35,8 @@ struct pci_ide_partner {
 	u8 stream_index;
 	unsigned int setup:1;
 	unsigned int enable:1;
+	int nr_mem;
+	struct range mem[PCI_IDE_AASOC_REG_MAX];
 };
 
 /**
@@ -60,6 +63,10 @@ struct pci_ide {
 
 struct pci_ide_partner *pci_ide_to_settings(struct pci_dev *pdev, struct pci_ide *ide);
 struct pci_ide *pci_ide_stream_alloc(struct pci_dev *pdev);
+int pci_ide_add_address_assoc_block(struct pci_dev *pdev,
+				    struct pci_ide *ide, u64 start, u64 end);
+int pci_ide_merge_address_assoc_block(struct pci_dev *pdev,
+				      struct pci_ide *ide, u64 start, u64 end);
 void pci_ide_stream_free(struct pci_ide *ide);
 int  pci_ide_stream_register(struct pci_ide *ide);
 void pci_ide_stream_unregister(struct pci_ide *ide);
-- 
2.43.0
Re: [PATCH v4 07/10] PCI/IDE: Add IDE establishment helpers
Posted by dan.j.williams@intel.com 3 weeks, 3 days ago
Aneesh Kumar K.V wrote:
> Aneesh Kumar K.V <aneesh.kumar@kernel.org> writes:
[..]
> >> Aneesh, could you perhaps extend the IDE driver by adding the RP address 
> >> association register programming in the next revision of the DA support 
> >> series?
> >>
> >
> > Sure, I can add that change as part of next update. 
> >
> 
> This is the change I am adding

Just thinking out loud...

I assume we will soon get to the point where at least one of the vendors
is ready to have their implementation pulled into tsm.git.

For truly vendor-specific bits that can be a pull request that I just
blindly pull. For core update proposals like this I expect it would be
best to cherry-pick those into the base at the next staging tree update.
This would be in support of prepping tsm.git (at least the base
infrastructure) for inclusion in linux-next.

As always, open to ideas on how to coordinate this.

In the meantime the tsm.git plan is to continue to rebase the base
infrastrcture branch until the review comments subside and all new
changes can be handled as incremental updates.

>  drivers/pci/ide.c                        | 128 ++++++++++++++++++++++-
>  drivers/virt/coco/arm-cca-host/arm-cca.c |  13 +++
>  include/linux/pci-ide.h                  |   7 ++
>  3 files changed, 147 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/pci/ide.c b/drivers/pci/ide.c
> index 3f772979eacb..23d1712ba97a 100644
> --- a/drivers/pci/ide.c
> +++ b/drivers/pci/ide.c
> @@ -101,7 +101,7 @@ void pci_ide_init(struct pci_dev *pdev)
>  	pdev->ide_cap = ide_cap;
>  	pdev->nr_link_ide = nr_link_ide;
>  	pdev->nr_sel_ide = nr_streams;
> -	pdev->nr_ide_mem = nr_ide_mem;
> +	pdev->nr_ide_mem = min(nr_ide_mem, PCI_IDE_AASOC_REG_MAX);
>  }
>  
>  struct stream_index {
> @@ -213,11 +213,13 @@ struct pci_ide *pci_ide_stream_alloc(struct pci_dev *pdev)
>  				.rid_start = pci_dev_id(rp),
>  				.rid_end = pci_dev_id(rp),
>  				.stream_index = no_free_ptr(ep_stream)->stream_index,
> +				.nr_mem = 0,

Designated initializers already zero by default.

>  			},
>  			[PCI_IDE_RP] = {
>  				.rid_start = pci_dev_id(pdev),
>  				.rid_end = rid_end,
>  				.stream_index = no_free_ptr(rp_stream)->stream_index,
> +				.nr_mem = 0,
>  			},
>  		},
>  		.host_bridge_stream = no_free_ptr(hb_stream)->stream_index,
> @@ -228,6 +230,109 @@ struct pci_ide *pci_ide_stream_alloc(struct pci_dev *pdev)
>  }
>  EXPORT_SYMBOL_GPL(pci_ide_stream_alloc);
>  
> +static int add_range_merge_overlap(struct range *range, int az, int nr_range,
> +				   u64 start, u64 end)
> +{
> +	int i;
> +
> +	if (start >= end)
> +		return nr_range;
> +
> +	/* get new start/end: */
> +	for (i = 0; i < nr_range; i++) {
> +
> +		if (!range[i].end)
> +			continue;
> +
> +		/* Try to add to the end */
> +		if (range[i].end + 1 == start) {
> +			range[i].end = end;
> +			return nr_range;
> +		}
> +
> +		/* Try to add to the start */
> +		if (range[i].start == end + 1) {
> +			range[i].start = start;
> +			return nr_range;
> +		}
> +	}
> +
> +	/* Need to add it: */
> +	return add_range(range, az, nr_range, start, end);
> +}
> +
> +int pci_ide_add_address_assoc_block(struct pci_dev *pdev,
> +				    struct pci_ide *ide,
> +				    u64 start, u64 end)

How about:

pci_ide_associate_address()?

...because the result is not always a new block.

> +{
> +	struct pci_ide_partner *partner;
> +
> +	if (!pci_is_pcie(pdev)) {
> +		pci_warn_once(pdev, "not a PCIe device\n");
> +		return -EINVAL;
> +	}
> +
> +	switch (pci_pcie_type(pdev)) {
> +	case PCI_EXP_TYPE_ENDPOINT:
> +
> +		if (pdev != ide->pdev)
> +			return -EINVAL;
> +		partner = &ide->partner[PCI_IDE_RP];
> +		break;
> +	default:
> +		pci_warn_once(pdev, "invalid device type\n");
> +		return -EINVAL;
> +	}
> +
> +	if (partner->nr_mem >= pdev->nr_ide_mem)
> +		return -ENOMEM;
> +
> +	partner->nr_mem = add_range_merge_overlap(partner->mem,
> +					   PCI_IDE_AASOC_REG_MAX, partner->nr_mem,
> +					   start, end);
> +	return 0;
> +}
> +
> +
> +int pci_ide_merge_address_assoc_block(struct pci_dev *pdev,
> +				      struct pci_ide *ide, u64 start, u64 end)

Is this really "merge", or "expand_to_fit" similar to
insert_resource_expand_to_fit()?

pci_ide_associate_address_force()? ...or am I reading it wrong?

[..]
> diff --git a/drivers/virt/coco/arm-cca-host/arm-cca.c b/drivers/virt/coco/arm-cca-host/arm-cca.c
> index c9717698af56..28993f9277e4 100644
> --- a/drivers/virt/coco/arm-cca-host/arm-cca.c
> +++ b/drivers/virt/coco/arm-cca-host/arm-cca.c
> @@ -137,6 +137,7 @@ static int cca_tsm_connect(struct pci_dev *pdev)
>  {
>  	struct pci_dev *rp = pcie_find_root_port(pdev);
>  	struct cca_host_pf0_dsc *dsc_pf0;
> +	struct resource *res;
>  	struct pci_ide *ide;
>  	int rc, stream_id;
>  
> @@ -163,9 +164,21 @@ static int cca_tsm_connect(struct pci_dev *pdev)
>  	if (rc)
>  		goto err_stream;
>  
> +	/*
> +	 * Try to use the available address assoc register blocks.
> +	 * If we fail with ENOMEM, create one block covering the entire
> +	 * address range. (Should work for arm64)
> +	 */
> +	pci_dev_for_each_resource(pdev, res) {
> +		rc = pci_ide_add_address_assoc_block(pdev, ide, res->start, res->end);
> +		if (rc == -ENOMEM)
> +			pci_ide_merge_address_assoc_block(pdev, ide, res->start, res->end);

How does this play with the "shared MSI-X MMIO" problem? Does this also
need to align with interface report expectations?

> +	}
> +
>  	pci_ide_stream_setup(pdev, ide);
>  	pci_ide_stream_setup(rp, ide);
>  
> +
>  	rc = tsm_ide_stream_register(ide);
>  	if (rc)
>  		goto err_tsm;
> diff --git a/include/linux/pci-ide.h b/include/linux/pci-ide.h
> index c3838d11af88..3d4f7f462a8d 100644
> --- a/include/linux/pci-ide.h
> +++ b/include/linux/pci-ide.h
> @@ -19,6 +19,7 @@ enum pci_ide_partner_select {
>  	PCI_IDE_HB = PCI_IDE_PARTNER_MAX,
>  };
>  
> +#define PCI_IDE_AASOC_REG_MAX	6

Where does 6 come from?

---
7.9.26.5.1 Selective IDE Stream Capability Register

The number of Selective IDE Address Association register blocks for a
given IDE Stream is hardware implementation specific, and is permitted
to be any number between 0 and 15.
---

Also, I would put this max in include/uapi/linux/pci_regs.h and match
the local naming.
Re: [PATCH v4 07/10] PCI/IDE: Add IDE establishment helpers
Posted by Bjorn Helgaas 1 month, 4 weeks ago
On Thu, Jul 17, 2025 at 11:33:55AM -0700, Dan Williams wrote:
> There are two components to establishing an encrypted link, provisioning
> the stream in Partner Port config-space, and programming the keys into
> the link layer via IDE_KM (IDE Key Management). This new library,
> drivers/pci/ide.c, enables the former. IDE_KM, via a TSM low-level
> driver, is saved for later.
> 
> With the platform TSM implementations of SEV-TIO and TDX Connect in mind
> this library abstracts small differences in those implementations. For
> example, TDX Connect handles Root Port register setup while SEV-TIO
> expects System Software to update the Root Port registers. This is the
> rationale for fine-grained 'setup' + 'enable' verbs.
> 
> The other design detail for TSM-coordinated IDE establishment is that
> the TSM may manage allocation of Stream IDs, this is why the Stream ID
> value is passed in to pci_ide_stream_setup().
> 
> The flow is:
> 
> pci_ide_stream_alloc()
>   Allocate a Selective IDE Stream Register Block in each Partner Port
>   (Endpoint + Root Port), and reserve a host bridge / platform stream
>   slot. Gather Partner Port specific stream settings like Requester ID.
> pci_ide_stream_register()
>   Publish the stream in sysfs after allocating a Stream ID. In the TSM
>   case the TSM allocates the Stream ID for the Partner Port pair.
> pci_ide_stream_setup()
>   Program the stream settings to a Partner Port. Caller is responsible
>   for optionally calling this for the Root Port as well if the TSM
>   implementation requires it.
> pci_ide_stream_enable()
>   Try to run the stream after IDE_KM.
> 
> In support of system administrators auditing where platform, Root Port,
> and Endpoint IDE stream resources are being spent, the allocated stream
> is reflected as a symlink from the host bridge to the endpoint with the
> name:
> 
>     stream%d.%d.%d
> 
> Where the tuple of integers reflects the allocated platform, Root Port,
> and Endpoint stream index (Selective IDE Stream Register Block) values.
> 
> Thanks to Wu Hao for a draft implementation of this infrastructure.
> 
> Cc: Bjorn Helgaas <bhelgaas@google.com>
> Cc: Lukas Wunner <lukas@wunner.de>
> Cc: Samuel Ortiz <sameo@rivosinc.com>
> Co-developed-by: Alexey Kardashevskiy <aik@amd.com>
> Signed-off-by: Alexey Kardashevskiy <aik@amd.com>
> Co-developed-by: Yilun Xu <yilun.xu@linux.intel.com>
> Signed-off-by: Yilun Xu <yilun.xu@linux.intel.com>
> Signed-off-by: Dan Williams <dan.j.williams@intel.com>

Acked-by: Bjorn Helgaas <bhelgaas@google.com>

> ---
>  .../ABI/testing/sysfs-devices-pci-host-bridge |  16 +
>  drivers/pci/ide.c                             | 422 ++++++++++++++++++
>  include/linux/pci-ide.h                       |  70 +++
>  include/linux/pci.h                           |   6 +
>  4 files changed, 514 insertions(+)
>  create mode 100644 include/linux/pci-ide.h
> 
> diff --git a/Documentation/ABI/testing/sysfs-devices-pci-host-bridge b/Documentation/ABI/testing/sysfs-devices-pci-host-bridge
> index 8c3a652799f1..c67d7c30efa0 100644
> --- a/Documentation/ABI/testing/sysfs-devices-pci-host-bridge
> +++ b/Documentation/ABI/testing/sysfs-devices-pci-host-bridge
> @@ -17,3 +17,19 @@ Description:
>  		PNP0A08 (/sys/devices/LNXSYSTM:00/LNXSYBUS:00/PNP0A08:00). See
>  		/sys/devices/pciDDDD:BB entry for details about the DDDD:BB
>  		format.
> +
> +What:		pciDDDD:BB/streamH.R.E
> +Contact:	linux-pci@vger.kernel.org
> +Description:
> +		(RO) When a platform has established a secure connection, PCIe
> +		IDE, between two Partner Ports, this symlink appears. The
> +		primary function is to account the stream slot / resources
> +		consumed in each of the (H)ost bridge, (R)oot Port and
> +		(E)ndpoint that will be freed when invoking the tsm/disconnect
> +		flow. The link points to the endpoint PCI device in the
> +		Selective IDE Stream. "R" and "E" represent the assigned
> +		Selective IDE Stream Register Block in the Root Port and
> +		Endpoint, and "H" represents a platform specific pool of stream
> +		resources shared by the Root Ports in a host bridge. See
> +		/sys/devices/pciDDDD:BB entry for details about the DDDD:BB
> +		format.
> diff --git a/drivers/pci/ide.c b/drivers/pci/ide.c
> index e15937cdb2a4..cdc773a8b381 100644
> --- a/drivers/pci/ide.c
> +++ b/drivers/pci/ide.c
> @@ -5,6 +5,8 @@
>  
>  #define dev_fmt(fmt) "PCI/IDE: " fmt
>  #include <linux/pci.h>
> +#include <linux/sysfs.h>
> +#include <linux/pci-ide.h>
>  #include <linux/bitfield.h>
>  #include "pci.h"
>  
> @@ -24,6 +26,13 @@ static int __sel_ide_offset(u16 ide_cap, u8 nr_link_ide, u8 stream_index,
>  	return offset;
>  }
>  
> +static int sel_ide_offset(struct pci_dev *pdev,
> +			  struct pci_ide_partner *settings)
> +{
> +	return __sel_ide_offset(pdev->ide_cap, pdev->nr_link_ide,
> +				settings->stream_index, pdev->nr_ide_mem);
> +}
> +
>  void pci_ide_init(struct pci_dev *pdev)
>  {
>  	u8 nr_link_ide, nr_ide_mem, nr_streams;
> @@ -89,5 +98,418 @@ void pci_ide_init(struct pci_dev *pdev)
>  
>  	pdev->ide_cap = ide_cap;
>  	pdev->nr_link_ide = nr_link_ide;
> +	pdev->nr_sel_ide = nr_streams;
>  	pdev->nr_ide_mem = nr_ide_mem;
>  }
> +
> +struct stream_index {
> +	unsigned long *map;
> +	u8 max, stream_index;
> +};
> +
> +static void free_stream_index(struct stream_index *stream)
> +{
> +	clear_bit_unlock(stream->stream_index, stream->map);
> +}
> +
> +DEFINE_FREE(free_stream, struct stream_index *, if (_T) free_stream_index(_T))
> +static struct stream_index *alloc_stream_index(unsigned long *map, u8 max,
> +					       struct stream_index *stream)
> +{
> +	if (!max)
> +		return NULL;
> +
> +	do {
> +		u8 stream_index = find_first_zero_bit(map, max);
> +
> +		if (stream_index == max)
> +			return NULL;
> +		if (!test_and_set_bit_lock(stream_index, map)) {
> +			*stream = (struct stream_index) {
> +				.map = map,
> +				.max = max,
> +				.stream_index = stream_index,
> +			};
> +			return stream;
> +		}
> +		/* collided with another stream acquisition */
> +	} while (1);
> +}
> +
> +/**
> + * pci_ide_stream_alloc() - Reserve stream indices and probe for settings
> + * @pdev: IDE capable PCIe Endpoint Physical Function
> + *
> + * Retrieve the Requester ID range of @pdev for programming its Root
> + * Port IDE RID Association registers, and conversely retrieve the
> + * Requester ID of the Root Port for programming @pdev's IDE RID
> + * Association registers.
> + *
> + * Allocate a Selective IDE Stream Register Block instance per port.
> + *
> + * Allocate a platform stream resource from the associated host bridge.
> + * Retrieve stream association parameters for Requester ID range and
> + * address range restrictions for the stream.
> + */
> +struct pci_ide *pci_ide_stream_alloc(struct pci_dev *pdev)
> +{
> +	/* EP, RP, + HB Stream allocation */
> +	struct stream_index __stream[PCI_IDE_HB + 1];
> +	struct pci_host_bridge *hb;
> +	struct pci_dev *rp;
> +	int num_vf, rid_end;
> +
> +	if (!pci_is_pcie(pdev))
> +		return NULL;
> +
> +	if (pci_pcie_type(pdev) != PCI_EXP_TYPE_ENDPOINT)
> +		return NULL;
> +
> +	if (!pdev->ide_cap)
> +		return NULL;
> +
> +	/*
> +	 * Catch buggy PCI platform initialization (missing
> +	 * pci_ide_init_nr_streams())
> +	 */
> +	hb = pci_find_host_bridge(pdev->bus);
> +	if (WARN_ON_ONCE(!hb->nr_ide_streams))
> +		return NULL;
> +
> +	struct pci_ide *ide __free(kfree) = kzalloc(sizeof(*ide), GFP_KERNEL);
> +	if (!ide)
> +		return NULL;
> +
> +	struct stream_index *hb_stream __free(free_stream) = alloc_stream_index(
> +		hb->ide_stream_map, hb->nr_ide_streams, &__stream[PCI_IDE_HB]);
> +	if (!hb_stream)
> +		return NULL;
> +
> +	rp = pcie_find_root_port(pdev);
> +	struct stream_index *rp_stream __free(free_stream) = alloc_stream_index(
> +		rp->ide_stream_map, rp->nr_sel_ide, &__stream[PCI_IDE_RP]);
> +	if (!rp_stream)
> +		return NULL;
> +
> +	struct stream_index *ep_stream __free(free_stream) = alloc_stream_index(
> +		pdev->ide_stream_map, pdev->nr_sel_ide, &__stream[PCI_IDE_EP]);
> +	if (!ep_stream)
> +		return NULL;
> +
> +	/* for SR-IOV case, cover all VFs */
> +	num_vf = pci_num_vf(pdev);
> +	if (num_vf)
> +		rid_end = PCI_DEVID(pci_iov_virtfn_bus(pdev, num_vf),
> +				    pci_iov_virtfn_devfn(pdev, num_vf));
> +	else
> +		rid_end = pci_dev_id(pdev);
> +
> +	*ide = (struct pci_ide) {
> +		.pdev = pdev,
> +		.partner = {
> +			[PCI_IDE_EP] = {
> +				.rid_start = pci_dev_id(rp),
> +				.rid_end = pci_dev_id(rp),
> +				.stream_index = no_free_ptr(ep_stream)->stream_index,
> +			},
> +			[PCI_IDE_RP] = {
> +				.rid_start = pci_dev_id(pdev),
> +				.rid_end = rid_end,
> +				.stream_index = no_free_ptr(rp_stream)->stream_index,
> +			},
> +		},
> +		.host_bridge_stream = no_free_ptr(hb_stream)->stream_index,
> +		.stream_id = -1,
> +	};
> +
> +	return_ptr(ide);
> +}
> +EXPORT_SYMBOL_GPL(pci_ide_stream_alloc);
> +
> +/**
> + * pci_ide_stream_free() - unwind pci_ide_stream_alloc()
> + * @ide: idle IDE settings descriptor
> + *
> + * Free all of the stream index (register block) allocations acquired by
> + * pci_ide_stream_alloc(). The stream represented by @ide is assumed to
> + * be unregistered and not instantiated in any device.
> + */
> +void pci_ide_stream_free(struct pci_ide *ide)
> +{
> +	struct pci_dev *pdev = ide->pdev;
> +	struct pci_dev *rp = pcie_find_root_port(pdev);
> +	struct pci_host_bridge *hb = pci_find_host_bridge(pdev->bus);
> +
> +	clear_bit_unlock(ide->partner[PCI_IDE_EP].stream_index,
> +			 pdev->ide_stream_map);
> +	clear_bit_unlock(ide->partner[PCI_IDE_RP].stream_index,
> +			 rp->ide_stream_map);
> +	clear_bit_unlock(ide->host_bridge_stream, hb->ide_stream_map);
> +	kfree(ide);
> +}
> +EXPORT_SYMBOL_GPL(pci_ide_stream_free);
> +
> +/**
> + * pci_ide_stream_release() - unwind and release an @ide context
> + * @ide: partially or fully registered IDE settings descriptor
> + *
> + * In support of automatic cleanup of IDE setup routines perform IDE
> + * teardown in expected reverse order of setup and with respect to which
> + * aspects of IDE setup have successfully completed.
> + *
> + * Be careful that setup order mirrors this shutdown order. Otherwise,
> + * open code releasing the IDE context.
> + */
> +void pci_ide_stream_release(struct pci_ide *ide)
> +{
> +	struct pci_dev *pdev = ide->pdev;
> +	struct pci_dev *rp = pcie_find_root_port(pdev);
> +
> +	if (ide->partner[PCI_IDE_RP].enable)
> +		pci_ide_stream_disable(rp, ide);
> +
> +	if (ide->partner[PCI_IDE_EP].enable)
> +		pci_ide_stream_disable(pdev, ide);
> +
> +	if (ide->partner[PCI_IDE_RP].setup)
> +		pci_ide_stream_teardown(rp, ide);
> +
> +	if (ide->partner[PCI_IDE_EP].setup)
> +		pci_ide_stream_teardown(pdev, ide);
> +
> +	if (ide->name)
> +		pci_ide_stream_unregister(ide);
> +
> +	pci_ide_stream_free(ide);
> +}
> +EXPORT_SYMBOL_GPL(pci_ide_stream_release);
> +
> +/**
> + * pci_ide_stream_register() - Prepare to activate an IDE Stream
> + * @ide: IDE settings descriptor
> + *
> + * After a Stream ID has been acquired for @ide, record the presence of
> + * the stream in sysfs. The expectation is that @ide is immutable while
> + * registered.
> + */
> +int pci_ide_stream_register(struct pci_ide *ide)
> +{
> +	struct pci_dev *pdev = ide->pdev;
> +	struct pci_host_bridge *hb = pci_find_host_bridge(pdev->bus);
> +	u8 ep_stream, rp_stream;
> +	int rc;
> +
> +	if (ide->stream_id < 0 || ide->stream_id > U8_MAX) {
> +		pci_err(pdev, "Setup fail: Invalid Stream ID: %d\n", ide->stream_id);
> +		return -ENXIO;
> +	}
> +
> +	ep_stream = ide->partner[PCI_IDE_EP].stream_index;
> +	rp_stream = ide->partner[PCI_IDE_RP].stream_index;
> +	const char *name __free(kfree) = kasprintf(GFP_KERNEL, "stream%d.%d.%d",
> +						   ide->host_bridge_stream,
> +						   rp_stream, ep_stream);
> +	if (!name)
> +		return -ENOMEM;
> +
> +	rc = sysfs_create_link(&hb->dev.kobj, &pdev->dev.kobj, name);
> +	if (rc)
> +		return rc;
> +
> +	ide->name = no_free_ptr(name);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(pci_ide_stream_register);
> +
> +/**
> + * pci_ide_stream_unregister() - unwind pci_ide_stream_register()
> + * @ide: idle IDE settings descriptor
> + *
> + * In preparation for freeing @ide, remove sysfs enumeration for the
> + * stream.
> + */
> +void pci_ide_stream_unregister(struct pci_ide *ide)
> +{
> +	struct pci_dev *pdev = ide->pdev;
> +	struct pci_host_bridge *hb = pci_find_host_bridge(pdev->bus);
> +
> +	sysfs_remove_link(&hb->dev.kobj, ide->name);
> +	kfree(ide->name);
> +	ide->name = NULL;
> +}
> +EXPORT_SYMBOL_GPL(pci_ide_stream_unregister);
> +
> +int pci_ide_domain(struct pci_dev *pdev)
> +{
> +	if (pdev->fm_enabled)
> +		return pci_domain_nr(pdev->bus);
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(pci_ide_domain);
> +
> +struct pci_ide_partner *pci_ide_to_settings(struct pci_dev *pdev, struct pci_ide *ide)
> +{
> +	if (!pci_is_pcie(pdev)) {
> +		pci_warn_once(pdev, "not a PCIe device\n");
> +		return NULL;
> +	}
> +
> +	switch (pci_pcie_type(pdev)) {
> +	case PCI_EXP_TYPE_ENDPOINT:
> +		if (pdev != ide->pdev) {
> +			pci_warn_once(pdev, "setup expected Endpoint: %s\n", pci_name(ide->pdev));
> +			return NULL;
> +		}
> +		return &ide->partner[PCI_IDE_EP];
> +	case PCI_EXP_TYPE_ROOT_PORT: {
> +		struct pci_dev *rp = pcie_find_root_port(ide->pdev);
> +
> +		if (pdev != rp) {
> +			pci_warn_once(pdev, "setup expected Root Port: %s\n",
> +				      pci_name(rp));
> +			return NULL;
> +		}
> +		return &ide->partner[PCI_IDE_RP];
> +	}
> +	default:
> +		pci_warn_once(pdev, "invalid device type\n");
> +		return NULL;
> +	}
> +}
> +EXPORT_SYMBOL_GPL(pci_ide_to_settings);
> +
> +static void set_ide_sel_ctl(struct pci_dev *pdev, struct pci_ide *ide, int pos,
> +			    bool enable)
> +{
> +	u32 val = FIELD_PREP(PCI_IDE_SEL_CTL_ID_MASK, ide->stream_id) |
> +		  FIELD_PREP(PCI_IDE_SEL_CTL_DEFAULT, 1) |
> +		  FIELD_PREP(PCI_IDE_SEL_CTL_CFG_EN, pdev->ide_cfg) |
> +		  FIELD_PREP(PCI_IDE_SEL_CTL_TEE_LIMITED, pdev->ide_tee_limit) |
> +		  FIELD_PREP(PCI_IDE_SEL_CTL_EN, enable);
> +
> +	pci_write_config_dword(pdev, pos + PCI_IDE_SEL_CTL, val);
> +}
> +
> +/**
> + * pci_ide_stream_setup() - program settings to Selective IDE Stream registers
> + * @pdev: PCIe device object for either a Root Port or Endpoint Partner Port
> + * @ide: registered IDE settings descriptor
> + *
> + * When @pdev is a PCI_EXP_TYPE_ENDPOINT then the PCI_IDE_EP partner
> + * settings are written to @pdev's Selective IDE Stream register block,
> + * and when @pdev is a PCI_EXP_TYPE_ROOT_PORT, the PCI_IDE_RP settings
> + * are selected.
> + */
> +void pci_ide_stream_setup(struct pci_dev *pdev, struct pci_ide *ide)
> +{
> +	struct pci_ide_partner *settings = pci_ide_to_settings(pdev, ide);
> +	int pos;
> +	u32 val;
> +
> +	if (!settings)
> +		return;
> +
> +	pos = sel_ide_offset(pdev, settings);
> +
> +	val = FIELD_PREP(PCI_IDE_SEL_RID_1_LIMIT_MASK, settings->rid_end);
> +	pci_write_config_dword(pdev, pos + PCI_IDE_SEL_RID_1, val);
> +
> +	val = FIELD_PREP(PCI_IDE_SEL_RID_2_VALID, 1) |
> +	      FIELD_PREP(PCI_IDE_SEL_RID_2_BASE_MASK, settings->rid_start) |
> +	      FIELD_PREP(PCI_IDE_SEL_RID_2_SEG_MASK, pci_ide_domain(pdev));
> +
> +	pci_write_config_dword(pdev, pos + PCI_IDE_SEL_RID_2, val);
> +
> +	/*
> +	 * Setup control register early for devices that expect
> +	 * stream_id is set during key programming.
> +	 */
> +	set_ide_sel_ctl(pdev, ide, pos, false);
> +	settings->setup = 1;
> +}
> +EXPORT_SYMBOL_GPL(pci_ide_stream_setup);
> +
> +/**
> + * pci_ide_stream_teardown() - disable the stream and clear all settings
> + * @pdev: PCIe device object for either a Root Port or Endpoint Partner Port
> + * @ide: registered IDE settings descriptor
> + *
> + * For stream destruction, zero all registers that may have been written
> + * by pci_ide_stream_setup(). Consider pci_ide_stream_disable() to leave
> + * settings in place while temporarily disabling the stream.
> + */
> +void pci_ide_stream_teardown(struct pci_dev *pdev, struct pci_ide *ide)
> +{
> +	struct pci_ide_partner *settings = pci_ide_to_settings(pdev, ide);
> +	int pos;
> +
> +	if (!settings)
> +		return;
> +
> +	pos = sel_ide_offset(pdev, settings);
> +
> +	pci_write_config_dword(pdev, pos + PCI_IDE_SEL_CTL, 0);
> +	pci_write_config_dword(pdev, pos + PCI_IDE_SEL_RID_2, 0);
> +	pci_write_config_dword(pdev, pos + PCI_IDE_SEL_RID_1, 0);
> +	settings->setup = 0;
> +}
> +EXPORT_SYMBOL_GPL(pci_ide_stream_teardown);
> +
> +/**
> + * pci_ide_stream_enable() - try to enable a Selective IDE Stream
> + * @pdev: PCIe device object for either a Root Port or Endpoint Partner Port
> + * @ide: registered and setup IDE settings descriptor
> + *
> + * Activate the stream by writing to the Selective IDE Stream Control
> + * Register, report whether the state successfully transitioned to
> + * secure mode. Note that the state may go "insecure" at any point after
> + * this check, but that is handled via asynchronous error reporting.
> + */
> +int pci_ide_stream_enable(struct pci_dev *pdev, struct pci_ide *ide)
> +{
> +	struct pci_ide_partner *settings = pci_ide_to_settings(pdev, ide);
> +	int pos;
> +	u32 val;
> +
> +	if (!settings)
> +		return -ENXIO;
> +
> +	pos = sel_ide_offset(pdev, settings);
> +
> +	set_ide_sel_ctl(pdev, ide, pos, true);
> +
> +	pci_read_config_dword(pdev, pos + PCI_IDE_SEL_STS, &val);
> +	if (FIELD_GET(PCI_IDE_SEL_STS_STATE_MASK, val) !=
> +	    PCI_IDE_SEL_STS_STATE_SECURE) {
> +		set_ide_sel_ctl(pdev, ide, pos, false);
> +		return -ENXIO;
> +	}
> +
> +	settings->enable = 1;
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(pci_ide_stream_enable);
> +
> +/**
> + * pci_ide_stream_disable() - disable a Selective IDE Stream
> + * @pdev: PCIe device object for either a Root Port or Endpoint Partner Port
> + * @ide: registered and setup IDE settings descriptor
> + *
> + * Clear the Selective IDE Stream Control Register, but leave all other
> + * registers untouched.
> + */
> +void pci_ide_stream_disable(struct pci_dev *pdev, struct pci_ide *ide)
> +{
> +	struct pci_ide_partner *settings = pci_ide_to_settings(pdev, ide);
> +	int pos;
> +
> +	if (!settings)
> +		return;
> +
> +	pos = sel_ide_offset(pdev, settings);
> +
> +	pci_write_config_dword(pdev, pos + PCI_IDE_SEL_CTL, 0);
> +	settings->enable = 0;
> +}
> +EXPORT_SYMBOL_GPL(pci_ide_stream_disable);
> diff --git a/include/linux/pci-ide.h b/include/linux/pci-ide.h
> new file mode 100644
> index 000000000000..89c1ef0de841
> --- /dev/null
> +++ b/include/linux/pci-ide.h
> @@ -0,0 +1,70 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/* Copyright(c) 2024 Intel Corporation. All rights reserved. */
> +
> +/* PCIe 6.2 section 6.33 Integrity & Data Encryption (IDE) */
> +
> +#ifndef __PCI_IDE_H__
> +#define __PCI_IDE_H__
> +
> +enum pci_ide_partner_select {
> +	PCI_IDE_EP,
> +	PCI_IDE_RP,
> +	PCI_IDE_PARTNER_MAX,
> +	/*
> +	 * In addition to the resources in each partner port the
> +	 * platform / host-bridge additionally has a Stream ID pool that
> +	 * it shares across root ports. Let pci_ide_stream_alloc() use
> +	 * the alloc_stream_index() helper as endpoints and root ports.
> +	 */
> +	PCI_IDE_HB = PCI_IDE_PARTNER_MAX,
> +};
> +
> +/**
> + * struct pci_ide_partner - Per port pair Selective IDE Stream settings
> + * @rid_start: Partner Port Requester ID range start
> + * @rid_start: Partner Port Requester ID range end
> + * @stream_index: Selective IDE Stream Register Block selection
> + * @setup: flag to track whether to run pci_ide_stream_teardown for this parnter slot
> + * @enable: flag whether to run pci_ide_stream_disable for this parnter slot
> + */
> +struct pci_ide_partner {
> +	u16 rid_start;
> +	u16 rid_end;
> +	u8 stream_index;
> +	unsigned int setup:1;
> +	unsigned int enable:1;
> +};
> +
> +/**
> + * struct pci_ide - PCIe Selective IDE Stream descriptor
> + * @pdev: PCIe Endpoint in the pci_ide_partner pair
> + * @partner: Per-partner settings
> + * @host_bridge_stream: track platform Stream ID
> + * @stream_id: unique Stream ID (within Partner Port pairing)
> + * @name: name of the established Selective IDE Stream in sysfs
> + *
> + * Negative @stream_id values indicate "uninitialized" on the
> + * expectation that with TSM established IDE the TSM owns the stream_id
> + * allocation.
> + */
> +struct pci_ide {
> +	struct pci_dev *pdev;
> +	struct pci_ide_partner partner[PCI_IDE_PARTNER_MAX];
> +	u8 host_bridge_stream;
> +	int stream_id;
> +	const char *name;
> +};
> +
> +int pci_ide_domain(struct pci_dev *pdev);
> +struct pci_ide_partner *pci_ide_to_settings(struct pci_dev *pdev, struct pci_ide *ide);
> +struct pci_ide *pci_ide_stream_alloc(struct pci_dev *pdev);
> +void pci_ide_stream_free(struct pci_ide *ide);
> +int  pci_ide_stream_register(struct pci_ide *ide);
> +void pci_ide_stream_unregister(struct pci_ide *ide);
> +void pci_ide_stream_setup(struct pci_dev *pdev, struct pci_ide *ide);
> +void pci_ide_stream_teardown(struct pci_dev *pdev, struct pci_ide *ide);
> +int pci_ide_stream_enable(struct pci_dev *pdev, struct pci_ide *ide);
> +void pci_ide_stream_disable(struct pci_dev *pdev, struct pci_ide *ide);
> +void pci_ide_stream_release(struct pci_ide *ide);
> +DEFINE_FREE(pci_ide_stream_release, struct pci_ide *, if (_T) pci_ide_stream_release(_T))
> +#endif /* __PCI_IDE_H__ */
> diff --git a/include/linux/pci.h b/include/linux/pci.h
> index a7353df51fea..cc83ae274601 100644
> --- a/include/linux/pci.h
> +++ b/include/linux/pci.h
> @@ -538,6 +538,8 @@ struct pci_dev {
>  	u16		ide_cap;	/* Link Integrity & Data Encryption */
>  	u8		nr_ide_mem;	/* Address association resources for streams */
>  	u8		nr_link_ide;	/* Link Stream count (Selective Stream offset) */
> +	u8		nr_sel_ide;	/* Selective Stream count (register block allocator) */
> +	DECLARE_BITMAP(ide_stream_map, CONFIG_PCI_IDE_STREAM_MAX);
>  	unsigned int	ide_cfg:1;	/* Config cycles over IDE */
>  	unsigned int	ide_tee_limit:1; /* Disallow T=0 traffic over IDE */
>  #endif
> @@ -607,6 +609,10 @@ struct pci_host_bridge {
>  	int		domain_nr;
>  	struct list_head windows;	/* resource_entry */
>  	struct list_head dma_ranges;	/* dma ranges resource list */
> +#ifdef CONFIG_PCI_IDE
> +	u8 nr_ide_streams;		/* Track available vs in-use streams */
> +	DECLARE_BITMAP(ide_stream_map, CONFIG_PCI_IDE_STREAM_MAX);
> +#endif
>  	u8 (*swizzle_irq)(struct pci_dev *, u8 *); /* Platform IRQ swizzler */
>  	int (*map_irq)(const struct pci_dev *, u8, u8);
>  	void (*release_fn)(struct pci_host_bridge *);
> -- 
> 2.50.1
>
Re: [PATCH v4 07/10] PCI/IDE: Add IDE establishment helpers
Posted by Bjorn Helgaas 1 month, 4 weeks ago
On Thu, Jul 17, 2025 at 11:33:55AM -0700, Dan Williams wrote:
> There are two components to establishing an encrypted link, provisioning
> the stream in Partner Port config-space, and programming the keys into
> the link layer via IDE_KM (IDE Key Management). This new library,
> drivers/pci/ide.c, enables the former. IDE_KM, via a TSM low-level
> driver, is saved for later.
> 
> With the platform TSM implementations of SEV-TIO and TDX Connect in mind
> this library abstracts small differences in those implementations. For
> example, TDX Connect handles Root Port register setup while SEV-TIO
> expects System Software to update the Root Port registers. This is the
> rationale for fine-grained 'setup' + 'enable' verbs.
> 
> The other design detail for TSM-coordinated IDE establishment is that
> the TSM may manage allocation of Stream IDs, this is why the Stream ID
> value is passed in to pci_ide_stream_setup().
> 
> The flow is:
> 
> pci_ide_stream_alloc()
>   Allocate a Selective IDE Stream Register Block in each Partner Port
>   (Endpoint + Root Port), and reserve a host bridge / platform stream
>   slot. Gather Partner Port specific stream settings like Requester ID.
> pci_ide_stream_register()
>   Publish the stream in sysfs after allocating a Stream ID. In the TSM
>   case the TSM allocates the Stream ID for the Partner Port pair.
> pci_ide_stream_setup()
>   Program the stream settings to a Partner Port. Caller is responsible
>   for optionally calling this for the Root Port as well if the TSM
>   implementation requires it.
> pci_ide_stream_enable()
>   Try to run the stream after IDE_KM.

IIUC this patch doesn't actually add this as a "flow"; it adds these
interfaces, and I guess it's up to callers to use them in a way that
establishes this flow.

Maybe indent a couple spaces and add blank lines between them?

> In support of system administrators auditing where platform, Root Port,
> and Endpoint IDE stream resources are being spent, the allocated stream
> is reflected as a symlink from the host bridge to the endpoint with the
> name:
> 
>     stream%d.%d.%d
> 
> Where the tuple of integers reflects the allocated platform, Root Port,
> and Endpoint stream index (Selective IDE Stream Register Block) values.

> +++ b/Documentation/ABI/testing/sysfs-devices-pci-host-bridge
> +What:		pciDDDD:BB/streamH.R.E
> +Contact:	linux-pci@vger.kernel.org
> +Description:
> +		(RO) When a platform has established a secure connection, PCIe
> +		IDE, between two Partner Ports, this symlink appears. The
> +		primary function is to account the stream slot / resources
> +		consumed in each of the (H)ost bridge, (R)oot Port and
> +		(E)ndpoint that will be freed when invoking the tsm/disconnect
> +		flow. The link points to the endpoint PCI device in the
> +		Selective IDE Stream. "R" and "E" represent the assigned
> +		Selective IDE Stream Register Block in the Root Port and
> +		Endpoint, and "H" represents a platform specific pool of stream
> +		resources shared by the Root Ports in a host bridge. See
> +		/sys/devices/pciDDDD:BB entry for details about the DDDD:BB
> +		format.

s/tsm/TSM/
s/endpoint/Endpoint/

For "(H)ost bridge", "(R)oot Port",

  - Could use "Host bridge (H)", etc, which makes spell checkers work
    better (trivial, I know)

  - What's the format of these parts?  From the patch (and the commit
    log), it looks like they're decimal stream index values?  (I don't
    know enough to know what stream index values are, but presumably
    users will.)

> +++ b/drivers/pci/ide.c
> +int pci_ide_domain(struct pci_dev *pdev)
> +{
> +	if (pdev->fm_enabled)
> +		return pci_domain_nr(pdev->bus);
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(pci_ide_domain);

Not mentioned in commit log.  Maybe it doesn't need to be.  The only
call I see is in this file, so it looks like it could even be static.

> +/**
> + * pci_ide_stream_enable() - try to enable a Selective IDE Stream

Do or do not.  There is no try.

> + * @pdev: PCIe device object for either a Root Port or Endpoint Partner Port
> + * @ide: registered and setup IDE settings descriptor
> + *
> + * Activate the stream by writing to the Selective IDE Stream Control
> + * Register, report whether the state successfully transitioned to
> + * secure mode. Note that the state may go "insecure" at any point after
> + * this check, but that is handled via asynchronous error reporting.

Maybe recast this as "Return:" instead of "report whether ..."  At
least, I assume this reporting is done via the return value.

> + */
> +int pci_ide_stream_enable(struct pci_dev *pdev, struct pci_ide *ide)
> +{
> +	struct pci_ide_partner *settings = pci_ide_to_settings(pdev, ide);
> +	int pos;
> +	u32 val;
> +
> +	if (!settings)
> +		return -ENXIO;
> +
> +	pos = sel_ide_offset(pdev, settings);
> +
> +	set_ide_sel_ctl(pdev, ide, pos, true);
> +
> +	pci_read_config_dword(pdev, pos + PCI_IDE_SEL_STS, &val);
> +	if (FIELD_GET(PCI_IDE_SEL_STS_STATE_MASK, val) !=
> +	    PCI_IDE_SEL_STS_STATE_SECURE) {
> +		set_ide_sel_ctl(pdev, ide, pos, false);
> +		return -ENXIO;
> +	}
> +
> +	settings->enable = 1;
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(pci_ide_stream_enable);

> +++ b/include/linux/pci-ide.h
> + * struct pci_ide_partner - Per port pair Selective IDE Stream settings
> + * @rid_start: Partner Port Requester ID range start
> + * @rid_start: Partner Port Requester ID range end
> + * @stream_index: Selective IDE Stream Register Block selection
> + * @setup: flag to track whether to run pci_ide_stream_teardown for this parnter slot

Wrap to fit in 80 columns like the rest of the file.  Add "()" after
function name (below too).  Jonathan mentioned the "parnter".

> + * @enable: flag whether to run pci_ide_stream_disable for this parnter slot
Re: [PATCH v4 07/10] PCI/IDE: Add IDE establishment helpers
Posted by dan.j.williams@intel.com 1 month, 4 weeks ago
Bjorn Helgaas wrote:
> On Thu, Jul 17, 2025 at 11:33:55AM -0700, Dan Williams wrote:
> > There are two components to establishing an encrypted link, provisioning
> > the stream in Partner Port config-space, and programming the keys into
> > the link layer via IDE_KM (IDE Key Management). This new library,
> > drivers/pci/ide.c, enables the former. IDE_KM, via a TSM low-level
> > driver, is saved for later.
> > 
> > With the platform TSM implementations of SEV-TIO and TDX Connect in mind
> > this library abstracts small differences in those implementations. For
> > example, TDX Connect handles Root Port register setup while SEV-TIO
> > expects System Software to update the Root Port registers. This is the
> > rationale for fine-grained 'setup' + 'enable' verbs.
> > 
> > The other design detail for TSM-coordinated IDE establishment is that
> > the TSM may manage allocation of Stream IDs, this is why the Stream ID
> > value is passed in to pci_ide_stream_setup().
> > 
> > The flow is:
> > 
> > pci_ide_stream_alloc()
> >   Allocate a Selective IDE Stream Register Block in each Partner Port
> >   (Endpoint + Root Port), and reserve a host bridge / platform stream
> >   slot. Gather Partner Port specific stream settings like Requester ID.
> > pci_ide_stream_register()
> >   Publish the stream in sysfs after allocating a Stream ID. In the TSM
> >   case the TSM allocates the Stream ID for the Partner Port pair.
> > pci_ide_stream_setup()
> >   Program the stream settings to a Partner Port. Caller is responsible
> >   for optionally calling this for the Root Port as well if the TSM
> >   implementation requires it.
> > pci_ide_stream_enable()
> >   Try to run the stream after IDE_KM.
> 
> IIUC this patch doesn't actually add this as a "flow"; it adds these
> interfaces, and I guess it's up to callers to use them in a way that
> establishes this flow.

Right, common helpers for low-level TSM drivers to use with an example
of such a driver (without all the arch specific complexities) in
samples/devsec/.

> Maybe indent a couple spaces and add blank lines between them?

Ok.

> 
> > In support of system administrators auditing where platform, Root Port,
> > and Endpoint IDE stream resources are being spent, the allocated stream
> > is reflected as a symlink from the host bridge to the endpoint with the
> > name:
> > 
> >     stream%d.%d.%d
> > 
> > Where the tuple of integers reflects the allocated platform, Root Port,
> > and Endpoint stream index (Selective IDE Stream Register Block) values.
> 
> > +++ b/Documentation/ABI/testing/sysfs-devices-pci-host-bridge
> > +What:		pciDDDD:BB/streamH.R.E
> > +Contact:	linux-pci@vger.kernel.org
> > +Description:
> > +		(RO) When a platform has established a secure connection, PCIe
> > +		IDE, between two Partner Ports, this symlink appears. The
> > +		primary function is to account the stream slot / resources
> > +		consumed in each of the (H)ost bridge, (R)oot Port and
> > +		(E)ndpoint that will be freed when invoking the tsm/disconnect
> > +		flow. The link points to the endpoint PCI device in the
> > +		Selective IDE Stream. "R" and "E" represent the assigned
> > +		Selective IDE Stream Register Block in the Root Port and
> > +		Endpoint, and "H" represents a platform specific pool of stream
> > +		resources shared by the Root Ports in a host bridge. See
> > +		/sys/devices/pciDDDD:BB entry for details about the DDDD:BB
> > +		format.
> 
> s/tsm/TSM/
> s/endpoint/Endpoint/
> 
> For "(H)ost bridge", "(R)oot Port",
> 
>   - Could use "Host bridge (H)", etc, which makes spell checkers work
>     better (trivial, I know)
> 
>   - What's the format of these parts?  From the patch (and the commit
>     log), it looks like they're decimal stream index values?  (I don't
>     know enough to know what stream index values are, but presumably
>     users will.)

I clarified that a bit:

"A stream consumes a Stream ID slot in each of the Host bridge (H), Root
Port (R) and Endpoint (E)"

Presumably users that are debugging why they are unable to establish any
more streams can use this to discover, for example, "oh, I have resources available
in my Host Bridge and Endpoint, but the Root Port is out of Stream
slots".

> 
> > +++ b/drivers/pci/ide.c
> > +int pci_ide_domain(struct pci_dev *pdev)
> > +{
> > +	if (pdev->fm_enabled)
> > +		return pci_domain_nr(pdev->bus);
> > +	return 0;
> > +}
> > +EXPORT_SYMBOL_GPL(pci_ide_domain);
> 
> Not mentioned in commit log.  Maybe it doesn't need to be.  The only
> call I see is in this file, so it looks like it could even be static.

True, not sure why I thought this would be consumed by TSM drivers.
Fixed.

> 
> > +/**
> > + * pci_ide_stream_enable() - try to enable a Selective IDE Stream
> 
> Do or do not.  There is no try.

Ha! It does always enable, it just may immediately transition to the
error state if one of the partners is upset about something.

> > + * @pdev: PCIe device object for either a Root Port or Endpoint Partner Port
> > + * @ide: registered and setup IDE settings descriptor
> > + *
> > + * Activate the stream by writing to the Selective IDE Stream Control
> > + * Register, report whether the state successfully transitioned to
> > + * secure mode. Note that the state may go "insecure" at any point after
> > + * this check, but that is handled via asynchronous error reporting.
> 
> Maybe recast this as "Return:" instead of "report whether ..."  At
> least, I assume this reporting is done via the return value.

Yup, that is better.

> 
> > + */
> > +int pci_ide_stream_enable(struct pci_dev *pdev, struct pci_ide *ide)
> > +{
> > +	struct pci_ide_partner *settings = pci_ide_to_settings(pdev, ide);
> > +	int pos;
> > +	u32 val;
> > +
> > +	if (!settings)
> > +		return -ENXIO;
> > +
> > +	pos = sel_ide_offset(pdev, settings);
> > +
> > +	set_ide_sel_ctl(pdev, ide, pos, true);
> > +
> > +	pci_read_config_dword(pdev, pos + PCI_IDE_SEL_STS, &val);
> > +	if (FIELD_GET(PCI_IDE_SEL_STS_STATE_MASK, val) !=
> > +	    PCI_IDE_SEL_STS_STATE_SECURE) {
> > +		set_ide_sel_ctl(pdev, ide, pos, false);
> > +		return -ENXIO;
> > +	}
> > +
> > +	settings->enable = 1;
> > +	return 0;
> > +}
> > +EXPORT_SYMBOL_GPL(pci_ide_stream_enable);
> 
> > +++ b/include/linux/pci-ide.h
> > + * struct pci_ide_partner - Per port pair Selective IDE Stream settings
> > + * @rid_start: Partner Port Requester ID range start
> > + * @rid_start: Partner Port Requester ID range end
> > + * @stream_index: Selective IDE Stream Register Block selection
> > + * @setup: flag to track whether to run pci_ide_stream_teardown for this parnter slot
> 
> Wrap to fit in 80 columns like the rest of the file.  Add "()" after
> function name (below too).  Jonathan mentioned the "parnter".

Done.
Re: [PATCH v4 07/10] PCI/IDE: Add IDE establishment helpers
Posted by Jonathan Cameron 2 months, 1 week ago
On Thu, 17 Jul 2025 11:33:55 -0700
Dan Williams <dan.j.williams@intel.com> wrote:

> There are two components to establishing an encrypted link, provisioning
> the stream in Partner Port config-space, and programming the keys into
> the link layer via IDE_KM (IDE Key Management). This new library,
> drivers/pci/ide.c, enables the former. IDE_KM, via a TSM low-level
> driver, is saved for later.
> 
> With the platform TSM implementations of SEV-TIO and TDX Connect in mind
> this library abstracts small differences in those implementations. For
> example, TDX Connect handles Root Port register setup while SEV-TIO
> expects System Software to update the Root Port registers. This is the
> rationale for fine-grained 'setup' + 'enable' verbs.
> 
> The other design detail for TSM-coordinated IDE establishment is that
> the TSM may manage allocation of Stream IDs, this is why the Stream ID
> value is passed in to pci_ide_stream_setup().
> 
> The flow is:
> 
> pci_ide_stream_alloc()
>   Allocate a Selective IDE Stream Register Block in each Partner Port
>   (Endpoint + Root Port), and reserve a host bridge / platform stream
>   slot. Gather Partner Port specific stream settings like Requester ID.
> pci_ide_stream_register()
>   Publish the stream in sysfs after allocating a Stream ID. In the TSM
>   case the TSM allocates the Stream ID for the Partner Port pair.
> pci_ide_stream_setup()
>   Program the stream settings to a Partner Port. Caller is responsible
>   for optionally calling this for the Root Port as well if the TSM
>   implementation requires it.
> pci_ide_stream_enable()
>   Try to run the stream after IDE_KM.
> 
> In support of system administrators auditing where platform, Root Port,
> and Endpoint IDE stream resources are being spent, the allocated stream
> is reflected as a symlink from the host bridge to the endpoint with the
> name:
> 
>     stream%d.%d.%d
> 
> Where the tuple of integers reflects the allocated platform, Root Port,
> and Endpoint stream index (Selective IDE Stream Register Block) values.
> 
> Thanks to Wu Hao for a draft implementation of this infrastructure.
> 
> Cc: Bjorn Helgaas <bhelgaas@google.com>
> Cc: Lukas Wunner <lukas@wunner.de>
> Cc: Samuel Ortiz <sameo@rivosinc.com>
> Co-developed-by: Alexey Kardashevskiy <aik@amd.com>
> Signed-off-by: Alexey Kardashevskiy <aik@amd.com>
> Co-developed-by: Yilun Xu <yilun.xu@linux.intel.com>
> Signed-off-by: Yilun Xu <yilun.xu@linux.intel.com>
> Signed-off-by: Dan Williams <dan.j.williams@intel.com>

A few minor things inline.

> diff --git a/drivers/pci/ide.c b/drivers/pci/ide.c
> index e15937cdb2a4..cdc773a8b381 100644
> --- a/drivers/pci/ide.c
> +++ b/drivers/pci/ide.c
> @@ -5,6 +5,8 @@
>  


> +/**
> + * pci_ide_stream_enable() - try to enable a Selective IDE Stream
> + * @pdev: PCIe device object for either a Root Port or Endpoint Partner Port
> + * @ide: registered and setup IDE settings descriptor
> + *
> + * Activate the stream by writing to the Selective IDE Stream Control
> + * Register, report whether the state successfully transitioned to
> + * secure mode.
and report

> ... Note that the state may go "insecure" at any point after
> + * this check, but that is handled via asynchronous error reporting.
> + */
> +int pci_ide_stream_enable(struct pci_dev *pdev, struct pci_ide *ide)
> +{
> +	struct pci_ide_partner *settings = pci_ide_to_settings(pdev, ide);
> +	int pos;
> +	u32 val;
> +
> +	if (!settings)
> +		return -ENXIO;
> +
> +	pos = sel_ide_offset(pdev, settings);
> +
> +	set_ide_sel_ctl(pdev, ide, pos, true);
> +
> +	pci_read_config_dword(pdev, pos + PCI_IDE_SEL_STS, &val);
> +	if (FIELD_GET(PCI_IDE_SEL_STS_STATE_MASK, val) !=
> +	    PCI_IDE_SEL_STS_STATE_SECURE) {
> +		set_ide_sel_ctl(pdev, ide, pos, false);
> +		return -ENXIO;
> +	}
> +
> +	settings->enable = 1;
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(pci_ide_stream_enable);

> diff --git a/include/linux/pci-ide.h b/include/linux/pci-ide.h
> new file mode 100644
> index 000000000000..89c1ef0de841
> --- /dev/null
> +++ b/include/linux/pci-ide.h
> @@ -0,0 +1,70 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/* Copyright(c) 2024 Intel Corporation. All rights reserved. */
...

> +/**
> + * struct pci_ide_partner - Per port pair Selective IDE Stream settings
> + * @rid_start: Partner Port Requester ID range start
> + * @rid_start: Partner Port Requester ID range end
> + * @stream_index: Selective IDE Stream Register Block selection
> + * @setup: flag to track whether to run pci_ide_stream_teardown for this parnter slot

partner.

> + * @enable: flag whether to run pci_ide_stream_disable for this parnter slot

same again.

> + */
> +struct pci_ide_partner {
> +	u16 rid_start;
> +	u16 rid_end;
> +	u8 stream_index;
> +	unsigned int setup:1;
> +	unsigned int enable:1;
> +};
> +
> +/**
> + * struct pci_ide - PCIe Selective IDE Stream descriptor
> + * @pdev: PCIe Endpoint in the pci_ide_partner pair
> + * @partner: Per-partner settings
per-partner maybe?  Capitalization seems a little random
as mostly you have used them for spec terms, but Per-partner probably
isn't one?

> + * @host_bridge_stream: track platform Stream ID
> + * @stream_id: unique Stream ID (within Partner Port pairing)
> + * @name: name of the established Selective IDE Stream in sysfs
> + *
> + * Negative @stream_id values indicate "uninitialized" on the
> + * expectation that with TSM established IDE the TSM owns the stream_id
> + * allocation.
> + */
> +struct pci_ide {
> +	struct pci_dev *pdev;
> +	struct pci_ide_partner partner[PCI_IDE_PARTNER_MAX];
> +	u8 host_bridge_stream;
> +	int stream_id;
> +	const char *name;
> +};

> diff --git a/include/linux/pci.h b/include/linux/pci.h
> index a7353df51fea..cc83ae274601 100644
> --- a/include/linux/pci.h
> +++ b/include/linux/pci.h
> @@ -538,6 +538,8 @@ struct pci_dev {
>  	u16		ide_cap;	/* Link Integrity & Data Encryption */
>  	u8		nr_ide_mem;	/* Address association resources for streams */
>  	u8		nr_link_ide;	/* Link Stream count (Selective Stream offset) */
> +	u8		nr_sel_ide;	/* Selective Stream count (register block allocator) */
> +	DECLARE_BITMAP(ide_stream_map, CONFIG_PCI_IDE_STREAM_MAX);
>  	unsigned int	ide_cfg:1;	/* Config cycles over IDE */
>  	unsigned int	ide_tee_limit:1; /* Disallow T=0 traffic over IDE */
>  #endif
> @@ -607,6 +609,10 @@ struct pci_host_bridge {
>  	int		domain_nr;
>  	struct list_head windows;	/* resource_entry */
>  	struct list_head dma_ranges;	/* dma ranges resource list */
> +#ifdef CONFIG_PCI_IDE
> +	u8 nr_ide_streams;		/* Track available vs in-use streams */

Which does it do?  Confusing comment.

> +	DECLARE_BITMAP(ide_stream_map, CONFIG_PCI_IDE_STREAM_MAX);
> +#endif
Re: [PATCH v4 07/10] PCI/IDE: Add IDE establishment helpers
Posted by dan.j.williams@intel.com 2 months ago
Jonathan Cameron wrote:
[..]
> A few minor things inline.
[..]
> > +/**
> > + * pci_ide_stream_enable() - try to enable a Selective IDE Stream
> > + * @pdev: PCIe device object for either a Root Port or Endpoint Partner Port
> > + * @ide: registered and setup IDE settings descriptor
> > + *
> > + * Activate the stream by writing to the Selective IDE Stream Control
> > + * Register, report whether the state successfully transitioned to
> > + * secure mode.
> and report

ack.

[..]
> > diff --git a/include/linux/pci-ide.h b/include/linux/pci-ide.h
> > new file mode 100644
> > index 000000000000..89c1ef0de841
> > --- /dev/null
> > +++ b/include/linux/pci-ide.h
> > @@ -0,0 +1,70 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +/* Copyright(c) 2024 Intel Corporation. All rights reserved. */
> ...
> 
> > +/**
> > + * struct pci_ide_partner - Per port pair Selective IDE Stream settings
> > + * @rid_start: Partner Port Requester ID range start
> > + * @rid_start: Partner Port Requester ID range end
> > + * @stream_index: Selective IDE Stream Register Block selection
> > + * @setup: flag to track whether to run pci_ide_stream_teardown for this parnter slot
> 
> partner.
> 
> > + * @enable: flag whether to run pci_ide_stream_disable for this parnter slot
> 
> same again.

yes.

> > +/**
> > + * struct pci_ide - PCIe Selective IDE Stream descriptor
> > + * @pdev: PCIe Endpoint in the pci_ide_partner pair
> > + * @partner: Per-partner settings
> per-partner maybe?  Capitalization seems a little random
> as mostly you have used them for spec terms, but Per-partner probably
> isn't one?

true.

> > + * @host_bridge_stream: track platform Stream ID
> > + * @stream_id: unique Stream ID (within Partner Port pairing)
> > + * @name: name of the established Selective IDE Stream in sysfs
> > + *
> > + * Negative @stream_id values indicate "uninitialized" on the
> > + * expectation that with TSM established IDE the TSM owns the stream_id
> > + * allocation.
> > + */
> > +struct pci_ide {
> > +	struct pci_dev *pdev;
> > +	struct pci_ide_partner partner[PCI_IDE_PARTNER_MAX];
> > +	u8 host_bridge_stream;
> > +	int stream_id;
> > +	const char *name;
> > +};
> 
> > diff --git a/include/linux/pci.h b/include/linux/pci.h
> > index a7353df51fea..cc83ae274601 100644
> > --- a/include/linux/pci.h
> > +++ b/include/linux/pci.h
> > @@ -538,6 +538,8 @@ struct pci_dev {
> >  	u16		ide_cap;	/* Link Integrity & Data Encryption */
> >  	u8		nr_ide_mem;	/* Address association resources for streams */
> >  	u8		nr_link_ide;	/* Link Stream count (Selective Stream offset) */
> > +	u8		nr_sel_ide;	/* Selective Stream count (register block allocator) */
> > +	DECLARE_BITMAP(ide_stream_map, CONFIG_PCI_IDE_STREAM_MAX);
> >  	unsigned int	ide_cfg:1;	/* Config cycles over IDE */
> >  	unsigned int	ide_tee_limit:1; /* Disallow T=0 traffic over IDE */
> >  #endif
> > @@ -607,6 +609,10 @@ struct pci_host_bridge {
> >  	int		domain_nr;
> >  	struct list_head windows;	/* resource_entry */
> >  	struct list_head dma_ranges;	/* dma ranges resource list */
> > +#ifdef CONFIG_PCI_IDE
> > +	u8 nr_ide_streams;		/* Track available vs in-use streams */
> 
> Which does it do?  Confusing comment.

Oh, true, I was going for a combo comment for nr_ide_streams and
ide_stream_map, but missed on the clarity. Make that relationship
clearer:

-       u8 nr_ide_streams;              /* Track available vs in-use streams */
+       u8 nr_ide_streams; /* Max streams possibly active in @ide_stream_map */