summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVolodymyr Babchuk <vlad.babchuk@gmail.com>2017-11-29 07:48:30 -0500
committerJens Wiklander <jens.wiklander@linaro.org>2017-12-15 07:32:31 -0500
commit3bb48ba5cd60f9685aa8f1ccd9b14a72e237c13f (patch)
tree21b037f0e3191eab3cdd441b6adb24d862be5c21
parentde5c6dfc43daa59feb824505f80fe4591f8f8f85 (diff)
tee: optee: add page list manipulation functions
These functions will be used to pass information about shared buffers to OP-TEE. ABI between Linux and OP-TEE is defined in optee_msg.h and optee_smc.h. optee_msg.h defines OPTEE_MSG_ATTR_NONCONTIG attribute for shared memory references and describes how such references should be passed. Note that it uses 64-bit page addresses even on 32 bit systems. This is done to support LPAE and to unify interface. Signed-off-by: Volodymyr Babchuk <vlad.babchuk@gmail.com> [jw: replacing uint64_t with u64 in optee_fill_pages_list()] Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
-rw-r--r--drivers/tee/optee/call.c91
-rw-r--r--drivers/tee/optee/optee_private.h5
2 files changed, 96 insertions, 0 deletions
diff --git a/drivers/tee/optee/call.c b/drivers/tee/optee/call.c
index f7b7b404c990..e85860f6e057 100644
--- a/drivers/tee/optee/call.c
+++ b/drivers/tee/optee/call.c
@@ -11,6 +11,7 @@
11 * GNU General Public License for more details. 11 * GNU General Public License for more details.
12 * 12 *
13 */ 13 */
14#include <asm/pgtable.h>
14#include <linux/arm-smccc.h> 15#include <linux/arm-smccc.h>
15#include <linux/device.h> 16#include <linux/device.h>
16#include <linux/err.h> 17#include <linux/err.h>
@@ -442,3 +443,93 @@ void optee_disable_shm_cache(struct optee *optee)
442 } 443 }
443 optee_cq_wait_final(&optee->call_queue, &w); 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 */
462void 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 */
519static 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
526u64 *optee_allocate_pages_list(size_t num_entries)
527{
528 return alloc_pages_exact(get_pages_list_size(num_entries), GFP_KERNEL);
529}
530
531void optee_free_pages_list(void *list, size_t num_entries)
532{
533 free_pages_exact(list, get_pages_list_size(num_entries));
534}
535
diff --git a/drivers/tee/optee/optee_private.h b/drivers/tee/optee/optee_private.h
index c374cd594314..b63213d09d68 100644
--- a/drivers/tee/optee/optee_private.h
+++ b/drivers/tee/optee/optee_private.h
@@ -165,6 +165,11 @@ int optee_from_msg_param(struct tee_param *params, size_t num_params,
165int optee_to_msg_param(struct optee_msg_param *msg_params, size_t num_params, 165int optee_to_msg_param(struct optee_msg_param *msg_params, size_t num_params,
166 const struct tee_param *params); 166 const struct tee_param *params);
167 167
168u64 *optee_allocate_pages_list(size_t num_entries);
169void optee_free_pages_list(void *array, size_t num_entries);
170void optee_fill_pages_list(u64 *dst, struct page **pages, int num_pages,
171 size_t page_offset);
172
168/* 173/*
169 * Small helpers 174 * Small helpers
170 */ 175 */