blob: d83beaf87b8a679d5293ef8fd0336ddfc2778d7f [file] [log] [blame]
Andrew Scull2e7a76d2019-01-24 11:47:26 +00001/*
Andrew Walbran692b3252019-03-07 15:51:31 +00002 * Copyright 2019 The Hafnium Authors.
Andrew Scull2e7a76d2019-01-24 11:47:26 +00003 *
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 <stdint.h>
18
Jose Marinhoa1dfeda2019-02-27 16:46:03 +000019#include "hf/spci.h"
Andrew Scull8d9e1212019-04-05 13:52:55 +010020#include "hf/std.h"
Jose Marinhoa1dfeda2019-02-27 16:46:03 +000021
Andrew Scull2e7a76d2019-01-24 11:47:26 +000022#include "vmapi/hf/call.h"
23
24#include "hftest.h"
25#include "primary_with_secondary.h"
Andrew Walbran53d9d042019-03-28 11:35:49 +000026#include "util.h"
Andrew Scull2e7a76d2019-01-24 11:47:26 +000027
28/**
29 * Reverses the order of the elements in the given array.
30 */
31static void reverse(char *s, size_t len)
32{
33 size_t i;
34
35 for (i = 0; i < len / 2; i++) {
36 char t = s[i];
37 s[i] = s[len - 1 - i];
38 s[len - 1 - i] = t;
39 }
40}
41
42/**
43 * Finds the next lexicographic permutation of the given array, if there is one.
44 */
45static void next_permutation(char *s, size_t len)
46{
47 size_t i, j;
48
49 for (i = len - 2; i < len; i--) {
50 const char t = s[i];
51 if (t >= s[i + 1]) {
52 continue;
53 }
54
55 for (j = len - 1; t >= s[j]; j--) {
56 }
57
58 s[i] = s[j];
59 s[j] = t;
60 reverse(s + i + 1, len - i - 1);
61 return;
62 }
63}
64
65/**
Andrew Scullaa7db8e2019-02-01 14:12:19 +000066 * Clearing an empty mailbox is a noop.
67 */
68TEST(mailbox, clear_empty)
69{
70 EXPECT_EQ(hf_mailbox_clear(), 0);
71 EXPECT_EQ(hf_mailbox_clear(), 0);
72 EXPECT_EQ(hf_mailbox_clear(), 0);
73}
74
75/**
Andrew Scull2e7a76d2019-01-24 11:47:26 +000076 * Send and receive the same message from the echo VM.
77 */
78TEST(mailbox, echo)
79{
80 const char message[] = "Echo this back to me!";
81 struct hf_vcpu_run_return run_res;
82 struct mailbox_buffers mb = set_up_mailbox();
83
84 SERVICE_SELECT(SERVICE_VM0, "echo", mb.send);
85
86 run_res = hf_vcpu_run(SERVICE_VM0, 0);
Andrew Scullb06d1752019-02-04 10:15:48 +000087 EXPECT_EQ(run_res.code, HF_VCPU_RUN_WAIT_FOR_MESSAGE);
88 EXPECT_EQ(run_res.sleep.ns, HF_SLEEP_INDEFINITE);
Andrew Scull2e7a76d2019-01-24 11:47:26 +000089
90 /* Set the message, echo it and check it didn't change. */
Andrew Walbran70bc8622019-10-07 14:15:58 +010091 memcpy_s(mb.send, SPCI_MSG_PAYLOAD_MAX, message, sizeof(message));
92 EXPECT_EQ(
93 spci_msg_send(HF_PRIMARY_VM_ID, SERVICE_VM0, sizeof(message), 0)
94 .func,
95 SPCI_SUCCESS_32);
Andrew Scull2e7a76d2019-01-24 11:47:26 +000096 run_res = hf_vcpu_run(SERVICE_VM0, 0);
97 EXPECT_EQ(run_res.code, HF_VCPU_RUN_MESSAGE);
Andrew Walbranf1bd6322019-10-03 16:45:11 +010098 EXPECT_EQ(run_res.message.size, sizeof(message));
Andrew Walbran70bc8622019-10-07 14:15:58 +010099 EXPECT_EQ(memcmp(mb.send, message, sizeof(message)), 0);
Andrew Scull2e7a76d2019-01-24 11:47:26 +0000100 EXPECT_EQ(hf_mailbox_clear(), 0);
101}
102
103/**
104 * Repeatedly send a message and receive it back from the echo VM.
105 */
106TEST(mailbox, repeated_echo)
107{
108 char message[] = "Echo this back to me!";
109 struct hf_vcpu_run_return run_res;
110 uint8_t i;
111 struct mailbox_buffers mb = set_up_mailbox();
112
113 SERVICE_SELECT(SERVICE_VM0, "echo", mb.send);
114
115 for (i = 0; i < 100; i++) {
116 /* Run secondary until it reaches the wait for messages. */
117 run_res = hf_vcpu_run(SERVICE_VM0, 0);
Andrew Scullb06d1752019-02-04 10:15:48 +0000118 EXPECT_EQ(run_res.code, HF_VCPU_RUN_WAIT_FOR_MESSAGE);
119 EXPECT_EQ(run_res.sleep.ns, HF_SLEEP_INDEFINITE);
Andrew Scull2e7a76d2019-01-24 11:47:26 +0000120
121 /* Set the message, echo it and check it didn't change. */
122 next_permutation(message, sizeof(message) - 1);
Andrew Walbran70bc8622019-10-07 14:15:58 +0100123 memcpy_s(mb.send, SPCI_MSG_PAYLOAD_MAX, message,
Andrew Sculla1aa2ba2019-04-05 11:49:02 +0100124 sizeof(message));
Andrew Walbran70bc8622019-10-07 14:15:58 +0100125 EXPECT_EQ(spci_msg_send(HF_PRIMARY_VM_ID, SERVICE_VM0,
126 sizeof(message), 0)
127 .func,
128 SPCI_SUCCESS_32);
Andrew Scull2e7a76d2019-01-24 11:47:26 +0000129 run_res = hf_vcpu_run(SERVICE_VM0, 0);
130 EXPECT_EQ(run_res.code, HF_VCPU_RUN_MESSAGE);
Andrew Walbranf1bd6322019-10-03 16:45:11 +0100131 EXPECT_EQ(run_res.message.size, sizeof(message));
Andrew Walbran70bc8622019-10-07 14:15:58 +0100132 EXPECT_EQ(memcmp(mb.recv, message, sizeof(message)), 0);
Andrew Scull2e7a76d2019-01-24 11:47:26 +0000133 EXPECT_EQ(hf_mailbox_clear(), 0);
134 }
135}
136
137/**
138 * Send a message to relay_a which will forward it to relay_b where it will be
139 * sent back here.
140 */
141TEST(mailbox, relay)
142{
143 const char message[] = "Send this round the relay!";
144 struct hf_vcpu_run_return run_res;
145 struct mailbox_buffers mb = set_up_mailbox();
146
147 SERVICE_SELECT(SERVICE_VM0, "relay", mb.send);
148 SERVICE_SELECT(SERVICE_VM1, "relay", mb.send);
149
150 run_res = hf_vcpu_run(SERVICE_VM0, 0);
Andrew Scullb06d1752019-02-04 10:15:48 +0000151 EXPECT_EQ(run_res.code, HF_VCPU_RUN_WAIT_FOR_MESSAGE);
152 EXPECT_EQ(run_res.sleep.ns, HF_SLEEP_INDEFINITE);
Andrew Scull2e7a76d2019-01-24 11:47:26 +0000153 run_res = hf_vcpu_run(SERVICE_VM1, 0);
Andrew Scullb06d1752019-02-04 10:15:48 +0000154 EXPECT_EQ(run_res.code, HF_VCPU_RUN_WAIT_FOR_MESSAGE);
155 EXPECT_EQ(run_res.sleep.ns, HF_SLEEP_INDEFINITE);
Andrew Scull2e7a76d2019-01-24 11:47:26 +0000156
157 /*
158 * Build the message chain so the message is sent from here to
159 * SERVICE_VM0, then to SERVICE_VM1 and finally back to here.
160 */
161 {
Andrew Walbran70bc8622019-10-07 14:15:58 +0100162 spci_vm_id_t *chain = (spci_vm_id_t *)mb.send;
Andrew Scull2e7a76d2019-01-24 11:47:26 +0000163 *chain++ = htole32(SERVICE_VM1);
164 *chain++ = htole32(HF_PRIMARY_VM_ID);
Andrew Walbran95534922019-06-19 11:32:54 +0100165 memcpy_s(chain,
166 SPCI_MSG_PAYLOAD_MAX - (2 * sizeof(spci_vm_id_t)),
Andrew Sculla1aa2ba2019-04-05 11:49:02 +0100167 message, sizeof(message));
Jose Marinhoa1dfeda2019-02-27 16:46:03 +0000168
Andrew Walbran70bc8622019-10-07 14:15:58 +0100169 EXPECT_EQ(
170 spci_msg_send(
171 HF_PRIMARY_VM_ID, SERVICE_VM0,
172 sizeof(message) + (2 * sizeof(spci_vm_id_t)), 0)
173 .func,
174 SPCI_SUCCESS_32);
Andrew Scull2e7a76d2019-01-24 11:47:26 +0000175 }
176
177 /* Let SERVICE_VM0 forward the message. */
178 run_res = hf_vcpu_run(SERVICE_VM0, 0);
Andrew Scullb06d1752019-02-04 10:15:48 +0000179 EXPECT_EQ(run_res.code, HF_VCPU_RUN_MESSAGE);
180 EXPECT_EQ(run_res.message.vm_id, SERVICE_VM1);
Andrew Walbranf1bd6322019-10-03 16:45:11 +0100181 EXPECT_EQ(run_res.message.size, 0);
Andrew Scull2e7a76d2019-01-24 11:47:26 +0000182
183 /* Let SERVICE_VM1 forward the message. */
184 run_res = hf_vcpu_run(SERVICE_VM1, 0);
185 EXPECT_EQ(run_res.code, HF_VCPU_RUN_MESSAGE);
186
Andrew Walbran95534922019-06-19 11:32:54 +0100187 /* Ensure the message is intact. */
Andrew Scullb06d1752019-02-04 10:15:48 +0000188 EXPECT_EQ(run_res.message.vm_id, HF_PRIMARY_VM_ID);
Andrew Walbranf1bd6322019-10-03 16:45:11 +0100189 EXPECT_EQ(run_res.message.size, sizeof(message));
Andrew Walbran70bc8622019-10-07 14:15:58 +0100190 EXPECT_EQ(memcmp(mb.recv, message, sizeof(message)), 0);
Andrew Scull2e7a76d2019-01-24 11:47:26 +0000191 EXPECT_EQ(hf_mailbox_clear(), 0);
192}
193
194/**
195 * Send a message before the secondary VM is configured, but do not register
196 * for notification. Ensure we're not notified.
197 */
198TEST(mailbox, no_primary_to_secondary_notification_on_configure)
199{
200 struct hf_vcpu_run_return run_res;
Andrew Walbran70bc8622019-10-07 14:15:58 +0100201 set_up_mailbox();
Andrew Scull2e7a76d2019-01-24 11:47:26 +0000202
Andrew Walbran70bc8622019-10-07 14:15:58 +0100203 EXPECT_SPCI_ERROR(spci_msg_send(HF_PRIMARY_VM_ID, SERVICE_VM0, 0, 0),
204 SPCI_BUSY);
Andrew Scull2e7a76d2019-01-24 11:47:26 +0000205
206 run_res = hf_vcpu_run(SERVICE_VM0, 0);
Andrew Scullb06d1752019-02-04 10:15:48 +0000207 EXPECT_EQ(run_res.code, HF_VCPU_RUN_WAIT_FOR_MESSAGE);
208 EXPECT_EQ(run_res.sleep.ns, HF_SLEEP_INDEFINITE);
Andrew Scull2e7a76d2019-01-24 11:47:26 +0000209
Andrew Walbran70bc8622019-10-07 14:15:58 +0100210 EXPECT_EQ(spci_msg_send(HF_PRIMARY_VM_ID, SERVICE_VM0, 0, 0).func,
211 SPCI_SUCCESS_32);
Andrew Scull2e7a76d2019-01-24 11:47:26 +0000212}
213
214/**
215 * Send a message before the secondary VM is configured, and receive a
216 * notification when it configures.
217 */
218TEST(mailbox, secondary_to_primary_notification_on_configure)
219{
220 struct hf_vcpu_run_return run_res;
Andrew Walbran70bc8622019-10-07 14:15:58 +0100221 set_up_mailbox();
Andrew Scull2e7a76d2019-01-24 11:47:26 +0000222
Andrew Walbran70bc8622019-10-07 14:15:58 +0100223 EXPECT_SPCI_ERROR(spci_msg_send(HF_PRIMARY_VM_ID, SERVICE_VM0, 0,
224 SPCI_MSG_SEND_NOTIFY),
225 SPCI_BUSY);
Andrew Scull2e7a76d2019-01-24 11:47:26 +0000226
227 /*
228 * Run first VM for it to configure itself. It should result in
229 * notifications having to be issued.
230 */
231 run_res = hf_vcpu_run(SERVICE_VM0, 0);
232 EXPECT_EQ(run_res.code, HF_VCPU_RUN_NOTIFY_WAITERS);
233
234 /* A single waiter is returned. */
235 EXPECT_EQ(hf_mailbox_waiter_get(SERVICE_VM0), HF_PRIMARY_VM_ID);
236 EXPECT_EQ(hf_mailbox_waiter_get(SERVICE_VM0), -1);
237
Andrew Scullb06d1752019-02-04 10:15:48 +0000238 /* Send should now succeed. */
Andrew Walbran70bc8622019-10-07 14:15:58 +0100239 EXPECT_EQ(spci_msg_send(HF_PRIMARY_VM_ID, SERVICE_VM0, 0, 0).func,
240 SPCI_SUCCESS_32);
Andrew Scull2e7a76d2019-01-24 11:47:26 +0000241}
242
243/**
244 * Causes secondary VM to send two messages to primary VM. The second message
245 * will reach the mailbox while it's not writable. Checks that notifications are
246 * properly delivered when mailbox is cleared.
247 */
248TEST(mailbox, primary_to_secondary)
249{
250 char message[] = "not ready echo";
251 struct hf_vcpu_run_return run_res;
252 struct mailbox_buffers mb = set_up_mailbox();
253
254 SERVICE_SELECT(SERVICE_VM0, "echo_with_notification", mb.send);
255
256 run_res = hf_vcpu_run(SERVICE_VM0, 0);
Andrew Scullb06d1752019-02-04 10:15:48 +0000257 EXPECT_EQ(run_res.code, HF_VCPU_RUN_WAIT_FOR_MESSAGE);
258 EXPECT_EQ(run_res.sleep.ns, HF_SLEEP_INDEFINITE);
Andrew Scull2e7a76d2019-01-24 11:47:26 +0000259
260 /* Send a message to echo service, and get response back. */
Andrew Walbran70bc8622019-10-07 14:15:58 +0100261 memcpy_s(mb.send, SPCI_MSG_PAYLOAD_MAX, message, sizeof(message));
262 EXPECT_EQ(
263 spci_msg_send(HF_PRIMARY_VM_ID, SERVICE_VM0, sizeof(message), 0)
264 .func,
265 SPCI_SUCCESS_32);
Andrew Scull2e7a76d2019-01-24 11:47:26 +0000266 run_res = hf_vcpu_run(SERVICE_VM0, 0);
267 EXPECT_EQ(run_res.code, HF_VCPU_RUN_MESSAGE);
Andrew Walbranf1bd6322019-10-03 16:45:11 +0100268 EXPECT_EQ(run_res.message.size, sizeof(message));
Andrew Walbran70bc8622019-10-07 14:15:58 +0100269 EXPECT_EQ(memcmp(mb.recv, message, sizeof(message)), 0);
Andrew Scull2e7a76d2019-01-24 11:47:26 +0000270
271 /* Let secondary VM continue running so that it will wait again. */
272 run_res = hf_vcpu_run(SERVICE_VM0, 0);
Andrew Scullb06d1752019-02-04 10:15:48 +0000273 EXPECT_EQ(run_res.code, HF_VCPU_RUN_WAIT_FOR_MESSAGE);
274 EXPECT_EQ(run_res.sleep.ns, HF_SLEEP_INDEFINITE);
Andrew Scull2e7a76d2019-01-24 11:47:26 +0000275
276 /* Without clearing our mailbox, send message again. */
Andrew Scull55baca62019-04-05 14:56:20 +0100277 reverse(message, strnlen_s(message, sizeof(message)));
Andrew Walbran70bc8622019-10-07 14:15:58 +0100278 memcpy_s(mb.send, SPCI_MSG_PAYLOAD_MAX, message, sizeof(message));
Jose Marinhoa1dfeda2019-02-27 16:46:03 +0000279
280 /* Message should be dropped since the mailbox was not cleared. */
Andrew Walbran70bc8622019-10-07 14:15:58 +0100281 EXPECT_EQ(
282 spci_msg_send(HF_PRIMARY_VM_ID, SERVICE_VM0, sizeof(message), 0)
283 .func,
284 SPCI_SUCCESS_32);
Andrew Scull2e7a76d2019-01-24 11:47:26 +0000285 run_res = hf_vcpu_run(SERVICE_VM0, 0);
286 EXPECT_EQ(run_res.code, HF_VCPU_RUN_WAIT_FOR_INTERRUPT);
Andrew Scullb06d1752019-02-04 10:15:48 +0000287 EXPECT_EQ(run_res.sleep.ns, HF_SLEEP_INDEFINITE);
Andrew Scull2e7a76d2019-01-24 11:47:26 +0000288
289 /* Clear the mailbox. We expect to be told there are pending waiters. */
290 EXPECT_EQ(hf_mailbox_clear(), 1);
291
292 /* Retrieve a single waiter. */
293 EXPECT_EQ(hf_mailbox_waiter_get(HF_PRIMARY_VM_ID), SERVICE_VM0);
294 EXPECT_EQ(hf_mailbox_waiter_get(HF_PRIMARY_VM_ID), -1);
295
296 /*
297 * Inject interrupt into VM and let it run again. We should receive
298 * the echoed message.
299 */
300 EXPECT_EQ(
301 hf_interrupt_inject(SERVICE_VM0, 0, HF_MAILBOX_WRITABLE_INTID),
302 1);
303 run_res = hf_vcpu_run(SERVICE_VM0, 0);
304 EXPECT_EQ(run_res.code, HF_VCPU_RUN_MESSAGE);
Andrew Walbranf1bd6322019-10-03 16:45:11 +0100305 EXPECT_EQ(run_res.message.size, sizeof(message));
Andrew Walbran70bc8622019-10-07 14:15:58 +0100306 EXPECT_EQ(memcmp(mb.recv, message, sizeof(message)), 0);
Andrew Scull2e7a76d2019-01-24 11:47:26 +0000307}
308
309/**
310 * Sends two messages to secondary VM without letting it run, so second message
311 * won't go through. Ensure that a notification is delivered when secondary VM
312 * clears the mailbox.
313 */
314TEST(mailbox, secondary_to_primary_notification)
315{
316 const char message[] = "not ready echo";
317 struct hf_vcpu_run_return run_res;
318 struct mailbox_buffers mb = set_up_mailbox();
319
320 SERVICE_SELECT(SERVICE_VM0, "echo_with_notification", mb.send);
321
322 run_res = hf_vcpu_run(SERVICE_VM0, 0);
Andrew Scullb06d1752019-02-04 10:15:48 +0000323 EXPECT_EQ(run_res.code, HF_VCPU_RUN_WAIT_FOR_MESSAGE);
324 EXPECT_EQ(run_res.sleep.ns, HF_SLEEP_INDEFINITE);
Andrew Scull2e7a76d2019-01-24 11:47:26 +0000325
326 /* Send a message to echo service twice. The second should fail. */
Andrew Walbran70bc8622019-10-07 14:15:58 +0100327 memcpy_s(mb.send, SPCI_MSG_PAYLOAD_MAX, message, sizeof(message));
328 EXPECT_EQ(
329 spci_msg_send(HF_PRIMARY_VM_ID, SERVICE_VM0, sizeof(message), 0)
330 .func,
331 SPCI_SUCCESS_32);
332 EXPECT_EQ(spci_msg_send(HF_PRIMARY_VM_ID, SERVICE_VM0, sizeof(message),
333 SPCI_MSG_SEND_NOTIFY)
334 .arg1,
335 SPCI_BUSY);
Andrew Scull2e7a76d2019-01-24 11:47:26 +0000336
337 /* Receive a reply for the first message. */
338 run_res = hf_vcpu_run(SERVICE_VM0, 0);
339 EXPECT_EQ(run_res.code, HF_VCPU_RUN_MESSAGE);
Andrew Walbranf1bd6322019-10-03 16:45:11 +0100340 EXPECT_EQ(run_res.message.size, sizeof(message));
Andrew Walbran70bc8622019-10-07 14:15:58 +0100341 EXPECT_EQ(memcmp(mb.recv, message, sizeof(message)), 0);
Andrew Scull2e7a76d2019-01-24 11:47:26 +0000342
343 /* Run VM again so that it clears its mailbox. */
344 run_res = hf_vcpu_run(SERVICE_VM0, 0);
345 EXPECT_EQ(run_res.code, HF_VCPU_RUN_NOTIFY_WAITERS);
346
347 /* Retrieve a single waiter. */
348 EXPECT_EQ(hf_mailbox_waiter_get(SERVICE_VM0), HF_PRIMARY_VM_ID);
349 EXPECT_EQ(hf_mailbox_waiter_get(SERVICE_VM0), -1);
350
Andrew Scullb06d1752019-02-04 10:15:48 +0000351 /* Send should now succeed. */
Andrew Walbran70bc8622019-10-07 14:15:58 +0100352 EXPECT_EQ(
353 spci_msg_send(HF_PRIMARY_VM_ID, SERVICE_VM0, sizeof(message), 0)
354 .func,
355 SPCI_SUCCESS_32);
Andrew Scull2e7a76d2019-01-24 11:47:26 +0000356}