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