arm-trusted-firmware/drivers/st/scmi-msg/base.c
Etienne Carriere 75366ccd9b drivers/scmi-msg: driver for processing scmi messages
This change introduces drivers to allow a platform to create a basic
SCMI service and register handlers for client request (SCMI agent) on
system resources. This is the first piece of the drivers: an entry
function, the SCMI base protocol support and helpers for create
the response message.

With this change, scmi_process_message() is the entry function to
process an incoming SCMI message. The function expect the message
is already copied from shared memory into secure memory. The message
structure stores message reference and output buffer reference where
response message shall be stored.

scmi_process_message() calls the SCMI protocol driver according to
the protocol ID in the message. The SCMI protocol driver will call
defined platform handlers according to the message content.

This change introduces only the SCMI base protocol as defined in
SCMI specification v2.0 [1]. Not all the messages defined
in the specification are supported.

The SCMI message implementation is derived from the OP-TEE project [2]
itself based on the SCP-firmware implementation [3] of the SCMI protocol
server side.

Link: [1] http://infocenter.arm.com/help/topic/com.arm.doc.den0056a/DEN0056A_System_Control_and_Management_Interface.pdf
Link: [2] ae8c806809
Link: [3] https://github.com/ARM-software/SCP-firmware/tree/v2.6.0

Change-Id: I639c4154a39fca60606264baf8d32452641f45e9
Signed-off-by: Etienne Carriere <etienne.carriere@linaro.org>
2020-06-17 11:05:04 +02:00

198 lines
5 KiB
C

