blob: ae081d4326134c7a9322915c45cbfb1cbf8ce0dc [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 Scull8d9e1212019-04-05 13:52:55 +010019#include "hf/std.h"
Andrew Scull2e7a76d2019-01-24 11:47:26 +000020
21#include "vmapi/hf/call.h"
22
23#include "hftest.h"
24#include "primary_with_secondary.h"
Andrew Walbran53d9d042019-03-28 11:35:49 +000025#include "util.h"
Andrew Scull2e7a76d2019-01-24 11:47:26 +000026
27/**
28 * Send a message to the interruptible VM, which will interrupt itself to send a
29 * response back.
30 */
31TEST(interrupts, interrupt_self)
32{
33 const char message[] = "Ping";
34 const char expected_response[] = "Got IRQ 05.";
35 struct hf_vcpu_run_return run_res;
36 struct mailbox_buffers mb = set_up_mailbox();
37
38 SERVICE_SELECT(SERVICE_VM0, "interruptible", mb.send);
39
40 run_res = hf_vcpu_run(SERVICE_VM0, 0);
Andrew Scullb06d1752019-02-04 10:15:48 +000041 EXPECT_EQ(run_res.code, HF_VCPU_RUN_WAIT_FOR_MESSAGE);
42 EXPECT_EQ(run_res.sleep.ns, HF_SLEEP_INDEFINITE);
Andrew Scull2e7a76d2019-01-24 11:47:26 +000043
44 /* Set the message, echo it and wait for a response. */
Andrew Sculla1aa2ba2019-04-05 11:49:02 +010045 memcpy_s(mb.send->payload, SPCI_MSG_PAYLOAD_MAX, message,
46 sizeof(message));
Jose Marinhoa1dfeda2019-02-27 16:46:03 +000047 spci_message_init(mb.send, sizeof(message), SERVICE_VM0,
48 HF_PRIMARY_VM_ID);
49 EXPECT_EQ(spci_msg_send(0), 0);
Andrew Scull2e7a76d2019-01-24 11:47:26 +000050 run_res = hf_vcpu_run(SERVICE_VM0, 0);
51 EXPECT_EQ(run_res.code, HF_VCPU_RUN_MESSAGE);
Andrew Walbranf1bd6322019-10-03 16:45:11 +010052 EXPECT_EQ(run_res.message.size, sizeof(expected_response));
Jose Marinhoa1dfeda2019-02-27 16:46:03 +000053 EXPECT_EQ(memcmp(mb.recv->payload, expected_response,
54 sizeof(expected_response)),
Andrew Scull2e7a76d2019-01-24 11:47:26 +000055 0);
56 EXPECT_EQ(hf_mailbox_clear(), 0);
57}
58
59/**
60 * Inject an interrupt to the interrupt VM, which will send a message back.
61 * Repeat this twice to make sure it doesn't get into a bad state after the
62 * first one.
63 */
64TEST(interrupts, inject_interrupt_twice)
65{
66 const char expected_response[] = "Got IRQ 07.";
67 struct hf_vcpu_run_return run_res;
68 struct mailbox_buffers mb = set_up_mailbox();
69
70 SERVICE_SELECT(SERVICE_VM0, "interruptible", mb.send);
71
72 run_res = hf_vcpu_run(SERVICE_VM0, 0);
Andrew Scullb06d1752019-02-04 10:15:48 +000073 EXPECT_EQ(run_res.code, HF_VCPU_RUN_WAIT_FOR_MESSAGE);
74 EXPECT_EQ(run_res.sleep.ns, HF_SLEEP_INDEFINITE);
Andrew Scull2e7a76d2019-01-24 11:47:26 +000075
76 /* Inject the interrupt and wait for a message. */
77 hf_interrupt_inject(SERVICE_VM0, 0, EXTERNAL_INTERRUPT_ID_A);
78 run_res = hf_vcpu_run(SERVICE_VM0, 0);
79 EXPECT_EQ(run_res.code, HF_VCPU_RUN_MESSAGE);
Andrew Walbranf1bd6322019-10-03 16:45:11 +010080 EXPECT_EQ(run_res.message.size, sizeof(expected_response));
Jose Marinhoa1dfeda2019-02-27 16:46:03 +000081 EXPECT_EQ(memcmp(mb.recv->payload, expected_response,
82 sizeof(expected_response)),
Andrew Scull2e7a76d2019-01-24 11:47:26 +000083 0);
84 EXPECT_EQ(hf_mailbox_clear(), 0);
85
86 /* Inject the interrupt again, and wait for the same message. */
87 hf_interrupt_inject(SERVICE_VM0, 0, EXTERNAL_INTERRUPT_ID_A);
88 run_res = hf_vcpu_run(SERVICE_VM0, 0);
89 EXPECT_EQ(run_res.code, HF_VCPU_RUN_MESSAGE);
Andrew Walbranf1bd6322019-10-03 16:45:11 +010090 EXPECT_EQ(run_res.message.size, sizeof(expected_response));
Jose Marinhoa1dfeda2019-02-27 16:46:03 +000091 EXPECT_EQ(memcmp(mb.recv->payload, expected_response,
92 sizeof(expected_response)),
Andrew Scull2e7a76d2019-01-24 11:47:26 +000093 0);
94 EXPECT_EQ(hf_mailbox_clear(), 0);
95}
96
97/**
98 * Inject two different interrupts to the interrupt VM, which will send a
99 * message back each time.
100 */
101TEST(interrupts, inject_two_interrupts)
102{
103 const char expected_response[] = "Got IRQ 07.";
104 const char expected_response_2[] = "Got IRQ 08.";
105 struct hf_vcpu_run_return run_res;
106 struct mailbox_buffers mb = set_up_mailbox();
107
108 SERVICE_SELECT(SERVICE_VM0, "interruptible", mb.send);
109
110 run_res = hf_vcpu_run(SERVICE_VM0, 0);
Andrew Scullb06d1752019-02-04 10:15:48 +0000111 EXPECT_EQ(run_res.code, HF_VCPU_RUN_WAIT_FOR_MESSAGE);
112 EXPECT_EQ(run_res.sleep.ns, HF_SLEEP_INDEFINITE);
Andrew Scull2e7a76d2019-01-24 11:47:26 +0000113
114 /* Inject the interrupt and wait for a message. */
115 hf_interrupt_inject(SERVICE_VM0, 0, EXTERNAL_INTERRUPT_ID_A);
116 run_res = hf_vcpu_run(SERVICE_VM0, 0);
117 EXPECT_EQ(run_res.code, HF_VCPU_RUN_MESSAGE);
Andrew Walbranf1bd6322019-10-03 16:45:11 +0100118 EXPECT_EQ(run_res.message.size, sizeof(expected_response));
Jose Marinhoa1dfeda2019-02-27 16:46:03 +0000119 EXPECT_EQ(memcmp(mb.recv->payload, expected_response,
120 sizeof(expected_response)),
Andrew Scull2e7a76d2019-01-24 11:47:26 +0000121 0);
122 EXPECT_EQ(hf_mailbox_clear(), 0);
123
124 /* Inject a different interrupt and wait for a different message. */
125 hf_interrupt_inject(SERVICE_VM0, 0, EXTERNAL_INTERRUPT_ID_B);
126 run_res = hf_vcpu_run(SERVICE_VM0, 0);
127 EXPECT_EQ(run_res.code, HF_VCPU_RUN_MESSAGE);
Andrew Walbranf1bd6322019-10-03 16:45:11 +0100128 EXPECT_EQ(run_res.message.size, sizeof(expected_response_2));
Jose Marinhoa1dfeda2019-02-27 16:46:03 +0000129 EXPECT_EQ(memcmp(mb.recv->payload, expected_response_2,
Andrew Scull2e7a76d2019-01-24 11:47:26 +0000130 sizeof(expected_response_2)),
131 0);
132 EXPECT_EQ(hf_mailbox_clear(), 0);
133}
134
135/**
136 * Inject an interrupt then send a message to the interrupt VM, which will send
137 * a message back each time. This is to test that interrupt injection doesn't
138 * interfere with message reception.
139 */
140TEST(interrupts, inject_interrupt_message)
141{
142 const char expected_response[] = "Got IRQ 07.";
143 const char message[] = "Ping";
144 const char expected_response_2[] = "Got IRQ 05.";
145 struct hf_vcpu_run_return run_res;
146 struct mailbox_buffers mb = set_up_mailbox();
147
148 SERVICE_SELECT(SERVICE_VM0, "interruptible", 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
154 /* Inject the interrupt and wait for a message. */
155 hf_interrupt_inject(SERVICE_VM0, 0, EXTERNAL_INTERRUPT_ID_A);
156 run_res = hf_vcpu_run(SERVICE_VM0, 0);
157 EXPECT_EQ(run_res.code, HF_VCPU_RUN_MESSAGE);
Andrew Walbranf1bd6322019-10-03 16:45:11 +0100158 EXPECT_EQ(run_res.message.size, sizeof(expected_response));
Jose Marinhoa1dfeda2019-02-27 16:46:03 +0000159 EXPECT_EQ(memcmp(mb.recv->payload, expected_response,
160 sizeof(expected_response)),
Andrew Scull2e7a76d2019-01-24 11:47:26 +0000161 0);
162 EXPECT_EQ(hf_mailbox_clear(), 0);
163
164 run_res = hf_vcpu_run(SERVICE_VM0, 0);
Andrew Scullb06d1752019-02-04 10:15:48 +0000165 EXPECT_EQ(run_res.code, HF_VCPU_RUN_WAIT_FOR_MESSAGE);
166 EXPECT_EQ(run_res.sleep.ns, HF_SLEEP_INDEFINITE);
Andrew Scull2e7a76d2019-01-24 11:47:26 +0000167
168 /* Now send a message to the secondary. */
Andrew Sculla1aa2ba2019-04-05 11:49:02 +0100169 memcpy_s(mb.send->payload, SPCI_MSG_PAYLOAD_MAX, message,
170 sizeof(message));
Jose Marinhoa1dfeda2019-02-27 16:46:03 +0000171 spci_message_init(mb.send, sizeof(message), SERVICE_VM0,
172 HF_PRIMARY_VM_ID);
173 EXPECT_EQ(spci_msg_send(0), 0);
Andrew Scull2e7a76d2019-01-24 11:47:26 +0000174 run_res = hf_vcpu_run(SERVICE_VM0, 0);
175 EXPECT_EQ(run_res.code, HF_VCPU_RUN_MESSAGE);
Andrew Walbranf1bd6322019-10-03 16:45:11 +0100176 EXPECT_EQ(run_res.message.size, sizeof(expected_response_2));
Jose Marinhoa1dfeda2019-02-27 16:46:03 +0000177 EXPECT_EQ(memcmp(mb.recv->payload, expected_response_2,
Andrew Scull2e7a76d2019-01-24 11:47:26 +0000178 sizeof(expected_response_2)),
179 0);
180 EXPECT_EQ(hf_mailbox_clear(), 0);
181}
182
183/**
184 * Inject an interrupt which the target VM has not enabled, and then send a
185 * message telling it to enable that interrupt ID. It should then (and only
186 * then) send a message back.
187 */
188TEST(interrupts, inject_interrupt_disabled)
189{
190 const char expected_response[] = "Got IRQ 09.";
191 const char message[] = "Enable interrupt C";
192 struct hf_vcpu_run_return run_res;
193 struct mailbox_buffers mb = set_up_mailbox();
194
195 SERVICE_SELECT(SERVICE_VM0, "interruptible", mb.send);
196
197 /* Inject the interrupt and expect not to get a message. */
198 hf_interrupt_inject(SERVICE_VM0, 0, EXTERNAL_INTERRUPT_ID_C);
199 run_res = hf_vcpu_run(SERVICE_VM0, 0);
Andrew Scullb06d1752019-02-04 10:15:48 +0000200 EXPECT_EQ(run_res.code, HF_VCPU_RUN_WAIT_FOR_MESSAGE);
201 EXPECT_EQ(run_res.sleep.ns, HF_SLEEP_INDEFINITE);
Andrew Scull2e7a76d2019-01-24 11:47:26 +0000202
203 /*
204 * Now send a message to the secondary to enable the interrupt ID, and
205 * expect the response from the interrupt we sent before.
206 */
Andrew Sculla1aa2ba2019-04-05 11:49:02 +0100207 memcpy_s(mb.send->payload, SPCI_MSG_PAYLOAD_MAX, message,
208 sizeof(message));
Jose Marinhoa1dfeda2019-02-27 16:46:03 +0000209 spci_message_init(mb.send, sizeof(message), SERVICE_VM0,
210 HF_PRIMARY_VM_ID);
211 EXPECT_EQ(spci_msg_send(0), 0);
Andrew Scull2e7a76d2019-01-24 11:47:26 +0000212 run_res = hf_vcpu_run(SERVICE_VM0, 0);
213 EXPECT_EQ(run_res.code, HF_VCPU_RUN_MESSAGE);
Andrew Walbranf1bd6322019-10-03 16:45:11 +0100214 EXPECT_EQ(run_res.message.size, sizeof(expected_response));
Jose Marinhoa1dfeda2019-02-27 16:46:03 +0000215 EXPECT_EQ(memcmp(mb.recv->payload, expected_response,
216 sizeof(expected_response)),
Andrew Scull2e7a76d2019-01-24 11:47:26 +0000217 0);
218 EXPECT_EQ(hf_mailbox_clear(), 0);
219}
Andrew Walbran9311c9a2019-03-12 16:59:04 +0000220
221/**
222 * If a secondary VM has an enabled and pending interrupt, even if interrupts
223 * are disabled globally via PSTATE, then hf_mailbox_receive should not block
224 * even if `block` is true.
225 */
226TEST(interrupts, pending_interrupt_no_blocking_receive)
227{
228 const char expected_response[] = "Done waiting";
229 struct hf_vcpu_run_return run_res;
230 struct mailbox_buffers mb = set_up_mailbox();
231
232 SERVICE_SELECT(SERVICE_VM0, "receive_block", mb.send);
233
234 /*
235 * Inject the interrupt and run the VM. It should disable interrupts
236 * globally, enable the specific interrupt, and then send us a message
237 * back after failing to receive a message a few times.
238 */
239 hf_interrupt_inject(SERVICE_VM0, 0, EXTERNAL_INTERRUPT_ID_A);
240 run_res = hf_vcpu_run(SERVICE_VM0, 0);
241 EXPECT_EQ(run_res.code, HF_VCPU_RUN_MESSAGE);
Andrew Walbranf1bd6322019-10-03 16:45:11 +0100242 EXPECT_EQ(run_res.message.size, sizeof(expected_response));
Jose Marinhoa1dfeda2019-02-27 16:46:03 +0000243 EXPECT_EQ(memcmp(mb.recv->payload, expected_response,
244 sizeof(expected_response)),
Andrew Walbran9311c9a2019-03-12 16:59:04 +0000245 0);
246 EXPECT_EQ(hf_mailbox_clear(), 0);
247}
Andrew Walbran2d10e882019-03-14 18:17:42 +0000248
249/**
250 * If a secondary VM has an enabled and pending interrupt, even if interrupts
251 * are disabled globally via PSTATE, then WFI should be treated as a no-op and
252 * not return to the primary.
253 */
254TEST(interrupts, pending_interrupt_wfi_not_trapped)
255{
256 const char expected_response[] = "Done waiting";
257 struct hf_vcpu_run_return run_res;
258 struct mailbox_buffers mb = set_up_mailbox();
259
260 SERVICE_SELECT(SERVICE_VM0, "wfi", mb.send);
261
262 /*
263 * Inject the interrupt and run the VM. It should disable interrupts
264 * globally, enable the specific interrupt, and then send us a message
265 * back after running WFI a few times.
266 */
267 hf_interrupt_inject(SERVICE_VM0, 0, EXTERNAL_INTERRUPT_ID_A);
268 run_res = hf_vcpu_run(SERVICE_VM0, 0);
269 EXPECT_EQ(run_res.code, HF_VCPU_RUN_MESSAGE);
Andrew Walbranf1bd6322019-10-03 16:45:11 +0100270 EXPECT_EQ(run_res.message.size, sizeof(expected_response));
Jose Marinhoa1dfeda2019-02-27 16:46:03 +0000271 EXPECT_EQ(memcmp(mb.recv->payload, expected_response,
272 sizeof(expected_response)),
Andrew Walbran2d10e882019-03-14 18:17:42 +0000273 0);
274 EXPECT_EQ(hf_mailbox_clear(), 0);
275}
Andrew Scullb06d1752019-02-04 10:15:48 +0000276
277/*
278 * Deliver an interrupt and a message to the same vCPU and check that both are
279 * delivered the next time the vCPU is run.
280 */
281TEST(interrupts, deliver_interrupt_and_message)
282{
283 const char message[] = "I\'ll see you again.";
284 struct hf_vcpu_run_return run_res;
285 struct mailbox_buffers mb = set_up_mailbox();
286
287 SERVICE_SELECT(SERVICE_VM0, "interruptible_echo", mb.send);
288
289 run_res = hf_vcpu_run(SERVICE_VM0, 0);
290 EXPECT_EQ(run_res.code, HF_VCPU_RUN_WAIT_FOR_MESSAGE);
291 EXPECT_EQ(run_res.sleep.ns, HF_SLEEP_INDEFINITE);
292
Andrew Sculla1aa2ba2019-04-05 11:49:02 +0100293 memcpy_s(mb.send->payload, SPCI_MSG_PAYLOAD_MAX, message,
294 sizeof(message));
Jose Marinhoa1dfeda2019-02-27 16:46:03 +0000295 spci_message_init(mb.send, sizeof(message), SERVICE_VM0,
296 HF_PRIMARY_VM_ID);
297 EXPECT_EQ(spci_msg_send(0), SPCI_SUCCESS);
Andrew Scullb06d1752019-02-04 10:15:48 +0000298 hf_interrupt_inject(SERVICE_VM0, 0, EXTERNAL_INTERRUPT_ID_A);
299 run_res = hf_vcpu_run(SERVICE_VM0, 0);
300 EXPECT_EQ(run_res.code, HF_VCPU_RUN_MESSAGE);
Andrew Walbranf1bd6322019-10-03 16:45:11 +0100301 EXPECT_EQ(run_res.message.size, sizeof(message));
Jose Marinhoa1dfeda2019-02-27 16:46:03 +0000302 EXPECT_EQ(memcmp(mb.recv->payload, message, sizeof(message)), 0);
Andrew Scullb06d1752019-02-04 10:15:48 +0000303 EXPECT_EQ(hf_mailbox_clear(), 0);
304}