* Copyright (c) 2015-2019, Arm Limited and Contributors. All rights reserved.
* Copyright (c) 2019-2022, Linaro Limited
*/
#include <assert.h>
#include <drivers/scmi-msg.h>
#include <drivers/scmi.h>
#include <io.h>
#include <stdbool.h>
#include <stdint.h>
#include <trace.h>
#include <util.h>
#include "common.h"
* struct smt_header - SMT formatted header for SMT base shared memory transfer
*
* @status: Bit flags, see SMT_STATUS_*
* @flags: Bit flags, see SMT_FLAG_*
* @length: Byte size of message payload (variable) + ::message_header (32bit)
* payload: SCMI message payload data
*/
struct smt_header {
uint32_t reserved0;
uint32_t status;
uint64_t reserved1;
uint32_t flags;
uint32_t length;
uint32_t message_header;
uint32_t payload[];
};
#define SMT_STATUS_FREE BIT(0)
#define SMT_STATUS_ERROR BIT(1)
#define SMT_FLAG_INTR_ENABLED BIT(1)
#define SMT_MSG_ID_MASK GENMASK_32(7, 0)
#define SMT_HDR_MSG_ID(_hdr) ((_hdr) & SMT_MSG_ID_MASK)
#define SMT_MSG_TYPE_MASK GENMASK_32(9, 8)
#define SMT_HDR_TYPE_ID(_hdr) (((_hdr) & SMT_MSG_TYPE_MASK) >> 8)
#define SMT_MSG_PROT_ID_MASK GENMASK_32(17, 10)
#define SMT_HDR_PROT_ID(_hdr) (((_hdr) & SMT_MSG_PROT_ID_MASK) >> 10)
static struct smt_header *channel_to_smt_hdr(struct scmi_msg_channel *channel)
{
if (!channel)
return NULL;
return (struct smt_header *)io_pa_or_va(&channel->shm_addr,
sizeof(struct smt_header));
}
* Creates a SCMI message instance in secure memory and push it in the SCMI
* message drivers. Message structure contains SCMI protocol meta-data and
* references to input payload in secure memory and output message buffer
* in shared memory.
*/
void scmi_entry_smt(unsigned int channel_id, uint32_t *payload_buf)
{
struct scmi_msg_channel *channel = NULL;
struct smt_header *smt_hdr = NULL;
size_t in_payload_size = 0;
uint32_t smt_status = 0;
struct scmi_msg msg = { };
bool error = true;
channel = plat_scmi_get_channel(channel_id);
if (!channel) {
DMSG("Invalid channel ID %u", channel_id);
return;
}
smt_hdr = channel_to_smt_hdr(channel);
if (!smt_hdr) {
DMSG("No shared buffer for channel ID %u", channel_id);
return;
}
if (!scmi_msg_claim_channel(channel)) {
DMSG("SCMI channel %u busy", channel_id);
goto out;
}
smt_status = READ_ONCE(smt_hdr->status);
in_payload_size = READ_ONCE(smt_hdr->length) -
sizeof(smt_hdr->message_header);
if (in_payload_size > SCMI_SEC_PAYLOAD_SIZE) {
DMSG("SCMI payload too big %u", in_payload_size);
goto out;
}
if (smt_status & (SMT_STATUS_ERROR | SMT_STATUS_FREE)) {
DMSG("SCMI channel bad status 0x%x",
smt_hdr->status & (SMT_STATUS_ERROR | SMT_STATUS_FREE));
goto out;
}
msg.in = (char *)payload_buf;
msg.in_size = in_payload_size;
msg.out = (char *)smt_hdr->payload;
msg.out_size = channel->shm_size - sizeof(*smt_hdr);
assert(msg.out && msg.out_size >= sizeof(int32_t));
memcpy(msg.in, smt_hdr->payload, in_payload_size);
msg.protocol_id = SMT_HDR_PROT_ID(smt_hdr->message_header);
msg.message_id = SMT_HDR_MSG_ID(smt_hdr->message_header);
msg.channel_id = channel_id;
scmi_process_message(&msg);
smt_hdr->length = msg.out_size_out + sizeof(smt_hdr->message_header);
scmi_msg_release_channel(channel);
error = false;
out:
if (error) {
DMSG("SCMI error");
smt_hdr->status |= SMT_STATUS_ERROR | SMT_STATUS_FREE;
} else {
smt_hdr->status |= SMT_STATUS_FREE;
}
}
void scmi_smt_init_agent_channel(struct scmi_msg_channel *channel)
{
struct smt_header *smt_header = channel_to_smt_hdr(channel);
static_assert(SCMI_SEC_PAYLOAD_SIZE + sizeof(struct smt_header) <=
SMT_BUF_SLOT_SIZE &&
IS_ALIGNED(SCMI_SEC_PAYLOAD_SIZE, sizeof(uint32_t)));
assert(smt_header);
memset(smt_header, 0, sizeof(*smt_header));
smt_header->status = SMT_STATUS_FREE;
}
void scmi_smt_set_shared_buffer(struct scmi_msg_channel *channel, void *base)
{
paddr_t p_base = 0;
if (base) {
assert(!channel->shm_addr.va && !channel->shm_addr.pa);
p_base = virt_to_phys(base);
assert(p_base);
}
channel->shm_addr.va = (vaddr_t)base;
channel->shm_addr.pa = p_base;
}