blob: 605a7a9cf53d0e905bd8e6400db53bb48c521c44 [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:
58 memory_to_attributes = spci_memory_attrs_to_mode(
59 memory_region->attributes[0].memory_attributes);
60 break;
61 case SPCI_MEMORY_RELINQUISH:
62 memory_to_attributes = MM_MODE_R | MM_MODE_W | MM_MODE_X;
63 break;
64 default:
65 dlog("Invalid memory sharing message.\n");
Andrew Walbran70bc8622019-10-07 14:15:58 +010066 return spci_error(SPCI_INVALID_PARAMETERS);
Jose Marinho75509b42019-04-09 09:34:59 +010067 }
68
69 return api_spci_share_memory(to_locked, from_locked, memory_region,
70 memory_to_attributes, share);
71}
72
73/**
74 * Performs initial architected message information parsing. Calls the
75 * corresponding api functions implementing the functionality requested
76 * in the architected message.
77 */
Andrew Walbran70bc8622019-10-07 14:15:58 +010078struct spci_value spci_msg_handle_architected_message(
Jose Marinho75509b42019-04-09 09:34:59 +010079 struct vm_locked to_locked, struct vm_locked from_locked,
80 const struct spci_architected_message_header
81 *architected_message_replica,
Andrew Walbran70bc8622019-10-07 14:15:58 +010082 uint32_t size)
Jose Marinho75509b42019-04-09 09:34:59 +010083{
Andrew Walbran70bc8622019-10-07 14:15:58 +010084 struct spci_value ret;
Andrew Walbranf5972182019-10-15 15:41:26 +010085 struct spci_memory_region *memory_region =
86 (struct spci_memory_region *)
87 architected_message_replica->payload;
88 uint32_t message_type = architected_message_replica->type;
89 uint32_t memory_share_size =
90 size - sizeof(struct spci_architected_message_header);
Jose Marinho75509b42019-04-09 09:34:59 +010091
Andrew Walbranf5972182019-10-15 15:41:26 +010092 ret = spci_validate_call_share_memory(to_locked, from_locked,
93 memory_region, memory_share_size,
94 message_type);
Jose Marinho75509b42019-04-09 09:34:59 +010095
96 /* Copy data to the destination Rx. */
97 /*
98 * TODO: Translate the <from> IPA addresses to <to> IPA addresses.
99 * Currently we assume identity mapping of the stage 2 translation.
100 * Removing this assumption relies on a mechanism to handle scenarios
101 * where the memory region fits in the source Tx buffer but cannot fit
102 * in the destination Rx buffer. This mechanism will be defined at the
103 * spec level.
104 */
Andrew Walbran70bc8622019-10-07 14:15:58 +0100105 if (ret.func == SPCI_SUCCESS_32) {
106 memcpy_s(to_locked.vm->mailbox.recv, SPCI_MSG_PAYLOAD_MAX,
107 architected_message_replica, size);
108 to_locked.vm->mailbox.recv_size = size;
109 to_locked.vm->mailbox.recv_sender = from_locked.vm->id;
110 to_locked.vm->mailbox.recv_attributes =
111 SPCI_MSG_SEND_LEGACY_MEMORY;
Jose Marinho75509b42019-04-09 09:34:59 +0100112 }
Jose Marinho75509b42019-04-09 09:34:59 +0100113
114 return ret;
115}
116
117/**
118 * Obtain the next mode to apply to the two VMs.
119 *
120 * Returns:
121 * The error code -1 indicates that a state transition was not found.
122 * Success is indicated by 0.
123 */
124static bool spci_msg_get_next_state(
125 const struct spci_mem_transitions *transitions,
126 uint32_t transition_count, uint32_t memory_to_attributes,
127 int orig_from_mode, int orig_to_mode, int *from_mode, int *to_mode)
128{
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,
169 int *orig_from_mode, ipaddr_t begin,
170 ipaddr_t end, uint32_t memory_to_attributes,
171 int *from_mode, int *to_mode)
172{
173 int orig_to_mode;
174 const struct spci_mem_transitions *mem_transition_table;
175 uint32_t transition_table_size;
176
177 /*
178 * TODO: Transition table does not currently consider the multiple
179 * shared case.
180 */
181 static const struct spci_mem_transitions donate_transitions[] = {
182 {
183 /* 1) {O-EA, !O-NA} -> {!O-NA, O-EA} */
184 .orig_from_mode = 0,
185 .orig_to_mode = MM_MODE_INVALID | MM_MODE_UNOWNED,
186 .from_mode = MM_MODE_INVALID | MM_MODE_UNOWNED,
187 .to_mode = 0,
188 },
189 {
190 /* 2) {O-NA, !O-EA} -> {!O-NA, O-EA} */
191 .orig_from_mode = MM_MODE_INVALID,
192 .orig_to_mode = MM_MODE_UNOWNED,
193 .from_mode = MM_MODE_INVALID | MM_MODE_UNOWNED,
194 .to_mode = 0,
195 },
196 {
197 /* 3) {O-SA, !O-SA} -> {!O-NA, O-EA} */
198 .orig_from_mode = MM_MODE_SHARED,
199 .orig_to_mode = MM_MODE_UNOWNED | MM_MODE_SHARED,
200 .from_mode = MM_MODE_INVALID | MM_MODE_UNOWNED,
201 .to_mode = 0,
202 },
203 {
204 /*
205 * Duplicate of 1) in order to cater for an alternative
206 * representation of !O-NA:
207 * (INVALID | UNOWNED | SHARED) and (INVALID | UNOWNED)
208 * are both alternate representations of !O-NA.
209 */
210 /* 4) {O-EA, !O-NA} -> {!O-NA, O-EA} */
211 .orig_from_mode = 0,
212 .orig_to_mode = MM_MODE_INVALID | MM_MODE_UNOWNED |
213 MM_MODE_SHARED,
214 .from_mode = MM_MODE_INVALID | MM_MODE_UNOWNED |
215 MM_MODE_SHARED,
216 .to_mode = 0,
217 },
218 };
Jose Marinho56c25732019-05-20 09:48:53 +0100219
Jose Marinho713f13a2019-05-21 11:54:16 +0100220 static const uint32_t size_donate_transitions =
221 ARRAY_SIZE(donate_transitions);
222
Jose Marinho56c25732019-05-20 09:48:53 +0100223 static const struct spci_mem_transitions relinquish_transitions[] = {
224 {
225 /* 1) {!O-EA, O-NA} -> {!O-NA, O-EA} */
226 .orig_from_mode = MM_MODE_UNOWNED,
227 .orig_to_mode = MM_MODE_INVALID,
228 .from_mode = MM_MODE_INVALID | MM_MODE_UNOWNED |
229 MM_MODE_SHARED,
230 .to_mode = 0,
231 },
232 {
233 /* 2) {!O-SA, O-SA} -> {!O-NA, O-EA} */
234 .orig_from_mode = MM_MODE_UNOWNED | MM_MODE_SHARED,
235 .orig_to_mode = MM_MODE_SHARED,
236 .from_mode = MM_MODE_INVALID | MM_MODE_UNOWNED |
237 MM_MODE_SHARED,
238 .to_mode = 0,
239 },
240 };
241
242 static const uint32_t size_relinquish_transitions =
Jose Marinho713f13a2019-05-21 11:54:16 +0100243 ARRAY_SIZE(relinquish_transitions);
Jose Marinho56c25732019-05-20 09:48:53 +0100244
Jose Marinho713f13a2019-05-21 11:54:16 +0100245 /*
246 * This data structure holds the allowed state transitions for the "lend
247 * with shared access" state machine. In this state machine the owner
248 * keeps the lent pages mapped on its stage2 table and keeps access as
249 * well.
250 */
251 static const struct spci_mem_transitions shared_lend_transitions[] = {
252 {
253 /* 1) {O-EA, !O-NA} -> {O-SA, !O-SA} */
254 .orig_from_mode = 0,
255 .orig_to_mode = MM_MODE_INVALID | MM_MODE_UNOWNED |
256 MM_MODE_SHARED,
257 .from_mode = MM_MODE_SHARED,
258 .to_mode = MM_MODE_UNOWNED | MM_MODE_SHARED,
259 },
260 {
261 /*
262 * Duplicate of 1) in order to cater for an alternative
263 * representation of !O-NA:
264 * (INVALID | UNOWNED | SHARED) and (INVALID | UNOWNED)
265 * are both alternate representations of !O-NA.
266 */
267 /* 2) {O-EA, !O-NA} -> {O-SA, !O-SA} */
268 .orig_from_mode = 0,
269 .orig_to_mode = MM_MODE_INVALID | MM_MODE_UNOWNED,
270 .from_mode = MM_MODE_SHARED,
271 .to_mode = MM_MODE_UNOWNED | MM_MODE_SHARED,
272 },
273 };
274
275 static const uint32_t size_shared_lend_transitions =
276 ARRAY_SIZE(shared_lend_transitions);
Jose Marinho75509b42019-04-09 09:34:59 +0100277
278 /* Fail if addresses are not page-aligned. */
279 if (!is_aligned(ipa_addr(begin), PAGE_SIZE) ||
280 !is_aligned(ipa_addr(end), PAGE_SIZE)) {
281 return false;
282 }
283
Andrew Scullb5f49e02019-10-02 13:20:47 +0100284 /* Ensure that the memory range is mapped with the same mode. */
Jose Marinho75509b42019-04-09 09:34:59 +0100285 if (!mm_vm_get_mode(&from->ptable, begin, end, orig_from_mode) ||
286 !mm_vm_get_mode(&to->ptable, begin, end, &orig_to_mode)) {
287 return false;
288 }
289
Andrew Scullb5f49e02019-10-02 13:20:47 +0100290 /* Ensure the address range is normal memory and not a device. */
291 if (*orig_from_mode & MM_MODE_D) {
292 return false;
293 }
294
Jose Marinho75509b42019-04-09 09:34:59 +0100295 switch (share) {
296 case SPCI_MEMORY_DONATE:
297 mem_transition_table = donate_transitions;
298 transition_table_size = size_donate_transitions;
299 break;
300
Jose Marinho56c25732019-05-20 09:48:53 +0100301 case SPCI_MEMORY_RELINQUISH:
302 mem_transition_table = relinquish_transitions;
303 transition_table_size = size_relinquish_transitions;
304 break;
305
Jose Marinho713f13a2019-05-21 11:54:16 +0100306 case SPCI_MEMORY_LEND:
307 mem_transition_table = shared_lend_transitions;
308 transition_table_size = size_shared_lend_transitions;
309 break;
310
Jose Marinho75509b42019-04-09 09:34:59 +0100311 default:
312 return false;
313 }
314
315 return spci_msg_get_next_state(mem_transition_table,
316 transition_table_size,
317 memory_to_attributes, *orig_from_mode,
318 orig_to_mode, from_mode, to_mode);
319}