[PATCH v6 2/3] sbi_utils: mpxy: add FDT-based MPXY bridge driver
Anup Patel
anup at brainfault.org
Sun Nov 1 04:32:31 PST 2026
On Mon, Oct 26, 2026 at 9:44 PM Pawandeep Oza
<pawandeep.oza at oss.qualcomm.com> wrote:
>
> From: Oza Pawandeep <pawandeep.oza at oss.qualcomm.com>
Patch subject should be: "sbi_utils: mpxy: Add FDT-based MPXY RPMI
bridge driver"
The word RPMI is very important because the bridge assumes RPMI protocol
and request forward service group is defined in RPMI spec.
>
> Introduce an FDT-configured MPXY bridge driver that enables
same comment as above.
> cooperative message passing between two SBI domains sharing the
> same physical hart via domain context switching.
>
> MPXY channels are registered at runtime when the named domains
> appear via the domain registration notifier:
>
> - src_ch: used by the source domain to send messages
> - dst_ch: used by the destination domain to retrieve and
> complete forwarded messages via RPMI REQFWD service group
>
> Signed-off-by: Oza Pawandeep <pawandeep.oza at oss.qualcomm.com>
> ---
> include/sbi_utils/mailbox/rpmi_msgprot.h | 1 +
> include/sbi_utils/mpxy/fdt_mpxy_bridge.h | 61 ++
> lib/utils/mpxy/Kconfig | 4 +
> lib/utils/mpxy/fdt_mpxy_bridge.c | 1000 ++++++++++++++++++++++
> lib/utils/mpxy/objects.mk | 3 +
> platform/generic/configs/defconfig | 2 +
> 6 files changed, 1071 insertions(+)
> create mode 100644 include/sbi_utils/mpxy/fdt_mpxy_bridge.h
> create mode 100644 lib/utils/mpxy/fdt_mpxy_bridge.c
>
> diff --git a/include/sbi_utils/mailbox/rpmi_msgprot.h b/include/sbi_utils/mailbox/rpmi_msgprot.h
> index c5d564ac..fce08a9d 100644
> --- a/include/sbi_utils/mailbox/rpmi_msgprot.h
> +++ b/include/sbi_utils/mailbox/rpmi_msgprot.h
> @@ -225,6 +225,7 @@ enum rpmi_servicegroup_id {
> RPMI_SRVGRP_DEVICE_POWER = 0x0009,
> RPMI_SRVGRP_PERFORMANCE = 0x0000A,
> RPMI_SRVGRP_MANAGEMENT_MODE = 0x000B,
> + RPMI_SRVGRP_REQFWD = 0x000D,
> RPMI_SRVGRP_LOGGING = 0x000E,
> RPMI_SRVGRP_ID_MAX_COUNT,
This change belongs to PATCH1.
>
> diff --git a/include/sbi_utils/mpxy/fdt_mpxy_bridge.h b/include/sbi_utils/mpxy/fdt_mpxy_bridge.h
> new file mode 100644
> index 00000000..c86fb6ae
> --- /dev/null
> +++ b/include/sbi_utils/mpxy/fdt_mpxy_bridge.h
> @@ -0,0 +1,61 @@
> +/* SPDX-License-Identifier: BSD-2-Clause */
> +/*
> + * Copyright (c) 2026 Qualcomm, Inc
> + *
> + * Authors:
> + * Pawandeep Oza <pawandeep.oza at oss.qualcomm.com>
> + */
> +
> +#ifndef __FDT_MPXY_BRIDGE_H__
> +#define __FDT_MPXY_BRIDGE_H__
> +
> +#include <sbi/sbi_console.h>
> +#include <sbi/sbi_types.h>
> +#include <sbi/sbi_domain.h>
> +#include <sbi/sbi_string.h>
> +#include <sbi_utils/mpxy/fdt_mpxy_rpmi.h>
> +
> +#define SBI_MPXY_BRIDGE_DEFAULT_MSG_DATA_MAXLEN 4096
> +#define SBI_MPXY_BRIDGE_DOMAIN_NAME_MAXLEN 64
> +
> +struct mpxy_bridge_source;
> +
> +struct mpxy_bridge_dst_ctx {
> + void *respbuf;
> + u32 resp_bufsize;
> + unsigned long *resp_len_ptr;
> + u32 start_index;
> +};
> +
> +struct mpxy_bridge_src_ctx {
> + struct rpmi_message_header header;;
> + bool occupied;
> + bool retrieved;
> + bool completed;
> + void *msgbuf;
> + void *respbuf;
> + u32 msg_len;
> + u32 resp_bufsize;
> + u32 resp_length;
> +};
> +
> +struct mpxy_bridge_source_driver {
> + const struct fdt_match *match_table;
> + int (*probe)(const void *fdt,
> + int nodeoff, const struct fdt_match *match);
> +};
> +
> +struct mpxy_bridge_source_ops {
> + int (*xfer) (struct mpxy_bridge_src_ctx *src,
> + struct mpxy_bridge_dst_ctx *dst);
> + int (*complete) (struct mpxy_bridge_src_ctx *src,
> + void *msgbuf, u32 msg_len);
> +};
> +
> +int mpxy_bridge_register_source(const void *fdt, int nodeoff,
> + const struct mpxy_bridge_source_ops *ops,
> + struct mpxy_rpmi_service_data *srvcdata,
> + u32 num_services,
> + enum rpmi_servicegroup_id srvgrpid);
> +
> +#endif /* __FDT_MPXY_BRIDGE_H__ */
> diff --git a/lib/utils/mpxy/Kconfig b/lib/utils/mpxy/Kconfig
> index 74cd3a32..13aa27ec 100644
> --- a/lib/utils/mpxy/Kconfig
> +++ b/lib/utils/mpxy/Kconfig
> @@ -7,6 +7,10 @@ config FDT_MPXY
> depends on FDT
> default n
>
> +config FDT_MPXY_BRIDGE
> + bool "MPXY bridge driver for interdomain communication"
> + default n
> +
This config option should be FDT_MPXY_RPMI_BRIDGE instead of FDT_MPXY_BRIDGE
Also, update option description accordingly.
Add the FDT_MPXY_RPMI_BRIDGE option under "if FDT_MPXY_RPMI"
because the MPXY RPMI bridge driver depends upon FDT_MPXY_RPMI.
> config FDT_MPXY_RPMI
> bool "FDT based MPXY RPMI drivers"
> depends on FDT_MAILBOX && FDT_MPXY
> diff --git a/lib/utils/mpxy/fdt_mpxy_bridge.c b/lib/utils/mpxy/fdt_mpxy_bridge.c
> new file mode 100644
> index 00000000..ac44c3a6
> --- /dev/null
> +++ b/lib/utils/mpxy/fdt_mpxy_bridge.c
The source file should be named fdt_mpxy_rpmi_bridge.c
> @@ -0,0 +1,1000 @@
> +/*
> + * SPDX-License-Identifier: BSD-2-Clause
> + *
> + * Copyright (c) 2026 Qualcomm, Inc
> + *
> + * Authors:
> + * Pawandeep Oza <pawandeep.oza at 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_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_utils/fdt/fdt_driver.h>
> +#include <sbi_utils/mailbox/rpmi_msgprot.h>
> +#include <sbi_utils/mailbox/rpmi_mailbox.h>
> +#include <sbi_utils/mailbox/mailbox.h>
> +#include <sbi_utils/mpxy/fdt_mpxy_bridge.h>
> +
> +#define SBI_MPXY_BRIDGE_COMPATIBLE "opensbi,rpmi-mpxy-reqfwd-bridge"
> +#define SBI_MPXY_BRIDGE_PROP_DOMAIN_NAME "riscv,domain"
Drop the "_NAME" from the define because "riscv,domain" points to
domain DT node using phandle.
> +#define SBI_MPXY_BRIDGE_PROP_CHAN "riscv,sbi-mpxy-channel-id"
> +
> +
> +static const struct mpxy_bridge_source_driver *source_drivers[] = {
> +};
It is better to create new carray fdt_mpxy_rpmi_bridge_source_drivers
under lib/utils/mpxy
This will allow us to add bridge source drivers without any
changes in the fdt_mpxy_rpmi_bridge.c
> +
> +static uint16_t token = 0;
> +
> +struct mpxy_bridge_hart_slot {
> + int active_source;
> + struct mpxy_bridge_src_ctx *src_ctx;
> + struct mpxy_bridge_dst_ctx dst_resp;
> +};
> +
> +struct mpxy_bridge_slot_store {
> + struct mpxy_bridge_hart_slot *slots;
> + u32 hart_count;
> +};
> +
> +struct bridge_rpmi_endpoint {
> + struct mpxy_rpmi_data rpmi_data;
> + struct mpxy_bridge *bridge;
> + struct sbi_domain *domain;
> + struct sbi_mpxy_channel *channel;
> +};
> +
> +struct mpxy_bridge_endpoint {
> + char domain_name[SBI_MPXY_BRIDGE_DOMAIN_NAME_MAXLEN];
> + u32 channel_id;
> + struct sbi_domain *dom;
> + struct sbi_mpxy_channel ch;
> + struct bridge_rpmi_endpoint rpmi_endpoint;
> +};
Merge "struct bridge_rpmi_endpoint" into "struct mpxy_bridge_endpoint"
otherwise endpoint related code is confusing.
> +
> +struct mpxy_bridge_source {
> + int nodeoff;
> + struct mpxy_bridge_endpoint ep;
> + const struct mpxy_bridge_source_ops *ops;
> +};
> +
> +struct mpxy_bridge_destination {
> + struct mpxy_bridge_endpoint ep;
> +};
> +
> +struct mpxy_bridge {
> + struct sbi_dlist node;
> + int nodeoff;
> + struct mpxy_bridge_destination dst;
> + struct mpxy_bridge_source *sources;
> + u32 source_count;
> + struct mpxy_bridge_slot_store store;
> +};
> +
> +static SBI_LIST_HEAD(mpxy_bridge_list);
> +static bool mpxy_bridge_notifier_registered;
> +
> +static bool bridge_name_match(const char *a, const char *b)
> +{
> + return a && b && !sbi_strcmp(a, b);
> +}
> +
> +static int bridge_source_index(struct mpxy_bridge *br,
> + struct sbi_mpxy_channel *channel)
> +{
> + u32 i;
> +
> + for (i = 0; i < br->source_count; i++)
> + if (&br->sources[i].ep.ch == channel)
> + return (int)i;
> +
> + return -1;
> +}
> +
> +static struct mpxy_bridge *bridge_from_channel(
> + struct sbi_mpxy_channel *channel)
> +{
> + struct mpxy_bridge *br;
> + u32 i;
> +
> + sbi_list_for_each_entry(br, &mpxy_bridge_list, node) {
> + if (&br->dst.ep.ch == channel)
> + return br;
> + for (i = 0; i < br->source_count; i++)
> + if (&br->sources[i].ep.ch == channel)
> + return br;
> + }
> +
> + return NULL;
> +}
> +
> +static int bridge_get_current_slot(struct mpxy_bridge *br,
> + struct mpxy_bridge_hart_slot **slot)
> +{
> + u32 hartindex = current_hartindex();
> +
> + if (hartindex >= br->store.hart_count)
> + return SBI_ERR_INVALID_PARAM;
> +
> + *slot = &br->store.slots[hartindex];
> + return SBI_OK;
> +}
> +
> +static void bridge_src_ctx_set(struct mpxy_bridge_src_ctx *src,
> + void *msgbuf, u32 msg_len,
> + void *respbuf, u32 resp_bufsize,
> + unsigned long *resp_len)
> +{
> + src->msgbuf = msgbuf;
> + src->msg_len = msg_len;
> + src->respbuf = respbuf;
> + src->resp_bufsize = resp_bufsize;
> +}
> +
> +static void bridge_src_ctx_release(struct mpxy_bridge_src_ctx *src)
> +{
> + if (src)
> + *src = (struct mpxy_bridge_src_ctx){ 0 };
> +}
> +
> +static void bridge_slot_release(struct mpxy_bridge *br,
> + struct mpxy_bridge_hart_slot *slot)
> +{
> + u32 i;
> +
> + if (!br || !slot)
> + return;
> +
> + for (i = 0; i < br->source_count; i++)
> + bridge_src_ctx_release(&slot->src_ctx[i]);
> +
> + slot->active_source = -1;
> + slot->dst_resp = (struct mpxy_bridge_dst_ctx){ 0 };
> +}
> +
> +static int bridge_find_pending_source(struct mpxy_bridge *br,
> + struct mpxy_bridge_hart_slot *slot)
> +{
> + u32 i;
> +
> + for (i = 0; i < br->source_count; i++)
> + if (slot->src_ctx[i].occupied && !slot->src_ctx[i].retrieved)
> + return (int)i;
> +
> + return -1;
> +}
> +
> +static int bridge_find_completed_source(struct mpxy_bridge *br,
> + struct mpxy_bridge_hart_slot *slot)
> +{
> + u32 i;
> +
> + for (i = 0; i < br->source_count; i++)
> + if (slot->src_ctx[i].occupied &&
> + slot->src_ctx[i].retrieved &&
> + slot->src_ctx[i].completed )
> + return (int)i;
> +
> + return -1;
> +}
> +
> +struct mpxy_bridge *bridge_find_by_nodeoff(int nodeoff)
> +{
> + struct mpxy_bridge *br;
> +
> + sbi_list_for_each_entry(br, &mpxy_bridge_list, node) {
> + if (br->nodeoff == nodeoff)
> + return br;
> + }
> +
> + return NULL;
> +}
> +
> +static void bridge_switch_domain(struct mpxy_bridge *br,
> + struct sbi_mpxy_channel *channel)
> +{
> + if (channel == &br->dst.ep.ch)
> + sbi_domain_context_exit();
> + else
> + sbi_domain_context_enter(br->dst.ep.ch.owner_domain);
> +}
> +
> +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 = msgbuf ? ((u32 *)msgbuf)[0] : 0;
> +}
> +
> +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 mpxy_bridge_src_ctx *src = NULL;
> + struct mpxy_bridge_source *source;
> + int active_source;
> + int ret = SBI_OK;
> +
> + resp = (struct rpmi_request_complete_resp *)respbuf;
> + active_source = slot->active_source;
> +
> + do {
> + if (active_source < 0) {
> + resp->status = RPMI_ERR_NO_DATA;
> + break;
> + }
> +
> + src = &slot->src_ctx[active_source];
> +
> + if (!src->occupied || !src->retrieved) {
> + resp->status = RPMI_ERR_NO_DATA;
> + break;
> + }
> +
> + source = &br->sources[active_source];
> + ret = source->ops->complete(src, msgbuf, msg_len);
> + src->completed = true;
> + if (ret)
> + resp->status = RPMI_ERR_NOTSUPP;
> + else
> + resp->status = RPMI_SUCCESS;
> + } while (0);
Why do we need this "do { } while (0)" ??
> +
> + /*
> + * Number of forwarded messages available for retrieval
> + * this is always 0 as source is stalled on the same hart till the complete
> + * completes.
> + */
> + resp->num_of_messages = 0;
> + *resp_len = sizeof(*resp);
> +
> + return SBI_OK;
> +}
> +
> +static int bridge_dst_send_message_with_response(
> + struct mpxy_bridge *br,
> + struct mpxy_bridge_hart_slot *slot,
> + u32 msg_id, void *msgbuf, u32 msg_len,
> + void *respbuf, u32 resp_bufsize,
> + unsigned long *resp_len)
> +{
> + struct mpxy_bridge_source *source;
> + struct mpxy_bridge_src_ctx *src;
> + int source_index;
> + int ret = SBI_OK;
> +
> + switch (msg_id) {
> + case REQFWD_RETRIEVE_CURRENT_MESSAGE:
> + source_index = bridge_find_pending_source(br, slot);
> + bridge_save_dst_ctx(slot, respbuf, resp_bufsize, resp_len, msgbuf);
> + /*
> + * Waiting for the message from one of the sources.
> + * schedule the next domain.
> + */
> + if (source_index < 0) {
> + source_index = bridge_find_completed_source(br, slot);
> + if (source_index >= 0) {
> + /* it is time to send reponse back to the source domain. */
> + source = &br->sources[source_index];
> + src = &slot->src_ctx[source_index];
> + slot->active_source = -1;
> + *resp_len = src->resp_length;
> + bridge_src_ctx_release(src);
> + }
> + bridge_switch_domain(br, &br->dst.ep.ch);
> + } else {
> + source = &br->sources[source_index];
> + src = &slot->src_ctx[source_index];
> + /* hand over the transfer to the source driver. */
> + ret = source->ops->xfer(src, &slot->dst_resp);
> + src->retrieved = true;
> + }
> + break;
> +
> + case REQFWD_COMPLETE_CURRENT_MESSAGE:
> + ret = bridge_dst_complete(br, slot, msgbuf, msg_len,
> + respbuf, resp_bufsize, resp_len);
> + break;
> +
> + default:
> + ret = SBI_ERR_NOT_SUPPORTED;
> + break;
> + }
> +
> + return ret;
> +}
> +
> +
> +static int bridge_src_ctx_occupy(struct mpxy_bridge_src_ctx *src)
> +{
> + if (src->occupied) {
> + sbi_printf("%s: slot occupied\n", __func__);
> + return SBI_ERR_FAILED;
> + }
> + src->occupied = true;
> + src->retrieved = false;
> +
> + 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;
> + struct mpxy_bridge_src_ctx *src;
> + struct mpxy_bridge_source *source;
> + struct mpxy_bridge_hart_slot *slot = NULL;
> + int source_index;
> + int ret = SBI_OK;
> +
> + do {
> + br = bridge_from_channel(channel);
> + if (!br) {
> + ret = SBI_ERR_NOT_SUPPORTED;
> + break;
> + }
> +
> + ret = bridge_get_current_slot(br, &slot);
> + if (ret)
> + break;
> +
> + if (channel == &br->dst.ep.ch) {
> + ret = bridge_dst_send_message_with_response(br, slot, msg_id,
> + msgbuf, msg_len,
> + respbuf, resp_bufsize,
> + resp_len);
> + break;
> + }
> +
> + source_index = bridge_source_index(br, channel);
> + if (source_index < 0) {
> + ret = SBI_ERR_NOT_SUPPORTED;
> + break;
> + }
> +
> + src = &slot->src_ctx[source_index];
> + source = &br->sources[source_index];
> +
> + src->header.servicegroup_id =
> + cpu_to_le16(source->ep.rpmi_endpoint.rpmi_data.servicegrp_id);
> + src->header.service_id = msg_id;
> + src->header.flags = 0;
> + src->header.datalen = cpu_to_le16(sizeof(src->header)) + msg_len;
> + src->header.token = cpu_to_le16(++token);
> +
> + bridge_src_ctx_set(src, msgbuf, msg_len, respbuf,
> + resp_bufsize, resp_len);
> +
> + ret = bridge_src_ctx_occupy(src);
> + slot->active_source = source_index;
> +
> + if (!ret) {
> + if (slot->dst_resp.respbuf) {
> + ret = source->ops->xfer(src, &slot->dst_resp);
> + src->retrieved = true;
> + }
> + bridge_switch_domain(br, &br->sources[source_index].ep.ch);
> + }
> + } while (0);
> +
> + return ret;
> +}
> +
> +static int bridge_rpmi_get_attribute_group(void *context,
> + struct mbox_chan *chan,
> + enum rpmi_channel_attribute_id attr_id,
> + u32 *out_value)
> +{
> + struct bridge_rpmi_endpoint *ep = context;
> + (void)chan;
> +
> + switch (attr_id) {
> + case RPMI_CHANNEL_ATTR_PROTOCOL_VERSION:
> + *((u32 *)out_value) = 1;
> + break;
> + case RPMI_CHANNEL_ATTR_MAX_DATA_LEN:
> + *((u32 *)out_value) = SBI_MPXY_BRIDGE_DEFAULT_MSG_DATA_MAXLEN;
> + break;
> + case RPMI_CHANNEL_ATTR_TX_TIMEOUT:
> + *((u32 *)out_value) = RPMI_DEF_TX_TIMEOUT;
> + break;
> + case RPMI_CHANNEL_ATTR_RX_TIMEOUT:
> + *((u32 *)out_value) = RPMI_DEF_RX_TIMEOUT;
> + break;
> + case RPMI_CHANNEL_ATTR_SERVICEGROUP_ID:
> + *((u32 *)out_value) = ep->rpmi_data.servicegrp_id;
> + break;
> + default:
> + *((u32 *)out_value) = 0;
> + break;
> + }
> +
> + return SBI_OK;
> +}
> +
> +static int bridge_rpmi_xfer_group(void *context,
> + struct mbox_chan *chan,
> + struct mbox_xfer *xfer)
> +{
> + struct bridge_rpmi_endpoint *ep = context;
> + struct rpmi_message_args *args;
> + unsigned long ack_len = 0;
> + int ret;
> +
> + (void)chan;
> +
> + args = xfer->args;
> +
> + /*
> + * we only support send message with response.
> + * TDO: bridge_send_message_without_response
> + */
> + if (! (args->flags & RPMI_MSG_FLAGS_NO_RX)) {
> + ret = bridge_send_message_with_response(
> + ep->channel,
> + args->service_id,
> + xfer->tx, xfer->tx_len,
> + xfer->rx, xfer->rx_len,
> + &ack_len);
> +
> + if (ret)
> + return ret;
> + }
> +
> + args->rx_data_len = ack_len;
> + xfer->rx_len = ack_len;
> +
> + return SBI_OK;
> +}
> +
> +static int bridge_rpmi_setup_group(void **context,
> + struct mbox_chan *chan,
> + struct sbi_domain *dom,
> + const struct mpxy_rpmi_data *data)
> +{
> + struct bridge_rpmi_endpoint *ep;
> + int ret = SBI_OK;
> +
> + (void)chan;
> +
> + if (!context || !dom || !data)
> + return SBI_EINVAL;
> +
> + ep = container_of(data, struct bridge_rpmi_endpoint, rpmi_data);
> +
> + ep->domain = dom;
> + *context = ep;
> + return ret;
> +}
> +
> +static struct mpxy_rpmi_service_data bridge_dst_rpmi_services[] = {
> + {
> + .id = REQFWD_RETRIEVE_CURRENT_MESSAGE,
> + .min_tx_len = 0,
> + .max_tx_len = SBI_MPXY_BRIDGE_DEFAULT_MSG_DATA_MAXLEN,
> + .min_rx_len = sizeof(struct rpmi_request_forward_resp),
> + .max_rx_len = SBI_MPXY_BRIDGE_DEFAULT_MSG_DATA_MAXLEN,
> + },
> + {
> + .id = REQFWD_COMPLETE_CURRENT_MESSAGE,
> + .min_tx_len = 0,
> + .max_tx_len = SBI_MPXY_BRIDGE_DEFAULT_MSG_DATA_MAXLEN,
> + .min_rx_len = sizeof(struct rpmi_request_complete_resp),
> + .max_rx_len = sizeof(struct rpmi_request_complete_resp),
> + },
> +};
> +
> +static const struct mpxy_rpmi_data bridge_dst_rpmi_data_template = {
> + .servicegrp_id = RPMI_SRVGRP_REQFWD,
> + .num_services = sizeof(bridge_dst_rpmi_services) /
> + sizeof(bridge_dst_rpmi_services[0]),
> + .service_data = bridge_dst_rpmi_services,
> + .get_attribute_group = bridge_rpmi_get_attribute_group,
> + .xfer_group = bridge_rpmi_xfer_group,
> + .setup_group = bridge_rpmi_setup_group,
> +};
> +
> +static const struct mpxy_rpmi_data bridge_src_rpmi_data_template = {
> + .get_attribute_group = bridge_rpmi_get_attribute_group,
> + .xfer_group = bridge_rpmi_xfer_group,
> + .setup_group = bridge_rpmi_setup_group,
> +};
> +
> +static void bridge_init_internal_channel(struct sbi_mpxy_channel *ch,
> + struct sbi_domain *dom, u32 id)
> +{
> + sbi_memset(ch, 0, sizeof(*ch));
> + ch->owner_domain = dom;
> + ch->channel_id = id;
> + ch->attrs.msg_data_maxlen = SBI_MPXY_BRIDGE_DEFAULT_MSG_DATA_MAXLEN;
> +}
> +
> +static int bridge_register_destination(struct mpxy_bridge *br,
> + struct sbi_domain *dom)
> +{
> + struct bridge_rpmi_endpoint *ep;
> + int ret;
> +
> + ep = &br->dst.ep.rpmi_endpoint;
> + ep->rpmi_data = bridge_dst_rpmi_data_template;
> + ep->bridge = br;
> + ep->domain = dom;
> +
> + bridge_init_internal_channel(&br->dst.ep.ch, dom,
> + br->dst.ep.channel_id);
> + ep->channel = &br->dst.ep.ch;
> +
> + ret = mpxy_rpmi_no_mbox_init(&ep->rpmi_data,
> + br->dst.ep.channel_id,
> + br->dst.ep.domain_name);
> +
Redundant new line.
> + if (!ret)
> + br->dst.ep.dom = dom;
> +
> + return ret;
> +}
> +
> +int mpxy_bridge_register_source(const void *fdt,
> + int nodeoff, const struct mpxy_bridge_source_ops *ops,
> + struct mpxy_rpmi_service_data *srvcdata,
> + u32 num_services,
> + enum rpmi_servicegroup_id srvgrpid)
> +{
> + struct mpxy_bridge *br;
> + struct mpxy_bridge_source *src;
> + struct bridge_rpmi_endpoint *ep;
> + int parent_nodeoff;
> + int ret = SBI_OK;
> + u32 i;
> +
> + do {
> + if (!fdt || nodeoff < 0 || !ops) {
> + ret = SBI_EINVAL;
> + break;
> + }
> +
> + parent_nodeoff = fdt_parent_offset(fdt, nodeoff);
> + if (parent_nodeoff < 0) {
> + ret = SBI_ENODEV;
> + break;
> + }
> +
> + br = bridge_find_by_nodeoff(parent_nodeoff);
> + if (!br) {
> + ret = SBI_ENODEV;
> + break;
> + }
> +
> + src = NULL;
> + for (i = 0; i < br->source_count; i++) {
> + if (br->sources[i].nodeoff == nodeoff) {
> + src = &br->sources[i];
> + break;
> + }
> + }
> +
> + if (!src) {
> + ret = SBI_ENODEV;
> + break;
> + }
> +
> + src->ops = ops;
> + if (!src->ops->xfer || !src->ops->complete) {
> + ret = SBI_ENOTSUPP;
> + sbi_printf("source driver must provide xfer and complete\n");
> + src->ep.dom = NULL;
> + break;
> + }
> +
> + ep = &src->ep.rpmi_endpoint;
> + ep->bridge = br;
> + ep->channel = &src->ep.ch;
> + ep->rpmi_data = bridge_src_rpmi_data_template;
> + ep->rpmi_data.service_data = srvcdata;
> + ep->rpmi_data.servicegrp_id = srvgrpid;
> + ep->rpmi_data.num_services = num_services;
> +
> + src->ep.ch.channel_id = src->ep.channel_id;
> + src->ep.ch.owner_domain = src->ep.dom;
> + } while (0);
Why do we need this "do { } while (0)" ??
> +
> + return ret;
> +}
Add empty newline between two consecutive functions here and
wherever it is missing.
> +static int bridge_get_domain_prop(const void *fdt, int nodeoff,
> + const char *propname, char *out,
> + size_t outsz)
> +{
> + const fdt32_t *value;
> + const char *name;
> + int len, domain_nodeoff;
> + size_t n;
> +
> + value = fdt_getprop(fdt, nodeoff, propname, &len);
> + if (!value || len < (int)sizeof(*value))
> + return SBI_ENODEV;
> +
> + domain_nodeoff = fdt_node_offset_by_phandle(fdt,
> + fdt32_to_cpu(*value));
> + if (domain_nodeoff < 0)
> + return SBI_ENODEV;
> +
> + name = fdt_get_name(fdt, domain_nodeoff, NULL);
> + if (!name || !name[0] || !outsz)
> + return SBI_ENODEV;
> +
> + n = sbi_strlen(name);
> + if (n >= outsz)
> + n = outsz - 1;
> + sbi_memcpy(out, name, n);
> + out[n] = '\0';
> +
> + return SBI_OK;
> +}
> +
> +static int bridge_get_u32_prop(const void *fdt, int nodeoff,
> + const char *propname, u32 *out)
> +{
> + const fdt32_t *value;
> + int len;
> +
> + value = fdt_getprop(fdt, nodeoff, propname, &len);
> + if (!value || len < (int)sizeof(*value))
> + return SBI_ENODEV;
> +
> + *out = fdt32_to_cpu(*value);
> + 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)
> +{
> + u32 i, rc;
> +
> + if (!br || !dom)
> + return;
> +
> + if (!br->dst.ep.dom &&
> + bridge_name_match(dom->name, br->dst.ep.domain_name)) {
> + bridge_register_destination(br, (struct sbi_domain *)dom);
> + }
> +
> + for (i = 0; i < br->source_count; i++) {
> + if (!br->sources[i].ep.dom &&
> + bridge_name_match(dom->name, br->sources[i].ep.domain_name)) {
> + br->sources[i].ep.dom = (struct sbi_domain *)dom;
> +
> + rc = mpxy_rpmi_no_mbox_init(&br->sources[i].ep.rpmi_endpoint.rpmi_data,
> + br->sources[i].ep.channel_id,
> + br->sources[i].ep.domain_name);
> + if (rc) {
> + sbi_printf("%s: mpxy_rpmi_no_mbox_init failed with ret %d for source[%u]\n",
> + __func__, rc, i);
> + br->sources[i].ep.dom = NULL;
> + }
> + }
> + }
> +
> + if (br->dst.ep.dom) {
> + for (i = 0; i < br->source_count; i++) {
> + if (br->sources[i].ep.dom &&
> + !sbi_hartmask_equal(
> + br->sources[i].ep.dom->possible_harts,
> + br->dst.ep.dom->possible_harts)) {
> + sbi_printf("%s: source[%u] and destination "
> + "have different possible harts\n",
> + __func__, i);
> + br->sources[i].ep.dom = NULL;
> + }
> + }
> + }
> +}
> +
> +static int bridge_domain_notifier(struct sbi_domain *dom,
> + enum sbi_domain_notify_event event,
> + void *priv)
> +{
> + struct mpxy_bridge *br;
> +
> + (void)priv;
> +
> + if (event == SBI_DOMAIN_NOTIFY_EVENT_REGISTER)
> + sbi_list_for_each_entry(br, &mpxy_bridge_list, node)
> + bridge_try_bind_domain(br, dom);
> +
> + return SBI_OK;
> +}
> +
> +static int bridge_parse_fdt(const void *fdt, int nodeoff,
> + struct mpxy_bridge *br)
> +{
> + int ret;
> +
> + ret = bridge_get_domain_prop(fdt, nodeoff,
> + SBI_MPXY_BRIDGE_PROP_DOMAIN_NAME,
> + br->dst.ep.domain_name,
> + sizeof(br->dst.ep.domain_name));
> + if (ret)
> + return ret;
> +
> + return bridge_get_u32_prop(fdt, nodeoff,
> + SBI_MPXY_BRIDGE_PROP_CHAN,
> + &br->dst.ep.channel_id);
> +}
> +
> +static int bridge_count_sources(const void *fdt, int nodeoff)
> +{
> + int child, count = 0;
> +
> + fdt_for_each_subnode(child, fdt, nodeoff)
> + count++;
> +
> + return count;
> +}
> +
> +static const struct fdt_match *bridge_match_node(const void *fdt, int nodeoff,
> + const struct fdt_match *match_table)
> +{
> + const struct fdt_match *m;
> +
> + for (m = match_table; m->compatible; m++) {
> + if (!fdt_node_check_compatible(fdt, nodeoff, m->compatible))
> + return m;
> + }
> +
> + return NULL;
> +}
> +
> +static const struct mpxy_bridge_source_driver *
> +bridge_find_source_driver(const void *fdt, int nodeoff,
> + const struct fdt_match **match)
> +{
> + const struct mpxy_bridge_source_driver *driver;
> + int i;
> +
> + for (i = 0; i < array_size(source_drivers); i++) {
> + driver = source_drivers[i];
> + *match = bridge_match_node(fdt, nodeoff, driver->match_table);
> + if (*match)
> + return driver;
> + }
> +
> + return NULL;
> +}
> +
> +static int bridge_probe_sources(const void *fdt, int bridge_nodeoff,
> + struct mpxy_bridge *bridge)
> +{
> + const struct mpxy_bridge_source_driver *driver;
> + const struct fdt_match *match;
> + int child_nodeoff;
> +
> + fdt_for_each_subnode(child_nodeoff, fdt, bridge_nodeoff) {
> + driver = bridge_find_source_driver(fdt, child_nodeoff, &match);
> + if (!driver)
> + continue;
> +
> + if (driver->probe(fdt, child_nodeoff, match)) {
> + sbi_printf("%s: failed to probe source at node %d\n",
> + __func__, child_nodeoff);
> + }
> + }
> +
> + return SBI_OK;
> +}
> +
> +static int bridge_parse_sources(const void *fdt, int nodeoff,
> + struct mpxy_bridge *br)
> +{
> + struct mpxy_bridge_source *src;
> + int child;
> + u32 index = 0;
> + int ret = SBI_OK;
> +
> + fdt_for_each_subnode(child, fdt, nodeoff) {
> + src = &br->sources[index];
> + src->nodeoff = child;
> +
> + if ((ret = bridge_get_domain_prop(fdt, child,
> + SBI_MPXY_BRIDGE_PROP_DOMAIN_NAME,
> + src->ep.domain_name,
> + sizeof(src->ep.domain_name))))
> + continue;
> +
> + if ((ret = bridge_get_u32_prop(fdt, child,
> + SBI_MPXY_BRIDGE_PROP_CHAN,
> + &src->ep.channel_id)))
> + continue;
> +
> + index++;
> + }
> +
> + return ret;
> +}
> +
> +static int bridge_validate(struct mpxy_bridge *br)
> +{
> + u32 i, j;
> +
> + if (!br->dst.ep.domain_name[0] || !br->source_count)
> + return SBI_EINVAL;
> +
> + for (i = 0; i < br->source_count; i++) {
> + if (!br->sources[i].ep.domain_name[0] ||
> + br->sources[i].ep.channel_id == br->dst.ep.channel_id)
> + return SBI_EINVAL;
> +
> + for (j = i + 1; j < br->source_count; j++)
> + if (br->sources[i].ep.channel_id ==
> + br->sources[j].ep.channel_id)
> + return SBI_EINVAL;
> + }
> +
> + return SBI_OK;
> +}
> +
> +static void bridge_free(struct mpxy_bridge *br)
> +{
> + u32 i;
> +
> + if (!br)
> + return;
> +
> + for (i = 0; i < br->store.hart_count; i++)
> + if (br->store.slots[i].src_ctx)
> + sbi_free(br->store.slots[i].src_ctx);
> +
> + if (br->store.slots)
> + sbi_free(br->store.slots);
> +
> + if (br->sources)
> + sbi_free(br->sources);
> +
> + sbi_free(br);
> +}
> +
> +static struct mpxy_bridge *bridge_alloc(u32 hart_count, u32 source_count)
> +{
> + struct mpxy_bridge *br;
> + u32 i;
> +
> + if (!hart_count || !source_count)
> + return NULL;
> +
> + br = sbi_zalloc(sizeof(*br));
> + if (!br)
> + return NULL;
> +
> + br->sources = sbi_zalloc(sizeof(*br->sources) * source_count);
> + br->store.slots = sbi_zalloc(sizeof(*br->store.slots) * hart_count);
> +
> + if (!br->sources || !br->store.slots) {
> + bridge_free(br);
> + return NULL;
> + }
> +
> + for (i = 0; i < hart_count; i++) {
> + br->store.slots[i].src_ctx = sbi_zalloc(
> + sizeof(struct mpxy_bridge_src_ctx) * source_count);
> + if (!br->store.slots[i].src_ctx) {
> + bridge_free(br);
> + return NULL;
> + }
> + }
> +
> + br->source_count = source_count;
> + br->store.hart_count = hart_count;
> + return br;
> +}
> +
> +static int bridge_register_notifier(void)
> +{
> + int ret = SBI_OK;
> +
> + if (!mpxy_bridge_notifier_registered) {
> + ret = sbi_domain_register_notifier(
> + bridge_domain_notifier,
> + SBI_DOMAIN_NOTIFY_EVENT_REGISTER,
> + NULL);
> + if (!ret)
> + mpxy_bridge_notifier_registered = true;
> + }
> +
> + return ret;
> +}
> +
> +static int bridge_init(const void *fdt, int nodeoff,
> + const struct fdt_match *match)
> +{
> + struct mpxy_bridge *br = NULL;
> + struct sbi_scratch *scratch;
> + u32 hart_count;
> + u32 source_count;
> + u32 i;
> + int ret = SBI_OK;
> +
> + (void)match;
> +
> + scratch = sbi_hartindex_to_scratch(current_hartindex());
> + hart_count = sbi_platform_hart_count(sbi_platform_ptr(scratch));
> +
> + do {
> + source_count = bridge_count_sources(fdt, nodeoff);
> + br = bridge_alloc(hart_count, source_count);
> + if (!br) {
> + ret = SBI_ENOMEM;
> + break;
> + }
> +
> + br->nodeoff = nodeoff;
> +
> + if ((ret = bridge_parse_fdt(fdt, nodeoff, br)))
> + break;
> +
> + if ((ret = bridge_parse_sources(fdt, nodeoff, br)))
> + break;
> +
> + if ((ret = bridge_validate(br)))
> + break;
> +
> + sbi_list_add_tail(&br->node, &mpxy_bridge_list);
> +
> + if ((ret = bridge_probe_sources(fdt, nodeoff, br))) {
> + sbi_list_del(&br->node);
> + break;
> + }
> +
> + for (i = 0; i < hart_count; i++)
> + bridge_slot_release(br, &br->store.slots[i]);
> +
> + if ((ret = bridge_register_notifier()))
> + sbi_list_del(&br->node);
> +
> + } while (0);
> +
> + if (ret && br)
> + bridge_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,
> +};
> diff --git a/lib/utils/mpxy/objects.mk b/lib/utils/mpxy/objects.mk
> index 7645d13a..0ba79d59 100644
> --- a/lib/utils/mpxy/objects.mk
> +++ b/lib/utils/mpxy/objects.mk
> @@ -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
> diff --git a/platform/generic/configs/defconfig b/platform/generic/configs/defconfig
> index 2617ea19..fc138659 100644
> --- a/platform/generic/configs/defconfig
> +++ b/platform/generic/configs/defconfig
> @@ -86,3 +86,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
s/CONFIG_FDT_MPXY_BRIDGE/CONFIG_FDT_MPXY_RPMI_BRIDGE
> +
Redundant new line.
> --
> 2.43.0
>
Regards,
Anup
More information about the opensbi
mailing list