[PATCH 3/5] lib: utils/mpxy: Add queue of forwarded RPMI messages
marouene.boubakri at oss.nxp.com
marouene.boubakri at oss.nxp.com
Mon Sep 7 04:59:11 PDT 2026
From: Marouene Boubakri <marouene.boubakri at nxp.com>
The RPMI REQUEST_FORWARD service group lets a software agent pull RPMI
messages out of the SBI implementation and answer them, which makes the
SBI implementation a forwarder between an RPMI client and that agent.
Add the queue sitting between the two. A producer appends a complete
RPMI message to a queue and waits for the agent to complete it, while
the agent retrieves the oldest message of the queue, possibly in several
chunks, and completes it with the response data. Nothing in here knows
which RPMI service group a forwarded message belongs to.
The producer and the agent run on different HARTs, so a message and its
response buffer must be reachable from M-mode on any HART. This is why
the queue holds references to firmware memory rather than to the memory
of the RPMI client.
Signed-off-by: Marouene Boubakri <marouene.boubakri at nxp.com>
---
include/sbi_utils/mpxy/reqfwd_queue.h | 157 ++++++++++++++++++++
lib/utils/mpxy/Kconfig | 4 +
lib/utils/mpxy/objects.mk | 2 +
lib/utils/mpxy/reqfwd_queue.c | 198 ++++++++++++++++++++++++++
4 files changed, 361 insertions(+)
create mode 100644 include/sbi_utils/mpxy/reqfwd_queue.h
create mode 100644 lib/utils/mpxy/reqfwd_queue.c
diff --git a/include/sbi_utils/mpxy/reqfwd_queue.h b/include/sbi_utils/mpxy/reqfwd_queue.h
new file mode 100644
index 0000000..cb703c4
--- /dev/null
+++ b/include/sbi_utils/mpxy/reqfwd_queue.h
@@ -0,0 +1,157 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright 2026 NXP
+ *
+ * Authors:
+ * Marouene Boubakri <marouene.boubakri at nxp.com>
+ */
+
+#ifndef __REQFWD_QUEUE_H__
+#define __REQFWD_QUEUE_H__
+
+#include <sbi/riscv_locks.h>
+#include <sbi/sbi_list.h>
+#include <sbi/sbi_types.h>
+
+/**
+ * Queue of RPMI messages forwarded to a software agent which serves the
+ * RPMI REQUEST_FORWARD service group.
+ *
+ * A producer forwards a message to a queue and waits for the agent to
+ * complete it. The oldest message of a queue is its current message and
+ * it is the only one which can be retrieved and completed.
+ *
+ * The producer and the agent run on different HARTs, so the message and
+ * response buffers must be reachable from M-mode on any HART of the
+ * system. In other words they must be in firmware memory.
+ */
+
+/** State of a forwarded message */
+enum reqfwd_state {
+ /** Queued and not retrieved yet */
+ REQFWD_STATE_QUEUED = 0,
+ /** At least partly retrieved by the agent */
+ REQFWD_STATE_RETRIEVED,
+ /** Completed by the agent, the response is available */
+ REQFWD_STATE_COMPLETED,
+};
+
+/** A forwarded message */
+struct reqfwd_message {
+ /** Node in the message list of a queue */
+ struct sbi_dlist node;
+ /** State of this message */
+ enum reqfwd_state state;
+ /** Message bytes */
+ const void *msg;
+ /** Number of message bytes */
+ unsigned long msg_len;
+ /** Buffer receiving the response */
+ void *rsp;
+ /** Size of the response buffer */
+ unsigned long rsp_max_len;
+ /** Number of response bytes written by the agent */
+ unsigned long rsp_len;
+};
+
+/** Queue of messages forwarded to one agent */
+struct reqfwd_queue {
+ /** Node in the list of registered queues */
+ struct sbi_dlist node;
+ /** Identifier of this queue, unique across registered queues */
+ u32 id;
+ /** Lock protecting the message list */
+ spinlock_t lock;
+ /** Forwarded messages, oldest first */
+ struct sbi_dlist messages;
+ /** Number of messages in the list */
+ unsigned long count;
+};
+
+/**
+ * Register a forwarded message queue
+ *
+ * @param queue message queue to register
+ * @param id identifier producers use to find the queue, in practice the
+ * MPXY channel ID of the REQUEST_FORWARD channel serving it
+ *
+ * @return 0 on success and negative error code on failure
+ */
+int reqfwd_queue_register(struct reqfwd_queue *queue, u32 id);
+
+/**
+ * Unregister a forwarded message queue
+ *
+ * The queue must be empty.
+ *
+ * @param queue message queue to unregister
+ */
+void reqfwd_queue_unregister(struct reqfwd_queue *queue);
+
+/**
+ * Find a registered forwarded message queue
+ *
+ * @param id identifier the queue was registered with
+ *
+ * @return pointer to the queue or NULL if there is no such queue
+ */
+struct reqfwd_queue *reqfwd_queue_find(u32 id);
+
+/**
+ * Forward a message and wait for the agent to complete it
+ *
+ * @param queue message queue to forward to
+ * @param fmsg caller provided state tracking the forwarded message
+ * @param msg message bytes, must stay valid until this function returns
+ * @param msg_len number of message bytes
+ * @param rsp buffer receiving the response, may be NULL
+ * @param rsp_max_len size of the response buffer
+ * @param rsp_len place to store the number of response bytes, may be NULL
+ * @param timeout_us how long to wait for completion in microseconds
+ *
+ * @return 0 on success, SBI_ETIMEDOUT if the agent did not complete the
+ * message in time, and a negative error code on failure
+ */
+int reqfwd_queue_send(struct reqfwd_queue *queue,
+ struct reqfwd_message *fmsg,
+ const void *msg, unsigned long msg_len,
+ void *rsp, unsigned long rsp_max_len,
+ unsigned long *rsp_len, unsigned long timeout_us);
+
+/**
+ * Copy a part of the current forwarded message of a queue
+ *
+ * The current message is marked as retrieved even if only a part of it
+ * was copied, because the agent may not need the whole message.
+ *
+ * @param queue message queue to retrieve from
+ * @param offset index of the first byte to copy
+ * @param buf buffer receiving the copied bytes
+ * @param buf_len size of the buffer
+ * @param returned place to store the number of bytes copied
+ * @param remaining place to store the number of bytes left after this call
+ *
+ * @return 0 on success, SBI_ENOENT if the queue has no current message,
+ * and a negative error code on failure
+ */
+int reqfwd_queue_retrieve(struct reqfwd_queue *queue, unsigned long offset,
+ void *buf, unsigned long buf_len,
+ unsigned long *returned, unsigned long *remaining);
+
+/**
+ * Complete the current forwarded message of a queue
+ *
+ * @param queue message queue to complete in
+ * @param rsp response bytes
+ * @param rsp_len number of response bytes
+ * @param count place to store the number of messages left behind
+ *
+ * @return 0 on success, SBI_ENOENT if the queue has no current message or
+ * the current message was not retrieved, and a negative error code on
+ * failure
+ */
+int reqfwd_queue_complete(struct reqfwd_queue *queue, const void *rsp,
+ unsigned long rsp_len, unsigned long *count);
+
+#endif /* __REQFWD_QUEUE_H__ */
diff --git a/lib/utils/mpxy/Kconfig b/lib/utils/mpxy/Kconfig
index a3ed1c2..d309735 100644
--- a/lib/utils/mpxy/Kconfig
+++ b/lib/utils/mpxy/Kconfig
@@ -44,4 +44,8 @@ config FDT_MPXY_RPMI_MM
endif
+config MPXY_REQFWD_QUEUE
+ bool "Queue of RPMI messages forwarded to a software agent"
+ default n
+
endmenu
diff --git a/lib/utils/mpxy/objects.mk b/lib/utils/mpxy/objects.mk
index c406c12..6cdfb6b 100644
--- a/lib/utils/mpxy/objects.mk
+++ b/lib/utils/mpxy/objects.mk
@@ -13,6 +13,8 @@ libsbiutils-objs-$(CONFIG_FDT_MPXY) += mpxy/fdt_mpxy_drivers.carray.o
libsbiutils-objs-$(CONFIG_FDT_MPXY_RPMI_MBOX) += mpxy/fdt_mpxy_rpmi_mbox.o
+libsbiutils-objs-$(CONFIG_MPXY_REQFWD_QUEUE) += mpxy/reqfwd_queue.o
+
carray-fdt_mpxy_drivers-$(CONFIG_FDT_MPXY_RPMI_CLOCK) += fdt_mpxy_rpmi_clock
libsbiutils-objs-$(CONFIG_FDT_MPXY_RPMI_CLOCK) += mpxy/fdt_mpxy_rpmi_clock.o
diff --git a/lib/utils/mpxy/reqfwd_queue.c b/lib/utils/mpxy/reqfwd_queue.c
new file mode 100644
index 0000000..b49c004
--- /dev/null
+++ b/lib/utils/mpxy/reqfwd_queue.c
@@ -0,0 +1,198 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright 2026 NXP
+ *
+ * Authors:
+ * Marouene Boubakri <marouene.boubakri at nxp.com>
+ */
+
+#include <sbi/riscv_barrier.h>
+#include <sbi/sbi_error.h>
+#include <sbi/sbi_string.h>
+#include <sbi/sbi_timer.h>
+#include <sbi_utils/mpxy/reqfwd_queue.h>
+
+/** List of registered forwarded message queues */
+static SBI_LIST_HEAD(reqfwd_queue_list);
+
+/** Current message of a queue, called with the queue lock held */
+static struct reqfwd_message *reqfwd_current(struct reqfwd_queue *queue)
+{
+ if (sbi_list_empty(&queue->messages))
+ return NULL;
+
+ return sbi_list_first_entry(&queue->messages,
+ struct reqfwd_message, node);
+}
+
+int reqfwd_queue_register(struct reqfwd_queue *queue, u32 id)
+{
+ if (!queue)
+ return SBI_EINVAL;
+
+ if (reqfwd_queue_find(id))
+ return SBI_EALREADY;
+
+ queue->id = id;
+ SPIN_LOCK_INIT(queue->lock);
+ SBI_INIT_LIST_HEAD(&queue->messages);
+ queue->count = 0;
+
+ sbi_list_add_tail(&queue->node, &reqfwd_queue_list);
+
+ return 0;
+}
+
+void reqfwd_queue_unregister(struct reqfwd_queue *queue)
+{
+ if (!queue)
+ return;
+
+ sbi_list_del(&queue->node);
+}
+
+struct reqfwd_queue *reqfwd_queue_find(u32 id)
+{
+ struct reqfwd_queue *queue;
+
+ sbi_list_for_each_entry(queue, &reqfwd_queue_list, node) {
+ if (queue->id == id)
+ return queue;
+ }
+
+ return NULL;
+}
+
+int reqfwd_queue_send(struct reqfwd_queue *queue,
+ struct reqfwd_message *fmsg,
+ const void *msg, unsigned long msg_len,
+ void *rsp, unsigned long rsp_max_len,
+ unsigned long *rsp_len, unsigned long timeout_us)
+{
+ u64 start, ticks;
+
+ if (!queue || !fmsg || !msg || !msg_len || !timeout_us)
+ return SBI_EINVAL;
+
+ fmsg->state = REQFWD_STATE_QUEUED;
+ fmsg->msg = msg;
+ fmsg->msg_len = msg_len;
+ fmsg->rsp = rsp;
+ fmsg->rsp_max_len = rsp ? rsp_max_len : 0;
+ fmsg->rsp_len = 0;
+
+ spin_lock(&queue->lock);
+ sbi_list_add_tail(&fmsg->node, &queue->messages);
+ queue->count++;
+ spin_unlock(&queue->lock);
+
+ start = sbi_timer_value();
+ ticks = sbi_timer_compute_udelta(timeout_us);
+
+ while (1) {
+ spin_lock(&queue->lock);
+
+ /*
+ * reqfwd_queue_complete() removes the message from the list
+ * before releasing the lock, so once it is completed the
+ * agent no longer refers to it.
+ */
+ if (fmsg->state == REQFWD_STATE_COMPLETED) {
+ spin_unlock(&queue->lock);
+ if (rsp_len)
+ *rsp_len = fmsg->rsp_len;
+ return 0;
+ }
+
+ if ((sbi_timer_value() - start) >= ticks) {
+ sbi_list_del(&fmsg->node);
+ queue->count--;
+ spin_unlock(&queue->lock);
+ return SBI_ETIMEDOUT;
+ }
+
+ spin_unlock(&queue->lock);
+ cpu_relax();
+ }
+}
+
+int reqfwd_queue_retrieve(struct reqfwd_queue *queue, unsigned long offset,
+ void *buf, unsigned long buf_len,
+ unsigned long *returned, unsigned long *remaining)
+{
+ struct reqfwd_message *fmsg;
+ unsigned long len;
+ int ret = 0;
+
+ if (!queue || (buf_len && !buf))
+ return SBI_EINVAL;
+
+ spin_lock(&queue->lock);
+
+ fmsg = reqfwd_current(queue);
+ if (!fmsg) {
+ ret = SBI_ENOENT;
+ goto out;
+ }
+
+ if (offset > fmsg->msg_len) {
+ ret = SBI_EINVAL;
+ goto out;
+ }
+
+ len = fmsg->msg_len - offset;
+ if (len > buf_len)
+ len = buf_len;
+
+ if (len)
+ sbi_memcpy(buf, (const char *)fmsg->msg + offset, len);
+ fmsg->state = REQFWD_STATE_RETRIEVED;
+
+ if (returned)
+ *returned = len;
+ if (remaining)
+ *remaining = fmsg->msg_len - offset - len;
+
+out:
+ spin_unlock(&queue->lock);
+ return ret;
+}
+
+int reqfwd_queue_complete(struct reqfwd_queue *queue, const void *rsp,
+ unsigned long rsp_len, unsigned long *count)
+{
+ struct reqfwd_message *fmsg;
+ int ret = 0;
+
+ if (!queue || (rsp_len && !rsp))
+ return SBI_EINVAL;
+
+ spin_lock(&queue->lock);
+
+ fmsg = reqfwd_current(queue);
+ if (!fmsg || fmsg->state != REQFWD_STATE_RETRIEVED) {
+ ret = SBI_ENOENT;
+ goto out;
+ }
+
+ if (rsp_len > fmsg->rsp_max_len) {
+ ret = SBI_EBAD_RANGE;
+ goto out;
+ }
+
+ if (rsp_len)
+ sbi_memcpy(fmsg->rsp, rsp, rsp_len);
+ fmsg->rsp_len = rsp_len;
+ fmsg->state = REQFWD_STATE_COMPLETED;
+
+ sbi_list_del(&fmsg->node);
+ queue->count--;
+
+out:
+ if (count)
+ *count = queue->count;
+ spin_unlock(&queue->lock);
+
+ return ret;
+}
--
2.43.0
More information about the opensbi
mailing list