SBI MPXY interface implements the SBI MPXY extension to interact with supported message protocols for platform management and control
Signed-off-by: Rahul Pathak <[email protected]> --- arch/riscv/Kconfig | 9 + arch/riscv/include/asm/sbi.h | 268 ++++++++++++++++++ arch/riscv/lib/Makefile | 1 + arch/riscv/lib/sbi_mpxy.c | 529 +++++++++++++++++++++++++++++++++++ 4 files changed, 807 insertions(+) create mode 100644 arch/riscv/lib/sbi_mpxy.c diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig index 265b5320777..e0efcd19ce5 100644 --- a/arch/riscv/Kconfig +++ b/arch/riscv/Kconfig @@ -473,6 +473,15 @@ config SBI_IPI default y if RISCV_SMODE || SPL_RISCV_SMODE depends on SMP +config SBI_MPXY + bool "SBI MPXY generic core" + depends on RISCV_SMODE || SPL_RISCV_SMODE + help + Enable the generic SBI MPXY (Message Proxy Extension) core. + Provides channel discovery, shared memory setup, attribute + caching, and protocol driver registration. Required by + CMD_MPXY and any driver that communicates via MPXY channels. + config XIP bool "XIP mode" help diff --git a/arch/riscv/include/asm/sbi.h b/arch/riscv/include/asm/sbi.h index 47124dbaac8..d98c98bf4f4 100644 --- a/arch/riscv/include/asm/sbi.h +++ b/arch/riscv/include/asm/sbi.h @@ -10,6 +10,7 @@ #define _ASM_RISCV_SBI_H #include <linux/types.h> +#include <compiler.h> enum sbi_ext_id { SBI_EXT_0_1_SET_TIMER = 0x0, @@ -104,6 +105,36 @@ enum sbi_ext_dbcn_fid { SBI_EXT_DBCN_CONSOLE_WRITE_BYTE, }; +/* MPXY Standard Channel Attribute IDs */ +enum sbi_mpxy_attr_id { + SBI_MPXY_ATTR_MSG_PROT_ID = 0x00000000, + SBI_MPXY_ATTR_MSG_PROT_VER = 0x00000001, + SBI_MPXY_ATTR_MSG_MAX_LEN = 0x00000002, + SBI_MPXY_ATTR_MSG_SEND_TIMEOUT = 0x00000003, + SBI_MPXY_ATTR_MSG_COMPLETION_TIMEOUT = 0x00000004, + SBI_MPXY_ATTR_CHANNEL_CAPABILITY = 0x00000005, + SBI_MPXY_ATTR_SSE_EVENT_ID = 0x00000006, + SBI_MPXY_ATTR_MSI_CONTROL = 0x00000007, + SBI_MPXY_ATTR_MSI_ADDR_LO = 0x00000008, + SBI_MPXY_ATTR_MSI_ADDR_HI = 0x00000009, + SBI_MPXY_ATTR_MSI_DATA = 0x0000000A, + SBI_MPXY_ATTR_EVENTS_STATE_CONTROL = 0x0000000B, + SBI_MPXY_ATTR_STD_ATTR_MAX_IDX, + SBI_MPXY_ATTR_MSGPROTO_ATTR_START = 0x80000000, + SBI_MPXY_ATTR_MSGPROTO_ATTR_END = 0xffffffff, +}; + +/* MPXY Function IDs */ +enum sbi_ext_mpxy_fid { + SBI_EXT_MPXY_GET_SHMEM_SIZE = 0, + SBI_EXT_MPXY_SET_SHMEM, + SBI_EXT_MPXY_GET_CHANNEL_IDS, + SBI_EXT_MPXY_READ_ATTRS, + SBI_EXT_MPXY_WRITE_ATTRS, + SBI_EXT_MPXY_SEND_MSG_WITH_RESP, + SBI_EXT_MPXY_SEND_MSG_WITHOUT_RESP, +}; + #ifdef CONFIG_SBI_V01 #define SBI_EXT_SET_TIMER SBI_EXT_0_1_SET_TIMER #define SBI_FID_SET_TIMER 0 @@ -128,6 +159,8 @@ enum sbi_ext_dbcn_fid { #define SBI_FID_REMOTE_SFENCE_VMA_ASID SBI_EXT_RFENCE_REMOTE_SFENCE_VMA_ASID #endif +/* SBI spec version fields */ +#define SBI_SPEC_VERSION_DEFAULT 0x1 #define SBI_SPEC_VERSION_MAJOR_SHIFT 24 #define SBI_SPEC_VERSION_MAJOR_MASK 0x7f #define SBI_SPEC_VERSION_MINOR_MASK 0xffffff @@ -140,11 +173,65 @@ enum sbi_ext_dbcn_fid { #define SBI_ERR_DENIED -4 #define SBI_ERR_INVALID_ADDRESS -5 +/* SBI MPXY extension defines */ +#define SBI_MPXY_MAX_CHANNELS 16 +#define SBI_MPXY_MAX_MSG_WORDS 64 +#define SBI_MPXY_SHMEM_ALIGN 4096 +#define SBI_MPXY_MSGPROTO_RPMI_ID 0x0 + struct sbiret { long error; long value; }; +struct mpxy_channel; + +/** + * struct mpxy_proto_ops - Message protocol driver operations + * + * @proto_id: Protocol identifier + * @name: Protocol name + * @readattr: Read a protocol-specific attribute (attr_id >= 0x80000000) + * @writeattr: Write a protocol-specific attribute + * @send: Send a message and receive a response + */ +struct mpxy_proto_ops { + u32 proto_id; + const char *name; + + int (*readattr)(struct mpxy_channel *chan, u32 attr_id, u32 *attr_val); + int (*writeattr)(struct mpxy_channel *chan, u32 attr_id, u32 attr_val); +}; + +/* Standard channel attributes as returned by firmware */ +struct sbi_mpxy_channel_attrs { + u32 msg_proto_id; + u32 msg_proto_version; + u32 msg_max_len; + u32 msg_send_timeout; + u32 msg_completion_timeout; + u32 capability; + u32 sse_event_id; + u32 msi_control; + u32 msi_addr_lo; + u32 msi_addr_hi; + u32 msi_data; + u32 events_state_ctrl; +}; + +/* + * struct mpxy_channel - Represents a single MPXY channel + * + * @channel_id: MPXY Channel ID + * @std_attrs: MPXY channel standard attributes + * @proto: Pointer to the protocol driver instance + */ +struct mpxy_channel { + u32 channel_id; + struct sbi_mpxy_channel_attrs std_attrs; + const struct mpxy_proto_ops *proto; +}; + struct sbiret sbi_ecall(int ext, int fid, unsigned long arg0, unsigned long arg1, unsigned long arg2, unsigned long arg3, unsigned long arg4, @@ -165,6 +252,26 @@ void sbi_remote_sfence_vma_asid(const unsigned long *hart_mask, unsigned long size, unsigned long asid); #endif + +/* Get the major version of SBI */ +static inline unsigned long sbi_major_version(long version) +{ + return (version >> SBI_SPEC_VERSION_MAJOR_SHIFT) & + SBI_SPEC_VERSION_MAJOR_MASK; +} + +/* Get the minor version of SBI */ +static inline unsigned long sbi_minor_version(long version) +{ + return (version & SBI_SPEC_VERSION_MINOR_MASK); +} + +/* Check if current SBI specification version is 0.1 or not */ +static inline int sbi_spec_is_0_1(long version) +{ + return (version == SBI_SPEC_VERSION_DEFAULT) ? 1 : 0; +} + void sbi_set_timer(uint64_t stime_value); long sbi_get_spec_version(void); int sbi_get_impl_id(void); @@ -176,4 +283,165 @@ int sbi_get_mimpid(long *mimpid); void sbi_srst_reset(unsigned long type, unsigned long reason); int sbi_dbcn_write_byte(unsigned char ch); +/** + * sbi_set_timer() - Program the next timer event + * @stime_value: Absolute time value at which the timer should fire + */ +void sbi_set_timer(uint64_t stime_value); + +/** + * sbi_get_spec_version() - Get the SBI specification version implemented + * by the running SBI implementation + */ +long sbi_get_spec_version(void); + +/** + * sbi_get_impl_id() - Get the SBI implementation ID + */ +int sbi_get_impl_id(void); + +/** + * sbi_get_impl_version() - Get the SBI implementation version + * @version: Output pointer to store the implementation version + */ +int sbi_get_impl_version(long *version); + +/** + * sbi_probe_extension() - Check whether an SBI extension is available + * @ext: SBI extension ID to probe + */ +int sbi_probe_extension(int ext); + +/** + * sbi_get_mvendorid() - Get the value of the mvendorid CSR + * @mvendorid: Output pointer to store the mvendorid value + */ +int sbi_get_mvendorid(long *mvendorid); + +/** + * sbi_get_marchid() - Get the value of the marchid CSR + * @marchid: Output pointer to store the marchid value + */ +int sbi_get_marchid(long *marchid); + +/** + * sbi_get_mimpid() - Get the value of the mimpid CSR + * @mimpid: Output pointer to store the mimpid value + */ +int sbi_get_mimpid(long *mimpid); + +/** + * sbi_srst_reset() - Request a system reset via the SBI SRST extension + * @type: Reset type (e.g. shutdown, cold reboot, warm reboot) + * @reason: Reset reason code + */ +void sbi_srst_reset(unsigned long type, unsigned long reason); + +/** + * sbi_dbcn_write_byte() - Write a single byte via the SBI Debug Console + * extension + * @ch: Byte to write + */ +int sbi_dbcn_write_byte(unsigned char ch); + +/** + * sbi_mpxy_register_proto() - Register a message protocol driver + * @ops: Protocol ops to register + */ +int sbi_mpxy_register_proto(const struct mpxy_proto_ops *ops); + +/** + * sbi_mpxy_probe() - Probe MPXY extension, set up shmem, scan channels + */ +int sbi_mpxy_probe(void); + +/** + * sbi_mpxy_is_available() - Check whether MPXY was successfully probed + */ +bool sbi_mpxy_is_available(void); + +/** + * sbi_mpxy_shmem_ptr() - Return pointer to the shared memory window + */ +void *sbi_mpxy_shmem_ptr(void); + +/** + * sbi_mpxy_shmem_size() - Return size of shared memory in bytes + */ +u32 sbi_mpxy_shmem_size(void); + +/** + * sbi_mpxy_channel_count() - Return number of discovered channels + */ +int sbi_mpxy_channel_count(void); + +/** + * sbi_mpxy_get_channel() - Get channel by index + * @channel_id: Index in [0, sbi_mpxy_channel_count()) + */ +struct mpxy_channel *sbi_mpxy_get_channel(int channel_id); + +/** + * sbi_mpxy_find_channel() - Find channel by channel ID + * @channel_id: Channel ID to search for + */ +struct mpxy_channel *sbi_mpxy_find_channel(u32 channel_id); + +/** + * sbi_mpxy_read_attrs() - Read a contiguous range of channel attributes + * @chan: Target channel + * @base_attr_id: First attribute ID in the range + * @attr_count: Number of attributes to read + * @attrs_buf: Output buffer (attr_count u32 words) + */ +int sbi_mpxy_read_attrs(struct mpxy_channel *chan, u32 base_attr_id, + u32 attr_count, u32 *attrs_buf); + +/** + * sbi_mpxy_write_attrs() - Write a contiguous range of channel attributes + * + * @chan: Target channel + * @base_attr_id: First attribute ID in the range + * @attr_count: Number of attributes to write + * @attrs_buf: Input buffer (attr_count u32 words) + */ +int sbi_mpxy_write_attrs(struct mpxy_channel *chan, u32 base_attr_id, + u32 attr_count, u32 *attrs_buf); + +/** + * sbi_mpxy_send_message_with_resp() - Send a message and receive a response + * @chan: Target channel + * @msg_id: Message ID + * @tx: TX payload buffer (copied into shmem before ecall) + * @tx_len: TX payload length in bytes + * @rx: RX buffer (copied from shmem after ecall) + * @max_rx_len: RX buffer capacity in bytes + * @rx_len: Output: actual response length in bytes (from sret.value) + */ +int sbi_mpxy_send_message_with_resp(struct mpxy_channel *chan, u32 msg_id, + void *tx, unsigned long tx_len, + void *rx, unsigned long max_rx_len, + unsigned long *rx_len); + +/** + * sbi_mpxy_send_message_without_resp() - Send a message with no response + * @chan: Target channel + * @msg_id: Message ID + * @tx: TX payload buffer + * @tx_len: TX payload length in bytes + */ +int sbi_mpxy_send_message_without_resp(struct mpxy_channel *chan, u32 msg_id, + void *tx, unsigned long tx_len); + +/** + * sbi_mpxy_send() - Word-oriented send used by the mpxy command + * @chan: Target channel + * @tx: tx[0]=msg_id, tx[1..n-1]=payload words + * @tx_words: Total words including msg_id + * @rx: Receive buffer + * @rx_words: In: capacity; Out: words received + */ +int sbi_mpxy_send(struct mpxy_channel *chan, u32 *tx, int tx_words, + u32 *rx, int *rx_words); + #endif diff --git a/arch/riscv/lib/Makefile b/arch/riscv/lib/Makefile index f1f50918eff..32f8fb5dbbe 100644 --- a/arch/riscv/lib/Makefile +++ b/arch/riscv/lib/Makefile @@ -18,6 +18,7 @@ obj-$(CONFIG_ANDES_PLICSW) += andes_plicsw.o else obj-$(CONFIG_SBI) += sbi.o obj-$(CONFIG_SBI_IPI) += sbi_ipi.o +obj-$(CONFIG_SBI_MPXY) += sbi_mpxy.o endif obj-y += interrupts.o ifeq ($(CONFIG_$(PHASE_)SYSRESET),) diff --git a/arch/riscv/lib/sbi_mpxy.c b/arch/riscv/lib/sbi_mpxy.c new file mode 100644 index 00000000000..f95cedaabac --- /dev/null +++ b/arch/riscv/lib/sbi_mpxy.c @@ -0,0 +1,529 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * SBI MPXY (Message Proxy) Generic Library + * + * Copyright (c) 2026, Rahul Pathak <[email protected]> + */ + +#include <errno.h> +#include <log.h> +#include <malloc.h> +#include <asm/sbi.h> +#include <linux/string.h> +#include <linux/types.h> + +/** Maximum number of registered protocol drivers */ +#define MPXY_MAX_PROTOS 8 + +/** + * SBI MPXY channel IDs data in shared memory. + */ +struct sbi_mpxy_channel_ids_data { + /* Remaining number of channel ids */ + __le32 remaining; + /* Returned channel ids in current function call */ + __le32 returned; + /* Returned channel id array */ + __le32 channel_array[]; +}; + +/** + * MPXY Context + */ +struct mpxy_context { + phys_addr_t shmem_addr; + u32 shmem_size; + struct mpxy_channel channels[SBI_MPXY_MAX_CHANNELS]; + int channel_count; +}; + +static struct mpxy_context mpxy_ctx; +static int mpxy_proto_count; +static const struct mpxy_proto_ops *mpxy_protos[MPXY_MAX_PROTOS]; + +static bool mpxy_probe_done; +static bool mpxy_available; + +/** + * Protocol registration + */ +int sbi_mpxy_register_proto(const struct mpxy_proto_ops *ops) +{ + if (!ops) + return -EINVAL; + + if (mpxy_proto_count >= MPXY_MAX_PROTOS) { + log_err("sbi_mpxy: protocol table full, cannot register %s\n", + ops->name ? ops->name : "unknown"); + return -ENOSPC; + } + + mpxy_protos[mpxy_proto_count++] = ops; + return 0; +} + +/** + * Return MPXY shared memory base pointer + */ +static inline void *shmem_base(void) +{ + return (void *)(uintptr_t)mpxy_ctx.shmem_addr; +} + +/** + * Probe MPXY extension + */ +static int mpxy_check_extension(void) +{ + long ret; + + ret = sbi_get_spec_version(); + if (ret < 0 || sbi_spec_is_0_1(ret)) { + log_err("sbi_mpxy: SBI v0.2+ not detected\n"); + return -ENOTSUPP; + } + + ret = sbi_probe_extension(SBI_EXT_MPXY); + if (ret <= 0) { + log_err("sbi_mpxy: MPXY extension not available\n"); + return -ENOTSUPP; + } + + return 0; +} + +/** + * Setup the MPXY shared memory + */ +static int mpxy_setup_shmem(void) +{ + struct sbiret ret; + + ret = sbi_ecall(SBI_EXT_MPXY, SBI_EXT_MPXY_GET_SHMEM_SIZE, + 0, 0, 0, 0, 0, 0); + if (ret.error) { + log_err("sbi_mpxy: GET_SHMEM_SIZE failed (%ld)\n", ret.error); + return -EINVAL; + } + + mpxy_ctx.shmem_size = ret.value; + + mpxy_ctx.shmem_addr = (phys_addr_t)(uintptr_t)memalign(SBI_MPXY_SHMEM_ALIGN, + mpxy_ctx.shmem_size); + if (!mpxy_ctx.shmem_addr) { + log_err("sbi_mpxy: failed to allocate %u bytes for shmem\n", + mpxy_ctx.shmem_size); + return -ENOMEM; + } + + ret = sbi_ecall(SBI_EXT_MPXY, SBI_EXT_MPXY_SET_SHMEM, + mpxy_ctx.shmem_addr, 0, 0, 0, 0, 0); + if (ret.error) { + log_err("sbi_mpxy: SET_SHMEM failed (%ld)\n", ret.error); + free((void *)(uintptr_t)mpxy_ctx.shmem_addr); + mpxy_ctx.shmem_addr = 0; + return -EINVAL; + } + + log_debug("sbi_mpxy: shmem at 0x%llx, size %u\n", + (unsigned long long)mpxy_ctx.shmem_addr, + mpxy_ctx.shmem_size); + + return 0; +} + +/** + * Get MPXY channels present in the system + */ +static int mpxy_scan_channels(void) +{ + struct sbi_mpxy_channel_ids_data *sdata = shmem_base(); + u32 start = 0, total = 0; + u32 remaining, returned; + struct sbiret ret; + int i; + + do { + ret = sbi_ecall(SBI_EXT_MPXY, SBI_EXT_MPXY_GET_CHANNEL_IDS, + start, 0, 0, 0, 0, 0); + if (ret.error) { + log_err("sbi_mpxy: GET_CHANNEL_IDS failed (%ld)\n", + ret.error); + return -EINVAL; + } + + remaining = le32_to_cpu(sdata->remaining); + returned = le32_to_cpu(sdata->returned); + + for (i = 0; i < returned; i++) { + if (total >= SBI_MPXY_MAX_CHANNELS) { + log_warning("sbi_mpxy: channel table full, " + "truncating at %d\n", + SBI_MPXY_MAX_CHANNELS); + goto done; + } + mpxy_ctx.channels[total].channel_id = + le32_to_cpu(sdata->channel_array[i]); + total++; + } + + start += returned; + + } while (remaining); + +done: + mpxy_ctx.channel_count = total; + log_debug("sbi_mpxy: found %d channel(s)\n", total); + return 0; +} + + +static void mpxy_bind_protocols(void) +{ + int i, j; + + for (i = 0; i < mpxy_ctx.channel_count; i++) { + struct mpxy_channel *chan = &mpxy_ctx.channels[i]; + + chan->proto = NULL; + for (j = 0; j < mpxy_proto_count; j++) { + if (mpxy_protos[j]->proto_id == + chan->std_attrs.msg_proto_id) { + chan->proto = mpxy_protos[j]; + break; + } + } + + if (!chan->proto) + log_warning("sbi_mpxy: no driver for channel 0x%x " + "protocol 0x%x\n", + chan->channel_id, + chan->std_attrs.msg_proto_id); + else + log_debug("sbi_mpxy: channel 0x%x bound to %s\n", + chan->channel_id, chan->proto->name); + } +} + +int sbi_mpxy_probe(void) +{ + int i, ret; + + if (mpxy_probe_done) + return mpxy_available ? 0 : -ENOTSUPP; + + mpxy_probe_done = true; + + ret = mpxy_check_extension(); + if (ret) + goto fail; + + ret = mpxy_setup_shmem(); + if (ret) + goto fail; + + ret = mpxy_scan_channels(); + if (ret) + goto fail; + + for (i = 0; i < mpxy_ctx.channel_count; i++) { + ret = sbi_mpxy_read_attrs(&mpxy_ctx.channels[i], 0, + sizeof(struct sbi_mpxy_channel_attrs) / + sizeof(u32), + (u32 *)&mpxy_ctx.channels[i].std_attrs); + if (ret) + goto fail; + } + + mpxy_bind_protocols(); + + mpxy_available = true; + return 0; + +fail: + mpxy_available = false; + return ret; +} + +bool sbi_mpxy_is_available(void) +{ + return mpxy_available; +} + +int sbi_mpxy_channel_count(void) +{ + if (!sbi_mpxy_is_available()) + return 0; + + return mpxy_ctx.channel_count; +} + +struct mpxy_channel *sbi_mpxy_get_channel(int channel_id) +{ + if (channel_id < 0 || channel_id >= mpxy_ctx.channel_count) + return NULL; + return &mpxy_ctx.channels[channel_id]; +} + +struct mpxy_channel *sbi_mpxy_find_channel(u32 channel_id) +{ + int i; + + for (i = 0; i < mpxy_ctx.channel_count; i++) { + if (mpxy_ctx.channels[i].channel_id == channel_id) + return &mpxy_ctx.channels[i]; + } + return NULL; +} + +/** + * Return pointer to the MPXY shared memory + */ +void *sbi_mpxy_shmem_ptr(void) +{ + if (!sbi_mpxy_is_available()) + return NULL; + return shmem_base(); +} + +/** + * Get the MPXY shared memory size + */ +u32 sbi_mpxy_shmem_size(void) +{ + if (!sbi_mpxy_is_available()) + return 0; + + return mpxy_ctx.shmem_size; +} + +/** + * sbi_mpxy_read_attrs() - Read a contiguous range of channel attributes + * + * @chan: Target channel + * @base_attr_id: First attribute ID in the range + * @attr_count: Number of attributes to read + * @attrs_buf: Output buffer (attr_count u32 words) + */ +int sbi_mpxy_read_attrs(struct mpxy_channel *chan, u32 base_attr_id, + u32 attr_count, u32 *attrs_buf) +{ + struct sbiret ret; + __le32 *shmem; + u32 end_id; + int i; + + if (!chan || !attr_count || !attrs_buf) + return -EINVAL; + + if (attr_count > mpxy_ctx.shmem_size / sizeof(u32)) + return -EINVAL; + + shmem = (__le32 *)shmem_base(); + if (!shmem) + return -ENXIO; + + end_id = base_attr_id + attr_count - 1; + + if (base_attr_id < SBI_MPXY_ATTR_STD_ATTR_MAX_IDX) { + /* Standard attributes */ + if (end_id >= SBI_MPXY_ATTR_STD_ATTR_MAX_IDX) + return -EINVAL; + + ret = sbi_ecall(SBI_EXT_MPXY, SBI_EXT_MPXY_READ_ATTRS, + chan->channel_id, base_attr_id, attr_count, + 0, 0, 0); + if (ret.error) + return -EINVAL; + + for (i = 0; i < attr_count; i++) + attrs_buf[i] = le32_to_cpu(shmem[i]); + + return 0; + } + + if (base_attr_id >= SBI_MPXY_ATTR_MSGPROTO_ATTR_START && + end_id < SBI_MPXY_ATTR_MSGPROTO_ATTR_END) { + /* Protocol-specific attributes */ + if (!chan->proto || !chan->proto->readattr) + return -ENOSYS; + + ret = sbi_ecall(SBI_EXT_MPXY, SBI_EXT_MPXY_READ_ATTRS, + chan->channel_id, base_attr_id, attr_count, + 0, 0, 0); + if (ret.error) + return -EINVAL; + + for (i = 0; i < attr_count; i++) + attrs_buf[i] = le32_to_cpu(shmem[i]); + + return 0; + } + + return -EINVAL; +} + +/** + * sbi_mpxy_write_attrs() - Write a contiguous range of channel attributes + * + * @chan: Target channel + * @base_attr_id: First attribute ID in the range + * @attr_count: Number of attributes to write + * @attrs_buf: Input buffer (attr_count u32 words) + */ +int sbi_mpxy_write_attrs(struct mpxy_channel *chan, u32 base_attr_id, + u32 attr_count, u32 *attrs_buf) +{ + struct sbiret ret; + __le32 *shmem; + u32 end_id; + int i; + + if (!chan || !attr_count || !attrs_buf) + return -EINVAL; + + if (attr_count > mpxy_ctx.shmem_size / sizeof(u32)) + return -EINVAL; + + shmem = (__le32 *)shmem_base(); + if (!shmem) + return -ENXIO; + + end_id = base_attr_id + attr_count - 1; + + if (base_attr_id < SBI_MPXY_ATTR_STD_ATTR_MAX_IDX) { + /* Standard attributes */ + if (end_id >= SBI_MPXY_ATTR_STD_ATTR_MAX_IDX) + return -EINVAL; + + for (i = 0; i < attr_count; i++) + shmem[i] = cpu_to_le32(attrs_buf[i]); + + ret = sbi_ecall(SBI_EXT_MPXY, SBI_EXT_MPXY_WRITE_ATTRS, + chan->channel_id, base_attr_id, attr_count, + 0, 0, 0); + return ret.error ? -EINVAL : 0; + } + + if (base_attr_id >= SBI_MPXY_ATTR_MSGPROTO_ATTR_START && + end_id < SBI_MPXY_ATTR_MSGPROTO_ATTR_END) { + /* Protocol-specific attributes */ + if (!chan->proto || !chan->proto->writeattr) + return -ENOSYS; + + for (i = 0; i < attr_count; i++) { + ret.error = chan->proto->writeattr(chan, + base_attr_id + i, + attrs_buf[i]); + if (ret.error) + return ret.error; + } + return 0; + } + + return -EINVAL; +} + +/** + * sbi_mpxy_send_message_with_resp() - Send a message and receive a response + */ +int sbi_mpxy_send_message_with_resp(struct mpxy_channel *chan, u32 msg_id, + void *tx, unsigned long tx_len, + void *rx, unsigned long max_rx_len, + unsigned long *rx_len) +{ + unsigned long rx_bytes; + struct sbiret ret; + void *shmem; + + if (!chan) + return -EINVAL; + if (!tx && tx_len) + return -EINVAL; + + shmem = sbi_mpxy_shmem_ptr(); + if (!shmem) + return -ENXIO; + + if (tx_len) + memcpy(shmem, tx, tx_len); + + ret = sbi_ecall(SBI_EXT_MPXY, SBI_EXT_MPXY_SEND_MSG_WITH_RESP, + chan->channel_id, msg_id, tx_len, 0, 0, 0); + if (ret.error) + return -EINVAL; + + if (rx) { + rx_bytes = ret.value; + if (rx_bytes > max_rx_len) + return -ENOSPC; + memcpy(rx, shmem, rx_bytes); + if (rx_len) + *rx_len = rx_bytes; + } + + return 0; +} + +/** + * sbi_mpxy_send_message_without_resp() - Send a message with no response + */ +int sbi_mpxy_send_message_without_resp(struct mpxy_channel *chan, u32 msg_id, + void *tx, unsigned long tx_len) +{ + struct sbiret ret; + void *shmem; + + if (!chan) + return -EINVAL; + if (!tx && tx_len) + return -EINVAL; + + shmem = sbi_mpxy_shmem_ptr(); + if (!shmem) + return -ENXIO; + + if (tx_len) + memcpy(shmem, tx, tx_len); + + ret = sbi_ecall(SBI_EXT_MPXY, SBI_EXT_MPXY_SEND_MSG_WITHOUT_RESP, + chan->channel_id, msg_id, tx_len, 0, 0, 0); + + return ret.error ? -EINVAL : 0; +} + +/** + * sbi_mpxy_send() - Word-oriented send used by the mpxy command + * + * tx[0] = msg_id, tx[1..n-1] = payload words. + * Wraps sbi_mpxy_send_message_with_resp(). + */ +int sbi_mpxy_send(struct mpxy_channel *chan, u32 *tx, int tx_words, + u32 *rx, int *rx_words) +{ + unsigned long rx_len = 0; + unsigned long tx_len; + u32 msg_id; + int ret; + + if (!chan || !tx || !rx || !rx_words) + return -EINVAL; + + if (tx_words < 1) + return -EINVAL; + + msg_id = tx[0]; + tx_len = (tx_words - 1) * sizeof(u32); + + ret = sbi_mpxy_send_message_with_resp(chan, msg_id, + tx_words > 1 ? &tx[1] : NULL, + tx_len, + rx, + (unsigned long)(*rx_words) * sizeof(u32), + &rx_len); + if (ret) + return ret; + + *rx_words = (int)(rx_len / sizeof(u32)); + return 0; +} -- 2.43.0

