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