diff mbox series

[RFC,2/2] PCI: Add generic netlink interface to TPH _DSM

Message ID 20250106163045.508959-3-wathsala.vithanage@arm.com
State New
Headers show
Series GENL interface for ACPI _DSM methods | expand

Commit Message

Wathsala Vithanage Jan. 6, 2025, 4:30 p.m. UTC
Applications capable of configuring direct cache injection for PCIe
devices from the user space require steering tags extracted from the
PCIe root port’s TPH _DSM method. This patch exposes the TPH _DSM method
via the ACPI GENL interface for such applications. The messages sent
over the GNEL interface querying PCIe steering tags should be prefixed
with an acpi_genl_dsm_id header and PCI domain, slot, and function
information, as shown in the message format diagram. PKG ARG0, ARG1, and
ARG2 correspond to input structures defined in PCIe firmware
specification ECN titled "Revised _DSM for Cache Locality TPH Features."

    0       1       2       3       4       5       6       7
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                                                               |
   +                     acpi_genl_dsm_id header                   +
   |                                                               |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
16 |           PCI DOMAIN          |           PCI SLOT            |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |           PCI DEVFN           |           PKG ARG0            |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |           PKG ARG1            |           PKG ARG2            |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                               |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Kernel responds via the same GENL socket with the steering tag info
and a status code in the following format. Steering tag info is the same
structure returned by TPH _DSM of the root port as specified in the PCI
firmware specification ECN titled "Revised _DSM for Cache Locality TPH
Features."

    0       1       2       3       4       5       6       7
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                                                               |
   +                     acpi_genl_dsm_id header                   +
   |                                                               |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
16 |            Status             |       Steering Tag Info       |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                               |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

The status code is always 0 for successful invocations of the _DSM.
Status is -EINVAL, -ENODEV, or -EOPNOTSUPP if ACPI or TPH is disabled,
No PCI device found or TPH is not supported respectively.

Signed-off-by: Wathsala Vithanage <wathsala.vithanage@arm.com>
---
 drivers/pci/tph.c | 119 ++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 119 insertions(+)
diff mbox series

Patch

diff --git a/drivers/pci/tph.c b/drivers/pci/tph.c
index 1e604fbbda65..8a70b663cb78 100644
--- a/drivers/pci/tph.c
+++ b/drivers/pci/tph.c
@@ -53,6 +53,36 @@  union st_info {
 	u64 value;
 };
 
+/*
+ * The tph_genl_dsm_info struct defines both the input and the return container
+ * for Generic Netlink interface of the TPH _DSM of PCIe root ports.
+ */
+struct tph_genl_dsm_info {
+	/* _DSM method identfier {GUID, REV, FUNCTION-INDEX}*/
+	struct acpi_genl_dsm_id id;
+	union {
+		struct {
+			/* bus, dev, devfn of the PCIe device */
+			int bus;
+			u32 dev;
+			u32 devfn;
+			/*
+			 * args for the TPH _DSM of the PCIe root-port of
+			 * the specified device.
+			 */
+			u32 pkg_arg0;
+			u32 pkg_arg1;
+			u64 pkg_arg2;
+		} arg;
+		struct {
+			/* return status */
+			int status;
+			/* st_info returned by the PCIe root-port TPH _DSM */
+			union st_info st;
+		} ret;
+	} info;
+};
+
 static u16 tph_extract_tag(enum tph_mem_type mem_type, u8 req_type,
 			   union st_info *info)
 {
@@ -130,6 +160,95 @@  static acpi_status tph_invoke_dsm(acpi_handle handle, u32 cpu_uid,
 
 	return AE_OK;
 }
+
+static int tph_invoke_dsm_genl_cb(struct acpi_genl_dsm_id *in,
+				  struct acpi_genl_dsm_id *out)
+{
+	struct pci_dev *pdev, *pdev_rp;
+	acpi_handle handle;
+	u32 cpu_uid;
+	int status = 0;
+
+	struct tph_genl_dsm_info *arg = (struct tph_genl_dsm_info *)in;
+	struct tph_genl_dsm_info *ret = (struct tph_genl_dsm_info *)out;
+
+	/* Honor  notph and acpi kernel parameters */
+	if (acpi_disabled || acpi_pci_disabled || pci_tph_disabled) {
+		status = -EINVAL;
+		goto out;
+	}
+
+	/*
+	 * pkg_arg1 contains the kernel logical CPU id provided by the user,
+	 * make sure it's a valid CPU id before passing it to down to firmware.
+	 * pkg_arg2 is not use by tph_invoke_dsm, hence no validation is
+	 * required.
+	 */
+	if (!(arg->info.arg.pkg_arg1 < nr_cpu_ids &&
+	    cpu_present(arg->info.arg.pkg_arg1))) {
+		status = -EINVAL;
+		goto out;
+	}
+
+	cpu_uid = topology_core_id(arg->info.arg.pkg_arg1);
+
+	ret->id.guid = pci_acpi_dsm_guid;
+	ret->id.rev = 7;
+	ret->id.func = TPH_ST_DSM_FUNC_INDEX;
+
+	pdev = pci_get_domain_bus_and_slot(arg->info.arg.bus, arg->info.arg.dev,
+					   arg->info.arg.devfn);
+	if (!pdev) {
+		status = -ENODEV;
+		goto out;
+	}
+
+	pdev_rp = pcie_find_root_port(pdev);
+	if (!pdev_rp || !pdev_rp->bus || !pdev_rp->bus->bridge) {
+		status = -ENODEV;
+		goto out;
+	}
+
+	handle = ACPI_HANDLE(pdev_rp->bus->bridge);
+
+	if (tph_invoke_dsm(handle, arg->info.arg.pkg_arg1, &ret->info.ret.st) !=
+			   AE_OK) {
+		status = -EOPNOTSUPP;
+		goto out;
+	}
+out:
+	ret->info.ret.status = status;
+	return 0;
+}
+
+static int tph_register_genl_cb(void)
+{
+	int err = 0;
+	struct acpi_genl_dsm_handle *handle =
+		kzalloc(sizeof(struct acpi_genl_dsm_handle), GFP_ATOMIC);
+	if (!handle) {
+		err = -ENOMEM;
+		goto out;
+	}
+
+	handle->id.guid = pci_acpi_dsm_guid;
+	handle->id.rev  = 7;
+	handle->id.func = TPH_ST_DSM_FUNC_INDEX;
+	handle->arg_len = sizeof(struct tph_genl_dsm_info);
+	handle->ret_len = sizeof(struct tph_genl_dsm_info);
+	handle->dsm_cb  = tph_invoke_dsm_genl_cb;
+	handle->cap     = CAP_SYS_ADMIN;
+
+	err = acpi_genl_dsm_add_handle(handle);
+	if (err) {
+		kfree(handle);
+		goto out;
+	}
+out:
+	return err;
+}
+
+late_initcall(tph_register_genl_cb);
 #endif
 
 /* Update the TPH Requester Enable field of TPH Control Register */