xref: /optee_os/core/drivers/scmi-msg/smt.c (revision a58c4d706d2333d2b21a3eba7e2ec0cb257bca1d)
1*a58c4d70SEtienne Carriere // SPDX-License-Identifier: BSD-2-Clause
2*a58c4d70SEtienne Carriere /*
3*a58c4d70SEtienne Carriere  * Copyright (c) 2015-2019, Arm Limited and Contributors. All rights reserved.
4*a58c4d70SEtienne Carriere  * Copyright (c) 2019-2020, Linaro Limited
5*a58c4d70SEtienne Carriere  */
6*a58c4d70SEtienne Carriere #include <assert.h>
7*a58c4d70SEtienne Carriere #include <drivers/scmi-msg.h>
8*a58c4d70SEtienne Carriere #include <drivers/scmi.h>
9*a58c4d70SEtienne Carriere #include <io.h>
10*a58c4d70SEtienne Carriere #include <kernel/misc.h>
11*a58c4d70SEtienne Carriere #include <kernel/panic.h>
12*a58c4d70SEtienne Carriere #include <kernel/spinlock.h>
13*a58c4d70SEtienne Carriere #include <stdbool.h>
14*a58c4d70SEtienne Carriere #include <stdint.h>
15*a58c4d70SEtienne Carriere #include <string.h>
16*a58c4d70SEtienne Carriere #include <trace.h>
17*a58c4d70SEtienne Carriere #include <util.h>
18*a58c4d70SEtienne Carriere 
19*a58c4d70SEtienne Carriere #include "common.h"
20*a58c4d70SEtienne Carriere 
21*a58c4d70SEtienne Carriere /* Legacy SMT/SCMI messages are 128 bytes at most including SMT header */
22*a58c4d70SEtienne Carriere #define SCMI_PLAYLOAD_MAX		92
23*a58c4d70SEtienne Carriere #define SCMI_PLAYLOAD_U32_MAX		(SCMI_PLAYLOAD_MAX / sizeof(uint32_t))
24*a58c4d70SEtienne Carriere 
25*a58c4d70SEtienne Carriere /**
26*a58c4d70SEtienne Carriere  * struct smt_header - SMT formatted header for SMT base shared memory transfer
27*a58c4d70SEtienne Carriere  *
28*a58c4d70SEtienne Carriere  * @status: Bit flags, see SMT_STATUS_*
29*a58c4d70SEtienne Carriere  * @flags: Bit flags, see SMT_FLAG_*
30*a58c4d70SEtienne Carriere  * @length: Byte size of message payload (variable) + ::message_header (32bit)
31*a58c4d70SEtienne Carriere  * payload: SCMI message payload data
32*a58c4d70SEtienne Carriere  */
33*a58c4d70SEtienne Carriere struct smt_header {
34*a58c4d70SEtienne Carriere 	uint32_t reserved0;
35*a58c4d70SEtienne Carriere 	uint32_t status;
36*a58c4d70SEtienne Carriere 	uint64_t reserved1;
37*a58c4d70SEtienne Carriere 	uint32_t flags;
38*a58c4d70SEtienne Carriere 	uint32_t length; /* message_header + payload */
39*a58c4d70SEtienne Carriere 	uint32_t message_header;
40*a58c4d70SEtienne Carriere 	uint32_t payload[];
41*a58c4d70SEtienne Carriere };
42*a58c4d70SEtienne Carriere 
43*a58c4d70SEtienne Carriere /* Flag set in smt_header::status when SMT does not contain pending message */
44*a58c4d70SEtienne Carriere #define SMT_STATUS_FREE			BIT(0)
45*a58c4d70SEtienne Carriere /* Flag set in smt_header::status when SMT reports an error */
46*a58c4d70SEtienne Carriere #define SMT_STATUS_ERROR		BIT(1)
47*a58c4d70SEtienne Carriere 
48*a58c4d70SEtienne Carriere /* Flag set in smt_header::flags when SMT uses interrupts */
49*a58c4d70SEtienne Carriere #define SMT_FLAG_INTR_ENABLED		BIT(1)
50*a58c4d70SEtienne Carriere 
51*a58c4d70SEtienne Carriere /* Bit fields packed in smt_header::message_header */
52*a58c4d70SEtienne Carriere #define SMT_MSG_ID_MASK			GENMASK_32(7, 0)
53*a58c4d70SEtienne Carriere #define SMT_HDR_MSG_ID(_hdr)		((_hdr) & SMT_MSG_ID_MASK)
54*a58c4d70SEtienne Carriere 
55*a58c4d70SEtienne Carriere #define SMT_MSG_TYPE_MASK		GENMASK_32(9, 8)
56*a58c4d70SEtienne Carriere #define SMT_HDR_TYPE_ID(_hdr)		(((_hdr) & SMT_MSG_TYPE_MASK) >> 8)
57*a58c4d70SEtienne Carriere 
58*a58c4d70SEtienne Carriere #define SMT_MSG_PROT_ID_MASK		GENMASK_32(17, 10)
59*a58c4d70SEtienne Carriere #define SMT_HDR_PROT_ID(_hdr)		(((_hdr) & SMT_MSG_PROT_ID_MASK) >> 10)
60*a58c4d70SEtienne Carriere 
61*a58c4d70SEtienne Carriere /* SMP protection on channel access */
62*a58c4d70SEtienne Carriere static unsigned int smt_channels_lock;
63*a58c4d70SEtienne Carriere 
64*a58c4d70SEtienne Carriere /* If channel is not busy, set busy and return true, otherwise return false */
65*a58c4d70SEtienne Carriere static bool channel_set_busy(struct scmi_msg_channel *chan)
66*a58c4d70SEtienne Carriere {
67*a58c4d70SEtienne Carriere 	uint32_t exceptions = cpu_spin_lock_xsave(&smt_channels_lock);
68*a58c4d70SEtienne Carriere 	bool channel_is_busy = chan->busy;
69*a58c4d70SEtienne Carriere 
70*a58c4d70SEtienne Carriere 	if (!channel_is_busy)
71*a58c4d70SEtienne Carriere 		chan->busy = true;
72*a58c4d70SEtienne Carriere 
73*a58c4d70SEtienne Carriere 	cpu_spin_unlock_xrestore(&smt_channels_lock, exceptions);
74*a58c4d70SEtienne Carriere 
75*a58c4d70SEtienne Carriere 	return !channel_is_busy;
76*a58c4d70SEtienne Carriere }
77*a58c4d70SEtienne Carriere 
78*a58c4d70SEtienne Carriere static void channel_release_busy(struct scmi_msg_channel *chan)
79*a58c4d70SEtienne Carriere {
80*a58c4d70SEtienne Carriere 	chan->busy = false;
81*a58c4d70SEtienne Carriere }
82*a58c4d70SEtienne Carriere 
83*a58c4d70SEtienne Carriere static struct smt_header *channel_to_smt_hdr(struct scmi_msg_channel *chan)
84*a58c4d70SEtienne Carriere {
85*a58c4d70SEtienne Carriere 	return (struct smt_header *)io_pa_or_va(&chan->shm_addr);
86*a58c4d70SEtienne Carriere }
87*a58c4d70SEtienne Carriere 
88*a58c4d70SEtienne Carriere /*
89*a58c4d70SEtienne Carriere  * Creates a SCMI message instance in secure memory and push it in the SCMI
90*a58c4d70SEtienne Carriere  * message drivers. Message structure contains SCMI protocol meta-data and
91*a58c4d70SEtienne Carriere  * references to input payload in secure memory and output message buffer
92*a58c4d70SEtienne Carriere  * in shared memory.
93*a58c4d70SEtienne Carriere  */
94*a58c4d70SEtienne Carriere static void scmi_proccess_smt(unsigned int agent_id, uint32_t *payload_buf)
95*a58c4d70SEtienne Carriere {
96*a58c4d70SEtienne Carriere 	struct scmi_msg_channel *chan = NULL;
97*a58c4d70SEtienne Carriere 	struct smt_header *smt_hdr = NULL;
98*a58c4d70SEtienne Carriere 	size_t in_payload_size = 0;
99*a58c4d70SEtienne Carriere 	uint32_t smt_status = 0;
100*a58c4d70SEtienne Carriere 	struct scmi_msg msg = { };
101*a58c4d70SEtienne Carriere 	bool error = true;
102*a58c4d70SEtienne Carriere 
103*a58c4d70SEtienne Carriere 	chan = plat_scmi_get_channel(agent_id);
104*a58c4d70SEtienne Carriere 	if (!chan)
105*a58c4d70SEtienne Carriere 		return;
106*a58c4d70SEtienne Carriere 
107*a58c4d70SEtienne Carriere 	smt_hdr = channel_to_smt_hdr(chan);
108*a58c4d70SEtienne Carriere 	assert(smt_hdr);
109*a58c4d70SEtienne Carriere 
110*a58c4d70SEtienne Carriere 	smt_status = READ_ONCE(smt_hdr->status);
111*a58c4d70SEtienne Carriere 
112*a58c4d70SEtienne Carriere 	if (!channel_set_busy(chan)) {
113*a58c4d70SEtienne Carriere 		DMSG("SCMI channel %u busy", agent_id);
114*a58c4d70SEtienne Carriere 		goto out;
115*a58c4d70SEtienne Carriere 	}
116*a58c4d70SEtienne Carriere 
117*a58c4d70SEtienne Carriere 	in_payload_size = READ_ONCE(smt_hdr->length) -
118*a58c4d70SEtienne Carriere 			  sizeof(smt_hdr->message_header);
119*a58c4d70SEtienne Carriere 
120*a58c4d70SEtienne Carriere 	if (in_payload_size > SCMI_PLAYLOAD_MAX) {
121*a58c4d70SEtienne Carriere 		DMSG("SCMI payload too big %u", in_payload_size);
122*a58c4d70SEtienne Carriere 		goto out;
123*a58c4d70SEtienne Carriere 	}
124*a58c4d70SEtienne Carriere 
125*a58c4d70SEtienne Carriere 	if (smt_status & (SMT_STATUS_ERROR | SMT_STATUS_FREE)) {
126*a58c4d70SEtienne Carriere 		DMSG("SCMI channel bad status 0x%x",
127*a58c4d70SEtienne Carriere 		     smt_hdr->status & (SMT_STATUS_ERROR | SMT_STATUS_FREE));
128*a58c4d70SEtienne Carriere 		goto out;
129*a58c4d70SEtienne Carriere 	}
130*a58c4d70SEtienne Carriere 
131*a58c4d70SEtienne Carriere 	/* Fill message */
132*a58c4d70SEtienne Carriere 	msg.in = (char *)payload_buf;
133*a58c4d70SEtienne Carriere 	msg.in_size = in_payload_size;
134*a58c4d70SEtienne Carriere 	msg.out = (char *)smt_hdr->payload;
135*a58c4d70SEtienne Carriere 	msg.out_size = chan->shm_size - sizeof(*smt_hdr);
136*a58c4d70SEtienne Carriere 
137*a58c4d70SEtienne Carriere 	assert(msg.out && msg.out_size >= sizeof(int32_t));
138*a58c4d70SEtienne Carriere 
139*a58c4d70SEtienne Carriere 	/* Here the payload is copied in secure memory */
140*a58c4d70SEtienne Carriere 	memcpy(msg.in, smt_hdr->payload, in_payload_size);
141*a58c4d70SEtienne Carriere 
142*a58c4d70SEtienne Carriere 	msg.protocol_id = SMT_HDR_PROT_ID(smt_hdr->message_header);
143*a58c4d70SEtienne Carriere 	msg.message_id = SMT_HDR_MSG_ID(smt_hdr->message_header);
144*a58c4d70SEtienne Carriere 	msg.agent_id = agent_id;
145*a58c4d70SEtienne Carriere 
146*a58c4d70SEtienne Carriere 	scmi_process_message(&msg);
147*a58c4d70SEtienne Carriere 
148*a58c4d70SEtienne Carriere 	/* Update message length with the length of the response message */
149*a58c4d70SEtienne Carriere 	smt_hdr->length = msg.out_size_out + sizeof(smt_hdr->message_header);
150*a58c4d70SEtienne Carriere 
151*a58c4d70SEtienne Carriere 	channel_release_busy(chan);
152*a58c4d70SEtienne Carriere 	error = false;
153*a58c4d70SEtienne Carriere 
154*a58c4d70SEtienne Carriere out:
155*a58c4d70SEtienne Carriere 	if (error) {
156*a58c4d70SEtienne Carriere 		DMSG("SCMI error");
157*a58c4d70SEtienne Carriere 		smt_hdr->status |= SMT_STATUS_ERROR | SMT_STATUS_FREE;
158*a58c4d70SEtienne Carriere 	} else {
159*a58c4d70SEtienne Carriere 		smt_hdr->status |= SMT_STATUS_FREE;
160*a58c4d70SEtienne Carriere 	}
161*a58c4d70SEtienne Carriere }
162*a58c4d70SEtienne Carriere 
163*a58c4d70SEtienne Carriere #ifdef CFG_SCMI_MSG_SMT_FASTCALL_ENTRY
164*a58c4d70SEtienne Carriere /* Provision input message payload buffers for fastcall SMC context entries */
165*a58c4d70SEtienne Carriere static uint32_t fast_smc_payload[CFG_TEE_CORE_NB_CORE][SCMI_PLAYLOAD_U32_MAX];
166*a58c4d70SEtienne Carriere 
167*a58c4d70SEtienne Carriere void scmi_smt_fastcall_smc_entry(unsigned int agent_id)
168*a58c4d70SEtienne Carriere {
169*a58c4d70SEtienne Carriere 	scmi_proccess_smt(agent_id, fast_smc_payload[get_core_pos()]);
170*a58c4d70SEtienne Carriere }
171*a58c4d70SEtienne Carriere #endif
172*a58c4d70SEtienne Carriere 
173*a58c4d70SEtienne Carriere #ifdef CFG_SCMI_MSG_SMT_INTERRUPT_ENTRY
174*a58c4d70SEtienne Carriere /* Provision input message payload buffers for fastcall SMC context entries */
175*a58c4d70SEtienne Carriere static uint32_t interrupt_payload[CFG_TEE_CORE_NB_CORE][SCMI_PLAYLOAD_U32_MAX];
176*a58c4d70SEtienne Carriere 
177*a58c4d70SEtienne Carriere void scmi_smt_interrupt_entry(unsigned int agent_id)
178*a58c4d70SEtienne Carriere {
179*a58c4d70SEtienne Carriere 	scmi_proccess_smt(agent_id, interrupt_payload[get_core_pos()]);
180*a58c4d70SEtienne Carriere }
181*a58c4d70SEtienne Carriere #endif
182*a58c4d70SEtienne Carriere 
183*a58c4d70SEtienne Carriere #ifdef CFG_SCMI_MSG_SMT_THREAD_ENTRY
184*a58c4d70SEtienne Carriere /* Provision input message payload buffers for fastcall SMC context entries */
185*a58c4d70SEtienne Carriere static uint32_t threaded_payload[CFG_NUM_THREADS][SCMI_PLAYLOAD_U32_MAX];
186*a58c4d70SEtienne Carriere 
187*a58c4d70SEtienne Carriere void scmi_smt_threaded_entry(unsigned int agent_id)
188*a58c4d70SEtienne Carriere {
189*a58c4d70SEtienne Carriere 	assert(plat_scmi_get_channel(agent_id)->threaded);
190*a58c4d70SEtienne Carriere 
191*a58c4d70SEtienne Carriere 	scmi_proccess_smt(agent_id, threaded_payload[thread_get_id()]);
192*a58c4d70SEtienne Carriere }
193*a58c4d70SEtienne Carriere #endif
194*a58c4d70SEtienne Carriere 
195*a58c4d70SEtienne Carriere /* Init a SMT header for a shared memory buffer: state it a free/no-error */
196*a58c4d70SEtienne Carriere void scmi_smt_init_agent_channel(struct scmi_msg_channel *chan)
197*a58c4d70SEtienne Carriere {
198*a58c4d70SEtienne Carriere 	COMPILE_TIME_ASSERT(SCMI_PLAYLOAD_MAX + sizeof(struct smt_header) <=
199*a58c4d70SEtienne Carriere 			    SMT_BUF_SLOT_SIZE);
200*a58c4d70SEtienne Carriere 
201*a58c4d70SEtienne Carriere 	if (chan) {
202*a58c4d70SEtienne Carriere 		struct smt_header *smt_header = channel_to_smt_hdr(chan);
203*a58c4d70SEtienne Carriere 
204*a58c4d70SEtienne Carriere 		if (smt_header) {
205*a58c4d70SEtienne Carriere 			memset(smt_header, 0, sizeof(*smt_header));
206*a58c4d70SEtienne Carriere 			smt_header->status = SMT_STATUS_FREE;
207*a58c4d70SEtienne Carriere 
208*a58c4d70SEtienne Carriere 			return;
209*a58c4d70SEtienne Carriere 		}
210*a58c4d70SEtienne Carriere 	}
211*a58c4d70SEtienne Carriere 
212*a58c4d70SEtienne Carriere 	panic();
213*a58c4d70SEtienne Carriere }
214