xref: /optee_os/core/drivers/scmi-msg/base.c (revision 737ed932a597b161e3ce51003da9ae88d633febf)
1ae8c8068SEtienne Carriere // SPDX-License-Identifier: BSD-3-Clause
2ae8c8068SEtienne Carriere /*
3ae8c8068SEtienne Carriere  * Copyright (c) 2015-2019, Arm Limited and Contributors. All rights reserved.
4ae8c8068SEtienne Carriere  * Copyright (c) 2019, Linaro Limited
5ae8c8068SEtienne Carriere  */
6ae8c8068SEtienne Carriere #include <assert.h>
7ae8c8068SEtienne Carriere #include <confine_array_index.h>
8ae8c8068SEtienne Carriere #include <drivers/scmi-msg.h>
9ae8c8068SEtienne Carriere #include <drivers/scmi.h>
10ae8c8068SEtienne Carriere #include <string.h>
11ae8c8068SEtienne Carriere #include <trace.h>
12ae8c8068SEtienne Carriere #include <util.h>
13ae8c8068SEtienne Carriere 
1460c96f68SEtienne Carriere #include "base.h"
15ae8c8068SEtienne Carriere #include "common.h"
16ae8c8068SEtienne Carriere 
17ae8c8068SEtienne Carriere static bool message_id_is_supported(unsigned int message_id);
18ae8c8068SEtienne Carriere 
report_version(struct scmi_msg * msg)19ae8c8068SEtienne Carriere static void report_version(struct scmi_msg *msg)
20ae8c8068SEtienne Carriere {
21ae8c8068SEtienne Carriere 	struct scmi_protocol_version_p2a return_values = {
22ae8c8068SEtienne Carriere 		.status = SCMI_SUCCESS,
23ae8c8068SEtienne Carriere 		.version = SCMI_PROTOCOL_VERSION_BASE,
24ae8c8068SEtienne Carriere 	};
25ae8c8068SEtienne Carriere 
26ae8c8068SEtienne Carriere 	if (msg->in_size) {
27ae8c8068SEtienne Carriere 		scmi_status_response(msg, SCMI_PROTOCOL_ERROR);
28ae8c8068SEtienne Carriere 		return;
29ae8c8068SEtienne Carriere 	}
30ae8c8068SEtienne Carriere 
31ae8c8068SEtienne Carriere 	scmi_write_response(msg, &return_values, sizeof(return_values));
32ae8c8068SEtienne Carriere }
33ae8c8068SEtienne Carriere 
report_attributes(struct scmi_msg * msg)34ae8c8068SEtienne Carriere static void report_attributes(struct scmi_msg *msg)
35ae8c8068SEtienne Carriere {
36ae8c8068SEtienne Carriere 	size_t protocol_count = plat_scmi_protocol_count();
37ae8c8068SEtienne Carriere 	struct scmi_protocol_attributes_p2a return_values = {
38ae8c8068SEtienne Carriere 		.status = SCMI_SUCCESS,
39ae8c8068SEtienne Carriere 		/* Null agent count since agent discovery is not supported */
40ae8c8068SEtienne Carriere 		.attributes = SCMI_BASE_PROTOCOL_ATTRIBUTES(protocol_count, 0),
41ae8c8068SEtienne Carriere 	};
42ae8c8068SEtienne Carriere 
43ae8c8068SEtienne Carriere 	if (msg->in_size) {
44ae8c8068SEtienne Carriere 		scmi_status_response(msg, SCMI_PROTOCOL_ERROR);
45ae8c8068SEtienne Carriere 		return;
46ae8c8068SEtienne Carriere 	}
47ae8c8068SEtienne Carriere 
48ae8c8068SEtienne Carriere 	scmi_write_response(msg, &return_values, sizeof(return_values));
49ae8c8068SEtienne Carriere }
50ae8c8068SEtienne Carriere 
report_message_attributes(struct scmi_msg * msg)51ae8c8068SEtienne Carriere static void report_message_attributes(struct scmi_msg *msg)
52ae8c8068SEtienne Carriere {
53ae8c8068SEtienne Carriere 	struct scmi_protocol_message_attributes_a2p *in_args = (void *)msg->in;
54ae8c8068SEtienne Carriere 	struct scmi_protocol_message_attributes_p2a return_values = {
55ae8c8068SEtienne Carriere 		.status = SCMI_SUCCESS,
56ae8c8068SEtienne Carriere 		/* For this protocol, attributes shall be zero */
57ae8c8068SEtienne Carriere 		.attributes = 0,
58ae8c8068SEtienne Carriere 	};
59ae8c8068SEtienne Carriere 
60ae8c8068SEtienne Carriere 	if (msg->in_size != sizeof(*in_args)) {
61ae8c8068SEtienne Carriere 		scmi_status_response(msg, SCMI_PROTOCOL_ERROR);
62ae8c8068SEtienne Carriere 		return;
63ae8c8068SEtienne Carriere 	}
64ae8c8068SEtienne Carriere 
65ae8c8068SEtienne Carriere 	if (!message_id_is_supported(in_args->message_id)) {
66ae8c8068SEtienne Carriere 		scmi_status_response(msg, SCMI_NOT_FOUND);
67ae8c8068SEtienne Carriere 		return;
68ae8c8068SEtienne Carriere 	}
69ae8c8068SEtienne Carriere 
70ae8c8068SEtienne Carriere 	scmi_write_response(msg, &return_values, sizeof(return_values));
71ae8c8068SEtienne Carriere }
72ae8c8068SEtienne Carriere 
discover_vendor(struct scmi_msg * msg)73ae8c8068SEtienne Carriere static void discover_vendor(struct scmi_msg *msg)
74ae8c8068SEtienne Carriere {
75ae8c8068SEtienne Carriere 	const char *name = plat_scmi_vendor_name();
76ae8c8068SEtienne Carriere 	struct scmi_base_discover_vendor_p2a return_values = {
77ae8c8068SEtienne Carriere 		.status = SCMI_SUCCESS,
78ae8c8068SEtienne Carriere 	};
79ae8c8068SEtienne Carriere 
80ae8c8068SEtienne Carriere 	if (msg->in_size) {
81ae8c8068SEtienne Carriere 		scmi_status_response(msg, SCMI_PROTOCOL_ERROR);
82ae8c8068SEtienne Carriere 		return;
83ae8c8068SEtienne Carriere 	}
84ae8c8068SEtienne Carriere 
85ae8c8068SEtienne Carriere 	COPY_NAME_IDENTIFIER(return_values.vendor_identifier, name);
86ae8c8068SEtienne Carriere 
87ae8c8068SEtienne Carriere 	scmi_write_response(msg, &return_values, sizeof(return_values));
88ae8c8068SEtienne Carriere }
89ae8c8068SEtienne Carriere 
discover_sub_vendor(struct scmi_msg * msg)90ae8c8068SEtienne Carriere static void discover_sub_vendor(struct scmi_msg *msg)
91ae8c8068SEtienne Carriere {
92ae8c8068SEtienne Carriere 	const char *name = plat_scmi_sub_vendor_name();
93ae8c8068SEtienne Carriere 	struct scmi_base_discover_sub_vendor_p2a return_values = {
94ae8c8068SEtienne Carriere 		.status = SCMI_SUCCESS,
95ae8c8068SEtienne Carriere 	};
96ae8c8068SEtienne Carriere 
97ae8c8068SEtienne Carriere 	if (msg->in_size) {
98ae8c8068SEtienne Carriere 		scmi_status_response(msg, SCMI_PROTOCOL_ERROR);
99ae8c8068SEtienne Carriere 		return;
100ae8c8068SEtienne Carriere 	}
101ae8c8068SEtienne Carriere 
102ae8c8068SEtienne Carriere 	COPY_NAME_IDENTIFIER(return_values.sub_vendor_identifier, name);
103ae8c8068SEtienne Carriere 
104ae8c8068SEtienne Carriere 	scmi_write_response(msg, &return_values, sizeof(return_values));
105ae8c8068SEtienne Carriere }
106ae8c8068SEtienne Carriere 
discover_implementation_version(struct scmi_msg * msg)107ae8c8068SEtienne Carriere static void discover_implementation_version(struct scmi_msg *msg)
108ae8c8068SEtienne Carriere {
109ae8c8068SEtienne Carriere 	struct scmi_protocol_version_p2a return_values = {
110ae8c8068SEtienne Carriere 		.status = SCMI_SUCCESS,
111ae8c8068SEtienne Carriere 		.version = SCMI_IMPL_VERSION,
112ae8c8068SEtienne Carriere 	};
113ae8c8068SEtienne Carriere 
114ae8c8068SEtienne Carriere 	if (msg->in_size) {
115ae8c8068SEtienne Carriere 		scmi_status_response(msg, SCMI_PROTOCOL_ERROR);
116ae8c8068SEtienne Carriere 		return;
117ae8c8068SEtienne Carriere 	}
118ae8c8068SEtienne Carriere 
119ae8c8068SEtienne Carriere 	scmi_write_response(msg, &return_values, sizeof(return_values));
120ae8c8068SEtienne Carriere }
121ae8c8068SEtienne Carriere 
count_protocols_in_list(const uint8_t * protocol_list)122ae8c8068SEtienne Carriere static unsigned int count_protocols_in_list(const uint8_t *protocol_list)
123ae8c8068SEtienne Carriere {
124ae8c8068SEtienne Carriere 	unsigned int count = 0;
125ae8c8068SEtienne Carriere 
126ae8c8068SEtienne Carriere 	if (protocol_list)
127ae8c8068SEtienne Carriere 		while (protocol_list[count])
128ae8c8068SEtienne Carriere 			count++;
129ae8c8068SEtienne Carriere 
130ae8c8068SEtienne Carriere 	return count;
131ae8c8068SEtienne Carriere }
132ae8c8068SEtienne Carriere 
discover_list_protocols(struct scmi_msg * msg)133ae8c8068SEtienne Carriere static void discover_list_protocols(struct scmi_msg *msg)
134ae8c8068SEtienne Carriere {
135ae8c8068SEtienne Carriere 	const struct scmi_base_discover_list_protocols_a2p *a2p = NULL;
136ae8c8068SEtienne Carriere 	struct scmi_base_discover_list_protocols_p2a p2a = {
137ae8c8068SEtienne Carriere 		.status = SCMI_SUCCESS,
138ae8c8068SEtienne Carriere 	};
139ae8c8068SEtienne Carriere 	const uint8_t *list = NULL;
140ae8c8068SEtienne Carriere 	unsigned int count = 0;
141ae8c8068SEtienne Carriere 
142ae8c8068SEtienne Carriere 	if (msg->in_size != sizeof(*a2p)) {
143ae8c8068SEtienne Carriere 		scmi_status_response(msg, SCMI_PROTOCOL_ERROR);
144ae8c8068SEtienne Carriere 		return;
145ae8c8068SEtienne Carriere 	}
146ae8c8068SEtienne Carriere 
147ae8c8068SEtienne Carriere 	a2p = (void *)msg->in;
148ae8c8068SEtienne Carriere 
149659a1f88SEtienne Carriere 	list = plat_scmi_protocol_list(msg->channel_id);
150ae8c8068SEtienne Carriere 	count = count_protocols_in_list(list);
151ae8c8068SEtienne Carriere 	if (count > a2p->skip)
152*737ed932SEtienne Carriere 		count = MIN(count - a2p->skip, msg->out_size - sizeof(p2a));
153ae8c8068SEtienne Carriere 	else
154ae8c8068SEtienne Carriere 		count = 0;
155ae8c8068SEtienne Carriere 
156ae8c8068SEtienne Carriere 	p2a.num_protocols = count;
157ae8c8068SEtienne Carriere 
158*737ed932SEtienne Carriere 	memcpy(msg->out, &p2a, sizeof(p2a));
159*737ed932SEtienne Carriere 	memcpy(msg->out + sizeof(p2a), list + a2p->skip, count);
160*737ed932SEtienne Carriere 	msg->out_size_out = sizeof(p2a) + ROUNDUP(count, sizeof(uint32_t));
161ae8c8068SEtienne Carriere }
162ae8c8068SEtienne Carriere 
163ae8c8068SEtienne Carriere static const scmi_msg_handler_t scmi_base_handler_table[] = {
164ae8c8068SEtienne Carriere 	[SCMI_PROTOCOL_VERSION] = report_version,
165ae8c8068SEtienne Carriere 	[SCMI_PROTOCOL_ATTRIBUTES] = report_attributes,
166ae8c8068SEtienne Carriere 	[SCMI_PROTOCOL_MESSAGE_ATTRIBUTES] = report_message_attributes,
167ae8c8068SEtienne Carriere 	[SCMI_BASE_DISCOVER_VENDOR] = discover_vendor,
168ae8c8068SEtienne Carriere 	[SCMI_BASE_DISCOVER_SUB_VENDOR] = discover_sub_vendor,
169ae8c8068SEtienne Carriere 	[SCMI_BASE_DISCOVER_IMPLEMENTATION_VERSION] =
170ae8c8068SEtienne Carriere 					discover_implementation_version,
171ae8c8068SEtienne Carriere 	[SCMI_BASE_DISCOVER_LIST_PROTOCOLS] = discover_list_protocols,
172ae8c8068SEtienne Carriere };
173ae8c8068SEtienne Carriere 
message_id_is_supported(unsigned int message_id)174ae8c8068SEtienne Carriere static bool message_id_is_supported(unsigned int message_id)
175ae8c8068SEtienne Carriere {
176ae8c8068SEtienne Carriere 	return message_id < ARRAY_SIZE(scmi_base_handler_table) &&
177ae8c8068SEtienne Carriere 	       scmi_base_handler_table[message_id];
178ae8c8068SEtienne Carriere }
179ae8c8068SEtienne Carriere 
scmi_msg_get_base_handler(struct scmi_msg * msg)180ae8c8068SEtienne Carriere scmi_msg_handler_t scmi_msg_get_base_handler(struct scmi_msg *msg)
181ae8c8068SEtienne Carriere {
182ae8c8068SEtienne Carriere 	const size_t array_size = ARRAY_SIZE(scmi_base_handler_table);
183ae8c8068SEtienne Carriere 	unsigned int message_id = 0;
184ae8c8068SEtienne Carriere 
185ae8c8068SEtienne Carriere 	if (msg->message_id >= array_size) {
186ae8c8068SEtienne Carriere 		DMSG("Base handle not found %u", msg->message_id);
187ae8c8068SEtienne Carriere 		return NULL;
188ae8c8068SEtienne Carriere 	}
189ae8c8068SEtienne Carriere 
190ae8c8068SEtienne Carriere 	message_id = confine_array_index(msg->message_id, array_size);
191ae8c8068SEtienne Carriere 
192ae8c8068SEtienne Carriere 	return scmi_base_handler_table[message_id];
193ae8c8068SEtienne Carriere }
194