xref: /optee_os/core/kernel/msg_param.c (revision e7a8839b3e0658a7deb6f980c8d73f3355533054)
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(const struct optee_msg_param *param,
114 					   bool map_buffer)
115 {
116 	struct mobj *mobj = NULL;
117 	paddr_t *pages;
118 	paddr_t page_offset;
119 	size_t num_pages;
120 	uint64_t buf_ptr;
121 
122 	assert(param->attr & OPTEE_MSG_ATTR_NONCONTIG);
123 
124 	/* Make sure that NW will not change value in SHM */
125 	buf_ptr = param->u.tmem.buf_ptr;
126 
127 	page_offset = buf_ptr & SMALL_PAGE_MASK;
128 	num_pages = (param->u.tmem.size + page_offset - 1) /
129 		    SMALL_PAGE_SIZE + 1;
130 
131 	pages = malloc(num_pages * sizeof(paddr_t));
132 	if (!pages)
133 		return NULL;
134 
135 	if (!msg_param_extract_pages(buf_ptr & ~SMALL_PAGE_MASK,
136 				     pages, num_pages))
137 		goto out;
138 
139 	if (map_buffer)
140 		mobj = mobj_mapped_shm_alloc(pages, num_pages, page_offset,
141 					     param->u.tmem.shm_ref);
142 	else
143 		mobj = mobj_reg_shm_alloc(pages, num_pages, page_offset,
144 					  param->u.tmem.shm_ref);
145 out:
146 	free(pages);
147 	return mobj;
148 }
149 
150 bool msg_param_init_memparam(struct optee_msg_param *param, struct mobj *mobj,
151 			     size_t offset, size_t size,
152 			     uint64_t cookie, enum msg_param_mem_dir dir)
153 {
154 	if (mobj_matches(mobj, CORE_MEM_REG_SHM)) {
155 		/* Registered SHM mobj */
156 		switch (dir) {
157 		case MSG_PARAM_MEM_DIR_IN:
158 			param->attr = OPTEE_MSG_ATTR_TYPE_RMEM_INPUT;
159 			break;
160 		case MSG_PARAM_MEM_DIR_OUT:
161 			param->attr = OPTEE_MSG_ATTR_TYPE_RMEM_OUTPUT;
162 			break;
163 		case MSG_PARAM_MEM_DIR_INOUT:
164 			param->attr = OPTEE_MSG_ATTR_TYPE_RMEM_INOUT;
165 			break;
166 		default:
167 			return false;
168 		}
169 
170 		param->u.rmem.size = size;
171 		param->u.rmem.offs = offset;
172 		param->u.rmem.shm_ref = cookie;
173 	} else if (mobj_matches(mobj, CORE_MEM_NSEC_SHM)) {
174 		/* MOBJ from from predefined pool */
175 		paddr_t pa;
176 
177 		if (mobj_get_pa(mobj, 0, 0, &pa) != TEE_SUCCESS)
178 			return false;
179 
180 		switch (dir) {
181 		case MSG_PARAM_MEM_DIR_IN:
182 			param->attr = OPTEE_MSG_ATTR_TYPE_TMEM_INPUT;
183 			break;
184 		case MSG_PARAM_MEM_DIR_OUT:
185 			param->attr = OPTEE_MSG_ATTR_TYPE_TMEM_OUTPUT;
186 			break;
187 		case MSG_PARAM_MEM_DIR_INOUT:
188 			param->attr = OPTEE_MSG_ATTR_TYPE_TMEM_INOUT;
189 			break;
190 		default:
191 			return false;
192 		}
193 
194 		param->u.tmem.buf_ptr = pa + offset;
195 		param->u.tmem.shm_ref = cookie;
196 		param->u.tmem.size = size;
197 	} else
198 		return false;
199 	return true;
200 }
201