1 /*
2 * Copyright 2021 The Hafnium Authors.
3 *
4 * Use of this source code is governed by a BSD-style
5 * license that can be found in the LICENSE file or at
6 * https://opensource.org/licenses/BSD-3-Clause.
7 */
8
9 #include "hf/arch/vm/power_mgmt.h"
10
11 #include "hf/dlog.h"
12 #include "hf/ffa.h"
13 #include "hf/spinlock.h"
14
15 #include "vmapi/hf/call.h"
16
17 #include "ffa_endpoints.h"
18 #include "partition_services.h"
19 #include "test/hftest.h"
20 #include "test/vmapi/ffa.h"
21
22 struct notif_cpu_entry_args {
23 struct spinlock *lock;
24 ffa_vcpu_index_t vcpu_id;
25 ffa_id_t sp_id;
26 bool is_sp_up;
27 };
28
notif_signal_vm_to_sp(ffa_id_t sender,ffa_id_t receiver,ffa_notifications_bitmap_t bitmap,uint32_t flags)29 static void notif_signal_vm_to_sp(ffa_id_t sender, ffa_id_t receiver,
30 ffa_notifications_bitmap_t bitmap,
31 uint32_t flags)
32 {
33 struct ffa_value res;
34 ffa_vcpu_index_t vcpu_id = (flags >> 16U) & 0xFFFFU;
35
36 /* Request receiver to bind notifications. */
37 res = sp_notif_bind_cmd_send(sender, receiver, sender,
38 flags & FFA_NOTIFICATION_FLAG_PER_VCPU,
39 bitmap);
40 EXPECT_EQ(res.func, FFA_MSG_SEND_DIRECT_RESP_32);
41 EXPECT_EQ(sp_resp(res), SP_SUCCESS);
42
43 res = ffa_notification_set(sender, receiver, flags, bitmap);
44 EXPECT_EQ(res.func, FFA_SUCCESS_32);
45
46 res = ffa_notification_info_get();
47 EXPECT_EQ(res.func, FFA_SUCCESS_64);
48
49 /* Request to get notifications pending */
50 res = sp_notif_get_cmd_send(sender, receiver, vcpu_id,
51 FFA_NOTIFICATION_FLAG_BITMAP_VM);
52 EXPECT_EQ(sp_resp(res), SP_SUCCESS);
53 EXPECT_EQ(sp_notif_get_from_sp(res), 0);
54 EXPECT_EQ(sp_notif_get_from_vm(res), bitmap);
55
56 /* Request to unbind notifications */
57 res = sp_notif_unbind_cmd_send(sender, receiver, sender, bitmap);
58 EXPECT_EQ(res.func, FFA_MSG_SEND_DIRECT_RESP_32);
59 EXPECT_EQ(sp_resp(res), SP_SUCCESS);
60 }
61
notif_signal_sp_to_vm(ffa_id_t sender,ffa_id_t receiver,ffa_notifications_bitmap_t bitmap,uint32_t flags)62 static void notif_signal_sp_to_vm(ffa_id_t sender, ffa_id_t receiver,
63 ffa_notifications_bitmap_t bitmap,
64 uint32_t flags)
65 {
66 struct ffa_value res;
67 ffa_vcpu_index_t vcpu_id = (ffa_vcpu_index_t)(flags >> 16U) & 0xFFFFU;
68
69 /* Arbitrarily bind notification. */
70 res = ffa_notification_bind(sender, receiver,
71 flags & FFA_NOTIFICATIONS_FLAG_PER_VCPU,
72 bitmap);
73 EXPECT_EQ(res.func, FFA_SUCCESS_32);
74
75 /* Requesting sender to set notification. */
76 res = sp_notif_set_cmd_send(receiver, sender, receiver, flags, bitmap);
77 EXPECT_EQ(res.func, FFA_MSG_SEND_DIRECT_RESP_32);
78 EXPECT_EQ(sp_resp(res), SP_SUCCESS);
79
80 /* Retrieve FF-A endpoints with pending notifications. */
81 res = ffa_notification_info_get();
82 EXPECT_EQ(res.func, FFA_SUCCESS_64);
83
84 /* Retrieving pending notification. */
85 res = ffa_notification_get(receiver, vcpu_id,
86 FFA_NOTIFICATION_FLAG_BITMAP_SP);
87 EXPECT_EQ(res.func, FFA_SUCCESS_32);
88
89 EXPECT_EQ(ffa_notification_get_from_sp(res), bitmap);
90 EXPECT_EQ(res.arg4, 0);
91 EXPECT_EQ(res.arg5, 0);
92 EXPECT_EQ(res.arg6, 0);
93 EXPECT_EQ(res.arg7, 0);
94
95 res = ffa_notification_unbind(sender, receiver, bitmap);
96 EXPECT_EQ(res.func, FFA_SUCCESS_32);
97 }
98
99 /**
100 * Test to validate notifications signaling from an SP to a VM.
101 */
TEST(ffa_notifications,signaling_from_sp_to_vm)102 TEST(ffa_notifications, signaling_from_sp_to_vm)
103 {
104 notif_signal_sp_to_vm(SP_ID(1), hf_vm_get_id(),
105 FFA_NOTIFICATION_MASK(20),
106 FFA_NOTIFICATIONS_FLAG_DELAY_SRI);
107 }
108
109 /**
110 * Validate notifications signaling from VM to an SP.
111 */
TEST(ffa_notifications,signaling_from_vm_to_sp)112 TEST(ffa_notifications, signaling_from_vm_to_sp)
113 {
114 notif_signal_vm_to_sp(hf_vm_get_id(), SP_ID(1),
115 FFA_NOTIFICATION_MASK(35),
116 FFA_NOTIFICATIONS_FLAG_DELAY_SRI);
117 }
118
cpu_entry_vm_to_sp_signaling(uintptr_t arg)119 static void cpu_entry_vm_to_sp_signaling(uintptr_t arg)
120 {
121 struct ffa_value res;
122 struct notif_cpu_entry_args *test_args =
123 // NOLINTNEXTLINE(performance-no-int-to-ptr)
124 (struct notif_cpu_entry_args *)arg;
125 ffa_vcpu_index_t sp_vcpu_id = test_args->is_sp_up
126 ? ((ffa_vcpu_index_t)0)
127 : test_args->vcpu_id;
128
129 /*
130 * Make receiver SP reach message loop.
131 * TODO: the FFA_RUN ABI only needs to be called for the MP UP endpoints
132 * to bootstrap the EC in the current core. Though there is an issue
133 * with the current FFA_RUN implementation: it returns back to the
134 * caller with FFA_MSG_WAIT interface, without resuming the target
135 * SP. When fixing the FFA_RUN issue, this bit of code needs addressing.
136 */
137 res = ffa_run(test_args->sp_id, sp_vcpu_id);
138 EXPECT_EQ(ffa_func_id(res), FFA_MSG_WAIT_32);
139
140 notif_signal_vm_to_sp(
141 hf_vm_get_id(), test_args->sp_id,
142 FFA_NOTIFICATION_MASK(test_args->vcpu_id),
143 FFA_NOTIFICATIONS_FLAG_DELAY_SRI |
144 FFA_NOTIFICATIONS_FLAG_PER_VCPU |
145 FFA_NOTIFICATIONS_FLAGS_VCPU_ID(sp_vcpu_id));
146
147 sl_unlock(test_args->lock);
148
149 arch_cpu_stop();
150 }
151
cpu_entry_sp_to_vm_signaling(uintptr_t arg)152 static void cpu_entry_sp_to_vm_signaling(uintptr_t arg)
153 {
154 struct ffa_value res;
155 struct notif_cpu_entry_args *test_args =
156 // NOLINTNEXTLINE(performance-no-int-to-ptr)
157 (struct notif_cpu_entry_args *)arg;
158 ffa_vcpu_index_t sp_vcpu_id = test_args->is_sp_up
159 ? ((ffa_vcpu_index_t)0)
160 : test_args->vcpu_id;
161
162 /*
163 * Make sender SP reach message loop.
164 * TODO: the FFA_RUN ABI only needs to be called for the MP UP endpoints
165 * to bootstrap the EC in the current core. Though there is an issue
166 * with the current FFA_RUN implementation: it returns back to the
167 * caller with FFA_MSG_WAIT interface, without resuming the target
168 * SP. When fixing the FFA_RUN issue, this bit of code needs addressing.
169 */
170 res = ffa_run(test_args->sp_id, sp_vcpu_id);
171 EXPECT_EQ(ffa_func_id(res), FFA_MSG_WAIT_32);
172
173 notif_signal_sp_to_vm(
174 test_args->sp_id, hf_vm_get_id(),
175 FFA_NOTIFICATION_MASK(test_args->vcpu_id),
176 FFA_NOTIFICATIONS_FLAG_DELAY_SRI |
177 FFA_NOTIFICATIONS_FLAG_PER_VCPU |
178 FFA_NOTIFICATIONS_FLAGS_VCPU_ID(test_args->vcpu_id));
179
180 sl_unlock(test_args->lock);
181
182 arch_cpu_stop();
183 }
184
base_per_cpu_notifications_test(void (* cpu_entry)(uintptr_t arg))185 static void base_per_cpu_notifications_test(void (*cpu_entry)(uintptr_t arg))
186 {
187 struct spinlock lock = SPINLOCK_INIT;
188 struct notif_cpu_entry_args args = {.lock = &lock};
189 struct ffa_partition_info sp;
190 struct mailbox_buffers mb = set_up_mailbox();
191
192 EXPECT_EQ(get_ffa_partition_info(
193 &(struct ffa_uuid){SP_SERVICE_SECOND_UUID}, &sp, 1,
194 mb.recv),
195 1);
196 args.sp_id = sp.vm_id;
197 args.is_sp_up = sp.vcpu_count == 1U;
198
199 /* Start secondary while holding lock. */
200 sl_lock(&lock);
201
202 for (size_t i = 1; i < MAX_CPUS - 1; i++) {
203 HFTEST_LOG("Notifications signaling VM to SP. Booting CPU %zu.",
204 i);
205
206 args.vcpu_id = i;
207
208 EXPECT_EQ(hftest_cpu_start(hftest_get_cpu_id(i),
209 hftest_get_secondary_ec_stack(i),
210 cpu_entry, (uintptr_t)&args),
211 true);
212
213 /* Wait for CPU to release the lock. */
214 sl_lock(&lock);
215
216 HFTEST_LOG("Done with CPU %zu\n", i);
217 }
218 }
219
TEST(ffa_notifications,per_vcpu_vm_to_sp)220 TEST(ffa_notifications, per_vcpu_vm_to_sp)
221 {
222 base_per_cpu_notifications_test(cpu_entry_vm_to_sp_signaling);
223 }
224
TEST(ffa_notifications,per_vcpu_sp_to_vm)225 TEST(ffa_notifications, per_vcpu_sp_to_vm)
226 {
227 base_per_cpu_notifications_test(cpu_entry_sp_to_vm_signaling);
228 }
229
TEST(ffa_notifications,fail_if_mbz_set_in_notification_get)230 TEST(ffa_notifications, fail_if_mbz_set_in_notification_get)
231 {
232 struct ffa_value res;
233 const ffa_id_t sender = SP_ID(1);
234 ffa_id_t own_id = hf_vm_get_id();
235
236 /* Arbitrarily bind notification. */
237 res = ffa_notification_bind(sender, own_id, 0,
238 FFA_NOTIFICATION_MASK(1));
239 EXPECT_EQ(res.func, FFA_SUCCESS_32);
240
241 /* Requesting sender to set notification. */
242 res = sp_notif_set_cmd_send(own_id, sender, own_id, 0,
243 FFA_NOTIFICATION_MASK(1));
244 EXPECT_EQ(res.func, FFA_MSG_SEND_DIRECT_RESP_32);
245 EXPECT_EQ(sp_resp(res), SP_SUCCESS);
246
247 /* Check return is FFA_INVALID_PARAMETERS if any bit that MBZ is set. */
248 res = ffa_notification_get(own_id, 0, 0xFF00U);
249 EXPECT_FFA_ERROR(res, FFA_INVALID_PARAMETERS);
250 }
251
TEST(ffa_notifications,fail_if_mbz_set_in_notification_set)252 TEST(ffa_notifications, fail_if_mbz_set_in_notification_set)
253 {
254 struct ffa_value res;
255 const ffa_id_t sender = SP_ID(1);
256 ffa_id_t own_id = hf_vm_get_id();
257
258 /* Arbitrarily bind notification. */
259 res = ffa_notification_bind(sender, own_id, 0,
260 FFA_NOTIFICATION_MASK(1));
261 EXPECT_EQ(res.func, FFA_SUCCESS_32);
262
263 /* Requesting sender to set notification. */
264 res = sp_notif_set_cmd_send(own_id, sender, own_id,
265 ~(FFA_NOTIFICATION_FLAG_PER_VCPU |
266 FFA_NOTIFICATIONS_FLAG_DELAY_SRI),
267 FFA_NOTIFICATION_MASK(1));
268 EXPECT_EQ(res.func, FFA_MSG_SEND_DIRECT_RESP_32);
269 EXPECT_EQ(sp_resp(res), SP_ERROR);
270 EXPECT_EQ((int32_t)sp_resp_value(res), FFA_INVALID_PARAMETERS);
271 }
272
273 /**
274 * Test that setting global notifications, specifying vCPU other than
275 * 0 fails with the appropriate error code.
276 */
TEST(ffa_notifications,fail_if_global_notif_vcpu_not_zero)277 TEST(ffa_notifications, fail_if_global_notif_vcpu_not_zero)
278 {
279 struct ffa_value res;
280 const ffa_id_t sender = SP_ID(1);
281 ffa_id_t own_id = hf_vm_get_id();
282
283 /* Arbitrarily bind notification. */
284 res = ffa_notification_bind(sender, own_id, 0,
285 FFA_NOTIFICATION_MASK(1));
286 EXPECT_EQ(res.func, FFA_SUCCESS_32);
287
288 /* Requesting sender to set notification. */
289 res = sp_notif_set_cmd_send(own_id, sender, own_id,
290 FFA_NOTIFICATIONS_FLAGS_VCPU_ID(5),
291 FFA_NOTIFICATION_MASK(1));
292 EXPECT_EQ(res.func, FFA_MSG_SEND_DIRECT_RESP_32);
293 EXPECT_EQ(sp_resp(res), SP_ERROR);
294 EXPECT_EQ((int32_t)sp_resp_value(res), FFA_INVALID_PARAMETERS);
295 }
296
TEST(ffa_notifications,fail_if_global_notif_set_as_per_vcpu)297 TEST(ffa_notifications, fail_if_global_notif_set_as_per_vcpu)
298 {
299 struct ffa_value res;
300 const ffa_id_t sender = SP_ID(1);
301 ffa_id_t own_id = hf_vm_get_id();
302
303 /* Arbitrarily bind global notification. */
304 res = ffa_notification_bind(sender, own_id, 0,
305 FFA_NOTIFICATION_MASK(1));
306 EXPECT_EQ(res.func, FFA_SUCCESS_32);
307
308 /* Requesting sender to set notification: as per-vCPU. */
309 res = sp_notif_set_cmd_send(own_id, sender, own_id,
310 FFA_NOTIFICATION_FLAG_PER_VCPU,
311 FFA_NOTIFICATION_MASK(1));
312 EXPECT_EQ(res.func, FFA_MSG_SEND_DIRECT_RESP_32);
313 EXPECT_EQ(sp_resp(res), SP_ERROR);
314 EXPECT_EQ((int32_t)sp_resp_value(res), FFA_INVALID_PARAMETERS);
315 }
316
TEST(ffa_notifications,fail_if_per_vcpu_notif_set_as_global)317 TEST(ffa_notifications, fail_if_per_vcpu_notif_set_as_global)
318 {
319 struct ffa_value res;
320 const ffa_id_t sender = SP_ID(1);
321 ffa_id_t own_id = hf_vm_get_id();
322
323 /* Arbitrarily bind per-vCPU notification. */
324 res = ffa_notification_bind(sender, own_id,
325 FFA_NOTIFICATION_FLAG_PER_VCPU,
326 FFA_NOTIFICATION_MASK(1));
327 EXPECT_EQ(res.func, FFA_SUCCESS_32);
328
329 /* Requesting sender to set notification: as global. */
330 res = sp_notif_set_cmd_send(own_id, sender, own_id, 0,
331 FFA_NOTIFICATION_MASK(1));
332 EXPECT_EQ(res.func, FFA_MSG_SEND_DIRECT_RESP_32);
333 EXPECT_EQ(sp_resp(res), SP_ERROR);
334 EXPECT_EQ((int32_t)sp_resp_value(res), FFA_INVALID_PARAMETERS);
335 }
336
TEST(ffa_notifications,fail_if_mbz_set_in_notifications_bind)337 TEST(ffa_notifications, fail_if_mbz_set_in_notifications_bind)
338 {
339 struct ffa_value res;
340 const ffa_id_t sender = SP_ID(1);
341 ffa_id_t own_id = hf_vm_get_id();
342
343 res = ffa_notification_bind(sender, own_id,
344 ~FFA_NOTIFICATION_FLAG_PER_VCPU,
345 FFA_NOTIFICATION_MASK(1));
346 EXPECT_FFA_ERROR(res, FFA_INVALID_PARAMETERS);
347 }
348