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