blob: 4d47c54a52271e72de3fccd310f9e3f484780388 [file] [log] [blame]
Andrew Scull3c257452019-11-26 13:32:50 +00001/*
2 * Copyright 2019 The Hafnium Authors.
3 *
Andrew Walbrane959ec12020-06-17 15:01:09 +01004 * 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.
Andrew Scull3c257452019-11-26 13:32:50 +00007 */
8
9#include <gmock/gmock.h>
10
11extern "C" {
12#include "hf/mpool.h"
13#include "hf/vm.h"
14}
15
J-Alvesb37fd082020-10-22 12:29:21 +010016#include <list>
Andrew Scull3c257452019-11-26 13:32:50 +000017#include <memory>
18#include <span>
19#include <vector>
20
21#include "mm_test.hh"
22
23namespace
24{
25using namespace ::std::placeholders;
26
27using ::testing::AllOf;
28using ::testing::Each;
29using ::testing::SizeIs;
30
31using struct_vm = struct vm;
J-Alves96f6e292021-06-08 17:32:40 +010032using struct_vm_locked = struct vm_locked;
Andrew Scull3c257452019-11-26 13:32:50 +000033
J-Alvesb37fd082020-10-22 12:29:21 +010034constexpr size_t TEST_HEAP_SIZE = PAGE_SIZE * 32;
Andrew Scull3c257452019-11-26 13:32:50 +000035const int TOP_LEVEL = arch_mm_stage2_max_level();
36
37class vm : public ::testing::Test
38{
39 void SetUp() override
40 {
41 /*
42 * TODO: replace with direct use of stdlib allocator so
43 * sanitizers are more effective.
44 */
45 test_heap = std::make_unique<uint8_t[]>(TEST_HEAP_SIZE);
46 mpool_init(&ppool, sizeof(struct mm_page_table));
47 mpool_add_chunk(&ppool, test_heap.get(), TEST_HEAP_SIZE);
48 }
49
50 std::unique_ptr<uint8_t[]> test_heap;
51
52 protected:
53 struct mpool ppool;
J-Alvesb37fd082020-10-22 12:29:21 +010054
55 public:
56 static bool BootOrderBiggerThan(struct_vm *vm1, struct_vm *vm2)
57 {
58 return vm1->boot_order > vm2->boot_order;
59 }
Andrew Scull3c257452019-11-26 13:32:50 +000060};
61
62/**
63 * If nothing is mapped, unmapping the hypervisor has no effect.
64 */
65TEST_F(vm, vm_unmap_hypervisor_not_mapped)
66{
67 struct_vm *vm;
68 struct vm_locked vm_locked;
69
Raghu Krishnamurthycd1eceb2021-01-04 12:20:48 -080070 EXPECT_TRUE(vm_init_next(1, &ppool, &vm, false));
Andrew Scull3c257452019-11-26 13:32:50 +000071 vm_locked = vm_lock(vm);
Raghu Krishnamurthy0132b512021-02-03 14:13:26 -080072 ASSERT_TRUE(mm_vm_init(&vm->ptable, vm->id, &ppool));
Andrew Scull3c257452019-11-26 13:32:50 +000073 EXPECT_TRUE(vm_unmap_hypervisor(vm_locked, &ppool));
74 EXPECT_THAT(
75 mm_test::get_ptable(vm->ptable),
76 AllOf(SizeIs(4), Each(Each(arch_mm_absent_pte(TOP_LEVEL)))));
77 mm_vm_fini(&vm->ptable, &ppool);
78 vm_unlock(&vm_locked);
79}
80
J-Alvesb37fd082020-10-22 12:29:21 +010081/**
82 * Validate the "boot_list" is created properly, according to vm's "boot_order"
83 * field.
84 */
85TEST_F(vm, vm_boot_order)
86{
87 struct_vm *vm_cur;
88 std::list<struct_vm *> expected_final_order;
89
90 EXPECT_FALSE(vm_get_first_boot());
91
92 /*
93 * Insertion when no call to "vm_update_boot" has been made yet.
94 * The "boot_list" is expected to be empty.
95 */
Raghu Krishnamurthycd1eceb2021-01-04 12:20:48 -080096 EXPECT_TRUE(vm_init_next(1, &ppool, &vm_cur, false));
J-Alvesb37fd082020-10-22 12:29:21 +010097 vm_cur->boot_order = 1;
98 vm_update_boot(vm_cur);
99 expected_final_order.push_back(vm_cur);
100
101 EXPECT_EQ(vm_get_first_boot()->id, vm_cur->id);
102
103 /* Insertion at the head of the boot list */
Raghu Krishnamurthycd1eceb2021-01-04 12:20:48 -0800104 EXPECT_TRUE(vm_init_next(1, &ppool, &vm_cur, false));
J-Alvesb37fd082020-10-22 12:29:21 +0100105 vm_cur->boot_order = 3;
106 vm_update_boot(vm_cur);
107 expected_final_order.push_back(vm_cur);
108
109 EXPECT_EQ(vm_get_first_boot()->id, vm_cur->id);
110
111 /* Insertion of two in the middle of the boot list */
112 for (int i = 0; i < 2; i++) {
Raghu Krishnamurthycd1eceb2021-01-04 12:20:48 -0800113 EXPECT_TRUE(vm_init_next(1, &ppool, &vm_cur, false));
J-Alvesb37fd082020-10-22 12:29:21 +0100114 vm_cur->boot_order = 2;
115 vm_update_boot(vm_cur);
116 expected_final_order.push_back(vm_cur);
117 }
118
119 /*
120 * Insertion in the end of the list.
121 * This tests shares the data with "vm_unmap_hypervisor_not_mapped".
122 * As such, a VM is expected to have been initialized before this
123 * test, with ID 1 and boot_order 0.
124 */
125 vm_cur = vm_find(1);
126 EXPECT_FALSE(vm_cur == NULL);
127 vm_update_boot(vm_cur);
128 expected_final_order.push_back(vm_cur);
129
130 /*
131 * Number of VMs initialized should be the same as in the
132 * "expected_final_order", before the final verification.
133 */
134 EXPECT_EQ(expected_final_order.size(), vm_get_count())
135 << "Something went wrong with the test itself...\n";
136
137 /* Sort "expected_final_order" by "boot_order" field */
138 expected_final_order.sort(vm::BootOrderBiggerThan);
139
140 std::list<struct_vm *>::iterator it;
141 for (it = expected_final_order.begin(), vm_cur = vm_get_first_boot();
142 it != expected_final_order.end() && vm_cur != NULL;
143 it++, vm_cur = vm_cur->next_boot) {
144 EXPECT_EQ((*it)->id, vm_cur->id);
145 }
146}
J-Alves60eaff92021-05-27 14:54:41 +0100147
148/**
149 * Validates updates and check functions for binding notifications to endpoints.
150 */
151TEST_F(vm, vm_notifications_bind_diff_senders)
152{
J-Alvesd3e81622021-10-05 14:55:57 +0100153 struct_vm *current_vm = nullptr;
154 struct vm_locked current_vm_locked;
J-Alves60eaff92021-05-27 14:54:41 +0100155 std::vector<struct_vm *> dummy_senders;
156 ffa_notifications_bitmap_t bitmaps[] = {
157 0x00000000FFFFFFFFU, 0xFFFFFFFF00000000U, 0x0000FFFFFFFF0000U};
158 bool is_from_vm = true;
159
160 /* For the subsequent tests three VMs are used. */
161 CHECK(vm_get_count() >= 3);
162
J-Alvesd3e81622021-10-05 14:55:57 +0100163 current_vm = vm_find_index(0);
J-Alves60eaff92021-05-27 14:54:41 +0100164
165 dummy_senders.push_back(vm_find_index(1));
166 dummy_senders.push_back(vm_find_index(2));
167
J-Alvesd3e81622021-10-05 14:55:57 +0100168 current_vm_locked = vm_lock(current_vm);
J-Alves60eaff92021-05-27 14:54:41 +0100169
170 for (unsigned int i = 0; i < 2; i++) {
171 /* Validate bindings condition after initialization. */
172 EXPECT_TRUE(vm_notifications_validate_binding(
J-Alvesd3e81622021-10-05 14:55:57 +0100173 current_vm_locked, is_from_vm, HF_INVALID_VM_ID,
174 bitmaps[i], false));
J-Alves60eaff92021-05-27 14:54:41 +0100175
176 /*
177 * Validate bind related operations. For this test considering
178 * only global notifications.
179 */
J-Alvesd3e81622021-10-05 14:55:57 +0100180 vm_notifications_update_bindings(current_vm_locked, is_from_vm,
J-Alves60eaff92021-05-27 14:54:41 +0100181 dummy_senders[i]->id,
182 bitmaps[i], false);
183
184 EXPECT_TRUE(vm_notifications_validate_binding(
J-Alvesd3e81622021-10-05 14:55:57 +0100185 current_vm_locked, is_from_vm, dummy_senders[i]->id,
J-Alves60eaff92021-05-27 14:54:41 +0100186 bitmaps[i], false));
187
188 EXPECT_FALSE(vm_notifications_validate_binding(
J-Alvesd3e81622021-10-05 14:55:57 +0100189 current_vm_locked, is_from_vm, dummy_senders[1 - i]->id,
J-Alves60eaff92021-05-27 14:54:41 +0100190 bitmaps[i], false));
191
192 EXPECT_FALSE(vm_notifications_validate_binding(
J-Alvesd3e81622021-10-05 14:55:57 +0100193 current_vm_locked, is_from_vm, dummy_senders[i]->id,
J-Alves60eaff92021-05-27 14:54:41 +0100194 bitmaps[1 - i], false));
195
196 EXPECT_FALSE(vm_notifications_validate_binding(
J-Alvesd3e81622021-10-05 14:55:57 +0100197 current_vm_locked, is_from_vm, dummy_senders[i]->id,
J-Alves60eaff92021-05-27 14:54:41 +0100198 bitmaps[2], false));
199 }
200
201 /** Clean up bind for other tests. */
J-Alvesd3e81622021-10-05 14:55:57 +0100202 vm_notifications_update_bindings(current_vm_locked, is_from_vm, 0,
J-Alves60eaff92021-05-27 14:54:41 +0100203 bitmaps[0], false);
J-Alvesd3e81622021-10-05 14:55:57 +0100204 vm_notifications_update_bindings(current_vm_locked, is_from_vm, 0,
J-Alves60eaff92021-05-27 14:54:41 +0100205 bitmaps[1], false);
206
J-Alvesd3e81622021-10-05 14:55:57 +0100207 vm_unlock(&current_vm_locked);
J-Alves60eaff92021-05-27 14:54:41 +0100208}
209
210/**
211 * Validates updates and check functions for binding notifications, namely the
J-Alves96f6e292021-06-08 17:32:40 +0100212 * configuration of bindings of global and per-vCPU notifications.
J-Alves60eaff92021-05-27 14:54:41 +0100213 */
214TEST_F(vm, vm_notification_bind_per_vcpu_vs_global)
215{
J-Alvesd3e81622021-10-05 14:55:57 +0100216 struct_vm *current_vm;
217 struct vm_locked current_vm_locked;
J-Alves60eaff92021-05-27 14:54:41 +0100218 struct_vm *dummy_sender;
219 ffa_notifications_bitmap_t global = 0x00000000FFFFFFFFU;
220 ffa_notifications_bitmap_t per_vcpu = ~global;
221 bool is_from_vm = true;
222
223 CHECK(vm_get_count() >= 2);
224
J-Alvesd3e81622021-10-05 14:55:57 +0100225 current_vm = vm_find_index(0);
J-Alves60eaff92021-05-27 14:54:41 +0100226
227 dummy_sender = vm_find_index(1);
228
J-Alvesd3e81622021-10-05 14:55:57 +0100229 current_vm_locked = vm_lock(current_vm);
J-Alves60eaff92021-05-27 14:54:41 +0100230
J-Alvesd3e81622021-10-05 14:55:57 +0100231 vm_notifications_update_bindings(current_vm_locked, is_from_vm,
J-Alves60eaff92021-05-27 14:54:41 +0100232 dummy_sender->id, global, false);
J-Alvesd3e81622021-10-05 14:55:57 +0100233 vm_notifications_update_bindings(current_vm_locked, is_from_vm,
J-Alves60eaff92021-05-27 14:54:41 +0100234 dummy_sender->id, per_vcpu, true);
235
236 /* Check validation of global notifications bindings. */
237 EXPECT_TRUE(vm_notifications_validate_binding(
J-Alvesd3e81622021-10-05 14:55:57 +0100238 current_vm_locked, is_from_vm, dummy_sender->id, global,
239 false));
J-Alves60eaff92021-05-27 14:54:41 +0100240
J-Alves96f6e292021-06-08 17:32:40 +0100241 /* Check validation of per-vCPU notifications bindings. */
J-Alves60eaff92021-05-27 14:54:41 +0100242 EXPECT_TRUE(vm_notifications_validate_binding(
J-Alvesd3e81622021-10-05 14:55:57 +0100243 current_vm_locked, is_from_vm, dummy_sender->id, per_vcpu,
244 true));
J-Alves60eaff92021-05-27 14:54:41 +0100245
246 /**
J-Alves96f6e292021-06-08 17:32:40 +0100247 * Check that global notifications are not validated as per-vCPU, and
J-Alves60eaff92021-05-27 14:54:41 +0100248 * vice-versa.
249 */
250 EXPECT_FALSE(vm_notifications_validate_binding(
J-Alvesd3e81622021-10-05 14:55:57 +0100251 current_vm_locked, is_from_vm, dummy_sender->id, global, true));
J-Alves60eaff92021-05-27 14:54:41 +0100252 EXPECT_FALSE(vm_notifications_validate_binding(
J-Alvesd3e81622021-10-05 14:55:57 +0100253 current_vm_locked, is_from_vm, dummy_sender->id, per_vcpu,
J-Alves60eaff92021-05-27 14:54:41 +0100254 false));
J-Alvesd3e81622021-10-05 14:55:57 +0100255 EXPECT_FALSE(vm_notifications_validate_binding(
256 current_vm_locked, is_from_vm, dummy_sender->id,
257 global | per_vcpu, true));
258 EXPECT_FALSE(vm_notifications_validate_binding(
259 current_vm_locked, is_from_vm, dummy_sender->id,
260 global | per_vcpu, false));
J-Alves60eaff92021-05-27 14:54:41 +0100261
262 /** Undo the bindings */
J-Alvesd3e81622021-10-05 14:55:57 +0100263 vm_notifications_update_bindings(current_vm_locked, is_from_vm, 0,
264 global, false);
265 EXPECT_TRUE(vm_notifications_validate_binding(
266 current_vm_locked, is_from_vm, 0, global, false));
J-Alves60eaff92021-05-27 14:54:41 +0100267
J-Alvesd3e81622021-10-05 14:55:57 +0100268 vm_notifications_update_bindings(current_vm_locked, is_from_vm, 0,
269 per_vcpu, false);
270 EXPECT_TRUE(vm_notifications_validate_binding(
271 current_vm_locked, is_from_vm, 0, per_vcpu, false));
J-Alves60eaff92021-05-27 14:54:41 +0100272
J-Alvesd3e81622021-10-05 14:55:57 +0100273 vm_unlock(&current_vm_locked);
J-Alves60eaff92021-05-27 14:54:41 +0100274}
275
J-Alvesce2f8d32021-06-10 18:30:21 +0100276/**
277 * Validates accesses to notifications bitmaps.
278 */
279TEST_F(vm, vm_notifications_set_and_get)
280{
J-Alvesd3e81622021-10-05 14:55:57 +0100281 struct_vm *current_vm;
282 struct vm_locked current_vm_locked;
J-Alvesce2f8d32021-06-10 18:30:21 +0100283 struct_vm *dummy_sender;
284 ffa_notifications_bitmap_t global = 0x00000000FFFFFFFFU;
285 ffa_notifications_bitmap_t per_vcpu = ~global;
286 ffa_notifications_bitmap_t ret;
287 const unsigned int vcpu_idx = 1;
288 struct notifications *notifications;
289 const bool is_from_vm = true;
290
291 CHECK(vm_get_count() >= 2);
292
J-Alvesd3e81622021-10-05 14:55:57 +0100293 current_vm = vm_find_index(0);
J-Alvesce2f8d32021-06-10 18:30:21 +0100294 dummy_sender = vm_find_index(1);
295
J-Alvesd3e81622021-10-05 14:55:57 +0100296 notifications = &current_vm->notifications.from_vm;
297 current_vm_locked = vm_lock(current_vm);
J-Alvesce2f8d32021-06-10 18:30:21 +0100298
J-Alvesd3e81622021-10-05 14:55:57 +0100299 vm_notifications_update_bindings(current_vm_locked, is_from_vm,
J-Alvesce2f8d32021-06-10 18:30:21 +0100300 dummy_sender->id, global, false);
J-Alvesd3e81622021-10-05 14:55:57 +0100301 vm_notifications_update_bindings(current_vm_locked, is_from_vm,
J-Alvesce2f8d32021-06-10 18:30:21 +0100302 dummy_sender->id, per_vcpu, true);
303
304 /*
J-Alvesd3e81622021-10-05 14:55:57 +0100305 * Validate get notifications bitmap for global notifications.
J-Alvesce2f8d32021-06-10 18:30:21 +0100306 */
J-Alvesd3e81622021-10-05 14:55:57 +0100307 vm_notifications_set(current_vm_locked, is_from_vm, global, 0ull,
308 false);
J-Alvesce2f8d32021-06-10 18:30:21 +0100309
J-Alvesd3e81622021-10-05 14:55:57 +0100310 ret = vm_notifications_get_pending_and_clear(current_vm_locked,
311 is_from_vm, 0ull);
J-Alvesce2f8d32021-06-10 18:30:21 +0100312 EXPECT_EQ(ret, global);
J-Alvesd3e81622021-10-05 14:55:57 +0100313 EXPECT_EQ(notifications->global.pending, 0ull);
J-Alvesce2f8d32021-06-10 18:30:21 +0100314
315 /*
J-Alvesd3e81622021-10-05 14:55:57 +0100316 * Validate get notifications bitmap for per-vCPU notifications.
J-Alvesce2f8d32021-06-10 18:30:21 +0100317 */
J-Alvesd3e81622021-10-05 14:55:57 +0100318 vm_notifications_set(current_vm_locked, is_from_vm, per_vcpu, vcpu_idx,
J-Alvesce2f8d32021-06-10 18:30:21 +0100319 true);
320
J-Alvesd3e81622021-10-05 14:55:57 +0100321 ret = vm_notifications_get_pending_and_clear(current_vm_locked,
322 is_from_vm, vcpu_idx);
J-Alvesce2f8d32021-06-10 18:30:21 +0100323 EXPECT_EQ(ret, per_vcpu);
J-Alvesd3e81622021-10-05 14:55:57 +0100324 EXPECT_EQ(notifications->per_vcpu[vcpu_idx].pending, 0ull);
J-Alvesce2f8d32021-06-10 18:30:21 +0100325
326 /*
J-Alvesd3e81622021-10-05 14:55:57 +0100327 * Validate that getting notifications for a specific vCPU also returns
328 * global notifications.
J-Alvesce2f8d32021-06-10 18:30:21 +0100329 */
J-Alvesd3e81622021-10-05 14:55:57 +0100330 vm_notifications_set(current_vm_locked, is_from_vm, per_vcpu, vcpu_idx,
J-Alvesce2f8d32021-06-10 18:30:21 +0100331 true);
J-Alvesd3e81622021-10-05 14:55:57 +0100332 vm_notifications_set(current_vm_locked, is_from_vm, global, 0ull,
333 false);
J-Alvesce2f8d32021-06-10 18:30:21 +0100334
J-Alvesd3e81622021-10-05 14:55:57 +0100335 ret = vm_notifications_get_pending_and_clear(current_vm_locked,
336 is_from_vm, vcpu_idx);
J-Alvesce2f8d32021-06-10 18:30:21 +0100337 EXPECT_EQ(ret, per_vcpu | global);
J-Alvesd3e81622021-10-05 14:55:57 +0100338 EXPECT_EQ(notifications->per_vcpu[vcpu_idx].pending, 0ull);
339 EXPECT_EQ(notifications->global.pending, 0ull);
J-Alvesce2f8d32021-06-10 18:30:21 +0100340
341 /** Undo the binding */
J-Alvesd3e81622021-10-05 14:55:57 +0100342 vm_notifications_update_bindings(current_vm_locked, is_from_vm, 0ull,
343 global, false);
344 vm_notifications_update_bindings(current_vm_locked, is_from_vm, 0ull,
345 per_vcpu, true);
346 vm_unlock(&current_vm_locked);
J-Alvesce2f8d32021-06-10 18:30:21 +0100347}
348
J-Alves96f6e292021-06-08 17:32:40 +0100349/**
350 * Validates simple getting of notifications info for global notifications.
351 */
352TEST_F(vm, vm_notifications_info_get_global)
353{
354 ffa_notifications_bitmap_t to_set = 0xFU;
355 ffa_notifications_bitmap_t got;
356
357 /**
358 * Following set of variables that are also expected to be used when
359 * handling FFA_NOTIFICATION_INFO_GET.
360 */
361 uint16_t ids[FFA_NOTIFICATIONS_INFO_GET_MAX_IDS] = {0};
362 uint32_t lists_sizes[FFA_NOTIFICATIONS_INFO_GET_MAX_IDS] = {0};
363 uint32_t ids_count = 0;
364 uint32_t lists_count = 0;
365 enum notifications_info_get_state current_state = INIT;
366
367 CHECK(vm_get_count() >= 2);
368
369 for (unsigned int i = 0; i < 2; i++) {
370 struct_vm *current_vm = vm_find_index(0);
371 struct vm_locked current_vm_locked = vm_lock(current_vm);
372 struct notifications *notifications =
373 &current_vm->notifications.from_sp;
374 const bool is_from_vm = false;
375
376 vm_notifications_set(current_vm_locked, is_from_vm, to_set, 0,
377 false);
378
379 vm_notifications_info_get_pending(
380 current_vm_locked, is_from_vm, ids, &ids_count,
381 lists_sizes, &lists_count,
382 FFA_NOTIFICATIONS_INFO_GET_MAX_IDS, &current_state);
383
384 /*
385 * Here the number of IDs and list count should be the same.
386 * As we are testing with Global notifications, this is
387 * expected.
388 */
389 EXPECT_EQ(ids_count, i + 1);
390 EXPECT_EQ(lists_count, i + 1);
391 EXPECT_EQ(lists_sizes[i], 0);
392 EXPECT_EQ(to_set, notifications->global.info_get_retrieved);
393
394 /* Action must be reset to initial state for each VM. */
395 current_state = INIT;
396
397 /*
398 * Check that getting pending notifications gives the expected
399 * return and cleans the 'pending' and 'info_get_retrieved'
400 * bitmaps.
401 */
402 got = vm_notifications_get_pending_and_clear(current_vm_locked,
403 is_from_vm, 0);
404 EXPECT_EQ(got, to_set);
405
406 EXPECT_EQ(notifications->global.info_get_retrieved, 0U);
407 EXPECT_EQ(notifications->global.pending, 0U);
408
409 vm_unlock(&current_vm_locked);
410 }
411}
412
413/**
414 * Validates simple getting of notifications info for per-vCPU notifications.
415 */
416TEST_F(vm, vm_notifications_info_get_per_vcpu)
417{
418 const ffa_notifications_bitmap_t per_vcpu = 0xFU;
419 ffa_notifications_bitmap_t got;
420
421 /*
422 * Following set of variables that are also expected to be used when
423 * handling ffa_notification_info_get.
424 */
425 uint16_t ids[FFA_NOTIFICATIONS_INFO_GET_MAX_IDS] = {0};
426 uint32_t ids_count = 0;
427 uint32_t lists_sizes[FFA_NOTIFICATIONS_INFO_GET_MAX_IDS] = {0};
428 uint32_t lists_count = 0;
429 enum notifications_info_get_state current_state = INIT;
430
431 CHECK(vm_get_count() >= 2);
432
433 for (unsigned int i = 0; i < 2; i++) {
434 struct_vm *current_vm = vm_find_index(0);
435 struct vm_locked current_vm_locked = vm_lock(current_vm);
436 struct notifications *notifications =
437 &current_vm->notifications.from_sp;
438 const bool is_from_vm = false;
439
440 vm_notifications_set(current_vm_locked, is_from_vm, per_vcpu, 0,
441 true);
442
443 vm_notifications_info_get_pending(
444 current_vm_locked, is_from_vm, ids, &ids_count,
445 lists_sizes, &lists_count,
446 FFA_NOTIFICATIONS_INFO_GET_MAX_IDS, &current_state);
447
448 /*
449 * Here the number of IDs and list count should be the same.
450 * As we are testing with Global notifications, this is
451 * expected.
452 */
453 EXPECT_EQ(ids_count, (i + 1) * 2);
454 EXPECT_EQ(lists_count, i + 1);
455 EXPECT_EQ(lists_sizes[i], 1);
456 EXPECT_EQ(per_vcpu,
457 notifications->per_vcpu[0].info_get_retrieved);
458
459 /* Action must be reset to initial state for each VM. */
460 current_state = INIT;
461
462 /*
463 * Check that getting pending notifications gives the expected
464 * return and cleans the 'pending' and 'info_get_retrieved'
465 * bitmaps.
466 */
467 got = vm_notifications_get_pending_and_clear(current_vm_locked,
468 is_from_vm, 0);
469 EXPECT_EQ(got, per_vcpu);
470
471 EXPECT_EQ(notifications->per_vcpu[0].info_get_retrieved, 0U);
472 EXPECT_EQ(notifications->per_vcpu[0].pending, 0U);
473
474 vm_unlock(&current_vm_locked);
475 }
476}
477
478/**
479 * Validate getting of notifications information if all VCPUs have notifications
480 * pending.
481 */
482TEST_F(vm, vm_notifications_info_get_per_vcpu_all_vcpus)
483{
484 struct_vm *current_vm = nullptr;
485 struct vm_locked current_vm_locked;
486 const ffa_vcpu_count_t vcpu_count = MAX_CPUS;
487 ffa_notifications_bitmap_t got;
488 const ffa_notifications_bitmap_t global = 0xF0000;
489
490 /*
491 * Following set of variables that are also expected to be used when
492 * handling ffa_notification_info_get.
493 */
494 struct notifications *notifications;
495 const bool is_from_sp = false;
496 uint16_t ids[FFA_NOTIFICATIONS_INFO_GET_MAX_IDS] = {0};
497 uint32_t ids_count = 0;
498 uint32_t lists_sizes[FFA_NOTIFICATIONS_INFO_GET_MAX_IDS] = {0};
499 uint32_t lists_count = 0;
500 enum notifications_info_get_state current_state = INIT;
501
502 EXPECT_TRUE(vm_init_next(vcpu_count, &ppool, &current_vm, false));
503 current_vm_locked = vm_lock(current_vm);
504 notifications = &current_vm->notifications.from_sp;
505
506 for (unsigned int i = 0; i < vcpu_count; i++) {
507 vm_notifications_set(current_vm_locked, is_from_sp,
508 FFA_NOTIFICATION_MASK(i), i, true);
509 }
510
511 /*
512 * Adding a global notification should not change the list of IDs,
513 * because global notifications only require the VM ID to be included in
514 * the list, at least once.
515 */
516 vm_notifications_set(current_vm_locked, is_from_sp, global, 0, false);
517
518 vm_notifications_info_get_pending(current_vm_locked, is_from_sp, ids,
519 &ids_count, lists_sizes, &lists_count,
520 FFA_NOTIFICATIONS_INFO_GET_MAX_IDS,
521 &current_state);
522
523 /*
524 * This test has been conceived for the expected MAX_CPUS 4.
525 * All VCPUs have notifications of the same VM, to be broken down in 2
526 * lists with 3 VCPU IDs, and 1 VCPU ID respectively.
527 * The list of IDs should look like: {<vm_id>, 0, 1, 2, <vm_id>, 3}.
528 */
529 CHECK(MAX_CPUS == 4);
530 EXPECT_EQ(ids_count, 6U);
531 EXPECT_EQ(lists_count, 2U);
532 EXPECT_EQ(lists_sizes[0], 3);
533 EXPECT_EQ(lists_sizes[1], 1);
534
535 for (unsigned int i = 0; i < vcpu_count; i++) {
536 got = vm_notifications_get_pending_and_clear(current_vm_locked,
537 is_from_sp, i);
538
539 /*
540 * The first call to vm_notifications_get_pending_and_clear
541 * should also include the global notifications on the return.
542 */
543 ffa_notifications_bitmap_t to_check =
544 (i != 0) ? FFA_NOTIFICATION_MASK(i)
545 : FFA_NOTIFICATION_MASK(i) | global;
546
547 EXPECT_EQ(got, to_check);
548
549 EXPECT_EQ(notifications->per_vcpu[i].pending, 0);
550 EXPECT_EQ(notifications->per_vcpu[i].info_get_retrieved, 0);
551 }
552
553 vm_unlock(&current_vm_locked);
554}
555
556/**
557 * Validate change of state from 'vm_notifications_info_get_pending', when the
558 * list of IDs is full.
559 */
560TEST_F(vm, vm_notifications_info_get_full_per_vcpu)
561{
562 struct_vm *current_vm = vm_find_index(0);
563 struct vm_locked current_vm_locked = vm_lock(current_vm);
564 struct notifications *notifications =
565 &current_vm->notifications.from_sp;
566 const bool is_from_vm = false;
567 ffa_notifications_bitmap_t got = 0;
568
569 /*
570 * Following set of variables that are also expected to be used when
571 * handling ffa_notification_info_get.
572 * For this 'ids_count' has been initialized such that it indicates
573 * there is no space in the list for a per-vCPU notification (VM ID and
574 * VCPU ID).
575 */
576 uint16_t ids[FFA_NOTIFICATIONS_INFO_GET_MAX_IDS] = {0};
577 uint32_t ids_count = FFA_NOTIFICATIONS_INFO_GET_MAX_IDS - 1;
578 uint32_t lists_sizes[FFA_NOTIFICATIONS_INFO_GET_MAX_IDS] = {0};
579 uint32_t lists_count = 10;
580 enum notifications_info_get_state current_state = INIT;
581 CHECK(vm_get_count() >= 2);
582
583 vm_notifications_set(current_vm_locked, is_from_vm,
584 FFA_NOTIFICATION_MASK(1), 0, true);
585
586 /* Call function to get notifications info, with only per-vCPU set. */
587 vm_notifications_info_get_pending(current_vm_locked, is_from_vm, ids,
588 &ids_count, lists_sizes, &lists_count,
589 FFA_NOTIFICATIONS_INFO_GET_MAX_IDS,
590 &current_state);
591
592 /*
593 * Verify that as soon as there isn't space to do the required
594 * insertion in the list, the 'vm_notifications_get_pending_and_clear'
595 * returns and changes list state to FULL.
596 * In this case returning, because it would need to add two IDs (VM ID
597 * and VCPU ID).
598 */
599 EXPECT_EQ(current_state, FULL);
600 EXPECT_EQ(ids_count, FFA_NOTIFICATIONS_INFO_GET_MAX_IDS - 1);
601 EXPECT_EQ(notifications->per_vcpu[0].info_get_retrieved, 0U);
602
603 /*
604 * At this point there is still room for the information of a global
605 * notification (only VM ID to be added). Reset 'current_state'
606 * for the insertion to happen at the last position of the array.
607 */
608 current_state = INIT;
609
610 /* Setting global notification */
611 vm_notifications_set(current_vm_locked, is_from_vm,
612 FFA_NOTIFICATION_MASK(2), 0, false);
613
614 vm_notifications_info_get_pending(current_vm_locked, is_from_vm, ids,
615 &ids_count, lists_sizes, &lists_count,
616 FFA_NOTIFICATIONS_INFO_GET_MAX_IDS,
617 &current_state);
618
619 /*
620 * Now List must be full, the set global notification must be part of
621 * 'info_get_retrieved', and the 'current_state' should be set to FULL
622 * due to the pending per-vCPU notification in VCPU 0.
623 */
624 EXPECT_EQ(ids_count, FFA_NOTIFICATIONS_INFO_GET_MAX_IDS);
625 EXPECT_EQ(current_state, FULL);
626 EXPECT_EQ(notifications->global.info_get_retrieved,
627 FFA_NOTIFICATION_MASK(2));
628
629 got = vm_notifications_get_pending_and_clear(current_vm_locked,
630 is_from_vm, 0);
631 EXPECT_EQ(got, FFA_NOTIFICATION_MASK(1) | FFA_NOTIFICATION_MASK(2));
632
633 vm_unlock(&current_vm_locked);
634}
635
636TEST_F(vm, vm_notifications_info_get_full_global)
637{
638 struct_vm *current_vm = vm_find_index(0);
639 struct vm_locked current_vm_locked = vm_lock(current_vm);
640 ffa_notifications_bitmap_t got;
641 struct notifications *notifications;
642 const bool is_from_vm = false;
643 /*
644 * Following set of variables that are also expected to be used when
645 * handling ffa_notification_info_get.
646 * For this 'ids_count' has been initialized such that it indicates
647 * there is no space in the list for a global notification (VM ID only).
648 */
649 uint16_t ids[FFA_NOTIFICATIONS_INFO_GET_MAX_IDS] = {0};
650 uint32_t ids_count = FFA_NOTIFICATIONS_INFO_GET_MAX_IDS;
651 uint32_t lists_sizes[FFA_NOTIFICATIONS_INFO_GET_MAX_IDS] = {0};
652 uint32_t lists_count = 10;
653 enum notifications_info_get_state current_state = INIT;
654
655 CHECK(vm_get_count() >= 1);
656
657 current_vm = vm_find_index(0);
658
659 notifications = &current_vm->notifications.from_sp;
660
661 /* Set global notification. */
662 vm_notifications_set(current_vm_locked, is_from_vm,
663 FFA_NOTIFICATION_MASK(10), 0, false);
664
665 /* Get notifications info for the given notifications. */
666 vm_notifications_info_get_pending(current_vm_locked, is_from_vm, ids,
667 &ids_count, lists_sizes, &lists_count,
668 FFA_NOTIFICATIONS_INFO_GET_MAX_IDS,
669 &current_state);
670
671 /* Expect 'info_get_retrieved' bitmap to be 0. */
672 EXPECT_EQ(notifications->global.info_get_retrieved, 0U);
673 EXPECT_EQ(notifications->global.pending, FFA_NOTIFICATION_MASK(10));
674 EXPECT_EQ(ids_count, FFA_NOTIFICATIONS_INFO_GET_MAX_IDS);
675 EXPECT_EQ(current_state, FULL);
676
677 got = vm_notifications_get_pending_and_clear(current_vm_locked,
678 is_from_vm, 0);
679 vm_unlock(&current_vm_locked);
680}
681
Andrew Scull3c257452019-11-26 13:32:50 +0000682} /* namespace */