1 /* 2 * Copyright (c) 2017, EPAM Systems 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright notice, 9 * this list of conditions and the following disclaimer. 10 * 11 * 2. Redistributions in binary form must reproduce the above copyright notice, 12 * this list of conditions and the following disclaimer in the documentation 13 * and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 19 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 20 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 21 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 22 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 23 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 24 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 25 * POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28 #include <optee_msg.h> 29 #include <stdio.h> 30 #include <types_ext.h> 31 #include <kernel/msg_param.h> 32 #include <mm/mobj.h> 33 34 /** 35 * msg_param_extract_pages() - extract list of pages from 36 * OPTEE_MSG_ATTR_NONCONTIG buffer. 37 * 38 * @buffer: pointer to parameters array 39 * @pages: output array of page addresses 40 * @num_pages: number of pages in array 41 * 42 * return: 43 * true on success, false otherwise 44 * 45 * @buffer points to the physical address of a structure that can be viewed as 46 * 47 * struct page_data { 48 * uint64_t pages_array[OPTEE_MSG_NONCONTIG_PAGE_SIZE/sizeof(uint64_t) - 1]; 49 * uint64_t next_page_data; 50 * }; 51 * 52 * So, it is a linked list of arrays, where each element of linked list fits 53 * exactly into one 4K page. 54 * 55 * This function extracts data from arrays into one array pointed by @pages 56 * 57 * @buffer points to data shared with normal world, so some precautions 58 * should be taken. 59 */ 60 static bool msg_param_extract_pages(paddr_t buffer, paddr_t *pages, 61 size_t num_pages) 62 { 63 size_t cnt; 64 struct mobj *mobj; 65 paddr_t page; 66 uint64_t *va; 67 bool ret = false; 68 69 if (buffer & SMALL_PAGE_MASK) 70 return false; 71 72 /* 73 * There we map first page of array. 74 * mobj_mapped_shm_alloc() will check if page resides in nonsec ddr 75 */ 76 mobj = mobj_mapped_shm_alloc(&buffer, 1, 0, 0); 77 if (!mobj) 78 return false; 79 80 va = mobj_get_va(mobj, 0); 81 assert(va); 82 83 for (cnt = 0; cnt < num_pages; cnt++, va++) { 84 /* 85 * If we about to roll over page boundary, then last entry holds 86 * address of next page of array. Unmap current page and map 87 * next one 88 */ 89 if (!((vaddr_t)(va + 1) & SMALL_PAGE_MASK)) { 90 page = *va; 91 if (page & SMALL_PAGE_MASK) 92 goto out; 93 94 mobj_free(mobj); 95 mobj = mobj_mapped_shm_alloc(&page, 1, 0, 0); 96 if (!mobj) 97 goto out; 98 99 va = mobj_get_va(mobj, 0); 100 assert(va); 101 } 102 pages[cnt] = *va; 103 if (pages[cnt] & SMALL_PAGE_MASK) 104 goto out; 105 } 106 107 ret = true; 108 out: 109 mobj_free(mobj); 110 return ret; 111 } 112 113 struct mobj *msg_param_mobj_from_noncontig(paddr_t buf_ptr, size_t size, 114 uint64_t shm_ref, bool map_buffer) 115 { 116 struct mobj *mobj = NULL; 117 paddr_t *pages; 118 paddr_t page_offset; 119 size_t num_pages; 120 121 page_offset = buf_ptr & SMALL_PAGE_MASK; 122 num_pages = (size + page_offset - 1) / SMALL_PAGE_SIZE + 1; 123 124 pages = malloc(num_pages * sizeof(paddr_t)); 125 if (!pages) 126 return NULL; 127 128 if (!msg_param_extract_pages(buf_ptr & ~SMALL_PAGE_MASK, 129 pages, num_pages)) 130 goto out; 131 132 if (map_buffer) 133 mobj = mobj_mapped_shm_alloc(pages, num_pages, page_offset, 134 shm_ref); 135 else 136 mobj = mobj_reg_shm_alloc(pages, num_pages, page_offset, 137 shm_ref); 138 out: 139 free(pages); 140 return mobj; 141 } 142 143 bool msg_param_init_memparam(struct optee_msg_param *param, struct mobj *mobj, 144 size_t offset, size_t size, 145 uint64_t cookie, enum msg_param_mem_dir dir) 146 { 147 if (mobj_matches(mobj, CORE_MEM_REG_SHM)) { 148 /* Registered SHM mobj */ 149 switch (dir) { 150 case MSG_PARAM_MEM_DIR_IN: 151 param->attr = OPTEE_MSG_ATTR_TYPE_RMEM_INPUT; 152 break; 153 case MSG_PARAM_MEM_DIR_OUT: 154 param->attr = OPTEE_MSG_ATTR_TYPE_RMEM_OUTPUT; 155 break; 156 case MSG_PARAM_MEM_DIR_INOUT: 157 param->attr = OPTEE_MSG_ATTR_TYPE_RMEM_INOUT; 158 break; 159 default: 160 return false; 161 } 162 163 param->u.rmem.size = size; 164 param->u.rmem.offs = offset; 165 param->u.rmem.shm_ref = cookie; 166 } else if (mobj_matches(mobj, CORE_MEM_NSEC_SHM)) { 167 /* MOBJ from from predefined pool */ 168 paddr_t pa; 169 170 if (mobj_get_pa(mobj, 0, 0, &pa) != TEE_SUCCESS) 171 return false; 172 173 switch (dir) { 174 case MSG_PARAM_MEM_DIR_IN: 175 param->attr = OPTEE_MSG_ATTR_TYPE_TMEM_INPUT; 176 break; 177 case MSG_PARAM_MEM_DIR_OUT: 178 param->attr = OPTEE_MSG_ATTR_TYPE_TMEM_OUTPUT; 179 break; 180 case MSG_PARAM_MEM_DIR_INOUT: 181 param->attr = OPTEE_MSG_ATTR_TYPE_TMEM_INOUT; 182 break; 183 default: 184 return false; 185 } 186 187 param->u.tmem.buf_ptr = pa + offset; 188 param->u.tmem.shm_ref = cookie; 189 param->u.tmem.size = size; 190 } else 191 return false; 192 return true; 193 } 194