blob: 2dc2899ae66707ad2605bf4bbc8c75d68d494c82 [file] [log] [blame]
Jose Marinho75509b42019-04-09 09:34:59 +01001/*
2 * Copyright 2019 The Hafnium Authors.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * https://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "hf/api.h"
Jose Marinho09b1db82019-08-08 09:16:59 +010018#include "hf/check.h"
Jose Marinho75509b42019-04-09 09:34:59 +010019#include "hf/dlog.h"
20#include "hf/spci_internal.h"
21#include "hf/std.h"
Andrew Scull3c257452019-11-26 13:32:50 +000022#include "hf/vm.h"
Jose Marinho75509b42019-04-09 09:34:59 +010023
24/**
Jose Marinho75509b42019-04-09 09:34:59 +010025 * Obtain the next mode to apply to the two VMs.
26 *
Jose Marinho7fbbf2e2019-08-05 13:19:58 +010027 * Returns true iff a state transition was found.
Jose Marinho75509b42019-04-09 09:34:59 +010028 */
29static bool spci_msg_get_next_state(
30 const struct spci_mem_transitions *transitions,
31 uint32_t transition_count, uint32_t memory_to_attributes,
Andrew Walbran1281ed42019-10-22 17:23:40 +010032 uint32_t orig_from_mode, uint32_t orig_to_mode, uint32_t *from_mode,
33 uint32_t *to_mode)
Jose Marinho75509b42019-04-09 09:34:59 +010034{
35 const uint32_t state_mask =
36 MM_MODE_INVALID | MM_MODE_UNOWNED | MM_MODE_SHARED;
37 const uint32_t orig_from_state = orig_from_mode & state_mask;
38
39 for (uint32_t index = 0; index < transition_count; index++) {
40 uint32_t table_orig_from_mode =
41 transitions[index].orig_from_mode;
42 uint32_t table_orig_to_mode = transitions[index].orig_to_mode;
43
44 if (((orig_from_state) == table_orig_from_mode) &&
45 ((orig_to_mode & state_mask) == table_orig_to_mode)) {
46 *to_mode = transitions[index].to_mode |
47 memory_to_attributes;
Jose Marinho713f13a2019-05-21 11:54:16 +010048
49 *from_mode = transitions[index].from_mode |
50 (~state_mask & orig_from_mode);
Jose Marinho75509b42019-04-09 09:34:59 +010051
52 return true;
53 }
54 }
55 return false;
56}
57
58/**
59 * Verify that all pages have the same mode, that the starting mode
60 * constitutes a valid state and obtain the next mode to apply
61 * to the two VMs.
62 *
63 * Returns:
64 * The error code false indicates that:
65 * 1) a state transition was not found;
66 * 2) the pages being shared do not have the same mode within the <to>
67 * or <form> VMs;
68 * 3) The beginning and end IPAs are not page aligned;
69 * 4) The requested share type was not handled.
70 * Success is indicated by true.
71 *
72 */
Andrew Walbranf4b51af2020-02-03 14:44:54 +000073static bool spci_msg_check_transition(
74 struct vm *to, struct vm *from, uint32_t share_func,
75 uint32_t *orig_from_mode,
76 struct spci_memory_region_constituent *constituents,
77 uint32_t constituent_count, uint32_t memory_to_attributes,
78 uint32_t *from_mode, uint32_t *to_mode)
Jose Marinho75509b42019-04-09 09:34:59 +010079{
Andrew Walbran1281ed42019-10-22 17:23:40 +010080 uint32_t orig_to_mode;
Jose Marinho75509b42019-04-09 09:34:59 +010081 const struct spci_mem_transitions *mem_transition_table;
82 uint32_t transition_table_size;
Jose Marinho7fbbf2e2019-08-05 13:19:58 +010083 uint32_t i;
Jose Marinho75509b42019-04-09 09:34:59 +010084
85 /*
86 * TODO: Transition table does not currently consider the multiple
87 * shared case.
88 */
89 static const struct spci_mem_transitions donate_transitions[] = {
90 {
91 /* 1) {O-EA, !O-NA} -> {!O-NA, O-EA} */
92 .orig_from_mode = 0,
93 .orig_to_mode = MM_MODE_INVALID | MM_MODE_UNOWNED,
94 .from_mode = MM_MODE_INVALID | MM_MODE_UNOWNED,
95 .to_mode = 0,
96 },
97 {
98 /* 2) {O-NA, !O-EA} -> {!O-NA, O-EA} */
99 .orig_from_mode = MM_MODE_INVALID,
100 .orig_to_mode = MM_MODE_UNOWNED,
101 .from_mode = MM_MODE_INVALID | MM_MODE_UNOWNED,
102 .to_mode = 0,
103 },
104 {
105 /* 3) {O-SA, !O-SA} -> {!O-NA, O-EA} */
106 .orig_from_mode = MM_MODE_SHARED,
107 .orig_to_mode = MM_MODE_UNOWNED | MM_MODE_SHARED,
108 .from_mode = MM_MODE_INVALID | MM_MODE_UNOWNED,
109 .to_mode = 0,
110 },
111 {
112 /*
113 * Duplicate of 1) in order to cater for an alternative
114 * representation of !O-NA:
115 * (INVALID | UNOWNED | SHARED) and (INVALID | UNOWNED)
116 * are both alternate representations of !O-NA.
117 */
118 /* 4) {O-EA, !O-NA} -> {!O-NA, O-EA} */
119 .orig_from_mode = 0,
120 .orig_to_mode = MM_MODE_INVALID | MM_MODE_UNOWNED |
121 MM_MODE_SHARED,
122 .from_mode = MM_MODE_INVALID | MM_MODE_UNOWNED |
123 MM_MODE_SHARED,
124 .to_mode = 0,
125 },
126 };
Jose Marinho56c25732019-05-20 09:48:53 +0100127
Jose Marinho713f13a2019-05-21 11:54:16 +0100128 static const uint32_t size_donate_transitions =
129 ARRAY_SIZE(donate_transitions);
130
Andrew Walbran648fc3e2019-10-22 16:23:05 +0100131 /*
132 * This data structure holds the allowed state transitions for the
133 * "lend" state machine. In this state machine the owner keeps ownership
134 * but loses access to the lent pages.
135 */
136 static const struct spci_mem_transitions lend_transitions[] = {
Jose Marinho56c25732019-05-20 09:48:53 +0100137 {
Andrew Walbran648fc3e2019-10-22 16:23:05 +0100138 /* 1) {O-EA, !O-NA} -> {O-NA, !O-EA} */
139 .orig_from_mode = 0,
140 .orig_to_mode = MM_MODE_INVALID | MM_MODE_UNOWNED |
141 MM_MODE_SHARED,
142 .from_mode = MM_MODE_INVALID,
143 .to_mode = MM_MODE_UNOWNED,
Jose Marinho56c25732019-05-20 09:48:53 +0100144 },
145 {
Andrew Walbran648fc3e2019-10-22 16:23:05 +0100146 /*
147 * Duplicate of 1) in order to cater for an alternative
148 * representation of !O-NA:
149 * (INVALID | UNOWNED | SHARED) and (INVALID | UNOWNED)
150 * are both alternate representations of !O-NA.
151 */
152 /* 2) {O-EA, !O-NA} -> {O-NA, !O-EA} */
153 .orig_from_mode = 0,
154 .orig_to_mode = MM_MODE_INVALID | MM_MODE_UNOWNED,
155 .from_mode = MM_MODE_INVALID,
156 .to_mode = MM_MODE_UNOWNED,
Jose Marinho56c25732019-05-20 09:48:53 +0100157 },
158 };
159
Andrew Walbran648fc3e2019-10-22 16:23:05 +0100160 static const uint32_t size_lend_transitions =
161 ARRAY_SIZE(lend_transitions);
Jose Marinho56c25732019-05-20 09:48:53 +0100162
Jose Marinho713f13a2019-05-21 11:54:16 +0100163 /*
Andrew Walbran648fc3e2019-10-22 16:23:05 +0100164 * This data structure holds the allowed state transitions for the
165 * "share" state machine. In this state machine the owner keeps the
166 * shared pages mapped on its stage2 table and keeps access as well.
Jose Marinho713f13a2019-05-21 11:54:16 +0100167 */
Andrew Walbran648fc3e2019-10-22 16:23:05 +0100168 static const struct spci_mem_transitions share_transitions[] = {
Jose Marinho713f13a2019-05-21 11:54:16 +0100169 {
170 /* 1) {O-EA, !O-NA} -> {O-SA, !O-SA} */
171 .orig_from_mode = 0,
172 .orig_to_mode = MM_MODE_INVALID | MM_MODE_UNOWNED |
173 MM_MODE_SHARED,
174 .from_mode = MM_MODE_SHARED,
175 .to_mode = MM_MODE_UNOWNED | MM_MODE_SHARED,
176 },
177 {
178 /*
179 * Duplicate of 1) in order to cater for an alternative
180 * representation of !O-NA:
181 * (INVALID | UNOWNED | SHARED) and (INVALID | UNOWNED)
182 * are both alternate representations of !O-NA.
183 */
184 /* 2) {O-EA, !O-NA} -> {O-SA, !O-SA} */
185 .orig_from_mode = 0,
186 .orig_to_mode = MM_MODE_INVALID | MM_MODE_UNOWNED,
187 .from_mode = MM_MODE_SHARED,
188 .to_mode = MM_MODE_UNOWNED | MM_MODE_SHARED,
189 },
190 };
191
Andrew Walbran648fc3e2019-10-22 16:23:05 +0100192 static const uint32_t size_share_transitions =
193 ARRAY_SIZE(share_transitions);
194
195 static const struct spci_mem_transitions relinquish_transitions[] = {
196 {
197 /* 1) {!O-EA, O-NA} -> {!O-NA, O-EA} */
198 .orig_from_mode = MM_MODE_UNOWNED,
199 .orig_to_mode = MM_MODE_INVALID,
200 .from_mode = MM_MODE_INVALID | MM_MODE_UNOWNED |
201 MM_MODE_SHARED,
202 .to_mode = 0,
203 },
204 {
205 /* 2) {!O-SA, O-SA} -> {!O-NA, O-EA} */
206 .orig_from_mode = MM_MODE_UNOWNED | MM_MODE_SHARED,
207 .orig_to_mode = MM_MODE_SHARED,
208 .from_mode = MM_MODE_INVALID | MM_MODE_UNOWNED |
209 MM_MODE_SHARED,
210 .to_mode = 0,
211 },
212 };
213
214 static const uint32_t size_relinquish_transitions =
215 ARRAY_SIZE(relinquish_transitions);
Jose Marinho75509b42019-04-09 09:34:59 +0100216
Andrew Walbranf4b51af2020-02-03 14:44:54 +0000217 if (constituent_count == 0) {
Jose Marinho7fbbf2e2019-08-05 13:19:58 +0100218 /*
219 * Fail if there are no constituents. Otherwise
220 * spci_msg_get_next_state would get an unitialised
221 * *orig_from_mode and orig_to_mode.
222 */
Jose Marinho75509b42019-04-09 09:34:59 +0100223 return false;
224 }
225
Andrew Walbranf4b51af2020-02-03 14:44:54 +0000226 for (i = 0; i < constituent_count; ++i) {
Andrew Walbrand88ee922020-01-15 18:13:21 +0000227 ipaddr_t begin =
228 ipa_init(spci_memory_region_constituent_get_address(
229 &constituents[i]));
Jose Marinho7fbbf2e2019-08-05 13:19:58 +0100230 size_t size = constituents[i].page_count * PAGE_SIZE;
231 ipaddr_t end = ipa_add(begin, size);
232 uint32_t current_from_mode;
233 uint32_t current_to_mode;
234
235 /* Fail if addresses are not page-aligned. */
236 if (!is_aligned(ipa_addr(begin), PAGE_SIZE) ||
237 !is_aligned(ipa_addr(end), PAGE_SIZE)) {
238 return false;
239 }
240
241 /*
242 * Ensure that this constituent memory range is all mapped with
243 * the same mode.
244 */
245 if (!mm_vm_get_mode(&from->ptable, begin, end,
246 &current_from_mode) ||
247 !mm_vm_get_mode(&to->ptable, begin, end,
248 &current_to_mode)) {
249 return false;
250 }
251
252 /*
253 * Ensure that all constituents are mapped with the same mode.
254 */
255 if (i == 0) {
256 *orig_from_mode = current_from_mode;
257 orig_to_mode = current_to_mode;
258 } else if (current_from_mode != *orig_from_mode ||
259 current_to_mode != orig_to_mode) {
260 return false;
261 }
Jose Marinho75509b42019-04-09 09:34:59 +0100262 }
263
Andrew Scullb5f49e02019-10-02 13:20:47 +0100264 /* Ensure the address range is normal memory and not a device. */
265 if (*orig_from_mode & MM_MODE_D) {
266 return false;
267 }
268
Andrew Walbrane7ad3c02019-12-24 17:03:04 +0000269 switch (share_func) {
270 case SPCI_MEM_DONATE_32:
Jose Marinho75509b42019-04-09 09:34:59 +0100271 mem_transition_table = donate_transitions;
272 transition_table_size = size_donate_transitions;
273 break;
274
Andrew Walbrane7ad3c02019-12-24 17:03:04 +0000275 case SPCI_MEM_LEND_32:
Andrew Walbran648fc3e2019-10-22 16:23:05 +0100276 mem_transition_table = lend_transitions;
277 transition_table_size = size_lend_transitions;
278 break;
279
Andrew Walbrane7ad3c02019-12-24 17:03:04 +0000280 case SPCI_MEM_SHARE_32:
Andrew Walbran648fc3e2019-10-22 16:23:05 +0100281 mem_transition_table = share_transitions;
282 transition_table_size = size_share_transitions;
283 break;
284
Andrew Walbrane7ad3c02019-12-24 17:03:04 +0000285 case HF_SPCI_MEM_RELINQUISH:
Jose Marinho56c25732019-05-20 09:48:53 +0100286 mem_transition_table = relinquish_transitions;
287 transition_table_size = size_relinquish_transitions;
288 break;
289
Jose Marinho75509b42019-04-09 09:34:59 +0100290 default:
291 return false;
292 }
293
294 return spci_msg_get_next_state(mem_transition_table,
295 transition_table_size,
296 memory_to_attributes, *orig_from_mode,
297 orig_to_mode, from_mode, to_mode);
298}
Jose Marinho09b1db82019-08-08 09:16:59 +0100299
300/**
301 * Updates a VM's page table such that the given set of physical address ranges
302 * are mapped in the address space at the corresponding address ranges, in the
303 * mode provided.
304 *
305 * If commit is false, the page tables will be allocated from the mpool but no
306 * mappings will actually be updated. This function must always be called first
307 * with commit false to check that it will succeed before calling with commit
308 * true, to avoid leaving the page table in a half-updated state. To make a
309 * series of changes atomically you can call them all with commit false before
310 * calling them all with commit true.
311 *
312 * mm_vm_defrag should always be called after a series of page table updates,
313 * whether they succeed or fail.
314 *
315 * Returns true on success, or false if the update failed and no changes were
316 * made to memory mappings.
317 */
318static bool spci_region_group_identity_map(
Andrew Walbranf4b51af2020-02-03 14:44:54 +0000319 struct vm_locked vm_locked,
320 struct spci_memory_region_constituent *constituents,
321 uint32_t constituent_count, int mode, struct mpool *ppool, bool commit)
Jose Marinho09b1db82019-08-08 09:16:59 +0100322{
Jose Marinho09b1db82019-08-08 09:16:59 +0100323 /* Iterate over the memory region constituents. */
Andrew Walbranf4b51af2020-02-03 14:44:54 +0000324 for (uint32_t index = 0; index < constituent_count; index++) {
Jose Marinho09b1db82019-08-08 09:16:59 +0100325 size_t size = constituents[index].page_count * PAGE_SIZE;
Andrew Walbrand88ee922020-01-15 18:13:21 +0000326 paddr_t pa_begin = pa_from_ipa(
327 ipa_init(spci_memory_region_constituent_get_address(
328 &constituents[index])));
Jose Marinho09b1db82019-08-08 09:16:59 +0100329 paddr_t pa_end = pa_add(pa_begin, size);
330
331 if (commit) {
Andrew Scull3c257452019-11-26 13:32:50 +0000332 vm_identity_commit(vm_locked, pa_begin, pa_end, mode,
333 ppool, NULL);
334 } else if (!vm_identity_prepare(vm_locked, pa_begin, pa_end,
335 mode, ppool)) {
Jose Marinho09b1db82019-08-08 09:16:59 +0100336 return false;
337 }
338 }
339
340 return true;
341}
342
343/**
344 * Clears a region of physical memory by overwriting it with zeros. The data is
345 * flushed from the cache so the memory has been cleared across the system.
346 */
347static bool clear_memory(paddr_t begin, paddr_t end, struct mpool *ppool)
348{
349 /*
Fuad Tabbaed294af2019-12-20 10:43:01 +0000350 * TODO: change this to a CPU local single page window rather than a
Jose Marinho09b1db82019-08-08 09:16:59 +0100351 * global mapping of the whole range. Such an approach will limit
352 * the changes to stage-1 tables and will allow only local
353 * invalidation.
354 */
355 bool ret;
356 struct mm_stage1_locked stage1_locked = mm_lock_stage1();
357 void *ptr =
358 mm_identity_map(stage1_locked, begin, end, MM_MODE_W, ppool);
359 size_t size = pa_difference(begin, end);
360
361 if (!ptr) {
362 /* TODO: partial defrag of failed range. */
363 /* Recover any memory consumed in failed mapping. */
364 mm_defrag(stage1_locked, ppool);
365 goto fail;
366 }
367
368 memset_s(ptr, size, 0, size);
369 arch_mm_flush_dcache(ptr, size);
370 mm_unmap(stage1_locked, begin, end, ppool);
371
372 ret = true;
373 goto out;
374
375fail:
376 ret = false;
377
378out:
379 mm_unlock_stage1(&stage1_locked);
380
381 return ret;
382}
383
384/**
385 * Clears a region of physical memory by overwriting it with zeros. The data is
386 * flushed from the cache so the memory has been cleared across the system.
387 */
Andrew Walbranf4b51af2020-02-03 14:44:54 +0000388static bool spci_clear_memory_constituents(
389 struct spci_memory_region_constituent *constituents,
390 uint32_t constituent_count, struct mpool *api_page_pool)
Jose Marinho09b1db82019-08-08 09:16:59 +0100391{
392 struct mpool local_page_pool;
Jose Marinho09b1db82019-08-08 09:16:59 +0100393 struct mm_stage1_locked stage1_locked;
394 bool ret = false;
395
396 /*
397 * Create a local pool so any freed memory can't be used by another
398 * thread. This is to ensure each constituent that is mapped can be
399 * unmapped again afterwards.
400 */
401 mpool_init_with_fallback(&local_page_pool, api_page_pool);
402
403 /* Iterate over the memory region constituents. */
Andrew Walbranf4b51af2020-02-03 14:44:54 +0000404 for (uint32_t i = 0; i < constituent_count; ++i) {
Jose Marinho09b1db82019-08-08 09:16:59 +0100405 size_t size = constituents[i].page_count * PAGE_SIZE;
Andrew Walbrand88ee922020-01-15 18:13:21 +0000406 paddr_t begin = pa_from_ipa(
407 ipa_init(spci_memory_region_constituent_get_address(
408 &constituents[i])));
Jose Marinho09b1db82019-08-08 09:16:59 +0100409 paddr_t end = pa_add(begin, size);
410
411 if (!clear_memory(begin, end, &local_page_pool)) {
412 /*
413 * api_clear_memory will defrag on failure, so no need
414 * to do it here.
415 */
416 goto out;
417 }
418 }
419
420 /*
421 * Need to defrag after clearing, as it may have added extra mappings to
422 * the stage 1 page table.
423 */
424 stage1_locked = mm_lock_stage1();
425 mm_defrag(stage1_locked, &local_page_pool);
426 mm_unlock_stage1(&stage1_locked);
427
428 ret = true;
429
430out:
431 mpool_fini(&local_page_pool);
432 return ret;
433}
434
435/**
436 * Shares memory from the calling VM with another. The memory can be shared in
437 * different modes.
438 *
439 * This function requires the calling context to hold the <to> and <from> locks.
440 *
441 * Returns:
442 * In case of error one of the following values is returned:
443 * 1) SPCI_INVALID_PARAMETERS - The endpoint provided parameters were
444 * erroneous;
445 * 2) SPCI_NO_MEMORY - Hafnium did not have sufficient memory to complete
446 * the request.
447 * Success is indicated by SPCI_SUCCESS.
448 */
449static struct spci_value spci_share_memory(
450 struct vm_locked to_locked, struct vm_locked from_locked,
Andrew Walbranf4b51af2020-02-03 14:44:54 +0000451 struct spci_memory_region_constituent *constituents,
452 uint32_t constituent_count, uint32_t memory_to_attributes,
453 uint32_t share_func, struct mpool *api_page_pool, bool clear)
Jose Marinho09b1db82019-08-08 09:16:59 +0100454{
455 struct vm *to = to_locked.vm;
456 struct vm *from = from_locked.vm;
457 uint32_t orig_from_mode;
458 uint32_t from_mode;
459 uint32_t to_mode;
460 struct mpool local_page_pool;
461 struct spci_value ret;
Jose Marinho09b1db82019-08-08 09:16:59 +0100462
463 /*
Andrew Walbrand88ee922020-01-15 18:13:21 +0000464 * Make sure constituents are properly aligned to a 32-bit boundary. If
465 * not we would get alignment faults trying to read (32-bit) values.
Jose Marinho09b1db82019-08-08 09:16:59 +0100466 */
Andrew Walbrand88ee922020-01-15 18:13:21 +0000467 if (!is_aligned(constituents, 4)) {
Jose Marinho09b1db82019-08-08 09:16:59 +0100468 return spci_error(SPCI_INVALID_PARAMETERS);
469 }
470
471 /* Disallow reflexive shares as this suggests an error in the VM. */
472 if (to == from) {
473 return spci_error(SPCI_INVALID_PARAMETERS);
474 }
475
476 /*
477 * Check if the state transition is lawful for both VMs involved
478 * in the memory exchange, ensure that all constituents of a memory
479 * region being shared are at the same state.
480 */
Andrew Walbrane7ad3c02019-12-24 17:03:04 +0000481 if (!spci_msg_check_transition(to, from, share_func, &orig_from_mode,
Andrew Walbranf4b51af2020-02-03 14:44:54 +0000482 constituents, constituent_count,
483 memory_to_attributes, &from_mode,
484 &to_mode)) {
Jose Marinho09b1db82019-08-08 09:16:59 +0100485 return spci_error(SPCI_INVALID_PARAMETERS);
486 }
487
488 /*
489 * Create a local pool so any freed memory can't be used by another
490 * thread. This is to ensure the original mapping can be restored if the
491 * clear fails.
492 */
493 mpool_init_with_fallback(&local_page_pool, api_page_pool);
494
495 /*
496 * First reserve all required memory for the new page table entries in
497 * both sender and recipient page tables without committing, to make
498 * sure the entire operation will succeed without exhausting the page
499 * pool.
500 */
Andrew Walbranf4b51af2020-02-03 14:44:54 +0000501 if (!spci_region_group_identity_map(from_locked, constituents,
502 constituent_count, from_mode,
503 api_page_pool, false) ||
504 !spci_region_group_identity_map(to_locked, constituents,
505 constituent_count, to_mode,
Jose Marinho09b1db82019-08-08 09:16:59 +0100506 api_page_pool, false)) {
507 /* TODO: partial defrag of failed range. */
508 ret = spci_error(SPCI_NO_MEMORY);
509 goto out;
510 }
511
512 /*
513 * First update the mapping for the sender so there is no overlap with
514 * the recipient. This won't allocate because the transaction was
515 * already prepared above, but may free pages in the case that a whole
516 * block is being unmapped that was previously partially mapped.
517 */
Andrew Walbranf4b51af2020-02-03 14:44:54 +0000518 CHECK(spci_region_group_identity_map(from_locked, constituents,
519 constituent_count, from_mode,
520 &local_page_pool, true));
Jose Marinho09b1db82019-08-08 09:16:59 +0100521
522 /* Clear the memory so no VM or device can see the previous contents. */
Andrew Walbranf4b51af2020-02-03 14:44:54 +0000523 if (clear && !spci_clear_memory_constituents(
524 constituents, constituent_count, api_page_pool)) {
Jose Marinho09b1db82019-08-08 09:16:59 +0100525 /*
526 * On failure, roll back by returning memory to the sender. This
527 * may allocate pages which were previously freed into
528 * `local_page_pool` by the call above, but will never allocate
529 * more pages than that so can never fail.
530 */
Andrew Walbranf4b51af2020-02-03 14:44:54 +0000531 CHECK(spci_region_group_identity_map(
532 from_locked, constituents, constituent_count,
533 orig_from_mode, &local_page_pool, true));
Jose Marinho09b1db82019-08-08 09:16:59 +0100534
535 ret = spci_error(SPCI_NO_MEMORY);
536 goto out;
537 }
538
539 /*
540 * Complete the transfer by mapping the memory into the recipient. This
541 * won't allocate because the transaction was already prepared above, so
542 * it doesn't need to use the `local_page_pool`.
543 */
Andrew Walbranf4b51af2020-02-03 14:44:54 +0000544 CHECK(spci_region_group_identity_map(to_locked, constituents,
545 constituent_count, to_mode,
Andrew Scull3c257452019-11-26 13:32:50 +0000546 api_page_pool, true));
Jose Marinho09b1db82019-08-08 09:16:59 +0100547
548 ret = (struct spci_value){.func = SPCI_SUCCESS_32};
549
550out:
551 mpool_fini(&local_page_pool);
552
553 /*
554 * Tidy up the page tables by reclaiming failed mappings (if there was
555 * an error) or merging entries into blocks where possible (on success).
556 */
557 mm_vm_defrag(&to->ptable, api_page_pool);
558 mm_vm_defrag(&from->ptable, api_page_pool);
559
560 return ret;
561}
562
563/**
564 * Check if the message length and the number of memory region constituents
565 * match, if the check is correct call the memory sharing routine.
566 */
567static struct spci_value spci_validate_call_share_memory(
568 struct vm_locked to_locked, struct vm_locked from_locked,
569 struct spci_memory_region *memory_region, uint32_t memory_share_size,
Andrew Walbrane7ad3c02019-12-24 17:03:04 +0000570 uint32_t share_func, struct mpool *api_page_pool)
Jose Marinho09b1db82019-08-08 09:16:59 +0100571{
572 uint32_t memory_to_attributes;
573 uint32_t attributes_size;
574 uint32_t constituents_size;
Andrew Walbranf4b51af2020-02-03 14:44:54 +0000575 struct spci_memory_region_constituent *constituents;
576 uint32_t constituent_count = memory_region->constituent_count;
Jose Marinho09b1db82019-08-08 09:16:59 +0100577
578 /*
579 * Ensure the number of constituents are within the memory
580 * bounds.
581 */
582 attributes_size = sizeof(struct spci_memory_region_attributes) *
583 memory_region->attribute_count;
584 constituents_size = sizeof(struct spci_memory_region_constituent) *
Andrew Walbranf4b51af2020-02-03 14:44:54 +0000585 constituent_count;
Jose Marinho09b1db82019-08-08 09:16:59 +0100586 if (memory_region->constituent_offset <
587 sizeof(struct spci_memory_region) + attributes_size ||
588 memory_share_size !=
589 memory_region->constituent_offset + constituents_size) {
590 return spci_error(SPCI_INVALID_PARAMETERS);
591 }
592
Andrew Walbrane28f4a22019-12-24 15:45:36 +0000593 /* The sender must match the message sender. */
594 if (memory_region->sender != from_locked.vm->id) {
595 return spci_error(SPCI_INVALID_PARAMETERS);
596 }
597
Jose Marinho09b1db82019-08-08 09:16:59 +0100598 /* We only support a single recipient. */
599 if (memory_region->attribute_count != 1) {
Andrew Walbrane908c4a2019-12-02 17:13:47 +0000600 return spci_error(SPCI_NOT_SUPPORTED);
Jose Marinho09b1db82019-08-08 09:16:59 +0100601 }
602
Andrew Walbrancfe61642019-12-02 15:34:06 +0000603 /* The recipient must match the message recipient. */
604 if (memory_region->attributes[0].receiver != to_locked.vm->id) {
605 return spci_error(SPCI_INVALID_PARAMETERS);
606 }
607
Andrew Walbrane7ad3c02019-12-24 17:03:04 +0000608 switch (share_func) {
609 case SPCI_MEM_DONATE_32:
610 case SPCI_MEM_LEND_32:
611 case SPCI_MEM_SHARE_32:
Jose Marinho09b1db82019-08-08 09:16:59 +0100612 memory_to_attributes = spci_memory_attrs_to_mode(
613 memory_region->attributes[0].memory_attributes);
614 break;
Andrew Walbrane7ad3c02019-12-24 17:03:04 +0000615 case HF_SPCI_MEM_RELINQUISH:
Jose Marinho09b1db82019-08-08 09:16:59 +0100616 memory_to_attributes = MM_MODE_R | MM_MODE_W | MM_MODE_X;
617 break;
618 default:
Andrew Walbran17eebf92020-02-05 16:35:49 +0000619 dlog_error("Invalid memory sharing message.\n");
Jose Marinho09b1db82019-08-08 09:16:59 +0100620 return spci_error(SPCI_INVALID_PARAMETERS);
621 }
622
Andrew Walbranf4b51af2020-02-03 14:44:54 +0000623 constituents = spci_memory_region_get_constituents(memory_region);
624 return spci_share_memory(
625 to_locked, from_locked, constituents, constituent_count,
626 memory_to_attributes, share_func, api_page_pool,
627 memory_region->flags & SPCI_MEMORY_REGION_FLAG_CLEAR);
Jose Marinho09b1db82019-08-08 09:16:59 +0100628}
629
630/**
631 * Performs initial architected message information parsing. Calls the
632 * corresponding api functions implementing the functionality requested
633 * in the architected message.
634 */
635struct spci_value spci_msg_handle_architected_message(
636 struct vm_locked to_locked, struct vm_locked from_locked,
Andrew Walbran85aabe92019-12-03 12:03:03 +0000637 struct spci_memory_region *memory_region, uint32_t size,
Andrew Walbrane7ad3c02019-12-24 17:03:04 +0000638 uint32_t share_func, struct mpool *api_page_pool)
Jose Marinho09b1db82019-08-08 09:16:59 +0100639{
Andrew Walbran85aabe92019-12-03 12:03:03 +0000640 struct spci_value ret = spci_validate_call_share_memory(
Andrew Walbrane7ad3c02019-12-24 17:03:04 +0000641 to_locked, from_locked, memory_region, size, share_func,
Andrew Walbran85aabe92019-12-03 12:03:03 +0000642 api_page_pool);
Jose Marinho09b1db82019-08-08 09:16:59 +0100643
644 /* Copy data to the destination Rx. */
645 /*
646 * TODO: Translate the <from> IPA addresses to <to> IPA addresses.
647 * Currently we assume identity mapping of the stage 2 translation.
648 * Removing this assumption relies on a mechanism to handle scenarios
649 * where the memory region fits in the source Tx buffer but cannot fit
650 * in the destination Rx buffer. This mechanism will be defined at the
651 * spec level.
652 */
653 if (ret.func == SPCI_SUCCESS_32) {
654 memcpy_s(to_locked.vm->mailbox.recv, SPCI_MSG_PAYLOAD_MAX,
Andrew Walbran85aabe92019-12-03 12:03:03 +0000655 memory_region, size);
Jose Marinho09b1db82019-08-08 09:16:59 +0100656 to_locked.vm->mailbox.recv_size = size;
657 to_locked.vm->mailbox.recv_sender = from_locked.vm->id;
Andrew Walbrane7ad3c02019-12-24 17:03:04 +0000658 to_locked.vm->mailbox.recv_func = share_func;
Jose Marinho09b1db82019-08-08 09:16:59 +0100659 }
660
661 return ret;
662}