@@ -428,6 +428,9 @@ enum sbi_sse_state {
#define SBI_ERR_NO_SHMEM -9
#define SBI_ERR_INVALID_STATE -10
#define SBI_ERR_BAD_RANGE -11
+#define SBI_ERR_NOT_IMPLEMENTED -12
+#define SBI_ERR_TIMEOUT -13
+#define SBI_ERR_IO -14
#define SBI_LAST_ERR SBI_ERR_BAD_RANGE
@@ -26,16 +26,18 @@
#define SBI_ENO_SHMEM SBI_ERR_NO_SHMEM
#define SBI_EINVALID_STATE SBI_ERR_INVALID_STATE
#define SBI_EBAD_RANGE SBI_ERR_BAD_RANGE
+#define SBI_ENOTIMPL SBI_ERR_NOT_IMPLEMENTED
+#define SBI_ETIMEOUT SBI_ERR_TIMEOUT
+#define SBI_EIO SBI_ERR_IO
#define SBI_ENODEV -1000
#define SBI_ENOSYS -1001
#define SBI_ETIMEDOUT -1002
-#define SBI_EIO -1003
-#define SBI_EILL -1004
-#define SBI_ENOSPC -1005
-#define SBI_ENOMEM -1006
-#define SBI_EUNKNOWN -1007
-#define SBI_ENOENT -1008
+#define SBI_EILL -1003
+#define SBI_ENOSPC -1004
+#define SBI_ENOMEM -1005
+#define SBI_EUNKNOWN -1006
+#define SBI_ENOENT -1007
/* clang-format on */
new file mode 100644
@@ -0,0 +1,181 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2024 Ventana Micro Systems Inc.
+ *
+ * Authors:
+ * Rahul Pathak <rpathak@ventanamicro.com>
+ */
+
+#ifndef __SBI_MPXY_H__
+#define __SBI_MPXY_H__
+
+#include <sbi/sbi_list.h>
+
+struct sbi_scratch;
+
+#define SBI_MPXY_MSGPROTO_VERSION(Major, Minor) ((Major << 16) | Minor)
+
+/** Channel Capability - Events State */
+#define CAP_EVENTSSTATE_POS 2
+#define CAP_EVENTSSTATE_MASK (1U << CAP_EVENTSSTATE_POS)
+
+/** Helpers to enable/disable channel capability bits
+ * _c: capability variable
+ * _m: capability mask
+ */
+#define CAP_ENABLE(_c, _m) INSERT_FIELD(_c, _m, 1)
+#define CAP_DISABLE(_c, _m) INSERT_FIELD(_c, _m, 0)
+#define CAP_GET(_c, _m) EXTRACT_FIELD(_c, _m)
+
+enum sbi_mpxy_attr_id {
+ /* Standard channel attributes managed by MPXY framework */
+ 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_CHANNEL_CAPABILITY = 0x00000004,
+ SBI_MPXY_ATTR_MSI_CONTROL = 0x00000005,
+ SBI_MPXY_ATTR_MSI_ADDR_LO = 0x00000006,
+ SBI_MPXY_ATTR_MSI_ADDR_HI = 0x00000007,
+ SBI_MPXY_ATTR_MSI_DATA = 0x00000008,
+ SBI_MPXY_ATTR_SSE_EVENT_ID = 0x00000009,
+ SBI_MPXY_ATTR_EVENTS_STATE_CONTROL = 0x0000000A,
+ SBI_MPXY_ATTR_STD_ATTR_MAX_IDX,
+ /* Message protocol specific attributes, managed by
+ * message protocol driver */
+ SBI_MPXY_ATTR_MSGPROTO_ATTR_START = 0x80000000,
+ SBI_MPXY_ATTR_MSGPROTO_ATTR_END = 0xffffffff
+};
+
+/**
+ * SBI MPXY Message Protocol IDs
+ */
+enum sbi_mpxy_msgproto_id {
+ SBI_MPXY_MSGPROTO_RPMI_ID = 0x0,
+};
+
+enum SBI_EXT_MPXY_SHMEM_FLAGS {
+ SBI_EXT_MPXY_SHMEM_FLAG_OVERWRITE = 0b00,
+ SBI_EXT_MPXY_SHMEM_FLAG_OVERWRITE_RETURN = 0b01,
+ SBI_EXT_MPXY_SHMEM_FLAG_MAX_IDX
+};
+
+struct sbi_mpxy_msi_info {
+ /* MSI target address low 32-bit */
+ u32 msi_addr_lo;
+ /* MSI target address high 32-bit */
+ u32 msi_addr_hi;
+ /* MSI data */
+ u32 msi_data;
+};
+
+/**
+ * Channel attributes.
+ * NOTE: The sequence of attribute fields are as per the
+ * defined sequence in the attribute table in spec(or as
+ * per the enum sbi_mpxy_attr_id).
+ */
+struct sbi_mpxy_channel_attrs {
+ /* Message protocol ID */
+ u32 msg_proto_id;
+ /* Message protocol Version */
+ u32 msg_proto_version;
+ /* Message protocol maximum message data length(bytes) */
+ u32 msg_data_maxlen;
+ /* Message protocol message send timeout
+ * in microseconds */
+ u32 msg_send_timeout;
+ /* Bit array for channel capabilities */
+ u32 capability;
+ u32 msi_control;
+ struct sbi_mpxy_msi_info msi_info;
+ u32 sse_event_id;
+ /* Events State Control */
+ u32 eventsstate_ctrl;
+};
+
+/** A Message proxy channel accessible through SBI interface */
+struct sbi_mpxy_channel {
+ /** List head to a set of channels */
+ struct sbi_dlist head;
+ u32 channel_id;
+ struct sbi_mpxy_channel_attrs attrs;
+
+ /**
+ * Read message protocol attributes
+ * NOTE: inmem requires little-endian byte-ordering
+ */
+ int (*read_attributes)(struct sbi_mpxy_channel *channel,
+ u32 *outmem,
+ u32 base_attr_id,
+ u32 attr_count);
+
+ /**
+ * Write message protocol attributes
+ * NOTE: outmem requires little-endian byte-ordering
+ */
+ int (*write_attributes)(struct sbi_mpxy_channel *channel,
+ u32 *inmem,
+ u32 base_attr_id,
+ u32 attr_count);
+ /**
+ * Send a message over a channel
+ * NOTE: For message without response, resp_len == NULL
+ * msgbuf requires little-endian byte-ordering
+ */
+ int (*send_message)(struct sbi_mpxy_channel *channel,
+ u32 msg_id, void *msgbuf, u32 msg_len,
+ void *respbuf, u32 resp_max_len,
+ unsigned long *resp_len);
+
+ /**
+ * Get notifications events if supported on a channel
+ * NOTE: eventsbuf requires little-endian byte-ordering
+ */
+ int (*get_notification_events)(struct sbi_mpxy_channel *channel,
+ void *eventsbuf, u32 bufsize,
+ unsigned long *events_len);
+
+ void (*switch_eventsstate)(u32 enable);
+};
+
+/** Register a Message proxy channel */
+int sbi_mpxy_register_channel(struct sbi_mpxy_channel *channel);
+
+/** Initialize Message proxy subsystem */
+int sbi_mpxy_init(struct sbi_scratch *scratch);
+
+/** Check if some Message proxy channel is available */
+bool sbi_mpxy_channel_available(void);
+
+/** Set Message proxy shared memory on the calling HART */
+int sbi_mpxy_set_shmem(unsigned long shmem_size,
+ unsigned long shmem_phys_lo,
+ unsigned long shmem_phys_hi,
+ unsigned long flags);
+
+/** Get channel IDs list */
+int sbi_mpxy_get_channel_ids(u32 start_index);
+
+/** Read MPXY channel attributes */
+int sbi_mpxy_read_attrs(u32 channel_id, u32 base_attr_id, u32 attr_count);
+
+/** Write MPXY channel attributes */
+int sbi_mpxy_write_attrs(u32 channel_id, u32 base_attr_id, u32 attr_count);
+
+/**
+ * Send a message over a MPXY channel.
+ * For message with response the resp_data_len must point
+ * to valid buffer.
+ * For message without response the resp_data_len must be NULL
+ **/
+int sbi_mpxy_send_message(u32 channel_id, u8 msg_id,
+ unsigned long msg_data_len,
+ unsigned long *resp_data_len);
+
+/** Get Message proxy notification events */
+int sbi_mpxy_get_notification_events(u32 channel_id,
+ unsigned long *events_len);
+
+#endif
@@ -132,6 +132,9 @@ struct sbi_platform_operations {
/** Exit platform timer for current HART */
void (*timer_exit)(void);
+ /** Initialize the platform Message Proxy(MPXY) driver */
+ int (*mpxy_init)(void);
+
/** Check if SBI vendor extension is implemented or not */
bool (*vendor_ext_check)(void);
/** platform specific SBI extension implementation provider */
@@ -627,6 +630,20 @@ static inline void sbi_platform_timer_exit(const struct sbi_platform *plat)
sbi_platform_ops(plat)->timer_exit();
}
+/**
+ * Initialize the platform Message Proxy drivers
+ *
+ * @param plat pointer to struct sbi_platform
+ *
+ * @return 0 on success and negative error code on failure
+ */
+static inline int sbi_platform_mpxy_init(const struct sbi_platform *plat)
+{
+ if (plat && sbi_platform_ops(plat)->mpxy_init)
+ return sbi_platform_ops(plat)->mpxy_init();
+ return 0;
+}
+
/**
* Check if SBI vendor extension is implemented or not.
*
@@ -81,6 +81,7 @@ libsbi-objs-y += sbi_irqchip.o
libsbi-objs-y += sbi_platform.o
libsbi-objs-y += sbi_pmu.o
libsbi-objs-y += sbi_dbtr.o
+libsbi-objs-y += sbi_mpxy.o
libsbi-objs-y += sbi_scratch.o
libsbi-objs-y += sbi_sse.o
libsbi-objs-y += sbi_string.o
@@ -24,6 +24,7 @@
#include <sbi/sbi_platform.h>
#include <sbi/sbi_pmu.h>
#include <sbi/sbi_dbtr.h>
+#include <sbi/sbi_mpxy.h>
#include <sbi/sbi_sse.h>
#include <sbi/sbi_system.h>
#include <sbi/sbi_string.h>
@@ -311,6 +312,11 @@ static void __noreturn init_coldboot(struct sbi_scratch *scratch, u32 hartid)
sbi_hart_hang();
}
+ rc = sbi_mpxy_init(scratch);
+ if (rc) {
+ sbi_printf("%s: mpxy init failed (error %d)\n", __func__, rc);
+ sbi_hart_hang();
+ }
/*
* Note: Finalize domains after HSM initialization so that we
* can startup non-root domains.
new file mode 100644
@@ -0,0 +1,644 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2024 Ventana Micro Systems Inc.
+ *
+ * Authors:
+ * Rahul Pathak <rpathak@ventanamicro.com>
+ */
+
+#include <sbi/riscv_asm.h>
+#include <sbi/sbi_domain.h>
+#include <sbi/sbi_error.h>
+#include <sbi/sbi_hart.h>
+#include <sbi/sbi_platform.h>
+#include <sbi/sbi_mpxy.h>
+#include <sbi/sbi_scratch.h>
+#include <sbi/sbi_string.h>
+#include <sbi/sbi_bitops.h>
+#include <sbi/sbi_console.h>
+#include <sbi/sbi_byteorder.h>
+
+/** Offset of pointer to MPXY state in scratch space */
+static unsigned long mpxy_state_offset;
+
+/** List of MPXY proxy channels */
+static SBI_LIST_HEAD(mpxy_channel_list);
+
+/** Invalid Physical Address(all bits 1) */
+#define INVALID_ADDR (-1U)
+
+/** MPXY Attribute size in bytes */
+#define ATTR_SIZE (4)
+
+/** Channel Capability - MSI */
+#define CAP_MSI_POS 0
+#define CAP_MSI_MASK (1U << CAP_MSI_POS)
+/** Channel Capability - SSE */
+#define CAP_SSE_POS 1
+#define CAP_SSE_MASK (1U << CAP_SSE_POS)
+
+#if __riscv_xlen == 64
+#define SHMEM_PHYS_ADDR(_hi, _lo) (_lo)
+#elif __riscv_xlen == 32
+#define SHMEM_PHYS_ADDR(_hi, _lo) (((u64)(_hi) << 32) | (_lo))
+#else
+#error "Undefined XLEN"
+#endif
+
+/** Per hart shared memory */
+struct mpxy_shmem {
+ unsigned long shmem_size;
+ unsigned long shmem_addr_lo;
+ unsigned long shmem_addr_hi;
+};
+
+struct mpxy_state {
+ /* MSI support in MPXY */
+ bool msi_avail;
+ /* SSE support in MPXY */
+ bool sse_avail;
+ /* MPXY Shared memory details */
+ struct mpxy_shmem shmem;
+};
+
+/** Disable hart shared memory */
+static inline void sbi_mpxy_shmem_disable(struct mpxy_state *rs)
+{
+ rs->shmem.shmem_size = 0;
+ rs->shmem.shmem_addr_lo = INVALID_ADDR;
+ rs->shmem.shmem_addr_hi = INVALID_ADDR;
+}
+
+/** Check if shared memory is already setup on hart */
+static inline bool mpxy_shmem_enabled(struct mpxy_state *rs)
+{
+ return (rs->shmem.shmem_addr_lo == INVALID_ADDR
+ && rs->shmem.shmem_addr_hi == INVALID_ADDR) ?
+ false : true;
+}
+
+/** Get hart shared memory base address */
+static inline void *hart_shmem_base(struct mpxy_state *rs)
+{
+ return (void *)(unsigned long)SHMEM_PHYS_ADDR(rs->shmem.shmem_addr_hi,
+ rs->shmem.shmem_addr_lo);
+}
+
+
+/** Make sure all attributes are packed for direct memcpy in ATTR_READ */
+#define assert_field_offset(field, attr_offset) \
+ _Static_assert( \
+ ((offsetof(struct sbi_mpxy_channel_attrs, field)) / \
+ sizeof(u32)) == attr_offset, \
+ "field " #field \
+ " from struct sbi_mpxy_channel_attrs invalid offset, expected " #attr_offset)
+
+assert_field_offset(msg_proto_id, SBI_MPXY_ATTR_MSG_PROT_ID);
+assert_field_offset(msg_proto_version, SBI_MPXY_ATTR_MSG_PROT_VER);
+assert_field_offset(msg_data_maxlen, SBI_MPXY_ATTR_MSG_MAX_LEN);
+assert_field_offset(msg_send_timeout, SBI_MPXY_ATTR_MSG_SEND_TIMEOUT);
+assert_field_offset(capability, SBI_MPXY_ATTR_CHANNEL_CAPABILITY);
+assert_field_offset(msi_control, SBI_MPXY_ATTR_MSI_CONTROL);
+assert_field_offset(msi_info.msi_addr_lo, SBI_MPXY_ATTR_MSI_ADDR_LO);
+assert_field_offset(msi_info.msi_addr_hi, SBI_MPXY_ATTR_MSI_ADDR_HI);
+assert_field_offset(msi_info.msi_data, SBI_MPXY_ATTR_MSI_DATA);
+assert_field_offset(sse_event_id, SBI_MPXY_ATTR_SSE_EVENT_ID);
+assert_field_offset(eventsstate_ctrl, SBI_MPXY_ATTR_EVENTS_STATE_CONTROL);
+
+/**
+ * Check if the attribute is a standard attribute or
+ * a message protocol specific attribute
+ * attr_id[31] = 0 for standard
+ * attr_id[31] = 1 for message protocol specific
+ */
+static inline bool mpxy_is_std_attr(u32 attr_id)
+{
+ return (attr_id >> 31) ? false : true;
+}
+
+/** Find channel_id in registered channels list */
+static struct sbi_mpxy_channel *mpxy_find_channel(u32 channel_id)
+{
+ struct sbi_mpxy_channel *channel;
+
+ sbi_list_for_each_entry(channel, &mpxy_channel_list, head)
+ if (channel->channel_id == channel_id)
+ return channel;
+
+ return NULL;
+}
+
+/** Copy attributes word size */
+static void mpxy_copy_std_attrs(u32 *outmem, u32 *inmem, u32 count)
+{
+ int idx;
+ for (idx = 0; idx < count; idx++)
+ outmem[idx] = cpu_to_le32(inmem[idx]);
+}
+
+/** Check if any channel is registered with mpxy framework */
+bool sbi_mpxy_channel_available(void)
+{
+ return sbi_list_empty(&mpxy_channel_list) ? false : true;
+}
+
+static void mpxy_std_attrs_init(struct sbi_mpxy_channel *channel)
+{
+ struct mpxy_state *rs =
+ sbi_scratch_thishart_offset_ptr(mpxy_state_offset);
+
+ /* Reset values */
+ channel->attrs.msi_control = 0;
+ channel->attrs.msi_info.msi_data = 0;
+ channel->attrs.msi_info.msi_addr_lo = INVALID_ADDR;
+ channel->attrs.msi_info.msi_addr_hi = INVALID_ADDR;
+ channel->attrs.capability = 0;
+ channel->attrs.eventsstate_ctrl = 0;
+
+ /**
+ * Check if MSI or SSE available for notification interrrupt.
+ * Priority given to MSI if both MSI and SSE are avaialble.
+ */
+ if (rs->msi_avail)
+ channel->attrs.capability =
+ CAP_ENABLE(channel->attrs.capability, CAP_MSI_MASK);
+ else if (rs->sse_avail) {
+ channel->attrs.capability =
+ CAP_ENABLE(channel->attrs.capability, CAP_SSE_MASK);
+ /* TODO: Assign SSE EVENT_ID for the channel */
+ }
+
+ /**
+ * Enable Events State in channel capability if message protocol
+ * provides callback to switch
+ */
+ if (channel->switch_eventsstate)
+ channel->attrs.capability =
+ CAP_ENABLE(channel->attrs.capability,
+ CAP_EVENTSSTATE_MASK);
+}
+
+/**
+ * Register a channel with MPXY framework.
+ * Called by message protocol drivers
+ */
+int sbi_mpxy_register_channel(struct sbi_mpxy_channel *channel)
+{
+ if (!channel)
+ return SBI_EINVAL;
+
+ if (mpxy_find_channel(channel->channel_id))
+ return SBI_EALREADY;
+
+ /* Initialize channel specific attributes */
+ mpxy_std_attrs_init(channel);
+
+ SBI_INIT_LIST_HEAD(&channel->head);
+ sbi_list_add_tail(&channel->head, &mpxy_channel_list);
+
+ return SBI_OK;
+}
+
+int sbi_mpxy_init(struct sbi_scratch *scratch)
+{
+ mpxy_state_offset = sbi_scratch_alloc_type_offset(struct mpxy_state);
+ if (!mpxy_state_offset)
+ return SBI_ENOMEM;
+
+ /** TODO: Proper support for checking msi support from platform.
+ * Currently disable msi and sse and use polling
+ **/
+ struct mpxy_state *rs =
+ sbi_scratch_thishart_offset_ptr(mpxy_state_offset);
+ rs->msi_avail = false;
+ rs->sse_avail = false;
+
+ sbi_mpxy_shmem_disable(rs);
+
+ return sbi_platform_mpxy_init(sbi_platform_ptr(scratch));
+}
+
+int sbi_mpxy_set_shmem(unsigned long shmem_size, unsigned long shmem_phys_lo,
+ unsigned long shmem_phys_hi, unsigned long flags)
+{
+ struct mpxy_state *rs =
+ sbi_scratch_thishart_offset_ptr(mpxy_state_offset);
+ unsigned long *ret_buf;
+
+ shmem_size = lle_to_cpu(shmem_size);
+ shmem_phys_hi = lle_to_cpu(shmem_phys_hi);
+ shmem_phys_lo = lle_to_cpu(shmem_phys_lo);
+
+ /** Disable shared memory if both hi and lo have all bit 1s */
+ if (shmem_phys_lo == INVALID_ADDR &&
+ shmem_phys_hi == INVALID_ADDR) {
+ sbi_mpxy_shmem_disable(rs);
+ return SBI_SUCCESS;
+ }
+
+ if (flags >= SBI_EXT_MPXY_SHMEM_FLAG_MAX_IDX)
+ return SBI_ERR_INVALID_PARAM;
+
+ /** Check shared memory size and address aligned to 4K Page */
+ if (!shmem_size || (shmem_size & ~PAGE_MASK) ||
+ (shmem_phys_lo & ~PAGE_MASK))
+ return SBI_ERR_INVALID_PARAM;
+
+ if (!sbi_domain_check_addr_range(sbi_domain_thishart_ptr(),
+ SHMEM_PHYS_ADDR(shmem_phys_hi, shmem_phys_lo),
+ shmem_size, PRV_S,
+ SBI_DOMAIN_READ | SBI_DOMAIN_WRITE))
+ return SBI_ERR_INVALID_ADDRESS;
+
+ /** Save the current shmem details in new shmem region */
+ if (flags == SBI_EXT_MPXY_SHMEM_FLAG_OVERWRITE_RETURN) {
+ ret_buf = (unsigned long *)(ulong)SHMEM_PHYS_ADDR(shmem_phys_hi, shmem_phys_lo);
+ ret_buf[0] = cpu_to_lle(rs->shmem.shmem_size);
+ ret_buf[1] = cpu_to_lle(rs->shmem.shmem_addr_lo);
+ ret_buf[2] = cpu_to_lle(rs->shmem.shmem_addr_hi);
+ }
+
+ /** Setup the new shared memory */
+ rs->shmem.shmem_size = shmem_size;
+ rs->shmem.shmem_addr_lo = shmem_phys_lo;
+ rs->shmem.shmem_addr_hi = shmem_phys_hi;
+
+ return SBI_SUCCESS;
+}
+
+int sbi_mpxy_get_channel_ids(u32 start_index)
+{
+ u32 node_index = 0, node_ret = 0;
+ u32 remaining, returned, max_channelids;
+ u32 channels_count = 0;
+ u32 *shmem_base;
+ struct sbi_mpxy_channel *channel;
+
+ /* Check if the shared memory is being setup or not. */
+ struct mpxy_state *rs =
+ sbi_scratch_thishart_offset_ptr(mpxy_state_offset);
+
+ if (!mpxy_shmem_enabled(rs))
+ return SBI_ERR_NO_SHMEM;
+
+ sbi_list_for_each_entry(channel, &mpxy_channel_list, head)
+ channels_count += 1;
+
+ if (start_index > channels_count)
+ return SBI_ERR_INVALID_PARAM;
+
+ shmem_base = hart_shmem_base(rs);
+ sbi_hart_map_saddr((unsigned long)hart_shmem_base(rs),
+ rs->shmem.shmem_size);
+
+ /** number of channel ids which can be stored in shmem adjusting
+ * for remaining and returned fields */
+ max_channelids = (rs->shmem.shmem_size / sizeof(u32)) - 2;
+ /* total remaining from the start index */
+ remaining = channels_count - start_index;
+ /* how many can be returned */
+ returned = (remaining > max_channelids)? max_channelids : remaining;
+
+ // Iterate over the list of channels to get the channel ids.
+ sbi_list_for_each_entry(channel, &mpxy_channel_list, head) {
+ if (node_index >= start_index &&
+ node_index < (start_index + returned)) {
+ shmem_base[2 + node_ret] = cpu_to_le32(channel->channel_id);
+ node_ret += 1;
+ }
+
+ node_index += 1;
+ }
+
+ /* final remaininig channel ids */
+ remaining = channels_count - (start_index + returned);
+
+ shmem_base[0] = cpu_to_le32(remaining);
+ shmem_base[1] = cpu_to_le32(returned);
+
+ sbi_hart_unmap_saddr();
+
+ return SBI_SUCCESS;
+}
+
+int sbi_mpxy_read_attrs(u32 channel_id, u32 base_attr_id, u32 attr_count)
+{
+ int ret = SBI_SUCCESS;
+ u32 *attr_ptr, end_id;
+ void *shmem_base;
+
+ struct mpxy_state *rs =
+ sbi_scratch_thishart_offset_ptr(mpxy_state_offset);
+
+ if (!mpxy_shmem_enabled(rs))
+ return SBI_ERR_NO_SHMEM;
+
+ struct sbi_mpxy_channel *channel = mpxy_find_channel(channel_id);
+ if (!channel)
+ return SBI_ERR_NOT_SUPPORTED;
+
+ /* base attribute id is not a defined std attribute or reserved */
+ if (base_attr_id >= SBI_MPXY_ATTR_STD_ATTR_MAX_IDX &&
+ base_attr_id < SBI_MPXY_ATTR_MSGPROTO_ATTR_START)
+ return SBI_ERR_INVALID_PARAM;
+
+ /* Sanity check for base_attr_id and attr_count */
+ if (!attr_count || (attr_count > (rs->shmem.shmem_size / ATTR_SIZE)))
+ return SBI_ERR_INVALID_PARAM;
+
+ shmem_base = hart_shmem_base(rs);
+ end_id = base_attr_id + attr_count - 1;
+
+ sbi_hart_map_saddr((unsigned long)hart_shmem_base(rs),
+ rs->shmem.shmem_size);
+
+ /* Standard attributes range check */
+ if (mpxy_is_std_attr(base_attr_id)) {
+ if (end_id >= SBI_MPXY_ATTR_STD_ATTR_MAX_IDX) {
+ ret = SBI_EBAD_RANGE;
+ goto out;
+ }
+
+ attr_ptr = (u32 *)&channel->attrs;
+ mpxy_copy_std_attrs((u32 *)shmem_base, &attr_ptr[base_attr_id],
+ attr_count);
+ } else {
+ /**
+ * Even if the message protocol driver does not provide
+ * read attribute callback, return bad range error instead
+ * of not supported to let client distinguish it from channel
+ * id not supported.
+ * Check the complate range supported for message protocol
+ * attributes. Actual supported attributes will be checked
+ * by the message protocol driver.
+ */
+ if (!channel->read_attributes ||
+ end_id > SBI_MPXY_ATTR_MSGPROTO_ATTR_END) {
+ ret = SBI_ERR_BAD_RANGE;
+ goto out;
+ }
+
+ /**
+ * Function expected to return the SBI supported errors
+ * At this point both base attribute id and only the mpxy
+ * supported range been verified. Platform callback must
+ * check if the range requested is supported by message
+ * protocol driver */
+ ret = channel->read_attributes(channel,
+ (u32 *)shmem_base,
+ base_attr_id, attr_count);
+ }
+out:
+ sbi_hart_unmap_saddr();
+ return ret;
+}
+
+/**
+ * Verify the channel standard attribute wrt to write permission
+ * and the value to be set if valid or not.
+ * Only attributes needs to be checked which are defined Read/Write
+ * permission. Other with Readonly permission will result in error.
+ *
+ * Attributes values to be written must also be checked because
+ * before writing a range of attributes, we need to make sure that
+ * either complete range of attributes is written successfully or not
+ * at all.
+ */
+static int mpxy_check_write_std_attr(struct sbi_mpxy_channel *channel,
+ u32 attr_id, u32 attr_val)
+{
+ int ret = SBI_SUCCESS;
+ struct sbi_mpxy_channel_attrs *attrs = &channel->attrs;
+
+ switch(attr_id) {
+ case SBI_MPXY_ATTR_MSI_CONTROL:
+ if (attr_val > 1)
+ ret = SBI_ERR_INVALID_PARAM;
+ if (attr_val == 1 &&
+ (attrs->msi_info.msi_addr_lo == INVALID_ADDR) &&
+ (attrs->msi_info.msi_addr_hi == INVALID_ADDR))
+ ret = SBI_ERR_DENIED;
+ break;
+ case SBI_MPXY_ATTR_MSI_ADDR_LO:
+ case SBI_MPXY_ATTR_MSI_ADDR_HI:
+ case SBI_MPXY_ATTR_MSI_DATA:
+ ret = SBI_SUCCESS;
+ break;
+ case SBI_MPXY_ATTR_EVENTS_STATE_CONTROL:
+ if (attr_val > 1)
+ ret = SBI_ERR_INVALID_PARAM;
+ break;
+ default:
+ /** All RO access attributes falls under default */
+ ret = SBI_ERR_BAD_RANGE;
+ };
+
+ return ret;
+}
+
+/**
+ * Write the attribute value
+ */
+static void mpxy_write_std_attr(struct sbi_mpxy_channel *channel, u32 attr_id,
+ u32 attr_val)
+{
+ struct mpxy_state *rs =
+ sbi_scratch_thishart_offset_ptr(mpxy_state_offset);
+
+ struct sbi_mpxy_channel_attrs *attrs = &channel->attrs;
+
+ switch(attr_id) {
+ case SBI_MPXY_ATTR_MSI_CONTROL:
+ if (rs->msi_avail && attr_val <= 1)
+ attrs->msi_control = attr_val;
+ break;
+ case SBI_MPXY_ATTR_MSI_ADDR_LO:
+ if (rs->msi_avail)
+ attrs->msi_info.msi_addr_lo = attr_val;
+ break;
+ case SBI_MPXY_ATTR_MSI_ADDR_HI:
+ if (rs->msi_avail)
+ attrs->msi_info.msi_addr_hi = attr_val;
+ break;
+ case SBI_MPXY_ATTR_MSI_DATA:
+ if (rs->msi_avail)
+ attrs->msi_info.msi_data = attr_val;
+ break;
+ case SBI_MPXY_ATTR_EVENTS_STATE_CONTROL:
+ if (CAP_GET(attrs->capability, CAP_EVENTSSTATE_MASK)) {
+ attrs->eventsstate_ctrl = attr_val;
+ /* call message protocol callback */
+ channel->switch_eventsstate(attr_val);
+ }
+
+ break;
+ };
+}
+
+int sbi_mpxy_write_attrs(u32 channel_id, u32 base_attr_id, u32 attr_count)
+{
+ int ret, mem_idx;
+ void *shmem_base;
+ u32 *mem_ptr, attr_id, end_id, attr_val;
+
+ struct mpxy_state *rs =
+ sbi_scratch_thishart_offset_ptr(mpxy_state_offset);
+
+ if (!mpxy_shmem_enabled(rs))
+ return SBI_ERR_NO_SHMEM;
+
+ struct sbi_mpxy_channel *channel = mpxy_find_channel(channel_id);
+ if (!channel)
+ return SBI_ERR_NOT_SUPPORTED;
+
+ /* base attribute id is not a defined std attribute or reserved */
+ if (base_attr_id >= SBI_MPXY_ATTR_STD_ATTR_MAX_IDX &&
+ base_attr_id < SBI_MPXY_ATTR_MSGPROTO_ATTR_START)
+ return SBI_ERR_INVALID_PARAM;
+
+ /* Sanity check for base_attr_id and attr_count */
+ if (!attr_count || (attr_count > (rs->shmem.shmem_size / ATTR_SIZE)))
+ return SBI_ERR_INVALID_PARAM;
+
+ shmem_base = hart_shmem_base(rs);
+ end_id = base_attr_id + attr_count - 1;
+
+ sbi_hart_map_saddr((unsigned long)shmem_base, rs->shmem.shmem_size);
+
+ mem_ptr = (u32 *)shmem_base;
+
+ if (mpxy_is_std_attr(base_attr_id)) {
+ if (end_id >= SBI_MPXY_ATTR_STD_ATTR_MAX_IDX) {
+ ret = SBI_ERR_BAD_RANGE;
+ goto out;
+ }
+
+ /** Verify the attribute ids range and values */
+ mem_idx = 0;
+ for (attr_id = base_attr_id; attr_id <= end_id; attr_id++) {
+ attr_val = le32_to_cpu(mem_ptr[mem_idx++]);
+ ret = mpxy_check_write_std_attr(channel,
+ attr_id, attr_val);
+ if (ret)
+ goto out;
+ }
+
+ /* Write the attribute ids values */
+ mem_idx = 0;
+ for (attr_id = base_attr_id; attr_id <= end_id; attr_id++) {
+ attr_val = le32_to_cpu(mem_ptr[mem_idx++]);
+ mpxy_write_std_attr(channel, attr_id, attr_val);
+ }
+ } else {/**
+ * Message protocol specific attributes:
+ * If attributes belong to message protocol, they
+ * are simply passed to the message protocol driver
+ * callback after checking the valid range.
+ * Attributes contiguous range & permission & other checks
+ * are done by the mpxy and message protocol glue layer.
+ */
+ /**
+ * Even if the message protocol driver does not provide
+ * write attribute callback, return bad range error instead
+ * of not supported to let client distinguish it from channel
+ * id not supported.
+ */
+ if (!channel->write_attributes ||
+ end_id > SBI_MPXY_ATTR_MSGPROTO_ATTR_END) {
+ ret = SBI_ERR_BAD_RANGE;
+ goto out;
+ }
+
+ /**
+ * Function expected to return the SBI supported errors
+ * At this point both base attribute id and only the mpxy
+ * supported range been verified. Platform callback must
+ * check if the range requested is supported by message
+ * protocol driver */
+ ret = channel->write_attributes(channel,
+ (u32 *)shmem_base,
+ base_attr_id, attr_count);
+ }
+out:
+ sbi_hart_unmap_saddr();
+ return ret;
+}
+
+int sbi_mpxy_send_message(u32 channel_id, u8 msg_id, unsigned long msg_data_len,
+ unsigned long *resp_data_len)
+{
+ int ret;
+ void *msgbuf, *shmem_base;
+
+ struct mpxy_state *rs =
+ sbi_scratch_thishart_offset_ptr(mpxy_state_offset);
+
+ if (!mpxy_shmem_enabled(rs))
+ return SBI_ERR_NO_SHMEM;
+
+ struct sbi_mpxy_channel *channel = mpxy_find_channel(channel_id);
+ if (!channel)
+ return SBI_ERR_NOT_SUPPORTED;
+
+ if (!channel->send_message)
+ return SBI_ERR_NOT_IMPLEMENTED;
+
+ if (msg_data_len > rs->shmem.shmem_size ||
+ msg_data_len > channel->attrs.msg_data_maxlen)
+ return SBI_ERR_INVALID_PARAM;
+
+ shmem_base = hart_shmem_base(rs);
+ sbi_hart_map_saddr((unsigned long)shmem_base, rs->shmem.shmem_size);
+ msgbuf = shmem_base;
+
+ ret = channel->send_message(channel, msg_id, msgbuf, msg_data_len,
+ resp_data_len ? shmem_base : NULL,
+ resp_data_len ? rs->shmem.shmem_size : 0,
+ resp_data_len);
+ sbi_hart_unmap_saddr();
+ if (ret)
+ return ret;
+
+ if (resp_data_len &&
+ (*resp_data_len > rs->shmem.shmem_size ||
+ *resp_data_len > channel->attrs.msg_data_maxlen))
+ return SBI_ERR_FAILED;
+
+ return SBI_SUCCESS;
+}
+
+int sbi_mpxy_get_notification_events(u32 channel_id, unsigned long *events_len)
+{
+ int ret;
+ void *eventsbuf, *shmem_base;
+
+ struct mpxy_state *rs =
+ sbi_scratch_thishart_offset_ptr(mpxy_state_offset);
+
+ if (!mpxy_shmem_enabled(rs))
+ return SBI_ERR_NO_SHMEM;
+
+ struct sbi_mpxy_channel *channel = mpxy_find_channel(channel_id);
+ if (!channel)
+ return SBI_ERR_NOT_SUPPORTED;
+
+ if (!channel->get_notification_events)
+ return SBI_ERR_NOT_IMPLEMENTED;
+
+ shmem_base = hart_shmem_base(rs);
+ sbi_hart_map_saddr((unsigned long)shmem_base, rs->shmem.shmem_size);
+ eventsbuf = shmem_base;
+ ret = channel->get_notification_events(channel, eventsbuf,
+ rs->shmem.shmem_size,
+ events_len);
+ sbi_hart_unmap_saddr();
+
+ if (ret)
+ return ret;
+
+ if (*events_len > rs->shmem.shmem_size)
+ return SBI_ERR_FAILED;
+
+ return SBI_SUCCESS;
+}