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>
This commit is contained in:
Etienne Carriere 2019-11-28 09:13:34 +01:00
parent 7afa5c9629
commit 75366ccd9b
6 changed files with 544 additions and 0 deletions

198
drivers/st/scmi-msg/base.c Normal file
View File

@ -0,0 +1,198 @@
// 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];
}

View File

@ -0,0 +1,75 @@
/* SPDX-License-Identifier: BSD-3-Clause */
/*
* Copyright (c) 2015-2019, Arm Limited and Contributors. All rights reserved.
* Copyright (c) 2019-2020, Linaro Limited
*/
#ifndef SCMI_MSG_BASE_H
#define SCMI_MSG_BASE_H
#include <stdint.h>
#define SCMI_PROTOCOL_VERSION_BASE 0x20000U
#define SCMI_DEFAULT_STRING_LENGTH 16U
enum scmi_base_message_id {
SCMI_BASE_DISCOVER_VENDOR = 0x003,
SCMI_BASE_DISCOVER_SUB_VENDOR = 0x004,
SCMI_BASE_DISCOVER_IMPLEMENTATION_VERSION = 0x005,
SCMI_BASE_DISCOVER_LIST_PROTOCOLS = 0x006,
SCMI_BASE_DISCOVER_AGENT = 0x007,
SCMI_BASE_NOTIFY_ERRORS = 0x008,
};
/*
* PROTOCOL_ATTRIBUTES
*/
#define SCMI_BASE_PROTOCOL_ATTRS_NUM_PROTOCOLS_POS 0
#define SCMI_BASE_PROTOCOL_ATTRS_NUM_AGENTS_POS 8
#define SCMI_BASE_PROTOCOL_ATTRS_NUM_PROTOCOLS_MASK 0xFFU
#define SCMI_BASE_PROTOCOL_ATTRS_NUM_AGENTS_MASK 0xFF00U
#define SCMI_BASE_PROTOCOL_ATTRIBUTES(NUM_PROTOCOLS, NUM_AGENTS) \
((((NUM_PROTOCOLS) << SCMI_BASE_PROTOCOL_ATTRS_NUM_PROTOCOLS_POS) & \
SCMI_BASE_PROTOCOL_ATTRS_NUM_PROTOCOLS_MASK) | \
(((NUM_AGENTS) << SCMI_BASE_PROTOCOL_ATTRS_NUM_AGENTS_POS) & \
SCMI_BASE_PROTOCOL_ATTRS_NUM_AGENTS_MASK))
/*
* BASE_DISCOVER_VENDOR
*/
struct scmi_base_discover_vendor_p2a {
int32_t status;
char vendor_identifier[SCMI_DEFAULT_STRING_LENGTH];
};
/*
* BASE_DISCOVER_SUB_VENDOR
*/
struct scmi_base_discover_sub_vendor_p2a {
int32_t status;
char sub_vendor_identifier[SCMI_DEFAULT_STRING_LENGTH];
};
/*
* BASE_DISCOVER_IMPLEMENTATION_VERSION
* No special structure right now, see protocol_version.
*/
/*
* BASE_DISCOVER_LIST_PROTOCOLS
*/
struct scmi_base_discover_list_protocols_a2p {
uint32_t skip;
};
struct scmi_base_discover_list_protocols_p2a {
int32_t status;
uint32_t num_protocols;
uint32_t protocols[];
};
#endif /* SCMI_MSG_BASE_H */

View File

