xref: /optee_os/core/kernel/ldelf_syscalls.c (revision f7e461dcdb5318e938ea3dc7383a1531d6d070a1)
1cbe7e1b8SBalint Dobszay // SPDX-License-Identifier: BSD-2-Clause
2cbe7e1b8SBalint Dobszay /*
36105aa86SJens Wiklander  * Copyright (c) 2018-2019, 2022 Linaro Limited
4c185655eSJelle Sels  * Copyright (c) 2020-2021, Arm Limited
5cbe7e1b8SBalint Dobszay  */
6cbe7e1b8SBalint Dobszay 
7cbe7e1b8SBalint Dobszay #include <assert.h>
8baa5161dSBalint Dobszay #include <crypto/crypto.h>
9cbe7e1b8SBalint Dobszay #include <kernel/ldelf_syscalls.h>
10*f7e461dcSJens Wiklander #include <kernel/user_access.h>
11cbe7e1b8SBalint Dobszay #include <kernel/user_mode_ctx.h>
12cbe7e1b8SBalint Dobszay #include <ldelf.h>
13cbe7e1b8SBalint Dobszay #include <mm/file.h>
14cbe7e1b8SBalint Dobszay #include <mm/fobj.h>
15cbe7e1b8SBalint Dobszay #include <mm/mobj.h>
16cbe7e1b8SBalint Dobszay #include <mm/vm.h>
17cbe7e1b8SBalint Dobszay #include <stdlib.h>
18cbe7e1b8SBalint Dobszay #include <string.h>
19cbe7e1b8SBalint Dobszay #include <trace.h>
20cbe7e1b8SBalint Dobszay #include <util.h>
21cbe7e1b8SBalint Dobszay 
22cbe7e1b8SBalint Dobszay struct bin_handle {
23cbe7e1b8SBalint Dobszay 	const struct ts_store_ops *op;
24cbe7e1b8SBalint Dobszay 	struct ts_store_handle *h;
25cbe7e1b8SBalint Dobszay 	struct file *f;
26cbe7e1b8SBalint Dobszay 	size_t offs_bytes;
27cbe7e1b8SBalint Dobszay 	size_t size_bytes;
28cbe7e1b8SBalint Dobszay };
29cbe7e1b8SBalint Dobszay 
unmap_or_panic(struct user_mode_ctx * uctx,vaddr_t va,size_t byte_count)30*f7e461dcSJens Wiklander static void unmap_or_panic(struct user_mode_ctx *uctx, vaddr_t va,
31*f7e461dcSJens Wiklander 			   size_t byte_count)
32*f7e461dcSJens Wiklander {
33*f7e461dcSJens Wiklander 	TEE_Result res = vm_unmap(uctx, va, byte_count);
34*f7e461dcSJens Wiklander 
35*f7e461dcSJens Wiklander 	if (res) {
36*f7e461dcSJens Wiklander 		EMSG("vm_unmap(%#"PRIxVA", %#zx) returned %#"PRIx32,
37*f7e461dcSJens Wiklander 		     va, byte_count, res);
38*f7e461dcSJens Wiklander 		panic("Can't restore memory map");
39*f7e461dcSJens Wiklander 	}
40*f7e461dcSJens Wiklander }
41*f7e461dcSJens Wiklander 
ldelf_syscall_map_zi(vaddr_t * va,size_t num_bytes,size_t pad_begin,size_t pad_end,unsigned long flags)42baa5161dSBalint Dobszay TEE_Result ldelf_syscall_map_zi(vaddr_t *va, size_t num_bytes, size_t pad_begin,
43baa5161dSBalint Dobszay 				size_t pad_end, unsigned long flags)
44baa5161dSBalint Dobszay {
45baa5161dSBalint Dobszay 	TEE_Result res = TEE_SUCCESS;
46baa5161dSBalint Dobszay 	struct ts_session *sess = ts_get_current_session();
47baa5161dSBalint Dobszay 	struct user_mode_ctx *uctx = to_user_mode_ctx(sess->ctx);
48baa5161dSBalint Dobszay 	struct fobj *f = NULL;
49baa5161dSBalint Dobszay 	struct mobj *mobj = NULL;
50baa5161dSBalint Dobszay 	uint32_t prot = TEE_MATTR_URW | TEE_MATTR_PRW;
51baa5161dSBalint Dobszay 	uint32_t vm_flags = 0;
52*f7e461dcSJens Wiklander 	vaddr_t va_copy = 0;
53baa5161dSBalint Dobszay 
54baa5161dSBalint Dobszay 	if (flags & ~LDELF_MAP_FLAG_SHAREABLE)
55baa5161dSBalint Dobszay 		return TEE_ERROR_BAD_PARAMETERS;
56baa5161dSBalint Dobszay 
57*f7e461dcSJens Wiklander 	res = GET_USER_SCALAR(va_copy, va);
58*f7e461dcSJens Wiklander 	if (res)
59*f7e461dcSJens Wiklander 		return res;
60*f7e461dcSJens Wiklander 
61baa5161dSBalint Dobszay 	if (flags & LDELF_MAP_FLAG_SHAREABLE)
62baa5161dSBalint Dobszay 		vm_flags |= VM_FLAG_SHAREABLE;
63baa5161dSBalint Dobszay 
64baa5161dSBalint Dobszay 	f = fobj_ta_mem_alloc(ROUNDUP_DIV(num_bytes, SMALL_PAGE_SIZE));
65baa5161dSBalint Dobszay 	if (!f)
66baa5161dSBalint Dobszay 		return TEE_ERROR_OUT_OF_MEMORY;
676105aa86SJens Wiklander 	mobj = mobj_with_fobj_alloc(f, NULL, TEE_MATTR_MEM_TYPE_TAGGED);
68baa5161dSBalint Dobszay 	fobj_put(f);
69baa5161dSBalint Dobszay 	if (!mobj)
70baa5161dSBalint Dobszay 		return TEE_ERROR_OUT_OF_MEMORY;
71*f7e461dcSJens Wiklander 	res = vm_map_pad(uctx, &va_copy, num_bytes, prot, vm_flags,
72baa5161dSBalint Dobszay 			 mobj, 0, pad_begin, pad_end, 0);
73baa5161dSBalint Dobszay 	mobj_put(mobj);
74*f7e461dcSJens Wiklander 	if (!res) {
75*f7e461dcSJens Wiklander 		res = PUT_USER_SCALAR(va_copy, va);
76*f7e461dcSJens Wiklander 		if (res)
77*f7e461dcSJens Wiklander 			unmap_or_panic(uctx, va_copy, num_bytes);
78*f7e461dcSJens Wiklander 	}
79baa5161dSBalint Dobszay 
80baa5161dSBalint Dobszay 	return res;
81baa5161dSBalint Dobszay }
82baa5161dSBalint Dobszay 
ldelf_syscall_unmap(vaddr_t va,size_t num_bytes)83baa5161dSBalint Dobszay TEE_Result ldelf_syscall_unmap(vaddr_t va, size_t num_bytes)
84baa5161dSBalint Dobszay {
85baa5161dSBalint Dobszay 	TEE_Result res = TEE_SUCCESS;
86baa5161dSBalint Dobszay 	struct ts_session *sess = ts_get_current_session();
87baa5161dSBalint Dobszay 	struct user_mode_ctx *uctx = to_user_mode_ctx(sess->ctx);
88baa5161dSBalint Dobszay 	size_t sz = ROUNDUP(num_bytes, SMALL_PAGE_SIZE);
89baa5161dSBalint Dobszay 	uint32_t vm_flags = 0;
90baa5161dSBalint Dobszay 	vaddr_t end_va = 0;
91baa5161dSBalint Dobszay 
92baa5161dSBalint Dobszay 	/*
93baa5161dSBalint Dobszay 	 * The vm_get_flags() and vm_unmap() are supposed to detect or handle
94baa5161dSBalint Dobszay 	 * overflow directly or indirectly. However, since this function is an
95baa5161dSBalint Dobszay 	 * API function it's worth having an extra guard here. If nothing else,
96baa5161dSBalint Dobszay 	 * to increase code clarity.
97baa5161dSBalint Dobszay 	 */
98baa5161dSBalint Dobszay 	if (ADD_OVERFLOW(va, sz, &end_va))
99baa5161dSBalint Dobszay 		return TEE_ERROR_BAD_PARAMETERS;
100baa5161dSBalint Dobszay 
101baa5161dSBalint Dobszay 	res = vm_get_flags(uctx, va, sz, &vm_flags);
102baa5161dSBalint Dobszay 	if (res)
103baa5161dSBalint Dobszay 		return res;
104baa5161dSBalint Dobszay 	if (vm_flags & VM_FLAG_PERMANENT)
105baa5161dSBalint Dobszay 		return TEE_ERROR_ACCESS_DENIED;
106baa5161dSBalint Dobszay 
107baa5161dSBalint Dobszay 	return vm_unmap(uctx, va, sz);
108baa5161dSBalint Dobszay }
109baa5161dSBalint Dobszay 
bin_close(void * ptr)110baa5161dSBalint Dobszay static void bin_close(void *ptr)
111cbe7e1b8SBalint Dobszay {
112cbe7e1b8SBalint Dobszay 	struct bin_handle *binh = ptr;
113cbe7e1b8SBalint Dobszay 
114cbe7e1b8SBalint Dobszay 	if (binh) {
115cbe7e1b8SBalint Dobszay 		if (binh->op && binh->h)
116cbe7e1b8SBalint Dobszay 			binh->op->close(binh->h);
117cbe7e1b8SBalint Dobszay 		file_put(binh->f);
118cbe7e1b8SBalint Dobszay 	}
119cbe7e1b8SBalint Dobszay 	free(binh);
120cbe7e1b8SBalint Dobszay }
121cbe7e1b8SBalint Dobszay 
ldelf_syscall_open_bin(const TEE_UUID * uuid,size_t uuid_size,uint32_t * handle)122baa5161dSBalint Dobszay TEE_Result ldelf_syscall_open_bin(const TEE_UUID *uuid, size_t uuid_size,
123baa5161dSBalint Dobszay 				  uint32_t *handle)
124cbe7e1b8SBalint Dobszay {
125cbe7e1b8SBalint Dobszay 	TEE_Result res = TEE_SUCCESS;
126baa5161dSBalint Dobszay 	struct ts_session *sess = ts_get_current_session();
127baa5161dSBalint Dobszay 	struct user_mode_ctx *uctx = to_user_mode_ctx(sess->ctx);
128baa5161dSBalint Dobszay 	struct system_ctx *sys_ctx = sess->user_ctx;
129cbe7e1b8SBalint Dobszay 	struct bin_handle *binh = NULL;
130cbe7e1b8SBalint Dobszay 	uint8_t tag[FILE_TAG_SIZE] = { 0 };
131cbe7e1b8SBalint Dobszay 	unsigned int tag_len = sizeof(tag);
132*f7e461dcSJens Wiklander 	TEE_UUID *bb_uuid = NULL;
133baa5161dSBalint Dobszay 	int h = 0;
134cbe7e1b8SBalint Dobszay 
135*f7e461dcSJens Wiklander 	res = BB_MEMDUP_USER(uuid, sizeof(*uuid), &bb_uuid);
136baa5161dSBalint Dobszay 	if (res)
137baa5161dSBalint Dobszay 		return res;
138baa5161dSBalint Dobszay 
139baa5161dSBalint Dobszay 	res = vm_check_access_rights(uctx,
140baa5161dSBalint Dobszay 				     TEE_MEMORY_ACCESS_WRITE |
141baa5161dSBalint Dobszay 				     TEE_MEMORY_ACCESS_ANY_OWNER,
142baa5161dSBalint Dobszay 				     (uaddr_t)handle, sizeof(uint32_t));
143baa5161dSBalint Dobszay 	if (res)
144baa5161dSBalint Dobszay 		return res;
145baa5161dSBalint Dobszay 
146baa5161dSBalint Dobszay 	if (uuid_size != sizeof(*uuid))
147cbe7e1b8SBalint Dobszay 		return TEE_ERROR_BAD_PARAMETERS;
148cbe7e1b8SBalint Dobszay 
149baa5161dSBalint Dobszay 	if (!sys_ctx) {
150baa5161dSBalint Dobszay 		sys_ctx = calloc(1, sizeof(*sys_ctx));
151baa5161dSBalint Dobszay 		if (!sys_ctx)
152baa5161dSBalint Dobszay 			return TEE_ERROR_OUT_OF_MEMORY;
153baa5161dSBalint Dobszay 		sess->user_ctx = sys_ctx;
154baa5161dSBalint Dobszay 	}
155cbe7e1b8SBalint Dobszay 
156cbe7e1b8SBalint Dobszay 	binh = calloc(1, sizeof(*binh));
157cbe7e1b8SBalint Dobszay 	if (!binh)
158cbe7e1b8SBalint Dobszay 		return TEE_ERROR_OUT_OF_MEMORY;
159cbe7e1b8SBalint Dobszay 
160c185655eSJelle Sels 	if (is_user_ta_ctx(sess->ctx) || is_stmm_ctx(sess->ctx)) {
161c185655eSJelle Sels 		SCATTERED_ARRAY_FOREACH(binh->op, ta_stores,
162c185655eSJelle Sels 					struct ts_store_ops) {
163cbe7e1b8SBalint Dobszay 			DMSG("Lookup user TA ELF %pUl (%s)",
164*f7e461dcSJens Wiklander 			     (void *)bb_uuid, binh->op->description);
165cbe7e1b8SBalint Dobszay 
166*f7e461dcSJens Wiklander 			res = binh->op->open(bb_uuid, &binh->h);
167baa5161dSBalint Dobszay 			DMSG("res=%#"PRIx32, res);
168cbe7e1b8SBalint Dobszay 			if (res != TEE_ERROR_ITEM_NOT_FOUND &&
169cbe7e1b8SBalint Dobszay 			    res != TEE_ERROR_STORAGE_NOT_AVAILABLE)
170cbe7e1b8SBalint Dobszay 				break;
171cbe7e1b8SBalint Dobszay 		}
172c185655eSJelle Sels 	} else if (is_sp_ctx(sess->ctx)) {
173c185655eSJelle Sels 		SCATTERED_ARRAY_FOREACH(binh->op, sp_stores,
174c185655eSJelle Sels 					struct ts_store_ops) {
175c185655eSJelle Sels 			DMSG("Lookup user SP ELF %pUl (%s)",
176*f7e461dcSJens Wiklander 			     (void *)bb_uuid, binh->op->description);
177c185655eSJelle Sels 
178*f7e461dcSJens Wiklander 			res = binh->op->open(bb_uuid, &binh->h);
179c185655eSJelle Sels 			DMSG("res=%#"PRIx32, res);
180c185655eSJelle Sels 			if (res != TEE_ERROR_ITEM_NOT_FOUND &&
181c185655eSJelle Sels 			    res != TEE_ERROR_STORAGE_NOT_AVAILABLE)
182c185655eSJelle Sels 				break;
183c185655eSJelle Sels 		}
184c185655eSJelle Sels 	} else {
185c185655eSJelle Sels 		res = TEE_ERROR_ITEM_NOT_FOUND;
186c185655eSJelle Sels 	}
187c185655eSJelle Sels 
188cbe7e1b8SBalint Dobszay 	if (res)
189cbe7e1b8SBalint Dobszay 		goto err;
190cbe7e1b8SBalint Dobszay 
191cbe7e1b8SBalint Dobszay 	res = binh->op->get_size(binh->h, &binh->size_bytes);
192cbe7e1b8SBalint Dobszay 	if (res)
193cbe7e1b8SBalint Dobszay 		goto err;
194cbe7e1b8SBalint Dobszay 	res = binh->op->get_tag(binh->h, tag, &tag_len);
195cbe7e1b8SBalint Dobszay 	if (res)
196cbe7e1b8SBalint Dobszay 		goto err;
197cbe7e1b8SBalint Dobszay 	binh->f = file_get_by_tag(tag, tag_len);
198cbe7e1b8SBalint Dobszay 	if (!binh->f)
199cbe7e1b8SBalint Dobszay 		goto err_oom;
200cbe7e1b8SBalint Dobszay 
201baa5161dSBalint Dobszay 	h = handle_get(&sys_ctx->db, binh);
202cbe7e1b8SBalint Dobszay 	if (h < 0)
203cbe7e1b8SBalint Dobszay 		goto err_oom;
204*f7e461dcSJens Wiklander 	res = PUT_USER_SCALAR(h, handle);
205*f7e461dcSJens Wiklander 	if (res) {
206*f7e461dcSJens Wiklander 		handle_put(&sys_ctx->db, h);
207*f7e461dcSJens Wiklander 		goto err;
208*f7e461dcSJens Wiklander 	}
209cbe7e1b8SBalint Dobszay 
210cbe7e1b8SBalint Dobszay 	return TEE_SUCCESS;
211baa5161dSBalint Dobszay 
212cbe7e1b8SBalint Dobszay err_oom:
213cbe7e1b8SBalint Dobszay 	res = TEE_ERROR_OUT_OF_MEMORY;
214cbe7e1b8SBalint Dobszay err:
215baa5161dSBalint Dobszay 	bin_close(binh);
216cbe7e1b8SBalint Dobszay 	return res;
217cbe7e1b8SBalint Dobszay }
218cbe7e1b8SBalint Dobszay 
ldelf_syscall_close_bin(unsigned long handle)219baa5161dSBalint Dobszay TEE_Result ldelf_syscall_close_bin(unsigned long handle)
220cbe7e1b8SBalint Dobszay {
221cbe7e1b8SBalint Dobszay 	TEE_Result res = TEE_SUCCESS;
222baa5161dSBalint Dobszay 	struct ts_session *sess = ts_get_current_session();
223baa5161dSBalint Dobszay 	struct system_ctx *sys_ctx = sess->user_ctx;
224cbe7e1b8SBalint Dobszay 	struct bin_handle *binh = NULL;
225cbe7e1b8SBalint Dobszay 
226baa5161dSBalint Dobszay 	if (!sys_ctx)
227cbe7e1b8SBalint Dobszay 		return TEE_ERROR_BAD_PARAMETERS;
228cbe7e1b8SBalint Dobszay 
229baa5161dSBalint Dobszay 	binh = handle_put(&sys_ctx->db, handle);
230cbe7e1b8SBalint Dobszay 	if (!binh)
231cbe7e1b8SBalint Dobszay 		return TEE_ERROR_BAD_PARAMETERS;
232cbe7e1b8SBalint Dobszay 
233cbe7e1b8SBalint Dobszay 	if (binh->offs_bytes < binh->size_bytes)
234ef44161fSJens Wiklander 		res = binh->op->read(binh->h, NULL, NULL,
235cbe7e1b8SBalint Dobszay 				     binh->size_bytes - binh->offs_bytes);
236cbe7e1b8SBalint Dobszay 
237baa5161dSBalint Dobszay 	bin_close(binh);
238baa5161dSBalint Dobszay 	if (handle_db_is_empty(&sys_ctx->db)) {
239baa5161dSBalint Dobszay 		handle_db_destroy(&sys_ctx->db, bin_close);
240baa5161dSBalint Dobszay 		free(sys_ctx);
241baa5161dSBalint Dobszay 		sess->user_ctx = NULL;
242baa5161dSBalint Dobszay 	}
243cbe7e1b8SBalint Dobszay 
244cbe7e1b8SBalint Dobszay 	return res;
245cbe7e1b8SBalint Dobszay }
246cbe7e1b8SBalint Dobszay 
binh_copy_to(struct bin_handle * binh,vaddr_t va_core,vaddr_t va_user,size_t offs_bytes,size_t num_bytes)247ef44161fSJens Wiklander static TEE_Result binh_copy_to(struct bin_handle *binh, vaddr_t va_core,
248ef44161fSJens Wiklander 			       vaddr_t va_user, size_t offs_bytes,
249ef44161fSJens Wiklander 			       size_t num_bytes)
250cbe7e1b8SBalint Dobszay {
251cbe7e1b8SBalint Dobszay 	TEE_Result res = TEE_SUCCESS;
252cbe7e1b8SBalint Dobszay 	size_t next_offs = 0;
253cbe7e1b8SBalint Dobszay 
254cbe7e1b8SBalint Dobszay 	if (offs_bytes < binh->offs_bytes)
255cbe7e1b8SBalint Dobszay 		return TEE_ERROR_BAD_STATE;
256cbe7e1b8SBalint Dobszay 
257cbe7e1b8SBalint Dobszay 	if (ADD_OVERFLOW(offs_bytes, num_bytes, &next_offs))
258cbe7e1b8SBalint Dobszay 		return TEE_ERROR_BAD_PARAMETERS;
259cbe7e1b8SBalint Dobszay 
260cbe7e1b8SBalint Dobszay 	if (offs_bytes > binh->offs_bytes) {
261ef44161fSJens Wiklander 		res = binh->op->read(binh->h, NULL, NULL,
262cbe7e1b8SBalint Dobszay 				     offs_bytes - binh->offs_bytes);
263cbe7e1b8SBalint Dobszay 		if (res)
264cbe7e1b8SBalint Dobszay 			return res;
265cbe7e1b8SBalint Dobszay 		binh->offs_bytes = offs_bytes;
266cbe7e1b8SBalint Dobszay 	}
267cbe7e1b8SBalint Dobszay 
268cbe7e1b8SBalint Dobszay 	if (next_offs > binh->size_bytes) {
269cbe7e1b8SBalint Dobszay 		size_t rb = binh->size_bytes - binh->offs_bytes;
270cbe7e1b8SBalint Dobszay 
271ef44161fSJens Wiklander 		res = binh->op->read(binh->h, (void *)va_core,
272ef44161fSJens Wiklander 				     (void *)va_user, rb);
273cbe7e1b8SBalint Dobszay 		if (res)
274cbe7e1b8SBalint Dobszay 			return res;
275ef44161fSJens Wiklander 		if (va_core)
276ef44161fSJens Wiklander 			memset((uint8_t *)va_core + rb, 0, num_bytes - rb);
277ef44161fSJens Wiklander 		if (va_user) {
278ef44161fSJens Wiklander 			res = clear_user((uint8_t *)va_user + rb,
279ef44161fSJens Wiklander 					 num_bytes - rb);
280ef44161fSJens Wiklander 			if (res)
281ef44161fSJens Wiklander 				return res;
282ef44161fSJens Wiklander 		}
283cbe7e1b8SBalint Dobszay 		binh->offs_bytes = binh->size_bytes;
284cbe7e1b8SBalint Dobszay 	} else {
285ef44161fSJens Wiklander 		res = binh->op->read(binh->h, (void *)va_core,
286ef44161fSJens Wiklander 				     (void *)va_user, num_bytes);
287cbe7e1b8SBalint Dobszay 		if (res)
288cbe7e1b8SBalint Dobszay 			return res;
289cbe7e1b8SBalint Dobszay 		binh->offs_bytes = next_offs;
290cbe7e1b8SBalint Dobszay 	}
291cbe7e1b8SBalint Dobszay 
292cbe7e1b8SBalint Dobszay 	return TEE_SUCCESS;
293cbe7e1b8SBalint Dobszay }
294cbe7e1b8SBalint Dobszay 
ldelf_syscall_map_bin(vaddr_t * va,size_t num_bytes,unsigned long handle,size_t offs_bytes,size_t pad_begin,size_t pad_end,unsigned long flags)295baa5161dSBalint Dobszay TEE_Result ldelf_syscall_map_bin(vaddr_t *va, size_t num_bytes,
296baa5161dSBalint Dobszay 				 unsigned long handle, size_t offs_bytes,
297baa5161dSBalint Dobszay 				 size_t pad_begin, size_t pad_end,
298baa5161dSBalint Dobszay 				 unsigned long flags)
299cbe7e1b8SBalint Dobszay {
300baa5161dSBalint Dobszay 	TEE_Result res = TEE_SUCCESS;
301baa5161dSBalint Dobszay 	struct ts_session *sess = ts_get_current_session();
302baa5161dSBalint Dobszay 	struct user_mode_ctx *uctx = to_user_mode_ctx(sess->ctx);
303baa5161dSBalint Dobszay 	struct system_ctx *sys_ctx = sess->user_ctx;
304cbe7e1b8SBalint Dobszay 	struct bin_handle *binh = NULL;
305cbe7e1b8SBalint Dobszay 	uint32_t num_rounded_bytes = 0;
306cbe7e1b8SBalint Dobszay 	struct file_slice *fs = NULL;
307cbe7e1b8SBalint Dobszay 	bool file_is_locked = false;
308cbe7e1b8SBalint Dobszay 	struct mobj *mobj = NULL;
309cbe7e1b8SBalint Dobszay 	uint32_t offs_pages = 0;
310cbe7e1b8SBalint Dobszay 	size_t num_pages = 0;
311*f7e461dcSJens Wiklander 	vaddr_t va_copy = 0;
312cbe7e1b8SBalint Dobszay 	uint32_t prot = 0;
313baa5161dSBalint Dobszay 	const uint32_t accept_flags = LDELF_MAP_FLAG_SHAREABLE |
314baa5161dSBalint Dobszay 				      LDELF_MAP_FLAG_WRITEABLE |
3150d482f82SRuchika Gupta 				      LDELF_MAP_FLAG_BTI |
316baa5161dSBalint Dobszay 				      LDELF_MAP_FLAG_EXECUTABLE;
317cbe7e1b8SBalint Dobszay 
318*f7e461dcSJens Wiklander 	res = GET_USER_SCALAR(va_copy, va);
319*f7e461dcSJens Wiklander 	if (res)
320*f7e461dcSJens Wiklander 		return res;
321*f7e461dcSJens Wiklander 
322baa5161dSBalint Dobszay 	if (!sys_ctx)
323cbe7e1b8SBalint Dobszay 		return TEE_ERROR_BAD_PARAMETERS;
324cbe7e1b8SBalint Dobszay 
325baa5161dSBalint Dobszay 	binh = handle_lookup(&sys_ctx->db, handle);
326cbe7e1b8SBalint Dobszay 	if (!binh)
327cbe7e1b8SBalint Dobszay 		return TEE_ERROR_BAD_PARAMETERS;
328cbe7e1b8SBalint Dobszay 
329cbe7e1b8SBalint Dobszay 	if ((flags & accept_flags) != flags)
330cbe7e1b8SBalint Dobszay 		return TEE_ERROR_BAD_PARAMETERS;
331cbe7e1b8SBalint Dobszay 
332baa5161dSBalint Dobszay 	if ((flags & LDELF_MAP_FLAG_SHAREABLE) &&
333baa5161dSBalint Dobszay 	    (flags & LDELF_MAP_FLAG_WRITEABLE))
334cbe7e1b8SBalint Dobszay 		return TEE_ERROR_BAD_PARAMETERS;
335cbe7e1b8SBalint Dobszay 
336baa5161dSBalint Dobszay 	if ((flags & LDELF_MAP_FLAG_EXECUTABLE) &&
337baa5161dSBalint Dobszay 	    (flags & LDELF_MAP_FLAG_WRITEABLE))
338cbe7e1b8SBalint Dobszay 		return TEE_ERROR_BAD_PARAMETERS;
339cbe7e1b8SBalint Dobszay 
340cbe7e1b8SBalint Dobszay 	if (offs_bytes & SMALL_PAGE_MASK)
341cbe7e1b8SBalint Dobszay 		return TEE_ERROR_BAD_PARAMETERS;
342cbe7e1b8SBalint Dobszay 
343cbe7e1b8SBalint Dobszay 	prot = TEE_MATTR_UR | TEE_MATTR_PR;
344baa5161dSBalint Dobszay 	if (flags & LDELF_MAP_FLAG_WRITEABLE)
345cbe7e1b8SBalint Dobszay 		prot |= TEE_MATTR_UW | TEE_MATTR_PW;
346baa5161dSBalint Dobszay 	if (flags & LDELF_MAP_FLAG_EXECUTABLE)
347cbe7e1b8SBalint Dobszay 		prot |= TEE_MATTR_UX;
3480d482f82SRuchika Gupta 	if (flags & LDELF_MAP_FLAG_BTI)
3490d482f82SRuchika Gupta 		prot |= TEE_MATTR_GUARDED;
350cbe7e1b8SBalint Dobszay 
351cbe7e1b8SBalint Dobszay 	offs_pages = offs_bytes >> SMALL_PAGE_SHIFT;
352cbe7e1b8SBalint Dobszay 	if (ROUNDUP_OVERFLOW(num_bytes, SMALL_PAGE_SIZE, &num_rounded_bytes))
353cbe7e1b8SBalint Dobszay 		return TEE_ERROR_BAD_PARAMETERS;
354cbe7e1b8SBalint Dobszay 	num_pages = num_rounded_bytes / SMALL_PAGE_SIZE;
355cbe7e1b8SBalint Dobszay 
356cbe7e1b8SBalint Dobszay 	if (!file_trylock(binh->f)) {
357cbe7e1b8SBalint Dobszay 		/*
358cbe7e1b8SBalint Dobszay 		 * Before we can block on the file lock we must make all
359cbe7e1b8SBalint Dobszay 		 * our page tables available for reclaiming in order to
360cbe7e1b8SBalint Dobszay 		 * avoid a dead-lock with the other thread (which already
361cbe7e1b8SBalint Dobszay 		 * is holding the file lock) mapping lots of memory below.
362cbe7e1b8SBalint Dobszay 		 */
363cbe7e1b8SBalint Dobszay 		vm_set_ctx(NULL);
364cbe7e1b8SBalint Dobszay 		file_lock(binh->f);
365cbe7e1b8SBalint Dobszay 		vm_set_ctx(uctx->ts_ctx);
366cbe7e1b8SBalint Dobszay 	}
367cbe7e1b8SBalint Dobszay 	file_is_locked = true;
368cbe7e1b8SBalint Dobszay 	fs = file_find_slice(binh->f, offs_pages);
369cbe7e1b8SBalint Dobszay 	if (fs) {
370cbe7e1b8SBalint Dobszay 		/* If there's registered slice it has to match */
371cbe7e1b8SBalint Dobszay 		if (fs->page_offset != offs_pages ||
372cbe7e1b8SBalint Dobszay 		    num_pages > fs->fobj->num_pages) {
373cbe7e1b8SBalint Dobszay 			res = TEE_ERROR_BAD_PARAMETERS;
374cbe7e1b8SBalint Dobszay 			goto err;
375cbe7e1b8SBalint Dobszay 		}
376cbe7e1b8SBalint Dobszay 
377cbe7e1b8SBalint Dobszay 		/* If there's a slice we must be mapping shareable */
378baa5161dSBalint Dobszay 		if (!(flags & LDELF_MAP_FLAG_SHAREABLE)) {
379cbe7e1b8SBalint Dobszay 			res = TEE_ERROR_BAD_PARAMETERS;
380cbe7e1b8SBalint Dobszay 			goto err;
381cbe7e1b8SBalint Dobszay 		}
382cbe7e1b8SBalint Dobszay 
3836105aa86SJens Wiklander 		mobj = mobj_with_fobj_alloc(fs->fobj, binh->f,
3846105aa86SJens Wiklander 					    TEE_MATTR_MEM_TYPE_TAGGED);
385cbe7e1b8SBalint Dobszay 		if (!mobj) {
386cbe7e1b8SBalint Dobszay 			res = TEE_ERROR_OUT_OF_MEMORY;
387cbe7e1b8SBalint Dobszay 			goto err;
388cbe7e1b8SBalint Dobszay 		}
389*f7e461dcSJens Wiklander 		res = vm_map_pad(uctx, &va_copy, num_rounded_bytes,
390cbe7e1b8SBalint Dobszay 				 prot, VM_FLAG_READONLY,
391cbe7e1b8SBalint Dobszay 				 mobj, 0, pad_begin, pad_end, 0);
392cbe7e1b8SBalint Dobszay 		mobj_put(mobj);
393cbe7e1b8SBalint Dobszay 		if (res)
394cbe7e1b8SBalint Dobszay 			goto err;
395cbe7e1b8SBalint Dobszay 	} else {
396cbe7e1b8SBalint Dobszay 		struct fobj *f = fobj_ta_mem_alloc(num_pages);
397cbe7e1b8SBalint Dobszay 		struct file *file = NULL;
398cbe7e1b8SBalint Dobszay 		uint32_t vm_flags = 0;
399cbe7e1b8SBalint Dobszay 
400cbe7e1b8SBalint Dobszay 		if (!f) {
401cbe7e1b8SBalint Dobszay 			res = TEE_ERROR_OUT_OF_MEMORY;
402cbe7e1b8SBalint Dobszay 			goto err;
403cbe7e1b8SBalint Dobszay 		}
404baa5161dSBalint Dobszay 		if (!(flags & LDELF_MAP_FLAG_WRITEABLE)) {
405cbe7e1b8SBalint Dobszay 			file = binh->f;
406cbe7e1b8SBalint Dobszay 			vm_flags |= VM_FLAG_READONLY;
407cbe7e1b8SBalint Dobszay 		}
408cbe7e1b8SBalint Dobszay 
4096105aa86SJens Wiklander 		mobj = mobj_with_fobj_alloc(f, file, TEE_MATTR_MEM_TYPE_TAGGED);
410cbe7e1b8SBalint Dobszay 		fobj_put(f);
411cbe7e1b8SBalint Dobszay 		if (!mobj) {
412cbe7e1b8SBalint Dobszay 			res = TEE_ERROR_OUT_OF_MEMORY;
413cbe7e1b8SBalint Dobszay 			goto err;
414cbe7e1b8SBalint Dobszay 		}
415*f7e461dcSJens Wiklander 		res = vm_map_pad(uctx, &va_copy, num_rounded_bytes,
416cbe7e1b8SBalint Dobszay 				 TEE_MATTR_PRW, vm_flags, mobj, 0,
417cbe7e1b8SBalint Dobszay 				 pad_begin, pad_end, 0);
418cbe7e1b8SBalint Dobszay 		mobj_put(mobj);
419cbe7e1b8SBalint Dobszay 		if (res)
420cbe7e1b8SBalint Dobszay 			goto err;
421*f7e461dcSJens Wiklander 		res = binh_copy_to(binh, va_copy, 0, offs_bytes, num_bytes);
422cbe7e1b8SBalint Dobszay 		if (res)
423cbe7e1b8SBalint Dobszay 			goto err_unmap_va;
424*f7e461dcSJens Wiklander 		res = vm_set_prot(uctx, va_copy, num_rounded_bytes,
425cbe7e1b8SBalint Dobszay 				  prot);
426cbe7e1b8SBalint Dobszay 		if (res)
427cbe7e1b8SBalint Dobszay 			goto err_unmap_va;
428cbe7e1b8SBalint Dobszay 
429cbe7e1b8SBalint Dobszay 		/*
430cbe7e1b8SBalint Dobszay 		 * The context currently is active set it again to update
431cbe7e1b8SBalint Dobszay 		 * the mapping.
432cbe7e1b8SBalint Dobszay 		 */
433cbe7e1b8SBalint Dobszay 		vm_set_ctx(uctx->ts_ctx);
434cbe7e1b8SBalint Dobszay 
435baa5161dSBalint Dobszay 		if (!(flags & LDELF_MAP_FLAG_WRITEABLE)) {
436cbe7e1b8SBalint Dobszay 			res = file_add_slice(binh->f, f, offs_pages);
437cbe7e1b8SBalint Dobszay 			if (res)
438cbe7e1b8SBalint Dobszay 				goto err_unmap_va;
439cbe7e1b8SBalint Dobszay 		}
440cbe7e1b8SBalint Dobszay 	}
441cbe7e1b8SBalint Dobszay 
442*f7e461dcSJens Wiklander 	res = PUT_USER_SCALAR(va_copy, va);
443*f7e461dcSJens Wiklander 	if (res)
444*f7e461dcSJens Wiklander 		goto err_unmap_va;
445*f7e461dcSJens Wiklander 
446cbe7e1b8SBalint Dobszay 	file_unlock(binh->f);
447cbe7e1b8SBalint Dobszay 
448cbe7e1b8SBalint Dobszay 	return TEE_SUCCESS;
449cbe7e1b8SBalint Dobszay 
450cbe7e1b8SBalint Dobszay err_unmap_va:
451*f7e461dcSJens Wiklander 	unmap_or_panic(uctx, va_copy, num_rounded_bytes);
452cbe7e1b8SBalint Dobszay 
453cbe7e1b8SBalint Dobszay 	/*
454cbe7e1b8SBalint Dobszay 	 * The context currently is active set it again to update
455cbe7e1b8SBalint Dobszay 	 * the mapping.
456cbe7e1b8SBalint Dobszay 	 */
457cbe7e1b8SBalint Dobszay 	vm_set_ctx(uctx->ts_ctx);
458cbe7e1b8SBalint Dobszay 
459cbe7e1b8SBalint Dobszay err:
460cbe7e1b8SBalint Dobszay 	if (file_is_locked)
461cbe7e1b8SBalint Dobszay 		file_unlock(binh->f);
462cbe7e1b8SBalint Dobszay 
463cbe7e1b8SBalint Dobszay 	return res;
464cbe7e1b8SBalint Dobszay }
465cbe7e1b8SBalint Dobszay 
ldelf_syscall_copy_from_bin(void * dst,size_t offs,size_t num_bytes,unsigned long handle)466baa5161dSBalint Dobszay TEE_Result ldelf_syscall_copy_from_bin(void *dst, size_t offs, size_t num_bytes,
467baa5161dSBalint Dobszay 				       unsigned long handle)
468cbe7e1b8SBalint Dobszay {
469baa5161dSBalint Dobszay 	TEE_Result res = TEE_SUCCESS;
470baa5161dSBalint Dobszay 	struct ts_session *sess = ts_get_current_session();
471baa5161dSBalint Dobszay 	struct user_mode_ctx *uctx = to_user_mode_ctx(sess->ctx);
472baa5161dSBalint Dobszay 	struct system_ctx *sys_ctx = sess->user_ctx;
473cbe7e1b8SBalint Dobszay 	struct bin_handle *binh = NULL;
474cbe7e1b8SBalint Dobszay 
475baa5161dSBalint Dobszay 	res = vm_check_access_rights(uctx,
476baa5161dSBalint Dobszay 				     TEE_MEMORY_ACCESS_WRITE |
477baa5161dSBalint Dobszay 				     TEE_MEMORY_ACCESS_ANY_OWNER,
478baa5161dSBalint Dobszay 				     (uaddr_t)dst, num_bytes);
479baa5161dSBalint Dobszay 	if (res)
480baa5161dSBalint Dobszay 		return res;
481baa5161dSBalint Dobszay 
482baa5161dSBalint Dobszay 	if (!sys_ctx)
483cbe7e1b8SBalint Dobszay 		return TEE_ERROR_BAD_PARAMETERS;
484cbe7e1b8SBalint Dobszay 
485baa5161dSBalint Dobszay 	binh = handle_lookup(&sys_ctx->db, handle);
486cbe7e1b8SBalint Dobszay 	if (!binh)
487cbe7e1b8SBalint Dobszay 		return TEE_ERROR_BAD_PARAMETERS;
488cbe7e1b8SBalint Dobszay 
489ef44161fSJens Wiklander 	return binh_copy_to(binh, 0, (vaddr_t)dst, offs, num_bytes);
490cbe7e1b8SBalint Dobszay }
491cbe7e1b8SBalint Dobszay 
ldelf_syscall_set_prot(unsigned long va,size_t num_bytes,unsigned long flags)492baa5161dSBalint Dobszay TEE_Result ldelf_syscall_set_prot(unsigned long va, size_t num_bytes,
493baa5161dSBalint Dobszay 				  unsigned long flags)
494cbe7e1b8SBalint Dobszay {
495cbe7e1b8SBalint Dobszay 	TEE_Result res = TEE_SUCCESS;
496baa5161dSBalint Dobszay 	struct ts_session *sess = ts_get_current_session();
497baa5161dSBalint Dobszay 	struct user_mode_ctx *uctx = to_user_mode_ctx(sess->ctx);
498baa5161dSBalint Dobszay 	size_t sz = ROUNDUP(num_bytes, SMALL_PAGE_SIZE);
499baa5161dSBalint Dobszay 	uint32_t prot = TEE_MATTR_UR | TEE_MATTR_PR;
500cbe7e1b8SBalint Dobszay 	uint32_t vm_flags = 0;
501cbe7e1b8SBalint Dobszay 	vaddr_t end_va = 0;
502baa5161dSBalint Dobszay 	const uint32_t accept_flags = LDELF_MAP_FLAG_WRITEABLE |
5030d482f82SRuchika Gupta 				      LDELF_MAP_FLAG_BTI |
504baa5161dSBalint Dobszay 				      LDELF_MAP_FLAG_EXECUTABLE;
505cbe7e1b8SBalint Dobszay 
506cbe7e1b8SBalint Dobszay 	if ((flags & accept_flags) != flags)
507cbe7e1b8SBalint Dobszay 		return TEE_ERROR_BAD_PARAMETERS;
508baa5161dSBalint Dobszay 	if (flags & LDELF_MAP_FLAG_WRITEABLE)
509cbe7e1b8SBalint Dobszay 		prot |= TEE_MATTR_UW | TEE_MATTR_PW;
510baa5161dSBalint Dobszay 	if (flags & LDELF_MAP_FLAG_EXECUTABLE)
511cbe7e1b8SBalint Dobszay 		prot |= TEE_MATTR_UX;
5120d482f82SRuchika Gupta 	if (flags & LDELF_MAP_FLAG_BTI)
5130d482f82SRuchika Gupta 		prot |= TEE_MATTR_GUARDED;
514cbe7e1b8SBalint Dobszay 
515cbe7e1b8SBalint Dobszay 	/*
516baa5161dSBalint Dobszay 	 * The vm_get_flags() and vm_unmap() are supposed to detect or handle
517baa5161dSBalint Dobszay 	 * overflow directly or indirectly. However, since this function is an
518baa5161dSBalint Dobszay 	 * API function it's worth having an extra guard here. If nothing else,
519baa5161dSBalint Dobszay 	 * to increase code clarity.
520cbe7e1b8SBalint Dobszay 	 */
521cbe7e1b8SBalint Dobszay 	if (ADD_OVERFLOW(va, sz, &end_va))
522cbe7e1b8SBalint Dobszay 		return TEE_ERROR_BAD_PARAMETERS;
523cbe7e1b8SBalint Dobszay 
524cbe7e1b8SBalint Dobszay 	res = vm_get_flags(uctx, va, sz, &vm_flags);
525cbe7e1b8SBalint Dobszay 	if (res)
526cbe7e1b8SBalint Dobszay 		return res;
527cbe7e1b8SBalint Dobszay 	if (vm_flags & VM_FLAG_PERMANENT)
528cbe7e1b8SBalint Dobszay 		return TEE_ERROR_ACCESS_DENIED;
529cbe7e1b8SBalint Dobszay 
530cbe7e1b8SBalint Dobszay 	/*
531cbe7e1b8SBalint Dobszay 	 * If the segment is a mapping of a part of a file (vm_flags &
532cbe7e1b8SBalint Dobszay 	 * VM_FLAG_READONLY) it cannot be made writeable as all mapped
533cbe7e1b8SBalint Dobszay 	 * files are mapped read-only.
534cbe7e1b8SBalint Dobszay 	 */
535cbe7e1b8SBalint Dobszay 	if ((vm_flags & VM_FLAG_READONLY) &&
536cbe7e1b8SBalint Dobszay 	    (prot & (TEE_MATTR_UW | TEE_MATTR_PW)))
537cbe7e1b8SBalint Dobszay 		return TEE_ERROR_ACCESS_DENIED;
538cbe7e1b8SBalint Dobszay 
539cbe7e1b8SBalint Dobszay 	return vm_set_prot(uctx, va, sz, prot);
540cbe7e1b8SBalint Dobszay }
541cbe7e1b8SBalint Dobszay 
ldelf_syscall_remap(unsigned long old_va,vaddr_t * new_va,size_t num_bytes,size_t pad_begin,size_t pad_end)542baa5161dSBalint Dobszay TEE_Result ldelf_syscall_remap(unsigned long old_va, vaddr_t *new_va,
543baa5161dSBalint Dobszay 			       size_t num_bytes, size_t pad_begin,
544baa5161dSBalint Dobszay 			       size_t pad_end)
545cbe7e1b8SBalint Dobszay {
546cbe7e1b8SBalint Dobszay 	TEE_Result res = TEE_SUCCESS;
547baa5161dSBalint Dobszay 	struct ts_session *sess = ts_get_current_session();
548baa5161dSBalint Dobszay 	struct user_mode_ctx *uctx = to_user_mode_ctx(sess->ctx);
549cbe7e1b8SBalint Dobszay 	uint32_t vm_flags = 0;
550*f7e461dcSJens Wiklander 	vaddr_t va_copy = 0;
551cbe7e1b8SBalint Dobszay 
552*f7e461dcSJens Wiklander 	res = GET_USER_SCALAR(va_copy, new_va);
553*f7e461dcSJens Wiklander 	if (res)
554*f7e461dcSJens Wiklander 		return res;
555cbe7e1b8SBalint Dobszay 	res = vm_get_flags(uctx, old_va, num_bytes, &vm_flags);
556cbe7e1b8SBalint Dobszay 	if (res)
557cbe7e1b8SBalint Dobszay 		return res;
558cbe7e1b8SBalint Dobszay 	if (vm_flags & VM_FLAG_PERMANENT)
559cbe7e1b8SBalint Dobszay 		return TEE_ERROR_ACCESS_DENIED;
560cbe7e1b8SBalint Dobszay 
561*f7e461dcSJens Wiklander 	res = vm_remap(uctx, &va_copy, old_va, num_bytes, pad_begin, pad_end);
562*f7e461dcSJens Wiklander 	if (res)
563cbe7e1b8SBalint Dobszay 		return res;
564*f7e461dcSJens Wiklander 
565*f7e461dcSJens Wiklander 	res = PUT_USER_SCALAR(va_copy, new_va);
566*f7e461dcSJens Wiklander 	if (res) {
567*f7e461dcSJens Wiklander 		TEE_Result res2 = TEE_SUCCESS;
568*f7e461dcSJens Wiklander 		vaddr_t va = old_va;
569*f7e461dcSJens Wiklander 
570*f7e461dcSJens Wiklander 		res2 = vm_remap(uctx, &va, va_copy, num_bytes, 0, 0);
571*f7e461dcSJens Wiklander 		if (res2) {
572*f7e461dcSJens Wiklander 			EMSG("vm_remap(%#"PRIxVA", %#"PRIxVA", %#zx) returned %#"PRIx32,
573*f7e461dcSJens Wiklander 			     va, va_copy, num_bytes, res2);
574*f7e461dcSJens Wiklander 			panic("Can't restore memory map");
575*f7e461dcSJens Wiklander 		}
576*f7e461dcSJens Wiklander 		return res;
577*f7e461dcSJens Wiklander 	}
578*f7e461dcSJens Wiklander 
579*f7e461dcSJens Wiklander 	return TEE_SUCCESS;
580cbe7e1b8SBalint Dobszay }
581baa5161dSBalint Dobszay 
ldelf_syscall_gen_rnd_num(void * buf,size_t num_bytes)582baa5161dSBalint Dobszay TEE_Result ldelf_syscall_gen_rnd_num(void *buf, size_t num_bytes)
583baa5161dSBalint Dobszay {
584baa5161dSBalint Dobszay 	TEE_Result res = TEE_SUCCESS;
585*f7e461dcSJens Wiklander 	void *bb = NULL;
586baa5161dSBalint Dobszay 
587*f7e461dcSJens Wiklander 	bb = bb_alloc(num_bytes);
588*f7e461dcSJens Wiklander 	if (!bb)
589*f7e461dcSJens Wiklander 		return TEE_ERROR_OUT_OF_MEMORY;
590*f7e461dcSJens Wiklander 
591*f7e461dcSJens Wiklander 	res = crypto_rng_read(bb, num_bytes);
592baa5161dSBalint Dobszay 	if (res)
593baa5161dSBalint Dobszay 		return res;
594baa5161dSBalint Dobszay 
595*f7e461dcSJens Wiklander 	return copy_to_user(buf, bb, num_bytes);
596baa5161dSBalint Dobszay }
597baa5161dSBalint Dobszay 
598baa5161dSBalint Dobszay /*
599baa5161dSBalint Dobszay  * Should be called after returning from ldelf. If user_ctx is not NULL means
600baa5161dSBalint Dobszay  * that ldelf crashed or otherwise didn't complete properly. This function will
601baa5161dSBalint Dobszay  * close the remaining handles and free the context structs allocated by ldelf.
602baa5161dSBalint Dobszay  */
ldelf_sess_cleanup(struct ts_session * sess)603baa5161dSBalint Dobszay void ldelf_sess_cleanup(struct ts_session *sess)
604baa5161dSBalint Dobszay {
605baa5161dSBalint Dobszay 	struct system_ctx *sys_ctx = sess->user_ctx;
606baa5161dSBalint Dobszay 
607baa5161dSBalint Dobszay 	if (sys_ctx) {
608baa5161dSBalint Dobszay 		handle_db_destroy(&sys_ctx->db, bin_close);
609baa5161dSBalint Dobszay 		free(sys_ctx);
610baa5161dSBalint Dobszay 		sess->user_ctx = NULL;
611baa5161dSBalint Dobszay 	}
612baa5161dSBalint Dobszay }
613