Primary Git Repository for the Zephyr Project. Zephyr is a new generation, scalable, optimized, secure RTOS for multiple hardware architectures.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

387 lines
11 KiB

/*
* Copyright (c) 2025 Texas Instruments Incorporated.
*
* TI Secureproxy Mailbox driver for Zephyr's MBOX model.
*/
#include <zephyr/spinlock.h>
#include <zephyr/kernel.h>
#include <stdio.h>
#include <zephyr/devicetree.h>
#include <zephyr/drivers/mbox.h>
#include <zephyr/irq.h>
#define LOG_LEVEL CONFIG_MBOX_LOG_LEVEL
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(ti_secure_proxy);
#define DT_DRV_COMPAT ti_secure_proxy
#define DEV_CFG(dev) ((const struct secproxy_mailbox_config *)((dev)->config))
#define DEV_DATA(dev) ((struct secproxy_mailbox_data *)(dev)->data)
#define DEV_TDATA(_dev) DEVICE_MMIO_NAMED_GET(_dev, target_data)
#define DEV_RT(_dev) DEVICE_MMIO_NAMED_GET(_dev, rt)
#define DEV_SCFG(_dev) DEVICE_MMIO_NAMED_GET(_dev, scfg)
#define RT_THREAD_STATUS 0x0
#define RT_THREAD_THRESHOLD 0x4
#define RT_THREAD_STATUS_ERROR_SHIFT 31
#define RT_THREAD_STATUS_ERROR_MASK BIT(31)
#define RT_THREAD_STATUS_CUR_CNT_SHIFT 0
#define RT_THREAD_STATUS_CUR_CNT_MASK GENMASK(7, 0)
#define SCFG_THREAD_CTRL 0x1000
#define SCFG_THREAD_CTRL_DIR_SHIFT 31
#define SCFG_THREAD_CTRL_DIR_MASK BIT(31)
#define SEC_PROXY_THREAD(base, x) ((base) + (0x1000 * (x)))
#define THREAD_IS_RX 1
#define THREAD_IS_TX 0
#define SECPROXY_MAILBOX_NUM_MSGS 5
#define MAILBOX_MAX_CHANNELS 32
#define MAILBOX_MBOX_SIZE 60
#define SEC_PROXY_DATA_START_OFFS 0x4
#define SEC_PROXY_DATA_END_OFFS 0x3c
#define SEC_PROXY_TIMEOUT_US 1000000
#define GET_MSG_SEQ(buffer) ((uint32_t *)buffer)[1]
struct secproxy_thread {
mem_addr_t target_data;
mem_addr_t rt;
mem_addr_t scfg;
};
/**
* @struct rx_msg
* @brief Received message details
* @param seq: Message sequence number
* @param size: Message size in bytes
* @param buf: Buffer for message data
*/
struct rx_msg {
uint8_t seq;
size_t size;
void *buf;
};
struct secproxy_mailbox_data {
mbox_callback_t cb[MAILBOX_MAX_CHANNELS];
void *user_data[MAILBOX_MAX_CHANNELS];
bool channel_enable[MAILBOX_MAX_CHANNELS];
DEVICE_MMIO_NAMED_RAM(target_data);
DEVICE_MMIO_NAMED_RAM(rt);
DEVICE_MMIO_NAMED_RAM(scfg);
struct k_spinlock lock;
};
struct secproxy_mailbox_config {
DEVICE_MMIO_NAMED_ROM(target_data);
DEVICE_MMIO_NAMED_ROM(rt);
DEVICE_MMIO_NAMED_ROM(scfg);
uint32_t irq;
};
static inline int secproxy_verify_thread(struct secproxy_thread *spt, uint8_t dir)
{
/* Check for any errors already available */
if (sys_read32(spt->rt + RT_THREAD_STATUS) & RT_THREAD_STATUS_ERROR_MASK) {
LOG_ERR("Thread is corrupted, cannot send data.\n");
return -EINVAL;
}
/* Make sure thread is configured for right direction */
if ((sys_read32(spt->scfg + SCFG_THREAD_CTRL) & SCFG_THREAD_CTRL_DIR_MASK) !=
(dir << SCFG_THREAD_CTRL_DIR_SHIFT)) {
if (dir == THREAD_IS_TX) {
LOG_ERR("Trying to send data on RX Thread\n");
} else {
LOG_ERR("Trying to receive data on TX Thread\n");
}
return -EINVAL;
}
/* Check the message queue before sending/receiving data */
int timeout_ms = SEC_PROXY_TIMEOUT_US;
int waited_ms = 0;
const int poll_interval_ms = 1000;
while (!(sys_read32(spt->rt + RT_THREAD_STATUS) & RT_THREAD_STATUS_CUR_CNT_MASK)) {
k_busy_wait(poll_interval_ms);
waited_ms += poll_interval_ms;
if (waited_ms >= timeout_ms) {
LOG_ERR("Timeout waiting for thread to %s\n",
(dir == THREAD_IS_TX) ? "empty" : "fill");
return -ETIMEDOUT;
}
}
return 0;
}
static void secproxy_mailbox_isr(const struct device *dev)
{
struct secproxy_mailbox_data *data = DEV_DATA(dev);
struct secproxy_thread spt;
uint32_t data_word;
mem_addr_t data_reg;
for (int i_channel = 0; i_channel < MAILBOX_MAX_CHANNELS; i_channel++) {
if (!data->channel_enable[i_channel]) {
continue;
}
spt.target_data = SEC_PROXY_THREAD(DEV_TDATA(dev), i_channel);
spt.rt = SEC_PROXY_THREAD(DEV_RT(dev), i_channel);
spt.scfg = SEC_PROXY_THREAD(DEV_SCFG(dev), i_channel);
uint32_t status = sys_read32(spt.rt + RT_THREAD_STATUS);
if (status & RT_THREAD_STATUS_ERROR_MASK) {
LOG_ERR("Thread %d error state detected in ISR", i_channel);
continue;
}
if (secproxy_verify_thread(&spt, THREAD_IS_RX)) {
LOG_ERR("Thread %d is in error state\n", i_channel);
continue;
}
if (!(sys_read32(spt.rt) & 0x7F)) {
continue;
}
data_reg = spt.target_data + SEC_PROXY_DATA_START_OFFS;
size_t msg_len = MAILBOX_MBOX_SIZE;
size_t num_words = msg_len / sizeof(uint32_t);
size_t i;
struct rx_msg *rx_data = data->user_data[i_channel];
if (!rx_data || !rx_data->buf) {
LOG_ERR("No buffer provided for channel %d", i_channel);
continue;
}
if (rx_data->size < MAILBOX_MBOX_SIZE) {
LOG_ERR("Buffer too small for channel %d", i_channel);
continue;
}
uint8_t *buf = (uint8_t *)rx_data->buf;
/* Copy full words */
for (i = 0; i < num_words; i++) {
data_word = sys_read32(data_reg);
memcpy(&buf[i * 4], &data_word, sizeof(uint32_t));
data_reg += sizeof(uint32_t);
}
/* Handle trail bytes */
size_t trail_bytes = msg_len % sizeof(uint32_t);
if (trail_bytes) {
uint32_t data_trail = sys_read32(data_reg);
i = msg_len - trail_bytes;
while (trail_bytes--) {
buf[i++] = data_trail & 0xff;
data_trail >>= 8;
}
}
/* Ensure we read the last register if we haven't already */
if (data_reg <= (spt.target_data + SEC_PROXY_DATA_END_OFFS)) {
sys_read32(spt.target_data + SEC_PROXY_DATA_END_OFFS);
}
rx_data->size = msg_len;
rx_data->seq = GET_MSG_SEQ(buf);
if (data->cb[i_channel]) {
data->cb[i_channel](dev, i_channel, data->user_data[i_channel], NULL);
}
}
}
static int secproxy_mailbox_send(const struct device *dev, uint32_t channel,
const struct mbox_msg *msg)
{
struct secproxy_thread spt;
struct secproxy_mailbox_data *data = DEV_DATA(dev);
mem_addr_t data_reg;
k_spinlock_key_t key;
if (!dev || !msg || !msg->data) {
LOG_ERR("Invalid parameters");
return -EINVAL;
}
if (msg->size == 0) {
LOG_ERR("Empty message not allowed");
return -EINVAL;
}
if (channel >= MAILBOX_MAX_CHANNELS) {
LOG_ERR("Channel %d exceeds max channels", channel);
return -EINVAL;
}
key = k_spin_lock(&data->lock);
spt.target_data = SEC_PROXY_THREAD(DEV_TDATA(dev), channel);
spt.rt = SEC_PROXY_THREAD(DEV_RT(dev), channel);
spt.scfg = SEC_PROXY_THREAD(DEV_SCFG(dev), channel);
if (secproxy_verify_thread(&spt, THREAD_IS_TX)) {
LOG_ERR("Thread is in error state\n");
k_spin_unlock(&data->lock, key);
return -EBUSY;
}
uint32_t status = sys_read32(spt.rt + RT_THREAD_STATUS);
if ((status & RT_THREAD_STATUS_CUR_CNT_MASK) == (SECPROXY_MAILBOX_NUM_MSGS)) {
k_spin_unlock(&data->lock, key);
return -EBUSY;
}
if (msg->size > MAILBOX_MBOX_SIZE) {
LOG_ERR("Message size %zu exceeds max size %d", msg->size, MAILBOX_MBOX_SIZE);
k_spin_unlock(&data->lock, key);
return -EMSGSIZE;
}
/* Start writing from the data start offset */
data_reg = spt.target_data + SEC_PROXY_DATA_START_OFFS;
/* Handle full words */
const uint8_t *byte_data = msg->data;
for (int num_words = msg->size / sizeof(uint32_t); num_words > 0; num_words--) {
uint32_t word_val;
memcpy(&word_val, byte_data, sizeof(uint32_t));
sys_write32(word_val, data_reg);
data_reg += sizeof(uint32_t);
byte_data += sizeof(uint32_t);
}
/* Handle trailing bytes */
int trail_bytes = msg->size % sizeof(uint32_t);
if (trail_bytes) {
uint32_t data_trail = 0;
for (int i = 0; i < trail_bytes; i++) {
data_trail <<= 8;
data_trail |= byte_data[i];
}
sys_write32(data_trail, data_reg);
data_reg += sizeof(uint32_t);
}
/* Only write end marker if needed */
if (data_reg <= (spt.target_data + SEC_PROXY_DATA_END_OFFS)) {
sys_write32(0, spt.target_data + SEC_PROXY_DATA_END_OFFS);
}
k_spin_unlock(&data->lock, key);
return 0;
}
static int secproxy_mailbox_register_callback(const struct device *dev, uint32_t channel,
mbox_callback_t cb, void *user_data)
{
struct secproxy_mailbox_data *data = DEV_DATA(dev);
k_spinlock_key_t key;
if (channel >= MAILBOX_MAX_CHANNELS) {
return -EINVAL;
}
key = k_spin_lock(&data->lock);
data->cb[channel] = cb;
data->user_data[channel] = user_data;
k_spin_unlock(&data->lock, key);
return 0;
}
static int secproxy_mailbox_mtu_get(const struct device *dev)
{
ARG_UNUSED(dev);
return MAILBOX_MBOX_SIZE;
}
static uint32_t secproxy_mailbox_max_channels_get(const struct device *dev)
{
ARG_UNUSED(dev);
return MAILBOX_MAX_CHANNELS;
}
static int secproxy_mailbox_set_enabled(const struct device *dev, uint32_t channel, bool enable)
{
const struct secproxy_mailbox_config *cfg = DEV_CFG(dev);
struct secproxy_mailbox_data *data = DEV_DATA(dev);
k_spinlock_key_t key;
if (channel >= MAILBOX_MAX_CHANNELS) {
return -EINVAL;
}
if (enable && data->channel_enable[channel]) {
return -EALREADY;
}
key = k_spin_lock(&data->lock);
data->channel_enable[channel] = enable;
if (enable) {
irq_enable(cfg->irq);
} else {
irq_disable(cfg->irq);
}
k_spin_unlock(&data->lock, key);
return 0;
}
static DEVICE_API(mbox, secproxy_mailbox_driver_api) = {
.send = secproxy_mailbox_send,
.register_callback = secproxy_mailbox_register_callback,
.mtu_get = secproxy_mailbox_mtu_get,
.max_channels_get = secproxy_mailbox_max_channels_get,
.set_enabled = secproxy_mailbox_set_enabled,
};
#define MAILBOX_INSTANCE_DEFINE(idx) \
static struct secproxy_mailbox_data secproxy_mailbox_##idx##_data; \
const static struct secproxy_mailbox_config secproxy_mailbox_##idx##_config = { \
DEVICE_MMIO_NAMED_ROM_INIT_BY_NAME(target_data, DT_DRV_INST(idx)), \
DEVICE_MMIO_NAMED_ROM_INIT_BY_NAME(rt, DT_DRV_INST(idx)), \
DEVICE_MMIO_NAMED_ROM_INIT_BY_NAME(scfg, DT_DRV_INST(idx)), \
.irq = DT_INST_IRQN(idx), \
}; \
static int secproxy_mailbox_##idx##_init(const struct device *dev) \
{ \
DEVICE_MMIO_NAMED_MAP(dev, target_data, K_MEM_CACHE_NONE); \
DEVICE_MMIO_NAMED_MAP(dev, rt, K_MEM_CACHE_NONE); \
DEVICE_MMIO_NAMED_MAP(dev, scfg, K_MEM_CACHE_NONE); \
IRQ_CONNECT(DT_INST_IRQN(idx), DT_INST_IRQ(idx, priority), secproxy_mailbox_isr, \
DEVICE_DT_INST_GET(idx), \
COND_CODE_1(DT_INST_IRQ_HAS_CELL(idx, flags), \
(DT_INST_IRQ(idx, flags)), (0))); \
return 0; \
} \
DEVICE_DT_INST_DEFINE(idx, secproxy_mailbox_##idx##_init, NULL, \
&secproxy_mailbox_##idx##_data, &secproxy_mailbox_##idx##_config, \
PRE_KERNEL_1, CONFIG_MBOX_TI_SECURE_PROXY_PRIORITY, \
&secproxy_mailbox_driver_api)
#define MAILBOX_INST(idx) MAILBOX_INSTANCE_DEFINE(idx);
DT_INST_FOREACH_STATUS_OKAY(MAILBOX_INST)