blob: bd5b588bdd038491737b24ec9e967bb2e383b3a3 [file] [log] [blame]
Andrew Scull2e7a76d2019-01-24 11:47:26 +00001/*
2 * Copyright 2019 Google LLC
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 <stdint.h>
18
19#include "hf/std.h"
20
21#include "vmapi/hf/call.h"
22
23#include "hftest.h"
24#include "primary_with_secondary.h"
25
26/**
27 * Reverses the order of the elements in the given array.
28 */
29static void reverse(char *s, size_t len)
30{
31 size_t i;
32
33 for (i = 0; i < len / 2; i++) {
34 char t = s[i];
35 s[i] = s[len - 1 - i];
36 s[len - 1 - i] = t;
37 }
38}
39
40/**
41 * Finds the next lexicographic permutation of the given array, if there is one.
42 */
43static void next_permutation(char *s, size_t len)
44{
45 size_t i, j;
46
47 for (i = len - 2; i < len; i--) {
48 const char t = s[i];
49 if (t >= s[i + 1]) {
50 continue;
51 }
52
53 for (j = len - 1; t >= s[j]; j--) {
54 }
55
56 s[i] = s[j];
57 s[j] = t;
58 reverse(s + i + 1, len - i - 1);
59 return;
60 }
61}
62
63/**
Andrew Scullaa7db8e2019-02-01 14:12:19 +000064 * Clearing an empty mailbox is a noop.
65 */
66TEST(mailbox, clear_empty)
67{
68 EXPECT_EQ(hf_mailbox_clear(), 0);
69 EXPECT_EQ(hf_mailbox_clear(), 0);
70 EXPECT_EQ(hf_mailbox_clear(), 0);
71}
72
73/**
Andrew Scull2e7a76d2019-01-24 11:47:26 +000074 * Send and receive the same message from the echo VM.
75 */
76TEST(mailbox, echo)
77{
78 const char message[] = "Echo this back to me!";
79 struct hf_vcpu_run_return run_res;
80 struct mailbox_buffers mb = set_up_mailbox();
81
82 SERVICE_SELECT(SERVICE_VM0, "echo", mb.send);
83
84 run_res = hf_vcpu_run(SERVICE_VM0, 0);
85 EXPECT_EQ(run_res.code, HF_VCPU_RUN_WAIT_FOR_INTERRUPT);
86
87 /* Set the message, echo it and check it didn't change. */
88 memcpy(mb.send, message, sizeof(message));
89 EXPECT_EQ(hf_mailbox_send(SERVICE_VM0, sizeof(message), false), 0);
90 run_res = hf_vcpu_run(SERVICE_VM0, 0);
91 EXPECT_EQ(run_res.code, HF_VCPU_RUN_MESSAGE);
92 EXPECT_EQ(run_res.message.size, sizeof(message));
93 EXPECT_EQ(memcmp(mb.recv, message, sizeof(message)), 0);
94 EXPECT_EQ(hf_mailbox_clear(), 0);
95}
96
97/**
98 * Repeatedly send a message and receive it back from the echo VM.
99 */
100TEST(mailbox, repeated_echo)
101{
102 char message[] = "Echo this back to me!";
103 struct hf_vcpu_run_return run_res;
104 uint8_t i;
105 struct mailbox_buffers mb = set_up_mailbox();
106
107 SERVICE_SELECT(SERVICE_VM0, "echo", mb.send);
108
109 for (i = 0; i < 100; i++) {
110 /* Run secondary until it reaches the wait for messages. */
111 run_res = hf_vcpu_run(SERVICE_VM0, 0);
112 EXPECT_EQ(run_res.code, HF_VCPU_RUN_WAIT_FOR_INTERRUPT);
113
114 /* Set the message, echo it and check it didn't change. */
115 next_permutation(message, sizeof(message) - 1);
116 memcpy(mb.send, message, sizeof(message));
117 EXPECT_EQ(hf_mailbox_send(SERVICE_VM0, sizeof(message), false),
118 0);
119 run_res = hf_vcpu_run(SERVICE_VM0, 0);
120 EXPECT_EQ(run_res.code, HF_VCPU_RUN_MESSAGE);
121 EXPECT_EQ(run_res.message.size, sizeof(message));
122 EXPECT_EQ(memcmp(mb.recv, message, sizeof(message)), 0);
123 EXPECT_EQ(hf_mailbox_clear(), 0);
124 }
125}
126
127/**
128 * Send a message to relay_a which will forward it to relay_b where it will be
129 * sent back here.
130 */
131TEST(mailbox, relay)
132{
133 const char message[] = "Send this round the relay!";
134 struct hf_vcpu_run_return run_res;
135 struct mailbox_buffers mb = set_up_mailbox();
136
137 SERVICE_SELECT(SERVICE_VM0, "relay", mb.send);
138 SERVICE_SELECT(SERVICE_VM1, "relay", mb.send);
139
140 run_res = hf_vcpu_run(SERVICE_VM0, 0);
141 EXPECT_EQ(run_res.code, HF_VCPU_RUN_WAIT_FOR_INTERRUPT);
142 run_res = hf_vcpu_run(SERVICE_VM1, 0);
143 EXPECT_EQ(run_res.code, HF_VCPU_RUN_WAIT_FOR_INTERRUPT);
144
145 /*
146 * Build the message chain so the message is sent from here to
147 * SERVICE_VM0, then to SERVICE_VM1 and finally back to here.
148 */
149 {
150 uint32_t *chain = mb.send;
151 *chain++ = htole32(SERVICE_VM1);
152 *chain++ = htole32(HF_PRIMARY_VM_ID);
153 memcpy(chain, message, sizeof(message));
154 EXPECT_EQ(hf_mailbox_send(
155 SERVICE_VM0,
156 sizeof(message) + (2 * sizeof(uint32_t)),
157 false),
158 0);
159 }
160
161 /* Let SERVICE_VM0 forward the message. */
162 run_res = hf_vcpu_run(SERVICE_VM0, 0);
163 EXPECT_EQ(run_res.code, HF_VCPU_RUN_WAKE_UP);
164 EXPECT_EQ(run_res.wake_up.vm_id, SERVICE_VM1);
165 EXPECT_EQ(run_res.wake_up.vcpu, 0);
166
167 /* Let SERVICE_VM1 forward the message. */
168 run_res = hf_vcpu_run(SERVICE_VM1, 0);
169 EXPECT_EQ(run_res.code, HF_VCPU_RUN_MESSAGE);
170
171 /* Ensure the message is in tact. */
172 EXPECT_EQ(run_res.message.size, sizeof(message));
173 EXPECT_EQ(memcmp(mb.recv, message, sizeof(message)), 0);
174 EXPECT_EQ(hf_mailbox_clear(), 0);
175}
176
177/**
178 * Send a message before the secondary VM is configured, but do not register
179 * for notification. Ensure we're not notified.
180 */
181TEST(mailbox, no_primary_to_secondary_notification_on_configure)
182{
183 struct hf_vcpu_run_return run_res;
184
185 set_up_mailbox();
186
187 EXPECT_EQ(hf_mailbox_send(SERVICE_VM0, 0, false), -1);
188
189 run_res = hf_vcpu_run(SERVICE_VM0, 0);
190 EXPECT_EQ(run_res.code, HF_VCPU_RUN_WAIT_FOR_INTERRUPT);
191
192 EXPECT_EQ(hf_mailbox_send(SERVICE_VM0, 0, false), 0);
193}
194
195/**
196 * Send a message before the secondary VM is configured, and receive a
197 * notification when it configures.
198 */
199TEST(mailbox, secondary_to_primary_notification_on_configure)
200{
201 struct hf_vcpu_run_return run_res;
202
203 set_up_mailbox();
204
205 EXPECT_EQ(hf_mailbox_send(SERVICE_VM0, 0, true), -1);
206
207 /*
208 * Run first VM for it to configure itself. It should result in
209 * notifications having to be issued.
210 */
211 run_res = hf_vcpu_run(SERVICE_VM0, 0);
212 EXPECT_EQ(run_res.code, HF_VCPU_RUN_NOTIFY_WAITERS);
213
214 /* A single waiter is returned. */
215 EXPECT_EQ(hf_mailbox_waiter_get(SERVICE_VM0), HF_PRIMARY_VM_ID);
216 EXPECT_EQ(hf_mailbox_waiter_get(SERVICE_VM0), -1);
217
218 /* Send should succeed now, though no vCPU is blocked waiting for it. */
219 EXPECT_EQ(hf_mailbox_send(SERVICE_VM0, 0, false), HF_INVALID_VCPU);
220}
221
222/**
223 * Causes secondary VM to send two messages to primary VM. The second message
224 * will reach the mailbox while it's not writable. Checks that notifications are
225 * properly delivered when mailbox is cleared.
226 */
227TEST(mailbox, primary_to_secondary)
228{
229 char message[] = "not ready echo";
230 struct hf_vcpu_run_return run_res;
231 struct mailbox_buffers mb = set_up_mailbox();
232
233 SERVICE_SELECT(SERVICE_VM0, "echo_with_notification", mb.send);
234
235 run_res = hf_vcpu_run(SERVICE_VM0, 0);
236 EXPECT_EQ(run_res.code, HF_VCPU_RUN_WAIT_FOR_INTERRUPT);
237
238 /* Send a message to echo service, and get response back. */
239 memcpy(mb.send, message, sizeof(message));
240 EXPECT_EQ(hf_mailbox_send(SERVICE_VM0, sizeof(message), false), 0);
241 run_res = hf_vcpu_run(SERVICE_VM0, 0);
242 EXPECT_EQ(run_res.code, HF_VCPU_RUN_MESSAGE);
243 EXPECT_EQ(run_res.message.size, sizeof(message));
244 EXPECT_EQ(memcmp(mb.recv, message, sizeof(message)), 0);
245
246 /* Let secondary VM continue running so that it will wait again. */
247 run_res = hf_vcpu_run(SERVICE_VM0, 0);
248 EXPECT_EQ(run_res.code, HF_VCPU_RUN_WAIT_FOR_INTERRUPT);
249
250 /* Without clearing our mailbox, send message again. */
251 reverse(message, strlen(message));
252 memcpy(mb.send, message, sizeof(message));
253 EXPECT_EQ(hf_mailbox_send(SERVICE_VM0, sizeof(message), false), 0);
254 run_res = hf_vcpu_run(SERVICE_VM0, 0);
255 EXPECT_EQ(run_res.code, HF_VCPU_RUN_WAIT_FOR_INTERRUPT);
256
257 /* Clear the mailbox. We expect to be told there are pending waiters. */
258 EXPECT_EQ(hf_mailbox_clear(), 1);
259
260 /* Retrieve a single waiter. */
261 EXPECT_EQ(hf_mailbox_waiter_get(HF_PRIMARY_VM_ID), SERVICE_VM0);
262 EXPECT_EQ(hf_mailbox_waiter_get(HF_PRIMARY_VM_ID), -1);
263
264 /*
265 * Inject interrupt into VM and let it run again. We should receive
266 * the echoed message.
267 */
268 EXPECT_EQ(
269 hf_interrupt_inject(SERVICE_VM0, 0, HF_MAILBOX_WRITABLE_INTID),
270 1);
271 run_res = hf_vcpu_run(SERVICE_VM0, 0);
272 EXPECT_EQ(run_res.code, HF_VCPU_RUN_MESSAGE);
273 EXPECT_EQ(run_res.message.size, sizeof(message));
274 EXPECT_EQ(memcmp(mb.recv, message, sizeof(message)), 0);
275}
276
277/**
278 * Sends two messages to secondary VM without letting it run, so second message
279 * won't go through. Ensure that a notification is delivered when secondary VM
280 * clears the mailbox.
281 */
282TEST(mailbox, secondary_to_primary_notification)
283{
284 const char message[] = "not ready echo";
285 struct hf_vcpu_run_return run_res;
286 struct mailbox_buffers mb = set_up_mailbox();
287
288 SERVICE_SELECT(SERVICE_VM0, "echo_with_notification", mb.send);
289
290 run_res = hf_vcpu_run(SERVICE_VM0, 0);
291 EXPECT_EQ(run_res.code, HF_VCPU_RUN_WAIT_FOR_INTERRUPT);
292
293 /* Send a message to echo service twice. The second should fail. */
294 memcpy(mb.send, message, sizeof(message));
295 EXPECT_EQ(hf_mailbox_send(SERVICE_VM0, sizeof(message), false), 0);
296 EXPECT_EQ(hf_mailbox_send(SERVICE_VM0, sizeof(message), true), -1);
297
298 /* Receive a reply for the first message. */
299 run_res = hf_vcpu_run(SERVICE_VM0, 0);
300 EXPECT_EQ(run_res.code, HF_VCPU_RUN_MESSAGE);
301 EXPECT_EQ(run_res.message.size, sizeof(message));
302 EXPECT_EQ(memcmp(mb.recv, message, sizeof(message)), 0);
303
304 /* Run VM again so that it clears its mailbox. */
305 run_res = hf_vcpu_run(SERVICE_VM0, 0);
306 EXPECT_EQ(run_res.code, HF_VCPU_RUN_NOTIFY_WAITERS);
307
308 /* Retrieve a single waiter. */
309 EXPECT_EQ(hf_mailbox_waiter_get(SERVICE_VM0), HF_PRIMARY_VM_ID);
310 EXPECT_EQ(hf_mailbox_waiter_get(SERVICE_VM0), -1);
311
312 /* Send should succeed now, though no vCPU is blocked waiting for it. */
313 EXPECT_EQ(hf_mailbox_send(SERVICE_VM0, 0, false), HF_INVALID_VCPU);
314}