@ -0,0 +1,120 @@
/* SPDX-License-Identifier: BSD-3-Clause */
/*
* Copyright (c) 2015-2019, Arm Limited and Contributors. All rights reserved.
* Copyright (c) 2019-2020, Linaro Limited
*/
#ifndef SCMI_MSG_COMMON_H
#define SCMI_MSG_COMMON_H
#include <assert.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include "base.h"
#define SCMI_VERSION 0x20000U
#define SCMI_IMPL_VERSION 0U
#define SCMI_PLAYLOAD_MAX 92U
/*
* Copy name identifier in target buffer following the SCMI specification
* that state name identifier shall be a null terminated string.
*/
#define COPY_NAME_IDENTIFIER(_dst_array, _name) \
do { \
assert(strlen(_name) < sizeof(_dst_array)); \
strlcpy((_dst_array), (_name), sizeof(_dst_array)); \
} while (0)
/* Common command identifiers shared by all procotols */
enum scmi_common_message_id {
SCMI_PROTOCOL_VERSION = 0x000,
SCMI_PROTOCOL_ATTRIBUTES = 0x001,
SCMI_PROTOCOL_MESSAGE_ATTRIBUTES = 0x002
};
/* Common platform-to-agent (p2a) PROTOCOL_VERSION structure */
struct scmi_protocol_version_p2a {
int32_t status;
uint32_t version;
};
/* Generic platform-to-agent (p2a) PROTOCOL_ATTRIBUTES structure */
struct scmi_protocol_attributes_p2a {
int32_t status;
uint32_t attributes;
};
/* Generic agent-to-platform (a2p) PROTOCOL_MESSAGE_ATTRIBUTES structure */
struct scmi_protocol_message_attributes_a2p {
uint32_t message_id;
};
/* Generic platform-to-agent (p2a) PROTOCOL_MESSAGE_ATTRIBUTES structure */
struct scmi_protocol_message_attributes_p2a {
int32_t status;
uint32_t attributes;
};
/*
* struct scmi_msg - SCMI message context
*
* @agent_id: SCMI agent ID, safely set from secure world
* @protocol_id: SCMI protocol ID for the related message, set by caller agent
* @message_id: SCMI message ID for the related message, set by caller agent
* @in: Address of the incoming message payload copied in secure memory
* @in_size: Byte length of the incoming message payload, set by caller agent
* @out: Address of of the output message payload message in non-secure memory
* @out_size: Byte length of the provisionned output buffer
* @out_size_out: Byte length of the output message payload
*/
struct scmi_msg {
unsigned int agent_id;
unsigned int protocol_id;
unsigned int message_id;
char *in;
size_t in_size;
char *out;
size_t out_size;
size_t out_size_out;
};
/*
* Type scmi_msg_handler_t is used by procotol drivers to safely find
* the handler function for the incoming message ID.
*/
typedef void (*scmi_msg_handler_t)(struct scmi_msg *msg);
/*
* scmi_msg_get_base_handler - Return a handler for a base message
* @msg - message to process
* Return a function handler for the message or NULL
*/
scmi_msg_handler_t scmi_msg_get_base_handler(struct scmi_msg *msg);
/*
* Process Read, process and write response for input SCMI message
*
* @msg: SCMI message context
*/
void scmi_process_message(struct scmi_msg *msg);
/*
* Write SCMI response payload to output message shared memory
*
* @msg: SCMI message context
* @payload: Output message payload
* @size: Byte size of output message payload
*/
void scmi_write_response(struct scmi_msg *msg, void *payload, size_t size);
/*
* Write status only SCMI response payload to output message shared memory
*
* @msg: SCMI message context
* @status: SCMI status value returned to caller
*/
void scmi_status_response(struct scmi_msg *msg, int32_t status);
#endif /* SCMI_MSG_COMMON_H */

View File

@ -0,0 +1,57 @@
// SPDX-License-Identifier: BSD-3-Clause
/*
* Copyright (c) 2015-2020, Arm Limited and Contributors. All rights reserved.
* Copyright (c) 2019-2020, Linaro Limited
*/
#include <assert.h>
#include <drivers/st/scmi-msg.h>
#include <drivers/st/scmi.h>
#include "common.h"
void scmi_status_response(struct scmi_msg *msg, int32_t status)
{
assert(msg->out && msg->out_size >= sizeof(int32_t));
memcpy(msg->out, &status, sizeof(int32_t));
msg->out_size_out = sizeof(int32_t);
}
void scmi_write_response(struct scmi_msg *msg, void *payload, size_t size)
{
/*
* Output payload shall be at least the size of the status
* Output buffer shall be at least be the size of the status
* Output paylaod shall fit in output buffer
*/
assert(payload && size >= sizeof(int32_t) && size <= msg->out_size &&
msg->out && msg->out_size >= sizeof(int32_t));
memcpy(msg->out, payload, size);
msg->out_size_out = size;
}
void scmi_process_message(struct scmi_msg *msg)
{
scmi_msg_handler_t handler = NULL;
switch (msg->protocol_id) {
case SCMI_PROTOCOL_ID_BASE:
handler = scmi_msg_get_base_handler(msg);
break;
default:
break;
}
if (handler) {
handler(msg);
return;
}
ERROR("Agent %u Protocol 0x%x Message 0x%x: not supported",
msg->agent_id, msg->protocol_id, msg->message_id);
scmi_status_response(msg, SCMI_NOT_SUPPORTED);
}

