|
11 | 11 | * GNU General Public License for more details.
|
12 | 12 | *
|
13 | 13 | */
|
| 14 | +#include <asm/pgtable.h> |
14 | 15 | #include <linux/arm-smccc.h>
|
15 | 16 | #include <linux/device.h>
|
16 | 17 | #include <linux/err.h>
|
@@ -442,3 +443,93 @@ void optee_disable_shm_cache(struct optee *optee)
|
442 | 443 | }
|
443 | 444 | optee_cq_wait_final(&optee->call_queue, &w);
|
444 | 445 | }
|
| 446 | + |
| 447 | +#define PAGELIST_ENTRIES_PER_PAGE \ |
| 448 | + ((OPTEE_MSG_NONCONTIG_PAGE_SIZE / sizeof(u64)) - 1) |
| 449 | + |
| 450 | +/** |
| 451 | + * optee_fill_pages_list() - write list of user pages to given shared |
| 452 | + * buffer. |
| 453 | + * |
| 454 | + * @dst: page-aligned buffer where list of pages will be stored |
| 455 | + * @pages: array of pages that represents shared buffer |
| 456 | + * @num_pages: number of entries in @pages |
| 457 | + * @page_offset: offset of user buffer from page start |
| 458 | + * |
| 459 | + * @dst should be big enough to hold list of user page addresses and |
| 460 | + * links to the next pages of buffer |
| 461 | + */ |
| 462 | +void optee_fill_pages_list(u64 *dst, struct page **pages, int num_pages, |
| 463 | + size_t page_offset) |
| 464 | +{ |
| 465 | + int n = 0; |
| 466 | + phys_addr_t optee_page; |
| 467 | + /* |
| 468 | + * Refer to OPTEE_MSG_ATTR_NONCONTIG description in optee_msg.h |
| 469 | + * for details. |
| 470 | + */ |
| 471 | + struct { |
| 472 | + u64 pages_list[PAGELIST_ENTRIES_PER_PAGE]; |
| 473 | + u64 next_page_data; |
| 474 | + } *pages_data; |
| 475 | + |
| 476 | + /* |
| 477 | + * Currently OP-TEE uses 4k page size and it does not looks |
| 478 | + * like this will change in the future. On other hand, there are |
| 479 | + * no know ARM architectures with page size < 4k. |
| 480 | + * Thus the next built assert looks redundant. But the following |
| 481 | + * code heavily relies on this assumption, so it is better be |
| 482 | + * safe than sorry. |
| 483 | + */ |
| 484 | + BUILD_BUG_ON(PAGE_SIZE < OPTEE_MSG_NONCONTIG_PAGE_SIZE); |
| 485 | + |
| 486 | + pages_data = (void *)dst; |
| 487 | + /* |
| 488 | + * If linux page is bigger than 4k, and user buffer offset is |
| 489 | + * larger than 4k/8k/12k/etc this will skip first 4k pages, |
| 490 | + * because they bear no value data for OP-TEE. |
| 491 | + */ |
| 492 | + optee_page = page_to_phys(*pages) + |
| 493 | + round_down(page_offset, OPTEE_MSG_NONCONTIG_PAGE_SIZE); |
| 494 | + |
| 495 | + while (true) { |
| 496 | + pages_data->pages_list[n++] = optee_page; |
| 497 | + |
| 498 | + if (n == PAGELIST_ENTRIES_PER_PAGE) { |
| 499 | + pages_data->next_page_data = |
| 500 | + virt_to_phys(pages_data + 1); |
| 501 | + pages_data++; |
| 502 | + n = 0; |
| 503 | + } |
| 504 | + |
| 505 | + optee_page += OPTEE_MSG_NONCONTIG_PAGE_SIZE; |
| 506 | + if (!(optee_page & ~PAGE_MASK)) { |
| 507 | + if (!--num_pages) |
| 508 | + break; |
| 509 | + pages++; |
| 510 | + optee_page = page_to_phys(*pages); |
| 511 | + } |
| 512 | + } |
| 513 | +} |
| 514 | + |
| 515 | +/* |
| 516 | + * The final entry in each pagelist page is a pointer to the next |
| 517 | + * pagelist page. |
| 518 | + */ |
| 519 | +static size_t get_pages_list_size(size_t num_entries) |
| 520 | +{ |
| 521 | + int pages = DIV_ROUND_UP(num_entries, PAGELIST_ENTRIES_PER_PAGE); |
| 522 | + |
| 523 | + return pages * OPTEE_MSG_NONCONTIG_PAGE_SIZE; |
| 524 | +} |
| 525 | + |
| 526 | +u64 *optee_allocate_pages_list(size_t num_entries) |
| 527 | +{ |
| 528 | + return alloc_pages_exact(get_pages_list_size(num_entries), GFP_KERNEL); |
| 529 | +} |
| 530 | + |
| 531 | +void optee_free_pages_list(void *list, size_t num_entries) |
| 532 | +{ |
| 533 | + free_pages_exact(list, get_pages_list_size(num_entries)); |
| 534 | +} |
| 535 | + |
0 commit comments