blob: c18c8c9885b9501d930b186c1ecf368ea18603c8 [file] [log] [blame]
Andrew Scull2e7a76d2019-01-24 11:47:26 +00001/*
2 * Copyright 2019 Google LLC
3 *
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
19#include "hf/std.h"
20
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);
40 EXPECT_EQ(run_res.code, HF_VCPU_RUN_WAIT_FOR_INTERRUPT);
41
42 /* Set the message, echo it and wait for a response. */
43 memcpy(mb.send, message, sizeof(message));
44 EXPECT_EQ(hf_mailbox_send(SERVICE_VM0, sizeof(message), false), 0);
45 run_res = hf_vcpu_run(SERVICE_VM0, 0);
46 EXPECT_EQ(run_res.code, HF_VCPU_RUN_MESSAGE);
47 EXPECT_EQ(run_res.message.size, sizeof(expected_response));
48 EXPECT_EQ(memcmp(mb.recv, expected_response, sizeof(expected_response)),
49 0);
50 EXPECT_EQ(hf_mailbox_clear(), 0);
51}
52
53/**
54 * Inject an interrupt to the interrupt VM, which will send a message back.
55 * Repeat this twice to make sure it doesn't get into a bad state after the
56 * first one.
57 */
58TEST(interrupts, inject_interrupt_twice)
59{
60 const char expected_response[] = "Got IRQ 07.";
61 struct hf_vcpu_run_return run_res;
62 struct mailbox_buffers mb = set_up_mailbox();
63
64 SERVICE_SELECT(SERVICE_VM0, "interruptible", mb.send);
65
66 run_res = hf_vcpu_run(SERVICE_VM0, 0);
67 EXPECT_EQ(run_res.code, HF_VCPU_RUN_WAIT_FOR_INTERRUPT);
68
69 /* Inject the interrupt and wait for a message. */
70 hf_interrupt_inject(SERVICE_VM0, 0, EXTERNAL_INTERRUPT_ID_A);
71 run_res = hf_vcpu_run(SERVICE_VM0, 0);
72 EXPECT_EQ(run_res.code, HF_VCPU_RUN_MESSAGE);
73 EXPECT_EQ(run_res.message.size, sizeof(expected_response));
74 EXPECT_EQ(memcmp(mb.recv, expected_response, sizeof(expected_response)),
75 0);
76 EXPECT_EQ(hf_mailbox_clear(), 0);
77
78 /* Inject the interrupt again, and wait for the same message. */
79 hf_interrupt_inject(SERVICE_VM0, 0, EXTERNAL_INTERRUPT_ID_A);
80 run_res = hf_vcpu_run(SERVICE_VM0, 0);
81 EXPECT_EQ(run_res.code, HF_VCPU_RUN_MESSAGE);
82 EXPECT_EQ(run_res.message.size, sizeof(expected_response));
83 EXPECT_EQ(memcmp(mb.recv, expected_response, sizeof(expected_response)),
84 0);
85 EXPECT_EQ(hf_mailbox_clear(), 0);
86}
87
88/**
89 * Inject two different interrupts to the interrupt VM, which will send a
90 * message back each time.
91 */
92TEST(interrupts, inject_two_interrupts)
93{
94 const char expected_response[] = "Got IRQ 07.";
95 const char expected_response_2[] = "Got IRQ 08.";
96 struct hf_vcpu_run_return run_res;
97 struct mailbox_buffers mb = set_up_mailbox();
98
99 SERVICE_SELECT(SERVICE_VM0, "interruptible", mb.send);
100
101 run_res = hf_vcpu_run(SERVICE_VM0, 0);
102 EXPECT_EQ(run_res.code, HF_VCPU_RUN_WAIT_FOR_INTERRUPT);
103
104 /* Inject the interrupt and wait for a message. */
105 hf_interrupt_inject(SERVICE_VM0, 0, EXTERNAL_INTERRUPT_ID_A);
106 run_res = hf_vcpu_run(SERVICE_VM0, 0);
107 EXPECT_EQ(run_res.code, HF_VCPU_RUN_MESSAGE);
108 EXPECT_EQ(run_res.message.size, sizeof(expected_response));
109 EXPECT_EQ(memcmp(mb.recv, expected_response, sizeof(expected_response)),
110 0);
111 EXPECT_EQ(hf_mailbox_clear(), 0);
112
113 /* Inject a different interrupt and wait for a different message. */
114 hf_interrupt_inject(SERVICE_VM0, 0, EXTERNAL_INTERRUPT_ID_B);
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_2));
118 EXPECT_EQ(memcmp(mb.recv, expected_response_2,
119 sizeof(expected_response_2)),
120 0);
121 EXPECT_EQ(hf_mailbox_clear(), 0);
122}
123
124/**
125 * Inject an interrupt then send a message to the interrupt VM, which will send
126 * a message back each time. This is to test that interrupt injection doesn't
127 * interfere with message reception.
128 */
129TEST(interrupts, inject_interrupt_message)
130{
131 const char expected_response[] = "Got IRQ 07.";
132 const char message[] = "Ping";
133 const char expected_response_2[] = "Got IRQ 05.";
134 struct hf_vcpu_run_return run_res;
135 struct mailbox_buffers mb = set_up_mailbox();
136
137 SERVICE_SELECT(SERVICE_VM0, "interruptible", mb.send);
138
139 run_res = hf_vcpu_run(SERVICE_VM0, 0);
140 EXPECT_EQ(run_res.code, HF_VCPU_RUN_WAIT_FOR_INTERRUPT);
141
142 /* Inject the interrupt and wait for a message. */
143 hf_interrupt_inject(SERVICE_VM0, 0, EXTERNAL_INTERRUPT_ID_A);
144 run_res = hf_vcpu_run(SERVICE_VM0, 0);
145 EXPECT_EQ(run_res.code, HF_VCPU_RUN_MESSAGE);
146 EXPECT_EQ(run_res.message.size, sizeof(expected_response));
147 EXPECT_EQ(memcmp(mb.recv, expected_response, sizeof(expected_response)),
148 0);
149 EXPECT_EQ(hf_mailbox_clear(), 0);
150
151 run_res = hf_vcpu_run(SERVICE_VM0, 0);
152 EXPECT_EQ(run_res.code, HF_VCPU_RUN_WAIT_FOR_INTERRUPT);
153
154 /* Now send a message to the secondary. */
155 memcpy(mb.send, message, sizeof(message));
156 EXPECT_EQ(hf_mailbox_send(SERVICE_VM0, sizeof(message), false), 0);
157 run_res = hf_vcpu_run(SERVICE_VM0, 0);
158 EXPECT_EQ(run_res.code, HF_VCPU_RUN_MESSAGE);
159 EXPECT_EQ(run_res.message.size, sizeof(expected_response_2));
160 EXPECT_EQ(memcmp(mb.recv, expected_response_2,
161 sizeof(expected_response_2)),
162 0);
163 EXPECT_EQ(hf_mailbox_clear(), 0);
164}
165
166/**
167 * Inject an interrupt which the target VM has not enabled, and then send a
168 * message telling it to enable that interrupt ID. It should then (and only
169 * then) send a message back.
170 */
171TEST(interrupts, inject_interrupt_disabled)
172{
173 const char expected_response[] = "Got IRQ 09.";
174 const char message[] = "Enable interrupt C";
175 struct hf_vcpu_run_return run_res;
176 struct mailbox_buffers mb = set_up_mailbox();
177
178 SERVICE_SELECT(SERVICE_VM0, "interruptible", mb.send);
179
180 /* Inject the interrupt and expect not to get a message. */
181 hf_interrupt_inject(SERVICE_VM0, 0, EXTERNAL_INTERRUPT_ID_C);
182 run_res = hf_vcpu_run(SERVICE_VM0, 0);
183 EXPECT_EQ(run_res.code, HF_VCPU_RUN_WAIT_FOR_INTERRUPT);
184 EXPECT_EQ(hf_mailbox_clear(), -1);
185
186 /*
187 * Now send a message to the secondary to enable the interrupt ID, and
188 * expect the response from the interrupt we sent before.
189 */
190 memcpy(mb.send, message, sizeof(message));
191 EXPECT_EQ(hf_mailbox_send(SERVICE_VM0, sizeof(message), false), 0);
192 run_res = hf_vcpu_run(SERVICE_VM0, 0);
193 EXPECT_EQ(run_res.code, HF_VCPU_RUN_MESSAGE);
194 EXPECT_EQ(run_res.message.size, sizeof(expected_response));
195 EXPECT_EQ(memcmp(mb.recv, expected_response, sizeof(expected_response)),
196 0);
197 EXPECT_EQ(hf_mailbox_clear(), 0);
198}