[PATCH 2/3] cmd: Add mpxy command and add RISC-V RPMI driver

Rahul Pathak rahul.pathak at oss.qualcomm.com
Thu Jul 9 19:56:03 CEST 2026


The mpxy command is an interactive command which uses
SBI MPXY interface to interact with supported message
protocol through available MPXY channels.

The command helps if there is a custom RPMI service
group or any other such message protocol implementation
and that can be tested in some manner without requiring
the S-Mode OS driver support.

Signed-off-by: Rahul Pathak <rahul.pathak at oss.qualcomm.com>
---
 cmd/Kconfig              |   8 +
 cmd/riscv/Makefile       |   1 +
 cmd/riscv/sbi_mpxy_cmd.c | 482 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 491 insertions(+)
 create mode 100644 cmd/riscv/sbi_mpxy_cmd.c

diff --git a/cmd/Kconfig b/cmd/Kconfig
index 5b9c13d85e7..413f7787343 100644
--- a/cmd/Kconfig
+++ b/cmd/Kconfig
@@ -264,6 +264,14 @@ config CMD_SBI
 	help
 	  Display information about the SBI implementation.
 
+config CMD_MPXY
+	bool "mpxy command"
+	depends on SBI_MPXY
+	help
+	  Enable the 'mpxy' U-Boot command for listing MPXY channels,
+	  reading/writing channel attributes, and sending raw messages.
+	  Includes the built-in RPMI protocol driver.
+
 config CMD_SMBIOS
 	bool "smbios"
 	depends on SMBIOS
diff --git a/cmd/riscv/Makefile b/cmd/riscv/Makefile
index 1e6ac364e34..40caf68d0df 100644
--- a/cmd/riscv/Makefile
+++ b/cmd/riscv/Makefile
@@ -2,3 +2,4 @@
 
 obj-$(CONFIG_CMD_EXCEPTION) += exception.o
 obj-$(CONFIG_CMD_SBI) += sbi.o
