[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