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