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