lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20250206141936.1117222-2-heikki.krogerus@linux.intel.com>
Date: Thu,  6 Feb 2025 16:19:31 +0200
From: Heikki Krogerus <heikki.krogerus@...ux.intel.com>
To: Ɓukasz Bartosik <ukaszb@...omium.org>
Cc: Abhishek Pandit-Subedi <abhishekpandit@...omium.org>,
	Benson Leung <bleung@...omium.org>,
	Pavan Holla <pholla@...omium.org>,
	Dmitry Baryshkov <dmitry.baryshkov@...aro.org>,
	"Christian A. Ehrhardt" <lk@...e.de>,
	Jameson Thies <jthies@...gle.com>,
	"Katiyar, Pooja" <pooja.katiyar@...el.com>,
	"Pathak, Asutosh" <asutosh.pathak@...el.com>,
	"Jayaraman, Venkat" <venkat.jayaraman@...el.com>,
	Greg Kroah-Hartman <gregkh@...uxfoundation.org>,
	linux-usb@...r.kernel.org,
	linux-kernel@...r.kernel.org
Subject: [PATCH v1 1/2] usb: typec: ucsi: Command mailbox interface for the userspace

Some of the UCSI commands can be used to configure the
entire Platform Policy Manager (PPM) instead of just
individual connectors. To allow the user space communicate
those commands with the PPM, adding a mailbox interface. The
interface is a single attribute file that represents the
main "OPM to PPM" UCSI data structure.

The mailbox allows any UCSI command to be sent to the PPM so
it should be also useful for validation, testing and
debugging purposes.

Signed-off-by: Heikki Krogerus <heikki.krogerus@...ux.intel.com>
---
 Documentation/ABI/testing/sysfs-driver-ucsi |  20 +++
 drivers/usb/typec/ucsi/Makefile             |   2 +-
 drivers/usb/typec/ucsi/sysfs.c              | 127 ++++++++++++++++++++
 drivers/usb/typec/ucsi/ucsi.c               |  31 +++--
 drivers/usb/typec/ucsi/ucsi.h               |   7 ++
 5 files changed, 173 insertions(+), 14 deletions(-)
 create mode 100644 Documentation/ABI/testing/sysfs-driver-ucsi
 create mode 100644 drivers/usb/typec/ucsi/sysfs.c

diff --git a/Documentation/ABI/testing/sysfs-driver-ucsi b/Documentation/ABI/testing/sysfs-driver-ucsi
new file mode 100644
index 000000000000..9da15577f4ae
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-driver-ucsi
@@ -0,0 +1,20 @@
+What:		/sys/class/typec/<port>/device/ucsi
+Date:		February 2025
+Contact:	Heikki Krogerus <heikki.krogerus@...ux.intel.com>
+Description:
+		Command mailbox for UCSI (USB Type-C System Software Interface).
+
+		The mailbox contains a copy of the main UCSI data structure.
+		Sending a command happens by writing the command specific data
+		structure to the CONTROL offset just like defined in the UCSI
+		specification. When a command is written to the mailbox, it is
+		automatically forwarded to the Platform Policy Manager (PPM) of
+		the UCSI instance.
+
+		After writing the command to the mailbox, the result can be read
+		directly from the CCI and MESSAGE_IN offsets. The mailbox takes
+		care of command completion acknowledges automatically.
+
+		Note. The mailbox is meant, and can only be used for, sending
+		commands. I.e. the mailbox is not updated when the UCSI receives
+		asynchronous events.
diff --git a/drivers/usb/typec/ucsi/Makefile b/drivers/usb/typec/ucsi/Makefile
index be98a879104d..5ebc74e3055b 100644
--- a/drivers/usb/typec/ucsi/Makefile
+++ b/drivers/usb/typec/ucsi/Makefile
@@ -3,7 +3,7 @@ CFLAGS_trace.o				:= -I$(src)
 
 obj-$(CONFIG_TYPEC_UCSI)		+= typec_ucsi.o
 
-typec_ucsi-y				:= ucsi.o
+typec_ucsi-y				:= ucsi.o sysfs.o
 
 typec_ucsi-$(CONFIG_DEBUG_FS)		+= debugfs.o
 
