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 */
channel_set_busy(struct scmi_msg_channel * chan)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
channel_release_busy(struct scmi_msg_channel * chan)78 static void channel_release_busy(struct scmi_msg_channel *chan)
79 {
80 chan->busy = false;
81 }
82
channel_to_smt_hdr(struct scmi_msg_channel * chan)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 sizeof(struct smt_header));
87 }
88
89 /*
90 * Creates a SCMI message instance in secure memory and push it in the SCMI
91 * message drivers. Message structure contains SCMI protocol meta-data and
92 * references to input payload in secure memory and output message buffer
93 * in shared memory.
94 */
scmi_process_smt(unsigned int channel_id,uint32_t * payload_buf)95 static void scmi_process_smt(unsigned int channel_id, uint32_t *payload_buf)
96 {
97 struct scmi_msg_channel *chan = NULL;
98 struct smt_header *smt_hdr = NULL;
99 size_t in_payload_size = 0;
100 uint32_t smt_status = 0;
101 struct scmi_msg msg = { };
102 bool error = true;
103
104 chan = plat_scmi_get_channel(channel_id);
105 if (!chan)
106 return;
107
108 smt_hdr = channel_to_smt_hdr(chan);
109 assert(smt_hdr);
110
111 smt_status = READ_ONCE(smt_hdr->status);
112
113 if (!channel_set_busy(chan)) {
114 DMSG("SCMI channel %u busy", channel_id);
115 goto out;
116 }
117
118 in_payload_size = READ_ONCE(smt_hdr->length) -
119 sizeof(smt_hdr->message_header);
120
121 if (in_payload_size > SCMI_PLAYLOAD_MAX) {
122 DMSG("SCMI payload too big %u", in_payload_size);
123 goto out;
124 }
125
126 if (smt_status & (SMT_STATUS_ERROR | SMT_STATUS_FREE)) {
127 DMSG("SCMI channel bad status 0x%x",
128 smt_hdr->status & (SMT_STATUS_ERROR | SMT_STATUS_FREE));
129 goto out;
130 }
131
132 /* Fill message */
133 msg.in = (char *)payload_buf;
134 msg.in_size = in_payload_size;
135 msg.out = (char *)smt_hdr->payload;
136 msg.out_size = chan->shm_size - sizeof(*smt_hdr);
137
138 assert(msg.out && msg.out_size >= sizeof(int32_t));
139
140 /* Here the payload is copied in secure memory */
141 memcpy(msg.in, smt_hdr->payload, in_payload_size);
142
143 msg.protocol_id = SMT_HDR_PROT_ID(smt_hdr->message_header);
144 msg.message_id = SMT_HDR_MSG_ID(smt_hdr->message_header);
145 msg.channel_id = channel_id;
146
147 scmi_process_message(&msg);
148
149 /* Update message length with the length of the response message */
150 smt_hdr->length = msg.out_size_out + sizeof(smt_hdr->message_header);
151
152 channel_release_busy(chan);
153 error = false;
154
155 out:
156 if (error) {
157 DMSG("SCMI error");
158 smt_hdr->status |= SMT_STATUS_ERROR | SMT_STATUS_FREE;
159 } else {
160 smt_hdr->status |= SMT_STATUS_FREE;
161 }
162 }
163
164 #ifdef CFG_SCMI_MSG_SMT_FASTCALL_ENTRY
165 /* Provision input message payload buffers for fastcall SMC context entries */
166 static uint32_t fast_smc_payload[CFG_TEE_CORE_NB_CORE][SCMI_PLAYLOAD_U32_MAX];
167
scmi_smt_fastcall_smc_entry(unsigned int channel_id)168 void scmi_smt_fastcall_smc_entry(unsigned int channel_id)
169 {
170 scmi_process_smt(channel_id, fast_smc_payload[get_core_pos()]);
171 }
172 #endif
173
174 #ifdef CFG_SCMI_MSG_SMT_INTERRUPT_ENTRY
175 /* Provision input message payload buffers for fastcall SMC context entries */
176 static uint32_t interrupt_payload[CFG_TEE_CORE_NB_CORE][SCMI_PLAYLOAD_U32_MAX];
177
scmi_smt_interrupt_entry(unsigned int channel_id)178 void scmi_smt_interrupt_entry(unsigned int channel_id)
179 {
180 scmi_process_smt(channel_id, interrupt_payload[get_core_pos()]);
181 }
182 #endif
183
184 #ifdef CFG_SCMI_MSG_SMT_THREAD_ENTRY
185 /* Provision input message payload buffers for fastcall SMC context entries */
186 static uint32_t threaded_payload[CFG_NUM_THREADS][SCMI_PLAYLOAD_U32_MAX];
187
scmi_smt_threaded_entry(unsigned int channel_id)188 void scmi_smt_threaded_entry(unsigned int channel_id)
189 {
190 assert(plat_scmi_get_channel(channel_id)->threaded);
191
192 scmi_process_smt(channel_id, threaded_payload[thread_get_id()]);
193 }
194 #endif
195
196 /* Init a SMT header for a shared memory buffer: state it a free/no-error */
scmi_smt_init_agent_channel(struct scmi_msg_channel * chan)197 void scmi_smt_init_agent_channel(struct scmi_msg_channel *chan)
198 {
199 COMPILE_TIME_ASSERT(SCMI_PLAYLOAD_MAX + sizeof(struct smt_header) <=
200 SMT_BUF_SLOT_SIZE);
201
202 if (chan) {
203 struct smt_header *smt_header = channel_to_smt_hdr(chan);
204
205 if (smt_header) {
206 memset(smt_header, 0, sizeof(*smt_header));
207 smt_header->status = SMT_STATUS_FREE;
208
209 return;
210 }
211 }
212
213 panic();
214 }
215
scmi_smt_set_shared_buffer(struct scmi_msg_channel * channel,void * base)216 void scmi_smt_set_shared_buffer(struct scmi_msg_channel *channel, void *base)
217 {
218 paddr_t p_base = 0;
219
220 if (base) {
221 assert(!channel->shm_addr.va && !channel->shm_addr.pa);
222 p_base = virt_to_phys(base);
223 assert(p_base);
224 }
225
226 channel->shm_addr.va = (vaddr_t)base;
227 channel->shm_addr.pa = p_base;
228 }
229