View File

@ -0,0 +1,65 @@
/* SPDX-License-Identifier: BSD-3-Clause */
/*
* Copyright (c) 2015-2019, Arm Limited and Contributors. All rights reserved.
* Copyright (c) 2019, Linaro Limited
*/
#ifndef SCMI_MSG_H
#define SCMI_MSG_H
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
/* Minimum size expected for SMT based shared memory message buffers */
#define SMT_BUF_SLOT_SIZE 128U
/* A channel abstract a communication path between agent and server */
struct scmi_msg_channel;
/*
* struct scmi_msg_channel - Shared memory buffer for a agent-to-server channel
*
* @shm_addr: Address of the shared memory for the SCMI channel
* @shm_size: Byte size of the shared memory for the SCMI channel
* @busy: True when channel is busy, flase when channel is free
* @agent_name: Agent name, SCMI protocol exposes 16 bytes max, or NULL
*/
struct scmi_msg_channel {
uintptr_t shm_addr;
size_t shm_size;
bool busy;
const char *agent_name;
};
/* Platform callback functions */
/*
* Return the SCMI channel related to an agent
* @agent_id: SCMI agent ID
* Return a pointer to channel on success, NULL otherwise
*/
struct scmi_msg_channel *plat_scmi_get_channel(unsigned int agent_id);
/*
* Return how many SCMI protocols supported by the platform
* According to the SCMI specification, this function does not target
* a specific agent ID and shall return all platform known capabilities.
*/
size_t plat_scmi_protocol_count(void);
/*
* Get the count and list of SCMI protocols (but base) supported for an agent
*
* @agent_id: SCMI agent ID
* Return a pointer to a null terminated array supported protocol IDs.
*/
const uint8_t *plat_scmi_protocol_list(unsigned int agent_id);
/* Get the name of the SCMI vendor for the platform */
const char *plat_scmi_vendor_name(void);
/* Get the name of the SCMI sub-vendor for the platform */
const char *plat_scmi_sub_vendor_name(void);
#endif /* SCMI_MSG_H */

29
include/drivers/st/scmi.h Normal file
View File

@ -0,0 +1,29 @@
/* SPDX-License-Identifier: BSD-3-Clause */
/*
* Copyright (c) 2015-2019, Arm Limited and Contributors. All rights reserved.
*/
#ifndef SCMI_MSG_SCMI_H
#define SCMI_MSG_SCMI_H
#define SCMI_PROTOCOL_ID_BASE 0x10U
#define SCMI_PROTOCOL_ID_POWER_DOMAIN 0x11U
#define SCMI_PROTOCOL_ID_SYS_POWER 0x12U
#define SCMI_PROTOCOL_ID_PERF 0x13U
#define SCMI_PROTOCOL_ID_CLOCK 0x14U
#define SCMI_PROTOCOL_ID_SENSOR 0x15U
#define SCMI_PROTOCOL_ID_RESET_DOMAIN 0x16U
/* SCMI error codes reported to agent through server-to-agent messages */
#define SCMI_SUCCESS 0
#define SCMI_NOT_SUPPORTED (-1)
#define SCMI_INVALID_PARAMETERS (-2)
#define SCMI_DENIED (-3)
#define SCMI_NOT_FOUND (-4)
#define SCMI_OUT_OF_RANGE (-5)
#define SCMI_BUSY (-6)
#define SCMI_COMMS_ERROR (-7)
#define SCMI_GENERIC_ERROR (-8)
#define SCMI_HARDWARE_ERROR (-9)
#define SCMI_PROTOCOL_ERROR (-10)
#endif /* SCMI_MSG_SCMI_H */