diff --git a/drivers/usb/typec/ucsi/sysfs.c b/drivers/usb/typec/ucsi/sysfs.c
new file mode 100644
index 000000000000..06ea1b54aefa
--- /dev/null
+++ b/drivers/usb/typec/ucsi/sysfs.c
@@ -0,0 +1,127 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * UCSI sysfs mailbox.
+ *
+ * Copyright (C) 2025, Intel Corporation
+ */
+
+#include <linux/mutex.h>
+#include <linux/overflow.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/unaligned.h>
+#include "ucsi.h"
+
+#define UCSI_MAILBOX_SIZE(ucsi) ((ucsi)->version < UCSI_VERSION_2_0 ? 48 : 528)
+
+struct ucsi_sysfs {
+	struct bin_attribute	bin_attr;
+	struct ucsi		*ucsi;
+	struct mutex		lock; /* mailbox lock */
+	u8			mailbox[];
+};
+
+static ssize_t ucsi_read(struct file *filp, struct kobject *kobj,
+			 const struct bin_attribute *attr,
+			 char *buf, loff_t off, size_t count)
+{
+	struct ucsi_sysfs *sysfs = attr->private;
+
+	mutex_lock(&sysfs->lock);
+	memcpy(buf, sysfs->mailbox + off, count);
+	mutex_unlock(&sysfs->lock);
+
+	return count;
+}
+
+static ssize_t ucsi_write(struct file *filp, struct kobject *kobj,
+			  const struct bin_attribute *attr,
+			  char *buf, loff_t off, size_t count)
+{
+	struct ucsi_sysfs *sysfs = attr->private;
+	struct ucsi *ucsi = sysfs->ucsi;
+	int ret;
+
+	u64 *control = (u64 *)&sysfs->mailbox[UCSI_CONTROL];
+	u32 *cci = (u32 *)&sysfs->mailbox[UCSI_CCI];
+	void *data = &sysfs->mailbox[UCSI_MESSAGE_IN];
+
+	/* TODO: MESSAGE_OUT. */
+	if (off != UCSI_CONTROL || count != sizeof(*control))
+		return -EFAULT;
+
+	mutex_lock(&sysfs->lock);
+
+	memset(data, 0, UCSI_MAX_DATA_LENGTH(ucsi));
+
+	/* PPM_RESET has to be handled separately. */
+	*control = get_unaligned_le64(buf);
+	if (UCSI_COMMAND(*control) == UCSI_PPM_RESET) {
+		ret = ucsi_reset_ppm(ucsi, cci);
+		goto out_unlock_sysfs;
+	}
+
+	mutex_lock(&ucsi->ppm_lock);
+
+	ret = ucsi->ops->sync_control(ucsi, *control, cci, NULL, 0);
+	if (ret)
+		goto out_unlock_ppm;
+
+	if (UCSI_CCI_LENGTH(*cci) && ucsi->ops->read_message_in(ucsi, data, UCSI_CCI_LENGTH(*cci)))
+		dev_err(ucsi->dev, "failed to read MESSAGE_IN\n");
+
+	ret = ucsi->ops->sync_control(ucsi, UCSI_ACK_CC_CI | UCSI_ACK_COMMAND_COMPLETE,
+				      NULL, NULL, 0);
+out_unlock_ppm:
+	mutex_unlock(&ucsi->ppm_lock);
+out_unlock_sysfs:
+	mutex_unlock(&sysfs->lock);
+
+	return ret ?: count;
+}
+
+int ucsi_sysfs_register(struct ucsi *ucsi)
+{
+	struct ucsi_sysfs *sysfs;
+	int ret;
+
+	sysfs = kzalloc(struct_size(sysfs, mailbox, UCSI_MAILBOX_SIZE(ucsi)), GFP_KERNEL);
+	if (!sysfs)
+		return -ENOMEM;
+
+	sysfs->ucsi = ucsi;
+	mutex_init(&sysfs->lock);
+	memcpy(sysfs->mailbox, &ucsi->version, sizeof(ucsi->version));
+
+	sysfs_bin_attr_init(&sysfs->bin_attr);
+
+	sysfs->bin_attr.attr.name = "ucsi";
+	sysfs->bin_attr.attr.mode = 0644;
+
+	sysfs->bin_attr.size = UCSI_MAILBOX_SIZE(ucsi);
+	sysfs->bin_attr.private = sysfs;
+	sysfs->bin_attr.read_new = ucsi_read;
+	sysfs->bin_attr.write_new = ucsi_write;
+
+	ret = sysfs_create_bin_file(&ucsi->dev->kobj, &sysfs->bin_attr);
+	if (ret)
+		kfree(sysfs);
+	else
+		ucsi->sysfs = sysfs;
+
+	return ret;
+}
+
+void ucsi_sysfs_unregister(struct ucsi *ucsi)
+{
+	struct ucsi_sysfs *sysfs = ucsi->sysfs;
+
+	if (!sysfs)
+		return;
+
+	sysfs_remove_bin_file(&ucsi->dev->kobj, &sysfs->bin_attr);
+	ucsi->sysfs = NULL;
+	kfree(sysfs);
+}
diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c
index 559390a07a4e..9dadfe879319 100644
--- a/drivers/usb/typec/ucsi/ucsi.c
+++ b/drivers/usb/typec/ucsi/ucsi.c
@@ -1340,16 +1340,18 @@ static int ucsi_reset_connector(struct ucsi_connector *con, bool hard)
 	return ucsi_send_command(con->ucsi, command, NULL, 0);
 }
 
