blob: 7c9d2b73d3728c4025613e315f735f0b29f31469 [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"
22
23/**
Jose Marinho75509b42019-04-09 09:34:59 +010024 * Obtain the next mode to apply to the two VMs.
25 *
Jose Marinho7fbbf2e2019-08-05 13:19:58 +010026 * Returns true iff a state transition was found.
Jose Marinho75509b42019-04-09 09:34:59 +010027 */
28static bool spci_msg_get_next_state(
29 const struct spci_mem_transitions *transitions,
30 uint32_t transition_count, uint32_t memory_to_attributes,
Andrew Walbran1281ed42019-10-22 17:23:40 +010031 uint32_t orig_from_mode, uint32_t orig_to_mode, uint32_t *from_mode,
32 uint32_t *to_mode)
Jose Marinho75509b42019-04-09 09:34:59 +010033{
34 const uint32_t state_mask =
35 MM_MODE_INVALID | MM_MODE_UNOWNED | MM_MODE_SHARED;
36 const uint32_t orig_from_state = orig_from_mode & state_mask;
37
38 for (uint32_t index = 0; index < transition_count; index++) {
39 uint32_t table_orig_from_mode =
40 transitions[index].orig_from_mode;
41 uint32_t table_orig_to_mode = transitions[index].orig_to_mode;
42
43 if (((orig_from_state) == table_orig_from_mode) &&
44 ((orig_to_mode & state_mask) == table_orig_to_mode)) {
45 *to_mode = transitions[index].to_mode |
46 memory_to_attributes;
Jose Marinho713f13a2019-05-21 11:54:16 +010047
48 *from_mode = transitions[index].from_mode |
49 (~state_mask & orig_from_mode);
Jose Marinho75509b42019-04-09 09:34:59 +010050
51 return true;
52 }
53 }
54 return false;
55}
56
57/**
58 * Verify that all pages have the same mode, that the starting mode
59 * constitutes a valid state and obtain the next mode to apply
60 * to the two VMs.
61 *
62 * Returns:
63 * The error code false indicates that:
64 * 1) a state transition was not found;
65 * 2) the pages being shared do not have the same mode within the <to>
66 * or <form> VMs;
67 * 3) The beginning and end IPAs are not page aligned;
68 * 4) The requested share type was not handled.
69 * Success is indicated by true.
70 *
71 */
Jose Marinho09b1db82019-08-08 09:16:59 +010072static bool spci_msg_check_transition(struct vm *to, struct vm *from,
73 enum spci_memory_share share,
74 uint32_t *orig_from_mode,
75 struct spci_memory_region *memory_region,
76 uint32_t memory_to_attributes,
77 uint32_t *from_mode, uint32_t *to_mode)
Jose Marinho75509b42019-04-09 09:34:59 +010078{
Andrew Walbran1281ed42019-10-22 17:23:40 +010079 uint32_t orig_to_mode;
Jose Marinho75509b42019-04-09 09:34:59 +010080 const struct spci_mem_transitions *mem_transition_table;
81 uint32_t transition_table_size;
Jose Marinho7fbbf2e2019-08-05 13:19:58 +010082 uint32_t i;
Jose Marinho75509b42019-04-09 09:34:59 +010083
84 /*
85 * TODO: Transition table does not currently consider the multiple
86 * shared case.
87 */
88 static const struct spci_mem_transitions donate_transitions[] = {
89 {
90 /* 1) {O-EA, !O-NA} -> {!O-NA, O-EA} */
91 .orig_from_mode = 0,
92 .orig_to_mode = MM_MODE_INVALID | MM_MODE_UNOWNED,
93 .from_mode = MM_MODE_INVALID | MM_MODE_UNOWNED,
94 .to_mode = 0,
95 },
96 {
97 /* 2) {O-NA, !O-EA} -> {!O-NA, O-EA} */
98 .orig_from_mode = MM_MODE_INVALID,
99 .orig_to_mode = MM_MODE_UNOWNED,
100 .from_mode = MM_MODE_INVALID | MM_MODE_UNOWNED,
101 .to_mode = 0,
102 },
103 {
104 /* 3) {O-SA, !O-SA} -> {!O-NA, O-EA} */
105 .orig_from_mode = MM_MODE_SHARED,
106 .orig_to_mode = MM_MODE_UNOWNED | MM_MODE_SHARED,
107 .from_mode = MM_MODE_INVALID | MM_MODE_UNOWNED,
108 .to_mode = 0,
109 },
110 {
111 /*
112 * Duplicate of 1) in order to cater for an alternative
113 * representation of !O-NA:
114 * (INVALID | UNOWNED | SHARED) and (INVALID | UNOWNED)
115 * are both alternate representations of !O-NA.
116 */
117 /* 4) {O-EA, !O-NA} -> {!O-NA, O-EA} */
118 .orig_from_mode = 0,
119 .orig_to_mode = MM_MODE_INVALID | MM_MODE_UNOWNED |
120 MM_MODE_SHARED,
121 .from_mode = MM_MODE_INVALID | MM_MODE_UNOWNED |
122 MM_MODE_SHARED,
123 .to_mode = 0,
124 },
125 };
Jose Marinho56c25732019-05-20 09:48:53 +0100126
Jose Marinho713f13a2019-05-21 11:54:16 +0100127 static const uint32_t size_donate_transitions =
128 ARRAY_SIZE(donate_transitions);
129
Andrew Walbran648fc3e2019-10-22 16:23:05 +0100130 /*
131 * This data structure holds the allowed state transitions for the
132 * "lend" state machine. In this state machine the owner keeps ownership
133 * but loses access to the lent pages.
134 */
135 static const struct spci_mem_transitions lend_transitions[] = {
Jose Marinho56c25732019-05-20 09:48:53 +0100136 {
Andrew Walbran648fc3e2019-10-22 16:23:05 +0100137 /* 1) {O-EA, !O-NA} -> {O-NA, !O-EA} */
138 .orig_from_mode = 0,
139 .orig_to_mode = MM_MODE_INVALID | MM_MODE_UNOWNED |
140 MM_MODE_SHARED,
141 .from_mode = MM_MODE_INVALID,
142 .to_mode = MM_MODE_UNOWNED,
Jose Marinho56c25732019-05-20 09:48:53 +0100143 },
144 {
Andrew Walbran648fc3e2019-10-22 16:23:05 +0100145 /*
146 * Duplicate of 1) in order to cater for an alternative
147 * representation of !O-NA:
148 * (INVALID | UNOWNED | SHARED) and (INVALID | UNOWNED)
149 * are both alternate representations of !O-NA.
150 */
151 /* 2) {O-EA, !O-NA} -> {O-NA, !O-EA} */
152 .orig_from_mode = 0,
153 .orig_to_mode = MM_MODE_INVALID | MM_MODE_UNOWNED,
154 .from_mode = MM_MODE_INVALID,
155 .to_mode = MM_MODE_UNOWNED,
Jose Marinho56c25732019-05-20 09:48:53 +0100156 },
157 };
158
Andrew Walbran648fc3e2019-10-22 16:23:05 +0100159 static const uint32_t size_lend_transitions =
160 ARRAY_SIZE(lend_transitions);
Jose Marinho56c25732019-05-20 09:48:53 +0100161
Jose Marinho713f13a2019-05-21 11:54:16 +0100162 /*
Andrew Walbran648fc3e2019-10-22 16:23:05 +0100163 * This data structure holds the allowed state transitions for the
164 * "share" state machine. In this state machine the owner keeps the
165 * shared pages mapped on its stage2 table and keeps access as well.
Jose Marinho713f13a2019-05-21 11:54:16 +0100166 */
Andrew Walbran648fc3e2019-10-22 16:23:05 +0100167 static const struct spci_mem_transitions share_transitions[] = {
Jose Marinho713f13a2019-05-21 11:54:16 +0100168 {
169 /* 1) {O-EA, !O-NA} -> {O-SA, !O-SA} */
170 .orig_from_mode = 0,
171 .orig_to_mode = MM_MODE_INVALID | MM_MODE_UNOWNED |
172 MM_MODE_SHARED,
173 .from_mode = MM_MODE_SHARED,
174 .to_mode = MM_MODE_UNOWNED | MM_MODE_SHARED,
175 },
176 {
177 /*
178 * Duplicate of 1) in order to cater for an alternative
179 * representation of !O-NA:
180 * (INVALID | UNOWNED | SHARED) and (INVALID | UNOWNED)
181 * are both alternate representations of !O-NA.
182 */
183 /* 2) {O-EA, !O-NA} -> {O-SA, !O-SA} */
184 .orig_from_mode = 0,
185 .orig_to_mode = MM_MODE_INVALID | MM_MODE_UNOWNED,
186 .from_mode = MM_MODE_SHARED,
187 .to_mode = MM_MODE_UNOWNED | MM_MODE_SHARED,
188 },
189 };
190
Andrew Walbran648fc3e2019-10-22 16:23:05 +0100191 static const uint32_t size_share_transitions =
192 ARRAY_SIZE(share_transitions);
193
194 static const struct spci_mem_transitions relinquish_transitions[] = {
195 {
196 /* 1) {!O-EA, O-NA} -> {!O-NA, O-EA} */
197 .orig_from_mode = MM_MODE_UNOWNED,
198 .orig_to_mode = MM_MODE_INVALID,
199 .from_mode = MM_MODE_INVALID | MM_MODE_UNOWNED |
200 MM_MODE_SHARED,
201 .to_mode = 0,
202 },
203 {
204 /* 2) {!O-SA, O-SA} -> {!O-NA, O-EA} */
205 .orig_from_mode = MM_MODE_UNOWNED | MM_MODE_SHARED,
206 .orig_to_mode = MM_MODE_SHARED,
207 .from_mode = MM_MODE_INVALID | MM_MODE_UNOWNED |
208 MM_MODE_SHARED,
209 .to_mode = 0,
210 },
211 };
212
213 static const uint32_t size_relinquish_transitions =
214 ARRAY_SIZE(relinquish_transitions);
Jose Marinho75509b42019-04-09 09:34:59 +0100215
Jose Marinho7fbbf2e2019-08-05 13:19:58 +0100216 struct spci_memory_region_constituent *constituents =
217 spci_memory_region_get_constituents(memory_region);
218
219 if (memory_region->constituent_count == 0) {
220 /*
221 * Fail if there are no constituents. Otherwise
222 * spci_msg_get_next_state would get an unitialised
223 * *orig_from_mode and orig_to_mode.
224 */
Jose Marinho75509b42019-04-09 09:34:59 +0100225 return false;
226 }
227
Jose Marinho7fbbf2e2019-08-05 13:19:58 +0100228 for (i = 0; i < memory_region->constituent_count; ++i) {
229 ipaddr_t begin = ipa_init(constituents[i].address);
230 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
Jose Marinho75509b42019-04-09 09:34:59 +0100269 switch (share) {
270 case SPCI_MEMORY_DONATE:
271 mem_transition_table = donate_transitions;
272 transition_table_size = size_donate_transitions;
273 break;
274
Andrew Walbran648fc3e2019-10-22 16:23:05 +0100275 case SPCI_MEMORY_LEND:
276 mem_transition_table = lend_transitions;
277 transition_table_size = size_lend_transitions;
278 break;
279
280 case SPCI_MEMORY_SHARE:
281 mem_transition_table = share_transitions;
282 transition_table_size = size_share_transitions;
283 break;
284
Jose Marinho56c25732019-05-20 09:48:53 +0100285 case SPCI_MEMORY_RELINQUISH:
286 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(
319 struct mm_ptable *t, struct spci_memory_region *memory_region, int mode,
320 struct mpool *ppool, bool commit)
321{
322 struct spci_memory_region_constituent *constituents =
323 spci_memory_region_get_constituents(memory_region);
324 uint32_t memory_constituent_count = memory_region->constituent_count;
325
326 /* Iterate over the memory region constituents. */
327 for (uint32_t index = 0; index < memory_constituent_count; index++) {
328 size_t size = constituents[index].page_count * PAGE_SIZE;
329 paddr_t pa_begin =
330 pa_from_ipa(ipa_init(constituents[index].address));
331 paddr_t pa_end = pa_add(pa_begin, size);
332
333 if (commit) {
334 mm_vm_identity_commit(t, pa_begin, pa_end, mode, ppool,
335 NULL);
336 } else if (!mm_vm_identity_prepare(t, pa_begin, pa_end, mode,
337 ppool)) {
338 return false;
339 }
340 }
341
342 return true;
343}
344
345/**
346 * Clears a region of physical memory by overwriting it with zeros. The data is
347 * flushed from the cache so the memory has been cleared across the system.
348 */
349static bool clear_memory(paddr_t begin, paddr_t end, struct mpool *ppool)
350{
351 /*
352 * TODO: change this to a cpu local single page window rather than a
353 * global mapping of the whole range. Such an approach will limit
354 * the changes to stage-1 tables and will allow only local
355 * invalidation.
356 */
357 bool ret;
358 struct mm_stage1_locked stage1_locked = mm_lock_stage1();
359 void *ptr =
360 mm_identity_map(stage1_locked, begin, end, MM_MODE_W, ppool);
361 size_t size = pa_difference(begin, end);
362
363 if (!ptr) {
364 /* TODO: partial defrag of failed range. */
365 /* Recover any memory consumed in failed mapping. */
366 mm_defrag(stage1_locked, ppool);
367 goto fail;
368 }
369
370 memset_s(ptr, size, 0, size);
371 arch_mm_flush_dcache(ptr, size);
372 mm_unmap(stage1_locked, begin, end, ppool);
373
374 ret = true;
375 goto out;
376
377fail:
378 ret = false;
379
380out:
381 mm_unlock_stage1(&stage1_locked);
382
383 return ret;
384}
385
386/**
387 * Clears a region of physical memory by overwriting it with zeros. The data is
388 * flushed from the cache so the memory has been cleared across the system.
389 */
390static bool spci_clear_memory_region(struct spci_memory_region *memory_region,
391 struct mpool *api_page_pool)
392{
393 struct mpool local_page_pool;
394 struct spci_memory_region_constituent *constituents =
395 spci_memory_region_get_constituents(memory_region);
396 uint32_t memory_constituent_count = memory_region->constituent_count;
397 struct mm_stage1_locked stage1_locked;
398 bool ret = false;
399
400 /*
401 * Create a local pool so any freed memory can't be used by another
402 * thread. This is to ensure each constituent that is mapped can be
403 * unmapped again afterwards.
404 */
405 mpool_init_with_fallback(&local_page_pool, api_page_pool);
406
407 /* Iterate over the memory region constituents. */
408 for (uint32_t i = 0; i < memory_constituent_count; ++i) {
409 size_t size = constituents[i].page_count * PAGE_SIZE;
410 paddr_t begin = pa_from_ipa(ipa_init(constituents[i].address));
411 paddr_t end = pa_add(begin, size);
412
413 if (!clear_memory(begin, end, &local_page_pool)) {
414 /*
415 * api_clear_memory will defrag on failure, so no need
416 * to do it here.
417 */
418 goto out;
419 }
420 }
421
422 /*
423 * Need to defrag after clearing, as it may have added extra mappings to
424 * the stage 1 page table.
425 */
426 stage1_locked = mm_lock_stage1();
427 mm_defrag(stage1_locked, &local_page_pool);
428 mm_unlock_stage1(&stage1_locked);
429
430 ret = true;
431
432out:
433 mpool_fini(&local_page_pool);
434 return ret;
435}
436
437/**
438 * Shares memory from the calling VM with another. The memory can be shared in
439 * different modes.
440 *
441 * This function requires the calling context to hold the <to> and <from> locks.
442 *
443 * Returns:
444 * In case of error one of the following values is returned:
445 * 1) SPCI_INVALID_PARAMETERS - The endpoint provided parameters were
446 * erroneous;
447 * 2) SPCI_NO_MEMORY - Hafnium did not have sufficient memory to complete
448 * the request.
449 * Success is indicated by SPCI_SUCCESS.
450 */
451static struct spci_value spci_share_memory(
452 struct vm_locked to_locked, struct vm_locked from_locked,
453 struct spci_memory_region *memory_region, uint32_t memory_to_attributes,
454 enum spci_memory_share share, struct mpool *api_page_pool)
455{
456 struct vm *to = to_locked.vm;
457 struct vm *from = from_locked.vm;
458 uint32_t orig_from_mode;
459 uint32_t from_mode;
460 uint32_t to_mode;
461 struct mpool local_page_pool;
462 struct spci_value ret;
463 struct spci_memory_region_constituent *constituents =
464 spci_memory_region_get_constituents(memory_region);
465
466 /*
467 * Make sure constituents are properly aligned to a 64-bit boundary. If
468 * not we would get alignment faults trying to read (64-bit) page
469 * addresses.
470 */
471 if (!is_aligned(constituents, 8)) {
472 return spci_error(SPCI_INVALID_PARAMETERS);
473 }
474
475 /* Disallow reflexive shares as this suggests an error in the VM. */
476 if (to == from) {
477 return spci_error(SPCI_INVALID_PARAMETERS);
478 }
479
480 /*
481 * Check if the state transition is lawful for both VMs involved
482 * in the memory exchange, ensure that all constituents of a memory
483 * region being shared are at the same state.
484 */
485 if (!spci_msg_check_transition(to, from, share, &orig_from_mode,
486 memory_region, memory_to_attributes,
487 &from_mode, &to_mode)) {
488 return spci_error(SPCI_INVALID_PARAMETERS);
489 }
490
491 /*
492 * Create a local pool so any freed memory can't be used by another
493 * thread. This is to ensure the original mapping can be restored if the
494 * clear fails.
495 */
496 mpool_init_with_fallback(&local_page_pool, api_page_pool);
497
498 /*
499 * First reserve all required memory for the new page table entries in
500 * both sender and recipient page tables without committing, to make
501 * sure the entire operation will succeed without exhausting the page
502 * pool.
503 */
504 if (!spci_region_group_identity_map(&from->ptable, memory_region,
505 from_mode, api_page_pool, false) ||
506 !spci_region_group_identity_map(&to->ptable, memory_region, to_mode,
507 api_page_pool, false)) {
508 /* TODO: partial defrag of failed range. */
509 ret = spci_error(SPCI_NO_MEMORY);
510 goto out;
511 }
512
513 /*
514 * First update the mapping for the sender so there is no overlap with
515 * the recipient. This won't allocate because the transaction was
516 * already prepared above, but may free pages in the case that a whole
517 * block is being unmapped that was previously partially mapped.
518 */
519 CHECK(spci_region_group_identity_map(&from->ptable, memory_region,
520 from_mode, &local_page_pool,
521 true));
522
523 /* Clear the memory so no VM or device can see the previous contents. */
524 if ((memory_region->flags & SPCI_MEMORY_REGION_FLAG_CLEAR) &&
525 !spci_clear_memory_region(memory_region, api_page_pool)) {
526 /*
527 * On failure, roll back by returning memory to the sender. This
528 * may allocate pages which were previously freed into
529 * `local_page_pool` by the call above, but will never allocate
530 * more pages than that so can never fail.
531 */
532 CHECK(spci_region_group_identity_map(
533 &from->ptable, memory_region, orig_from_mode,
534 &local_page_pool, true));
535
536 ret = spci_error(SPCI_NO_MEMORY);
537 goto out;
538 }
539
540 /*
541 * Complete the transfer by mapping the memory into the recipient. This
542 * won't allocate because the transaction was already prepared above, so
543 * it doesn't need to use the `local_page_pool`.
544 */
545 CHECK(spci_region_group_identity_map(&to->ptable, memory_region,
546 to_mode, api_page_pool, true));
547
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,
570 enum spci_memory_share share, struct mpool *api_page_pool)
571{
572 uint32_t memory_to_attributes;
573 uint32_t attributes_size;
574 uint32_t constituents_size;
575
576 /*
577 * Ensure the number of constituents are within the memory
578 * bounds.
579 */
580 attributes_size = sizeof(struct spci_memory_region_attributes) *
581 memory_region->attribute_count;
582 constituents_size = sizeof(struct spci_memory_region_constituent) *
583 memory_region->constituent_count;
584 if (memory_region->constituent_offset <
585 sizeof(struct spci_memory_region) + attributes_size ||
586 memory_share_size !=
587 memory_region->constituent_offset + constituents_size) {
588 return spci_error(SPCI_INVALID_PARAMETERS);
589 }
590
591 /* We only support a single recipient. */
592 if (memory_region->attribute_count != 1) {
593 return spci_error(SPCI_INVALID_PARAMETERS);
594 }
595
Andrew Walbrancfe61642019-12-02 15:34:06 +0000596 /* The recipient must match the message recipient. */
597 if (memory_region->attributes[0].receiver != to_locked.vm->id) {
598 return spci_error(SPCI_INVALID_PARAMETERS);
599 }
600
Jose Marinho09b1db82019-08-08 09:16:59 +0100601 switch (share) {
602 case SPCI_MEMORY_DONATE:
603 case SPCI_MEMORY_LEND:
604 case SPCI_MEMORY_SHARE:
605 memory_to_attributes = spci_memory_attrs_to_mode(
606 memory_region->attributes[0].memory_attributes);
607 break;
608 case SPCI_MEMORY_RELINQUISH:
609 memory_to_attributes = MM_MODE_R | MM_MODE_W | MM_MODE_X;
610 break;
611 default:
612 dlog("Invalid memory sharing message.\n");
613 return spci_error(SPCI_INVALID_PARAMETERS);
614 }
615
616 return spci_share_memory(to_locked, from_locked, memory_region,
617 memory_to_attributes, share, api_page_pool);
618}
619
620/**
621 * Performs initial architected message information parsing. Calls the
622 * corresponding api functions implementing the functionality requested
623 * in the architected message.
624 */
625struct spci_value spci_msg_handle_architected_message(
626 struct vm_locked to_locked, struct vm_locked from_locked,
627 const struct spci_architected_message_header
628 *architected_message_replica,
629 uint32_t size, struct mpool *api_page_pool)
630{
631 struct spci_value ret;
632 struct spci_memory_region *memory_region =
633 (struct spci_memory_region *)
634 architected_message_replica->payload;
635 uint32_t message_type = architected_message_replica->type;
636 uint32_t memory_share_size =
637 size - sizeof(struct spci_architected_message_header);
638
639 ret = spci_validate_call_share_memory(to_locked, from_locked,
640 memory_region, memory_share_size,
641 message_type, api_page_pool);
642
643 /* Copy data to the destination Rx. */
644 /*
645 * TODO: Translate the <from> IPA addresses to <to> IPA addresses.
646 * Currently we assume identity mapping of the stage 2 translation.
647 * Removing this assumption relies on a mechanism to handle scenarios
648 * where the memory region fits in the source Tx buffer but cannot fit
649 * in the destination Rx buffer. This mechanism will be defined at the
650 * spec level.
651 */
652 if (ret.func == SPCI_SUCCESS_32) {
653 memcpy_s(to_locked.vm->mailbox.recv, SPCI_MSG_PAYLOAD_MAX,
654 architected_message_replica, size);
655 to_locked.vm->mailbox.recv_size = size;
656 to_locked.vm->mailbox.recv_sender = from_locked.vm->id;
657 to_locked.vm->mailbox.recv_attributes =
658 SPCI_MSG_SEND_LEGACY_MEMORY;
659 }
660
661 return ret;
662}