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 37e59bc1dbSJens Wiklander TEE_Result check_user_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); 53e59bc1dbSJens Wiklander res = check_user_access(flags, uaddr, len); 544e154320SSeonghyun Park if (!res) { 554e154320SSeonghyun Park enter_user_access(); 56c40a6505SJens Wiklander memcpy(kaddr, uaddr, len); 574e154320SSeonghyun Park exit_user_access(); 584e154320SSeonghyun Park } 597e4100f3SJens Wiklander 607e4100f3SJens Wiklander return res; 61c40a6505SJens Wiklander } 62c40a6505SJens Wiklander 63c40a6505SJens Wiklander TEE_Result copy_to_user(void *uaddr, const void *kaddr, size_t len) 64c40a6505SJens Wiklander { 657e4100f3SJens Wiklander uint32_t flags = TEE_MEMORY_ACCESS_WRITE | TEE_MEMORY_ACCESS_ANY_OWNER; 66ef142203SJens Wiklander TEE_Result res = TEE_SUCCESS; 67c40a6505SJens Wiklander 68ef142203SJens Wiklander uaddr = memtag_strip_tag(uaddr); 69e59bc1dbSJens Wiklander res = check_user_access(flags, uaddr, len); 704e154320SSeonghyun Park if (!res) { 714e154320SSeonghyun Park enter_user_access(); 72c40a6505SJens Wiklander memcpy(uaddr, kaddr, len); 734e154320SSeonghyun Park exit_user_access(); 744e154320SSeonghyun Park } 757e4100f3SJens Wiklander 767e4100f3SJens Wiklander return res; 777e4100f3SJens Wiklander } 787e4100f3SJens Wiklander 797e4100f3SJens Wiklander TEE_Result copy_from_user_private(void *kaddr, const void *uaddr, size_t len) 807e4100f3SJens Wiklander { 817e4100f3SJens Wiklander uint32_t flags = TEE_MEMORY_ACCESS_READ; 82ef142203SJens Wiklander TEE_Result res = TEE_SUCCESS; 837e4100f3SJens Wiklander 84ef142203SJens Wiklander uaddr = memtag_strip_tag_const(uaddr); 85e59bc1dbSJens Wiklander res = check_user_access(flags, uaddr, len); 864e154320SSeonghyun Park if (!res) { 874e154320SSeonghyun Park enter_user_access(); 887e4100f3SJens Wiklander memcpy(kaddr, uaddr, len); 894e154320SSeonghyun Park exit_user_access(); 904e154320SSeonghyun Park } 917e4100f3SJens Wiklander 927e4100f3SJens Wiklander return res; 937e4100f3SJens Wiklander } 947e4100f3SJens Wiklander 957e4100f3SJens Wiklander TEE_Result copy_to_user_private(void *uaddr, const void *kaddr, size_t len) 967e4100f3SJens Wiklander { 977e4100f3SJens Wiklander uint32_t flags = TEE_MEMORY_ACCESS_WRITE; 98ef142203SJens Wiklander TEE_Result res = TEE_SUCCESS; 997e4100f3SJens Wiklander 100ef142203SJens Wiklander uaddr = memtag_strip_tag(uaddr); 101e59bc1dbSJens Wiklander res = check_user_access(flags, uaddr, len); 1024e154320SSeonghyun Park if (!res) { 1034e154320SSeonghyun Park enter_user_access(); 1047e4100f3SJens Wiklander memcpy(uaddr, kaddr, len); 1054e154320SSeonghyun Park exit_user_access(); 1064e154320SSeonghyun Park } 1077e4100f3SJens Wiklander 1087e4100f3SJens Wiklander return res; 109c40a6505SJens Wiklander } 110c40a6505SJens Wiklander 111e980c685SJens Wiklander static void *maybe_tag_bb(void *buf, size_t sz) 112e980c685SJens Wiklander { 113e980c685SJens Wiklander static_assert(MEMTAG_GRANULE_SIZE <= BB_ALIGNMENT); 114e980c685SJens Wiklander 115e980c685SJens Wiklander if (!MEMTAG_IS_ENABLED) 116e980c685SJens Wiklander return buf; 117e980c685SJens Wiklander 118e980c685SJens Wiklander assert(!((vaddr_t)buf % MEMTAG_GRANULE_SIZE)); 119e980c685SJens Wiklander return memtag_set_random_tags(buf, ROUNDUP(sz, MEMTAG_GRANULE_SIZE)); 120e980c685SJens Wiklander } 121e980c685SJens Wiklander 122e980c685SJens Wiklander static void maybe_untag_bb(void *buf, size_t sz) 123e980c685SJens Wiklander { 124e980c685SJens Wiklander if (MEMTAG_IS_ENABLED) { 125e980c685SJens Wiklander assert(!((vaddr_t)buf % MEMTAG_GRANULE_SIZE)); 126e980c685SJens Wiklander memtag_set_tags(buf, ROUNDUP(sz, MEMTAG_GRANULE_SIZE), 0); 127e980c685SJens Wiklander } 128e980c685SJens Wiklander } 129e980c685SJens Wiklander 130c5a0db99SJens Wiklander void *bb_alloc(size_t len) 131c5a0db99SJens Wiklander { 132c5a0db99SJens Wiklander struct user_mode_ctx *uctx = get_current_uctx(); 133c5a0db99SJens Wiklander size_t offs = 0; 134c5a0db99SJens Wiklander void *bb = NULL; 135c5a0db99SJens Wiklander 136c5a0db99SJens Wiklander if (uctx && !ADD_OVERFLOW(uctx->bbuf_offs, len, &offs) && 137c5a0db99SJens Wiklander offs <= uctx->bbuf_size) { 138e980c685SJens Wiklander bb = maybe_tag_bb(uctx->bbuf + uctx->bbuf_offs, len); 139c5a0db99SJens Wiklander uctx->bbuf_offs = ROUNDUP(offs, BB_ALIGNMENT); 140c5a0db99SJens Wiklander } 141c5a0db99SJens Wiklander return bb; 142c5a0db99SJens Wiklander } 143c5a0db99SJens Wiklander 144c5a0db99SJens Wiklander static void bb_free_helper(struct user_mode_ctx *uctx, vaddr_t bb, size_t len) 145c5a0db99SJens Wiklander { 146c5a0db99SJens Wiklander vaddr_t bbuf = (vaddr_t)uctx->bbuf; 147c5a0db99SJens Wiklander 148c5a0db99SJens Wiklander if (bb >= bbuf && IS_ALIGNED(bb, BB_ALIGNMENT)) { 149c5a0db99SJens Wiklander size_t prev_offs = bb - bbuf; 150c5a0db99SJens Wiklander 151e980c685SJens Wiklander /* 152e980c685SJens Wiklander * Even if we can't update offset we can still invalidate 153e980c685SJens Wiklander * the memory allocation. 154e980c685SJens Wiklander */ 155e980c685SJens Wiklander maybe_untag_bb((void *)bb, len); 156e980c685SJens Wiklander 157c5a0db99SJens Wiklander if (prev_offs + ROUNDUP(len, BB_ALIGNMENT) == uctx->bbuf_offs) 158c5a0db99SJens Wiklander uctx->bbuf_offs = prev_offs; 159c5a0db99SJens Wiklander } 160c5a0db99SJens Wiklander } 161c5a0db99SJens Wiklander 162c5a0db99SJens Wiklander void bb_free(void *bb, size_t len) 163c5a0db99SJens Wiklander { 164c5a0db99SJens Wiklander struct user_mode_ctx *uctx = get_current_uctx(); 165c5a0db99SJens Wiklander 166c5a0db99SJens Wiklander if (uctx) 167e980c685SJens Wiklander bb_free_helper(uctx, memtag_strip_tag_vaddr(bb), len); 168c5a0db99SJens Wiklander } 169c5a0db99SJens Wiklander 170b39fcd95SJens Wiklander void bb_free_wipe(void *bb, size_t len) 171b39fcd95SJens Wiklander { 172b39fcd95SJens Wiklander if (bb) 173b39fcd95SJens Wiklander memset(bb, 0, len); 174b39fcd95SJens Wiklander bb_free(bb, len); 175b39fcd95SJens Wiklander } 176b39fcd95SJens Wiklander 177c5a0db99SJens Wiklander void bb_reset(void) 178c5a0db99SJens Wiklander { 179c5a0db99SJens Wiklander struct user_mode_ctx *uctx = get_current_uctx(); 180c5a0db99SJens Wiklander 181e980c685SJens Wiklander if (uctx) { 182e980c685SJens Wiklander /* 183e980c685SJens Wiklander * Only the part up to the offset have been allocated, so 184e980c685SJens Wiklander * no need to clear tags beyond that. 185e980c685SJens Wiklander */ 186e980c685SJens Wiklander maybe_untag_bb(uctx->bbuf, uctx->bbuf_offs); 187e980c685SJens Wiklander 188c5a0db99SJens Wiklander uctx->bbuf_offs = 0; 189c5a0db99SJens Wiklander } 190e980c685SJens Wiklander } 191c5a0db99SJens Wiklander 192e5aa0f8cSSeonghyun Park TEE_Result clear_user(void *uaddr, size_t n) 193e5aa0f8cSSeonghyun Park { 194e5aa0f8cSSeonghyun Park uint32_t flags = TEE_MEMORY_ACCESS_WRITE | TEE_MEMORY_ACCESS_ANY_OWNER; 195e5aa0f8cSSeonghyun Park TEE_Result res = TEE_SUCCESS; 196e5aa0f8cSSeonghyun Park 197e5aa0f8cSSeonghyun Park uaddr = memtag_strip_tag(uaddr); 198e59bc1dbSJens Wiklander res = check_user_access(flags, uaddr, n); 199e5aa0f8cSSeonghyun Park if (res) 200e5aa0f8cSSeonghyun Park return res; 201e5aa0f8cSSeonghyun Park 2024e154320SSeonghyun Park enter_user_access(); 203e5aa0f8cSSeonghyun Park memset(uaddr, 0, n); 2044e154320SSeonghyun Park exit_user_access(); 205e5aa0f8cSSeonghyun Park 206e5aa0f8cSSeonghyun Park return TEE_SUCCESS; 207e5aa0f8cSSeonghyun Park } 208e5aa0f8cSSeonghyun Park 209e5aa0f8cSSeonghyun Park size_t strnlen_user(const void *uaddr, size_t len) 210e5aa0f8cSSeonghyun Park { 211e5aa0f8cSSeonghyun Park uint32_t flags = TEE_MEMORY_ACCESS_READ | TEE_MEMORY_ACCESS_ANY_OWNER; 212e5aa0f8cSSeonghyun Park TEE_Result res = TEE_SUCCESS; 213e5aa0f8cSSeonghyun Park size_t n = 0; 214e5aa0f8cSSeonghyun Park 215*0bba39ebSJens Wiklander if (!len) 216*0bba39ebSJens Wiklander return 0; 217*0bba39ebSJens Wiklander 218e5aa0f8cSSeonghyun Park uaddr = memtag_strip_tag_const(uaddr); 219e59bc1dbSJens Wiklander res = check_user_access(flags, uaddr, len); 2204e154320SSeonghyun Park if (!res) { 2214e154320SSeonghyun Park enter_user_access(); 222e5aa0f8cSSeonghyun Park n = strnlen(uaddr, len); 2234e154320SSeonghyun Park exit_user_access(); 2244e154320SSeonghyun Park } 225e5aa0f8cSSeonghyun Park 226e5aa0f8cSSeonghyun Park return n; 227e5aa0f8cSSeonghyun Park } 228e5aa0f8cSSeonghyun Park 229*0bba39ebSJens Wiklander static TEE_Result __bb_memdup_user(TEE_Result (*copy_func)(void *uaddr, 230*0bba39ebSJens Wiklander const void *kaddr, 231*0bba39ebSJens Wiklander size_t len), 232*0bba39ebSJens Wiklander const void *src, size_t len, void **p) 233e5aa0f8cSSeonghyun Park { 234e5aa0f8cSSeonghyun Park TEE_Result res = TEE_SUCCESS; 235e5aa0f8cSSeonghyun Park void *buf = NULL; 236e5aa0f8cSSeonghyun Park 237e5aa0f8cSSeonghyun Park buf = bb_alloc(len); 238e5aa0f8cSSeonghyun Park if (!buf) 239e5aa0f8cSSeonghyun Park return TEE_ERROR_OUT_OF_MEMORY; 240e5aa0f8cSSeonghyun Park 241*0bba39ebSJens Wiklander if (len) 242*0bba39ebSJens Wiklander res = copy_func(buf, src, len); 243*0bba39ebSJens Wiklander 244e5aa0f8cSSeonghyun Park if (res) 245e5aa0f8cSSeonghyun Park bb_free(buf, len); 246e5aa0f8cSSeonghyun Park else 247e5aa0f8cSSeonghyun Park *p = buf; 248e5aa0f8cSSeonghyun Park 249e5aa0f8cSSeonghyun Park return res; 250e5aa0f8cSSeonghyun Park } 251e5aa0f8cSSeonghyun Park 252*0bba39ebSJens Wiklander TEE_Result bb_memdup_user(const void *src, size_t len, void **p) 253*0bba39ebSJens Wiklander { 254*0bba39ebSJens Wiklander return __bb_memdup_user(copy_from_user, src, len, p); 255*0bba39ebSJens Wiklander } 256*0bba39ebSJens Wiklander 257e5aa0f8cSSeonghyun Park TEE_Result bb_memdup_user_private(const void *src, size_t len, void **p) 258e5aa0f8cSSeonghyun Park { 259*0bba39ebSJens Wiklander return __bb_memdup_user(copy_from_user_private, src, len, p); 260e5aa0f8cSSeonghyun Park } 261e5aa0f8cSSeonghyun Park 2629c99bb1dSJens Wiklander TEE_Result bb_strndup_user(const char *src, size_t maxlen, char **dst, 2639c99bb1dSJens Wiklander size_t *dstlen) 2649c99bb1dSJens Wiklander { 2659c99bb1dSJens Wiklander uint32_t flags = TEE_MEMORY_ACCESS_READ | TEE_MEMORY_ACCESS_ANY_OWNER; 2669c99bb1dSJens Wiklander TEE_Result res = TEE_SUCCESS; 2679c99bb1dSJens Wiklander size_t l = 0; 2689c99bb1dSJens Wiklander char *d = NULL; 2699c99bb1dSJens Wiklander 2709c99bb1dSJens Wiklander src = memtag_strip_tag_const(src); 271*0bba39ebSJens Wiklander if (maxlen) { 272e59bc1dbSJens Wiklander res = check_user_access(flags, src, maxlen); 2739c99bb1dSJens Wiklander if (res) 2749c99bb1dSJens Wiklander return res; 2754e154320SSeonghyun Park 2764e154320SSeonghyun Park enter_user_access(); 2779c99bb1dSJens Wiklander l = strnlen(src, maxlen); 2784e154320SSeonghyun Park exit_user_access(); 279*0bba39ebSJens Wiklander } 2804e154320SSeonghyun Park 2819c99bb1dSJens Wiklander d = bb_alloc(l + 1); 2829c99bb1dSJens Wiklander if (!d) 2839c99bb1dSJens Wiklander return TEE_ERROR_OUT_OF_MEMORY; 2844e154320SSeonghyun Park 285*0bba39ebSJens Wiklander if (l) { 2864e154320SSeonghyun Park enter_user_access(); 2879c99bb1dSJens Wiklander memcpy(d, src, l); 2884e154320SSeonghyun Park exit_user_access(); 289*0bba39ebSJens Wiklander } 2904e154320SSeonghyun Park 2919c99bb1dSJens Wiklander d[l] = 0; 2929c99bb1dSJens Wiklander 2939c99bb1dSJens Wiklander *dst = d; 2949c99bb1dSJens Wiklander *dstlen = l; 2959c99bb1dSJens Wiklander return TEE_SUCCESS; 2969c99bb1dSJens Wiklander } 2979c99bb1dSJens Wiklander 298c40a6505SJens Wiklander TEE_Result copy_kaddr_to_uref(uint32_t *uref, void *kaddr) 299c40a6505SJens Wiklander { 300c40a6505SJens Wiklander uint32_t ref = kaddr_to_uref(kaddr); 301c40a6505SJens Wiklander 3027e4100f3SJens Wiklander return copy_to_user_private(uref, &ref, sizeof(ref)); 303c40a6505SJens Wiklander } 304c40a6505SJens Wiklander 305c40a6505SJens Wiklander uint32_t kaddr_to_uref(void *kaddr) 306c40a6505SJens Wiklander { 307a0e8ffe9SJens Wiklander if (MEMTAG_IS_ENABLED) { 308a0e8ffe9SJens Wiklander unsigned int uref_tag_shift = 32 - MEMTAG_TAG_WIDTH; 309a0e8ffe9SJens Wiklander vaddr_t uref = memtag_strip_tag_vaddr(kaddr); 310a0e8ffe9SJens Wiklander 311a0e8ffe9SJens Wiklander uref -= VCORE_START_VA; 312a0e8ffe9SJens Wiklander assert(uref < (UINT32_MAX >> MEMTAG_TAG_WIDTH)); 313a0e8ffe9SJens Wiklander uref |= memtag_get_tag(kaddr) << uref_tag_shift; 314a0e8ffe9SJens Wiklander return uref; 315a0e8ffe9SJens Wiklander } 316a0e8ffe9SJens Wiklander 317c40a6505SJens Wiklander assert(((vaddr_t)kaddr - VCORE_START_VA) < UINT32_MAX); 318c40a6505SJens Wiklander return (vaddr_t)kaddr - VCORE_START_VA; 319c40a6505SJens Wiklander } 320c40a6505SJens Wiklander 321c40a6505SJens Wiklander vaddr_t uref_to_vaddr(uint32_t uref) 322c40a6505SJens Wiklander { 323a0e8ffe9SJens Wiklander if (MEMTAG_IS_ENABLED) { 324a0e8ffe9SJens Wiklander vaddr_t u = uref & (UINT32_MAX >> MEMTAG_TAG_WIDTH); 325a0e8ffe9SJens Wiklander unsigned int uref_tag_shift = 32 - MEMTAG_TAG_WIDTH; 326a0e8ffe9SJens Wiklander uint8_t tag = uref >> uref_tag_shift; 327a0e8ffe9SJens Wiklander 328a0e8ffe9SJens Wiklander return memtag_insert_tag_vaddr(VCORE_START_VA + u, tag); 329a0e8ffe9SJens Wiklander } 330a0e8ffe9SJens Wiklander 331c40a6505SJens Wiklander return VCORE_START_VA + uref; 332c40a6505SJens Wiklander } 333