-static int ucsi_reset_ppm(struct ucsi *ucsi)
+int ucsi_reset_ppm(struct ucsi *ucsi, u32 *cci)
 {
 	u64 command;
 	unsigned long tmo;
-	u32 cci;
+	u32 _cci;
 	int ret;
 
+	cci = cci ?: &_cci;
+
 	mutex_lock(&ucsi->ppm_lock);
 
-	ret = ucsi->ops->read_cci(ucsi, &cci);
+	ret = ucsi->ops->read_cci(ucsi, cci);
 	if (ret < 0)
 		goto out;
 
@@ -1359,7 +1361,7 @@ static int ucsi_reset_ppm(struct ucsi *ucsi)
 	 * UCSI_SET_NOTIFICATION_ENABLE command to achieve this.
 	 * Ignore a timeout and try the reset anyway if this fails.
 	 */
-	if (cci & UCSI_CCI_RESET_COMPLETE) {
+	if (*cci & UCSI_CCI_RESET_COMPLETE) {
 		command = UCSI_SET_NOTIFICATION_ENABLE;
 		ret = ucsi->ops->async_control(ucsi, command);
 		if (ret < 0)
@@ -1367,17 +1369,17 @@ static int ucsi_reset_ppm(struct ucsi *ucsi)
 
 		tmo = jiffies + msecs_to_jiffies(UCSI_TIMEOUT_MS);
 		do {
-			ret = ucsi->ops->read_cci(ucsi, &cci);
+			ret = ucsi->ops->read_cci(ucsi, cci);
 			if (ret < 0)
 				goto out;
-			if (cci & UCSI_CCI_COMMAND_COMPLETE)
+			if (*cci & UCSI_CCI_COMMAND_COMPLETE)
 				break;
 			if (time_is_before_jiffies(tmo))
 				break;
 			msleep(20);
 		} while (1);
 
-		WARN_ON(cci & UCSI_CCI_RESET_COMPLETE);
+		WARN_ON(*cci & UCSI_CCI_RESET_COMPLETE);
 	}
 
 	command = UCSI_PPM_RESET;
@@ -1396,18 +1398,18 @@ static int ucsi_reset_ppm(struct ucsi *ucsi)
 		/* Give the PPM time to process a reset before reading CCI */
 		msleep(20);
 
-		ret = ucsi->ops->read_cci(ucsi, &cci);
+		ret = ucsi->ops->read_cci(ucsi, cci);
 		if (ret)
 			goto out;
 
 		/* If the PPM is still doing something else, reset it again. */
-		if (cci & ~UCSI_CCI_RESET_COMPLETE) {
+		if (*cci & ~UCSI_CCI_RESET_COMPLETE) {
 			ret = ucsi->ops->async_control(ucsi, command);
 			if (ret < 0)
 				goto out;
 		}
 
-	} while (!(cci & UCSI_CCI_RESET_COMPLETE));
+	} while (!(*cci & UCSI_CCI_RESET_COMPLETE));
 
 out:
 	mutex_unlock(&ucsi->ppm_lock);
@@ -1423,7 +1425,7 @@ static int ucsi_role_cmd(struct ucsi_connector *con, u64 command)
 		u64 c;
 
 		/* PPM most likely stopped responding. Resetting everything. */
-		ucsi_reset_ppm(con->ucsi);
+		ucsi_reset_ppm(con->ucsi, NULL);
 
 		c = UCSI_SET_NOTIFICATION_ENABLE | con->ucsi->ntfy;
 		ucsi_send_command(con->ucsi, c, NULL, 0);
@@ -1766,7 +1768,7 @@ static int ucsi_init(struct ucsi *ucsi)
 	int i;
 
 	/* Reset the PPM */
-	ret = ucsi_reset_ppm(ucsi);
+	ret = ucsi_reset_ppm(ucsi, NULL);
 	if (ret) {
 		dev_err(ucsi->dev, "failed to reset PPM!\n");
 		goto err;
@@ -1846,7 +1848,7 @@ static int ucsi_init(struct ucsi *ucsi)
 	kfree(connector);
 err_reset:
 	memset(&ucsi->cap, 0, sizeof(ucsi->cap));
-	ucsi_reset_ppm(ucsi);
+	ucsi_reset_ppm(ucsi, NULL);
 err:
 	return ret;
 }
@@ -1958,6 +1960,7 @@ EXPORT_SYMBOL_GPL(ucsi_create);
 void ucsi_destroy(struct ucsi *ucsi)
 {
 	ucsi_debugfs_unregister(ucsi);
+	ucsi_sysfs_unregister(ucsi);
 	kfree(ucsi);
 }
 EXPORT_SYMBOL_GPL(ucsi_destroy);
@@ -1989,6 +1992,8 @@ int ucsi_register(struct ucsi *ucsi)
 	queue_delayed_work(system_long_wq, &ucsi->work, 0);
 
 	ucsi_debugfs_register(ucsi);
+	ucsi_sysfs_register(ucsi);
+
 	return 0;
 }
 EXPORT_SYMBOL_GPL(ucsi_register);
diff --git a/drivers/usb/typec/ucsi/ucsi.h b/drivers/usb/typec/ucsi/ucsi.h
index feb012db4c89..d5b90e57cf42 100644
--- a/drivers/usb/typec/ucsi/ucsi.h
+++ b/drivers/usb/typec/ucsi/ucsi.h
@@ -468,6 +468,8 @@ struct ucsi {
 	unsigned long quirks;
 #define UCSI_NO_PARTNER_PDOS	BIT(0)	/* Don't read partner's PDOs */
 #define UCSI_DELAY_DEVICE_PDOS	BIT(1)	/* Reading PDOs fails until the parter is in PD mode */
+
+	void *sysfs;
 };
 
 #define UCSI_MAX_DATA_LENGTH(u) (((u)->version < UCSI_VERSION_2_0) ? 0x10 : 0xff)
@@ -535,6 +537,8 @@ void ucsi_notify_common(struct ucsi *ucsi, u32 cci);
 int ucsi_sync_control_common(struct ucsi *ucsi, u64 command, u32 *cci,
 			     void *data, size_t size);
 
+int ucsi_reset_ppm(struct ucsi *ucsi, u32 *cci);
+
 #if IS_ENABLED(CONFIG_POWER_SUPPLY)
 int ucsi_register_port_psy(struct ucsi_connector *con);
 void ucsi_unregister_port_psy(struct ucsi_connector *con);
@@ -578,6 +582,9 @@ static inline void ucsi_debugfs_register(struct ucsi *ucsi) { }
 static inline void ucsi_debugfs_unregister(struct ucsi *ucsi) { }
 #endif /* CONFIG_DEBUG_FS */
 
+int ucsi_sysfs_register(struct ucsi *ucsi);
+void ucsi_sysfs_unregister(struct ucsi *ucsi);
+
 /*
  * NVIDIA VirtualLink (svid 0x955) has two altmode. VirtualLink
  * DP mode with vdo=0x1 and NVIDIA test mode with vdo=0x3
-- 
2.47.2


Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