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