1 // SPDX-License-Identifier: BSD-2-Clause 2 /* 3 * Copyright (c) 2016-2020, Linaro Limited 4 */ 5 6 #include <assert.h> 7 #include <bitstring.h> 8 #include <config.h> 9 #include <ffa.h> 10 #include <initcall.h> 11 #include <kernel/refcount.h> 12 #include <kernel/spinlock.h> 13 #include <kernel/tee_misc.h> 14 #include <kernel/thread_spmc.h> 15 #include <kernel/virtualization.h> 16 #include <mm/mobj.h> 17 #include <sys/queue.h> 18 19 /* 20 * Life cycle of struct mobj_ffa 21 * 22 * SPMC at S-EL1 (CFG_CORE_SEL1_SPMC=y) 23 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 24 * During FFA_MEM_SHARE allocated in mobj_ffa_sel1_spmc_new() and finally 25 * added to the inactive list at the end of add_mem_share() once 26 * successfully filled in. 27 * registered_by_cookie = false 28 * mobj.refs.val = 0 29 * inactive_refs = 0 30 * 31 * During FFA_MEM_RECLAIM reclaimed/freed using 32 * mobj_ffa_sel1_spmc_reclaim(). This will always succeed if the normal 33 * world is only calling this when all other threads are done with the 34 * shared memory object. However, there are some conditions that must be 35 * met to make sure that this is the case: 36 * mobj not in the active list, else -> return TEE_ERROR_BUSY 37 * mobj not in inactive list, else -> return TEE_ERROR_ITEM_NOT_FOUND 38 * mobj inactive_refs is 0, else -> return TEE_ERROR_BUSY 39 * 40 * mobj is activated using mobj_ffa_get_by_cookie() which unless the mobj 41 * is active already: 42 * - move the mobj into the active list 43 * - if not registered_by_cookie -> 44 * set registered_by_cookie and increase inactive_refs 45 * - set mobj.refc.val to 1 46 * - increase inactive_refs 47 * 48 * A previously activated mobj is made ready for reclaim using 49 * mobj_ffa_unregister_by_cookie() which only succeeds if the mobj is in 50 * the inactive list and registered_by_cookie is set and then: 51 * - clears registered_by_cookie 52 * - decreases inactive_refs 53 * 54 * Each successful call to mobj_ffa_get_by_cookie() must be matched by a 55 * call to mobj_put(). If the mobj.refc.val reaches 0 it's 56 * - moved to the inactive list 57 * - inactive_refs is decreased 58 * 59 * SPMC at S-EL2/EL3 (CFG_CORE_SEL1_SPMC=n) 60 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 61 * mobj is activated/allocated using mobj_ffa_get_by_cookie() which if 62 * already active only is 63 * - increasing mobj.refc.val and inactive_refs 64 * if found in inactive list is 65 * - setting mobj.refc.val to 1 66 * - increasing inactive_refs 67 * - moved into active list 68 * if not found is created using thread_spmc_populate_mobj_from_rx() and 69 * then: 70 * - setting mobj.refc.val to 1 71 * - increasing inactive_refs 72 * - moved into active list 73 * 74 * A previously activated mobj is relinquished using 75 * mobj_ffa_unregister_by_cookie() which only succeeds if the mobj is in 76 * the inactive list and inactive_refs is 1 77 */ 78 struct mobj_ffa { 79 struct mobj mobj; 80 SLIST_ENTRY(mobj_ffa) link; 81 uint64_t cookie; 82 unsigned int inactive_refs; 83 #ifdef CFG_CORE_SEL1_SPMC 84 bool registered_by_cookie; 85 #endif 86 }; 87 88 struct mobj_ffa_shm { 89 struct mobj_ffa mf; 90 tee_mm_entry_t *mm; 91 struct refcount mapcount; 92 uint16_t page_offset; 93 paddr_t pages[]; 94 }; 95 96 struct mobj_ffa_prm { 97 struct mobj_ffa mf; 98 paddr_t pa; 99 enum mobj_use_case use_case; 100 bool assigned_use_case; 101 }; 102 103 SLIST_HEAD(mobj_ffa_head, mobj_ffa); 104 105 #ifdef CFG_CORE_SEL1_SPMC 106 #ifdef CFG_NS_VIRTUALIZATION 107 static bitstr_t *get_shm_bits(void) 108 { 109 return virt_get_shm_bits(); 110 } 111 #else 112 static bitstr_t bit_decl(__shm_bits, SPMC_CORE_SEL1_MAX_SHM_COUNT); 113 114 static bitstr_t *get_shm_bits(void) 115 { 116 return __shm_bits; 117 } 118 #endif 119 #endif 120 121 static struct mobj_ffa_head shm_head = SLIST_HEAD_INITIALIZER(shm_head); 122 static struct mobj_ffa_head shm_inactive_head = 123 SLIST_HEAD_INITIALIZER(shm_inactive_head); 124 125 static unsigned int shm_lock = SPINLOCK_UNLOCK; 126 127 static const struct mobj_ops mobj_ffa_shm_ops; 128 static const struct mobj_ops mobj_ffa_prm_ops; 129 130 static bool is_mobj_ffa_shm(struct mobj *mobj) 131 { 132 return mobj->ops == &mobj_ffa_shm_ops; 133 } 134 135 static struct mobj_ffa_shm *to_mobj_ffa_shm(struct mobj *mobj) 136 { 137 assert(is_mobj_ffa_shm(mobj)); 138 return container_of(mobj, struct mobj_ffa_shm, mf.mobj); 139 } 140 141 static bool is_mobj_ffa_prm(struct mobj *mobj) 142 { 143 return mobj->ops == &mobj_ffa_prm_ops; 144 } 145 146 static struct mobj_ffa_prm *to_mobj_ffa_prm(struct mobj *mobj) 147 { 148 assert(is_mobj_ffa_prm(mobj)); 149 return container_of(mobj, struct mobj_ffa_prm, mf.mobj); 150 } 151 152 static size_t shm_size(size_t num_pages) 153 { 154 size_t s = 0; 155 156 if (MUL_OVERFLOW(sizeof(paddr_t), num_pages, &s)) 157 return 0; 158 if (ADD_OVERFLOW(sizeof(struct mobj_ffa_shm), s, &s)) 159 return 0; 160 return s; 161 } 162 163 static struct mobj_ffa *ffa_shm_new(unsigned int num_pages) 164 { 165 struct mobj_ffa_shm *m = NULL; 166 size_t s = 0; 167 168 if (!num_pages) 169 return NULL; 170 171 s = shm_size(num_pages); 172 if (!s) 173 return NULL; 174 m = calloc(1, s); 175 if (!m) 176 return NULL; 177 178 m->mf.mobj.ops = &mobj_ffa_shm_ops; 179 m->mf.mobj.size = num_pages * SMALL_PAGE_SIZE; 180 m->mf.mobj.phys_granule = SMALL_PAGE_SIZE; 181 refcount_set(&m->mf.mobj.refc, 0); 182 m->mf.inactive_refs = 0; 183 184 return &m->mf; 185 } 186 187 static struct mobj_ffa *ffa_prm_new(unsigned int num_pages, 188 enum mobj_use_case use_case) 189 { 190 struct mobj_ffa_prm *m = NULL; 191 size_t sz = 0; 192 193 if (!num_pages || MUL_OVERFLOW(num_pages, SMALL_PAGE_SIZE, &sz) || 194 use_case == MOBJ_USE_CASE_NS_SHM) 195 return NULL; 196 197 m = calloc(1, sizeof(*m)); 198 if (!m) 199 return NULL; 200 201 m->mf.mobj.ops = &mobj_ffa_prm_ops; 202 m->mf.mobj.size = sz; 203 m->mf.mobj.phys_granule = SMALL_PAGE_SIZE; 204 refcount_set(&m->mf.mobj.refc, 0); 205 m->mf.inactive_refs = 0; 206 m->use_case = use_case; 207 208 return &m->mf; 209 } 210 211 #ifdef CFG_CORE_SEL1_SPMC 212 struct mobj_ffa *mobj_ffa_sel1_spmc_new(uint64_t cookie, 213 unsigned int num_pages, 214 enum mobj_use_case use_case) 215 { 216 struct mobj_ffa *m = NULL; 217 bitstr_t *shm_bits = NULL; 218 uint32_t exceptions = 0; 219 int i = 0; 220 221 if (cookie != OPTEE_MSG_FMEM_INVALID_GLOBAL_ID) { 222 if (!(cookie & FFA_MEMORY_HANDLE_HYPERVISOR_BIT)) 223 return NULL; 224 if (virt_add_cookie_to_current_guest(cookie)) 225 return NULL; 226 } 227 228 switch (use_case) { 229 case MOBJ_USE_CASE_NS_SHM: 230 m = ffa_shm_new(num_pages); 231 break; 232 case MOBJ_USE_CASE_SEC_VIDEO_PLAY: 233 case MOBJ_USE_CASE_TRUSED_UI: 234 m = ffa_prm_new(num_pages, use_case); 235 break; 236 default: 237 break; 238 } 239 if (!m) { 240 if (cookie != OPTEE_MSG_FMEM_INVALID_GLOBAL_ID) 241 virt_remove_cookie(cookie); 242 return NULL; 243 } 244 245 if (cookie != OPTEE_MSG_FMEM_INVALID_GLOBAL_ID) { 246 m->cookie = cookie; 247 return m; 248 } 249 250 shm_bits = get_shm_bits(); 251 exceptions = cpu_spin_lock_xsave(&shm_lock); 252 bit_ffc(shm_bits, SPMC_CORE_SEL1_MAX_SHM_COUNT, &i); 253 if (i != -1) { 254 bit_set(shm_bits, i); 255 m->cookie = i; 256 m->cookie |= FFA_MEMORY_HANDLE_NON_SECURE_BIT; 257 /* 258 * Encode the partition ID into the handle so we know which 259 * partition to switch to when reclaiming a handle. 260 */ 261 m->cookie |= SHIFT_U64(virt_get_current_guest_id(), 262 FFA_MEMORY_HANDLE_PRTN_SHIFT); 263 } 264 cpu_spin_unlock_xrestore(&shm_lock, exceptions); 265 266 if (i == -1) { 267 mobj_ffa_sel1_spmc_delete(m); 268 return NULL; 269 } 270 271 return m; 272 } 273 #endif /*CFG_CORE_SEL1_SPMC*/ 274 275 static size_t get_page_count(struct mobj_ffa *mf) 276 { 277 return ROUNDUP_DIV(mf->mobj.size, SMALL_PAGE_SIZE); 278 } 279 280 static bool cmp_cookie(struct mobj_ffa *mf, uint64_t cookie) 281 { 282 return mf->cookie == cookie; 283 } 284 285 static bool cmp_ptr(struct mobj_ffa *mf, uint64_t ptr) 286 { 287 return mf == (void *)(vaddr_t)ptr; 288 } 289 290 static bool check_shm_overlaps_prm(struct mobj_ffa_shm *shm, 291 struct mobj_ffa_prm *prm) 292 { 293 size_t n = 0; 294 295 for (n = 0; n < shm->mf.mobj.size / SMALL_PAGE_SIZE; n++) 296 if (core_is_buffer_intersect(prm->pa, prm->mf.mobj.size, 297 shm->pages[n], SMALL_PAGE_SIZE)) 298 return true; 299 300 return false; 301 } 302 303 static bool cmp_pa_overlap(struct mobj_ffa *mf, uint64_t ptr) 304 { 305 struct mobj_ffa *mf2 = (void *)(vaddr_t)ptr; 306 bool mf_is_shm = is_mobj_ffa_shm(&mf->mobj); 307 bool mf2_is_shm = is_mobj_ffa_shm(&mf2->mobj); 308 309 if (mf_is_shm && mf2_is_shm) { 310 /* 311 * Not a security issue and might be too expensive to check 312 * if we have many pages in each registered shared memory 313 * object. 314 */ 315 return false; 316 } 317 318 if (mf_is_shm) 319 return check_shm_overlaps_prm(to_mobj_ffa_shm(&mf->mobj), 320 to_mobj_ffa_prm(&mf2->mobj)); 321 if (mf2_is_shm) 322 return check_shm_overlaps_prm(to_mobj_ffa_shm(&mf2->mobj), 323 to_mobj_ffa_prm(&mf->mobj)); 324 325 return core_is_buffer_intersect(to_mobj_ffa_prm(&mf->mobj)->pa, 326 mf->mobj.size, 327 to_mobj_ffa_prm(&mf2->mobj)->pa, 328 mf2->mobj.size); 329 } 330 331 static struct mobj_ffa *pop_from_list(struct mobj_ffa_head *head, 332 bool (*cmp_func)(struct mobj_ffa *mf, 333 uint64_t val), 334 uint64_t val) 335 { 336 struct mobj_ffa *mf = SLIST_FIRST(head); 337 struct mobj_ffa *p = NULL; 338 339 if (!mf) 340 return NULL; 341 342 if (cmp_func(mf, val)) { 343 SLIST_REMOVE_HEAD(head, link); 344 return mf; 345 } 346 347 while (true) { 348 p = SLIST_NEXT(mf, link); 349 if (!p) 350 return NULL; 351 if (cmp_func(p, val)) { 352 SLIST_REMOVE_AFTER(mf, link); 353 return p; 354 } 355 mf = p; 356 } 357 } 358 359 static struct mobj_ffa *find_in_list(struct mobj_ffa_head *head, 360 bool (*cmp_func)(struct mobj_ffa *mf, 361 uint64_t val), 362 uint64_t val) 363 { 364 struct mobj_ffa *mf = NULL; 365 366 SLIST_FOREACH(mf, head, link) 367 if (cmp_func(mf, val)) 368 return mf; 369 370 return NULL; 371 } 372 373 #if defined(CFG_CORE_SEL1_SPMC) 374 void mobj_ffa_sel1_spmc_delete(struct mobj_ffa *mf) 375 { 376 if (!IS_ENABLED(CFG_NS_VIRTUALIZATION) || 377 !(mf->cookie & FFA_MEMORY_HANDLE_HYPERVISOR_BIT)) { 378 uint64_t mask = FFA_MEMORY_HANDLE_NON_SECURE_BIT; 379 bitstr_t *shm_bits = get_shm_bits(); 380 uint32_t exceptions = 0; 381 int64_t i = 0; 382 383 if (IS_ENABLED(CFG_NS_VIRTUALIZATION)) 384 mask |= SHIFT_U64(FFA_MEMORY_HANDLE_PRTN_MASK, 385 FFA_MEMORY_HANDLE_PRTN_SHIFT); 386 i = mf->cookie & ~mask; 387 assert(i >= 0 && i < SPMC_CORE_SEL1_MAX_SHM_COUNT); 388 389 exceptions = cpu_spin_lock_xsave(&shm_lock); 390 assert(bit_test(shm_bits, i)); 391 bit_clear(shm_bits, i); 392 cpu_spin_unlock_xrestore(&shm_lock, exceptions); 393 } 394 395 if (is_mobj_ffa_shm(&mf->mobj)) { 396 struct mobj_ffa_shm *m = to_mobj_ffa_shm(&mf->mobj); 397 398 assert(!m->mm); 399 free(m); 400 } else { 401 free(to_mobj_ffa_prm(&mf->mobj)); 402 } 403 } 404 #else /* !defined(CFG_CORE_SEL1_SPMC) */ 405 struct mobj_ffa *mobj_ffa_spmc_new(uint64_t cookie, unsigned int num_pages, 406 enum mobj_use_case use_case) 407 { 408 struct mobj_ffa *mf = NULL; 409 410 assert(cookie != OPTEE_MSG_FMEM_INVALID_GLOBAL_ID); 411 if (use_case == MOBJ_USE_CASE_NS_SHM) 412 mf = ffa_shm_new(num_pages); 413 else 414 mf = ffa_prm_new(num_pages, use_case); 415 if (mf) 416 mf->cookie = cookie; 417 return mf; 418 } 419 420 void mobj_ffa_spmc_delete(struct mobj_ffa *mf) 421 { 422 if (is_mobj_ffa_shm(&mf->mobj)) 423 free(to_mobj_ffa_shm(&mf->mobj)); 424 else 425 free(to_mobj_ffa_prm(&mf->mobj)); 426 } 427 #endif /* !defined(CFG_CORE_SEL1_SPMC) */ 428 429 TEE_Result mobj_ffa_add_pages_at(struct mobj_ffa *mf, unsigned int *idx, 430 paddr_t pa, unsigned int num_pages) 431 { 432 size_t tot_page_count = tot_page_count = get_page_count(mf); 433 unsigned int n = 0; 434 435 if (ADD_OVERFLOW(*idx, num_pages, &n) || n > tot_page_count) 436 return TEE_ERROR_BAD_PARAMETERS; 437 438 if (!IS_ENABLED(CFG_CORE_SEL2_SPMC) && 439 !core_pbuf_is(CORE_MEM_NON_SEC, pa, num_pages * SMALL_PAGE_SIZE)) 440 return TEE_ERROR_BAD_PARAMETERS; 441 442 if (is_mobj_ffa_shm(&mf->mobj)) { 443 struct mobj_ffa_shm *mfs = to_mobj_ffa_shm(&mf->mobj); 444 445 for (n = 0; n < num_pages; n++) 446 mfs->pages[n + *idx] = pa + n * SMALL_PAGE_SIZE; 447 } else { 448 struct mobj_ffa_prm *mfr = to_mobj_ffa_prm(&mf->mobj); 449 450 if (!*idx) 451 mfr->pa = pa; 452 else if (mfr->pa != pa + *idx * SMALL_PAGE_SIZE) 453 return TEE_ERROR_BAD_PARAMETERS; 454 } 455 456 (*idx) += n; 457 458 return TEE_SUCCESS; 459 } 460 461 uint64_t mobj_ffa_get_cookie(struct mobj_ffa *mf) 462 { 463 return mf->cookie; 464 } 465 466 static TEE_Result protect_mem(struct mobj_ffa_prm *m) 467 { 468 DMSG("use_case %d pa %#"PRIxPA", size %#zx cookie %#"PRIx64, 469 m->use_case, m->pa, m->mf.mobj.size, m->mf.cookie); 470 471 return plat_set_protmem_range(m->use_case, m->pa, m->mf.mobj.size); 472 } 473 474 static TEE_Result __maybe_unused restore_mem(struct mobj_ffa_prm *m) 475 { 476 DMSG("use_case %d pa %#" PRIxPA ", size %#zx cookie %#"PRIx64, 477 m->use_case, m->pa, m->mf.mobj.size, m->mf.cookie); 478 479 return plat_set_protmem_range(MOBJ_USE_CASE_NS_SHM, m->pa, 480 m->mf.mobj.size); 481 } 482 483 TEE_Result mobj_ffa_push_to_inactive(struct mobj_ffa *mf) 484 { 485 TEE_Result res = TEE_SUCCESS; 486 uint32_t exceptions = 0; 487 488 exceptions = cpu_spin_lock_xsave(&shm_lock); 489 assert(!find_in_list(&shm_inactive_head, cmp_ptr, (vaddr_t)mf)); 490 assert(!find_in_list(&shm_inactive_head, cmp_cookie, mf->cookie)); 491 assert(!find_in_list(&shm_head, cmp_cookie, mf->cookie)); 492 493 if (find_in_list(&shm_inactive_head, cmp_pa_overlap, (vaddr_t)mf) || 494 find_in_list(&shm_head, cmp_pa_overlap, (vaddr_t)mf)) { 495 res = TEE_ERROR_BAD_PARAMETERS; 496 goto out; 497 } 498 if (is_mobj_ffa_prm(&mf->mobj)) { 499 res = protect_mem(to_mobj_ffa_prm(&mf->mobj)); 500 if (res) 501 goto out; 502 } 503 504 SLIST_INSERT_HEAD(&shm_inactive_head, mf, link); 505 506 out: 507 cpu_spin_unlock_xrestore(&shm_lock, exceptions); 508 509 return res; 510 } 511 512 static void unmap_helper(struct mobj_ffa_shm *m) 513 { 514 if (m->mm) { 515 core_mmu_unmap_pages(tee_mm_get_smem(m->mm), 516 get_page_count(&m->mf)); 517 tee_mm_free(m->mm); 518 m->mm = NULL; 519 } 520 } 521 522 #ifdef CFG_CORE_SEL1_SPMC 523 TEE_Result mobj_ffa_sel1_spmc_reclaim(uint64_t cookie) 524 { 525 TEE_Result res = TEE_SUCCESS; 526 struct mobj_ffa *mf = NULL; 527 uint32_t exceptions = 0; 528 529 exceptions = cpu_spin_lock_xsave(&shm_lock); 530 mf = find_in_list(&shm_head, cmp_cookie, cookie); 531 /* 532 * If the mobj is found here it's still active and cannot be 533 * reclaimed. 534 */ 535 if (mf) { 536 DMSG("cookie %#"PRIx64" busy refc %u", 537 cookie, refcount_val(&mf->mobj.refc)); 538 res = TEE_ERROR_BUSY; 539 goto out; 540 } 541 542 mf = find_in_list(&shm_inactive_head, cmp_cookie, cookie); 543 if (!mf) { 544 res = TEE_ERROR_ITEM_NOT_FOUND; 545 goto out; 546 } 547 /* 548 * If the mobj has been registered via mobj_ffa_get_by_cookie() 549 * but not unregistered yet with mobj_ffa_unregister_by_cookie(). 550 */ 551 if (mf->inactive_refs) { 552 DMSG("cookie %#"PRIx64" busy inactive_refs %u", 553 cookie, mf->inactive_refs); 554 res = TEE_ERROR_BUSY; 555 goto out; 556 } 557 558 if (!pop_from_list(&shm_inactive_head, cmp_ptr, (vaddr_t)mf)) 559 panic(); 560 if (is_mobj_ffa_prm(&mf->mobj)) 561 res = restore_mem(to_mobj_ffa_prm(&mf->mobj)); 562 else 563 res = TEE_SUCCESS; 564 out: 565 cpu_spin_unlock_xrestore(&shm_lock, exceptions); 566 if (!res) { 567 mobj_ffa_sel1_spmc_delete(mf); 568 virt_remove_cookie(cookie); 569 } 570 return res; 571 } 572 #endif /*CFG_CORE_SEL1_SPMC*/ 573 574 TEE_Result mobj_ffa_unregister_by_cookie(uint64_t cookie) 575 { 576 TEE_Result res = TEE_SUCCESS; 577 struct mobj_ffa *mf = NULL; 578 uint32_t exceptions = 0; 579 580 assert(cookie != OPTEE_MSG_FMEM_INVALID_GLOBAL_ID); 581 exceptions = cpu_spin_lock_xsave(&shm_lock); 582 mf = find_in_list(&shm_head, cmp_cookie, cookie); 583 /* 584 * If the mobj is found here it's still active and cannot be 585 * unregistered. 586 */ 587 if (mf) { 588 EMSG("cookie %#"PRIx64" busy refc %u:%u", 589 cookie, refcount_val(&mf->mobj.refc), mf->inactive_refs); 590 res = TEE_ERROR_BUSY; 591 goto out; 592 } 593 mf = find_in_list(&shm_inactive_head, cmp_cookie, cookie); 594 /* 595 * If the mobj isn't found or if it already has been unregistered. 596 */ 597 if (!mf) { 598 EMSG("cookie %#"PRIx64" not found", cookie); 599 res = TEE_ERROR_ITEM_NOT_FOUND; 600 goto out; 601 } 602 #if defined(CFG_CORE_SEL1_SPMC) 603 if (!mf->registered_by_cookie) { 604 /* 605 * This is expected behaviour if the normal world has 606 * registered the memory but OP-TEE has not yet used the 607 * corresponding cookie with mobj_ffa_get_by_cookie(). It 608 * can be non-trivial for the normal world to predict if 609 * the cookie really has been used or not. So even if we 610 * return it as an error it will be ignored by 611 * handle_unregister_shm(). 612 */ 613 EMSG("cookie %#"PRIx64" not registered refs %u:%u", 614 cookie, refcount_val(&mf->mobj.refc), mf->inactive_refs); 615 res = TEE_ERROR_ITEM_NOT_FOUND; 616 goto out; 617 } 618 assert(mf->inactive_refs); 619 mf->inactive_refs--; 620 mf->registered_by_cookie = false; 621 #else 622 if (mf->inactive_refs) { 623 EMSG("cookie %#"PRIx64" busy refc %u:%u", 624 cookie, refcount_val(&mf->mobj.refc), mf->inactive_refs); 625 res = TEE_ERROR_BUSY; 626 goto out; 627 } 628 mf = pop_from_list(&shm_inactive_head, cmp_cookie, cookie); 629 mobj_ffa_spmc_delete(mf); 630 thread_spmc_relinquish(cookie); 631 #endif 632 res = TEE_SUCCESS; 633 634 out: 635 cpu_spin_unlock_xrestore(&shm_lock, exceptions); 636 return res; 637 } 638 639 struct mobj *mobj_ffa_get_by_cookie(uint64_t cookie, 640 unsigned int internal_offs) 641 { 642 struct mobj_ffa_shm *mfs = NULL; 643 struct mobj_ffa *mf = NULL; 644 uint32_t exceptions = 0; 645 uint16_t offs = 0; 646 647 if (internal_offs >= SMALL_PAGE_SIZE) 648 return NULL; 649 exceptions = cpu_spin_lock_xsave(&shm_lock); 650 mf = find_in_list(&shm_head, cmp_cookie, cookie); 651 if (mf) { 652 if (is_mobj_ffa_shm(&mf->mobj)) 653 offs = to_mobj_ffa_shm(&mf->mobj)->page_offset; 654 else 655 offs = 0; 656 if (offs == internal_offs) { 657 if (!refcount_inc(&mf->mobj.refc)) { 658 /* 659 * If refcount is 0 some other thread has 660 * called mobj_put() on this reached 0 and 661 * before ffa_shm_inactivate() got the lock 662 * we found it. Let's reinitialize it. 663 */ 664 refcount_set(&mf->mobj.refc, 1); 665 mf->inactive_refs++; 666 } 667 DMSG("cookie %#"PRIx64" active: refc %u:%u", 668 cookie, refcount_val(&mf->mobj.refc), 669 mf->inactive_refs); 670 } else { 671 EMSG("cookie %#"PRIx64" mismatching internal_offs got %#"PRIx16" expected %#x", 672 cookie, offs, internal_offs); 673 mf = NULL; 674 } 675 } else { 676 mf = pop_from_list(&shm_inactive_head, cmp_cookie, cookie); 677 #if !defined(CFG_CORE_SEL1_SPMC) 678 /* Try to retrieve it from the SPM at S-EL2 */ 679 if (mf) { 680 DMSG("cookie %#"PRIx64" resurrecting", cookie); 681 } else { 682 enum mobj_use_case uc = MOBJ_USE_CASE_NS_SHM; 683 684 DMSG("Populating mobj from rx buffer, cookie %#"PRIx64, 685 cookie); 686 mf = thread_spmc_populate_mobj_from_rx(cookie, uc); 687 } 688 #endif 689 if (mf) { 690 #if defined(CFG_CORE_SEL1_SPMC) 691 if (!mf->registered_by_cookie) { 692 mf->inactive_refs++; 693 mf->registered_by_cookie = true; 694 } 695 #endif 696 assert(refcount_val(&mf->mobj.refc) == 0); 697 refcount_set(&mf->mobj.refc, 1); 698 mf->inactive_refs++; 699 if (is_mobj_ffa_shm(&mf->mobj)) { 700 mfs = to_mobj_ffa_shm(&mf->mobj); 701 refcount_set(&mfs->mapcount, 0); 702 703 /* 704 * mfs->page_offset is offset into the 705 * first page. This offset is assigned 706 * from the internal_offs parameter to this 707 * function. 708 * 709 * While a mobj_ffa is active (ref_count > 710 * 0) this will not change, but when being 711 * pushed to the inactive list it can be 712 * changed again. 713 * 714 * So below we're backing out the old 715 * mfs->page_offset and then assigning a 716 * new from internal_offset. 717 */ 718 mf->mobj.size += mfs->page_offset; 719 assert(!(mf->mobj.size & SMALL_PAGE_MASK)); 720 mf->mobj.size -= internal_offs; 721 mfs->page_offset = internal_offs; 722 } else if (is_mobj_ffa_prm(&mf->mobj) && 723 internal_offs) { 724 mf = NULL; 725 } 726 727 SLIST_INSERT_HEAD(&shm_head, mf, link); 728 } 729 } 730 731 cpu_spin_unlock_xrestore(&shm_lock, exceptions); 732 733 if (!mf) { 734 EMSG("Failed to get cookie %#"PRIx64" internal_offs %#x", 735 cookie, internal_offs); 736 return NULL; 737 } 738 return &mf->mobj; 739 } 740 741 static TEE_Result ffa_shm_get_pa(struct mobj *mobj, size_t offset, 742 size_t granule, paddr_t *pa) 743 { 744 struct mobj_ffa_shm *m = to_mobj_ffa_shm(mobj); 745 size_t full_offset = 0; 746 paddr_t p = 0; 747 748 if (!pa) 749 return TEE_ERROR_GENERIC; 750 751 if (offset >= mobj->size) 752 return TEE_ERROR_GENERIC; 753 754 full_offset = offset + m->page_offset; 755 switch (granule) { 756 case 0: 757 p = m->pages[full_offset / SMALL_PAGE_SIZE] + 758 (full_offset & SMALL_PAGE_MASK); 759 break; 760 case SMALL_PAGE_SIZE: 761 p = m->pages[full_offset / SMALL_PAGE_SIZE]; 762 break; 763 default: 764 return TEE_ERROR_GENERIC; 765 } 766 *pa = p; 767 768 return TEE_SUCCESS; 769 } 770 771 static size_t ffa_shm_get_phys_offs(struct mobj *mobj, 772 size_t granule __maybe_unused) 773 { 774 assert(granule >= mobj->phys_granule); 775 776 return to_mobj_ffa_shm(mobj)->page_offset; 777 } 778 779 static void *ffa_shm_get_va(struct mobj *mobj, size_t offset, size_t len) 780 { 781 struct mobj_ffa_shm *m = to_mobj_ffa_shm(mobj); 782 783 if (!m->mm || !mobj_check_offset_and_len(mobj, offset, len)) 784 return NULL; 785 786 return (void *)(tee_mm_get_smem(m->mm) + offset + m->page_offset); 787 } 788 789 static void ffa_inactivate(struct mobj_ffa *mf) 790 { 791 uint32_t exceptions = 0; 792 793 exceptions = cpu_spin_lock_xsave(&shm_lock); 794 /* 795 * If refcount isn't 0 some other thread has found this mobj in 796 * shm_head after the mobj_put() that put us here and before we got 797 * the lock. 798 */ 799 if (refcount_val(&mf->mobj.refc)) { 800 DMSG("cookie %#"PRIx64" was resurrected", mf->cookie); 801 goto out; 802 } 803 804 /* 805 * pop_from_list() can fail to find the mobj if we had just 806 * decreased the refcount to 0 in mobj_put() and was going to 807 * acquire the shm_lock but another thread found this mobj and 808 * reinitialized the refcount to 1. Then before we got cpu time the 809 * other thread called mobj_put() and deactivated the mobj again. 810 * 811 * However, we still have the inactive count that guarantees 812 * that the mobj can't be freed until it reaches 0. 813 * At this point the mobj is in the inactive list. 814 */ 815 if (pop_from_list(&shm_head, cmp_ptr, (vaddr_t)mf)) { 816 if (is_mobj_ffa_shm(&mf->mobj)) 817 unmap_helper(to_mobj_ffa_shm(&mf->mobj)); 818 SLIST_INSERT_HEAD(&shm_inactive_head, mf, link); 819 } 820 out: 821 if (!mf->inactive_refs) 822 panic(); 823 mf->inactive_refs--; 824 cpu_spin_unlock_xrestore(&shm_lock, exceptions); 825 } 826 827 static void ffa_shm_inactivate(struct mobj *mobj) 828 { 829 ffa_inactivate(&to_mobj_ffa_shm(mobj)->mf); 830 } 831 832 static TEE_Result ffa_shm_get_mem_type(struct mobj *mobj __unused, uint32_t *mt) 833 { 834 if (!mt) 835 return TEE_ERROR_GENERIC; 836 837 *mt = TEE_MATTR_MEM_TYPE_CACHED; 838 839 return TEE_SUCCESS; 840 } 841 842 static bool ffa_shm_matches(struct mobj *mobj __maybe_unused, 843 enum buf_is_attr attr) 844 { 845 assert(is_mobj_ffa_shm(mobj)); 846 847 return attr == CORE_MEM_NON_SEC || attr == CORE_MEM_REG_SHM; 848 } 849 850 static uint64_t ffa_shm_get_cookie(struct mobj *mobj) 851 { 852 return to_mobj_ffa_shm(mobj)->mf.cookie; 853 } 854 855 static TEE_Result ffa_shm_inc_map(struct mobj *mobj) 856 { 857 struct mobj_ffa_shm *m = to_mobj_ffa_shm(mobj); 858 TEE_Result res = TEE_SUCCESS; 859 uint32_t exceptions = 0; 860 size_t sz = 0; 861 862 while (true) { 863 if (refcount_inc(&m->mapcount)) 864 return TEE_SUCCESS; 865 866 exceptions = cpu_spin_lock_xsave(&shm_lock); 867 868 if (!refcount_val(&m->mapcount)) 869 break; /* continue to reinitialize */ 870 /* 871 * If another thread beat us to initialize mapcount, 872 * restart to make sure we still increase it. 873 */ 874 cpu_spin_unlock_xrestore(&shm_lock, exceptions); 875 } 876 877 /* 878 * If we have beated another thread calling ffa_dec_map() 879 * to get the lock we need only to reinitialize mapcount to 1. 880 */ 881 if (!m->mm) { 882 sz = ROUNDUP(mobj->size + m->page_offset, SMALL_PAGE_SIZE); 883 m->mm = tee_mm_alloc(&core_virt_shm_pool, sz); 884 if (!m->mm) { 885 res = TEE_ERROR_OUT_OF_MEMORY; 886 goto out; 887 } 888 889 res = core_mmu_map_pages(tee_mm_get_smem(m->mm), m->pages, 890 sz / SMALL_PAGE_SIZE, 891 MEM_AREA_NSEC_SHM); 892 if (res) { 893 tee_mm_free(m->mm); 894 m->mm = NULL; 895 goto out; 896 } 897 } 898 899 refcount_set(&m->mapcount, 1); 900 out: 901 cpu_spin_unlock_xrestore(&shm_lock, exceptions); 902 903 return res; 904 } 905 906 static TEE_Result ffa_shm_dec_map(struct mobj *mobj) 907 { 908 struct mobj_ffa_shm *m = to_mobj_ffa_shm(mobj); 909 uint32_t exceptions = 0; 910 911 if (!refcount_dec(&m->mapcount)) 912 return TEE_SUCCESS; 913 914 exceptions = cpu_spin_lock_xsave(&shm_lock); 915 if (!refcount_val(&m->mapcount)) 916 unmap_helper(m); 917 cpu_spin_unlock_xrestore(&shm_lock, exceptions); 918 919 return TEE_SUCCESS; 920 } 921 922 static TEE_Result mapped_shm_init(void) 923 { 924 vaddr_t pool_start = 0; 925 vaddr_t pool_end = 0; 926 927 core_mmu_get_mem_by_type(MEM_AREA_SHM_VASPACE, &pool_start, &pool_end); 928 if (!pool_start || !pool_end) 929 panic("Can't find region for shmem pool"); 930 931 if (!tee_mm_init(&core_virt_shm_pool, pool_start, pool_end - pool_start, 932 SMALL_PAGE_SHIFT, 933 TEE_MM_POOL_NO_FLAGS)) 934 panic("Could not create shmem pool"); 935 936 DMSG("Shared memory address range: %#"PRIxVA", %#"PRIxVA, 937 pool_start, pool_end); 938 return TEE_SUCCESS; 939 } 940 941 static const struct mobj_ops mobj_ffa_shm_ops = { 942 .get_pa = ffa_shm_get_pa, 943 .get_phys_offs = ffa_shm_get_phys_offs, 944 .get_va = ffa_shm_get_va, 945 .get_mem_type = ffa_shm_get_mem_type, 946 .matches = ffa_shm_matches, 947 .free = ffa_shm_inactivate, 948 .get_cookie = ffa_shm_get_cookie, 949 .inc_map = ffa_shm_inc_map, 950 .dec_map = ffa_shm_dec_map, 951 }; 952 953 preinit(mapped_shm_init); 954 955 #ifdef CFG_CORE_DYN_PROTMEM 956 static TEE_Result ffa_prm_get_pa(struct mobj *mobj, size_t offset, 957 size_t granule, paddr_t *pa) 958 { 959 struct mobj_ffa_prm *m = to_mobj_ffa_prm(mobj); 960 paddr_t p; 961 962 if (!pa || offset >= mobj->size) 963 return TEE_ERROR_GENERIC; 964 965 p = m->pa + offset; 966 967 if (granule) { 968 if (granule != SMALL_PAGE_SIZE && 969 granule != CORE_MMU_PGDIR_SIZE) 970 return TEE_ERROR_GENERIC; 971 p &= ~(granule - 1); 972 } 973 974 *pa = p; 975 return TEE_SUCCESS; 976 } 977 978 static TEE_Result ffa_prm_get_mem_type(struct mobj *mobj __maybe_unused, 979 uint32_t *mt) 980 { 981 assert(is_mobj_ffa_prm(mobj)); 982 983 if (!mt) 984 return TEE_ERROR_GENERIC; 985 986 *mt = TEE_MATTR_MEM_TYPE_CACHED; 987 988 return TEE_SUCCESS; 989 } 990 991 static bool ffa_prm_matches(struct mobj *mobj __maybe_unused, 992 enum buf_is_attr attr) 993 { 994 assert(is_mobj_ffa_prm(mobj)); 995 996 return attr == CORE_MEM_SEC || attr == CORE_MEM_SDP_MEM; 997 } 998 999 static void ffa_prm_inactivate(struct mobj *mobj) 1000 { 1001 ffa_inactivate(&to_mobj_ffa_prm(mobj)->mf); 1002 } 1003 1004 static uint64_t ffa_prm_get_cookie(struct mobj *mobj) 1005 { 1006 return to_mobj_ffa_prm(mobj)->mf.cookie; 1007 } 1008 1009 static TEE_Result ffa_prm_no_map(struct mobj *mobj __maybe_unused) 1010 { 1011 assert(is_mobj_ffa_prm(mobj)); 1012 1013 return TEE_ERROR_GENERIC; 1014 } 1015 1016 static const struct mobj_ops mobj_ffa_prm_ops = { 1017 .get_pa = ffa_prm_get_pa, 1018 .get_mem_type = ffa_prm_get_mem_type, 1019 .matches = ffa_prm_matches, 1020 .free = ffa_prm_inactivate, 1021 .get_cookie = ffa_prm_get_cookie, 1022 .inc_map = ffa_prm_no_map, 1023 .dec_map = ffa_prm_no_map, 1024 }; 1025 1026 static bool cmp_protmem_pa(struct mobj_ffa *mf, uint64_t pa) 1027 { 1028 struct mobj_ffa_prm *m = NULL; 1029 1030 if (!is_mobj_ffa_prm(&mf->mobj)) 1031 return false; 1032 1033 m = to_mobj_ffa_prm(&mf->mobj); 1034 return pa >= m->pa && pa < m->pa + m->mf.mobj.size; 1035 } 1036 1037 struct mobj *mobj_ffa_protmem_get_by_pa(paddr_t pa, paddr_size_t size) 1038 { 1039 struct mobj_ffa_prm *m = NULL; 1040 struct mobj_ffa *mf = NULL; 1041 struct mobj *mobj = NULL; 1042 uint32_t exceptions = 0; 1043 1044 if (!size) 1045 size = 1; 1046 1047 exceptions = cpu_spin_lock_xsave(&shm_lock); 1048 1049 mf = find_in_list(&shm_head, cmp_protmem_pa, pa); 1050 if (mf) { 1051 m = to_mobj_ffa_prm(&mf->mobj); 1052 if (core_is_buffer_inside(pa, size, m->pa, m->mf.mobj.size)) 1053 mobj = mobj_get(&mf->mobj); 1054 } 1055 1056 cpu_spin_unlock_xrestore(&shm_lock, exceptions); 1057 return mobj; 1058 } 1059 1060 TEE_Result mobj_ffa_assign_protmem(uint64_t cookie, enum mobj_use_case use_case) 1061 { 1062 TEE_Result res = TEE_SUCCESS; 1063 struct mobj_ffa_prm *m = NULL; 1064 struct mobj_ffa *mf = NULL; 1065 uint32_t exceptions = 0; 1066 1067 exceptions = cpu_spin_lock_xsave(&shm_lock); 1068 mf = find_in_list(&shm_inactive_head, cmp_cookie, cookie); 1069 if (mf) { 1070 if (!is_mobj_ffa_prm(&mf->mobj)) { 1071 res = TEE_ERROR_ITEM_NOT_FOUND; 1072 goto out; 1073 } 1074 m = to_mobj_ffa_prm(&mf->mobj); 1075 if (m->assigned_use_case) { 1076 res = TEE_ERROR_BUSY; 1077 goto out; 1078 } 1079 if (m->use_case != use_case) { 1080 res = TEE_ERROR_BAD_PARAMETERS; 1081 goto out; 1082 } 1083 m->assigned_use_case = true; 1084 goto out; 1085 } 1086 mf = find_in_list(&shm_head, cmp_cookie, cookie); 1087 if (mf) { 1088 if (!is_mobj_ffa_prm(&mf->mobj)) 1089 res = TEE_ERROR_BUSY; 1090 else 1091 res = TEE_ERROR_ITEM_NOT_FOUND; 1092 goto out; 1093 } 1094 #if !defined(CFG_CORE_SEL1_SPMC) 1095 /* Try to retrieve it from the SPM at S-EL2 */ 1096 DMSG("Populating mobj from rx buffer, cookie %#"PRIx64" use-case %d", 1097 cookie, use_case); 1098 mf = thread_spmc_populate_mobj_from_rx(cookie, use_case); 1099 if (mf) { 1100 SLIST_INSERT_HEAD(&shm_inactive_head, mf, link); 1101 } else { 1102 EMSG("Failed to assign use-case %d to cookie %#"PRIx64"", 1103 use_case, cookie); 1104 res = TEE_ERROR_ITEM_NOT_FOUND; 1105 goto out; 1106 } 1107 #endif 1108 out: 1109 cpu_spin_unlock_xrestore(&shm_lock, exceptions); 1110 return res; 1111 } 1112 #endif /*CFG_CORE_DYN_PROTMEM*/ 1113