@@ -7,6 +7,10 @@ config FDT_MPXY
depends on FDT
default n
+config FDT_MPXY_BRIDGE
+ bool "MPXY bridge driver for interdomina communication"
+ default n
+
config FDT_MPXY_RPMI_MBOX
bool "MPXY drivers as RPMI mailbox client"
depends on FDT_MAILBOX && FDT_MPXY
new file mode 100644
@@ -0,0 +1,676 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2026 Qualcomm, Inc
+ *
+ * Authors:
+ * Pawandeep Oza <pawandeep.oza@oss.qualcomm.com>
+ */
+
+#include <libfdt.h>
+#include <stdbool.h>
+#include <stddef.h>
+
+#include <sbi/riscv_asm.h>
+#include <sbi/sbi_byteorder.h>
+#include <sbi/sbi_console.h>
+#include <sbi/sbi_domain.h>
+#include <sbi/sbi_domain_context.h>
+#include <sbi/sbi_error.h>
+#include <sbi/sbi_heap.h>
+#include <sbi/sbi_list.h>
+#include <sbi/sbi_mpxy.h>
+#include <sbi/sbi_platform.h>
+#include <sbi/sbi_scratch.h>
+#include <sbi/sbi_string.h>
+#include <sbi/sbi_types.h>
+#include <sbi_utils/fdt/fdt_helper.h>
+#include <sbi_utils/fdt/fdt_driver.h>
+#include <sbi_utils/mailbox/rpmi_msgprot.h>
+
+#define SBI_MPXY_BRIDGE_COMPATIBLE "opensbi,mpxy-bridge"
+#define SBI_MPXY_BRIDGE_PROP_SRC_DOMAIN_NAME "opensbi,source-domain-name"
+#define SBI_MPXY_BRIDGE_PROP_DST_DOMAIN_NAME "opensbi,destination-domain-name"
+#define SBI_MPXY_BRIDGE_PROP_SRC_CHAN "opensbi,source-channel-id"
+#define SBI_MPXY_BRIDGE_PROP_DST_CHAN "opensbi,destination-channel-id"
+
+#define SBI_MPXY_BRIDGE_DEFAULT_MSG_DATA_MAXLEN 256U
+#define SBI_MPXY_BRIDGE_DOMAIN_NAME_MAXLEN 64
+
+struct mpxy_bridge_dst_ctx {
+ void *respbuf;
+ u32 resp_bufsize;
+ unsigned long *resp_len_ptr;
+ u32 start_index;
+};
+
+struct mpxy_bridge_src_ctx {
+ void *msgbuf;
+ void *respbuf;
+ u32 msg_len;
+ u32 resp_bufsize;
+ unsigned long *resp_len_ptr;
+};
+
+struct mpxy_bridge_hart_slot {
+ bool occupied;
+ bool retrieved;
+ u32 msg_id;
+ struct mpxy_bridge_src_ctx src_resp;
+ struct mpxy_bridge_dst_ctx dst_resp;
+};
+
+struct mpxy_bridge_slot_store {
+ struct mpxy_bridge_hart_slot *slots;
+ u32 hart_count;
+ u32 lost;
+};
+
+struct mpxy_bridge {
+ struct sbi_dlist node;
+
+ char src_domain_name[SBI_MPXY_BRIDGE_DOMAIN_NAME_MAXLEN];
+ char dst_domain_name[SBI_MPXY_BRIDGE_DOMAIN_NAME_MAXLEN];
+
+ u32 src_channel_id;
+ u32 dst_channel_id;
+
+ struct sbi_domain *src_dom;
+ struct sbi_domain *dst_dom;
+
+ struct sbi_mpxy_channel src_ch;
+ struct sbi_mpxy_channel dst_ch;
+
+ struct mpxy_bridge_slot_store store;
+};
+
+static SBI_LIST_HEAD(mpxy_bridge_list);
+static bool mpxy_bridge_notifier_registered;
+
+static inline struct mpxy_bridge *bridge_from_channel(struct sbi_mpxy_channel *ch)
+{
+ struct mpxy_bridge *br;
+
+ sbi_list_for_each_entry(br, &mpxy_bridge_list, node) {
+ if (&br->src_ch == ch || &br->dst_ch == ch)
+ return br;
+ }
+ return NULL;
+}
+
+static bool bridge_name_match(const char *a, const char *b)
+{
+ if (!a || !b)
+ return false;
+
+ return !sbi_strcmp(a, b);
+}
+
+static int bridge_get_string_prop(const void *fdt, int nodeoff,
+ const char *propname,
+ char *out, size_t outsz)
+{
+ const char *val;
+ int len;
+
+ val = fdt_getprop(fdt, nodeoff, propname, &len);
+ if (!val || len <= 0)
+ return SBI_ENODEV;
+
+ if ((size_t)len >= outsz)
+ len = (int)outsz - 1;
+
+ sbi_memcpy(out, val, len);
+ out[len] = '\0';
+ return SBI_OK;
+}
+
+static int bridge_get_u32_prop(const void *fdt, int nodeoff,
+ const char *propname, u32 *out)
+{
+ const fdt32_t *val;
+ int len;
+
+ val = fdt_getprop(fdt, nodeoff, propname, &len);
+ if (!val || len < (int)sizeof(*val))
+ return SBI_ENODEV;
+
+ *out = fdt32_to_cpu(*val);
+ return SBI_OK;
+}
+
+static void bridge_switch_domain(struct mpxy_bridge *br,
+ struct sbi_mpxy_channel *channel)
+{
+ bool is_src = (channel == &br->src_ch);
+ struct sbi_domain *peer = br->dst_ch.owner_domain;
+
+ if (is_src)
+ sbi_domain_context_enter(peer);
+ else
+ sbi_domain_context_exit();
+}
+
+static int bridge_slot_occupy(struct mpxy_bridge *br,
+ struct mpxy_bridge_hart_slot *slot)
+{
+ if (slot->occupied) {
+ br->store.lost++;
+ return SBI_ERR_FAILED;
+ }
+ slot->occupied = true;
+ slot->retrieved = false;
+ return SBI_OK;
+}
+
+static void bridge_fill_forward_resp(struct mpxy_bridge_hart_slot *slot,
+ void *msgbuf, u32 msg_len)
+{
+ struct rpmi_request_forward_resp *resp;
+ u32 offset, remaining;
+
+ if (slot->dst_resp.respbuf) {
+ resp = (struct rpmi_request_forward_resp *)slot->dst_resp.respbuf;
+ if (slot->dst_resp.resp_bufsize >= msg_len) {
+ offset = slot->dst_resp.start_index;
+ remaining = slot->dst_resp.resp_bufsize >= msg_len ?
+ 0 : msg_len - slot->dst_resp.resp_bufsize;
+ resp->status = RPMI_SUCCESS;
+ resp->remaining = remaining;
+ resp->returned = msg_len;
+ sbi_memcpy(resp->data, msgbuf + offset, msg_len);
+ *slot->dst_resp.resp_len_ptr = msg_len;
+ } else {
+ resp->status = RPMI_ERR_INVALID_PARAM;
+ resp->remaining = 0;
+ resp->returned = 0;
+ *slot->dst_resp.resp_len_ptr = 0;
+ }
+ } else {
+ slot->src_resp.msgbuf = msgbuf;
+ slot->src_resp.msg_len = msg_len;
+ }
+}
+
+static int bridge_slot_put(struct mpxy_bridge *br,
+ void *msgbuf,
+ u32 msg_len)
+{
+ struct mpxy_bridge_hart_slot *slot;
+ u32 hartindex = current_hartindex();
+ int rc;
+
+ if (hartindex >= br->store.hart_count)
+ return SBI_ERR_INVALID_PARAM;
+
+ slot = &br->store.slots[hartindex];
+
+ rc = bridge_slot_occupy(br, slot);
+ if (rc)
+ return rc;
+
+ bridge_fill_forward_resp(slot, msgbuf, msg_len);
+ bridge_switch_domain(br, &br->src_ch);
+
+ return SBI_OK;
+}
+
+static int bridge_src_send(struct mpxy_bridge *br,
+ struct mpxy_bridge_hart_slot *slot,
+ void *msgbuf,
+ u32 msg_len,
+ void *respbuf,
+ u32 resp_bufsize,
+ unsigned long *resp_len)
+{
+ int rc;
+
+ rc = bridge_slot_occupy(br, slot);
+ if (rc)
+ return rc;
+
+ slot->src_resp.respbuf = respbuf;
+ slot->src_resp.resp_bufsize = resp_bufsize;
+ slot->src_resp.resp_len_ptr = resp_len;
+
+ bridge_fill_forward_resp(slot, msgbuf, msg_len);
+ bridge_switch_domain(br, &br->src_ch);
+
+ return SBI_OK;
+}
+
+static void bridge_save_dst_ctx(struct mpxy_bridge_hart_slot *slot,
+ void *respbuf, u32 resp_bufsize,
+ unsigned long *resp_len, void *msgbuf)
+{
+ slot->dst_resp.respbuf = respbuf;
+ slot->dst_resp.resp_bufsize = resp_bufsize;
+ slot->dst_resp.resp_len_ptr = resp_len;
+ slot->dst_resp.start_index = ((u32 *)msgbuf)[0];
+}
+
+static void bridge_slot_release(struct mpxy_bridge_hart_slot *slot)
+{
+ slot->occupied = false;
+ slot->retrieved = false;
+ slot->src_resp = (struct mpxy_bridge_src_ctx){ 0 };
+ slot->dst_resp = (struct mpxy_bridge_dst_ctx){ 0 };
+}
+
+static void bridge_build_retrieve_resp(struct mpxy_bridge_hart_slot *slot,
+ void *respbuf, u32 resp_bufsize,
+ unsigned long *resp_len)
+{
+ struct rpmi_request_forward_resp *resp;
+ u32 src_msg_len = slot->src_resp.msg_len;
+ u32 remaining;
+
+ resp = (struct rpmi_request_forward_resp *)respbuf;
+
+ if (resp_bufsize >= sizeof(*resp) + src_msg_len) {
+ remaining = 0;
+ resp->status = RPMI_SUCCESS;
+ resp->remaining = remaining;
+ resp->returned = src_msg_len;
+ sbi_memcpy(resp->data, slot->src_resp.msgbuf, src_msg_len);
+ *resp_len = sizeof(*resp) + src_msg_len;
+ } else {
+ resp->status = RPMI_ERR_INVALID_PARAM;
+ resp->remaining = 0;
+ resp->returned = 0;
+ *resp_len = sizeof(*resp);
+ }
+
+ slot->retrieved = true;
+}
+
+static int bridge_dst_retrieve(struct mpxy_bridge *br,
+ struct mpxy_bridge_hart_slot *slot,
+ void *msgbuf, u32 msg_len,
+ void *respbuf, u32 resp_bufsize,
+ unsigned long *resp_len)
+{
+ struct rpmi_request_forward_resp *resp = (struct rpmi_request_forward_resp *)respbuf;
+
+ if (!slot->occupied) {
+ bridge_save_dst_ctx(slot, respbuf, resp_bufsize, resp_len, msgbuf);
+ bridge_switch_domain(br, &br->dst_ch);
+ } else if (slot->src_resp.respbuf) {
+ if (!respbuf) {
+ if (resp_len)
+ *resp_len = 0;
+ return SBI_OK;
+ }
+ bridge_build_retrieve_resp(slot, respbuf, resp_bufsize, resp_len);
+ } else{
+ resp->status = RPMI_ERR_NO_DATA;
+ resp->remaining = 0;
+ resp->returned = 0;
+ *resp_len = sizeof(*resp);
+ }
+
+ return SBI_OK;
+}
+
+static int bridge_dst_complete(struct mpxy_bridge *br,
+ struct mpxy_bridge_hart_slot *slot,
+ void *msgbuf, u32 msg_len,
+ void *respbuf, u32 resp_max_len,
+ unsigned long *resp_len)
+{
+ struct rpmi_request_complete_resp *resp = (struct rpmi_request_complete_resp *) respbuf;
+ u32 num_messages = 0;
+
+ if (!slot->retrieved || !slot->src_resp.respbuf) {
+ resp->status = RPMI_ERR_NO_DATA;
+ resp->num_of_messages = 0;
+ *resp_len = 8;
+ } else {
+ sbi_memcpy(slot->src_resp.respbuf, msgbuf, msg_len);
+ }
+
+ resp->status = RPMI_SUCCESS;
+ resp->num_of_messages = num_messages;
+ *resp_len = sizeof(struct rpmi_request_complete_resp);
+
+ bridge_slot_release(slot);
+ bridge_switch_domain(br, &br->dst_ch);
+
+ return SBI_OK;
+}
+
+static int bridge_send_message_with_response(struct sbi_mpxy_channel *channel,
+ u32 msg_id,
+ void *msgbuf,
+ u32 msg_len,
+ void *respbuf,
+ u32 resp_bufsize,
+ unsigned long *resp_len)
+{
+ struct mpxy_bridge *br = bridge_from_channel(channel);
+ u32 hartindex;
+ struct mpxy_bridge_hart_slot *slot;
+ bool is_src;
+
+ if (!br)
+ return SBI_ERR_NOT_SUPPORTED;
+
+ hartindex = current_hartindex();
+ slot = &br->store.slots[hartindex];
+ is_src = (channel == &br->src_ch);
+
+ if (is_src)
+ return bridge_src_send(br, slot,
+ msgbuf, msg_len,
+ respbuf, resp_bufsize, resp_len);
+
+ if (msg_id == REQFWD_RETRIEVE_CURRENT_MESSAGE)
+ return bridge_dst_retrieve(br, slot,
+ msgbuf, msg_len,
+ respbuf, resp_bufsize, resp_len);
+
+ if (msg_id == REQFWD_COMPLETE_CURRENT_MESSAGE)
+ return bridge_dst_complete(br, slot,
+ msgbuf, msg_len,
+ respbuf, resp_bufsize, resp_len);
+
+ return SBI_ERR_NOT_SUPPORTED;
+}
+
+static int bridge_post_message(struct sbi_mpxy_channel *channel,
+ u32 msg_id,
+ void *msgbuf,
+ u32 msg_len)
+{
+ struct mpxy_bridge *br = bridge_from_channel(channel);
+
+ if (!br->dst_dom)
+ return SBI_ERR_NOT_SUPPORTED;
+
+ return bridge_slot_put(br, msgbuf, msg_len);
+}
+
+static int bridge_send_message_without_response(struct sbi_mpxy_channel *channel,
+ u32 msg_id,
+ void *msgbuf,
+ u32 msg_len)
+{
+ return bridge_post_message(channel, msg_id, msgbuf, msg_len);
+}
+
+static void bridge_init_channel(struct sbi_mpxy_channel *ch,
+ struct sbi_domain *dom,
+ u32 channel_id)
+{
+ sbi_memset(ch, 0, sizeof(*ch));
+ ch->owner_domain = dom;
+ ch->channel_id = channel_id;
+ ch->attrs.msg_data_maxlen = SBI_MPXY_BRIDGE_DEFAULT_MSG_DATA_MAXLEN;
+ ch->attrs.msi_control = 0;
+ ch->attrs.eventsstate_ctrl = 0;
+ ch->send_message_with_response = bridge_send_message_with_response;
+ ch->send_message_without_response = bridge_send_message_without_response;
+ ch->get_notification_events = NULL;
+}
+
+static int bridge_register_source(struct mpxy_bridge *br,
+ struct sbi_domain *dom)
+{
+ int ret;
+
+ if (br->src_dom)
+ return SBI_OK;
+
+ bridge_init_channel(&br->src_ch, dom, br->src_channel_id);
+
+ ret = sbi_mpxy_register_channel(&br->src_ch);
+ if (ret) {
+ sbi_printf("%s: src channel register failed ret=%d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ br->src_dom = dom;
+ return SBI_OK;
+}
+
+static int bridge_register_destination(struct mpxy_bridge *br,
+ struct sbi_domain *dom)
+{
+ int ret;
+
+ if (br->dst_dom)
+ return SBI_OK;
+
+ bridge_init_channel(&br->dst_ch, dom, br->dst_channel_id);
+
+ ret = sbi_mpxy_register_channel(&br->dst_ch);
+ if (ret) {
+ sbi_printf("%s: dst channel register failed ret=%d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ br->dst_dom = dom;
+ return SBI_OK;
+}
+
+static bool sbi_hartmask_equal(const struct sbi_hartmask *a,
+ const struct sbi_hartmask *b)
+{
+ u32 hartindex;
+
+ sbi_hartmask_for_each_hartindex(hartindex, a)
+ if (!sbi_hartmask_test_hartindex(hartindex, b))
+ return false;
+
+ sbi_hartmask_for_each_hartindex(hartindex, b)
+ if (!sbi_hartmask_test_hartindex(hartindex, a))
+ return false;
+
+ return true;
+}
+
+static void bridge_try_bind_domain(struct mpxy_bridge *br,
+ const struct sbi_domain *dom)
+{
+ int match_src, match_dst;
+
+ if (!br || !dom)
+ return;
+
+ match_src = bridge_name_match(dom->name, br->src_domain_name);
+ match_dst = bridge_name_match(dom->name, br->dst_domain_name);
+
+ if (!br->src_dom && match_src)
+ bridge_register_source(br, (struct sbi_domain *)dom);
+
+ if (!br->dst_dom && match_dst)
+ bridge_register_destination(br, (struct sbi_domain *)dom);
+
+ if (br->src_dom && br->dst_dom) {
+ if (!sbi_hartmask_equal(br->src_dom->possible_harts,
+ br->dst_dom->possible_harts)) {
+ sbi_printf("%s: src='%s' dst='%s'"
+ "Error!: bridge suuports only domains with the same possible harts.\n",
+ __func__,
+ br->src_dom->name,
+ br->dst_dom->name);
+ br->src_dom = NULL;
+ br->dst_dom = NULL;
+ }
+}
+}
+
+static void bridge_domain_notifier(const struct sbi_domain *dom, void *priv)
+{
+ struct mpxy_bridge *br;
+
+ (void)priv;
+
+ sbi_list_for_each_entry(br, &mpxy_bridge_list, node)
+ bridge_try_bind_domain(br, dom);
+}
+
+static struct mpxy_bridge *bridge_alloc(u32 hart_count)
+{
+ struct mpxy_bridge *br;
+
+ br = sbi_zalloc(sizeof(*br));
+ if (!br)
+ return NULL;
+
+ br->store.slots = sbi_zalloc(sizeof(*br->store.slots) * hart_count);
+ if (!br->store.slots) {
+ sbi_printf("%s: failed to alloc %u hart slots\n",
+ __func__, hart_count);
+ sbi_free(br);
+ return NULL;
+ }
+
+ br->store.hart_count = hart_count;
+ br->store.lost = 0;
+ return br;
+}
+
+static int bridge_parse_fdt(const void *fdt, int nodeoff,
+ struct mpxy_bridge *br)
+{
+ int ret;
+
+ ret = bridge_get_string_prop(fdt, nodeoff,
+ SBI_MPXY_BRIDGE_PROP_SRC_DOMAIN_NAME,
+ br->src_domain_name,
+ sizeof(br->src_domain_name));
+ if (ret) {
+ sbi_printf("%s: failed to read src-domain ret=%d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ ret = bridge_get_string_prop(fdt, nodeoff,
+ SBI_MPXY_BRIDGE_PROP_DST_DOMAIN_NAME,
+ br->dst_domain_name,
+ sizeof(br->dst_domain_name));
+ if (ret) {
+ sbi_printf("%s: failed to read dst-domain ret=%d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ ret = bridge_get_u32_prop(fdt, nodeoff,
+ SBI_MPXY_BRIDGE_PROP_SRC_CHAN,
+ &br->src_channel_id);
+ if (ret) {
+ sbi_printf("%s: failed to read src-channel-id ret=%d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ ret = bridge_get_u32_prop(fdt, nodeoff,
+ SBI_MPXY_BRIDGE_PROP_DST_CHAN,
+ &br->dst_channel_id);
+ if (ret) {
+ sbi_printf("%s: failed to read dst-channel-id ret=%d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ return SBI_OK;
+}
+
+static int bridge_validate(struct mpxy_bridge *br)
+{
+ if (!br->src_domain_name[0] || !br->dst_domain_name[0]) {
+ sbi_printf("%s: empty domain name\n", __func__);
+ return SBI_EINVAL;
+ }
+
+ if (!sbi_strcmp(br->src_domain_name, br->dst_domain_name)) {
+ sbi_printf("%s: src and dst domain names identical\n", __func__);
+ return SBI_EINVAL;
+ }
+
+ if (br->src_channel_id == br->dst_channel_id) {
+ sbi_printf("%s: src and dst channel IDs identical\n", __func__);
+ return SBI_EINVAL;
+ }
+
+ return SBI_OK;
+}
+
+static int bridge_register_notifier(struct mpxy_bridge *br)
+{
+ int ret;
+
+ if (mpxy_bridge_notifier_registered)
+ return SBI_OK;
+
+ ret = sbi_domain_register_notifier(bridge_domain_notifier, NULL);
+ if (ret) {
+ sbi_printf("%s: notifier register failed ret=%d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ mpxy_bridge_notifier_registered = true;
+ return SBI_OK;
+}
+
+static int bridge_init(const void *fdt, int nodeoff,
+ const struct fdt_match *match)
+{
+ struct mpxy_bridge *br;
+ struct sbi_scratch *scratch;
+ u32 hart_count;
+ u32 hartindex = current_hartindex();
+ int i, ret;
+
+ (void)match;
+
+ scratch = sbi_hartindex_to_scratch(hartindex);
+ if (!scratch)
+ return SBI_EINVAL;
+
+ hart_count = sbi_platform_hart_count(sbi_platform_ptr(scratch));
+
+ br = bridge_alloc(hart_count);
+ if (!br)
+ return SBI_ENOMEM;
+
+ ret = bridge_parse_fdt(fdt, nodeoff, br);
+ if (ret)
+ goto fail_free;
+
+ ret = bridge_validate(br);
+ if (ret)
+ goto fail_free;
+
+ for (i = 0; i < hart_count; i++)
+ bridge_slot_release(&br->store.slots[i]);
+
+ sbi_list_add_tail(&br->node, &mpxy_bridge_list);
+
+ ret = bridge_register_notifier(br);
+ if (ret) {
+ sbi_list_del(&br->node);
+ goto fail_free;
+ }
+
+ return SBI_OK;
+
+fail_free:
+ sbi_free(br->store.slots);
+ sbi_free(br);
+ return ret;
+}
+
+static const struct fdt_match bridge_match[] = {
+ { .compatible = SBI_MPXY_BRIDGE_COMPATIBLE },
+ { }
+};
+
+const struct fdt_driver fdt_mpxy_bridge = {
+ .match_table = bridge_match,
+ .init = bridge_init,
+};
@@ -32,3 +32,6 @@ libsbiutils-objs-$(CONFIG_FDT_MPXY_RPMI_MM) += mpxy/fdt_mpxy_rpmi_mm.o
carray-fdt_mpxy_drivers-$(CONFIG_FDT_MPXY_RPMI_LOGGING) += fdt_mpxy_rpmi_logging
libsbiutils-objs-$(CONFIG_FDT_MPXY_RPMI_LOGGING) += mpxy/fdt_mpxy_rpmi_logging.o
+
+carray-fdt_mpxy_drivers-$(CONFIG_FDT_MPXY_BRIDGE) += fdt_mpxy_bridge
+libsbiutils-objs-$(CONFIG_FDT_MPXY_BRIDGE) += mpxy/fdt_mpxy_bridge.o
@@ -84,3 +84,5 @@ CONFIG_FDT_MPXY_RPMI_PERFORMANCE=y
CONFIG_FDT_MPXY_RPMI_SYSMSI=y
CONFIG_FDT_MPXY_RPMI_MM=y
CONFIG_FDT_MPXY_RPMI_LOGGING=y
+CONFIG_FDT_MPXY_BRIDGE=y
+