blob: 9429acc8f85976ebc1b8d4d9edc0d8890b838617 [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
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. */
Jose Marinhoa1dfeda2019-02-27 16:46:03 +000045 memcpy(mb.send->payload, message, sizeof(message));
46 spci_message_init(mb.send, sizeof(message), SERVICE_VM0,
47 HF_PRIMARY_VM_ID);
48 EXPECT_EQ(spci_msg_send(0), 0);
Andrew Scull2e7a76d2019-01-24 11:47:26 +000049 run_res = hf_vcpu_run(SERVICE_VM0, 0);
50 EXPECT_EQ(run_res.code, HF_VCPU_RUN_MESSAGE);
51 EXPECT_EQ(run_res.message.size, sizeof(expected_response));
Jose Marinhoa1dfeda2019-02-27 16:46:03 +000052 EXPECT_EQ(memcmp(mb.recv->payload, expected_response,
53 sizeof(expected_response)),
Andrew Scull2e7a76d2019-01-24 11:47:26 +000054 0);
55 EXPECT_EQ(hf_mailbox_clear(), 0);
56}
57
58/**
59 * Inject an interrupt to the interrupt VM, which will send a message back.
60 * Repeat this twice to make sure it doesn't get into a bad state after the
61 * first one.
62 */
63TEST(interrupts, inject_interrupt_twice)
64{
65 const char expected_response[] = "Got IRQ 07.";
66 struct hf_vcpu_run_return run_res;
67 struct mailbox_buffers mb = set_up_mailbox();
68
69 SERVICE_SELECT(SERVICE_VM0, "interruptible", mb.send);
70
71 run_res = hf_vcpu_run(SERVICE_VM0, 0);
Andrew Scullb06d1752019-02-04 10:15:48 +000072 EXPECT_EQ(run_res.code, HF_VCPU_RUN_WAIT_FOR_MESSAGE);
73 EXPECT_EQ(run_res.sleep.ns, HF_SLEEP_INDEFINITE);
Andrew Scull2e7a76d2019-01-24 11:47:26 +000074
75 /* Inject the interrupt and wait for a message. */
76 hf_interrupt_inject(SERVICE_VM0, 0, EXTERNAL_INTERRUPT_ID_A);
77 run_res = hf_vcpu_run(SERVICE_VM0, 0);
78 EXPECT_EQ(run_res.code, HF_VCPU_RUN_MESSAGE);
79 EXPECT_EQ(run_res.message.size, sizeof(expected_response));
Jose Marinhoa1dfeda2019-02-27 16:46:03 +000080 EXPECT_EQ(memcmp(mb.recv->payload, expected_response,
81 sizeof(expected_response)),
Andrew Scull2e7a76d2019-01-24 11:47:26 +000082 0);
83 EXPECT_EQ(hf_mailbox_clear(), 0);
84
85 /* Inject the interrupt again, and wait for the same message. */
86 hf_interrupt_inject(SERVICE_VM0, 0, EXTERNAL_INTERRUPT_ID_A);
87 run_res = hf_vcpu_run(SERVICE_VM0, 0);
88 EXPECT_EQ(run_res.code, HF_VCPU_RUN_MESSAGE);
89 EXPECT_EQ(run_res.message.size, sizeof(expected_response));
Jose Marinhoa1dfeda2019-02-27 16:46:03 +000090 EXPECT_EQ(memcmp(mb.recv->payload, expected_response,
91 sizeof(expected_response)),
Andrew Scull2e7a76d2019-01-24 11:47:26 +000092 0);
93 EXPECT_EQ(hf_mailbox_clear(), 0);
94}
95
96/**
97 * Inject two different interrupts to the interrupt VM, which will send a
98 * message back each time.
99 */
100TEST(interrupts, inject_two_interrupts)
101{
102 const char expected_response[] = "Got IRQ 07.";
103 const char expected_response_2[] = "Got IRQ 08.";
104 struct hf_vcpu_run_return run_res;
105 struct mailbox_buffers mb = set_up_mailbox();
106
107 SERVICE_SELECT(SERVICE_VM0, "interruptible", mb.send);
108
109 run_res = hf_vcpu_run(SERVICE_VM0, 0);
Andrew Scullb06d1752019-02-04 10:15:48 +0000110 EXPECT_EQ(run_res.code, HF_VCPU_RUN_WAIT_FOR_MESSAGE);
111 EXPECT_EQ(run_res.sleep.ns, HF_SLEEP_INDEFINITE);
Andrew Scull2e7a76d2019-01-24 11:47:26 +0000112
113 /* Inject the interrupt and wait for a message. */
114 hf_interrupt_inject(SERVICE_VM0, 0, EXTERNAL_INTERRUPT_ID_A);
115 run_res = hf_vcpu_run(SERVICE_VM0, 0);
116 EXPECT_EQ(run_res.code, HF_VCPU_RUN_MESSAGE);
117 EXPECT_EQ(run_res.message.size, sizeof(expected_response));
Jose Marinhoa1dfeda2019-02-27 16:46:03 +0000118 EXPECT_EQ(memcmp(mb.recv->payload, expected_response,
119 sizeof(expected_response)),
Andrew Scull2e7a76d2019-01-24 11:47:26 +0000120 0);
121 EXPECT_EQ(hf_mailbox_clear(), 0);
122
123 /* Inject a different interrupt and wait for a different message. */
124 hf_interrupt_inject(SERVICE_VM0, 0, EXTERNAL_INTERRUPT_ID_B);
125 run_res = hf_vcpu_run(SERVICE_VM0, 0);
126 EXPECT_EQ(run_res.code, HF_VCPU_RUN_MESSAGE);
127 EXPECT_EQ(run_res.message.size, sizeof(expected_response_2));
Jose Marinhoa1dfeda2019-02-27 16:46:03 +0000128 EXPECT_EQ(memcmp(mb.recv->payload, expected_response_2,
Andrew Scull2e7a76d2019-01-24 11:47:26 +0000129 sizeof(expected_response_2)),
130 0);
131 EXPECT_EQ(hf_mailbox_clear(), 0);
132}
133
134/**
135 * Inject an interrupt then send a message to the interrupt VM, which will send
136 * a message back each time. This is to test that interrupt injection doesn't
137 * interfere with message reception.
138 */
139TEST(interrupts, inject_interrupt_message)
140{
141 const char expected_response[] = "Got IRQ 07.";
142 const char message[] = "Ping";
143 const char expected_response_2[] = "Got IRQ 05.";
144 struct hf_vcpu_run_return run_res;
145 struct mailbox_buffers mb = set_up_mailbox();
146
147 SERVICE_SELECT(SERVICE_VM0, "interruptible", 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
153 /* Inject the interrupt and wait for a message. */
154 hf_interrupt_inject(SERVICE_VM0, 0, EXTERNAL_INTERRUPT_ID_A);
155 run_res = hf_vcpu_run(SERVICE_VM0, 0);
156 EXPECT_EQ(run_res.code, HF_VCPU_RUN_MESSAGE);
157 EXPECT_EQ(run_res.message.size, sizeof(expected_response));
Jose Marinhoa1dfeda2019-02-27 16:46:03 +0000158 EXPECT_EQ(memcmp(mb.recv->payload, expected_response,
159 sizeof(expected_response)),
Andrew Scull2e7a76d2019-01-24 11:47:26 +0000160 0);
161 EXPECT_EQ(hf_mailbox_clear(), 0);
162
163 run_res = hf_vcpu_run(SERVICE_VM0, 0);
Andrew Scullb06d1752019-02-04 10:15:48 +0000164 EXPECT_EQ(run_res.code, HF_VCPU_RUN_WAIT_FOR_MESSAGE);
165 EXPECT_EQ(run_res.sleep.ns, HF_SLEEP_INDEFINITE);
Andrew Scull2e7a76d2019-01-24 11:47:26 +0000166
167 /* Now send a message to the secondary. */
Jose Marinhoa1dfeda2019-02-27 16:46:03 +0000168 memcpy(mb.send->payload, message, sizeof(message));
169 spci_message_init(mb.send, sizeof(message), SERVICE_VM0,
170 HF_PRIMARY_VM_ID);
171 EXPECT_EQ(spci_msg_send(0), 0);
Andrew Scull2e7a76d2019-01-24 11:47:26 +0000172 run_res = hf_vcpu_run(SERVICE_VM0, 0);
173 EXPECT_EQ(run_res.code, HF_VCPU_RUN_MESSAGE);
174 EXPECT_EQ(run_res.message.size, sizeof(expected_response_2));
Jose Marinhoa1dfeda2019-02-27 16:46:03 +0000175 EXPECT_EQ(memcmp(mb.recv->payload, expected_response_2,
Andrew Scull2e7a76d2019-01-24 11:47:26 +0000176 sizeof(expected_response_2)),
177 0);
178 EXPECT_EQ(hf_mailbox_clear(), 0);
179}
180
181/**
182 * Inject an interrupt which the target VM has not enabled, and then send a
183 * message telling it to enable that interrupt ID. It should then (and only
184 * then) send a message back.
185 */
186TEST(interrupts, inject_interrupt_disabled)
187{
188 const char expected_response[] = "Got IRQ 09.";
189 const char message[] = "Enable interrupt C";
190 struct hf_vcpu_run_return run_res;
191 struct mailbox_buffers mb = set_up_mailbox();
192
193 SERVICE_SELECT(SERVICE_VM0, "interruptible", mb.send);
194
195 /* Inject the interrupt and expect not to get a message. */
196 hf_interrupt_inject(SERVICE_VM0, 0, EXTERNAL_INTERRUPT_ID_C);
197 run_res = hf_vcpu_run(SERVICE_VM0, 0);
Andrew Scullb06d1752019-02-04 10:15:48 +0000198 EXPECT_EQ(run_res.code, HF_VCPU_RUN_WAIT_FOR_MESSAGE);
199 EXPECT_EQ(run_res.sleep.ns, HF_SLEEP_INDEFINITE);
Andrew Scull2e7a76d2019-01-24 11:47:26 +0000200
201 /*
202 * Now send a message to the secondary to enable the interrupt ID, and
203 * expect the response from the interrupt we sent before.
204 */
Jose Marinhoa1dfeda2019-02-27 16:46:03 +0000205 memcpy(mb.send->payload, message, sizeof(message));
206 spci_message_init(mb.send, sizeof(message), SERVICE_VM0,
207 HF_PRIMARY_VM_ID);
208 EXPECT_EQ(spci_msg_send(0), 0);
Andrew Scull2e7a76d2019-01-24 11:47:26 +0000209 run_res = hf_vcpu_run(SERVICE_VM0, 0);
210 EXPECT_EQ(run_res.code, HF_VCPU_RUN_MESSAGE);
211 EXPECT_EQ(run_res.message.size, sizeof(expected_response));
Jose Marinhoa1dfeda2019-02-27 16:46:03 +0000212 EXPECT_EQ(memcmp(mb.recv->payload, expected_response,
213 sizeof(expected_response)),
Andrew Scull2e7a76d2019-01-24 11:47:26 +0000214 0);
215 EXPECT_EQ(hf_mailbox_clear(), 0);
216}
Andrew Walbran9311c9a2019-03-12 16:59:04 +0000217
218/**
219 * If a secondary VM has an enabled and pending interrupt, even if interrupts
220 * are disabled globally via PSTATE, then hf_mailbox_receive should not block
221 * even if `block` is true.
222 */
223TEST(interrupts, pending_interrupt_no_blocking_receive)
224{
225 const char expected_response[] = "Done waiting";
226 struct hf_vcpu_run_return run_res;
227 struct mailbox_buffers mb = set_up_mailbox();
228
229 SERVICE_SELECT(SERVICE_VM0, "receive_block", mb.send);
230
231 /*
232 * Inject the interrupt and run the VM. It should disable interrupts
233 * globally, enable the specific interrupt, and then send us a message
234 * back after failing to receive a message a few times.
235 */
236 hf_interrupt_inject(SERVICE_VM0, 0, EXTERNAL_INTERRUPT_ID_A);
237 run_res = hf_vcpu_run(SERVICE_VM0, 0);
238 EXPECT_EQ(run_res.code, HF_VCPU_RUN_MESSAGE);
239 EXPECT_EQ(run_res.message.size, sizeof(expected_response));
Jose Marinhoa1dfeda2019-02-27 16:46:03 +0000240 EXPECT_EQ(memcmp(mb.recv->payload, expected_response,
241 sizeof(expected_response)),
Andrew Walbran9311c9a2019-03-12 16:59:04 +0000242 0);
243 EXPECT_EQ(hf_mailbox_clear(), 0);
244}
Andrew Walbran2d10e882019-03-14 18:17:42 +0000245
246/**
247 * If a secondary VM has an enabled and pending interrupt, even if interrupts
248 * are disabled globally via PSTATE, then WFI should be treated as a no-op and
249 * not return to the primary.
250 */
251TEST(interrupts, pending_interrupt_wfi_not_trapped)
252{
253 const char expected_response[] = "Done waiting";
254 struct hf_vcpu_run_return run_res;
255 struct mailbox_buffers mb = set_up_mailbox();
256
257 SERVICE_SELECT(SERVICE_VM0, "wfi", mb.send);
258
259 /*
260 * Inject the interrupt and run the VM. It should disable interrupts
261 * globally, enable the specific interrupt, and then send us a message
262 * back after running WFI a few times.
263 */
264 hf_interrupt_inject(SERVICE_VM0, 0, EXTERNAL_INTERRUPT_ID_A);
265 run_res = hf_vcpu_run(SERVICE_VM0, 0);
266 EXPECT_EQ(run_res.code, HF_VCPU_RUN_MESSAGE);
267 EXPECT_EQ(run_res.message.size, sizeof(expected_response));
Jose Marinhoa1dfeda2019-02-27 16:46:03 +0000268 EXPECT_EQ(memcmp(mb.recv->payload, expected_response,
269 sizeof(expected_response)),
Andrew Walbran2d10e882019-03-14 18:17:42 +0000270 0);
271 EXPECT_EQ(hf_mailbox_clear(), 0);
272}
Andrew Scullb06d1752019-02-04 10:15:48 +0000273
274/*
275 * Deliver an interrupt and a message to the same vCPU and check that both are
276 * delivered the next time the vCPU is run.
277 */
278TEST(interrupts, deliver_interrupt_and_message)
279{
280 const char message[] = "I\'ll see you again.";
281 struct hf_vcpu_run_return run_res;
282 struct mailbox_buffers mb = set_up_mailbox();
283
284 SERVICE_SELECT(SERVICE_VM0, "interruptible_echo", mb.send);
285
286 run_res = hf_vcpu_run(SERVICE_VM0, 0);
287 EXPECT_EQ(run_res.code, HF_VCPU_RUN_WAIT_FOR_MESSAGE);
288 EXPECT_EQ(run_res.sleep.ns, HF_SLEEP_INDEFINITE);
289
Jose Marinhoa1dfeda2019-02-27 16:46:03 +0000290 memcpy(mb.send->payload, message, sizeof(message));
291 spci_message_init(mb.send, sizeof(message), SERVICE_VM0,
292 HF_PRIMARY_VM_ID);
293 EXPECT_EQ(spci_msg_send(0), SPCI_SUCCESS);
Andrew Scullb06d1752019-02-04 10:15:48 +0000294 hf_interrupt_inject(SERVICE_VM0, 0, EXTERNAL_INTERRUPT_ID_A);
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 Scullb06d1752019-02-04 10:15:48 +0000299 EXPECT_EQ(hf_mailbox_clear(), 0);
300}