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