blob: 9a32d7536610e9c95c821c6fc436d2e91cedd8be [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"
18#include "hf/dlog.h"
19#include "hf/spci_internal.h"
20#include "hf/std.h"
21
22/**
23 * Check if the message length and the number of memory region constituents
24 * match, if the check is correct call the memory sharing routine.
25 */
Andrew Walbran70bc8622019-10-07 14:15:58 +010026static struct spci_value spci_validate_call_share_memory(
Jose Marinho75509b42019-04-09 09:34:59 +010027 struct vm_locked to_locked, struct vm_locked from_locked,
28 struct spci_memory_region *memory_region, uint32_t memory_share_size,
Andrew Walbranf5972182019-10-15 15:41:26 +010029 enum spci_memory_share share)
Jose Marinho75509b42019-04-09 09:34:59 +010030{
Andrew Walbranf5972182019-10-15 15:41:26 +010031 uint32_t memory_to_attributes;
32 uint32_t attributes_size;
33 uint32_t constituents_size;
Jose Marinho75509b42019-04-09 09:34:59 +010034
35 /*
36 * Ensure the number of constituents are within the memory
37 * bounds.
38 */
Andrew Walbranf5972182019-10-15 15:41:26 +010039 attributes_size = sizeof(struct spci_memory_region_attributes) *
40 memory_region->attribute_count;
41 constituents_size = sizeof(struct spci_memory_region_constituent) *
42 memory_region->constituent_count;
43 if (memory_region->constituent_offset <
44 sizeof(struct spci_memory_region) + attributes_size ||
45 memory_share_size !=
46 memory_region->constituent_offset + constituents_size) {
47 return spci_error(SPCI_INVALID_PARAMETERS);
48 }
49
50 /* We only support a single recipient. */
51 if (memory_region->attribute_count != 1) {
52 return spci_error(SPCI_INVALID_PARAMETERS);
53 }
54
55 switch (share) {
56 case SPCI_MEMORY_DONATE:
57 case SPCI_MEMORY_LEND:
Andrew Walbran648fc3e2019-10-22 16:23:05 +010058 case SPCI_MEMORY_SHARE:
Andrew Walbranf5972182019-10-15 15:41:26 +010059 memory_to_attributes = spci_memory_attrs_to_mode(
60 memory_region->attributes[0].memory_attributes);
61 break;
62 case SPCI_MEMORY_RELINQUISH:
63 memory_to_attributes = MM_MODE_R | MM_MODE_W | MM_MODE_X;
64 break;
65 default:
66 dlog("Invalid memory sharing message.\n");
Andrew Walbran70bc8622019-10-07 14:15:58 +010067 return spci_error(SPCI_INVALID_PARAMETERS);
Jose Marinho75509b42019-04-09 09:34:59 +010068 }
69
70 return api_spci_share_memory(to_locked, from_locked, memory_region,
71 memory_to_attributes, share);
72}
73
74/**
75 * Performs initial architected message information parsing. Calls the
76 * corresponding api functions implementing the functionality requested
77 * in the architected message.
78 */
Andrew Walbran70bc8622019-10-07 14:15:58 +010079struct spci_value spci_msg_handle_architected_message(
Jose Marinho75509b42019-04-09 09:34:59 +010080 struct vm_locked to_locked, struct vm_locked from_locked,
81 const struct spci_architected_message_header
82 *architected_message_replica,
Andrew Walbran70bc8622019-10-07 14:15:58 +010083 uint32_t size)
Jose Marinho75509b42019-04-09 09:34:59 +010084{
Andrew Walbran70bc8622019-10-07 14:15:58 +010085 struct spci_value ret;
Andrew Walbranf5972182019-10-15 15:41:26 +010086 struct spci_memory_region *memory_region =
87 (struct spci_memory_region *)
88 architected_message_replica->payload;
89 uint32_t message_type = architected_message_replica->type;
90 uint32_t memory_share_size =
91 size - sizeof(struct spci_architected_message_header);
Jose Marinho75509b42019-04-09 09:34:59 +010092
Andrew Walbranf5972182019-10-15 15:41:26 +010093 ret = spci_validate_call_share_memory(to_locked, from_locked,
94 memory_region, memory_share_size,
95 message_type);
Jose Marinho75509b42019-04-09 09:34:59 +010096
97 /* Copy data to the destination Rx. */
98 /*
99 * TODO: Translate the <from> IPA addresses to <to> IPA addresses.
100 * Currently we assume identity mapping of the stage 2 translation.
101 * Removing this assumption relies on a mechanism to handle scenarios
102 * where the memory region fits in the source Tx buffer but cannot fit
103 * in the destination Rx buffer. This mechanism will be defined at the
104 * spec level.
105 */
Andrew Walbran70bc8622019-10-07 14:15:58 +0100106 if (ret.func == SPCI_SUCCESS_32) {
107 memcpy_s(to_locked.vm->mailbox.recv, SPCI_MSG_PAYLOAD_MAX,
108 architected_message_replica, size);
109 to_locked.vm->mailbox.recv_size = size;
110 to_locked.vm->mailbox.recv_sender = from_locked.vm->id;
111 to_locked.vm->mailbox.recv_attributes =
112 SPCI_MSG_SEND_LEGACY_MEMORY;
Jose Marinho75509b42019-04-09 09:34:59 +0100113 }
Jose Marinho75509b42019-04-09 09:34:59 +0100114
115 return ret;
116}
117
118/**
119 * Obtain the next mode to apply to the two VMs.
120 *
Jose Marinho7fbbf2e2019-08-05 13:19:58 +0100121 * Returns true iff a state transition was found.
Jose Marinho75509b42019-04-09 09:34:59 +0100122 */
123static bool spci_msg_get_next_state(
124 const struct spci_mem_transitions *transitions,
125 uint32_t transition_count, uint32_t memory_to_attributes,
Andrew Walbran1281ed42019-10-22 17:23:40 +0100126 uint32_t orig_from_mode, uint32_t orig_to_mode, uint32_t *from_mode,
127 uint32_t *to_mode)
Jose Marinho75509b42019-04-09 09:34:59 +0100128{
129 const uint32_t state_mask =
130 MM_MODE_INVALID | MM_MODE_UNOWNED | MM_MODE_SHARED;
131 const uint32_t orig_from_state = orig_from_mode & state_mask;
132
133 for (uint32_t index = 0; index < transition_count; index++) {
134 uint32_t table_orig_from_mode =
135 transitions[index].orig_from_mode;
136 uint32_t table_orig_to_mode = transitions[index].orig_to_mode;
137
138 if (((orig_from_state) == table_orig_from_mode) &&
139 ((orig_to_mode & state_mask) == table_orig_to_mode)) {
140 *to_mode = transitions[index].to_mode |
141 memory_to_attributes;
Jose Marinho713f13a2019-05-21 11:54:16 +0100142
143 *from_mode = transitions[index].from_mode |
144 (~state_mask & orig_from_mode);
Jose Marinho75509b42019-04-09 09:34:59 +0100145
146 return true;
147 }
148 }
149 return false;
150}
151
152/**
153 * Verify that all pages have the same mode, that the starting mode
154 * constitutes a valid state and obtain the next mode to apply
155 * to the two VMs.
156 *
157 * Returns:
158 * The error code false indicates that:
159 * 1) a state transition was not found;
160 * 2) the pages being shared do not have the same mode within the <to>
161 * or <form> VMs;
162 * 3) The beginning and end IPAs are not page aligned;
163 * 4) The requested share type was not handled.
164 * Success is indicated by true.
165 *
166 */
167bool spci_msg_check_transition(struct vm *to, struct vm *from,
168 enum spci_memory_share share,
Jose Marinho7fbbf2e2019-08-05 13:19:58 +0100169 uint32_t *orig_from_mode,
170 struct spci_memory_region *memory_region,
171 uint32_t memory_to_attributes,
Andrew Walbran1281ed42019-10-22 17:23:40 +0100172 uint32_t *from_mode, uint32_t *to_mode)
Jose Marinho75509b42019-04-09 09:34:59 +0100173{
Andrew Walbran1281ed42019-10-22 17:23:40 +0100174 uint32_t orig_to_mode;
Jose Marinho75509b42019-04-09 09:34:59 +0100175 const struct spci_mem_transitions *mem_transition_table;
176 uint32_t transition_table_size;
Jose Marinho7fbbf2e2019-08-05 13:19:58 +0100177 uint32_t i;
Jose Marinho75509b42019-04-09 09:34:59 +0100178
179 /*
180 * TODO: Transition table does not currently consider the multiple
181 * shared case.
182 */
183 static const struct spci_mem_transitions donate_transitions[] = {
184 {
185 /* 1) {O-EA, !O-NA} -> {!O-NA, O-EA} */
186 .orig_from_mode = 0,
187 .orig_to_mode = MM_MODE_INVALID | MM_MODE_UNOWNED,
188 .from_mode = MM_MODE_INVALID | MM_MODE_UNOWNED,
189 .to_mode = 0,
190 },
191 {
192 /* 2) {O-NA, !O-EA} -> {!O-NA, O-EA} */
193 .orig_from_mode = MM_MODE_INVALID,
194 .orig_to_mode = MM_MODE_UNOWNED,
195 .from_mode = MM_MODE_INVALID | MM_MODE_UNOWNED,
196 .to_mode = 0,
197 },
198 {
199 /* 3) {O-SA, !O-SA} -> {!O-NA, O-EA} */
200 .orig_from_mode = MM_MODE_SHARED,
201 .orig_to_mode = MM_MODE_UNOWNED | MM_MODE_SHARED,
202 .from_mode = MM_MODE_INVALID | MM_MODE_UNOWNED,
203 .to_mode = 0,
204 },
205 {
206 /*
207 * Duplicate of 1) in order to cater for an alternative
208 * representation of !O-NA:
209 * (INVALID | UNOWNED | SHARED) and (INVALID | UNOWNED)
210 * are both alternate representations of !O-NA.
211 */
212 /* 4) {O-EA, !O-NA} -> {!O-NA, O-EA} */
213 .orig_from_mode = 0,
214 .orig_to_mode = MM_MODE_INVALID | MM_MODE_UNOWNED |
215 MM_MODE_SHARED,
216 .from_mode = MM_MODE_INVALID | MM_MODE_UNOWNED |
217 MM_MODE_SHARED,
218 .to_mode = 0,
219 },
220 };
Jose Marinho56c25732019-05-20 09:48:53 +0100221
Jose Marinho713f13a2019-05-21 11:54:16 +0100222 static const uint32_t size_donate_transitions =
223 ARRAY_SIZE(donate_transitions);
224
Andrew Walbran648fc3e2019-10-22 16:23:05 +0100225 /*
226 * This data structure holds the allowed state transitions for the
227 * "lend" state machine. In this state machine the owner keeps ownership
228 * but loses access to the lent pages.
229 */
230 static const struct spci_mem_transitions lend_transitions[] = {
Jose Marinho56c25732019-05-20 09:48:53 +0100231 {
Andrew Walbran648fc3e2019-10-22 16:23:05 +0100232 /* 1) {O-EA, !O-NA} -> {O-NA, !O-EA} */
233 .orig_from_mode = 0,
234 .orig_to_mode = MM_MODE_INVALID | MM_MODE_UNOWNED |
235 MM_MODE_SHARED,
236 .from_mode = MM_MODE_INVALID,
237 .to_mode = MM_MODE_UNOWNED,
Jose Marinho56c25732019-05-20 09:48:53 +0100238 },
239 {
Andrew Walbran648fc3e2019-10-22 16:23:05 +0100240 /*
241 * Duplicate of 1) in order to cater for an alternative
242 * representation of !O-NA:
243 * (INVALID | UNOWNED | SHARED) and (INVALID | UNOWNED)
244 * are both alternate representations of !O-NA.
245 */
246 /* 2) {O-EA, !O-NA} -> {O-NA, !O-EA} */
247 .orig_from_mode = 0,
248 .orig_to_mode = MM_MODE_INVALID | MM_MODE_UNOWNED,
249 .from_mode = MM_MODE_INVALID,
250 .to_mode = MM_MODE_UNOWNED,
Jose Marinho56c25732019-05-20 09:48:53 +0100251 },
252 };
253
Andrew Walbran648fc3e2019-10-22 16:23:05 +0100254 static const uint32_t size_lend_transitions =
255 ARRAY_SIZE(lend_transitions);
Jose Marinho56c25732019-05-20 09:48:53 +0100256
Jose Marinho713f13a2019-05-21 11:54:16 +0100257 /*
Andrew Walbran648fc3e2019-10-22 16:23:05 +0100258 * This data structure holds the allowed state transitions for the
259 * "share" state machine. In this state machine the owner keeps the
260 * shared pages mapped on its stage2 table and keeps access as well.
Jose Marinho713f13a2019-05-21 11:54:16 +0100261 */
Andrew Walbran648fc3e2019-10-22 16:23:05 +0100262 static const struct spci_mem_transitions share_transitions[] = {
Jose Marinho713f13a2019-05-21 11:54:16 +0100263 {
264 /* 1) {O-EA, !O-NA} -> {O-SA, !O-SA} */
265 .orig_from_mode = 0,
266 .orig_to_mode = MM_MODE_INVALID | MM_MODE_UNOWNED |
267 MM_MODE_SHARED,
268 .from_mode = MM_MODE_SHARED,
269 .to_mode = MM_MODE_UNOWNED | MM_MODE_SHARED,
270 },
271 {
272 /*
273 * Duplicate of 1) in order to cater for an alternative
274 * representation of !O-NA:
275 * (INVALID | UNOWNED | SHARED) and (INVALID | UNOWNED)
276 * are both alternate representations of !O-NA.
277 */
278 /* 2) {O-EA, !O-NA} -> {O-SA, !O-SA} */
279 .orig_from_mode = 0,
280 .orig_to_mode = MM_MODE_INVALID | MM_MODE_UNOWNED,
281 .from_mode = MM_MODE_SHARED,
282 .to_mode = MM_MODE_UNOWNED | MM_MODE_SHARED,
283 },
284 };
285
Andrew Walbran648fc3e2019-10-22 16:23:05 +0100286 static const uint32_t size_share_transitions =
287 ARRAY_SIZE(share_transitions);
288
289 static const struct spci_mem_transitions relinquish_transitions[] = {
290 {
291 /* 1) {!O-EA, O-NA} -> {!O-NA, O-EA} */
292 .orig_from_mode = MM_MODE_UNOWNED,
293 .orig_to_mode = MM_MODE_INVALID,
294 .from_mode = MM_MODE_INVALID | MM_MODE_UNOWNED |
295 MM_MODE_SHARED,
296 .to_mode = 0,
297 },
298 {
299 /* 2) {!O-SA, O-SA} -> {!O-NA, O-EA} */
300 .orig_from_mode = MM_MODE_UNOWNED | MM_MODE_SHARED,
301 .orig_to_mode = MM_MODE_SHARED,
302 .from_mode = MM_MODE_INVALID | MM_MODE_UNOWNED |
303 MM_MODE_SHARED,
304 .to_mode = 0,
305 },
306 };
307
308 static const uint32_t size_relinquish_transitions =
309 ARRAY_SIZE(relinquish_transitions);
Jose Marinho75509b42019-04-09 09:34:59 +0100310
Jose Marinho7fbbf2e2019-08-05 13:19:58 +0100311 struct spci_memory_region_constituent *constituents =
312 spci_memory_region_get_constituents(memory_region);
313
314 if (memory_region->constituent_count == 0) {
315 /*
316 * Fail if there are no constituents. Otherwise
317 * spci_msg_get_next_state would get an unitialised
318 * *orig_from_mode and orig_to_mode.
319 */
Jose Marinho75509b42019-04-09 09:34:59 +0100320 return false;
321 }
322
Jose Marinho7fbbf2e2019-08-05 13:19:58 +0100323 for (i = 0; i < memory_region->constituent_count; ++i) {
324 ipaddr_t begin = ipa_init(constituents[i].address);
325 size_t size = constituents[i].page_count * PAGE_SIZE;
326 ipaddr_t end = ipa_add(begin, size);
327 uint32_t current_from_mode;
328 uint32_t current_to_mode;
329
330 /* Fail if addresses are not page-aligned. */
331 if (!is_aligned(ipa_addr(begin), PAGE_SIZE) ||
332 !is_aligned(ipa_addr(end), PAGE_SIZE)) {
333 return false;
334 }
335
336 /*
337 * Ensure that this constituent memory range is all mapped with
338 * the same mode.
339 */
340 if (!mm_vm_get_mode(&from->ptable, begin, end,
341 &current_from_mode) ||
342 !mm_vm_get_mode(&to->ptable, begin, end,
343 &current_to_mode)) {
344 return false;
345 }
346
347 /*
348 * Ensure that all constituents are mapped with the same mode.
349 */
350 if (i == 0) {
351 *orig_from_mode = current_from_mode;
352 orig_to_mode = current_to_mode;
353 } else if (current_from_mode != *orig_from_mode ||
354 current_to_mode != orig_to_mode) {
355 return false;
356 }
Jose Marinho75509b42019-04-09 09:34:59 +0100357 }
358
Andrew Scullb5f49e02019-10-02 13:20:47 +0100359 /* Ensure the address range is normal memory and not a device. */
360 if (*orig_from_mode & MM_MODE_D) {
361 return false;
362 }
363
Jose Marinho75509b42019-04-09 09:34:59 +0100364 switch (share) {
365 case SPCI_MEMORY_DONATE:
366 mem_transition_table = donate_transitions;
367 transition_table_size = size_donate_transitions;
368 break;
369
Andrew Walbran648fc3e2019-10-22 16:23:05 +0100370 case SPCI_MEMORY_LEND:
371 mem_transition_table = lend_transitions;
372 transition_table_size = size_lend_transitions;
373 break;
374
375 case SPCI_MEMORY_SHARE:
376 mem_transition_table = share_transitions;
377 transition_table_size = size_share_transitions;
378 break;
379
Jose Marinho56c25732019-05-20 09:48:53 +0100380 case SPCI_MEMORY_RELINQUISH:
381 mem_transition_table = relinquish_transitions;
382 transition_table_size = size_relinquish_transitions;
383 break;
384
Jose Marinho75509b42019-04-09 09:34:59 +0100385 default:
386 return false;
387 }
388
389 return spci_msg_get_next_state(mem_transition_table,
390 transition_table_size,
391 memory_to_attributes, *orig_from_mode,
392 orig_to_mode, from_mode, to_mode);
393}