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

