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 <[email protected]>
---
 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 <[email protected]>
+ */
+
+#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

Reply via email to