xref: /optee_os/core/kernel/user_access.c (revision c5a0db9903f1889a430a5d7143e2fa8b3cff6729)
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 
17*c5a0db99SJens Wiklander #define BB_ALIGNMENT	(sizeof(long) * 2)
18*c5a0db99SJens Wiklander 
19*c5a0db99SJens Wiklander static struct user_mode_ctx *get_current_uctx(void)
20c40a6505SJens Wiklander {
2100b3b9a2SJens Wiklander 	struct ts_session *s = ts_get_current_session();
227e4100f3SJens Wiklander 
23*c5a0db99SJens Wiklander 	if (!is_user_mode_ctx(s->ctx)) {
24*c5a0db99SJens Wiklander 		/*
25*c5a0db99SJens Wiklander 		 * We may be called within a PTA session, which doesn't
26*c5a0db99SJens Wiklander 		 * have a user_mode_ctx. Here, try to retrieve the
27*c5a0db99SJens Wiklander 		 * user_mode_ctx associated with the calling session.
28*c5a0db99SJens Wiklander 		 */
29*c5a0db99SJens Wiklander 		s = TAILQ_NEXT(s, link_tsd);
30*c5a0db99SJens Wiklander 		if (!s || !is_user_mode_ctx(s->ctx))
31*c5a0db99SJens Wiklander 			return NULL;
32*c5a0db99SJens Wiklander 	}
33*c5a0db99SJens Wiklander 
34*c5a0db99SJens Wiklander 	return to_user_mode_ctx(s->ctx);
35*c5a0db99SJens Wiklander }
36*c5a0db99SJens Wiklander 
37*c5a0db99SJens Wiklander static TEE_Result check_access(uint32_t flags, const void *uaddr, size_t len)
38*c5a0db99SJens Wiklander {
39*c5a0db99SJens Wiklander 	struct user_mode_ctx *uctx = get_current_uctx();
40*c5a0db99SJens Wiklander 
41*c5a0db99SJens Wiklander 	if (!uctx)
42*c5a0db99SJens Wiklander 		return TEE_ERROR_GENERIC;
43*c5a0db99SJens Wiklander 
44*c5a0db99SJens 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 
99*c5a0db99SJens Wiklander void *bb_alloc(size_t len)
100*c5a0db99SJens Wiklander {
101*c5a0db99SJens Wiklander 	struct user_mode_ctx *uctx = get_current_uctx();
102*c5a0db99SJens Wiklander 	size_t offs = 0;
103*c5a0db99SJens Wiklander 	void *bb = NULL;
104*c5a0db99SJens Wiklander 
105*c5a0db99SJens Wiklander 	if (uctx && !ADD_OVERFLOW(uctx->bbuf_offs, len, &offs) &&
106*c5a0db99SJens Wiklander 	    offs <= uctx->bbuf_size) {
107*c5a0db99SJens Wiklander 		bb = uctx->bbuf + uctx->bbuf_offs;
108*c5a0db99SJens Wiklander 		uctx->bbuf_offs = ROUNDUP(offs, BB_ALIGNMENT);
109*c5a0db99SJens Wiklander 	}
110*c5a0db99SJens Wiklander 	return bb;
111*c5a0db99SJens Wiklander }
112*c5a0db99SJens Wiklander 
113*c5a0db99SJens Wiklander static void bb_free_helper(struct user_mode_ctx *uctx, vaddr_t bb, size_t len)
114*c5a0db99SJens Wiklander {
115*c5a0db99SJens Wiklander 	vaddr_t bbuf = (vaddr_t)uctx->bbuf;
116*c5a0db99SJens Wiklander 
117*c5a0db99SJens Wiklander 	if (bb >= bbuf && IS_ALIGNED(bb, BB_ALIGNMENT)) {
118*c5a0db99SJens Wiklander 		size_t prev_offs = bb - bbuf;
119*c5a0db99SJens Wiklander 
120*c5a0db99SJens Wiklander 		if (prev_offs + ROUNDUP(len, BB_ALIGNMENT) == uctx->bbuf_offs)
121*c5a0db99SJens Wiklander 			uctx->bbuf_offs = prev_offs;
122*c5a0db99SJens Wiklander 	}
123*c5a0db99SJens Wiklander }
124*c5a0db99SJens Wiklander 
125*c5a0db99SJens Wiklander void bb_free(void *bb, size_t len)
126*c5a0db99SJens Wiklander {
127*c5a0db99SJens Wiklander 	struct user_mode_ctx *uctx = get_current_uctx();
128*c5a0db99SJens Wiklander 
129*c5a0db99SJens Wiklander 	if (uctx)
130*c5a0db99SJens Wiklander 		bb_free_helper(uctx, (vaddr_t)bb, len);
131*c5a0db99SJens Wiklander }
132*c5a0db99SJens Wiklander 
133*c5a0db99SJens Wiklander void bb_reset(void)
134*c5a0db99SJens Wiklander {
135*c5a0db99SJens Wiklander 	struct user_mode_ctx *uctx = get_current_uctx();
136*c5a0db99SJens Wiklander 
137*c5a0db99SJens Wiklander 	if (uctx)
138*c5a0db99SJens Wiklander 		uctx->bbuf_offs = 0;
139*c5a0db99SJens Wiklander }
140*c5a0db99SJens Wiklander 
141c40a6505SJens Wiklander TEE_Result copy_kaddr_to_uref(uint32_t *uref, void *kaddr)
142c40a6505SJens Wiklander {
143c40a6505SJens Wiklander 	uint32_t ref = kaddr_to_uref(kaddr);
144c40a6505SJens Wiklander 
1457e4100f3SJens Wiklander 	return copy_to_user_private(uref, &ref, sizeof(ref));
146c40a6505SJens Wiklander }
147c40a6505SJens Wiklander 
148c40a6505SJens Wiklander uint32_t kaddr_to_uref(void *kaddr)
149c40a6505SJens Wiklander {
150a0e8ffe9SJens Wiklander 	if (MEMTAG_IS_ENABLED) {
151a0e8ffe9SJens Wiklander 		unsigned int uref_tag_shift = 32 - MEMTAG_TAG_WIDTH;
152a0e8ffe9SJens Wiklander 		vaddr_t uref = memtag_strip_tag_vaddr(kaddr);
153a0e8ffe9SJens Wiklander 
154a0e8ffe9SJens Wiklander 		uref -= VCORE_START_VA;
155a0e8ffe9SJens Wiklander 		assert(uref < (UINT32_MAX >> MEMTAG_TAG_WIDTH));
156a0e8ffe9SJens Wiklander 		uref |= memtag_get_tag(kaddr) << uref_tag_shift;
157a0e8ffe9SJens Wiklander 		return uref;
158a0e8ffe9SJens Wiklander 	}
159a0e8ffe9SJens Wiklander 
160c40a6505SJens Wiklander 	assert(((vaddr_t)kaddr - VCORE_START_VA) < UINT32_MAX);
161c40a6505SJens Wiklander 	return (vaddr_t)kaddr - VCORE_START_VA;
162c40a6505SJens Wiklander }
163c40a6505SJens Wiklander 
164c40a6505SJens Wiklander vaddr_t uref_to_vaddr(uint32_t uref)
165c40a6505SJens Wiklander {
166a0e8ffe9SJens Wiklander 	if (MEMTAG_IS_ENABLED) {
167a0e8ffe9SJens Wiklander 		vaddr_t u = uref & (UINT32_MAX >> MEMTAG_TAG_WIDTH);
168a0e8ffe9SJens Wiklander 		unsigned int uref_tag_shift = 32 - MEMTAG_TAG_WIDTH;
169a0e8ffe9SJens Wiklander 		uint8_t tag = uref >> uref_tag_shift;
170a0e8ffe9SJens Wiklander 
171a0e8ffe9SJens Wiklander 		return memtag_insert_tag_vaddr(VCORE_START_VA + u, tag);
172a0e8ffe9SJens Wiklander 	}
173a0e8ffe9SJens Wiklander 
174c40a6505SJens Wiklander 	return VCORE_START_VA + uref;
175c40a6505SJens Wiklander }
176