xref: /optee_os/core/arch/arm/kernel/stmm_sp.c (revision ed89aa36c3d83cdc1d26d4512ed6f56cfe047919)
1f9cd31c5SJelle Sels // SPDX-License-Identifier: BSD-2-Clause
2f9cd31c5SJelle Sels /*
3f9cd31c5SJelle Sels  * Copyright (c) 2019, Linaro Limited
4f9cd31c5SJelle Sels  * Copyright (c) 2020, Arm Limited.
5f9cd31c5SJelle Sels  */
6f9cd31c5SJelle Sels 
7f9cd31c5SJelle Sels #include <crypto/crypto.h>
8aa6d7fc3SLevi Yun #include <efi/hob.h>
9f9cd31c5SJelle Sels #include <ffa.h>
10409c619bSEtienne Carriere #include <keep.h>
11f9cd31c5SJelle Sels #include <kernel/abort.h>
12f9cd31c5SJelle Sels #include <kernel/stmm_sp.h>
13956c2d50SEtienne Carriere #include <kernel/tee_ta_manager.h>
147e399f9bSJens Wiklander #include <kernel/thread_private.h>
15f9cd31c5SJelle Sels #include <kernel/user_mode_ctx.h>
16a951fe52SEtienne Carriere #include <mempool.h>
17f9cd31c5SJelle Sels #include <mm/fobj.h>
18f9cd31c5SJelle Sels #include <mm/mobj.h>
19f9cd31c5SJelle Sels #include <mm/vm.h>
20f9cd31c5SJelle Sels #include <pta_stmm.h>
21f9cd31c5SJelle Sels #include <tee_api_defines_extensions.h>
22f9cd31c5SJelle Sels #include <tee/tee_pobj.h>
23f9cd31c5SJelle Sels #include <tee/tee_svc.h>
24f9cd31c5SJelle Sels #include <tee/tee_svc_storage.h>
25f9cd31c5SJelle Sels #include <zlib.h>
26f9cd31c5SJelle Sels 
27d9339333SEtienne Carriere #ifdef ARM64
28d9339333SEtienne Carriere #define SVC_REGS_A0(_regs)	((_regs)->x0)
29d9339333SEtienne Carriere #define SVC_REGS_A1(_regs)	((_regs)->x1)
30d9339333SEtienne Carriere #define SVC_REGS_A2(_regs)	((_regs)->x2)
31d9339333SEtienne Carriere #define SVC_REGS_A3(_regs)	((_regs)->x3)
32d9339333SEtienne Carriere #define SVC_REGS_A4(_regs)	((_regs)->x4)
33d9339333SEtienne Carriere #define SVC_REGS_A5(_regs)	((_regs)->x5)
34d9339333SEtienne Carriere #define SVC_REGS_A6(_regs)	((_regs)->x6)
35d9339333SEtienne Carriere #define SVC_REGS_A7(_regs)	((_regs)->x7)
36d9339333SEtienne Carriere #define __FFA_SVC_RPMB_READ		FFA_SVC_RPMB_READ
37d9339333SEtienne Carriere #define __FFA_SVC_RPMB_WRITE		FFA_SVC_RPMB_WRITE
38d9339333SEtienne Carriere #define __FFA_MSG_SEND_DIRECT_RESP	FFA_MSG_SEND_DIRECT_RESP_64
39d9339333SEtienne Carriere #define __FFA_MSG_SEND_DIRECT_REQ	FFA_MSG_SEND_DIRECT_REQ_64
40aa6d7fc3SLevi Yun #define __FFA_MEM_PERM_GET	FFA_MEM_PERM_GET_64
41aa6d7fc3SLevi Yun #define __FFA_MEM_PERM_SET	FFA_MEM_PERM_SET_64
42d9339333SEtienne Carriere #endif
43d9339333SEtienne Carriere #ifdef ARM32
44d9339333SEtienne Carriere #define SVC_REGS_A0(_regs)	((_regs)->r0)
45d9339333SEtienne Carriere #define SVC_REGS_A1(_regs)	((_regs)->r1)
46d9339333SEtienne Carriere #define SVC_REGS_A2(_regs)	((_regs)->r2)
47d9339333SEtienne Carriere #define SVC_REGS_A3(_regs)	((_regs)->r3)
48d9339333SEtienne Carriere #define SVC_REGS_A4(_regs)	((_regs)->r4)
49d9339333SEtienne Carriere #define SVC_REGS_A5(_regs)	((_regs)->r5)
50d9339333SEtienne Carriere #define SVC_REGS_A6(_regs)	((_regs)->r6)
51d9339333SEtienne Carriere #define SVC_REGS_A7(_regs)	((_regs)->r7)
52d9339333SEtienne Carriere #define __FFA_SVC_RPMB_READ		FFA_SVC_RPMB_READ_32
53d9339333SEtienne Carriere #define __FFA_SVC_RPMB_WRITE		FFA_SVC_RPMB_WRITE_32
54d9339333SEtienne Carriere #define __FFA_MSG_SEND_DIRECT_RESP	FFA_MSG_SEND_DIRECT_RESP_32
55d9339333SEtienne Carriere #define __FFA_MSG_SEND_DIRECT_REQ	FFA_MSG_SEND_DIRECT_REQ_32
56aa6d7fc3SLevi Yun #define __FFA_MEM_PERM_GET	FFA_MEM_PERM_GET_32
57aa6d7fc3SLevi Yun #define __FFA_MEM_PERM_SET	FFA_MEM_PERM_SET_32
58d9339333SEtienne Carriere #endif
59d9339333SEtienne Carriere 
60f9cd31c5SJelle Sels static const TEE_UUID stmm_uuid = PTA_STMM_UUID;
61aa6d7fc3SLevi Yun static TEE_UUID ns_buf_guid = MM_NS_BUFFER_GUID;
62aa6d7fc3SLevi Yun static TEE_UUID mmram_resv_guid = MM_PEI_MMRAM_MEMORY_RESERVE_GUID;
63f9cd31c5SJelle Sels 
64f9cd31c5SJelle Sels /*
65f9cd31c5SJelle Sels  * Once a complete FFA spec is added, these will become discoverable.
66f9cd31c5SJelle Sels  * Until then these are considered part of the internal ABI between
67f9cd31c5SJelle Sels  * OP-TEE and StMM.
68f9cd31c5SJelle Sels  */
69f9cd31c5SJelle Sels static const uint16_t stmm_id = 1U;
70f9cd31c5SJelle Sels static const uint16_t stmm_pta_id = 2U;
71f9cd31c5SJelle Sels static const uint16_t ffa_storage_id = 4U;
72f9cd31c5SJelle Sels 
73aa6d7fc3SLevi Yun static const unsigned int stmm_heap_size = 402 * SMALL_PAGE_SIZE;
742452979fSIlias Apalodimas static const unsigned int stmm_sec_buf_size = 4 * SMALL_PAGE_SIZE;
752452979fSIlias Apalodimas static const unsigned int stmm_ns_comm_buf_size = 4 * SMALL_PAGE_SIZE;
76f9cd31c5SJelle Sels 
77f9cd31c5SJelle Sels extern unsigned char stmm_image[];
78f9cd31c5SJelle Sels extern const unsigned int stmm_image_size;
79f9cd31c5SJelle Sels extern const unsigned int stmm_image_uncompressed_size;
80f9cd31c5SJelle Sels 
81aa6d7fc3SLevi Yun static vaddr_t stmm_image_addr;
82aa6d7fc3SLevi Yun static vaddr_t stmm_heap_addr;
83aa6d7fc3SLevi Yun static vaddr_t stmm_ns_comm_buf_addr;
84aa6d7fc3SLevi Yun static vaddr_t stmm_sec_buf_addr;
85aa6d7fc3SLevi Yun 
stmm_get_uuid(void)868f31ccb0SJens Wiklander const TEE_UUID *stmm_get_uuid(void)
878f31ccb0SJens Wiklander {
888f31ccb0SJens Wiklander 	return &stmm_uuid;
898f31ccb0SJens Wiklander }
908f31ccb0SJens Wiklander 
stmm_alloc_ctx(const TEE_UUID * uuid)91f9cd31c5SJelle Sels static struct stmm_ctx *stmm_alloc_ctx(const TEE_UUID *uuid)
92f9cd31c5SJelle Sels {
93aa6d7fc3SLevi Yun 	TEE_Result res = TEE_ERROR_GENERIC;
94f9cd31c5SJelle Sels 	struct stmm_ctx *spc = NULL;
95f9cd31c5SJelle Sels 
96f9cd31c5SJelle Sels 	spc = calloc(1, sizeof(*spc));
97f9cd31c5SJelle Sels 	if (!spc)
98f9cd31c5SJelle Sels 		return NULL;
99f9cd31c5SJelle Sels 
100f9cd31c5SJelle Sels 	spc->ta_ctx.ts_ctx.ops = &stmm_sp_ops;
101f9cd31c5SJelle Sels 	spc->ta_ctx.ts_ctx.uuid = *uuid;
102f9cd31c5SJelle Sels 	spc->ta_ctx.flags = TA_FLAG_SINGLE_INSTANCE |
103f9cd31c5SJelle Sels 			    TA_FLAG_INSTANCE_KEEP_ALIVE;
104f9cd31c5SJelle Sels 
10560d3fc69SJens Wiklander 	res = vm_info_init(&spc->uctx, &spc->ta_ctx.ts_ctx);
106f9cd31c5SJelle Sels 	if (res) {
107f9cd31c5SJelle Sels 		free(spc);
108f9cd31c5SJelle Sels 		return NULL;
109f9cd31c5SJelle Sels 	}
110f9cd31c5SJelle Sels 
111f9cd31c5SJelle Sels 	spc->ta_ctx.ref_count = 1;
112f9cd31c5SJelle Sels 	condvar_init(&spc->ta_ctx.busy_cv);
113f9cd31c5SJelle Sels 
114f9cd31c5SJelle Sels 	return spc;
115f9cd31c5SJelle Sels }
116f9cd31c5SJelle Sels 
stmm_enter_user_mode(struct stmm_ctx * spc)117f9cd31c5SJelle Sels static TEE_Result stmm_enter_user_mode(struct stmm_ctx *spc)
118f9cd31c5SJelle Sels {
119f9cd31c5SJelle Sels 	uint32_t exceptions = 0;
120f9cd31c5SJelle Sels 	uint32_t panic_code = 0;
121f9cd31c5SJelle Sels 	uint32_t panicked = 0;
122f9cd31c5SJelle Sels 	uint64_t cntkctl = 0;
123f9cd31c5SJelle Sels 
124f9cd31c5SJelle Sels 	exceptions = thread_mask_exceptions(THREAD_EXCP_ALL);
125f9cd31c5SJelle Sels 	cntkctl = read_cntkctl();
126f9cd31c5SJelle Sels 	write_cntkctl(cntkctl | CNTKCTL_PL0PCTEN);
12727c1358cSEtienne Carriere 
12827c1358cSEtienne Carriere #ifdef ARM32
12927c1358cSEtienne Carriere 	/* Handle usr_lr in place of __thread_enter_user_mode() */
13027c1358cSEtienne Carriere 	thread_set_usr_lr(spc->regs.usr_lr);
13127c1358cSEtienne Carriere #endif
13227c1358cSEtienne Carriere 
133f9cd31c5SJelle Sels 	__thread_enter_user_mode(&spc->regs, &panicked, &panic_code);
13427c1358cSEtienne Carriere 
13527c1358cSEtienne Carriere #ifdef ARM32
13627c1358cSEtienne Carriere 	spc->regs.usr_lr = thread_get_usr_lr();
13727c1358cSEtienne Carriere #endif
13827c1358cSEtienne Carriere 
139f9cd31c5SJelle Sels 	write_cntkctl(cntkctl);
140f9cd31c5SJelle Sels 	thread_unmask_exceptions(exceptions);
141f9cd31c5SJelle Sels 
142b351c689SBalint Dobszay 	thread_user_clear_vfp(&spc->uctx);
143f9cd31c5SJelle Sels 
144f9cd31c5SJelle Sels 	if (panicked) {
1454e994fd8SJelle Sels 		abort_print_current_ts();
146f9cd31c5SJelle Sels 		DMSG("stmm panicked with code %#"PRIx32, panic_code);
147f9cd31c5SJelle Sels 		return TEE_ERROR_TARGET_DEAD;
148f9cd31c5SJelle Sels 	}
149f9cd31c5SJelle Sels 
150f9cd31c5SJelle Sels 	return TEE_SUCCESS;
151f9cd31c5SJelle Sels }
152f9cd31c5SJelle Sels 
153d9339333SEtienne Carriere #ifdef ARM64
init_stmm_regs(struct stmm_ctx * spc,unsigned long a0,unsigned long a1,unsigned long a2,unsigned long a3,unsigned long pc)154f9cd31c5SJelle Sels static void init_stmm_regs(struct stmm_ctx *spc, unsigned long a0,
155aa6d7fc3SLevi Yun 			   unsigned long a1, unsigned long a2, unsigned long a3,
156aa6d7fc3SLevi Yun 				 unsigned long pc)
157f9cd31c5SJelle Sels {
158f9cd31c5SJelle Sels 	spc->regs.x[0] = a0;
159f9cd31c5SJelle Sels 	spc->regs.x[1] = a1;
160aa6d7fc3SLevi Yun 	spc->regs.x[2] = a2;
161aa6d7fc3SLevi Yun 	spc->regs.x[3] = a3;
162f9cd31c5SJelle Sels 	spc->regs.pc = pc;
163f9cd31c5SJelle Sels }
164d9339333SEtienne Carriere #endif
165d9339333SEtienne Carriere 
166d9339333SEtienne Carriere #ifdef ARM32
get_spsr(void)167d9339333SEtienne Carriere static uint32_t __maybe_unused get_spsr(void)
168d9339333SEtienne Carriere {
169d9339333SEtienne Carriere 	uint32_t s = 0;
170d9339333SEtienne Carriere 
171d9339333SEtienne Carriere 	s = read_cpsr();
172d9339333SEtienne Carriere 	s &= ~(CPSR_MODE_MASK | CPSR_T | ARM32_CPSR_IT_MASK);
173d9339333SEtienne Carriere 	s |= CPSR_MODE_USR;
174d9339333SEtienne Carriere 
175d9339333SEtienne Carriere 	return s;
176d9339333SEtienne Carriere }
177d9339333SEtienne Carriere 
init_stmm_regs(struct stmm_ctx * spc,unsigned long a0,unsigned long a1,unsigned long a2,unsigned long a3,unsigned long pc)178d9339333SEtienne Carriere static void init_stmm_regs(struct stmm_ctx *spc, unsigned long a0,
179aa6d7fc3SLevi Yun 			   unsigned long a1, unsigned long a2, unsigned long a3,
180aa6d7fc3SLevi Yun 				 unsigned long pc)
181d9339333SEtienne Carriere {
182d9339333SEtienne Carriere 	spc->regs.r0 = a0;
183d9339333SEtienne Carriere 	spc->regs.r1 = a1;
184aa6d7fc3SLevi Yun 	spc->regs.r2 = a2;
185aa6d7fc3SLevi Yun 	spc->regs.r3 = a3;
186d9339333SEtienne Carriere 	spc->regs.cpsr = get_spsr();
187d9339333SEtienne Carriere 	spc->regs.pc = pc;
188d9339333SEtienne Carriere }
189d9339333SEtienne Carriere #endif
190f9cd31c5SJelle Sels 
alloc_and_map_sp_fobj(struct stmm_ctx * spc,size_t sz,uint32_t prot,vaddr_t * va)191f9cd31c5SJelle Sels static TEE_Result alloc_and_map_sp_fobj(struct stmm_ctx *spc, size_t sz,
192f9cd31c5SJelle Sels 					uint32_t prot, vaddr_t *va)
193f9cd31c5SJelle Sels {
19404e46975SEtienne Carriere 	size_t num_pgs = ROUNDUP_DIV(sz, SMALL_PAGE_SIZE);
195f9cd31c5SJelle Sels 	struct fobj *fobj = fobj_ta_mem_alloc(num_pgs);
196f9cd31c5SJelle Sels 	TEE_Result res = TEE_SUCCESS;
1976105aa86SJens Wiklander 	struct mobj *mobj = NULL;
198f9cd31c5SJelle Sels 
1996105aa86SJens Wiklander 	mobj = mobj_with_fobj_alloc(fobj, NULL, TEE_MATTR_MEM_TYPE_TAGGED);
200f9cd31c5SJelle Sels 	fobj_put(fobj);
201f9cd31c5SJelle Sels 	if (!mobj)
202f9cd31c5SJelle Sels 		return TEE_ERROR_OUT_OF_MEMORY;
203f9cd31c5SJelle Sels 
204f9cd31c5SJelle Sels 	res = vm_map(&spc->uctx, va, num_pgs * SMALL_PAGE_SIZE,
205f9cd31c5SJelle Sels 		     prot, 0, mobj, 0);
206f9cd31c5SJelle Sels 	if (res)
207f9cd31c5SJelle Sels 		mobj_put(mobj);
208f9cd31c5SJelle Sels 
209f9cd31c5SJelle Sels 	return TEE_SUCCESS;
210f9cd31c5SJelle Sels }
211f9cd31c5SJelle Sels 
zalloc(void * opaque __unused,unsigned int items,unsigned int size)212f9cd31c5SJelle Sels static void *zalloc(void *opaque __unused, unsigned int items,
213f9cd31c5SJelle Sels 		    unsigned int size)
214f9cd31c5SJelle Sels {
215a951fe52SEtienne Carriere 	return mempool_alloc(mempool_default, items * size);
216f9cd31c5SJelle Sels }
217f9cd31c5SJelle Sels 
zfree(void * opaque __unused,void * address)218f9cd31c5SJelle Sels static void zfree(void *opaque __unused, void *address)
219f9cd31c5SJelle Sels {
220a951fe52SEtienne Carriere 	mempool_free(mempool_default, address);
221f9cd31c5SJelle Sels }
222f9cd31c5SJelle Sels 
uncompress_image(void * dst,size_t dst_size,void * src,size_t src_size)223f9cd31c5SJelle Sels static void uncompress_image(void *dst, size_t dst_size, void *src,
224f9cd31c5SJelle Sels 			     size_t src_size)
225f9cd31c5SJelle Sels {
226f9cd31c5SJelle Sels 	z_stream strm = {
227f9cd31c5SJelle Sels 		.next_in = src,
228f9cd31c5SJelle Sels 		.avail_in = src_size,
229f9cd31c5SJelle Sels 		.next_out = dst,
230f9cd31c5SJelle Sels 		.avail_out = dst_size,
231f9cd31c5SJelle Sels 		.zalloc = zalloc,
232f9cd31c5SJelle Sels 		.zfree = zfree,
233f9cd31c5SJelle Sels 	};
234f9cd31c5SJelle Sels 
235f9cd31c5SJelle Sels 	if (inflateInit(&strm) != Z_OK)
236f9cd31c5SJelle Sels 		panic("inflateInit");
237f9cd31c5SJelle Sels 
238f9cd31c5SJelle Sels 	if (inflate(&strm, Z_SYNC_FLUSH) != Z_STREAM_END)
239f9cd31c5SJelle Sels 		panic("inflate");
240f9cd31c5SJelle Sels 
241f9cd31c5SJelle Sels 	if (inflateEnd(&strm) != Z_OK)
242f9cd31c5SJelle Sels 		panic("inflateEnd");
243f9cd31c5SJelle Sels }
244f9cd31c5SJelle Sels 
245aa6d7fc3SLevi Yun static struct efi_hob_handoff_info_table *
build_stmm_boot_hob_list(vaddr_t sp_addr,uint32_t sp_size,uint32_t * hob_table_size)246aa6d7fc3SLevi Yun build_stmm_boot_hob_list(vaddr_t sp_addr,
247aa6d7fc3SLevi Yun 			 uint32_t sp_size, uint32_t *hob_table_size)
248f9cd31c5SJelle Sels {
249aa6d7fc3SLevi Yun 	struct efi_hob_handoff_info_table *hob_table = NULL;
250f9cd31c5SJelle Sels 	unsigned int uncompressed_size_roundup = 0;
251aa6d7fc3SLevi Yun 	struct efi_mmram_descriptor *mmram_desc_data = NULL;
252aa6d7fc3SLevi Yun 	struct efi_mmram_hob_descriptor_block *mmram_resv_data = NULL;
253aa6d7fc3SLevi Yun 	uint16_t mmram_resv_data_size = 0;
254aa6d7fc3SLevi Yun 	TEE_Result ret = TEE_ERROR_GENERIC;
255aa6d7fc3SLevi Yun 	uint32_t hob_table_offset = 0;
256aa6d7fc3SLevi Yun 	void *guid_hob_data = NULL;
257f9cd31c5SJelle Sels 
258f9cd31c5SJelle Sels 	uncompressed_size_roundup = ROUNDUP(stmm_image_uncompressed_size,
259f9cd31c5SJelle Sels 					    SMALL_PAGE_SIZE);
260aa6d7fc3SLevi Yun 	stmm_image_addr = sp_addr;
261aa6d7fc3SLevi Yun 	stmm_heap_addr = stmm_image_addr + uncompressed_size_roundup;
262aa6d7fc3SLevi Yun 	stmm_sec_buf_addr = stmm_heap_addr + stmm_heap_size;
263aa6d7fc3SLevi Yun 	hob_table_offset = sizeof(struct ffa_boot_info_header_1_1) +
264aa6d7fc3SLevi Yun 			   sizeof(struct ffa_boot_info_1_1);
265aa6d7fc3SLevi Yun 
266aa6d7fc3SLevi Yun 	hob_table = efi_create_hob_list(sp_addr, sp_size,
267aa6d7fc3SLevi Yun 					stmm_sec_buf_addr + hob_table_offset,
268aa6d7fc3SLevi Yun 					stmm_sec_buf_size - hob_table_offset);
269aa6d7fc3SLevi Yun 	if (!hob_table) {
270aa6d7fc3SLevi Yun 		EMSG("Failed to create hob_table.");
271aa6d7fc3SLevi Yun 		return NULL;
272aa6d7fc3SLevi Yun 	}
273aa6d7fc3SLevi Yun 
274aa6d7fc3SLevi Yun 	ret = efi_create_fv_hob(hob_table, sp_addr, uncompressed_size_roundup);
275aa6d7fc3SLevi Yun 	if (ret) {
276aa6d7fc3SLevi Yun 		EMSG("Failed to create fv hob.");
277aa6d7fc3SLevi Yun 		return NULL;
278aa6d7fc3SLevi Yun 	}
279aa6d7fc3SLevi Yun 
280aa6d7fc3SLevi Yun 	ret = efi_create_guid_hob(hob_table, &ns_buf_guid,
281aa6d7fc3SLevi Yun 				  sizeof(struct efi_mmram_descriptor),
282aa6d7fc3SLevi Yun 				  &guid_hob_data);
283aa6d7fc3SLevi Yun 	if (ret) {
284aa6d7fc3SLevi Yun 		EMSG("Failed to create ns buffer hob.");
285aa6d7fc3SLevi Yun 		return NULL;
286aa6d7fc3SLevi Yun 	}
287aa6d7fc3SLevi Yun 
288aa6d7fc3SLevi Yun 	mmram_desc_data = guid_hob_data;
289aa6d7fc3SLevi Yun 	mmram_desc_data->physical_start = stmm_ns_comm_buf_addr;
290aa6d7fc3SLevi Yun 	mmram_desc_data->physical_size = stmm_ns_comm_buf_size;
291aa6d7fc3SLevi Yun 	mmram_desc_data->cpu_start = stmm_ns_comm_buf_addr;
292aa6d7fc3SLevi Yun 	mmram_desc_data->region_state = EFI_CACHEABLE | EFI_ALLOCATED;
293aa6d7fc3SLevi Yun 
294aa6d7fc3SLevi Yun 	mmram_resv_data_size = sizeof(struct efi_mmram_hob_descriptor_block) +
295aa6d7fc3SLevi Yun 			       sizeof(struct efi_mmram_descriptor) * 5;
296aa6d7fc3SLevi Yun 
297aa6d7fc3SLevi Yun 	ret = efi_create_guid_hob(hob_table, &mmram_resv_guid,
298aa6d7fc3SLevi Yun 				  mmram_resv_data_size, &guid_hob_data);
299aa6d7fc3SLevi Yun 	if (ret) {
300aa6d7fc3SLevi Yun 		EMSG("Failed to create mm range hob");
301aa6d7fc3SLevi Yun 		return NULL;
302aa6d7fc3SLevi Yun 	}
303aa6d7fc3SLevi Yun 
304aa6d7fc3SLevi Yun 	mmram_resv_data = guid_hob_data;
305aa6d7fc3SLevi Yun 	mmram_resv_data->number_of_mm_reserved_regions = 4;
306aa6d7fc3SLevi Yun 	mmram_desc_data = &mmram_resv_data->descriptor[0];
307aa6d7fc3SLevi Yun 
308aa6d7fc3SLevi Yun 	mmram_desc_data[0].physical_start = stmm_image_addr;
309aa6d7fc3SLevi Yun 	mmram_desc_data[0].physical_size = uncompressed_size_roundup;
310aa6d7fc3SLevi Yun 	mmram_desc_data[0].cpu_start = stmm_image_addr;
311aa6d7fc3SLevi Yun 	mmram_desc_data[0].region_state = EFI_CACHEABLE | EFI_ALLOCATED;
312aa6d7fc3SLevi Yun 
313aa6d7fc3SLevi Yun 	mmram_desc_data[1].physical_start = stmm_sec_buf_addr;
314aa6d7fc3SLevi Yun 	mmram_desc_data[1].physical_size = stmm_sec_buf_size;
315aa6d7fc3SLevi Yun 	mmram_desc_data[1].cpu_start = stmm_sec_buf_addr;
316aa6d7fc3SLevi Yun 	mmram_desc_data[1].region_state = EFI_CACHEABLE | EFI_ALLOCATED;
317aa6d7fc3SLevi Yun 
318aa6d7fc3SLevi Yun 	mmram_desc_data[2].physical_start = stmm_ns_comm_buf_addr;
319aa6d7fc3SLevi Yun 	mmram_desc_data[2].physical_size = stmm_ns_comm_buf_size;
320aa6d7fc3SLevi Yun 	mmram_desc_data[2].cpu_start = stmm_ns_comm_buf_addr;
321aa6d7fc3SLevi Yun 	mmram_desc_data[2].region_state = EFI_CACHEABLE | EFI_ALLOCATED;
322aa6d7fc3SLevi Yun 
323aa6d7fc3SLevi Yun 	mmram_desc_data[3].physical_start = stmm_heap_addr;
324aa6d7fc3SLevi Yun 	mmram_desc_data[3].physical_size = stmm_heap_size;
325aa6d7fc3SLevi Yun 	mmram_desc_data[3].cpu_start = stmm_heap_addr;
326aa6d7fc3SLevi Yun 	mmram_desc_data[3].region_state = EFI_CACHEABLE;
327aa6d7fc3SLevi Yun 
328aa6d7fc3SLevi Yun 	*hob_table_size = hob_table->efi_free_memory_bottom -
329aa6d7fc3SLevi Yun 			  (efi_physical_address_t)hob_table;
330aa6d7fc3SLevi Yun 
331aa6d7fc3SLevi Yun 	return hob_table;
332aa6d7fc3SLevi Yun }
333aa6d7fc3SLevi Yun 
load_stmm(struct stmm_ctx * spc)334aa6d7fc3SLevi Yun static TEE_Result load_stmm(struct stmm_ctx *spc)
335aa6d7fc3SLevi Yun {
336aa6d7fc3SLevi Yun 	struct ffa_boot_info_header_1_1 *hdr = NULL;
337aa6d7fc3SLevi Yun 	struct ffa_boot_info_1_1 *desc = NULL;
338aa6d7fc3SLevi Yun 	struct efi_hob_handoff_info_table *hob_table = NULL;
339aa6d7fc3SLevi Yun 	uint32_t hob_table_size = 0;
340aa6d7fc3SLevi Yun 	vaddr_t sp_addr = 0;
341aa6d7fc3SLevi Yun 	unsigned int sp_size = 0;
342aa6d7fc3SLevi Yun 	unsigned int uncompressed_size_roundup = 0;
343aa6d7fc3SLevi Yun 	TEE_Result res = TEE_ERROR_GENERIC;
344aa6d7fc3SLevi Yun 
345aa6d7fc3SLevi Yun 	uncompressed_size_roundup = ROUNDUP(stmm_image_uncompressed_size,
346aa6d7fc3SLevi Yun 					    SMALL_PAGE_SIZE);
347aa6d7fc3SLevi Yun 	sp_size = uncompressed_size_roundup + stmm_heap_size +
348aa6d7fc3SLevi Yun 		  stmm_sec_buf_size;
349f9cd31c5SJelle Sels 	res = alloc_and_map_sp_fobj(spc, sp_size,
350f9cd31c5SJelle Sels 				    TEE_MATTR_PRW, &sp_addr);
351f9cd31c5SJelle Sels 	if (res)
352f9cd31c5SJelle Sels 		return res;
353f9cd31c5SJelle Sels 
354f9cd31c5SJelle Sels 	res = alloc_and_map_sp_fobj(spc, stmm_ns_comm_buf_size,
355f9cd31c5SJelle Sels 				    TEE_MATTR_URW | TEE_MATTR_PRW,
356aa6d7fc3SLevi Yun 				    &stmm_ns_comm_buf_addr);
357f9cd31c5SJelle Sels 	/*
358f9cd31c5SJelle Sels 	 * We don't need to free the previous instance here, they'll all be
359f9cd31c5SJelle Sels 	 * handled during the destruction call (stmm_ctx_destroy())
360f9cd31c5SJelle Sels 	 */
361f9cd31c5SJelle Sels 	if (res)
362f9cd31c5SJelle Sels 		return res;
363f9cd31c5SJelle Sels 
364aa6d7fc3SLevi Yun 	hob_table = build_stmm_boot_hob_list(sp_addr, sp_size, &hob_table_size);
365aa6d7fc3SLevi Yun 	if (!hob_table)
366aa6d7fc3SLevi Yun 		return TEE_ERROR_NO_DATA;
367aa6d7fc3SLevi Yun 
368aa6d7fc3SLevi Yun 	hdr = (void *)stmm_sec_buf_addr;
369aa6d7fc3SLevi Yun 
370aa6d7fc3SLevi Yun 	hdr->signature = FFA_BOOT_INFO_SIGNATURE;
371aa6d7fc3SLevi Yun 	hdr->version = FFA_VERSION_1_2;
372aa6d7fc3SLevi Yun 	hdr->desc_size = sizeof(struct ffa_boot_info_1_1);
373aa6d7fc3SLevi Yun 	hdr->desc_count = 1;
374aa6d7fc3SLevi Yun 	hdr->desc_offset = sizeof(struct ffa_boot_info_header_1_1);
375aa6d7fc3SLevi Yun 	hdr->reserved = 0;
376aa6d7fc3SLevi Yun 	hdr->blob_size = hdr->desc_size * hdr->desc_count + hdr->desc_offset;
377aa6d7fc3SLevi Yun 
378aa6d7fc3SLevi Yun 	desc = (void *)(stmm_sec_buf_addr + hdr->desc_offset);
379aa6d7fc3SLevi Yun 
380aa6d7fc3SLevi Yun 	memset(desc->name, 0, FFA_BOOT_INFO_NAME_LEN);
381aa6d7fc3SLevi Yun 	desc->type = FFA_BOOT_INFO_TYPE_ID_HOB;
382aa6d7fc3SLevi Yun 	desc->flags = FFA_BOOT_INFO_FLAG_NAME_FORMAT_UUID |
383aa6d7fc3SLevi Yun 				    (FFA_BOOT_INFO_FLAG_CONTENT_FORMAT_ADDR <<
384aa6d7fc3SLevi Yun 				    FFA_BOOT_INFO_FLAG_CONTENT_FORMAT_SHIFT);
385aa6d7fc3SLevi Yun 	desc->size = hob_table_size;
386aa6d7fc3SLevi Yun 	desc->contents = (vaddr_t)hob_table;
387f9cd31c5SJelle Sels 
388f9cd31c5SJelle Sels 	vm_set_ctx(&spc->ta_ctx.ts_ctx);
389aa6d7fc3SLevi Yun 	uncompress_image((void *)stmm_image_addr, stmm_image_uncompressed_size,
390f9cd31c5SJelle Sels 			 stmm_image, stmm_image_size);
391f9cd31c5SJelle Sels 
392aa6d7fc3SLevi Yun 	res = vm_set_prot(&spc->uctx, stmm_image_addr,
393aa6d7fc3SLevi Yun 			  uncompressed_size_roundup,
394f9cd31c5SJelle Sels 			  TEE_MATTR_URX | TEE_MATTR_PR);
395f9cd31c5SJelle Sels 	if (res)
396f9cd31c5SJelle Sels 		return res;
397f9cd31c5SJelle Sels 
398aa6d7fc3SLevi Yun 	res = vm_set_prot(&spc->uctx, stmm_heap_addr, stmm_heap_size,
399f9cd31c5SJelle Sels 			  TEE_MATTR_URW | TEE_MATTR_PRW);
400f9cd31c5SJelle Sels 	if (res)
401f9cd31c5SJelle Sels 		return res;
402f9cd31c5SJelle Sels 
403aa6d7fc3SLevi Yun 	res = vm_set_prot(&spc->uctx, stmm_sec_buf_addr, stmm_sec_buf_size,
404f9cd31c5SJelle Sels 			  TEE_MATTR_URW | TEE_MATTR_PRW);
405f9cd31c5SJelle Sels 	if (res)
406f9cd31c5SJelle Sels 		return res;
407f9cd31c5SJelle Sels 
408aa6d7fc3SLevi Yun 	DMSG("stmm load address %#"PRIxVA, stmm_image_addr);
409f9cd31c5SJelle Sels 
410aa6d7fc3SLevi Yun 	spc->ns_comm_buf_addr = stmm_ns_comm_buf_addr;
411f9cd31c5SJelle Sels 	spc->ns_comm_buf_size = stmm_ns_comm_buf_size;
412f9cd31c5SJelle Sels 
413aa6d7fc3SLevi Yun 	init_stmm_regs(spc, (unsigned long)hdr, 0, 0, 0, stmm_image_addr);
414f9cd31c5SJelle Sels 
415f9cd31c5SJelle Sels 	return stmm_enter_user_mode(spc);
416f9cd31c5SJelle Sels }
417f9cd31c5SJelle Sels 
stmm_init_session(const TEE_UUID * uuid,struct tee_ta_session * sess)418f9cd31c5SJelle Sels TEE_Result stmm_init_session(const TEE_UUID *uuid, struct tee_ta_session *sess)
419f9cd31c5SJelle Sels {
420f9cd31c5SJelle Sels 	struct stmm_ctx *spc = NULL;
421956c2d50SEtienne Carriere 
422956c2d50SEtienne Carriere 	/* Caller is expected to hold tee_ta_mutex for safe changes in @sess */
423956c2d50SEtienne Carriere 	assert(mutex_is_locked(&tee_ta_mutex));
424f9cd31c5SJelle Sels 
425f9cd31c5SJelle Sels 	if (memcmp(uuid, &stmm_uuid, sizeof(*uuid)))
426f9cd31c5SJelle Sels 		return TEE_ERROR_ITEM_NOT_FOUND;
427f9cd31c5SJelle Sels 
428f9cd31c5SJelle Sels 	spc = stmm_alloc_ctx(uuid);
429f9cd31c5SJelle Sels 	if (!spc)
430f9cd31c5SJelle Sels 		return TEE_ERROR_OUT_OF_MEMORY;
431f9cd31c5SJelle Sels 
432fee55718SEtienne Carriere 	spc->ta_ctx.is_initializing = true;
433f9cd31c5SJelle Sels 
434f9cd31c5SJelle Sels 	sess->ts_sess.ctx = &spc->ta_ctx.ts_ctx;
435ab5363c6SJens Wiklander 	sess->ts_sess.handle_scall = sess->ts_sess.ctx->ops->handle_scall;
436956c2d50SEtienne Carriere 
437956c2d50SEtienne Carriere 	return TEE_SUCCESS;
438956c2d50SEtienne Carriere }
439956c2d50SEtienne Carriere 
stmm_complete_session(struct tee_ta_session * sess)440956c2d50SEtienne Carriere TEE_Result stmm_complete_session(struct tee_ta_session *sess)
441956c2d50SEtienne Carriere {
442956c2d50SEtienne Carriere 	struct stmm_ctx *spc = to_stmm_ctx(sess->ts_sess.ctx);
443aa6d7fc3SLevi Yun 	TEE_Result res = TEE_ERROR_GENERIC;
444f9cd31c5SJelle Sels 
445f9cd31c5SJelle Sels 	ts_push_current_session(&sess->ts_sess);
446f9cd31c5SJelle Sels 	res = load_stmm(spc);
447f9cd31c5SJelle Sels 	ts_pop_current_session();
448f9cd31c5SJelle Sels 	vm_set_ctx(NULL);
449f9cd31c5SJelle Sels 	if (res) {
450f9cd31c5SJelle Sels 		sess->ts_sess.ctx = NULL;
451f9cd31c5SJelle Sels 		spc->ta_ctx.ts_ctx.ops->destroy(&spc->ta_ctx.ts_ctx);
452f9cd31c5SJelle Sels 
453f9cd31c5SJelle Sels 		return res;
454f9cd31c5SJelle Sels 	}
455f9cd31c5SJelle Sels 
456f9cd31c5SJelle Sels 	mutex_lock(&tee_ta_mutex);
457fee55718SEtienne Carriere 	spc->ta_ctx.is_initializing = false;
458f9cd31c5SJelle Sels 	TAILQ_INSERT_TAIL(&tee_ctxes, &spc->ta_ctx, link);
459f9cd31c5SJelle Sels 	mutex_unlock(&tee_ta_mutex);
460f9cd31c5SJelle Sels 
461f9cd31c5SJelle Sels 	return TEE_SUCCESS;
462f9cd31c5SJelle Sels }
463f9cd31c5SJelle Sels 
stmm_enter_open_session(struct ts_session * s)464f9cd31c5SJelle Sels static TEE_Result stmm_enter_open_session(struct ts_session *s)
465f9cd31c5SJelle Sels {
466f9cd31c5SJelle Sels 	struct stmm_ctx *spc = to_stmm_ctx(s->ctx);
467f9cd31c5SJelle Sels 	struct tee_ta_session *ta_sess = to_ta_session(s);
468f9cd31c5SJelle Sels 	const uint32_t exp_pt = TEE_PARAM_TYPES(TEE_PARAM_TYPE_NONE,
469f9cd31c5SJelle Sels 						TEE_PARAM_TYPE_NONE,
470f9cd31c5SJelle Sels 						TEE_PARAM_TYPE_NONE,
471f9cd31c5SJelle Sels 						TEE_PARAM_TYPE_NONE);
472f9cd31c5SJelle Sels 
473f9cd31c5SJelle Sels 	if (ta_sess->param->types != exp_pt)
474f9cd31c5SJelle Sels 		return TEE_ERROR_BAD_PARAMETERS;
475f9cd31c5SJelle Sels 
476fee55718SEtienne Carriere 	if (spc->ta_ctx.is_initializing) {
477f9cd31c5SJelle Sels 		/* StMM is initialized in stmm_init_session() */
478f9cd31c5SJelle Sels 		ta_sess->err_origin = TEE_ORIGIN_TEE;
479f9cd31c5SJelle Sels 		return TEE_ERROR_BAD_STATE;
480f9cd31c5SJelle Sels 	}
481f9cd31c5SJelle Sels 
482f9cd31c5SJelle Sels 	return TEE_SUCCESS;
483f9cd31c5SJelle Sels }
484f9cd31c5SJelle Sels 
stmm_enter_invoke_cmd(struct ts_session * s,uint32_t cmd)485f9cd31c5SJelle Sels static TEE_Result stmm_enter_invoke_cmd(struct ts_session *s, uint32_t cmd)
486f9cd31c5SJelle Sels {
487f9cd31c5SJelle Sels 	struct stmm_ctx *spc = to_stmm_ctx(s->ctx);
488f9cd31c5SJelle Sels 	struct tee_ta_session *ta_sess = to_ta_session(s);
489f9cd31c5SJelle Sels 	TEE_Result res = TEE_SUCCESS;
490f9cd31c5SJelle Sels 	TEE_Result __maybe_unused tmp_res = TEE_SUCCESS;
491f9cd31c5SJelle Sels 	unsigned int ns_buf_size = 0;
492f9cd31c5SJelle Sels 	struct param_mem *mem = NULL;
493f9cd31c5SJelle Sels 	void *va = NULL;
494f9cd31c5SJelle Sels 	const uint32_t exp_pt = TEE_PARAM_TYPES(TEE_PARAM_TYPE_MEMREF_INOUT,
495f9cd31c5SJelle Sels 						TEE_PARAM_TYPE_VALUE_OUTPUT,
496f9cd31c5SJelle Sels 						TEE_PARAM_TYPE_NONE,
497f9cd31c5SJelle Sels 						TEE_PARAM_TYPE_NONE);
498f9cd31c5SJelle Sels 
499f9cd31c5SJelle Sels 	if (cmd != PTA_STMM_CMD_COMMUNICATE)
500f9cd31c5SJelle Sels 		return TEE_ERROR_BAD_PARAMETERS;
501f9cd31c5SJelle Sels 
502f9cd31c5SJelle Sels 	if (ta_sess->param->types != exp_pt)
503f9cd31c5SJelle Sels 		return TEE_ERROR_BAD_PARAMETERS;
504f9cd31c5SJelle Sels 
505f9cd31c5SJelle Sels 	mem = &ta_sess->param->u[0].mem;
506f9cd31c5SJelle Sels 	ns_buf_size = mem->size;
507f9cd31c5SJelle Sels 	if (ns_buf_size > spc->ns_comm_buf_size) {
508f9cd31c5SJelle Sels 		mem->size = spc->ns_comm_buf_size;
509f9cd31c5SJelle Sels 		return TEE_ERROR_EXCESS_DATA;
510f9cd31c5SJelle Sels 	}
511f9cd31c5SJelle Sels 
512f9cd31c5SJelle Sels 	res = mobj_inc_map(mem->mobj);
513f9cd31c5SJelle Sels 	if (res)
514f9cd31c5SJelle Sels 		return res;
515f9cd31c5SJelle Sels 
5169c4aaf67SJens Wiklander 	va = mobj_get_va(mem->mobj, mem->offs, mem->size);
517f9cd31c5SJelle Sels 	if (!va) {
518f9cd31c5SJelle Sels 		EMSG("Can't get a valid VA for NS buffer");
519f9cd31c5SJelle Sels 		res = TEE_ERROR_BAD_PARAMETERS;
520f9cd31c5SJelle Sels 		goto out_va;
521f9cd31c5SJelle Sels 	}
522f9cd31c5SJelle Sels 
523d9339333SEtienne Carriere #ifdef ARM64
524d9339333SEtienne Carriere 	spc->regs.x[0] = __FFA_MSG_SEND_DIRECT_REQ;
525f9cd31c5SJelle Sels 	spc->regs.x[1] = (stmm_pta_id << 16) | stmm_id;
526f9cd31c5SJelle Sels 	spc->regs.x[2] = FFA_PARAM_MBZ;
527f9cd31c5SJelle Sels 	spc->regs.x[3] = spc->ns_comm_buf_addr;
528f9cd31c5SJelle Sels 	spc->regs.x[4] = ns_buf_size;
529f9cd31c5SJelle Sels 	spc->regs.x[5] = 0;
530f9cd31c5SJelle Sels 	spc->regs.x[6] = 0;
531f9cd31c5SJelle Sels 	spc->regs.x[7] = 0;
532d9339333SEtienne Carriere #endif
533d9339333SEtienne Carriere #ifdef ARM32
534d9339333SEtienne Carriere 	spc->regs.r0 = __FFA_MSG_SEND_DIRECT_REQ;
535d9339333SEtienne Carriere 	spc->regs.r1 = (stmm_pta_id << 16) | stmm_id;
536d9339333SEtienne Carriere 	spc->regs.r2 = FFA_PARAM_MBZ;
537d9339333SEtienne Carriere 	spc->regs.r3 = spc->ns_comm_buf_addr;
538d9339333SEtienne Carriere 	spc->regs.r4 = ns_buf_size;
539d9339333SEtienne Carriere 	spc->regs.r5 = 0;
540d9339333SEtienne Carriere 	spc->regs.r6 = 0;
541d9339333SEtienne Carriere 	spc->regs.r7 = 0;
542d9339333SEtienne Carriere #endif
543f9cd31c5SJelle Sels 
544f9cd31c5SJelle Sels 	ts_push_current_session(s);
545f9cd31c5SJelle Sels 
546f9cd31c5SJelle Sels 	memcpy((void *)spc->ns_comm_buf_addr, va, ns_buf_size);
547f9cd31c5SJelle Sels 
548f9cd31c5SJelle Sels 	res = stmm_enter_user_mode(spc);
549f9cd31c5SJelle Sels 	if (res)
550f9cd31c5SJelle Sels 		goto out_session;
551f9cd31c5SJelle Sels 	/*
552f9cd31c5SJelle Sels 	 * Copy the SPM response from secure partition back to the non-secure
553f9cd31c5SJelle Sels 	 * buffer of the client that called us.
554f9cd31c5SJelle Sels 	 */
555d9339333SEtienne Carriere #ifdef ARM64
556f9cd31c5SJelle Sels 	ta_sess->param->u[1].val.a = spc->regs.x[4];
557d9339333SEtienne Carriere #endif
558d9339333SEtienne Carriere #ifdef ARM32
559d9339333SEtienne Carriere 	ta_sess->param->u[1].val.a = spc->regs.r4;
560d9339333SEtienne Carriere #endif
561f9cd31c5SJelle Sels 
562f9cd31c5SJelle Sels 	memcpy(va, (void *)spc->ns_comm_buf_addr, ns_buf_size);
563f9cd31c5SJelle Sels 
564f9cd31c5SJelle Sels out_session:
565f9cd31c5SJelle Sels 	ts_pop_current_session();
566f9cd31c5SJelle Sels out_va:
567f9cd31c5SJelle Sels 	tmp_res = mobj_dec_map(mem->mobj);
568f9cd31c5SJelle Sels 	assert(!tmp_res);
569f9cd31c5SJelle Sels 
570f9cd31c5SJelle Sels 	return res;
571f9cd31c5SJelle Sels }
572f9cd31c5SJelle Sels 
stmm_enter_close_session(struct ts_session * s __unused)573f9cd31c5SJelle Sels static void stmm_enter_close_session(struct ts_session *s __unused)
574f9cd31c5SJelle Sels {
575f9cd31c5SJelle Sels }
576f9cd31c5SJelle Sels 
stmm_dump_state(struct ts_ctx * ctx)577f9cd31c5SJelle Sels static void stmm_dump_state(struct ts_ctx *ctx)
578f9cd31c5SJelle Sels {
579f9cd31c5SJelle Sels 	user_mode_ctx_print_mappings(to_user_mode_ctx(ctx));
580f9cd31c5SJelle Sels }
581409c619bSEtienne Carriere DECLARE_KEEP_PAGER(stmm_dump_state);
582f9cd31c5SJelle Sels 
stmm_get_instance_id(struct ts_ctx * ctx)583f9cd31c5SJelle Sels static uint32_t stmm_get_instance_id(struct ts_ctx *ctx)
584f9cd31c5SJelle Sels {
585f9cd31c5SJelle Sels 	return to_stmm_ctx(ctx)->uctx.vm_info.asid;
586f9cd31c5SJelle Sels }
587f9cd31c5SJelle Sels 
stmm_ctx_destroy(struct ts_ctx * ctx)588f9cd31c5SJelle Sels static void stmm_ctx_destroy(struct ts_ctx *ctx)
589f9cd31c5SJelle Sels {
590f9cd31c5SJelle Sels 	struct stmm_ctx *spc = to_stmm_ctx(ctx);
591f9cd31c5SJelle Sels 
592f9cd31c5SJelle Sels 	vm_info_final(&spc->uctx);
593f9cd31c5SJelle Sels 	free(spc);
594f9cd31c5SJelle Sels }
595f9cd31c5SJelle Sels 
596d9339333SEtienne Carriere #ifdef ARM64
save_sp_ctx(struct stmm_ctx * spc,struct thread_scall_regs * regs)597ab5363c6SJens Wiklander static void save_sp_ctx(struct stmm_ctx *spc,
598ab5363c6SJens Wiklander 			struct thread_scall_regs *regs)
599f9cd31c5SJelle Sels {
600f9cd31c5SJelle Sels 	size_t n = 0;
601f9cd31c5SJelle Sels 
602f9cd31c5SJelle Sels 	/* Save the return values from StMM */
603f9cd31c5SJelle Sels 	for (n = 0; n <= 7; n++)
604ab5363c6SJens Wiklander 		spc->regs.x[n] = *(&regs->x0 + n);
605f9cd31c5SJelle Sels 
606ab5363c6SJens Wiklander 	spc->regs.sp = regs->sp_el0;
607ab5363c6SJens Wiklander 	spc->regs.pc = regs->elr;
608ab5363c6SJens Wiklander 	spc->regs.cpsr = regs->spsr;
609f9cd31c5SJelle Sels }
610d9339333SEtienne Carriere #endif
611f9cd31c5SJelle Sels 
612d9339333SEtienne Carriere #ifdef ARM32
save_sp_ctx(struct stmm_ctx * spc,struct thread_scall_regs * regs)613ab5363c6SJens Wiklander static void save_sp_ctx(struct stmm_ctx *spc,
614ab5363c6SJens Wiklander 			struct thread_scall_regs *regs)
615d9339333SEtienne Carriere {
616ab5363c6SJens Wiklander 	spc->regs.r0 = regs->r0;
617ab5363c6SJens Wiklander 	spc->regs.r1 = regs->r1;
618ab5363c6SJens Wiklander 	spc->regs.r2 = regs->r2;
619ab5363c6SJens Wiklander 	spc->regs.r3 = regs->r3;
620ab5363c6SJens Wiklander 	spc->regs.r4 = regs->r4;
621ab5363c6SJens Wiklander 	spc->regs.r5 = regs->r5;
622ab5363c6SJens Wiklander 	spc->regs.r6 = regs->r6;
623ab5363c6SJens Wiklander 	spc->regs.r7 = regs->r7;
624ab5363c6SJens Wiklander 	spc->regs.pc = regs->lr;
625ab5363c6SJens Wiklander 	spc->regs.cpsr = regs->spsr;
626d9339333SEtienne Carriere 	spc->regs.usr_sp = thread_get_usr_sp();
627d9339333SEtienne Carriere }
628d9339333SEtienne Carriere #endif
629d9339333SEtienne Carriere 
return_from_sp_helper(bool panic,uint32_t panic_code,struct thread_scall_regs * regs)630c232eb8dSEtienne Carriere static void return_from_sp_helper(bool panic, uint32_t panic_code,
631ab5363c6SJens Wiklander 				  struct thread_scall_regs *regs)
632d9339333SEtienne Carriere {
633d9339333SEtienne Carriere 	struct ts_session *sess = ts_get_current_session();
634d9339333SEtienne Carriere 	struct stmm_ctx *spc = to_stmm_ctx(sess->ctx);
635d9339333SEtienne Carriere 
6364348e834SEtienne Carriere 	if (panic)
6374348e834SEtienne Carriere 		spc->ta_ctx.panicked = true;
6384348e834SEtienne Carriere 	else
639ab5363c6SJens Wiklander 		save_sp_ctx(spc, regs);
640d9339333SEtienne Carriere 
641ab5363c6SJens Wiklander 	SVC_REGS_A0(regs) = 0;
642ab5363c6SJens Wiklander 	SVC_REGS_A1(regs) = panic;
643ab5363c6SJens Wiklander 	SVC_REGS_A2(regs) = panic_code;
644f9cd31c5SJelle Sels }
645f9cd31c5SJelle Sels 
service_compose_direct_resp(struct thread_scall_regs * regs,uint32_t ret_val)646ab5363c6SJens Wiklander static void service_compose_direct_resp(struct thread_scall_regs *regs,
647f9cd31c5SJelle Sels 					uint32_t ret_val)
648f9cd31c5SJelle Sels {
649f9cd31c5SJelle Sels 	uint16_t src_id = 0;
650f9cd31c5SJelle Sels 	uint16_t dst_id = 0;
651f9cd31c5SJelle Sels 
652f9cd31c5SJelle Sels 	/* extract from request */
653d9339333SEtienne Carriere 	src_id = (SVC_REGS_A1(regs) >> 16) & UINT16_MAX;
654d9339333SEtienne Carriere 	dst_id = SVC_REGS_A1(regs) & UINT16_MAX;
655f9cd31c5SJelle Sels 
656f9cd31c5SJelle Sels 	/* compose message */
657d9339333SEtienne Carriere 	SVC_REGS_A0(regs) = __FFA_MSG_SEND_DIRECT_RESP;
658f9cd31c5SJelle Sels 	/* swap endpoint ids */
659d9339333SEtienne Carriere 	SVC_REGS_A1(regs) = SHIFT_U32(dst_id, 16) | src_id;
660d9339333SEtienne Carriere 	SVC_REGS_A2(regs) = FFA_PARAM_MBZ;
661d9339333SEtienne Carriere 	SVC_REGS_A3(regs) = ret_val;
662d9339333SEtienne Carriere 	SVC_REGS_A4(regs) = 0;
663d9339333SEtienne Carriere 	SVC_REGS_A5(regs) = 0;
664d9339333SEtienne Carriere 	SVC_REGS_A6(regs) = 0;
665d9339333SEtienne Carriere 	SVC_REGS_A7(regs) = 0;
666f9cd31c5SJelle Sels }
667f9cd31c5SJelle Sels 
668f9cd31c5SJelle Sels /*
669f9cd31c5SJelle Sels  * Combined read from secure partition, this will open, read and
670f9cd31c5SJelle Sels  * close the file object.
671f9cd31c5SJelle Sels  */
sec_storage_obj_read(unsigned long storage_id,char * obj_id,unsigned long obj_id_len,void * data,unsigned long len,unsigned long offset,unsigned long flags)672f9cd31c5SJelle Sels static TEE_Result sec_storage_obj_read(unsigned long storage_id, char *obj_id,
673f9cd31c5SJelle Sels 				       unsigned long obj_id_len, void *data,
674f9cd31c5SJelle Sels 				       unsigned long len, unsigned long offset,
675f9cd31c5SJelle Sels 				       unsigned long flags)
676f9cd31c5SJelle Sels {
677f9cd31c5SJelle Sels 	const struct tee_file_operations *fops = NULL;
678f9cd31c5SJelle Sels 	TEE_Result res = TEE_ERROR_BAD_STATE;
679f9cd31c5SJelle Sels 	struct ts_session *sess = NULL;
680f9cd31c5SJelle Sels 	struct tee_file_handle *fh = NULL;
681f9cd31c5SJelle Sels 	struct tee_pobj *po = NULL;
682f9cd31c5SJelle Sels 	size_t file_size = 0;
683f9cd31c5SJelle Sels 	size_t read_len = 0;
684f9cd31c5SJelle Sels 
685f9cd31c5SJelle Sels 	fops = tee_svc_storage_file_ops(storage_id);
686f9cd31c5SJelle Sels 	if (!fops)
687f9cd31c5SJelle Sels 		return TEE_ERROR_ITEM_NOT_FOUND;
688f9cd31c5SJelle Sels 
689f9cd31c5SJelle Sels 	if (obj_id_len > TEE_OBJECT_ID_MAX_LEN)
690f9cd31c5SJelle Sels 		return TEE_ERROR_BAD_PARAMETERS;
691f9cd31c5SJelle Sels 
692f9cd31c5SJelle Sels 	sess = ts_get_current_session();
693f9cd31c5SJelle Sels 
694f9cd31c5SJelle Sels 	res = tee_pobj_get(&sess->ctx->uuid, obj_id, obj_id_len, flags,
695d0989b48SEtienne Carriere 			   TEE_POBJ_USAGE_OPEN, fops, &po);
696f9cd31c5SJelle Sels 	if (res != TEE_SUCCESS)
697f9cd31c5SJelle Sels 		return res;
698f9cd31c5SJelle Sels 
699f9cd31c5SJelle Sels 	res = po->fops->open(po, &file_size, &fh);
700f9cd31c5SJelle Sels 	if (res != TEE_SUCCESS)
701f9cd31c5SJelle Sels 		goto out;
702f9cd31c5SJelle Sels 
703f9cd31c5SJelle Sels 	read_len = len;
704b2284b11SJens Wiklander 	res = po->fops->read(fh, offset, NULL, data, &read_len);
705f9cd31c5SJelle Sels 	if (res == TEE_ERROR_CORRUPT_OBJECT) {
706f9cd31c5SJelle Sels 		EMSG("Object corrupt");
707f9cd31c5SJelle Sels 		po->fops->remove(po);
708f9cd31c5SJelle Sels 	} else if (res == TEE_SUCCESS && len != read_len) {
709f9cd31c5SJelle Sels 		res = TEE_ERROR_CORRUPT_OBJECT;
710f9cd31c5SJelle Sels 	}
711f9cd31c5SJelle Sels 
712f9cd31c5SJelle Sels 	po->fops->close(&fh);
713f9cd31c5SJelle Sels 
714f9cd31c5SJelle Sels out:
715f9cd31c5SJelle Sels 	tee_pobj_release(po);
716f9cd31c5SJelle Sels 
717f9cd31c5SJelle Sels 	return res;
718f9cd31c5SJelle Sels }
719f9cd31c5SJelle Sels 
720f9cd31c5SJelle Sels /*
721f9cd31c5SJelle Sels  * Combined write from secure partition, this will create/open, write and
722f9cd31c5SJelle Sels  * close the file object.
723f9cd31c5SJelle Sels  */
sec_storage_obj_write(unsigned long storage_id,char * obj_id,unsigned long obj_id_len,void * data,unsigned long len,unsigned long offset,unsigned long flags)724f9cd31c5SJelle Sels static TEE_Result sec_storage_obj_write(unsigned long storage_id, char *obj_id,
725f9cd31c5SJelle Sels 					unsigned long obj_id_len, void *data,
726f9cd31c5SJelle Sels 					unsigned long len, unsigned long offset,
727f9cd31c5SJelle Sels 					unsigned long flags)
728f9cd31c5SJelle Sels 
729f9cd31c5SJelle Sels {
730f9cd31c5SJelle Sels 	const struct tee_file_operations *fops = NULL;
731f9cd31c5SJelle Sels 	struct ts_session *sess = NULL;
732f9cd31c5SJelle Sels 	struct tee_file_handle *fh = NULL;
733f9cd31c5SJelle Sels 	TEE_Result res = TEE_SUCCESS;
734f9cd31c5SJelle Sels 	struct tee_pobj *po = NULL;
735f9cd31c5SJelle Sels 
736f9cd31c5SJelle Sels 	fops = tee_svc_storage_file_ops(storage_id);
737f9cd31c5SJelle Sels 	if (!fops)
738f9cd31c5SJelle Sels 		return TEE_ERROR_ITEM_NOT_FOUND;
739f9cd31c5SJelle Sels 
740f9cd31c5SJelle Sels 	if (obj_id_len > TEE_OBJECT_ID_MAX_LEN)
741f9cd31c5SJelle Sels 		return TEE_ERROR_BAD_PARAMETERS;
742f9cd31c5SJelle Sels 
743f9cd31c5SJelle Sels 	sess = ts_get_current_session();
744f9cd31c5SJelle Sels 
745f9cd31c5SJelle Sels 	res = tee_pobj_get(&sess->ctx->uuid, obj_id, obj_id_len, flags,
746d0989b48SEtienne Carriere 			   TEE_POBJ_USAGE_OPEN, fops, &po);
747f9cd31c5SJelle Sels 	if (res != TEE_SUCCESS)
748f9cd31c5SJelle Sels 		return res;
749f9cd31c5SJelle Sels 
750f9cd31c5SJelle Sels 	res = po->fops->open(po, NULL, &fh);
751f9cd31c5SJelle Sels 	if (res == TEE_ERROR_ITEM_NOT_FOUND)
752b2284b11SJens Wiklander 		res = po->fops->create(po, false, NULL, 0, NULL, 0,
753b2284b11SJens Wiklander 				       NULL, NULL, 0, &fh);
754f9cd31c5SJelle Sels 	if (res == TEE_SUCCESS) {
755b2284b11SJens Wiklander 		res = po->fops->write(fh, offset, NULL, data, len);
756f9cd31c5SJelle Sels 		po->fops->close(&fh);
757f9cd31c5SJelle Sels 	}
758f9cd31c5SJelle Sels 
759f9cd31c5SJelle Sels 	tee_pobj_release(po);
760f9cd31c5SJelle Sels 
761f9cd31c5SJelle Sels 	return res;
762f9cd31c5SJelle Sels }
763f9cd31c5SJelle Sels 
tee2ffa_ret_val(TEE_Result res)764aa6d7fc3SLevi Yun static uint32_t tee2ffa_ret_val(TEE_Result res)
765c899c027SEtienne Carriere {
766c899c027SEtienne Carriere 	switch (res) {
767c899c027SEtienne Carriere 	case TEE_SUCCESS:
768aa6d7fc3SLevi Yun 		return FFA_OK;
769aa6d7fc3SLevi Yun 	case TEE_ERROR_NOT_IMPLEMENTED:
770c899c027SEtienne Carriere 	case TEE_ERROR_NOT_SUPPORTED:
771aa6d7fc3SLevi Yun 		return FFA_NOT_SUPPORTED;
772c899c027SEtienne Carriere 	case TEE_ERROR_OUT_OF_MEMORY:
773aa6d7fc3SLevi Yun 		return FFA_NO_MEMORY;
774aa6d7fc3SLevi Yun 	case TEE_ERROR_ACCESS_DENIED:
775aa6d7fc3SLevi Yun 		return FFA_DENIED;
776aa6d7fc3SLevi Yun 	case TEE_ERROR_NO_DATA:
777aa6d7fc3SLevi Yun 		return FFA_NO_DATA;
778c899c027SEtienne Carriere 	case TEE_ERROR_BAD_PARAMETERS:
779c899c027SEtienne Carriere 	default:
780aa6d7fc3SLevi Yun 		return FFA_INVALID_PARAMETERS;
781c899c027SEtienne Carriere 	}
782c899c027SEtienne Carriere }
783c899c027SEtienne Carriere 
spm_eret_error(int32_t error_code,struct thread_scall_regs * regs)784aa6d7fc3SLevi Yun static void spm_eret_error(int32_t error_code, struct thread_scall_regs *regs)
785aa6d7fc3SLevi Yun {
786aa6d7fc3SLevi Yun 	SVC_REGS_A0(regs) = FFA_ERROR;
787aa6d7fc3SLevi Yun 	SVC_REGS_A1(regs) = FFA_PARAM_MBZ;
788aa6d7fc3SLevi Yun 	SVC_REGS_A2(regs) = error_code;
789aa6d7fc3SLevi Yun 	SVC_REGS_A3(regs) = FFA_PARAM_MBZ;
790aa6d7fc3SLevi Yun 	SVC_REGS_A4(regs) = FFA_PARAM_MBZ;
791aa6d7fc3SLevi Yun 	SVC_REGS_A5(regs) = FFA_PARAM_MBZ;
792aa6d7fc3SLevi Yun 	SVC_REGS_A6(regs) = FFA_PARAM_MBZ;
793aa6d7fc3SLevi Yun 	SVC_REGS_A7(regs) = FFA_PARAM_MBZ;
794aa6d7fc3SLevi Yun }
795aa6d7fc3SLevi Yun 
796f9cd31c5SJelle Sels #define FILENAME "EFI_VARS"
stmm_handle_storage_service(struct thread_scall_regs * regs)797ab5363c6SJens Wiklander static void stmm_handle_storage_service(struct thread_scall_regs *regs)
798f9cd31c5SJelle Sels {
799f9cd31c5SJelle Sels 	uint32_t flags = TEE_DATA_FLAG_ACCESS_READ |
800f9cd31c5SJelle Sels 			 TEE_DATA_FLAG_ACCESS_WRITE |
801f9cd31c5SJelle Sels 			 TEE_DATA_FLAG_SHARE_READ |
802f9cd31c5SJelle Sels 			 TEE_DATA_FLAG_SHARE_WRITE;
803d9339333SEtienne Carriere 	uint32_t action = SVC_REGS_A3(regs);
804d9339333SEtienne Carriere 	void *va = (void *)SVC_REGS_A4(regs);
805d9339333SEtienne Carriere 	unsigned long len = SVC_REGS_A5(regs);
806d9339333SEtienne Carriere 	unsigned long offset = SVC_REGS_A6(regs);
807f9cd31c5SJelle Sels 	char obj_id[] = FILENAME;
808f9cd31c5SJelle Sels 	size_t obj_id_len = strlen(obj_id);
809f9cd31c5SJelle Sels 	TEE_Result res = TEE_SUCCESS;
810c899c027SEtienne Carriere 	uint32_t stmm_rc = STMM_RET_INVALID_PARAM;
811f9cd31c5SJelle Sels 
812f9cd31c5SJelle Sels 	switch (action) {
813d9339333SEtienne Carriere 	case __FFA_SVC_RPMB_READ:
814d9339333SEtienne Carriere 		DMSG("RPMB read");
815f9cd31c5SJelle Sels 		res = sec_storage_obj_read(TEE_STORAGE_PRIVATE_RPMB, obj_id,
816f9cd31c5SJelle Sels 					   obj_id_len, va, len, offset, flags);
817aa6d7fc3SLevi Yun 		stmm_rc = tee2ffa_ret_val(res);
818c899c027SEtienne Carriere 		break;
819d9339333SEtienne Carriere 	case __FFA_SVC_RPMB_WRITE:
820d9339333SEtienne Carriere 		DMSG("RPMB write");
821f9cd31c5SJelle Sels 		res = sec_storage_obj_write(TEE_STORAGE_PRIVATE_RPMB, obj_id,
822f9cd31c5SJelle Sels 					    obj_id_len, va, len, offset, flags);
823aa6d7fc3SLevi Yun 		stmm_rc = tee2ffa_ret_val(res);
824c899c027SEtienne Carriere 		break;
825f9cd31c5SJelle Sels 	default:
826f9cd31c5SJelle Sels 		EMSG("Undefined service id %#"PRIx32, action);
827c899c027SEtienne Carriere 		break;
828f9cd31c5SJelle Sels 	}
829c899c027SEtienne Carriere 
830c899c027SEtienne Carriere 	service_compose_direct_resp(regs, stmm_rc);
831f9cd31c5SJelle Sels }
832f9cd31c5SJelle Sels 
spm_handle_direct_req(struct thread_scall_regs * regs)833ab5363c6SJens Wiklander static void spm_handle_direct_req(struct thread_scall_regs *regs)
834f9cd31c5SJelle Sels {
835d9339333SEtienne Carriere 	uint16_t dst_id = SVC_REGS_A1(regs) & UINT16_MAX;
836f9cd31c5SJelle Sels 
837aa6d7fc3SLevi Yun 	if (dst_id == ffa_storage_id) {
838c232eb8dSEtienne Carriere 		stmm_handle_storage_service(regs);
839c232eb8dSEtienne Carriere 	} else {
840f9cd31c5SJelle Sels 		EMSG("Undefined endpoint id %#"PRIx16, dst_id);
841c232eb8dSEtienne Carriere 		spm_eret_error(STMM_RET_INVALID_PARAM, regs);
842c232eb8dSEtienne Carriere 	}
843f9cd31c5SJelle Sels }
844f9cd31c5SJelle Sels 
spm_handle_get_mem_attr(struct thread_scall_regs * regs)845aa6d7fc3SLevi Yun static void spm_handle_get_mem_attr(struct thread_scall_regs *regs)
846aa6d7fc3SLevi Yun {
847aa6d7fc3SLevi Yun 	TEE_Result res = TEE_ERROR_GENERIC;
848aa6d7fc3SLevi Yun 	struct ts_session *sess = NULL;
849aa6d7fc3SLevi Yun 	struct stmm_ctx *spc = NULL;
850aa6d7fc3SLevi Yun 	uint16_t attrs = 0;
851aa6d7fc3SLevi Yun 	uint16_t perm = 0;
852aa6d7fc3SLevi Yun 	vaddr_t va = 0;
853aa6d7fc3SLevi Yun 	uint32_t ffa_ret = FFA_INVALID_PARAMETERS;
854aa6d7fc3SLevi Yun 
855aa6d7fc3SLevi Yun 	sess = ts_get_current_session();
856aa6d7fc3SLevi Yun 	spc = to_stmm_ctx(sess->ctx);
857aa6d7fc3SLevi Yun 
858aa6d7fc3SLevi Yun 	va = SVC_REGS_A1(regs);
859aa6d7fc3SLevi Yun 	if (!va)
860aa6d7fc3SLevi Yun 		goto err;
861aa6d7fc3SLevi Yun 
862aa6d7fc3SLevi Yun 	res = vm_get_prot(&spc->uctx, va, SMALL_PAGE_SIZE, &attrs);
863aa6d7fc3SLevi Yun 	if (res)
864aa6d7fc3SLevi Yun 		goto err;
865aa6d7fc3SLevi Yun 
866aa6d7fc3SLevi Yun 	if ((attrs & TEE_MATTR_URW) == TEE_MATTR_URW)
867aa6d7fc3SLevi Yun 		perm |= FFA_MEM_PERM_RW;
868aa6d7fc3SLevi Yun 	else if ((attrs & TEE_MATTR_UR) == TEE_MATTR_UR)
869aa6d7fc3SLevi Yun 		perm |= FFA_MEM_PERM_RO;
870aa6d7fc3SLevi Yun 
871aa6d7fc3SLevi Yun 	if (!(attrs & TEE_MATTR_UX))
872aa6d7fc3SLevi Yun 		perm |= FFA_MEM_PERM_NX;
873aa6d7fc3SLevi Yun 
874aa6d7fc3SLevi Yun 	SVC_REGS_A0(regs) = FFA_SUCCESS_32;
875aa6d7fc3SLevi Yun 	SVC_REGS_A1(regs) = FFA_PARAM_MBZ;
876aa6d7fc3SLevi Yun 	SVC_REGS_A2(regs) = perm;
877aa6d7fc3SLevi Yun 	SVC_REGS_A3(regs) = FFA_PARAM_MBZ;
878aa6d7fc3SLevi Yun 	SVC_REGS_A4(regs) = FFA_PARAM_MBZ;
879aa6d7fc3SLevi Yun 	SVC_REGS_A5(regs) = FFA_PARAM_MBZ;
880aa6d7fc3SLevi Yun 	SVC_REGS_A6(regs) = FFA_PARAM_MBZ;
881aa6d7fc3SLevi Yun 	SVC_REGS_A7(regs) = FFA_PARAM_MBZ;
882aa6d7fc3SLevi Yun 
883aa6d7fc3SLevi Yun 	return;
884aa6d7fc3SLevi Yun 
885aa6d7fc3SLevi Yun err:
886aa6d7fc3SLevi Yun 	spm_eret_error(ffa_ret, regs);
887aa6d7fc3SLevi Yun }
888aa6d7fc3SLevi Yun 
spm_handle_set_mem_attr(struct thread_scall_regs * regs)889aa6d7fc3SLevi Yun static void spm_handle_set_mem_attr(struct thread_scall_regs *regs)
890aa6d7fc3SLevi Yun {
891aa6d7fc3SLevi Yun 	TEE_Result res = TEE_ERROR_GENERIC;
892aa6d7fc3SLevi Yun 	struct ts_session *sess = NULL;
893aa6d7fc3SLevi Yun 	struct stmm_ctx *spc = NULL;
894aa6d7fc3SLevi Yun 	uintptr_t va = SVC_REGS_A1(regs);
895aa6d7fc3SLevi Yun 	uint32_t nr_pages = SVC_REGS_A2(regs);
896aa6d7fc3SLevi Yun 	uint32_t perm = SVC_REGS_A3(regs);
897aa6d7fc3SLevi Yun 	size_t sz = 0;
898aa6d7fc3SLevi Yun 	uint32_t prot = 0;
899aa6d7fc3SLevi Yun 	uint32_t ffa_ret = FFA_INVALID_PARAMETERS;
900aa6d7fc3SLevi Yun 
901aa6d7fc3SLevi Yun 	if (!va || !nr_pages ||
902aa6d7fc3SLevi Yun 	    MUL_OVERFLOW(nr_pages, SMALL_PAGE_SIZE, &sz) ||
903aa6d7fc3SLevi Yun 	    (perm & FFA_MEM_PERM_RESERVED))
904aa6d7fc3SLevi Yun 		goto err;
905aa6d7fc3SLevi Yun 
906aa6d7fc3SLevi Yun 	sess = ts_get_current_session();
907aa6d7fc3SLevi Yun 	spc = to_stmm_ctx(sess->ctx);
908aa6d7fc3SLevi Yun 
909aa6d7fc3SLevi Yun 	if ((perm & FFA_MEM_PERM_DATA_PERM) == FFA_MEM_PERM_RO)
910aa6d7fc3SLevi Yun 		prot |= TEE_MATTR_UR;
911aa6d7fc3SLevi Yun 	else if ((perm & FFA_MEM_PERM_DATA_PERM) == FFA_MEM_PERM_RW)
912aa6d7fc3SLevi Yun 		prot |= TEE_MATTR_URW;
913aa6d7fc3SLevi Yun 
914aa6d7fc3SLevi Yun 	if ((perm & FFA_MEM_PERM_INSTRUCTION_PERM) != FFA_MEM_PERM_NX)
915aa6d7fc3SLevi Yun 		prot |= TEE_MATTR_UX;
916aa6d7fc3SLevi Yun 
917aa6d7fc3SLevi Yun 	res = vm_set_prot(&spc->uctx, va, sz, prot);
918aa6d7fc3SLevi Yun 	if (res) {
919aa6d7fc3SLevi Yun 		ffa_ret = FFA_DENIED;
920aa6d7fc3SLevi Yun 		goto err;
921aa6d7fc3SLevi Yun 	}
922aa6d7fc3SLevi Yun 
923aa6d7fc3SLevi Yun 	SVC_REGS_A0(regs) = FFA_SUCCESS_32;
924aa6d7fc3SLevi Yun 	SVC_REGS_A1(regs) = FFA_PARAM_MBZ;
925aa6d7fc3SLevi Yun 	SVC_REGS_A2(regs) = FFA_PARAM_MBZ;
926aa6d7fc3SLevi Yun 	SVC_REGS_A3(regs) = FFA_PARAM_MBZ;
927aa6d7fc3SLevi Yun 	SVC_REGS_A4(regs) = FFA_PARAM_MBZ;
928aa6d7fc3SLevi Yun 	SVC_REGS_A5(regs) = FFA_PARAM_MBZ;
929aa6d7fc3SLevi Yun 	SVC_REGS_A6(regs) = FFA_PARAM_MBZ;
930aa6d7fc3SLevi Yun 	SVC_REGS_A7(regs) = FFA_PARAM_MBZ;
931aa6d7fc3SLevi Yun 
932aa6d7fc3SLevi Yun 	return;
933aa6d7fc3SLevi Yun 
934aa6d7fc3SLevi Yun err:
935aa6d7fc3SLevi Yun 	spm_eret_error(ffa_ret, regs);
936aa6d7fc3SLevi Yun }
937aa6d7fc3SLevi Yun 
938c232eb8dSEtienne Carriere /* Return true if returning to SP, false if returning to caller */
spm_handle_scall(struct thread_scall_regs * regs)939ab5363c6SJens Wiklander static bool spm_handle_scall(struct thread_scall_regs *regs)
940f9cd31c5SJelle Sels {
941d9339333SEtienne Carriere #ifdef ARM64
942d9339333SEtienne Carriere 	uint64_t *a0 = &regs->x0;
943d9339333SEtienne Carriere #endif
944d9339333SEtienne Carriere #ifdef ARM32
945d9339333SEtienne Carriere 	uint32_t *a0 = &regs->r0;
946d9339333SEtienne Carriere #endif
947d9339333SEtienne Carriere 
948d9339333SEtienne Carriere 	switch (*a0) {
949f9cd31c5SJelle Sels 	case FFA_VERSION:
950f9cd31c5SJelle Sels 		DMSG("Received FFA version");
951bef959c8SJens Wiklander 		*a0 = FFA_VERSION_1_2;
952f9cd31c5SJelle Sels 		return true;
953aa6d7fc3SLevi Yun 	case FFA_ID_GET:
954aa6d7fc3SLevi Yun 		DMSG("Received FFA ID GET");
955aa6d7fc3SLevi Yun 		SVC_REGS_A0(regs) = FFA_SUCCESS_32;
956aa6d7fc3SLevi Yun 		SVC_REGS_A2(regs) = stmm_id;
957aa6d7fc3SLevi Yun 		return true;
958aa6d7fc3SLevi Yun 	case FFA_MSG_WAIT:
959aa6d7fc3SLevi Yun 		DMSG("Received FFA_MSG_WAIT");
960aa6d7fc3SLevi Yun 		return_from_sp_helper(false, 0, regs);
961aa6d7fc3SLevi Yun 		return false;
962d9339333SEtienne Carriere 	case __FFA_MSG_SEND_DIRECT_RESP:
963f9cd31c5SJelle Sels 		DMSG("Received FFA direct response");
964c232eb8dSEtienne Carriere 		return_from_sp_helper(false, 0, regs);
965c232eb8dSEtienne Carriere 		return false;
966d9339333SEtienne Carriere 	case __FFA_MSG_SEND_DIRECT_REQ:
967f9cd31c5SJelle Sels 		DMSG("Received FFA direct request");
968c232eb8dSEtienne Carriere 		spm_handle_direct_req(regs);
969c232eb8dSEtienne Carriere 		return true;
970aa6d7fc3SLevi Yun 	case __FFA_MEM_PERM_GET:
971aa6d7fc3SLevi Yun 		DMSG("Received FFA mem perm get");
972aa6d7fc3SLevi Yun 		spm_handle_get_mem_attr(regs);
973aa6d7fc3SLevi Yun 		return true;
974aa6d7fc3SLevi Yun 	case __FFA_MEM_PERM_SET:
975aa6d7fc3SLevi Yun 		DMSG("Received FFA mem perm set");
976aa6d7fc3SLevi Yun 		spm_handle_set_mem_attr(regs);
977aa6d7fc3SLevi Yun 		return true;
978*ed89aa36SYeoreum Yun 	case FFA_ERROR:
979*ed89aa36SYeoreum Yun 		EMSG("Received FFA error");
980c232eb8dSEtienne Carriere 		return_from_sp_helper(true /*panic*/, 0xabcd, regs);
981c232eb8dSEtienne Carriere 		return false;
982*ed89aa36SYeoreum Yun 	default:
983*ed89aa36SYeoreum Yun 		DMSG("Undefined syscall %#"PRIx32, (uint32_t)*a0);
984*ed89aa36SYeoreum Yun 		spm_eret_error(FFA_NOT_SUPPORTED, regs);
985*ed89aa36SYeoreum Yun 		return true;
986f9cd31c5SJelle Sels 	}
987f9cd31c5SJelle Sels }
988f9cd31c5SJelle Sels 
9896abfa44eSJens Wiklander /*
9906abfa44eSJens Wiklander  * Note: this variable is weak just to ease breaking its dependency chain
9916abfa44eSJens Wiklander  * when added to the unpaged area.
9926abfa44eSJens Wiklander  */
99339e8c200SJerome Forissier const struct ts_ops stmm_sp_ops __weak __relrodata_unpaged("stmm_sp_ops") = {
994f9cd31c5SJelle Sels 	.enter_open_session = stmm_enter_open_session,
995f9cd31c5SJelle Sels 	.enter_invoke_cmd = stmm_enter_invoke_cmd,
996f9cd31c5SJelle Sels 	.enter_close_session = stmm_enter_close_session,
997f9cd31c5SJelle Sels 	.dump_state = stmm_dump_state,
998f9cd31c5SJelle Sels 	.destroy = stmm_ctx_destroy,
999f9cd31c5SJelle Sels 	.get_instance_id = stmm_get_instance_id,
1000ab5363c6SJens Wiklander 	.handle_scall = spm_handle_scall,
1001f9cd31c5SJelle Sels };
1002