forked from mirrors/linux
		
	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>
This commit is contained in:
		
							parent
							
								
									de5c6dfc43
								
							
						
					
					
						commit
						3bb48ba5cd
					
				
					 2 changed files with 96 additions and 0 deletions
				
			
		| 
						 | 
					@ -11,6 +11,7 @@
 | 
				
			||||||
 * GNU General Public License for more details.
 | 
					 * GNU General Public License for more details.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					#include <asm/pgtable.h>
 | 
				
			||||||
#include <linux/arm-smccc.h>
 | 
					#include <linux/arm-smccc.h>
 | 
				
			||||||
#include <linux/device.h>
 | 
					#include <linux/device.h>
 | 
				
			||||||
#include <linux/err.h>
 | 
					#include <linux/err.h>
 | 
				
			||||||
| 
						 | 
					@ -442,3 +443,93 @@ void optee_disable_shm_cache(struct optee *optee)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	optee_cq_wait_final(&optee->call_queue, &w);
 | 
						optee_cq_wait_final(&optee->call_queue, &w);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define PAGELIST_ENTRIES_PER_PAGE				\
 | 
				
			||||||
 | 
						((OPTEE_MSG_NONCONTIG_PAGE_SIZE / sizeof(u64)) - 1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * optee_fill_pages_list() - write list of user pages to given shared
 | 
				
			||||||
 | 
					 * buffer.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @dst: page-aligned buffer where list of pages will be stored
 | 
				
			||||||
 | 
					 * @pages: array of pages that represents shared buffer
 | 
				
			||||||
 | 
					 * @num_pages: number of entries in @pages
 | 
				
			||||||
 | 
					 * @page_offset: offset of user buffer from page start
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @dst should be big enough to hold list of user page addresses and
 | 
				
			||||||
 | 
					 *	links to the next pages of buffer
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					void optee_fill_pages_list(u64 *dst, struct page **pages, int num_pages,
 | 
				
			||||||
 | 
								   size_t page_offset)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int n = 0;
 | 
				
			||||||
 | 
						phys_addr_t optee_page;
 | 
				
			||||||
 | 
						/*
 | 
				
			||||||
 | 
						 * Refer to OPTEE_MSG_ATTR_NONCONTIG description in optee_msg.h
 | 
				
			||||||
 | 
						 * for details.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						struct {
 | 
				
			||||||
 | 
							u64 pages_list[PAGELIST_ENTRIES_PER_PAGE];
 | 
				
			||||||
 | 
							u64 next_page_data;
 | 
				
			||||||
 | 
						} *pages_data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/*
 | 
				
			||||||
 | 
						 * Currently OP-TEE uses 4k page size and it does not looks
 | 
				
			||||||
 | 
						 * like this will change in the future.  On other hand, there are
 | 
				
			||||||
 | 
						 * no know ARM architectures with page size < 4k.
 | 
				
			||||||
 | 
						 * Thus the next built assert looks redundant. But the following
 | 
				
			||||||
 | 
						 * code heavily relies on this assumption, so it is better be
 | 
				
			||||||
 | 
						 * safe than sorry.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						BUILD_BUG_ON(PAGE_SIZE < OPTEE_MSG_NONCONTIG_PAGE_SIZE);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						pages_data = (void *)dst;
 | 
				
			||||||
 | 
						/*
 | 
				
			||||||
 | 
						 * If linux page is bigger than 4k, and user buffer offset is
 | 
				
			||||||
 | 
						 * larger than 4k/8k/12k/etc this will skip first 4k pages,
 | 
				
			||||||
 | 
						 * because they bear no value data for OP-TEE.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						optee_page = page_to_phys(*pages) +
 | 
				
			||||||
 | 
							round_down(page_offset, OPTEE_MSG_NONCONTIG_PAGE_SIZE);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						while (true) {
 | 
				
			||||||
 | 
							pages_data->pages_list[n++] = optee_page;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (n == PAGELIST_ENTRIES_PER_PAGE) {
 | 
				
			||||||
 | 
								pages_data->next_page_data =
 | 
				
			||||||
 | 
									virt_to_phys(pages_data + 1);
 | 
				
			||||||
 | 
								pages_data++;
 | 
				
			||||||
 | 
								n = 0;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							optee_page += OPTEE_MSG_NONCONTIG_PAGE_SIZE;
 | 
				
			||||||
 | 
							if (!(optee_page & ~PAGE_MASK)) {
 | 
				
			||||||
 | 
								if (!--num_pages)
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
								pages++;
 | 
				
			||||||
 | 
								optee_page = page_to_phys(*pages);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * The final entry in each pagelist page is a pointer to the next
 | 
				
			||||||
 | 
					 * pagelist page.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static size_t get_pages_list_size(size_t num_entries)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int pages = DIV_ROUND_UP(num_entries, PAGELIST_ENTRIES_PER_PAGE);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return pages * OPTEE_MSG_NONCONTIG_PAGE_SIZE;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					u64 *optee_allocate_pages_list(size_t num_entries)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return alloc_pages_exact(get_pages_list_size(num_entries), GFP_KERNEL);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void optee_free_pages_list(void *list, size_t num_entries)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						free_pages_exact(list, get_pages_list_size(num_entries));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -165,6 +165,11 @@ int optee_from_msg_param(struct tee_param *params, size_t num_params,
 | 
				
			||||||
int optee_to_msg_param(struct optee_msg_param *msg_params, size_t num_params,
 | 
					int optee_to_msg_param(struct optee_msg_param *msg_params, size_t num_params,
 | 
				
			||||||
		       const struct tee_param *params);
 | 
							       const struct tee_param *params);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					u64 *optee_allocate_pages_list(size_t num_entries);
 | 
				
			||||||
 | 
					void optee_free_pages_list(void *array, size_t num_entries);
 | 
				
			||||||
 | 
					void optee_fill_pages_list(u64 *dst, struct page **pages, int num_pages,
 | 
				
			||||||
 | 
								   size_t page_offset);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * Small helpers
 | 
					 * Small helpers
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue