blob: 655ca548c481903155e56c4f99f6c7849bf7b708 [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. */
45 memcpy(mb.send, message, sizeof(message));
46 EXPECT_EQ(hf_mailbox_send(SERVICE_VM0, sizeof(message), false), 0);
47 run_res = hf_vcpu_run(SERVICE_VM0, 0);
48 EXPECT_EQ(run_res.code, HF_VCPU_RUN_MESSAGE);
49 EXPECT_EQ(run_res.message.size, sizeof(expected_response));
50 EXPECT_EQ(memcmp(mb.recv, expected_response, sizeof(expected_response)),
51 0);
52 EXPECT_EQ(hf_mailbox_clear(), 0);
53}
54
55/**
56 * Inject an interrupt to the interrupt VM, which will send a message back.
57 * Repeat this twice to make sure it doesn't get into a bad state after the
58 * first one.
59 */
60TEST(interrupts, inject_interrupt_twice)
61{
62 const char expected_response[] = "Got IRQ 07.";
63 struct hf_vcpu_run_return run_res;
64 struct mailbox_buffers mb = set_up_mailbox();
65
66 SERVICE_SELECT(SERVICE_VM0, "interruptible", mb.send);
67
68 run_res = hf_vcpu_run(SERVICE_VM0, 0);
Andrew Scullb06d1752019-02-04 10:15:48 +000069 EXPECT_EQ(run_res.code, HF_VCPU_RUN_WAIT_FOR_MESSAGE);
70 EXPECT_EQ(run_res.sleep.ns, HF_SLEEP_INDEFINITE);
Andrew Scull2e7a76d2019-01-24 11:47:26 +000071
72 /* Inject the interrupt and wait for a message. */
73 hf_interrupt_inject(SERVICE_VM0, 0, EXTERNAL_INTERRUPT_ID_A);
74 run_res = hf_vcpu_run(SERVICE_VM0, 0);
75 EXPECT_EQ(run_res.code, HF_VCPU_RUN_MESSAGE);
76 EXPECT_EQ(run_res.message.size, sizeof(expected_response));
77 EXPECT_EQ(memcmp(mb.recv, expected_response, sizeof(expected_response)),
78 0);
79 EXPECT_EQ(hf_mailbox_clear(), 0);
80
81 /* Inject the interrupt again, and wait for the same message. */
82 hf_interrupt_inject(SERVICE_VM0, 0, EXTERNAL_INTERRUPT_ID_A);
83 run_res = hf_vcpu_run(SERVICE_VM0, 0);
84 EXPECT_EQ(run_res.code, HF_VCPU_RUN_MESSAGE);
85 EXPECT_EQ(run_res.message.size, sizeof(expected_response));
86 EXPECT_EQ(memcmp(mb.recv, expected_response, sizeof(expected_response)),
87 0);
88 EXPECT_EQ(hf_mailbox_clear(), 0);
89}
90
91/**
92 * Inject two different interrupts to the interrupt VM, which will send a
93 * message back each time.
94 */
95TEST(interrupts, inject_two_interrupts)
96{
97 const char expected_response[] = "Got IRQ 07.";
98 const char expected_response_2[] = "Got IRQ 08.";
99 struct hf_vcpu_run_return run_res;
100 struct mailbox_buffers mb = set_up_mailbox();
101
102 SERVICE_SELECT(SERVICE_VM0, "interruptible", mb.send);
103
104 run_res = hf_vcpu_run(SERVICE_VM0, 0);
Andrew Scullb06d1752019-02-04 10:15:48 +0000105 EXPECT_EQ(run_res.code, HF_VCPU_RUN_WAIT_FOR_MESSAGE);
106 EXPECT_EQ(run_res.sleep.ns, HF_SLEEP_INDEFINITE);
Andrew Scull2e7a76d2019-01-24 11:47:26 +0000107
108 /* Inject the interrupt and wait for a message. */
109 hf_interrupt_inject(SERVICE_VM0, 0, EXTERNAL_INTERRUPT_ID_A);
110 run_res = hf_vcpu_run(SERVICE_VM0, 0);
111 EXPECT_EQ(run_res.code, HF_VCPU_RUN_MESSAGE);
112 EXPECT_EQ(run_res.message.size, sizeof(expected_response));
113 EXPECT_EQ(memcmp(mb.recv, expected_response, sizeof(expected_response)),
114 0);
115 EXPECT_EQ(hf_mailbox_clear(), 0);
116
117 /* Inject a different interrupt and wait for a different message. */
118 hf_interrupt_inject(SERVICE_VM0, 0, EXTERNAL_INTERRUPT_ID_B);
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(expected_response_2));
122 EXPECT_EQ(memcmp(mb.recv, expected_response_2,
123 sizeof(expected_response_2)),
124 0);
125 EXPECT_EQ(hf_mailbox_clear(), 0);
126}
127
128/**
129 * Inject an interrupt then send a message to the interrupt VM, which will send
130 * a message back each time. This is to test that interrupt injection doesn't
131 * interfere with message reception.
132 */
133TEST(interrupts, inject_interrupt_message)
134{
135 const char expected_response[] = "Got IRQ 07.";
136 const char message[] = "Ping";
137 const char expected_response_2[] = "Got IRQ 05.";
138 struct hf_vcpu_run_return run_res;
139 struct mailbox_buffers mb = set_up_mailbox();
140
141 SERVICE_SELECT(SERVICE_VM0, "interruptible", mb.send);
142
143 run_res = hf_vcpu_run(SERVICE_VM0, 0);
Andrew Scullb06d1752019-02-04 10:15:48 +0000144 EXPECT_EQ(run_res.code, HF_VCPU_RUN_WAIT_FOR_MESSAGE);
145 EXPECT_EQ(run_res.sleep.ns, HF_SLEEP_INDEFINITE);
Andrew Scull2e7a76d2019-01-24 11:47:26 +0000146
147 /* Inject the interrupt and wait for a message. */
148 hf_interrupt_inject(SERVICE_VM0, 0, EXTERNAL_INTERRUPT_ID_A);
149 run_res = hf_vcpu_run(SERVICE_VM0, 0);
150 EXPECT_EQ(run_res.code, HF_VCPU_RUN_MESSAGE);
151 EXPECT_EQ(run_res.message.size, sizeof(expected_response));
152 EXPECT_EQ(memcmp(mb.recv, expected_response, sizeof(expected_response)),
153 0);
154 EXPECT_EQ(hf_mailbox_clear(), 0);
155
156 run_res = hf_vcpu_run(SERVICE_VM0, 0);
Andrew Scullb06d1752019-02-04 10:15:48 +0000157 EXPECT_EQ(run_res.code, HF_VCPU_RUN_WAIT_FOR_MESSAGE);
158 EXPECT_EQ(run_res.sleep.ns, HF_SLEEP_INDEFINITE);
Andrew Scull2e7a76d2019-01-24 11:47:26 +0000159
160 /* Now send a message to the secondary. */
161 memcpy(mb.send, message, sizeof(message));
162 EXPECT_EQ(hf_mailbox_send(SERVICE_VM0, sizeof(message), false), 0);
163 run_res = hf_vcpu_run(SERVICE_VM0, 0);
164 EXPECT_EQ(run_res.code, HF_VCPU_RUN_MESSAGE);
165 EXPECT_EQ(run_res.message.size, sizeof(expected_response_2));
166 EXPECT_EQ(memcmp(mb.recv, expected_response_2,
167 sizeof(expected_response_2)),
168 0);
169 EXPECT_EQ(hf_mailbox_clear(), 0);
170}
171
172/**
173 * Inject an interrupt which the target VM has not enabled, and then send a
174 * message telling it to enable that interrupt ID. It should then (and only
175 * then) send a message back.
176 */
177TEST(interrupts, inject_interrupt_disabled)
178{
179 const char expected_response[] = "Got IRQ 09.";
180 const char message[] = "Enable interrupt C";
181 struct hf_vcpu_run_return run_res;
182 struct mailbox_buffers mb = set_up_mailbox();
183
184 SERVICE_SELECT(SERVICE_VM0, "interruptible", mb.send);
185
186 /* Inject the interrupt and expect not to get a message. */
187 hf_interrupt_inject(SERVICE_VM0, 0, EXTERNAL_INTERRUPT_ID_C);
188 run_res = hf_vcpu_run(SERVICE_VM0, 0);
Andrew Scullb06d1752019-02-04 10:15:48 +0000189 EXPECT_EQ(run_res.code, HF_VCPU_RUN_WAIT_FOR_MESSAGE);
190 EXPECT_EQ(run_res.sleep.ns, HF_SLEEP_INDEFINITE);
Andrew Scull2e7a76d2019-01-24 11:47:26 +0000191
192 /*
193 * Now send a message to the secondary to enable the interrupt ID, and
194 * expect the response from the interrupt we sent before.
195 */
196 memcpy(mb.send, message, sizeof(message));
197 EXPECT_EQ(hf_mailbox_send(SERVICE_VM0, sizeof(message), false), 0);
198 run_res = hf_vcpu_run(SERVICE_VM0, 0);
199 EXPECT_EQ(run_res.code, HF_VCPU_RUN_MESSAGE);
200 EXPECT_EQ(run_res.message.size, sizeof(expected_response));
201 EXPECT_EQ(memcmp(mb.recv, expected_response, sizeof(expected_response)),
202 0);
203 EXPECT_EQ(hf_mailbox_clear(), 0);
204}
Andrew Walbran9311c9a2019-03-12 16:59:04 +0000205
206/**
207 * If a secondary VM has an enabled and pending interrupt, even if interrupts
208 * are disabled globally via PSTATE, then hf_mailbox_receive should not block
209 * even if `block` is true.
210 */
211TEST(interrupts, pending_interrupt_no_blocking_receive)
212{
213 const char expected_response[] = "Done waiting";
214 struct hf_vcpu_run_return run_res;
215 struct mailbox_buffers mb = set_up_mailbox();
216
217 SERVICE_SELECT(SERVICE_VM0, "receive_block", mb.send);
218
219 /*
220 * Inject the interrupt and run the VM. It should disable interrupts
221 * globally, enable the specific interrupt, and then send us a message
222 * back after failing to receive a message a few times.
223 */
224 hf_interrupt_inject(SERVICE_VM0, 0, EXTERNAL_INTERRUPT_ID_A);
225 run_res = hf_vcpu_run(SERVICE_VM0, 0);
226 EXPECT_EQ(run_res.code, HF_VCPU_RUN_MESSAGE);
227 EXPECT_EQ(run_res.message.size, sizeof(expected_response));
228 EXPECT_EQ(memcmp(mb.recv, expected_response, sizeof(expected_response)),
229 0);
230 EXPECT_EQ(hf_mailbox_clear(), 0);
231}
Andrew Walbran2d10e882019-03-14 18:17:42 +0000232
233/**
234 * If a secondary VM has an enabled and pending interrupt, even if interrupts
235 * are disabled globally via PSTATE, then WFI should be treated as a no-op and
236 * not return to the primary.
237 */
238TEST(interrupts, pending_interrupt_wfi_not_trapped)
239{
240 const char expected_response[] = "Done waiting";
241 struct hf_vcpu_run_return run_res;
242 struct mailbox_buffers mb = set_up_mailbox();
243
244 SERVICE_SELECT(SERVICE_VM0, "wfi", mb.send);
245
246 /*
247 * Inject the interrupt and run the VM. It should disable interrupts
248 * globally, enable the specific interrupt, and then send us a message
249 * back after running WFI a few times.
250 */
251 hf_interrupt_inject(SERVICE_VM0, 0, EXTERNAL_INTERRUPT_ID_A);
252 run_res = hf_vcpu_run(SERVICE_VM0, 0);
253 EXPECT_EQ(run_res.code, HF_VCPU_RUN_MESSAGE);
254 EXPECT_EQ(run_res.message.size, sizeof(expected_response));
255 EXPECT_EQ(memcmp(mb.recv, expected_response, sizeof(expected_response)),
256 0);
257 EXPECT_EQ(hf_mailbox_clear(), 0);
258}
Andrew Scullb06d1752019-02-04 10:15:48 +0000259
260/*
261 * Deliver an interrupt and a message to the same vCPU and check that both are
262 * delivered the next time the vCPU is run.
263 */
264TEST(interrupts, deliver_interrupt_and_message)
265{
266 const char message[] = "I\'ll see you again.";
267 struct hf_vcpu_run_return run_res;
268 struct mailbox_buffers mb = set_up_mailbox();
269
270 SERVICE_SELECT(SERVICE_VM0, "interruptible_echo", mb.send);
271
272 run_res = hf_vcpu_run(SERVICE_VM0, 0);
273 EXPECT_EQ(run_res.code, HF_VCPU_RUN_WAIT_FOR_MESSAGE);
274 EXPECT_EQ(run_res.sleep.ns, HF_SLEEP_INDEFINITE);
275
276 memcpy(mb.send, message, sizeof(message));
277 EXPECT_EQ(hf_mailbox_send(SERVICE_VM0, sizeof(message), false), 0);
278 hf_interrupt_inject(SERVICE_VM0, 0, EXTERNAL_INTERRUPT_ID_A);
279 run_res = hf_vcpu_run(SERVICE_VM0, 0);
280 EXPECT_EQ(run_res.code, HF_VCPU_RUN_MESSAGE);
281 EXPECT_EQ(run_res.message.size, sizeof(message));
282 EXPECT_EQ(memcmp(mb.recv, message, sizeof(message)), 0);
283 EXPECT_EQ(hf_mailbox_clear(), 0);
284}