xref: /optee_os/core/kernel/user_access.c (revision e5aa0f8cc69645dc4f0caffa53e905e59da2a92a)
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 
141*e5aa0f8cSSeonghyun Park TEE_Result clear_user(void *uaddr, size_t n)
142*e5aa0f8cSSeonghyun Park {
143*e5aa0f8cSSeonghyun Park 	uint32_t flags = TEE_MEMORY_ACCESS_WRITE | TEE_MEMORY_ACCESS_ANY_OWNER;
144*e5aa0f8cSSeonghyun Park 	TEE_Result res = TEE_SUCCESS;
145*e5aa0f8cSSeonghyun Park 
146*e5aa0f8cSSeonghyun Park 	uaddr = memtag_strip_tag(uaddr);
147*e5aa0f8cSSeonghyun Park 	res = check_access(flags, uaddr, n);
148*e5aa0f8cSSeonghyun Park 	if (res)
149*e5aa0f8cSSeonghyun Park 		return res;
150*e5aa0f8cSSeonghyun Park 
151*e5aa0f8cSSeonghyun Park 	memset(uaddr, 0, n);
152*e5aa0f8cSSeonghyun Park 
153*e5aa0f8cSSeonghyun Park 	return TEE_SUCCESS;
154*e5aa0f8cSSeonghyun Park }
155*e5aa0f8cSSeonghyun Park 
156*e5aa0f8cSSeonghyun Park size_t strnlen_user(const void *uaddr, size_t len)
157*e5aa0f8cSSeonghyun Park {
158*e5aa0f8cSSeonghyun Park 	uint32_t flags = TEE_MEMORY_ACCESS_READ | TEE_MEMORY_ACCESS_ANY_OWNER;
159*e5aa0f8cSSeonghyun Park 	TEE_Result res = TEE_SUCCESS;
160*e5aa0f8cSSeonghyun Park 	size_t n = 0;
161*e5aa0f8cSSeonghyun Park 
162*e5aa0f8cSSeonghyun Park 	uaddr = memtag_strip_tag_const(uaddr);
163*e5aa0f8cSSeonghyun Park 	res = check_access(flags, uaddr, len);
164*e5aa0f8cSSeonghyun Park 	if (!res)
165*e5aa0f8cSSeonghyun Park 		n = strnlen(uaddr, len);
166*e5aa0f8cSSeonghyun Park 
167*e5aa0f8cSSeonghyun Park 	return n;
168*e5aa0f8cSSeonghyun Park }
169*e5aa0f8cSSeonghyun Park 
170*e5aa0f8cSSeonghyun Park TEE_Result bb_memdup_user(const void *src, size_t len, void **p)
171*e5aa0f8cSSeonghyun Park {
172*e5aa0f8cSSeonghyun Park 	TEE_Result res = TEE_SUCCESS;
173*e5aa0f8cSSeonghyun Park 	void *buf = NULL;
174*e5aa0f8cSSeonghyun Park 
175*e5aa0f8cSSeonghyun Park 	buf = bb_alloc(len);
176*e5aa0f8cSSeonghyun Park 	if (!buf)
177*e5aa0f8cSSeonghyun Park 		return TEE_ERROR_OUT_OF_MEMORY;
178*e5aa0f8cSSeonghyun Park 
179*e5aa0f8cSSeonghyun Park 	res = copy_from_user(buf, src, len);
180*e5aa0f8cSSeonghyun Park 	if (res)
181*e5aa0f8cSSeonghyun Park 		bb_free(buf, len);
182*e5aa0f8cSSeonghyun Park 	else
183*e5aa0f8cSSeonghyun Park 		*p = buf;
184*e5aa0f8cSSeonghyun Park 
185*e5aa0f8cSSeonghyun Park 	return res;
186*e5aa0f8cSSeonghyun Park }
187*e5aa0f8cSSeonghyun Park 
188*e5aa0f8cSSeonghyun Park TEE_Result bb_memdup_user_private(const void *src, size_t len, void **p)
189*e5aa0f8cSSeonghyun Park {
190*e5aa0f8cSSeonghyun Park 	TEE_Result res = TEE_SUCCESS;
191*e5aa0f8cSSeonghyun Park 	void *buf = NULL;
192*e5aa0f8cSSeonghyun Park 
193*e5aa0f8cSSeonghyun Park 	buf = bb_alloc(len);
194*e5aa0f8cSSeonghyun Park 	if (!buf)
195*e5aa0f8cSSeonghyun Park 		return TEE_ERROR_OUT_OF_MEMORY;
196*e5aa0f8cSSeonghyun Park 
197*e5aa0f8cSSeonghyun Park 	res = copy_from_user_private(buf, src, len);
198*e5aa0f8cSSeonghyun Park 	if (res)
199*e5aa0f8cSSeonghyun Park 		bb_free(buf, len);
200*e5aa0f8cSSeonghyun Park 	else
201*e5aa0f8cSSeonghyun Park 		*p = buf;
202*e5aa0f8cSSeonghyun Park 
203*e5aa0f8cSSeonghyun Park 	return res;
204*e5aa0f8cSSeonghyun Park }
205*e5aa0f8cSSeonghyun Park 
206c40a6505SJens Wiklander TEE_Result copy_kaddr_to_uref(uint32_t *uref, void *kaddr)
207c40a6505SJens Wiklander {
208c40a6505SJens Wiklander 	uint32_t ref = kaddr_to_uref(kaddr);
209c40a6505SJens Wiklander 
2107e4100f3SJens Wiklander 	return copy_to_user_private(uref, &ref, sizeof(ref));
211c40a6505SJens Wiklander }
212c40a6505SJens Wiklander 
213c40a6505SJens Wiklander uint32_t kaddr_to_uref(void *kaddr)
214c40a6505SJens Wiklander {
215a0e8ffe9SJens Wiklander 	if (MEMTAG_IS_ENABLED) {
216a0e8ffe9SJens Wiklander 		unsigned int uref_tag_shift = 32 - MEMTAG_TAG_WIDTH;
217a0e8ffe9SJens Wiklander 		vaddr_t uref = memtag_strip_tag_vaddr(kaddr);
218a0e8ffe9SJens Wiklander 
219a0e8ffe9SJens Wiklander 		uref -= VCORE_START_VA;
220a0e8ffe9SJens Wiklander 		assert(uref < (UINT32_MAX >> MEMTAG_TAG_WIDTH));
221a0e8ffe9SJens Wiklander 		uref |= memtag_get_tag(kaddr) << uref_tag_shift;
222a0e8ffe9SJens Wiklander 		return uref;
223a0e8ffe9SJens Wiklander 	}
224a0e8ffe9SJens Wiklander 
225c40a6505SJens Wiklander 	assert(((vaddr_t)kaddr - VCORE_START_VA) < UINT32_MAX);
226c40a6505SJens Wiklander 	return (vaddr_t)kaddr - VCORE_START_VA;
227c40a6505SJens Wiklander }
228c40a6505SJens Wiklander 
229c40a6505SJens Wiklander vaddr_t uref_to_vaddr(uint32_t uref)
230c40a6505SJens Wiklander {
231a0e8ffe9SJens Wiklander 	if (MEMTAG_IS_ENABLED) {
232a0e8ffe9SJens Wiklander 		vaddr_t u = uref & (UINT32_MAX >> MEMTAG_TAG_WIDTH);
233a0e8ffe9SJens Wiklander 		unsigned int uref_tag_shift = 32 - MEMTAG_TAG_WIDTH;
234a0e8ffe9SJens Wiklander 		uint8_t tag = uref >> uref_tag_shift;
235a0e8ffe9SJens Wiklander 
236a0e8ffe9SJens Wiklander 		return memtag_insert_tag_vaddr(VCORE_START_VA + u, tag);
237a0e8ffe9SJens Wiklander 	}
238a0e8ffe9SJens Wiklander 
239c40a6505SJens Wiklander 	return VCORE_START_VA + uref;
240c40a6505SJens Wiklander }
241