+obj-$(CONFIG_CMD_MPXY) += sbi_mpxy_cmd.o
diff --git a/cmd/riscv/sbi_mpxy_cmd.c b/cmd/riscv/sbi_mpxy_cmd.c
new file mode 100644
index 00000000000..20a9411677f
--- /dev/null
+++ b/cmd/riscv/sbi_mpxy_cmd.c
@@ -0,0 +1,482 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * SBI MPXY U-Boot Command + RPMI Protocol Driver
+ * Copyright (c) 2026, Rahul Pathak <rahul.pathak at oss.qualcomm.com>
+ */
+
+#include <command.h>
+#include <errno.h>
+#include <vsprintf.h>
+#include <asm/sbi.h>
+#include <linux/string.h>
+#include <linux/types.h>
+
+/**
+ * RPMI protocol driver
+ */
+
+/** RPMI Protocol ID assigned for MPXY */
+#define SBI_MPXY_MSGPROTO_RPMI_ID	0x0
+
+static int rpmi_readattr(struct mpxy_channel *chan, u32 attr_id, u32 *attr_val)
+{
+	struct sbiret ret;
+	__le32 *shmem;
+
+	shmem = (__le32 *)sbi_mpxy_shmem_ptr();
+	if (!shmem)
+		return -ENXIO;
+
+	ret = sbi_ecall(SBI_EXT_MPXY, SBI_EXT_MPXY_READ_ATTRS,
+			chan->channel_id, attr_id, 1, 0, 0, 0);
+	if (ret.error)
+		return -EINVAL;
+
+	*attr_val = le32_to_cpu(shmem[0]);
+	return 0;
+}
+
+static int rpmi_writeattr(struct mpxy_channel *chan, u32 attr_id, u32 attr_val)
+{
+	struct sbiret ret;
+	__le32 *shmem;
+
+	shmem = (__le32 *)sbi_mpxy_shmem_ptr();
+	if (!shmem)
+		return -ENXIO;
+
+	shmem[0] = cpu_to_le32(attr_val);
+
+	ret = sbi_ecall(SBI_EXT_MPXY, SBI_EXT_MPXY_WRITE_ATTRS,
+			chan->channel_id, attr_id, 1, 0, 0, 0);
+	if (ret.error)
+		return -EINVAL;
+
+	return 0;
+}
+
+
+static const struct mpxy_proto_ops rpmi_proto_ops = {
+	.proto_id	= SBI_MPXY_MSGPROTO_RPMI_ID,
+	.name		= "RPMI",
+	.readattr	= rpmi_readattr,
+	.writeattr	= rpmi_writeattr,
+};
+
+static int cmd_mpxy_probed(void)
+{
+	static bool proto_registered;
+	int ret;
+
+	if (!proto_registered) {
+		ret = sbi_mpxy_register_proto(&rpmi_proto_ops);
+		if (ret) {
+			printf("Failed to register RPMI protocol (%d)\n", ret);
+			return CMD_RET_FAILURE;
+		}
+		proto_registered = true;
+	}
+
+	ret = sbi_mpxy_probe();
+	if (ret) {
+		printf("MPXY not available (error %d)\n", ret);
+		return CMD_RET_FAILURE;
+	}
+
+	return 0;
+}
+
+/**
+ * mpxy list
+ *
+ * List the mpxy channels
+ */
+static int do_mpxy_list(struct cmd_tbl *cmdtp, int flag, int argc,
+			char *const argv[])
+{
+	int i, count;
+
+	if (cmd_mpxy_probed())
+		return CMD_RET_FAILURE;
+
+	count = sbi_mpxy_channel_count();
+	for (i = 0; i < count; i++) {
+		struct mpxy_channel *chan = sbi_mpxy_get_channel(i);
+
+		if (!chan)
+			continue;
+
+		printf("Channel 0x%x: %s (proto_id=0x%x, ver=0x%x)\n",
+			chan->channel_id,
+			chan->proto ? chan->proto->name : "Unknown protocol",
+			chan->std_attrs.msg_proto_id,
+			chan->std_attrs.msg_proto_version);
+	}
+
+	printf("sbi_mpxy: found %d channels\n", count);
+	return CMD_RET_SUCCESS;
+}
+
+/**
+ * mpxy readattr <channel_id> <attr_id>
+ *
+ * Read the attribute on a channel
+ */
+static int do_mpxy_readattr(struct cmd_tbl *cmdtp, int flag, int argc,
+			    char *const argv[])
+{
+	struct mpxy_channel *chan;
+	u32 chan_id, attr_id, attr_val;
+	int ret;
+
+	if (argc < 3)
+		return CMD_RET_USAGE;
+
+	if (cmd_mpxy_probed())
+		return CMD_RET_FAILURE;
+
+	chan_id = simple_strtoul(argv[1], NULL, 0);
+	attr_id = simple_strtoul(argv[2], NULL, 0);
+
+	chan = sbi_mpxy_find_channel(chan_id);
+	if (!chan) {
+		printf("Channel 0x%x not found\n", chan_id);
+		return CMD_RET_FAILURE;
+	}
+
+	ret = sbi_mpxy_read_attrs(chan, attr_id, 1, &attr_val);
+	if (ret) {
+		printf("Read attr 0x%x on channel 0x%x failed (%d)\n",
+		       attr_id, chan_id, ret);
+		return CMD_RET_FAILURE;
+	}
+
+	printf("Channel 0x%x: attr[0x%x] = 0x%x\n", chan_id, attr_id, attr_val);
+	return CMD_RET_SUCCESS;
+}
+
+/**
+ * Attribute name tables
+ */
+
+/* Standard MPXY attribute names, indexed by attr_id */
+static const char * const mpxy_std_attr_names[] = {
+	[SBI_MPXY_ATTR_MSG_PROT_ID]		= "MSG_PROT_ID",
+	[SBI_MPXY_ATTR_MSG_PROT_VER]		= "MSG_PROT_VER",
+	[SBI_MPXY_ATTR_MSG_MAX_LEN]		= "MSG_MAX_LEN",
+	[SBI_MPXY_ATTR_MSG_SEND_TIMEOUT]	= "MSG_SEND_TIMEOUT",
+	[SBI_MPXY_ATTR_MSG_COMPLETION_TIMEOUT]	= "MSG_COMPLETION_TIMEOUT",
+	[SBI_MPXY_ATTR_CHANNEL_CAPABILITY]	= "CHANNEL_CAPABILITY",
+	[SBI_MPXY_ATTR_SSE_EVENT_ID]		= "SSE_EVENT_ID",
+	[SBI_MPXY_ATTR_MSI_CONTROL]		= "MSI_CONTROL",
+	[SBI_MPXY_ATTR_MSI_ADDR_LO]		= "MSI_ADDR_LO",
+	[SBI_MPXY_ATTR_MSI_ADDR_HI]		= "MSI_ADDR_HI",
+	[SBI_MPXY_ATTR_MSI_DATA]		= "MSI_DATA",
+	[SBI_MPXY_ATTR_EVENTS_STATE_CONTROL]	= "EVENTS_STATE_CONTROL",
+};
+
+/* RPMI protocol-specific attribute names */
+struct mpxy_attr_name {
+	u32		 attr_id;
+	const char	*name;
+};
+
+static const struct mpxy_attr_name rpmi_proto_attr_names[] = {
+	{ 0x80000000, "SERVICEGROUP_ID"		},
+	{ 0x80000001, "SERVICEGROUP_VERSION"	},
+	{ 0x80000002, "IMPLEMENTATION_ID"	},
+	{ 0x80000003, "IMPLEMENTATION_VER"	},
+};
+
+static const char *rpmi_proto_attr_name(u32 attr_id)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(rpmi_proto_attr_names); i++) {
+		if (rpmi_proto_attr_names[i].attr_id == attr_id)
+			return rpmi_proto_attr_names[i].name;
+	}
+
+	return "UNKNOWN";
+}
+
+/*
+ * CHANNEL_CAPABILITY bit definitions (SBI MPXY spec):
+ *	[0]	MSI support
+ *	[1]	SSE Event support
+ *	[2]	Events State support
+ *	[3]	Send Message with Response (FID #5) support
+ *	[4]	Send Message without Response (FID #6) support
+ *	[5]	Get Notifications (FID #7) support
+ *	[31:6]	Reserved, must be 0
+ */
+#define MPXY_CAP_MSI			BIT(0)
+#define MPXY_CAP_SSE_EVENT		BIT(1)
+#define MPXY_CAP_EVENTS_STATE		BIT(2)
+#define MPXY_CAP_SEND_WITH_RESP		BIT(3)
+#define MPXY_CAP_SEND_WITHOUT_RESP	BIT(4)
+#define MPXY_CAP_GET_NOTIFICATIONS	BIT(5)
+
+struct capability {
+	u32		 bit;
+	const char	*name;
+};
+
+static const struct capability caps[] = {
+	{ MPXY_CAP_MSI,			"MSI"			},
+	{ MPXY_CAP_SSE_EVENT,		"SSE_EVENT"		},
+	{ MPXY_CAP_EVENTS_STATE,	"EVENTS_STATE"		},
+	{ MPXY_CAP_SEND_WITH_RESP,	"SEND_WITH_RESP"	},
+	{ MPXY_CAP_SEND_WITHOUT_RESP,	"SEND_WITHOUT_RESP"	},
+	{ MPXY_CAP_GET_NOTIFICATIONS,	"GET_NOTIFICATIONS"	},
+};
+
+static void mpxy_print_capability(u32 val)
+{
+	bool any = false;
+	int i;
+
+	printf("  [0x%02x] %-28s: 0x%08x (", SBI_MPXY_ATTR_CHANNEL_CAPABILITY,
+	       mpxy_std_attr_names[SBI_MPXY_ATTR_CHANNEL_CAPABILITY], val);
+
+	if (!val) {
+		printf("none)\n");
+		return;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(caps); i++) {
+		if (val & caps[i].bit) {
+			if (any)
+				printf("|");
+			printf("%s", caps[i].name);
+			any = true;
+		}
+	}
+	printf(")\n");
+}
+
+/* Decode well-known field values for selected standard attributes */
+static void mpxy_print_std_attr(u32 attr_id, u32 val)
+{
+	switch (attr_id) {
+	case SBI_MPXY_ATTR_MSG_PROT_ID:
+		printf("  [0x%02x] %-28s: 0x%08x (%s)\n", attr_id,
+		       mpxy_std_attr_names[attr_id], val,
+		       val == SBI_MPXY_MSGPROTO_RPMI_ID ? "RPMI" : "Unknown");
+		break;
+	case SBI_MPXY_ATTR_MSG_PROT_VER:
+		printf("  [0x%02x] %-28s: 0x%08x (v%u.%u)\n", attr_id,
+		       mpxy_std_attr_names[attr_id], val,
+		       (val >> 16) & 0xffff, val & 0xffff);
+		break;
+	case SBI_MPXY_ATTR_MSG_MAX_LEN:
+		printf("  [0x%02x] %-28s: 0x%08x (%u bytes)\n", attr_id,
+		       mpxy_std_attr_names[attr_id], val, val);
+		break;
+	case SBI_MPXY_ATTR_MSG_SEND_TIMEOUT:
+	case SBI_MPXY_ATTR_MSG_COMPLETION_TIMEOUT:
+		printf("  [0x%02x] %-28s: 0x%08x (%u ms)\n", attr_id,
+		       mpxy_std_attr_names[attr_id], val, val);
+		break;
+	case SBI_MPXY_ATTR_CHANNEL_CAPABILITY:
+		mpxy_print_capability(val);
+		break;
+	default:
+		printf("  [0x%02x] %-28s: 0x%08x\n", attr_id,
+		       mpxy_std_attr_names[attr_id], val);
+		break;
+	}
+}
+
+static void mpxy_print_rpmi_attr(u32 attr_id, u32 val)
+{
+	const char *name = rpmi_proto_attr_name(attr_id);
+
+	switch (attr_id) {
+	case 0x80000001: /* SERVICEGROUP_VERSION */
+	case 0x80000003: /* IMPLEMENTATION_VER */
+		printf("  [0x%08x] %-24s: 0x%08x (v%u.%u)\n", attr_id, name,
+		       val, (val >> 16) & 0xffff, val & 0xffff);
+		break;
+	default:
+		printf("  [0x%08x] %-24s: 0x%08x\n", attr_id, name, val);
+		break;
+	}
+}
+
+/**
+ * mpxy readattrall <channel_id>
+ */
+static int do_mpxy_readattr_all(struct cmd_tbl *cmdtp, int flag, int argc,
+				char *const argv[])
+{
+	struct mpxy_channel *chan;
+	u32 chan_id, attr_id, attr_val;
+	int j, ret;
+
+	if (argc < 2)
+		return CMD_RET_USAGE;
+
+	if (cmd_mpxy_probed())
+		return CMD_RET_FAILURE;
+
+	chan_id = simple_strtoul(argv[1], NULL, 0);
+
+	chan = sbi_mpxy_find_channel(chan_id);
+	if (!chan) {
+		printf("Channel 0x%x not found\n", chan_id);
+		return CMD_RET_FAILURE;
+	}
+
+	/* Standard attributes from cache */
+	printf("Channel 0x%x standard attributes:\n", chan_id);
+	for (j = 0; j < SBI_MPXY_ATTR_STD_ATTR_MAX_IDX; j++) {
+		sbi_mpxy_read_attrs(chan, j, 1, &attr_val);
+		mpxy_print_std_attr(j, attr_val);
+	}
+
+	/* Protocol-specific attributes — stop on first error */
+	if (!chan->proto || !chan->proto->readattr)
+		return CMD_RET_SUCCESS;
+
+	printf("Channel 0x%x %s protocol attributes:\n",
+	       chan_id, chan->proto->name);
+
+	for (attr_id = SBI_MPXY_ATTR_MSGPROTO_ATTR_START;
+	     attr_id < SBI_MPXY_ATTR_MSGPROTO_ATTR_END; attr_id++) {
+		ret = sbi_mpxy_read_attrs(chan, attr_id, 1, &attr_val);
+		if (ret)
+			break;
+
+		if (chan->proto->proto_id == SBI_MPXY_MSGPROTO_RPMI_ID)
+			mpxy_print_rpmi_attr(attr_id, attr_val);
+		else
+			printf("  [0x%08x] %-24s: 0x%08x\n", attr_id,
+			       "UNKNOWN", attr_val);
+	}
+
+	return CMD_RET_SUCCESS;
+}
+
+/**
+ * mpxy writeattr <channel_id> <attr_id> <value>
+ */
+static int do_mpxy_writeattr(struct cmd_tbl *cmdtp, int flag, int argc,
+			     char *const argv[])
+{
+	struct mpxy_channel *chan;
+	u32 chan_id, attr_id, attr_val;
+	int ret;
+
+	if (argc < 4)
+		return CMD_RET_USAGE;
+
+	if (cmd_mpxy_probed())
+		return CMD_RET_FAILURE;
+
+	chan_id  = simple_strtoul(argv[1], NULL, 0);
+	attr_id  = simple_strtoul(argv[2], NULL, 0);
+	attr_val = simple_strtoul(argv[3], NULL, 0);
+
+	chan = sbi_mpxy_find_channel(chan_id);
+	if (!chan) {
+		printf("Channel 0x%x not found\n", chan_id);
+		return CMD_RET_FAILURE;
+	}
+
+	ret = sbi_mpxy_write_attrs(chan, attr_id, 1, &attr_val);
+	if (ret) {
+		printf("Write attr 0x%x on channel 0x%x failed (%d)\n",
+		       attr_id, chan_id, ret);
+		return CMD_RET_FAILURE;
+	}
+
+	printf("Channel 0x%x: attr[0x%x] written 0x%x\n",
+	       chan_id, attr_id, attr_val);
+	return CMD_RET_SUCCESS;
+}
+
+/**
+ * mpxy send <channel_id> <msg_id> [payload_word ...]
+ *
+ * For RPMI, msg_id is the service_id. Payload words follow.
+ */
+static int do_mpxy_send(struct cmd_tbl *cmdtp, int flag, int argc,
+			char *const argv[])
+{
+	struct mpxy_channel *chan;
+	u32 tx[SBI_MPXY_MAX_MSG_WORDS];
+	u32 rx[SBI_MPXY_MAX_MSG_WORDS];
+	int tx_words, rx_words = SBI_MPXY_MAX_MSG_WORDS;
+	u32 chan_id;
+	int i, ret;
+
+	if (argc < 3)
+		return CMD_RET_USAGE;
+
+	if (cmd_mpxy_probed())
+		return CMD_RET_FAILURE;
+
+	chan_id  = simple_strtoul(argv[1], NULL, 0);
+	tx_words = argc - 2;
+
+	if (tx_words > SBI_MPXY_MAX_MSG_WORDS) {
+		printf("Too many words (max %d)\n", SBI_MPXY_MAX_MSG_WORDS);
+		return CMD_RET_FAILURE;
+	}
+
+	for (i = 0; i < tx_words; i++)
+		tx[i] = simple_strtoul(argv[2 + i], NULL, 0);
+
+	chan = sbi_mpxy_find_channel(chan_id);
+	if (!chan) {
+		printf("Channel 0x%x not found\n", chan_id);
+		return CMD_RET_FAILURE;
+	}
+
+	ret = sbi_mpxy_send(chan, tx, tx_words, rx, &rx_words);
+	if (ret) {
+		printf("Send on channel 0x%x failed (%d)\n", chan_id, ret);
+		return CMD_RET_FAILURE;
+	}
+
+	printf("Response (%d word(s)):\n", rx_words);
+	for (i = 0; i < rx_words; i++)
+		printf("  [%2d] 0x%08x\n", i, rx[i]);
+
+	return CMD_RET_SUCCESS;
+}
+
+/**
+ * Subcommand table and top-level dispatch
+ */
+static struct cmd_tbl cmd_mpxy_sub[] = {
+	U_BOOT_CMD_MKENT(list,		1,		0,	do_mpxy_list,		"",	""),
+	U_BOOT_CMD_MKENT(readattr,	3,		0,	do_mpxy_readattr,	"",	""),
+	U_BOOT_CMD_MKENT(readattrall,	2,		0,	do_mpxy_readattr_all,	"",	""),
+	U_BOOT_CMD_MKENT(writeattr,	4,		0,	do_mpxy_writeattr,	"",	""),
+	U_BOOT_CMD_MKENT(send,	CONFIG_SYS_MAXARGS,	0,	do_mpxy_send,		"",	""),
+};
+
+static int do_mpxy(struct cmd_tbl *cmdtp, int flag, int argc,
+		   char *const argv[])
+{
+	struct cmd_tbl *c;
+
+	if (argc < 2)
+		return CMD_RET_USAGE;
+
+	c = find_cmd_tbl(argv[1], cmd_mpxy_sub, ARRAY_SIZE(cmd_mpxy_sub));
+	if (!c)
+		return CMD_RET_USAGE;
+
+	return c->cmd(cmdtp, flag, argc - 1, argv + 1);
+}
+
+U_BOOT_CMD(mpxy, CONFIG_SYS_MAXARGS, 0, do_mpxy,
+	   "SBI MPXY channel debug commands",
+	   "list                              - list discovered channels\n"
+	   "mpxy readattr    <chan> <attr>         - read one attribute\n"
+	   "mpxy readattrall <chan>                - read all attributes\n"
+	   "mpxy writeattr   <chan> <attr> <val>   - write an attribute\n"
+	   "mpxy send        <chan> <msg_id> [payload_word ...]  - send message\n");
-- 
2.43.0



More information about the U-Boot mailing list