blob: 8615957a9a0a83edac200b5bcfae917ef78f53f4 [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
Andrew Walbran4a53ba62019-03-05 17:26:12 +000019#include "hf/arch/std.h"
Andrew Scull2e7a76d2019-01-24 11:47:26 +000020
Jose Marinhoa1dfeda2019-02-27 16:46:03 +000021#include "hf/spci.h"
22
Andrew Scull2e7a76d2019-01-24 11:47:26 +000023#include "vmapi/hf/call.h"
24
25#include "hftest.h"
26#include "primary_with_secondary.h"
Andrew Walbran53d9d042019-03-28 11:35:49 +000027#include "util.h"
Andrew Scull2e7a76d2019-01-24 11:47:26 +000028
29/**
30 * Reverses the order of the elements in the given array.
31 */
32static void reverse(char *s, size_t len)
33{
34 size_t i;
35
36 for (i = 0; i < len / 2; i++) {
37 char t = s[i];
38 s[i] = s[len - 1 - i];
39 s[len - 1 - i] = t;
40 }
41}
42
43/**
44 * Finds the next lexicographic permutation of the given array, if there is one.
45 */
46static void next_permutation(char *s, size_t len)
47{
48 size_t i, j;
49
50 for (i = len - 2; i < len; i--) {
51 const char t = s[i];
52 if (t >= s[i + 1]) {
53 continue;
54 }
55
56 for (j = len - 1; t >= s[j]; j--) {
57 }
58
59 s[i] = s[j];
60 s[j] = t;
61 reverse(s + i + 1, len - i - 1);
62 return;
63 }
64}
65
66/**
Andrew Scullaa7db8e2019-02-01 14:12:19 +000067 * Clearing an empty mailbox is a noop.
68 */
69TEST(mailbox, clear_empty)
70{
71 EXPECT_EQ(hf_mailbox_clear(), 0);
72 EXPECT_EQ(hf_mailbox_clear(), 0);
73 EXPECT_EQ(hf_mailbox_clear(), 0);
74}
75
76/**
Andrew Scull2e7a76d2019-01-24 11:47:26 +000077 * Send and receive the same message from the echo VM.
78 */
79TEST(mailbox, echo)
80{
81 const char message[] = "Echo this back to me!";
82 struct hf_vcpu_run_return run_res;
83 struct mailbox_buffers mb = set_up_mailbox();
84
85 SERVICE_SELECT(SERVICE_VM0, "echo", mb.send);
86
87 run_res = hf_vcpu_run(SERVICE_VM0, 0);
Andrew Scullb06d1752019-02-04 10:15:48 +000088 EXPECT_EQ(run_res.code, HF_VCPU_RUN_WAIT_FOR_MESSAGE);
89 EXPECT_EQ(run_res.sleep.ns, HF_SLEEP_INDEFINITE);
Andrew Scull2e7a76d2019-01-24 11:47:26 +000090
91 /* Set the message, echo it and check it didn't change. */
Jose Marinhoa1dfeda2019-02-27 16:46:03 +000092 memcpy(mb.send->payload, message, sizeof(message));
93 spci_message_init(mb.send, sizeof(message), SERVICE_VM0,
94 HF_PRIMARY_VM_ID);
95 EXPECT_EQ(spci_msg_send(0), 0);
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);
98 EXPECT_EQ(run_res.message.size, sizeof(message));
Jose Marinhoa1dfeda2019-02-27 16:46:03 +000099 EXPECT_EQ(memcmp(mb.send->payload, 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);
Jose Marinhoa1dfeda2019-02-27 16:46:03 +0000123 memcpy(mb.send->payload, message, sizeof(message));
124 spci_message_init(mb.send, sizeof(message), SERVICE_VM0,
125 HF_PRIMARY_VM_ID);
126 EXPECT_EQ(spci_msg_send(0), 0);
Andrew Scull2e7a76d2019-01-24 11:47:26 +0000127 run_res = hf_vcpu_run(SERVICE_VM0, 0);
128 EXPECT_EQ(run_res.code, HF_VCPU_RUN_MESSAGE);
129 EXPECT_EQ(run_res.message.size, sizeof(message));
Jose Marinhoa1dfeda2019-02-27 16:46:03 +0000130 EXPECT_EQ(memcmp(mb.recv->payload, message, sizeof(message)),
131 0);
Andrew Scull2e7a76d2019-01-24 11:47:26 +0000132 EXPECT_EQ(hf_mailbox_clear(), 0);
133 }
134}
135
136/**
137 * Send a message to relay_a which will forward it to relay_b where it will be
138 * sent back here.
139 */
140TEST(mailbox, relay)
141{
142 const char message[] = "Send this round the relay!";
143 struct hf_vcpu_run_return run_res;
144 struct mailbox_buffers mb = set_up_mailbox();
145
146 SERVICE_SELECT(SERVICE_VM0, "relay", mb.send);
147 SERVICE_SELECT(SERVICE_VM1, "relay", mb.send);
148
149 run_res = hf_vcpu_run(SERVICE_VM0, 0);
Andrew Scullb06d1752019-02-04 10:15:48 +0000150 EXPECT_EQ(run_res.code, HF_VCPU_RUN_WAIT_FOR_MESSAGE);
151 EXPECT_EQ(run_res.sleep.ns, HF_SLEEP_INDEFINITE);
Andrew Scull2e7a76d2019-01-24 11:47:26 +0000152 run_res = hf_vcpu_run(SERVICE_VM1, 0);
Andrew Scullb06d1752019-02-04 10:15:48 +0000153 EXPECT_EQ(run_res.code, HF_VCPU_RUN_WAIT_FOR_MESSAGE);
154 EXPECT_EQ(run_res.sleep.ns, HF_SLEEP_INDEFINITE);
Andrew Scull2e7a76d2019-01-24 11:47:26 +0000155
156 /*
157 * Build the message chain so the message is sent from here to
158 * SERVICE_VM0, then to SERVICE_VM1 and finally back to here.
159 */
160 {
Jose Marinhoa1dfeda2019-02-27 16:46:03 +0000161 uint32_t *chain = (uint32_t *)mb.send->payload;
Andrew Scull2e7a76d2019-01-24 11:47:26 +0000162 *chain++ = htole32(SERVICE_VM1);
163 *chain++ = htole32(HF_PRIMARY_VM_ID);
164 memcpy(chain, message, sizeof(message));
Jose Marinhoa1dfeda2019-02-27 16:46:03 +0000165
166 spci_message_init(mb.send,
Andrew Scull2e7a76d2019-01-24 11:47:26 +0000167 sizeof(message) + (2 * sizeof(uint32_t)),
Jose Marinhoa1dfeda2019-02-27 16:46:03 +0000168 SERVICE_VM0, HF_PRIMARY_VM_ID);
169 EXPECT_EQ(spci_msg_send(0), 0);
Andrew Scull2e7a76d2019-01-24 11:47:26 +0000170 }
171
172 /* Let SERVICE_VM0 forward the message. */
173 run_res = hf_vcpu_run(SERVICE_VM0, 0);
Andrew Scullb06d1752019-02-04 10:15:48 +0000174 EXPECT_EQ(run_res.code, HF_VCPU_RUN_MESSAGE);
175 EXPECT_EQ(run_res.message.vm_id, SERVICE_VM1);
176 EXPECT_EQ(run_res.message.size, 0);
Andrew Scull2e7a76d2019-01-24 11:47:26 +0000177
178 /* Let SERVICE_VM1 forward the message. */
179 run_res = hf_vcpu_run(SERVICE_VM1, 0);
180 EXPECT_EQ(run_res.code, HF_VCPU_RUN_MESSAGE);
181
182 /* Ensure the message is in tact. */
Andrew Scullb06d1752019-02-04 10:15:48 +0000183 EXPECT_EQ(run_res.message.vm_id, HF_PRIMARY_VM_ID);
Andrew Scull2e7a76d2019-01-24 11:47:26 +0000184 EXPECT_EQ(run_res.message.size, sizeof(message));
Jose Marinhoa1dfeda2019-02-27 16:46:03 +0000185 EXPECT_EQ(memcmp(mb.recv->payload, message, sizeof(message)), 0);
Andrew Scull2e7a76d2019-01-24 11:47:26 +0000186 EXPECT_EQ(hf_mailbox_clear(), 0);
187}
188
189/**
190 * Send a message before the secondary VM is configured, but do not register
191 * for notification. Ensure we're not notified.
192 */
193TEST(mailbox, no_primary_to_secondary_notification_on_configure)
194{
195 struct hf_vcpu_run_return run_res;
196
Jose Marinhoa1dfeda2019-02-27 16:46:03 +0000197 struct mailbox_buffers mb = set_up_mailbox();
198 spci_message_init(mb.send, 0, SERVICE_VM0, HF_PRIMARY_VM_ID);
199 EXPECT_EQ(spci_msg_send(0), SPCI_BUSY);
Andrew Scull2e7a76d2019-01-24 11:47:26 +0000200
201 run_res = hf_vcpu_run(SERVICE_VM0, 0);
Jose Marinhoa1dfeda2019-02-27 16:46:03 +0000202 spci_message_init(mb.send, 0, SERVICE_VM0, HF_PRIMARY_VM_ID);
Andrew Scullb06d1752019-02-04 10:15:48 +0000203 EXPECT_EQ(run_res.code, HF_VCPU_RUN_WAIT_FOR_MESSAGE);
204 EXPECT_EQ(run_res.sleep.ns, HF_SLEEP_INDEFINITE);
Andrew Scull2e7a76d2019-01-24 11:47:26 +0000205
Jose Marinhoa1dfeda2019-02-27 16:46:03 +0000206 EXPECT_EQ(spci_msg_send(0), SPCI_SUCCESS);
Andrew Scull2e7a76d2019-01-24 11:47:26 +0000207}
208
209/**
210 * Send a message before the secondary VM is configured, and receive a
211 * notification when it configures.
212 */
213TEST(mailbox, secondary_to_primary_notification_on_configure)
214{
215 struct hf_vcpu_run_return run_res;
216
Jose Marinhoa1dfeda2019-02-27 16:46:03 +0000217 struct mailbox_buffers mb = set_up_mailbox();
Andrew Scull2e7a76d2019-01-24 11:47:26 +0000218
Jose Marinhoa1dfeda2019-02-27 16:46:03 +0000219 spci_message_init(mb.send, 0, SERVICE_VM0, HF_PRIMARY_VM_ID);
220 EXPECT_EQ(spci_msg_send(SPCI_MSG_SEND_NOTIFY), SPCI_BUSY);
Andrew Scull2e7a76d2019-01-24 11:47:26 +0000221
222 /*
223 * Run first VM for it to configure itself. It should result in
224 * notifications having to be issued.
225 */
226 run_res = hf_vcpu_run(SERVICE_VM0, 0);
227 EXPECT_EQ(run_res.code, HF_VCPU_RUN_NOTIFY_WAITERS);
228
229 /* A single waiter is returned. */
230 EXPECT_EQ(hf_mailbox_waiter_get(SERVICE_VM0), HF_PRIMARY_VM_ID);
231 EXPECT_EQ(hf_mailbox_waiter_get(SERVICE_VM0), -1);
232
Andrew Scullb06d1752019-02-04 10:15:48 +0000233 /* Send should now succeed. */
Jose Marinhoa1dfeda2019-02-27 16:46:03 +0000234 EXPECT_EQ(spci_msg_send(0), SPCI_SUCCESS);
Andrew Scull2e7a76d2019-01-24 11:47:26 +0000235}
236
237/**
238 * Causes secondary VM to send two messages to primary VM. The second message
239 * will reach the mailbox while it's not writable. Checks that notifications are
240 * properly delivered when mailbox is cleared.
241 */
242TEST(mailbox, primary_to_secondary)
243{
244 char message[] = "not ready echo";
245 struct hf_vcpu_run_return run_res;
246 struct mailbox_buffers mb = set_up_mailbox();
247
248 SERVICE_SELECT(SERVICE_VM0, "echo_with_notification", mb.send);
249
250 run_res = hf_vcpu_run(SERVICE_VM0, 0);
Andrew Scullb06d1752019-02-04 10:15:48 +0000251 EXPECT_EQ(run_res.code, HF_VCPU_RUN_WAIT_FOR_MESSAGE);
252 EXPECT_EQ(run_res.sleep.ns, HF_SLEEP_INDEFINITE);
Andrew Scull2e7a76d2019-01-24 11:47:26 +0000253
254 /* Send a message to echo service, and get response back. */
Jose Marinhoa1dfeda2019-02-27 16:46:03 +0000255 memcpy(mb.send->payload, message, sizeof(message));
256 spci_message_init(mb.send, sizeof(message), SERVICE_VM0,
257 HF_PRIMARY_VM_ID);
258 EXPECT_EQ(spci_msg_send(0), 0);
Andrew Scull2e7a76d2019-01-24 11:47:26 +0000259 run_res = hf_vcpu_run(SERVICE_VM0, 0);
260 EXPECT_EQ(run_res.code, HF_VCPU_RUN_MESSAGE);
261 EXPECT_EQ(run_res.message.size, sizeof(message));
Jose Marinhoa1dfeda2019-02-27 16:46:03 +0000262 EXPECT_EQ(memcmp(mb.recv->payload, message, sizeof(message)), 0);
Andrew Scull2e7a76d2019-01-24 11:47:26 +0000263
264 /* Let secondary VM continue running so that it will wait again. */
265 run_res = hf_vcpu_run(SERVICE_VM0, 0);
Andrew Scullb06d1752019-02-04 10:15:48 +0000266 EXPECT_EQ(run_res.code, HF_VCPU_RUN_WAIT_FOR_MESSAGE);
267 EXPECT_EQ(run_res.sleep.ns, HF_SLEEP_INDEFINITE);
Andrew Scull2e7a76d2019-01-24 11:47:26 +0000268
269 /* Without clearing our mailbox, send message again. */
270 reverse(message, strlen(message));
Jose Marinhoa1dfeda2019-02-27 16:46:03 +0000271 memcpy(mb.send->payload, message, sizeof(message));
272 spci_message_init(mb.send, sizeof(message), SERVICE_VM0,
273 HF_PRIMARY_VM_ID);
274
275 /* Message should be dropped since the mailbox was not cleared. */
276 EXPECT_EQ(spci_msg_send(0), 0);
Andrew Scull2e7a76d2019-01-24 11:47:26 +0000277 run_res = hf_vcpu_run(SERVICE_VM0, 0);
278 EXPECT_EQ(run_res.code, HF_VCPU_RUN_WAIT_FOR_INTERRUPT);
Andrew Scullb06d1752019-02-04 10:15:48 +0000279 EXPECT_EQ(run_res.sleep.ns, HF_SLEEP_INDEFINITE);
Andrew Scull2e7a76d2019-01-24 11:47:26 +0000280
281 /* Clear the mailbox. We expect to be told there are pending waiters. */
282 EXPECT_EQ(hf_mailbox_clear(), 1);
283
284 /* Retrieve a single waiter. */
285 EXPECT_EQ(hf_mailbox_waiter_get(HF_PRIMARY_VM_ID), SERVICE_VM0);
286 EXPECT_EQ(hf_mailbox_waiter_get(HF_PRIMARY_VM_ID), -1);
287
288 /*
289 * Inject interrupt into VM and let it run again. We should receive
290 * the echoed message.
291 */
292 EXPECT_EQ(
293 hf_interrupt_inject(SERVICE_VM0, 0, HF_MAILBOX_WRITABLE_INTID),
294 1);
295 run_res = hf_vcpu_run(SERVICE_VM0, 0);
296 EXPECT_EQ(run_res.code, HF_VCPU_RUN_MESSAGE);
297 EXPECT_EQ(run_res.message.size, sizeof(message));
Jose Marinhoa1dfeda2019-02-27 16:46:03 +0000298 EXPECT_EQ(memcmp(mb.recv->payload, message, sizeof(message)), 0);
Andrew Scull2e7a76d2019-01-24 11:47:26 +0000299}
300
301/**
302 * Sends two messages to secondary VM without letting it run, so second message
303 * won't go through. Ensure that a notification is delivered when secondary VM
304 * clears the mailbox.
305 */
306TEST(mailbox, secondary_to_primary_notification)
307{
308 const char message[] = "not ready echo";
309 struct hf_vcpu_run_return run_res;
310 struct mailbox_buffers mb = set_up_mailbox();
311
312 SERVICE_SELECT(SERVICE_VM0, "echo_with_notification", mb.send);
313
314 run_res = hf_vcpu_run(SERVICE_VM0, 0);
Andrew Scullb06d1752019-02-04 10:15:48 +0000315 EXPECT_EQ(run_res.code, HF_VCPU_RUN_WAIT_FOR_MESSAGE);
316 EXPECT_EQ(run_res.sleep.ns, HF_SLEEP_INDEFINITE);
Andrew Scull2e7a76d2019-01-24 11:47:26 +0000317
318 /* Send a message to echo service twice. The second should fail. */
Jose Marinhoa1dfeda2019-02-27 16:46:03 +0000319 memcpy(mb.send->payload, message, sizeof(message));
320 spci_message_init(mb.send, sizeof(message), SERVICE_VM0,
321 HF_PRIMARY_VM_ID);
322 EXPECT_EQ(spci_msg_send(0), SPCI_SUCCESS);
323 EXPECT_EQ(spci_msg_send(SPCI_MSG_SEND_NOTIFY), SPCI_BUSY);
Andrew Scull2e7a76d2019-01-24 11:47:26 +0000324
325 /* Receive a reply for the first message. */
326 run_res = hf_vcpu_run(SERVICE_VM0, 0);
327 EXPECT_EQ(run_res.code, HF_VCPU_RUN_MESSAGE);
328 EXPECT_EQ(run_res.message.size, sizeof(message));
Jose Marinhoa1dfeda2019-02-27 16:46:03 +0000329 EXPECT_EQ(memcmp(mb.recv->payload, message, sizeof(message)), 0);
Andrew Scull2e7a76d2019-01-24 11:47:26 +0000330
331 /* Run VM again so that it clears its mailbox. */
332 run_res = hf_vcpu_run(SERVICE_VM0, 0);
333 EXPECT_EQ(run_res.code, HF_VCPU_RUN_NOTIFY_WAITERS);
334
335 /* Retrieve a single waiter. */
336 EXPECT_EQ(hf_mailbox_waiter_get(SERVICE_VM0), HF_PRIMARY_VM_ID);
337 EXPECT_EQ(hf_mailbox_waiter_get(SERVICE_VM0), -1);
338
Andrew Scullb06d1752019-02-04 10:15:48 +0000339 /* Send should now succeed. */
Jose Marinhoa1dfeda2019-02-27 16:46:03 +0000340 EXPECT_EQ(spci_msg_send(0), SPCI_SUCCESS);
Andrew Scull2e7a76d2019-01-24 11:47:26 +0000341}