// SPDX-License-Identifier: BSD-3-Clause
/*
* Copyright (c) 2015-2019, Arm Limited and Contributors. All rights reserved.
* Copyright (c) 2019-2020, Linaro Limited
*/
#include <assert.h>
#include <string.h>
#include <drivers/st/scmi-msg.h>
#include <drivers/st/scmi.h>
#include <lib/utils.h>
#include <lib/utils_def.h>
#include "common.h"
static bool message_id_is_supported(unsigned int message_id);
static void report_version(struct scmi_msg *msg)
{
struct scmi_protocol_version_p2a return_values = {
.status = SCMI_SUCCESS,
.version = SCMI_PROTOCOL_VERSION_BASE,
};
if (msg->in_size != 0U) {
scmi_status_response(msg, SCMI_PROTOCOL_ERROR);
return;
}
scmi_write_response(msg, &return_values, sizeof(return_values));
}
static void report_attributes(struct scmi_msg *msg)
{
size_t protocol_count = plat_scmi_protocol_count();
struct scmi_protocol_attributes_p2a return_values = {
.status = SCMI_SUCCESS,
/* Null agent count since agent discovery is not supported */
.attributes = SCMI_BASE_PROTOCOL_ATTRIBUTES(protocol_count, 0U),
};
if (msg->in_size != 0U) {
scmi_status_response(msg, SCMI_PROTOCOL_ERROR);
return;
}
scmi_write_response(msg, &return_values, sizeof(return_values));
}
static void report_message_attributes(struct scmi_msg *msg)
{
struct scmi_protocol_message_attributes_a2p *in_args = (void *)msg->in;
struct scmi_protocol_message_attributes_p2a return_values = {
.status = SCMI_SUCCESS,
/* For this protocol, attributes shall be zero */
.attributes = 0U,
};
if (msg->in_size != sizeof(*in_args)) {
scmi_status_response(msg, SCMI_PROTOCOL_ERROR);
return;
}
if (!message_id_is_supported(in_args->message_id)) {
scmi_status_response(msg, SCMI_NOT_FOUND);
return;
}
scmi_write_response(msg, &return_values, sizeof(return_values));
}
static void discover_vendor(struct scmi_msg *msg)
{
const char *name = plat_scmi_vendor_name();
struct scmi_base_discover_vendor_p2a return_values = {
.status = SCMI_SUCCESS,
};
if (msg->in_size != 0U) {
scmi_status_response(msg, SCMI_PROTOCOL_ERROR);
return;
}
COPY_NAME_IDENTIFIER(return_values.vendor_identifier, name);
scmi_write_response(msg, &return_values, sizeof(return_values));
}
static void discover_sub_vendor(struct scmi_msg *msg)
{
const char *name = plat_scmi_sub_vendor_name();
struct scmi_base_discover_sub_vendor_p2a return_values = {
.status = SCMI_SUCCESS,
};
if (msg->in_size != 0U) {
scmi_status_response(msg, SCMI_PROTOCOL_ERROR);
return;
}
COPY_NAME_IDENTIFIER(return_values.sub_vendor_identifier, name);
scmi_write_response(msg, &return_values, sizeof(return_values));
}
static void discover_implementation_version(struct scmi_msg *msg)
{
struct scmi_protocol_version_p2a return_values = {
.status = SCMI_SUCCESS,
.version = SCMI_IMPL_VERSION,
};
if (msg->in_size != 0U) {
scmi_status_response(msg, SCMI_PROTOCOL_ERROR);
return;
}
scmi_write_response(msg, &return_values, sizeof(return_values));
}
static unsigned int count_protocols_in_list(const uint8_t *protocol_list)
{
unsigned int count = 0U;
if (protocol_list != NULL) {
while (protocol_list[count] != 0U) {
count++;
}
}
return count;
}
#define MAX_PROTOCOL_IN_LIST 8U
static void discover_list_protocols(struct scmi_msg *msg)
{
const struct scmi_base_discover_list_protocols_a2p *a2p = NULL;
struct scmi_base_discover_list_protocols_p2a p2a = {
.status = SCMI_SUCCESS,
};
uint8_t outargs[sizeof(p2a) + MAX_PROTOCOL_IN_LIST] = { 0U };
const uint8_t *list = NULL;
unsigned int count = 0U;
if (msg->in_size != sizeof(*a2p)) {
scmi_status_response(msg, SCMI_PROTOCOL_ERROR);
return;
}
assert(msg->out_size > sizeof(outargs));
a2p = (void *)msg->in;
list = plat_scmi_protocol_list(msg->agent_id);
count = count_protocols_in_list(list);
if (count > a2p->skip) {
count = MIN(count - a2p->skip, MAX_PROTOCOL_IN_LIST);
} else {
count = 0U;
}
p2a.num_protocols = count;
memcpy(outargs, &p2a, sizeof(p2a));
memcpy(outargs + sizeof(p2a), list + a2p->skip, count);
scmi_write_response(msg, outargs, sizeof(outargs));
}
static const scmi_msg_handler_t scmi_base_handler_table[] = {
[SCMI_PROTOCOL_VERSION] = report_version,
[SCMI_PROTOCOL_ATTRIBUTES] = report_attributes,
[SCMI_PROTOCOL_MESSAGE_ATTRIBUTES] = report_message_attributes,
[SCMI_BASE_DISCOVER_VENDOR] = discover_vendor,
[SCMI_BASE_DISCOVER_SUB_VENDOR] = discover_sub_vendor,
[SCMI_BASE_DISCOVER_IMPLEMENTATION_VERSION] =
discover_implementation_version,
[SCMI_BASE_DISCOVER_LIST_PROTOCOLS] = discover_list_protocols,
};
static bool message_id_is_supported(unsigned int message_id)
{
return (message_id < ARRAY_SIZE(scmi_base_handler_table)) &&
(scmi_base_handler_table[message_id] != NULL);
}
scmi_msg_handler_t scmi_msg_get_base_handler(struct scmi_msg *msg)
{
unsigned int message_id = SPECULATION_SAFE_VALUE(msg->message_id);
if (message_id >= ARRAY_SIZE(scmi_base_handler_table)) {
VERBOSE("Base handle not found %u\n", msg->message_id);
return NULL;
}
return scmi_base_handler_table[message_id];
}