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