diff options
author | Volodymyr Babchuk <vlad.babchuk@gmail.com> | 2017-06-15 23:28:44 +0300 |
---|---|---|
committer | Jérôme Forissier <jerome.forissier@linaro.org> | 2017-06-22 13:51:38 +0200 |
commit | e7a8839b3e0658a7deb6f980c8d73f3355533054 (patch) | |
tree | f1884202c791468c75fd0b036c808b24ec48deb6 | |
parent | 13f187f4c19a1b4c1c8f2028c8aed4bb11bb5e69 (diff) |
ABI change: add OPTEE_MSG_ATTR_NONCONTIG handling
This patch introduces new attribute OPTEE_MSG_ATTR_NONCONTIG to allow
Normal World pass arbitrary list of physical pages as a shared buffer.
To read this list of page address two new functions are added:
- msg_param_extract_pages() is a helper function that read pages list
into provided array
- msg_param_mobj_from_noncontig_param() constructs mobj from provided
struct optee_msg_param parameter. This mobj then can be used in
various parts of OP-TEE
Signed-off-by: Volodymyr Babchuk <vlad.babchuk@gmail.com>
Reviewed-by: Jens Wiklander <jens.wiklander@linaro.org>
Reviewed-by: Etienne Carriere <etienne.carriere@linaro.org>
-rw-r--r-- | core/arch/arm/tee/entry_std.c | 2 | ||||
-rw-r--r-- | core/include/kernel/msg_param.h | 13 | ||||
-rw-r--r-- | core/include/optee_msg.h | 34 | ||||
-rw-r--r-- | core/kernel/msg_param.c | 116 |
4 files changed, 160 insertions, 5 deletions
diff --git a/core/arch/arm/tee/entry_std.c b/core/arch/arm/tee/entry_std.c index 615cf58d..08d11677 100644 --- a/core/arch/arm/tee/entry_std.c +++ b/core/arch/arm/tee/entry_std.c @@ -111,7 +111,7 @@ static TEE_Result copy_in_params(const struct optee_msg_param *params, if (params[n].attr & OPTEE_MSG_ATTR_META) return TEE_ERROR_BAD_PARAMETERS; - if (params[n].attr & OPTEE_MSG_ATTR_FRAGMENT) + if (params[n].attr & OPTEE_MSG_ATTR_NONCONTIG) return TEE_ERROR_BAD_PARAMETERS; attr = params[n].attr & OPTEE_MSG_ATTR_TYPE_MASK; diff --git a/core/include/kernel/msg_param.h b/core/include/kernel/msg_param.h index feef4ae2..99051676 100644 --- a/core/include/kernel/msg_param.h +++ b/core/include/kernel/msg_param.h @@ -45,6 +45,19 @@ enum msg_param_mem_dir { }; /** + * msg_param_mobj_from_noncontig() - construct mobj from non-contiguous + * list of pages. + * + * @param - pointer to msg_param with OPTEE_MSG_ATTR_NONCONTIG flag set + * @map_buffer - true if buffer needs to be mapped into OP-TEE address space + * + * return: + * mobj or NULL on error + */ +struct mobj *msg_param_mobj_from_noncontig(const struct optee_msg_param *param, + bool map_buffer); + +/** * msg_param_init_memparam() - fill memory reference parameter for RPC call * @param - parameter to fill * @mobj - mobj describing the shared memory buffer diff --git a/core/include/optee_msg.h b/core/include/optee_msg.h index ddc3110c..30ec0bbf 100644 --- a/core/include/optee_msg.h +++ b/core/include/optee_msg.h @@ -68,11 +68,32 @@ #define OPTEE_MSG_ATTR_META BIT(8) /* - * The temporary shared memory object is not physically contiguous and this - * temp memref is followed by another fragment until the last temp memref - * that doesn't have this bit set. + * Pointer to a list of pages used to register user-defined SHM buffer. + * Used with OPTEE_MSG_ATTR_TYPE_TMEM_*. + * buf_ptr should point to the beginning of the buffer. Buffer will contain + * list of page addresses. OP-TEE core can reconstruct contiguous buffer from + * that page addresses list. Page addresses are stored as 64 bit values. + * Last entry on a page should point to the next page of buffer. + * Every entry in buffer should point to a 4k page beginning (12 least + * significant bits must be equal to zero). + * + * 12 least significant of optee_msg_param.u.tmem.buf_ptr should hold page + * offset of user buffer. + * + * So, entries should be placed like members of this structure: + * + * struct page_data { + * uint64_t pages_array[OPTEE_MSG_NONCONTIG_PAGE_SIZE/sizeof(uint64_t) - 1]; + * uint64_t next_page_data; + * }; + * + * Structure is designed to exactly fit into the page size + * OPTEE_MSG_NONCONTIG_PAGE_SIZE which is a standard 4KB page. + * + * The size of 4KB is chosen because this is the smallest page size for ARM + * architectures. If REE uses larger pages, it should divide them to 4KB ones. */ -#define OPTEE_MSG_ATTR_FRAGMENT BIT(9) +#define OPTEE_MSG_ATTR_NONCONTIG BIT(9) /* * Memory attributes for caching passed with temp memrefs. The actual value @@ -95,6 +116,11 @@ #define OPTEE_MSG_LOGIN_APPLICATION_USER 0x00000005 #define OPTEE_MSG_LOGIN_APPLICATION_GROUP 0x00000006 +/* + * Page size used in non-contiguous buffer entries + */ +#define OPTEE_MSG_NONCONTIG_PAGE_SIZE 4096 + #ifndef ASM /** * struct optee_msg_param_tmem - temporary memory reference parameter diff --git a/core/kernel/msg_param.c b/core/kernel/msg_param.c index 71b9f22a..bc4683f7 100644 --- a/core/kernel/msg_param.c +++ b/core/kernel/msg_param.c @@ -31,6 +31,122 @@ #include <kernel/msg_param.h> #include <mm/mobj.h> +/** + * msg_param_extract_pages() - extract list of pages from + * OPTEE_MSG_ATTR_NONCONTIG buffer. + * + * @buffer: pointer to parameters array + * @pages: output array of page addresses + * @num_pages: number of pages in array + * + * return: + * true on success, false otherwise + * + * @buffer points to the physical address of a structure that can be viewed as + * + * struct page_data { + * uint64_t pages_array[OPTEE_MSG_NONCONTIG_PAGE_SIZE/sizeof(uint64_t) - 1]; + * uint64_t next_page_data; + * }; + * + * So, it is a linked list of arrays, where each element of linked list fits + * exactly into one 4K page. + * + * This function extracts data from arrays into one array pointed by @pages + * + * @buffer points to data shared with normal world, so some precautions + * should be taken. + */ +static bool msg_param_extract_pages(paddr_t buffer, paddr_t *pages, + size_t num_pages) +{ + size_t cnt; + struct mobj *mobj; + paddr_t page; + uint64_t *va; + bool ret = false; + + if (buffer & SMALL_PAGE_MASK) + return false; + + /* + * There we map first page of array. + * mobj_mapped_shm_alloc() will check if page resides in nonsec ddr + */ + mobj = mobj_mapped_shm_alloc(&buffer, 1, 0, 0); + if (!mobj) + return false; + + va = mobj_get_va(mobj, 0); + assert(va); + + for (cnt = 0; cnt < num_pages; cnt++, va++) { + /* + * If we about to roll over page boundary, then last entry holds + * address of next page of array. Unmap current page and map + * next one + */ + if (!((vaddr_t)(va + 1) & SMALL_PAGE_MASK)) { + page = *va; + if (page & SMALL_PAGE_MASK) + goto out; + + mobj_free(mobj); + mobj = mobj_mapped_shm_alloc(&page, 1, 0, 0); + if (!mobj) + goto out; + + va = mobj_get_va(mobj, 0); + assert(va); + } + pages[cnt] = *va; + if (pages[cnt] & SMALL_PAGE_MASK) + goto out; + } + + ret = true; +out: + mobj_free(mobj); + return ret; +} + +struct mobj *msg_param_mobj_from_noncontig(const struct optee_msg_param *param, + bool map_buffer) +{ + struct mobj *mobj = NULL; + paddr_t *pages; + paddr_t page_offset; + size_t num_pages; + uint64_t buf_ptr; + + assert(param->attr & OPTEE_MSG_ATTR_NONCONTIG); + + /* Make sure that NW will not change value in SHM */ + buf_ptr = param->u.tmem.buf_ptr; + + page_offset = buf_ptr & SMALL_PAGE_MASK; + num_pages = (param->u.tmem.size + page_offset - 1) / + SMALL_PAGE_SIZE + 1; + + pages = malloc(num_pages * sizeof(paddr_t)); + if (!pages) + return NULL; + + if (!msg_param_extract_pages(buf_ptr & ~SMALL_PAGE_MASK, + pages, num_pages)) + goto out; + + if (map_buffer) + mobj = mobj_mapped_shm_alloc(pages, num_pages, page_offset, + param->u.tmem.shm_ref); + else + mobj = mobj_reg_shm_alloc(pages, num_pages, page_offset, + param->u.tmem.shm_ref); +out: + free(pages); + return mobj; +} + bool msg_param_init_memparam(struct optee_msg_param *param, struct mobj *mobj, size_t offset, size_t size, uint64_t cookie, enum msg_param_mem_dir dir) |