Charles,

On Wed, Oct 14, 2026 at 7:47 PM Charles Perry
<[email protected]> wrote:
>
> From: Rahul Pathak <[email protected]>
>
> Add the mpxy command which allows listing MPXY channels, reading/writing
> arbitrary channel attributes and sending raw messages.
>
> The mpxy command is useful for interacting with custom RPMI service groups
> that have no associated driver. It can also be useful for debugging and
> testing SBI proxy drivers as well as the PuC firmware.
>
> Signed-off-by: Rahul Pathak <[email protected]>
> Signed-off-by: Charles Perry <[email protected]>
> ---
> Changes in v4:
>  - Force a device_probe() of all UCLASS_RPMI devices if g_mpxy is NULL
>  - Remove the unnecessary edit to "cmd/riscv/Makefile"
>  - Fix a printf placeholder for 32 bits %ld -> %zu
>
> Changes in v3:
>  - Add this patch
> ---
>  drivers/firmware/rpmi/Kconfig         |   7 +
>  drivers/firmware/rpmi/rpmi-sbi-mpxy.c | 404 
> +++++++++++++++++++++++++++++++++-
>  2 files changed, 407 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/firmware/rpmi/Kconfig b/drivers/firmware/rpmi/Kconfig
> index b462d3693987..c6a6b6fe61de 100644
> --- a/drivers/firmware/rpmi/Kconfig
> +++ b/drivers/firmware/rpmi/Kconfig
> @@ -18,6 +18,13 @@ config RPMI_SBI_MPXY
>           specification for the MPXY extension is available at:
>           https://github.com/riscv-non-isa/riscv-sbi-doc (chapter 20)
>
> +config CMD_MPXY
> +       bool "mpxy command"
> +       depends on RPMI_SBI_MPXY
> +       help
> +         Enable the 'mpxy' U-Boot command for listing MPXY channels,
> +         reading/writing arbitrary channel attributes and sending raw 
> messages.
> +
>  config RPMI_SHMEM
>         bool "RPMI over shared memory transport"
>         depends on RPMI_FIRMWARE
> diff --git a/drivers/firmware/rpmi/rpmi-sbi-mpxy.c 
> b/drivers/firmware/rpmi/rpmi-sbi-mpxy.c
> index 9ccca0eb59b2..5f01a6303323 100644
> --- a/drivers/firmware/rpmi/rpmi-sbi-mpxy.c
> +++ b/drivers/firmware/rpmi/rpmi-sbi-mpxy.c
> @@ -1,6 +1,7 @@
>  // SPDX-License-Identifier: GPL-2.0
>  /*
>   * Copyright (C) 2026 Microchip Technology Inc. All rights reserved.
> + * Copyright (c) 2026, Rahul Pathak <[email protected]>
>   */
>
>  #define LOG_CATEGORY UCLASS_RPMI
> @@ -16,6 +17,7 @@
>  #include <malloc.h>
>  #include <rpmi-uclass.h>
>  #include <string.h>
> +#include <dm/device-internal.h>
>
>  /* SBI MPXY channel IDs data in shared memory */
>  struct sbi_mpxy_channel_ids_data {
> @@ -60,10 +62,10 @@ enum sbi_mpxy_attribute_id {
>
>  /* RPMI message protocol specific MPXY attributes */
>  enum sbi_mpxy_rpmi_attribute_id {
> -       SBI_MPXY_RPMI_ATTR_SERVICEGROUP_ID,
> -       SBI_MPXY_RPMI_ATTR_SERVICEGROUP_VERSION,
> -       SBI_MPXY_RPMI_ATTR_IMPL_ID,
> -       SBI_MPXY_RPMI_ATTR_IMPL_VERSION,
> +       SBI_MPXY_RPMI_ATTR_SERVICEGROUP_ID = 0x00,
> +       SBI_MPXY_RPMI_ATTR_SERVICEGROUP_VERSION = 0x01,
> +       SBI_MPXY_RPMI_ATTR_IMPL_ID = 0x02,
> +       SBI_MPXY_RPMI_ATTR_IMPL_VERSION = 0x03,
>         SBI_MPXY_RPMI_ATTR_MAX_ID
>  };
>
> @@ -87,6 +89,24 @@ struct rpmi_sbi_mpxy {
>         struct sbi_mpxy_chan_desc *channel_descs;
>  };
>
> +static struct rpmi_sbi_mpxy *g_mpxy;
> +
> +static __maybe_unused struct rpmi_sbi_mpxy *mpxy_get_global(void)
> +{
> +       struct udevice *dev;
> +       struct uclass *uc;
> +
> +       if (!g_mpxy) {
> +               uclass_get(UCLASS_RPMI, &uc);
> +               uclass_foreach_dev(dev, uc)
> +               {
> +                       device_probe(dev);
> +               }
> +       }
> +
> +       return g_mpxy;
> +}
> +
>  static int rpmi_sbi_mpxy_read_attrs(struct rpmi_sbi_mpxy *mpxy, u32 
> channel_id,
>                                     u32 base_attrid, u32 attr_count,
>                                     u32 *attrs_buf)
> @@ -105,6 +125,24 @@ static int rpmi_sbi_mpxy_read_attrs(struct rpmi_sbi_mpxy 
> *mpxy, u32 channel_id,
>         return 0;
>  }
>
> +static __maybe_unused int
> +rpmi_sbi_mpxy_write_attrs(struct rpmi_sbi_mpxy *mpxy, u32 channel_id,
> +                         u32 base_attrid, u32 attr_count, u32 *attrs_buf)
> +{
> +       struct sbiret sret;
> +       int i;
> +
> +       for (i = 0; i < attr_count; i++)
> +               ((__le32 *)mpxy->shmem)[i] = cpu_to_le32(attrs_buf[i]);
> +
> +       sret = sbi_ecall(SBI_EXT_MPXY, SBI_EXT_MPXY_WRITE_ATTRS, channel_id,
> +                        base_attrid, attr_count, 0, 0, 0);
> +       if (sret.error)
> +               return sbi_to_linux_error(sret.error);
> +
> +       return 0;
> +}
> +
>  static int rpmi_sbi_mpxy_send_message(void *shmem, u32 mpxy_fid, u32 
> channel_id,
>                                       u32 msg_id, const void *tx,
>                                       unsigned long tx_len, void *rx,
> @@ -368,6 +406,8 @@ static int rpmi_sbi_mpxy_probe(struct udevice *dev)
>                             desc->attrs[SBI_MPXY_ATTR_MSG_MAX_LEN]);
>         }
>
> +       g_mpxy = mpxy;
> +
>         return 0;
>
>  err:
> @@ -408,3 +448,359 @@ U_BOOT_DRIVER(rpmi_sbi_mpxy) = {
>         .ops = &rpmi_sbi_mpxy_ops,
>         .flags = DM_FLAG_PRE_RELOC,
>  };
> +
> +#if CONFIG_IS_ENABLED(CMD_MPXY) && !CONFIG_IS_ENABLED(XPL_BUILD)
> +
> +#include <command.h>
> +#include <hexdump.h>
> +
> +static const char *mpxy_proto_to_str(u32 proto_id)
> +{
> +       switch (proto_id) {
> +       case SBI_MPXY_MSGPROTO_RPMI_ID:
> +               return "RPMI";
> +       default:
> +               return "Unknown";
> +       }
> +}
> +
> +/**
> + * mpxy list
> + *
> + * List the mpxy channels
> + */
> +static int do_mpxy_list(struct cmd_tbl *cmdtp, int flag, int argc,
> +                       char *const argv[])
> +{
> +       struct rpmi_sbi_mpxy *mpxy = mpxy_get_global();
> +       struct sbi_mpxy_chan_desc *chan;
> +       u32 proto;
> +       int i;
> +
> +       if (!mpxy)
> +               return CMD_RET_FAILURE;
> +
> +       for (i = 0; i < mpxy->channel_count; i++) {
> +               chan = &mpxy->channel_descs[i];
> +               proto = chan->attrs[SBI_MPXY_ATTR_MSG_PROT_ID];
> +               printf("Channel 0x%x: proto_id=0x%x (%s), ver=0x%x\n", 
> chan->id,
> +                      proto, mpxy_proto_to_str(proto),
> +                      chan->attrs[SBI_MPXY_ATTR_MSG_PROT_VER]);
> +       }
> +
> +       printf("sbi_mpxy: found %d channels\n", mpxy->channel_count);
> +       return CMD_RET_SUCCESS;
> +}
> +
> +static void mpxy_print_capability(u32 val)
> +{
> +       static const struct {
> +               u32 bit;
> +               const char *name;
> +       } caps[] = {
> +               { SBI_MPXY_CHAN_CAP_MSI, "MSI" },
> +               { SBI_MPXY_CHAN_CAP_SSE, "SSE_EVENT" },
> +               { SBI_MPXY_CHAN_CAP_EVENTS_STATE, "EVENTS_STATE" },
> +               { SBI_MPXY_CHAN_CAP_SEND_WITH_RESP, "SEND_WITH_RESP" },
> +               { SBI_MPXY_CHAN_CAP_SEND_WITHOUT_RESP, "SEND_WITHOUT_RESP" },
> +               { SBI_MPXY_CHAN_CAP_GET_NOTIFICATIONS, "GET_NOTIFICATIONS" },
> +       };
> +
> +       bool any = false;
> +       int i;
> +
> +       printf(" (");
> +
> +       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)
> +{
> +       /* 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",
> +       };
> +
> +       printf("  [0x%02x] %-28s: 0x%08x", attr_id,
> +              mpxy_std_attr_names[attr_id], val);
> +
> +       switch (attr_id) {
> +       case SBI_MPXY_ATTR_MSG_PROT_ID:
> +               printf(" (%s)\n", mpxy_proto_to_str(val));
> +               break;
> +       case SBI_MPXY_ATTR_MSG_PROT_VER:
> +               printf(" (v%u.%u)\n", (val >> 16) & 0xffff, val & 0xffff);
> +               break;
> +       case SBI_MPXY_ATTR_MSG_MAX_LEN:
> +               printf(" (%u bytes)\n", val);
> +               break;
> +       case SBI_MPXY_ATTR_MSG_SEND_TIMEOUT:
> +       case SBI_MPXY_ATTR_MSG_COMPLETION_TIMEOUT:
> +               printf(" (%u ms)\n", val);
> +               break;
> +       case SBI_MPXY_ATTR_CHANNEL_CAPABILITY:
> +               mpxy_print_capability(val);
> +               break;
> +       default:
> +               printf("\n");
> +               break;
> +       }
> +}
> +
> +static const char *mpxy_rpmi_proto_attr_name(u32 attr_id)
> +{
> +       switch (attr_id) {
> +       case SBI_MPXY_RPMI_ATTR_SERVICEGROUP_ID:
> +               return "SERVICEGROUP_ID";
> +       case SBI_MPXY_RPMI_ATTR_SERVICEGROUP_VERSION:
> +               return "SERVICEGROUP_VERSION";
> +       case SBI_MPXY_RPMI_ATTR_IMPL_ID:
> +               return "IMPLEMENTATION_ID";
> +       case SBI_MPXY_RPMI_ATTR_IMPL_VERSION:
> +               return "IMPLEMENTATION_VER";
> +       default:
> +               return "UNKNOWN";
> +       };
> +}
> +
> +static void mpxy_print_proto_attr(u32 attr_id, u32 val, bool is_rpmi)
> +{
> +       u32 rpmi_attr_id = attr_id - SBI_MPXY_ATTR_MSGPROTO_ATTR_START;
> +       const char *name = is_rpmi ? mpxy_rpmi_proto_attr_name(rpmi_attr_id) :
> +                                    "UNKNOWN";
> +
> +       printf("  [0x%08x] %-24s: 0x%08x", attr_id, name, val);
> +
> +       if (is_rpmi &&
> +           (rpmi_attr_id == SBI_MPXY_RPMI_ATTR_SERVICEGROUP_VERSION ||
> +            rpmi_attr_id == SBI_MPXY_RPMI_ATTR_IMPL_VERSION)) {
> +               printf(" (v%u.%u)\n", (val >> 16) & 0xffff, val & 0xffff);
> +       } else {
> +               printf("\n");
> +       }
> +}
> +
> +/**
> + * 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 rpmi_sbi_mpxy *mpxy = mpxy_get_global();
> +       u32 chan_id, attr_id, attr_val;
> +       int ret;
> +
> +       if (argc < 3)
> +               return CMD_RET_USAGE;
> +
> +       if (!mpxy)
> +               return CMD_RET_FAILURE;
> +
> +       chan_id = simple_strtoul(argv[1], NULL, 0);
> +       attr_id = simple_strtoul(argv[2], NULL, 0);
> +
> +       ret = rpmi_sbi_mpxy_read_attrs(mpxy, chan_id, 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;
> +}
> +
> +/**
> + * mpxy readattrall <channel_id>
> + */
> +static int do_mpxy_readattr_all(struct cmd_tbl *cmdtp, int flag, int argc,
> +                               char *const argv[])
> +{
> +       struct rpmi_sbi_mpxy *mpxy = mpxy_get_global();
> +       u32 chan_id, attr_val[SBI_MPXY_ATTR_STD_ATTR_MAX_IDX];
> +       bool is_rpmi;
> +       int i, ret;
> +
> +       if (argc < 2)
> +               return CMD_RET_USAGE;
> +
> +       if (!mpxy)
> +               return CMD_RET_FAILURE;
> +
> +       chan_id = simple_strtoul(argv[1], NULL, 0);
> +
> +       ret = rpmi_sbi_mpxy_read_attrs(mpxy, chan_id, 
> SBI_MPXY_ATTR_MSG_PROT_ID,
> +                                      SBI_MPXY_ATTR_STD_ATTR_MAX_IDX,
> +                                      attr_val);
> +       if (ret) {
> +               printf("Read attr on channel 0x%x failed (%d)\n", chan_id, 
> ret);
> +               return CMD_RET_FAILURE;
> +       }
> +
> +       /* Standard attributes */
> +       printf("Channel 0x%x standard attributes:\n", chan_id);
> +       for (i = 0; i < SBI_MPXY_ATTR_STD_ATTR_MAX_IDX; i++)
> +               mpxy_print_std_attr(i, attr_val[i]);
> +
> +       /* Protocol-specific attributes */
> +       printf("Channel 0x%x protocol attributes:\n", chan_id);
> +
> +       for (i = SBI_MPXY_ATTR_MSGPROTO_ATTR_START;
> +            i < SBI_MPXY_ATTR_MSGPROTO_ATTR_END; i++) {
> +               ret = rpmi_sbi_mpxy_read_attrs(mpxy, chan_id, i, 1,
> +                                              &attr_val[0]);
> +               if (ret)
> +                       break;
> +
> +               is_rpmi = mpxy->channel_descs[chan_id]
> +                                 .attrs[SBI_MPXY_ATTR_MSG_PROT_ID] ==
> +                         SBI_MPXY_MSGPROTO_RPMI_ID;
 `chan_id` is an external MPXY channel ID, not necessarily the descriptor
 array index. The descriptors are stored by discovery position, so a valid
 non-contiguous channel ID can cause an out-of-bounds read here. Please resolve
 the channel ID to its descriptor index before accessing `channel_descs`.

 Regards,
 Subrahmanya

> +               mpxy_print_proto_attr(i, attr_val[0], is_rpmi);
> +       }
> +
> +       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 rpmi_sbi_mpxy *mpxy = mpxy_get_global();
> +       u32 chan_id, attr_id, attr_val;
> +       int ret;
> +
> +       if (argc < 4)
> +               return CMD_RET_USAGE;
> +
> +       if (!mpxy)
> +               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);
> +
> +       ret = rpmi_sbi_mpxy_write_attrs(mpxy, chan_id, 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 rpmi_sbi_mpxy *mpxy = mpxy_get_global();
> +       u32 tx[64];
> +       u32 rx[64];
> +       unsigned long tx_words, rx_bytes;
> +       u32 chan_id, service_id;
> +       int i, ret;
> +
> +       if (argc < 3)
> +               return CMD_RET_USAGE;
> +
> +       if (!mpxy)
> +               return CMD_RET_FAILURE;
> +
> +       chan_id = simple_strtoul(argv[1], NULL, 0);
> +       service_id = simple_strtoul(argv[2], NULL, 0);
> +       tx_words = argc - 3;
> +
> +       if (tx_words > ARRAY_SIZE(tx)) {
> +               printf("Too many words (max %zu)\n", ARRAY_SIZE(tx));
> +               return CMD_RET_FAILURE;
> +       }
> +
> +       for (i = 0; i < tx_words; i++)
> +               tx[i] = simple_strtoul(argv[3 + i], NULL, 0);
> +
> +       ret = rpmi_sbi_mpxy_send_message(mpxy->shmem,
> +                                        SBI_EXT_MPXY_SEND_MSG_WITH_RESP,
> +                                        chan_id, service_id, tx, tx_words * 
> 4,
> +                                        rx, sizeof(rx), &rx_bytes);
> +       if (ret) {
> +               printf("Send on channel 0x%x failed (%d)\n", chan_id, ret);
> +               return CMD_RET_FAILURE;
> +       }
> +
> +       printf("Response (%ld word(s)):\n", rx_bytes / 4);
> +       print_hex_dump(" ", DUMP_PREFIX_OFFSET, 4, 4, rx, rx_bytes, true);
> +
> +       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");
> +
> +#endif // CONFIG_CMD_MPXY
>
> --
> 2.53.0
>

Reply via email to