xref: /optee_os/core/kernel/user_access.c (revision 9c99bb1d8d879682f8bf2ca6f27a4a910cad20f5)
1c40a6505SJens Wiklander // SPDX-License-Identifier: BSD-2-Clause
2c40a6505SJens Wiklander /*
3c40a6505SJens Wiklander  * Copyright (c) 2014, STMicroelectronics International N.V.
4ef142203SJens Wiklander  * Copyright (c) 2015-2020, 2022 Linaro Limited
5c40a6505SJens Wiklander  */
6c40a6505SJens Wiklander 
7c40a6505SJens Wiklander #include <initcall.h>
8c40a6505SJens Wiklander #include <kernel/linker.h>
9c40a6505SJens Wiklander #include <kernel/user_access.h>
10c185655eSJelle Sels #include <kernel/user_mode_ctx.h>
11a0e8ffe9SJens Wiklander #include <memtag.h>
1289c9728dSJens Wiklander #include <mm/vm.h>
13c40a6505SJens Wiklander #include <string.h>
14c40a6505SJens Wiklander #include <tee_api_types.h>
15c40a6505SJens Wiklander #include <types_ext.h>
16c40a6505SJens Wiklander 
17c5a0db99SJens Wiklander #define BB_ALIGNMENT	(sizeof(long) * 2)
18c5a0db99SJens Wiklander 
19c5a0db99SJens Wiklander static struct user_mode_ctx *get_current_uctx(void)
20c40a6505SJens Wiklander {
2100b3b9a2SJens Wiklander 	struct ts_session *s = ts_get_current_session();
227e4100f3SJens Wiklander 
23c5a0db99SJens Wiklander 	if (!is_user_mode_ctx(s->ctx)) {
24c5a0db99SJens Wiklander 		/*
25c5a0db99SJens Wiklander 		 * We may be called within a PTA session, which doesn't
26c5a0db99SJens Wiklander 		 * have a user_mode_ctx. Here, try to retrieve the
27c5a0db99SJens Wiklander 		 * user_mode_ctx associated with the calling session.
28c5a0db99SJens Wiklander 		 */
29c5a0db99SJens Wiklander 		s = TAILQ_NEXT(s, link_tsd);
30c5a0db99SJens Wiklander 		if (!s || !is_user_mode_ctx(s->ctx))
31c5a0db99SJens Wiklander 			return NULL;
32c5a0db99SJens Wiklander 	}
33c5a0db99SJens Wiklander 
34c5a0db99SJens Wiklander 	return to_user_mode_ctx(s->ctx);
35c5a0db99SJens Wiklander }
36c5a0db99SJens Wiklander 
37c5a0db99SJens Wiklander static TEE_Result check_access(uint32_t flags, const void *uaddr, size_t len)
38c5a0db99SJens Wiklander {
39c5a0db99SJens Wiklander 	struct user_mode_ctx *uctx = get_current_uctx();
40c5a0db99SJens Wiklander 
41c5a0db99SJens Wiklander 	if (!uctx)
42c5a0db99SJens Wiklander 		return TEE_ERROR_GENERIC;
43c5a0db99SJens Wiklander 
44c5a0db99SJens Wiklander 	return vm_check_access_rights(uctx, flags, (vaddr_t)uaddr, len);
45c40a6505SJens Wiklander }
46c40a6505SJens Wiklander 
47c40a6505SJens Wiklander TEE_Result copy_from_user(void *kaddr, const void *uaddr, size_t len)
48c40a6505SJens Wiklander {
497e4100f3SJens Wiklander 	uint32_t flags = TEE_MEMORY_ACCESS_READ | TEE_MEMORY_ACCESS_ANY_OWNER;
50ef142203SJens Wiklander 	TEE_Result res = TEE_SUCCESS;
51c40a6505SJens Wiklander 
52ef142203SJens Wiklander 	uaddr = memtag_strip_tag_const(uaddr);
53ef142203SJens Wiklander 	res = check_access(flags, uaddr, len);
547e4100f3SJens Wiklander 	if (!res)
55c40a6505SJens Wiklander 		memcpy(kaddr, uaddr, len);
567e4100f3SJens Wiklander 
577e4100f3SJens Wiklander 	return res;
58c40a6505SJens Wiklander }
59c40a6505SJens Wiklander 
60c40a6505SJens Wiklander TEE_Result copy_to_user(void *uaddr, const void *kaddr, size_t len)
61c40a6505SJens Wiklander {
627e4100f3SJens Wiklander 	uint32_t flags = TEE_MEMORY_ACCESS_WRITE | TEE_MEMORY_ACCESS_ANY_OWNER;
63ef142203SJens Wiklander 	TEE_Result res = TEE_SUCCESS;
64c40a6505SJens Wiklander 
65ef142203SJens Wiklander 	uaddr = memtag_strip_tag(uaddr);
66ef142203SJens Wiklander 	res = check_access(flags, uaddr, len);
677e4100f3SJens Wiklander 	if (!res)
68c40a6505SJens Wiklander 		memcpy(uaddr, kaddr, len);
697e4100f3SJens Wiklander 
707e4100f3SJens Wiklander 	return res;
717e4100f3SJens Wiklander }
727e4100f3SJens Wiklander 
737e4100f3SJens Wiklander TEE_Result copy_from_user_private(void *kaddr, const void *uaddr, size_t len)
747e4100f3SJens Wiklander {
757e4100f3SJens Wiklander 	uint32_t flags = TEE_MEMORY_ACCESS_READ;
76ef142203SJens Wiklander 	TEE_Result res = TEE_SUCCESS;
777e4100f3SJens Wiklander 
78ef142203SJens Wiklander 	uaddr = memtag_strip_tag_const(uaddr);
79ef142203SJens Wiklander 	res = check_access(flags, uaddr, len);
807e4100f3SJens Wiklander 	if (!res)
817e4100f3SJens Wiklander 		memcpy(kaddr, uaddr, len);
827e4100f3SJens Wiklander 
837e4100f3SJens Wiklander 	return res;
847e4100f3SJens Wiklander }
857e4100f3SJens Wiklander 
867e4100f3SJens Wiklander TEE_Result copy_to_user_private(void *uaddr, const void *kaddr, size_t len)
877e4100f3SJens Wiklander {
887e4100f3SJens Wiklander 	uint32_t flags = TEE_MEMORY_ACCESS_WRITE;
89ef142203SJens Wiklander 	TEE_Result res = TEE_SUCCESS;
907e4100f3SJens Wiklander 
91ef142203SJens Wiklander 	uaddr = memtag_strip_tag(uaddr);
92ef142203SJens Wiklander 	res = check_access(flags, uaddr, len);
937e4100f3SJens Wiklander 	if (!res)
947e4100f3SJens Wiklander 		memcpy(uaddr, kaddr, len);
957e4100f3SJens Wiklander 
967e4100f3SJens Wiklander 	return res;
97c40a6505SJens Wiklander }
98c40a6505SJens Wiklander 
99c5a0db99SJens Wiklander void *bb_alloc(size_t len)
100c5a0db99SJens Wiklander {
101c5a0db99SJens Wiklander 	struct user_mode_ctx *uctx = get_current_uctx();
102c5a0db99SJens Wiklander 	size_t offs = 0;
103c5a0db99SJens Wiklander 	void *bb = NULL;
104c5a0db99SJens Wiklander 
105c5a0db99SJens Wiklander 	if (uctx && !ADD_OVERFLOW(uctx->bbuf_offs, len, &offs) &&
106c5a0db99SJens Wiklander 	    offs <= uctx->bbuf_size) {
107c5a0db99SJens Wiklander 		bb = uctx->bbuf + uctx->bbuf_offs;
108c5a0db99SJens Wiklander 		uctx->bbuf_offs = ROUNDUP(offs, BB_ALIGNMENT);
109c5a0db99SJens Wiklander 	}
110c5a0db99SJens Wiklander 	return bb;
111c5a0db99SJens Wiklander }
112c5a0db99SJens Wiklander 
113c5a0db99SJens Wiklander static void bb_free_helper(struct user_mode_ctx *uctx, vaddr_t bb, size_t len)
114c5a0db99SJens Wiklander {
115c5a0db99SJens Wiklander 	vaddr_t bbuf = (vaddr_t)uctx->bbuf;
116c5a0db99SJens Wiklander 
117c5a0db99SJens Wiklander 	if (bb >= bbuf && IS_ALIGNED(bb, BB_ALIGNMENT)) {
118c5a0db99SJens Wiklander 		size_t prev_offs = bb - bbuf;
119c5a0db99SJens Wiklander 
120c5a0db99SJens Wiklander 		if (prev_offs + ROUNDUP(len, BB_ALIGNMENT) == uctx->bbuf_offs)
121c5a0db99SJens Wiklander 			uctx->bbuf_offs = prev_offs;
122c5a0db99SJens Wiklander 	}
123c5a0db99SJens Wiklander }
124c5a0db99SJens Wiklander 
125c5a0db99SJens Wiklander void bb_free(void *bb, size_t len)
126c5a0db99SJens Wiklander {
127c5a0db99SJens Wiklander 	struct user_mode_ctx *uctx = get_current_uctx();
128c5a0db99SJens Wiklander 
129c5a0db99SJens Wiklander 	if (uctx)
130c5a0db99SJens Wiklander 		bb_free_helper(uctx, (vaddr_t)bb, len);
131c5a0db99SJens Wiklander }
132c5a0db99SJens Wiklander 
133c5a0db99SJens Wiklander void bb_reset(void)
134c5a0db99SJens Wiklander {
135c5a0db99SJens Wiklander 	struct user_mode_ctx *uctx = get_current_uctx();
136c5a0db99SJens Wiklander 
137c5a0db99SJens Wiklander 	if (uctx)
138c5a0db99SJens Wiklander 		uctx->bbuf_offs = 0;
139c5a0db99SJens Wiklander }
140c5a0db99SJens Wiklander 
141e5aa0f8cSSeonghyun Park TEE_Result clear_user(void *uaddr, size_t n)
142e5aa0f8cSSeonghyun Park {
143e5aa0f8cSSeonghyun Park 	uint32_t flags = TEE_MEMORY_ACCESS_WRITE | TEE_MEMORY_ACCESS_ANY_OWNER;
144e5aa0f8cSSeonghyun Park 	TEE_Result res = TEE_SUCCESS;
145e5aa0f8cSSeonghyun Park 
146e5aa0f8cSSeonghyun Park 	uaddr = memtag_strip_tag(uaddr);
147e5aa0f8cSSeonghyun Park 	res = check_access(flags, uaddr, n);
148e5aa0f8cSSeonghyun Park 	if (res)
149e5aa0f8cSSeonghyun Park 		return res;
150e5aa0f8cSSeonghyun Park 
151e5aa0f8cSSeonghyun Park 	memset(uaddr, 0, n);
152e5aa0f8cSSeonghyun Park 
153e5aa0f8cSSeonghyun Park 	return TEE_SUCCESS;
154e5aa0f8cSSeonghyun Park }
155e5aa0f8cSSeonghyun Park 
156e5aa0f8cSSeonghyun Park size_t strnlen_user(const void *uaddr, size_t len)
157e5aa0f8cSSeonghyun Park {
158e5aa0f8cSSeonghyun Park 	uint32_t flags = TEE_MEMORY_ACCESS_READ | TEE_MEMORY_ACCESS_ANY_OWNER;
159e5aa0f8cSSeonghyun Park 	TEE_Result res = TEE_SUCCESS;
160e5aa0f8cSSeonghyun Park 	size_t n = 0;
161e5aa0f8cSSeonghyun Park 
162e5aa0f8cSSeonghyun Park 	uaddr = memtag_strip_tag_const(uaddr);
163e5aa0f8cSSeonghyun Park 	res = check_access(flags, uaddr, len);
164e5aa0f8cSSeonghyun Park 	if (!res)
165e5aa0f8cSSeonghyun Park 		n = strnlen(uaddr, len);
166e5aa0f8cSSeonghyun Park 
167e5aa0f8cSSeonghyun Park 	return n;
168e5aa0f8cSSeonghyun Park }
169e5aa0f8cSSeonghyun Park 
170e5aa0f8cSSeonghyun Park TEE_Result bb_memdup_user(const void *src, size_t len, void **p)
171e5aa0f8cSSeonghyun Park {
172e5aa0f8cSSeonghyun Park 	TEE_Result res = TEE_SUCCESS;
173e5aa0f8cSSeonghyun Park 	void *buf = NULL;
174e5aa0f8cSSeonghyun Park 
175e5aa0f8cSSeonghyun Park 	buf = bb_alloc(len);
176e5aa0f8cSSeonghyun Park 	if (!buf)
177e5aa0f8cSSeonghyun Park 		return TEE_ERROR_OUT_OF_MEMORY;
178e5aa0f8cSSeonghyun Park 
179e5aa0f8cSSeonghyun Park 	res = copy_from_user(buf, src, len);
180e5aa0f8cSSeonghyun Park 	if (res)
181e5aa0f8cSSeonghyun Park 		bb_free(buf, len);
182e5aa0f8cSSeonghyun Park 	else
183e5aa0f8cSSeonghyun Park 		*p = buf;
184e5aa0f8cSSeonghyun Park 
185e5aa0f8cSSeonghyun Park 	return res;
186e5aa0f8cSSeonghyun Park }
187e5aa0f8cSSeonghyun Park 
188e5aa0f8cSSeonghyun Park TEE_Result bb_memdup_user_private(const void *src, size_t len, void **p)
189e5aa0f8cSSeonghyun Park {
190e5aa0f8cSSeonghyun Park 	TEE_Result res = TEE_SUCCESS;
191e5aa0f8cSSeonghyun Park 	void *buf = NULL;
192e5aa0f8cSSeonghyun Park 
193e5aa0f8cSSeonghyun Park 	buf = bb_alloc(len);
194e5aa0f8cSSeonghyun Park 	if (!buf)
195e5aa0f8cSSeonghyun Park 		return TEE_ERROR_OUT_OF_MEMORY;
196e5aa0f8cSSeonghyun Park 
197e5aa0f8cSSeonghyun Park 	res = copy_from_user_private(buf, src, len);
198e5aa0f8cSSeonghyun Park 	if (res)
199e5aa0f8cSSeonghyun Park 		bb_free(buf, len);
200e5aa0f8cSSeonghyun Park 	else
201e5aa0f8cSSeonghyun Park 		*p = buf;
202e5aa0f8cSSeonghyun Park 
203e5aa0f8cSSeonghyun Park 	return res;
204e5aa0f8cSSeonghyun Park }
205e5aa0f8cSSeonghyun Park 
206*9c99bb1dSJens Wiklander TEE_Result bb_strndup_user(const char *src, size_t maxlen, char **dst,
207*9c99bb1dSJens Wiklander 			   size_t *dstlen)
208*9c99bb1dSJens Wiklander {
209*9c99bb1dSJens Wiklander 	uint32_t flags = TEE_MEMORY_ACCESS_READ | TEE_MEMORY_ACCESS_ANY_OWNER;
210*9c99bb1dSJens Wiklander 	TEE_Result res = TEE_SUCCESS;
211*9c99bb1dSJens Wiklander 	size_t l = 0;
212*9c99bb1dSJens Wiklander 	char *d = NULL;
213*9c99bb1dSJens Wiklander 
214*9c99bb1dSJens Wiklander 	src = memtag_strip_tag_const(src);
215*9c99bb1dSJens Wiklander 	res = check_access(flags, src, maxlen);
216*9c99bb1dSJens Wiklander 	if (res)
217*9c99bb1dSJens Wiklander 		return res;
218*9c99bb1dSJens Wiklander 	l = strnlen(src, maxlen);
219*9c99bb1dSJens Wiklander 	d = bb_alloc(l + 1);
220*9c99bb1dSJens Wiklander 	if (!d)
221*9c99bb1dSJens Wiklander 		return TEE_ERROR_OUT_OF_MEMORY;
222*9c99bb1dSJens Wiklander 	memcpy(d, src, l);
223*9c99bb1dSJens Wiklander 	d[l] = 0;
224*9c99bb1dSJens Wiklander 
225*9c99bb1dSJens Wiklander 	*dst = d;
226*9c99bb1dSJens Wiklander 	*dstlen = l;
227*9c99bb1dSJens Wiklander 	return TEE_SUCCESS;
228*9c99bb1dSJens Wiklander }
229*9c99bb1dSJens Wiklander 
230c40a6505SJens Wiklander TEE_Result copy_kaddr_to_uref(uint32_t *uref, void *kaddr)
231c40a6505SJens Wiklander {
232c40a6505SJens Wiklander 	uint32_t ref = kaddr_to_uref(kaddr);
233c40a6505SJens Wiklander 
2347e4100f3SJens Wiklander 	return copy_to_user_private(uref, &ref, sizeof(ref));
235c40a6505SJens Wiklander }
236c40a6505SJens Wiklander 
237c40a6505SJens Wiklander uint32_t kaddr_to_uref(void *kaddr)
238c40a6505SJens Wiklander {
239a0e8ffe9SJens Wiklander 	if (MEMTAG_IS_ENABLED) {
240a0e8ffe9SJens Wiklander 		unsigned int uref_tag_shift = 32 - MEMTAG_TAG_WIDTH;
241a0e8ffe9SJens Wiklander 		vaddr_t uref = memtag_strip_tag_vaddr(kaddr);
242a0e8ffe9SJens Wiklander 
243a0e8ffe9SJens Wiklander 		uref -= VCORE_START_VA;
244a0e8ffe9SJens Wiklander 		assert(uref < (UINT32_MAX >> MEMTAG_TAG_WIDTH));
245a0e8ffe9SJens Wiklander 		uref |= memtag_get_tag(kaddr) << uref_tag_shift;
246a0e8ffe9SJens Wiklander 		return uref;
247a0e8ffe9SJens Wiklander 	}
248a0e8ffe9SJens Wiklander 
249c40a6505SJens Wiklander 	assert(((vaddr_t)kaddr - VCORE_START_VA) < UINT32_MAX);
250c40a6505SJens Wiklander 	return (vaddr_t)kaddr - VCORE_START_VA;
251c40a6505SJens Wiklander }
252c40a6505SJens Wiklander 
253c40a6505SJens Wiklander vaddr_t uref_to_vaddr(uint32_t uref)
254c40a6505SJens Wiklander {
255a0e8ffe9SJens Wiklander 	if (MEMTAG_IS_ENABLED) {
256a0e8ffe9SJens Wiklander 		vaddr_t u = uref & (UINT32_MAX >> MEMTAG_TAG_WIDTH);
257a0e8ffe9SJens Wiklander 		unsigned int uref_tag_shift = 32 - MEMTAG_TAG_WIDTH;
258a0e8ffe9SJens Wiklander 		uint8_t tag = uref >> uref_tag_shift;
259a0e8ffe9SJens Wiklander 
260a0e8ffe9SJens Wiklander 		return memtag_insert_tag_vaddr(VCORE_START_VA + u, tag);
261a0e8ffe9SJens Wiklander 	}
262a0e8ffe9SJens Wiklander 
263c40a6505SJens Wiklander 	return VCORE_START_VA + uref;
264c40a6505SJens Wiklander }
265