1 /*
2 * Copyright (c) 2019-2020 LK Trusty Authors. All Rights Reserved.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining
5 * a copy of this software and associated documentation files
6 * (the "Software"), to deal in the Software without restriction,
7 * including without limitation the rights to use, copy, modify, merge,
8 * publish, distribute, sublicense, and/or sell copies of the Software,
9 * and to permit persons to whom the Software is furnished to do so,
10 * subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be
13 * included in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 */
23
24 #include <compiler.h>
25 #include <debug.h>
26 #include <err.h>
27 #include <interface/arm_ffa/arm_ffa.h>
28 #include <inttypes.h>
29 #include <kernel/mutex.h>
30 #include <kernel/vm.h>
31 #include <lib/arm_ffa/arm_ffa.h>
32 #include <lib/extmem/extmem.h>
33 #include <lib/page_alloc.h>
34 #include <lib/sm.h>
35 #include <lib/smc/smc.h>
36 #include <lk/init.h>
37 #include <string.h>
38 #include <sys/types.h>
39 #include <trace.h>
40
41 #define LOCAL_TRACE 0
42
43 struct sm_mem_obj {
44 uint16_t sender_id;
45 struct ext_mem_obj ext_mem_obj;
46 };
47
sm_mem_obj_compat_destroy(struct vmm_obj * vmm_obj)48 static void sm_mem_obj_compat_destroy(struct vmm_obj* vmm_obj) {
49 struct ext_mem_obj* obj = containerof(vmm_obj, struct ext_mem_obj, vmm_obj);
50 free(obj);
51 }
52
53 static struct vmm_obj_ops sm_mem_obj_compat_ops = {
54 .check_flags = ext_mem_obj_check_flags,
55 .get_page = ext_mem_obj_get_page,
56 .destroy = sm_mem_obj_compat_destroy,
57 };
58
59 /**
60 * sm_mem_compat_get_vmm_obj - Create vmm_obj from id.
61 * @client_id: Id of external entity where the memory originated.
62 * @mem_obj_id: Object id containing a packed address and attibutes.
63 * @size: Size of object.
64 * @objp: Pointer to return object in.
65 * @obj_ref: Reference to *@objp.
66 *
67 * The object paddr and attibutes are encoded in the id for now. Convert it to a
68 * paddr and mmu-flags using the existing helper function.
69 *
70 * Return: 0 on success, negative error code if object could not be created.
71 */
sm_mem_compat_get_vmm_obj(ext_mem_client_id_t client_id,ext_mem_obj_id_t mem_obj_id,size_t size,struct vmm_obj ** objp,struct obj_ref * obj_ref)72 static status_t sm_mem_compat_get_vmm_obj(ext_mem_client_id_t client_id,
73 ext_mem_obj_id_t mem_obj_id,
74 size_t size,
75 struct vmm_obj** objp,
76 struct obj_ref* obj_ref) {
77 int ret;
78 struct ext_mem_obj* obj;
79 struct ns_page_info pinf = {mem_obj_id};
80 ns_addr_t ns_paddr;
81 paddr_t paddr;
82 uint arch_mmu_flags;
83
84 ret = sm_decode_ns_memory_attr(&pinf, &ns_paddr, &arch_mmu_flags);
85 if (ret) {
86 return ret;
87 }
88
89 paddr = (paddr_t)ns_paddr;
90 if (paddr != ns_paddr) {
91 /*
92 * If ns_addr_t is larger than paddr_t and we get an address that does
93 * not fit, return an error as we cannot map that address.
94 */
95 TRACEF("unsupported paddr, 0x%0" PRIxNS_ADDR "\n", ns_paddr);
96 return ERR_INVALID_ARGS;
97 }
98
99 obj = malloc(sizeof(*obj) + ext_mem_obj_page_runs_size(1));
100 if (!obj) {
101 return ERR_NO_MEMORY;
102 }
103
104 arch_mmu_flags |= ARCH_MMU_FLAG_NS | ARCH_MMU_FLAG_PERM_NO_EXECUTE;
105 ext_mem_obj_initialize(obj, obj_ref, mem_obj_id, 0, &sm_mem_obj_compat_ops,
106 arch_mmu_flags, 1);
107 obj->page_runs[0].paddr = paddr;
108 obj->page_runs[0].size = size;
109 *objp = &obj->vmm_obj;
110
111 return 0;
112 }
113
114 /**
115 * sm_mem_obj_destroy: Destroy memory object.
116 * @vmm_obj: VMM object to destroy.
117 *
118 * Called after the last reference to @vmm_obj has been released. Relinquish
119 * shared memory object id with SPM/Hypervisor and free local tracking object.
120 */
sm_mem_obj_destroy(struct vmm_obj * vmm_obj)121 static void sm_mem_obj_destroy(struct vmm_obj* vmm_obj) {
122 int ret;
123 struct sm_mem_obj* obj =
124 containerof(vmm_obj, struct sm_mem_obj, ext_mem_obj.vmm_obj);
125
126 DEBUG_ASSERT(obj);
127
128 ret = arm_ffa_mem_relinquish(obj->ext_mem_obj.id);
129 if (ret != NO_ERROR) {
130 TRACEF("Failed to relinquish the shared memory (%d)\n", ret);
131 }
132
133 free(obj);
134 }
135
136 static struct vmm_obj_ops sm_mem_obj_ops = {
137 .check_flags = ext_mem_obj_check_flags,
138 .get_page = ext_mem_obj_get_page,
139 .destroy = sm_mem_obj_destroy,
140 };
141
142 /**
143 * sm_mem_alloc_obj - Allocate and initialize memory object.
144 * @sender_id: FF-A vm id of sender.
145 * @mem_id: Id of object.
146 * @tag: Tag of the object
147 * @page_run_count: Number of page runs to allocate for object.
148 * @arch_mmu_flags: Memory type and permissions.
149 * @obj_ref: Reference to returned object.
150 *
151 * Return: Pointer to &struct sm_mem_obj, or %NULL if allocation fails.
152 */
sm_mem_alloc_obj(uint16_t sender_id,ext_mem_obj_id_t mem_id,uint64_t tag,size_t page_run_count,uint arch_mmu_flags,struct obj_ref * obj_ref)153 static struct sm_mem_obj* sm_mem_alloc_obj(uint16_t sender_id,
154 ext_mem_obj_id_t mem_id,
155 uint64_t tag,
156 size_t page_run_count,
157 uint arch_mmu_flags,
158 struct obj_ref* obj_ref) {
159 struct sm_mem_obj* obj =
160 malloc(sizeof(*obj) + ext_mem_obj_page_runs_size(page_run_count));
161 if (!obj) {
162 return NULL;
163 }
164 ext_mem_obj_initialize(&obj->ext_mem_obj, obj_ref, mem_id, tag,
165 &sm_mem_obj_ops, arch_mmu_flags, page_run_count);
166 obj->sender_id = sender_id;
167
168 return obj;
169 }
170
171 /* sm_mem_get_vmm_obj - Looks up a shared memory object using FF-A.
172 * @client_id: Id of external entity where the memory originated.
173 * @mem_obj_id: Id of shared memory object to lookup and return.
174 * @tag: Tag of the memory.
175 * @size: Size hint for object. Caller expects an object at least this
176 * big.
177 * @objp: Pointer to return object in.
178 * @obj_ref: Reference to *@objp.
179 *
180 * Return: 0 on success. ERR_NOT_FOUND if @id does not exist.
181 */
sm_mem_get_vmm_obj(ext_mem_client_id_t client_id,ext_mem_obj_id_t mem_obj_id,uint64_t tag,size_t size,struct vmm_obj ** objp,struct obj_ref * obj_ref)182 static status_t sm_mem_get_vmm_obj(ext_mem_client_id_t client_id,
183 ext_mem_obj_id_t mem_obj_id,
184 uint64_t tag,
185 size_t size,
186 struct vmm_obj** objp,
187 struct obj_ref* obj_ref) {
188 int ret;
189 struct arm_ffa_mem_frag_info frag_info;
190 uint32_t address_range_count;
191 uint arch_mmu_flags;
192 struct sm_mem_obj* obj;
193 struct obj_ref tmp_obj_ref = OBJ_REF_INITIAL_VALUE(tmp_obj_ref);
194
195 DEBUG_ASSERT(objp);
196 DEBUG_ASSERT(obj_ref);
197
198 if ((client_id & 0xffff) != client_id) {
199 TRACEF("Invalid client ID\n");
200 return ERR_INVALID_ARGS;
201 }
202
203 ret = arm_ffa_mem_retrieve_start((uint16_t)client_id, mem_obj_id, tag,
204 &address_range_count, &arch_mmu_flags,
205 &frag_info);
206
207 if (ret != NO_ERROR) {
208 TRACEF("Failed to get FF-A memory buffer, err=%d\n", ret);
209 goto err_mem_get_access;
210 }
211 obj = sm_mem_alloc_obj(client_id, mem_obj_id, tag, address_range_count,
212 arch_mmu_flags, &tmp_obj_ref);
213 if (!obj) {
214 TRACEF("Failed to allocate a shared memory object\n");
215 ret = ERR_NO_MEMORY;
216 goto err_mem_alloc_obj;
217 }
218
219 for (uint32_t i = 0; i < address_range_count; i++) {
220 if (frag_info.start_index + frag_info.count <= i) {
221 arm_ffa_rx_release();
222 ret = arm_ffa_mem_retrieve_next_frag(mem_obj_id, &frag_info);
223 if (ret != NO_ERROR) {
224 TRACEF("Failed to get next fragment, err=%d\n", ret);
225 goto err_mem_next_frag;
226 }
227 }
228 ret = arm_ffa_mem_address_range_get(
229 &frag_info, i, &obj->ext_mem_obj.page_runs[i].paddr,
230 &obj->ext_mem_obj.page_runs[i].size);
231 if (ret != NO_ERROR) {
232 TRACEF("Failed to get address range, err=%d\n", ret);
233 goto err_mem_address_range;
234 }
235 }
236
237 /* No lock needed as the object is not yet visible to anyone else */
238 obj_ref_transfer(obj_ref, &tmp_obj_ref);
239 *objp = &obj->ext_mem_obj.vmm_obj;
240
241 arm_ffa_rx_release();
242
243 return 0;
244
245 err_mem_address_range:
246 err_mem_next_frag:
247 DEBUG_ASSERT(obj_ref_active(&tmp_obj_ref));
248 vmm_obj_del_ref(&obj->ext_mem_obj.vmm_obj, &tmp_obj_ref);
249
250 err_mem_alloc_obj:
251 err_mem_get_access:
252 arm_ffa_rx_release();
253 return ret;
254 }
255
256 /*
257 * ext_mem_get_vmm_obj - Lookup or create shared memory object.
258 * @client_id: Id of external entity where the memory originated.
259 * @mem_obj_id: Id of shared memory object to lookup and return.
260 * @tag: Value to identify the transaction.
261 * @size: Size hint for object.
262 * @objp: Pointer to return object in.
263 * @obj_ref: Reference to *@objp.
264 *
265 * Call SPM/Hypervisor to retrieve memory region or extract address and
266 * attributes from id for old clients.
267 */
ext_mem_get_vmm_obj(ext_mem_client_id_t client_id,ext_mem_obj_id_t mem_obj_id,uint64_t tag,size_t size,struct vmm_obj ** objp,struct obj_ref * obj_ref)268 status_t ext_mem_get_vmm_obj(ext_mem_client_id_t client_id,
269 ext_mem_obj_id_t mem_obj_id,
270 uint64_t tag,
271 size_t size,
272 struct vmm_obj** objp,
273 struct obj_ref* obj_ref) {
274 if (sm_get_api_version() >= TRUSTY_API_VERSION_MEM_OBJ) {
275 return sm_mem_get_vmm_obj(client_id, mem_obj_id, tag, size, objp,
276 obj_ref);
277 } else if (!client_id && !tag) {
278 /* If client is not running under a hypervisor allow using
279 old api. */
280 return sm_mem_compat_get_vmm_obj(client_id, mem_obj_id, size, objp,
281 obj_ref);
282 } else {
283 return ERR_NOT_SUPPORTED;
284 }
285 }
286
287 /**
288 * shared_mem_init - Connect to SPM/Hypervisor.
289 * @level: Unused.
290 *
291 */
shared_mem_init(uint level)292 static void shared_mem_init(uint level) {
293 /* Check the FF-A module initialized successfully */
294 if (!arm_ffa_is_init()) {
295 TRACEF("arm_ffa module is not initialized\n");
296 if (sm_check_and_lock_api_version(TRUSTY_API_VERSION_MEM_OBJ)) {
297 panic("shared_mem_init failed after mem_obj version selected\n");
298 }
299 }
300 }
301
302 LK_INIT_HOOK(shared_mem, shared_mem_init, LK_INIT_LEVEL_APPS);
303