blob: 31db0b02f5ca701364ec1ad7bc9be58762528694 [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.";
David Brazdilb2bfcd42019-11-01 16:26:44 +000035 struct hf_vcpu_run_return run_res;
Andrew Scull2e7a76d2019-01-24 11:47:26 +000036 struct mailbox_buffers mb = set_up_mailbox();
37
38 SERVICE_SELECT(SERVICE_VM0, "interruptible", mb.send);
39
David Brazdilb2bfcd42019-11-01 16:26:44 +000040 run_res = hf_vcpu_run(SERVICE_VM0, 0);
41 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 Walbran70bc8622019-10-07 14:15:58 +010045 memcpy_s(mb.send, SPCI_MSG_PAYLOAD_MAX, message, sizeof(message));
46 EXPECT_EQ(
47 spci_msg_send(HF_PRIMARY_VM_ID, SERVICE_VM0, sizeof(message), 0)
48 .func,
49 SPCI_SUCCESS_32);
David Brazdilb2bfcd42019-11-01 16:26:44 +000050 run_res = hf_vcpu_run(SERVICE_VM0, 0);
51 EXPECT_EQ(run_res.code, HF_VCPU_RUN_MESSAGE);
52 EXPECT_EQ(run_res.message.size, sizeof(expected_response));
Andrew Walbran70bc8622019-10-07 14:15:58 +010053 EXPECT_EQ(memcmp(mb.recv, expected_response, 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.";
David Brazdilb2bfcd42019-11-01 16:26:44 +000066 struct hf_vcpu_run_return run_res;
Andrew Scull2e7a76d2019-01-24 11:47:26 +000067 struct mailbox_buffers mb = set_up_mailbox();
68
69 SERVICE_SELECT(SERVICE_VM0, "interruptible", mb.send);
70
David Brazdilb2bfcd42019-11-01 16:26:44 +000071 run_res = hf_vcpu_run(SERVICE_VM0, 0);
72 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);
David Brazdilb2bfcd42019-11-01 16:26:44 +000077 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));
Andrew Walbran70bc8622019-10-07 14:15:58 +010080 EXPECT_EQ(memcmp(mb.recv, expected_response, sizeof(expected_response)),
Andrew Scull2e7a76d2019-01-24 11:47:26 +000081 0);
82 EXPECT_EQ(hf_mailbox_clear(), 0);
83
84 /* Inject the interrupt again, and wait for the same message. */
85 hf_interrupt_inject(SERVICE_VM0, 0, EXTERNAL_INTERRUPT_ID_A);
David Brazdilb2bfcd42019-11-01 16:26:44 +000086 run_res = hf_vcpu_run(SERVICE_VM0, 0);
87 EXPECT_EQ(run_res.code, HF_VCPU_RUN_MESSAGE);
88 EXPECT_EQ(run_res.message.size, sizeof(expected_response));
Andrew Walbran70bc8622019-10-07 14:15:58 +010089 EXPECT_EQ(memcmp(mb.recv, expected_response, sizeof(expected_response)),
Andrew Scull2e7a76d2019-01-24 11:47:26 +000090 0);
91 EXPECT_EQ(hf_mailbox_clear(), 0);
92}
93
94/**
95 * Inject two different interrupts to the interrupt VM, which will send a
96 * message back each time.
97 */
98TEST(interrupts, inject_two_interrupts)
99{
100 const char expected_response[] = "Got IRQ 07.";
101 const char expected_response_2[] = "Got IRQ 08.";
David Brazdilb2bfcd42019-11-01 16:26:44 +0000102 struct hf_vcpu_run_return run_res;
Andrew Scull2e7a76d2019-01-24 11:47:26 +0000103 struct mailbox_buffers mb = set_up_mailbox();
104
105 SERVICE_SELECT(SERVICE_VM0, "interruptible", mb.send);
106
David Brazdilb2bfcd42019-11-01 16:26:44 +0000107 run_res = hf_vcpu_run(SERVICE_VM0, 0);
108 EXPECT_EQ(run_res.code, HF_VCPU_RUN_WAIT_FOR_MESSAGE);
109 EXPECT_EQ(run_res.sleep.ns, HF_SLEEP_INDEFINITE);
Andrew Scull2e7a76d2019-01-24 11:47:26 +0000110
111 /* Inject the interrupt and wait for a message. */
112 hf_interrupt_inject(SERVICE_VM0, 0, EXTERNAL_INTERRUPT_ID_A);
David Brazdilb2bfcd42019-11-01 16:26:44 +0000113 run_res = hf_vcpu_run(SERVICE_VM0, 0);
114 EXPECT_EQ(run_res.code, HF_VCPU_RUN_MESSAGE);
115 EXPECT_EQ(run_res.message.size, sizeof(expected_response));
Andrew Walbran70bc8622019-10-07 14:15:58 +0100116 EXPECT_EQ(memcmp(mb.recv, expected_response, sizeof(expected_response)),
Andrew Scull2e7a76d2019-01-24 11:47:26 +0000117 0);
118 EXPECT_EQ(hf_mailbox_clear(), 0);
119
120 /* Inject a different interrupt and wait for a different message. */
121 hf_interrupt_inject(SERVICE_VM0, 0, EXTERNAL_INTERRUPT_ID_B);
David Brazdilb2bfcd42019-11-01 16:26:44 +0000122 run_res = hf_vcpu_run(SERVICE_VM0, 0);
123 EXPECT_EQ(run_res.code, HF_VCPU_RUN_MESSAGE);
124 EXPECT_EQ(run_res.message.size, sizeof(expected_response_2));
Andrew Walbran70bc8622019-10-07 14:15:58 +0100125 EXPECT_EQ(memcmp(mb.recv, expected_response_2,
Andrew Scull2e7a76d2019-01-24 11:47:26 +0000126 sizeof(expected_response_2)),
127 0);
128 EXPECT_EQ(hf_mailbox_clear(), 0);
129}
130
131/**
132 * Inject an interrupt then send a message to the interrupt VM, which will send
133 * a message back each time. This is to test that interrupt injection doesn't
134 * interfere with message reception.
135 */
136TEST(interrupts, inject_interrupt_message)
137{
138 const char expected_response[] = "Got IRQ 07.";
139 const char message[] = "Ping";
140 const char expected_response_2[] = "Got IRQ 05.";
David Brazdilb2bfcd42019-11-01 16:26:44 +0000141 struct hf_vcpu_run_return run_res;
Andrew Scull2e7a76d2019-01-24 11:47:26 +0000142 struct mailbox_buffers mb = set_up_mailbox();
143
144 SERVICE_SELECT(SERVICE_VM0, "interruptible", mb.send);
145
David Brazdilb2bfcd42019-11-01 16:26:44 +0000146 run_res = hf_vcpu_run(SERVICE_VM0, 0);
147 EXPECT_EQ(run_res.code, HF_VCPU_RUN_WAIT_FOR_MESSAGE);
148 EXPECT_EQ(run_res.sleep.ns, HF_SLEEP_INDEFINITE);
Andrew Scull2e7a76d2019-01-24 11:47:26 +0000149
150 /* Inject the interrupt and wait for a message. */
151 hf_interrupt_inject(SERVICE_VM0, 0, EXTERNAL_INTERRUPT_ID_A);
David Brazdilb2bfcd42019-11-01 16:26:44 +0000152 run_res = hf_vcpu_run(SERVICE_VM0, 0);
153 EXPECT_EQ(run_res.code, HF_VCPU_RUN_MESSAGE);
154 EXPECT_EQ(run_res.message.size, sizeof(expected_response));
Andrew Walbran70bc8622019-10-07 14:15:58 +0100155 EXPECT_EQ(memcmp(mb.recv, expected_response, sizeof(expected_response)),
Andrew Scull2e7a76d2019-01-24 11:47:26 +0000156 0);
157 EXPECT_EQ(hf_mailbox_clear(), 0);
158
David Brazdilb2bfcd42019-11-01 16:26:44 +0000159 run_res = hf_vcpu_run(SERVICE_VM0, 0);
160 EXPECT_EQ(run_res.code, HF_VCPU_RUN_WAIT_FOR_MESSAGE);
161 EXPECT_EQ(run_res.sleep.ns, HF_SLEEP_INDEFINITE);
Andrew Scull2e7a76d2019-01-24 11:47:26 +0000162
163 /* Now send a message to the secondary. */
Andrew Walbran70bc8622019-10-07 14:15:58 +0100164 memcpy_s(mb.send, SPCI_MSG_PAYLOAD_MAX, message, sizeof(message));
165 EXPECT_EQ(
166 spci_msg_send(HF_PRIMARY_VM_ID, SERVICE_VM0, sizeof(message), 0)
167 .func,
168 SPCI_SUCCESS_32);
David Brazdilb2bfcd42019-11-01 16:26:44 +0000169 run_res = hf_vcpu_run(SERVICE_VM0, 0);
170 EXPECT_EQ(run_res.code, HF_VCPU_RUN_MESSAGE);
171 EXPECT_EQ(run_res.message.size, sizeof(expected_response_2));
Andrew Walbran70bc8622019-10-07 14:15:58 +0100172 EXPECT_EQ(memcmp(mb.recv, expected_response_2,
Andrew Scull2e7a76d2019-01-24 11:47:26 +0000173 sizeof(expected_response_2)),
174 0);
175 EXPECT_EQ(hf_mailbox_clear(), 0);
176}
177
178/**
179 * Inject an interrupt which the target VM has not enabled, and then send a
180 * message telling it to enable that interrupt ID. It should then (and only
181 * then) send a message back.
182 */
183TEST(interrupts, inject_interrupt_disabled)
184{
185 const char expected_response[] = "Got IRQ 09.";
186 const char message[] = "Enable interrupt C";
David Brazdilb2bfcd42019-11-01 16:26:44 +0000187 struct hf_vcpu_run_return run_res;
Andrew Scull2e7a76d2019-01-24 11:47:26 +0000188 struct mailbox_buffers mb = set_up_mailbox();
189
190 SERVICE_SELECT(SERVICE_VM0, "interruptible", mb.send);
191
192 /* Inject the interrupt and expect not to get a message. */
193 hf_interrupt_inject(SERVICE_VM0, 0, EXTERNAL_INTERRUPT_ID_C);
David Brazdilb2bfcd42019-11-01 16:26:44 +0000194 run_res = hf_vcpu_run(SERVICE_VM0, 0);
195 EXPECT_EQ(run_res.code, HF_VCPU_RUN_WAIT_FOR_MESSAGE);
196 EXPECT_EQ(run_res.sleep.ns, HF_SLEEP_INDEFINITE);
Andrew Scull2e7a76d2019-01-24 11:47:26 +0000197
198 /*
199 * Now send a message to the secondary to enable the interrupt ID, and
200 * expect the response from the interrupt we sent before.
201 */
Andrew Walbran70bc8622019-10-07 14:15:58 +0100202 memcpy_s(mb.send, SPCI_MSG_PAYLOAD_MAX, message, sizeof(message));
203 EXPECT_EQ(
204 spci_msg_send(HF_PRIMARY_VM_ID, SERVICE_VM0, sizeof(message), 0)
205 .func,
206 SPCI_SUCCESS_32);
David Brazdilb2bfcd42019-11-01 16:26:44 +0000207 run_res = hf_vcpu_run(SERVICE_VM0, 0);
208 EXPECT_EQ(run_res.code, HF_VCPU_RUN_MESSAGE);
209 EXPECT_EQ(run_res.message.size, sizeof(expected_response));
Andrew Walbran70bc8622019-10-07 14:15:58 +0100210 EXPECT_EQ(memcmp(mb.recv, expected_response, sizeof(expected_response)),
Andrew Scull2e7a76d2019-01-24 11:47:26 +0000211 0);
212 EXPECT_EQ(hf_mailbox_clear(), 0);
213}
Andrew Walbran9311c9a2019-03-12 16:59:04 +0000214
215/**
216 * If a secondary VM has an enabled and pending interrupt, even if interrupts
217 * are disabled globally via PSTATE, then hf_mailbox_receive should not block
218 * even if `block` is true.
219 */
220TEST(interrupts, pending_interrupt_no_blocking_receive)
221{
222 const char expected_response[] = "Done waiting";
David Brazdilb2bfcd42019-11-01 16:26:44 +0000223 struct hf_vcpu_run_return run_res;
Andrew Walbran9311c9a2019-03-12 16:59:04 +0000224 struct mailbox_buffers mb = set_up_mailbox();
225
226 SERVICE_SELECT(SERVICE_VM0, "receive_block", mb.send);
227
228 /*
229 * Inject the interrupt and run the VM. It should disable interrupts
230 * globally, enable the specific interrupt, and then send us a message
231 * back after failing to receive a message a few times.
232 */
233 hf_interrupt_inject(SERVICE_VM0, 0, EXTERNAL_INTERRUPT_ID_A);
David Brazdilb2bfcd42019-11-01 16:26:44 +0000234 run_res = hf_vcpu_run(SERVICE_VM0, 0);
235 EXPECT_EQ(run_res.code, HF_VCPU_RUN_MESSAGE);
236 EXPECT_EQ(run_res.message.size, sizeof(expected_response));
Andrew Walbran70bc8622019-10-07 14:15:58 +0100237 EXPECT_EQ(memcmp(mb.recv, expected_response, sizeof(expected_response)),
Andrew Walbran9311c9a2019-03-12 16:59:04 +0000238 0);
239 EXPECT_EQ(hf_mailbox_clear(), 0);
240}
Andrew Walbran2d10e882019-03-14 18:17:42 +0000241
242/**
243 * If a secondary VM has an enabled and pending interrupt, even if interrupts
244 * are disabled globally via PSTATE, then WFI should be treated as a no-op and
245 * not return to the primary.
246 */
247TEST(interrupts, pending_interrupt_wfi_not_trapped)
248{
249 const char expected_response[] = "Done waiting";
David Brazdilb2bfcd42019-11-01 16:26:44 +0000250 struct hf_vcpu_run_return run_res;
Andrew Walbran2d10e882019-03-14 18:17:42 +0000251 struct mailbox_buffers mb = set_up_mailbox();
252
253 SERVICE_SELECT(SERVICE_VM0, "wfi", mb.send);
254
255 /*
256 * Inject the interrupt and run the VM. It should disable interrupts
257 * globally, enable the specific interrupt, and then send us a message
258 * back after running WFI a few times.
259 */
260 hf_interrupt_inject(SERVICE_VM0, 0, EXTERNAL_INTERRUPT_ID_A);
David Brazdilb2bfcd42019-11-01 16:26:44 +0000261 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(expected_response));
Andrew Walbran70bc8622019-10-07 14:15:58 +0100264 EXPECT_EQ(memcmp(mb.recv, expected_response, sizeof(expected_response)),
Andrew Walbran2d10e882019-03-14 18:17:42 +0000265 0);
266 EXPECT_EQ(hf_mailbox_clear(), 0);
267}
Andrew Scullb06d1752019-02-04 10:15:48 +0000268
269/*
270 * Deliver an interrupt and a message to the same vCPU and check that both are
271 * delivered the next time the vCPU is run.
272 */
273TEST(interrupts, deliver_interrupt_and_message)
274{
275 const char message[] = "I\'ll see you again.";
David Brazdilb2bfcd42019-11-01 16:26:44 +0000276 struct hf_vcpu_run_return run_res;
Andrew Scullb06d1752019-02-04 10:15:48 +0000277 struct mailbox_buffers mb = set_up_mailbox();
278
279 SERVICE_SELECT(SERVICE_VM0, "interruptible_echo", mb.send);
280
David Brazdilb2bfcd42019-11-01 16:26:44 +0000281 run_res = hf_vcpu_run(SERVICE_VM0, 0);
282 EXPECT_EQ(run_res.code, HF_VCPU_RUN_WAIT_FOR_MESSAGE);
283 EXPECT_EQ(run_res.sleep.ns, HF_SLEEP_INDEFINITE);
Andrew Scullb06d1752019-02-04 10:15:48 +0000284
Andrew Walbran70bc8622019-10-07 14:15:58 +0100285 memcpy_s(mb.send, SPCI_MSG_PAYLOAD_MAX, message, sizeof(message));
286 EXPECT_EQ(
287 spci_msg_send(HF_PRIMARY_VM_ID, SERVICE_VM0, sizeof(message), 0)
288 .func,
289 SPCI_SUCCESS_32);
Andrew Scullb06d1752019-02-04 10:15:48 +0000290 hf_interrupt_inject(SERVICE_VM0, 0, EXTERNAL_INTERRUPT_ID_A);
David Brazdilb2bfcd42019-11-01 16:26:44 +0000291 run_res = hf_vcpu_run(SERVICE_VM0, 0);
292 EXPECT_EQ(run_res.code, HF_VCPU_RUN_MESSAGE);
293 EXPECT_EQ(run_res.message.size, sizeof(message));
Andrew Walbran70bc8622019-10-07 14:15:58 +0100294 EXPECT_EQ(memcmp(mb.recv, message, sizeof(message)), 0);
Andrew Scullb06d1752019-02-04 10:15:48 +0000295 EXPECT_EQ(hf_mailbox_clear(), 0);
296}