This change fixes the deregister functionality for icmsg backend. There are two changes: Memory access layer was not initialized properly when the endpoint was registered again. This lead to memory fault when attepmt to write to the buffer was done. Mbox driver was initialized before memory access layer was initialized. This could lead to race condition where immediately after the mbox driver was initialized, the receiving work is scheduled due to pending interrupt. In that case an attempt to read from garbage address will occur. Signed-off-by: Emil Obalski <Emil.Obalski@nordicsemi.no>
318 lines
6.7 KiB
C
318 lines
6.7 KiB
C
/*
|
|
* Copyright (c) 2023 Nordic Semiconductor ASA
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include <zephyr/ipc/icmsg_me.h>
|
|
|
|
#include <string.h>
|
|
|
|
#define SEND_BUF_SIZE CONFIG_IPC_SERVICE_BACKEND_ICMSG_ME_SEND_BUF_SIZE
|
|
#define NUM_EP CONFIG_IPC_SERVICE_BACKEND_ICMSG_ME_NUM_EP
|
|
|
|
#define EVENT_BOUND 0x01
|
|
|
|
#define HEADER_SIZE (sizeof(icmsg_me_ept_id_t))
|
|
|
|
static void *icmsg_buffer_to_user_buffer(const void *icmsg_buffer)
|
|
{
|
|
return (void *)(((char *)icmsg_buffer) + HEADER_SIZE);
|
|
}
|
|
|
|
static void *user_buffer_to_icmsg_buffer(const void *user_buffer)
|
|
{
|
|
return (void *)(((char *)user_buffer) - HEADER_SIZE);
|
|
}
|
|
|
|
static size_t icmsg_buffer_len_to_user_buffer_len(size_t icmsg_buffer_len)
|
|
{
|
|
return icmsg_buffer_len - HEADER_SIZE;
|
|
}
|
|
|
|
static size_t user_buffer_len_to_icmsg_buffer_len(size_t user_buffer_len)
|
|
{
|
|
return user_buffer_len + HEADER_SIZE;
|
|
}
|
|
|
|
static void set_ept_id_in_send_buffer(uint8_t *send_buffer,
|
|
icmsg_me_ept_id_t ept_id)
|
|
{
|
|
send_buffer[0] = ept_id;
|
|
}
|
|
|
|
int icmsg_me_init(const struct icmsg_config_t *conf,
|
|
struct icmsg_me_data_t *data)
|
|
{
|
|
k_event_init(&data->event);
|
|
k_mutex_init(&data->send_mutex);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int icmsg_me_open(const struct icmsg_config_t *conf,
|
|
struct icmsg_me_data_t *data,
|
|
const struct ipc_service_cb *cb,
|
|
void *ctx)
|
|
{
|
|
data->ept_cfg.cb = *cb;
|
|
data->ept_cfg.priv = ctx;
|
|
|
|
return icmsg_open(conf, &data->icmsg_data, &data->ept_cfg.cb,
|
|
data->ept_cfg.priv);
|
|
}
|
|
|
|
void icmsg_me_icmsg_bound(struct icmsg_me_data_t *data)
|
|
{
|
|
k_event_post(&data->event, EVENT_BOUND);
|
|
}
|
|
|
|
void icmsg_me_wait_for_icmsg_bind(struct icmsg_me_data_t *data)
|
|
{
|
|
k_event_wait(&data->event, EVENT_BOUND, false, K_FOREVER);
|
|
}
|
|
|
|
int icmsg_me_set_empty_ept_cfg_slot(struct icmsg_me_data_t *data,
|
|
const struct ipc_ept_cfg *ept_cfg,
|
|
icmsg_me_ept_id_t *id)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < NUM_EP; i++) {
|
|
if (data->epts[i] == NULL) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i >= NUM_EP) {
|
|
return -ENOMEM;
|
|
}
|
|
|
|
data->epts[i] = ept_cfg;
|
|
*id = i + 1;
|
|
return 0;
|
|
}
|
|
|
|
static int get_ept_cfg_index(icmsg_me_ept_id_t id)
|
|
{
|
|
int i = id - 1;
|
|
|
|
if (i >= NUM_EP || i < 0) {
|
|
return -ENOENT;
|
|
}
|
|
|
|
return i;
|
|
}
|
|
|
|
int icmsg_me_set_ept_cfg(struct icmsg_me_data_t *data, icmsg_me_ept_id_t id,
|
|
const struct ipc_ept_cfg *ept_cfg)
|
|
{
|
|
int i = get_ept_cfg_index(id);
|
|
|
|
if (i < 0) {
|
|
return i;
|
|
}
|
|
|
|
data->epts[i] = ept_cfg;
|
|
return 0;
|
|
}
|
|
|
|
int icmsg_me_get_ept_cfg(struct icmsg_me_data_t *data, icmsg_me_ept_id_t id,
|
|
const struct ipc_ept_cfg **ept_cfg)
|
|
{
|
|
int i = get_ept_cfg_index(id);
|
|
|
|
if (i < 0) {
|
|
return i;
|
|
}
|
|
|
|
*ept_cfg = data->epts[i];
|
|
return 0;
|
|
}
|
|
|
|
void icmsg_me_reset_ept_cfg(struct icmsg_me_data_t *data, icmsg_me_ept_id_t id)
|
|
{
|
|
int i = get_ept_cfg_index(id);
|
|
|
|
if (i < 0) {
|
|
return;
|
|
}
|
|
|
|
data->epts[i] = NULL;
|
|
}
|
|
|
|
void icmsg_me_received_data(struct icmsg_me_data_t *data, icmsg_me_ept_id_t id,
|
|
const void *msg, size_t len)
|
|
{
|
|
int r;
|
|
const struct ipc_ept_cfg *ept;
|
|
|
|
r = icmsg_me_get_ept_cfg(data, id, &ept);
|
|
if (r < 0) {
|
|
return;
|
|
}
|
|
|
|
if (ept == NULL) {
|
|
return;
|
|
}
|
|
|
|
if (ept->cb.received) {
|
|
ept->cb.received(icmsg_buffer_to_user_buffer(msg),
|
|
icmsg_buffer_len_to_user_buffer_len(len),
|
|
ept->priv);
|
|
}
|
|
}
|
|
|
|
int icmsg_me_send(const struct icmsg_config_t *conf,
|
|
struct icmsg_me_data_t *data, icmsg_me_ept_id_t id,
|
|
const void *msg, size_t len)
|
|
{
|
|
int r;
|
|
int sent_bytes;
|
|
|
|
if (user_buffer_len_to_icmsg_buffer_len(len) >= SEND_BUF_SIZE) {
|
|
return -EBADMSG;
|
|
}
|
|
|
|
k_mutex_lock(&data->send_mutex, K_FOREVER);
|
|
|
|
/* TODO: Optimization: How to avoid this copying? */
|
|
/* We could implement scatter list for icmsg_send, but it would require
|
|
* scatter list also for SPSC buffer implementation.
|
|
*/
|
|
set_ept_id_in_send_buffer(data->send_buffer, id);
|
|
memcpy(icmsg_buffer_to_user_buffer(data->send_buffer), msg, len);
|
|
|
|
r = icmsg_send(conf, &data->icmsg_data, data->send_buffer,
|
|
user_buffer_len_to_icmsg_buffer_len(len));
|
|
if (r > 0) {
|
|
sent_bytes = icmsg_buffer_len_to_user_buffer_len(r);
|
|
}
|
|
|
|
k_mutex_unlock(&data->send_mutex);
|
|
|
|
if (r < 0) {
|
|
return r;
|
|
}
|
|
|
|
__ASSERT_NO_MSG(r >= HEADER_SIZE);
|
|
if (r < HEADER_SIZE) {
|
|
return 0;
|
|
}
|
|
|
|
return sent_bytes;
|
|
}
|
|
|
|
static size_t get_buffer_length_to_pass(size_t icmsg_buffer_len)
|
|
{
|
|
if (icmsg_buffer_len >= HEADER_SIZE) {
|
|
return icmsg_buffer_len_to_user_buffer_len(icmsg_buffer_len);
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
int icmsg_me_get_tx_buffer(const struct icmsg_config_t *conf,
|
|
struct icmsg_me_data_t *data,
|
|
void **buffer, uint32_t *user_len, k_timeout_t wait)
|
|
{
|
|
void *icmsg_buffer;
|
|
int r;
|
|
size_t icmsg_len;
|
|
|
|
if (!K_TIMEOUT_EQ(wait, K_NO_WAIT)) {
|
|
return -ENOTSUP;
|
|
}
|
|
|
|
if (*user_len) {
|
|
icmsg_len = user_buffer_len_to_icmsg_buffer_len(*user_len);
|
|
} else {
|
|
icmsg_len = 0;
|
|
}
|
|
|
|
r = icmsg_get_tx_buffer(conf, &data->icmsg_data,
|
|
&icmsg_buffer, &icmsg_len);
|
|
if (r == -ENOMEM) {
|
|
*user_len = get_buffer_length_to_pass(icmsg_len);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
if (r < 0) {
|
|
return r;
|
|
}
|
|
|
|
/* If requested max buffer length (*len == 0) allocated buffer might be
|
|
* shorter than HEADER_SIZE. In such circumstances drop the buffer
|
|
* and return error.
|
|
*/
|
|
*user_len = get_buffer_length_to_pass(icmsg_len);
|
|
|
|
if (!(*user_len)) {
|
|
r = icmsg_drop_tx_buffer(conf, &data->icmsg_data, icmsg_buffer);
|
|
__ASSERT_NO_MSG(!r);
|
|
return -ENOBUFS;
|
|
}
|
|
|
|
*buffer = icmsg_buffer_to_user_buffer(icmsg_buffer);
|
|
return 0;
|
|
|
|
}
|
|
|
|
int icmsg_me_drop_tx_buffer(const struct icmsg_config_t *conf,
|
|
struct icmsg_me_data_t *data,
|
|
const void *buffer)
|
|
{
|
|
const void *buffer_to_drop = user_buffer_to_icmsg_buffer(buffer);
|
|
|
|
return icmsg_drop_tx_buffer(conf, &data->icmsg_data, buffer_to_drop);
|
|
}
|
|
|
|
int icmsg_me_send_nocopy(const struct icmsg_config_t *conf,
|
|
struct icmsg_me_data_t *data, icmsg_me_ept_id_t id,
|
|
const void *msg, size_t len)
|
|
{
|
|
void *buffer_to_send;
|
|
size_t len_to_send;
|
|
int r;
|
|
int sent_bytes;
|
|
|
|
buffer_to_send = user_buffer_to_icmsg_buffer(msg);
|
|
len_to_send = user_buffer_len_to_icmsg_buffer_len(len);
|
|
|
|
set_ept_id_in_send_buffer(buffer_to_send, id);
|
|
|
|
r = icmsg_send_nocopy(conf, &data->icmsg_data,
|
|
buffer_to_send, len_to_send);
|
|
|
|
if (r < 0) {
|
|
return r;
|
|
}
|
|
|
|
__ASSERT_NO_MSG(r >= HEADER_SIZE);
|
|
if (r < HEADER_SIZE) {
|
|
return 0;
|
|
}
|
|
|
|
sent_bytes = icmsg_buffer_len_to_user_buffer_len(r);
|
|
|
|
return sent_bytes;
|
|
}
|
|
|
|
#ifdef CONFIG_IPC_SERVICE_ICMSG_ME_NOCOPY_RX
|
|
int icmsg_me_hold_rx_buffer(const struct icmsg_config_t *conf,
|
|
struct icmsg_me_data_t *data, void *buffer)
|
|
{
|
|
void *icmsg_buffer = user_buffer_to_icmsg_buffer(buffer);
|
|
|
|
return icmsg_hold_rx_buffer(conf, &data->icmsg_data, icmsg_buffer);
|
|
}
|
|
|
|
int icmsg_me_release_rx_buffer(const struct icmsg_config_t *conf,
|
|
struct icmsg_me_data_t *data, void *buffer)
|
|
{
|
|
void *icmsg_buffer = user_buffer_to_icmsg_buffer(buffer);
|
|
|
|
return icmsg_release_rx_buffer(conf, &data->icmsg_data, icmsg_buffer);
|
|
}
|
|
#endif /* CONFIG_IPC_SERVICE_ICMSG_ME_NOCOPY_RX */
|