// SPDX-License-Identifier: BSD-2-Clause /* * Copyright (c) 2017, EPAM Systems * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include /** * 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(paddr_t buf_ptr, size_t size, uint64_t shm_ref, bool map_buffer) { struct mobj *mobj = NULL; paddr_t *pages; paddr_t page_offset; size_t num_pages; page_offset = buf_ptr & SMALL_PAGE_MASK; num_pages = (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, shm_ref); else mobj = mobj_reg_shm_alloc(pages, num_pages, page_offset, shm_ref); out: free(pages); return mobj; }