test(notifications): unit tests for info get functions
This patch includes a set of unit tests around the function
'vm_notifications_info_get_pending', which is at the core of the
handling of FFA_NOTIFICATIONS_INFO_GET.
Change-Id: I1b48dbc1b883d51a86f7c0359d349dd5d0e1b607
Signed-off-by: J-Alves <joao.alves@arm.com>
diff --git a/src/vm_test.cc b/src/vm_test.cc
index c45252a..4d47c54 100644
--- a/src/vm_test.cc
+++ b/src/vm_test.cc
@@ -29,6 +29,7 @@
using ::testing::SizeIs;
using struct_vm = struct vm;
+using struct_vm_locked = struct vm_locked;
constexpr size_t TEST_HEAP_SIZE = PAGE_SIZE * 32;
const int TOP_LEVEL = arch_mm_stage2_max_level();
@@ -208,7 +209,7 @@
/**
* Validates updates and check functions for binding notifications, namely the
- * configuration of bindings of global and per VCPU notifications.
+ * configuration of bindings of global and per-vCPU notifications.
*/
TEST_F(vm, vm_notification_bind_per_vcpu_vs_global)
{
@@ -237,13 +238,13 @@
current_vm_locked, is_from_vm, dummy_sender->id, global,
false));
- /* Check validation of per vcpu notifications bindings. */
+ /* Check validation of per-vCPU notifications bindings. */
EXPECT_TRUE(vm_notifications_validate_binding(
current_vm_locked, is_from_vm, dummy_sender->id, per_vcpu,
true));
/**
- * Check that global notifications are not validated as per VCPU, and
+ * Check that global notifications are not validated as per-vCPU, and
* vice-versa.
*/
EXPECT_FALSE(vm_notifications_validate_binding(
@@ -345,4 +346,337 @@
vm_unlock(¤t_vm_locked);
}
+/**
+ * Validates simple getting of notifications info for global notifications.
+ */
+TEST_F(vm, vm_notifications_info_get_global)
+{
+ ffa_notifications_bitmap_t to_set = 0xFU;
+ ffa_notifications_bitmap_t got;
+
+ /**
+ * Following set of variables that are also expected to be used when
+ * handling FFA_NOTIFICATION_INFO_GET.
+ */
+ uint16_t ids[FFA_NOTIFICATIONS_INFO_GET_MAX_IDS] = {0};
+ uint32_t lists_sizes[FFA_NOTIFICATIONS_INFO_GET_MAX_IDS] = {0};
+ uint32_t ids_count = 0;
+ uint32_t lists_count = 0;
+ enum notifications_info_get_state current_state = INIT;
+
+ CHECK(vm_get_count() >= 2);
+
+ for (unsigned int i = 0; i < 2; i++) {
+ struct_vm *current_vm = vm_find_index(0);
+ struct vm_locked current_vm_locked = vm_lock(current_vm);
+ struct notifications *notifications =
+ ¤t_vm->notifications.from_sp;
+ const bool is_from_vm = false;
+
+ vm_notifications_set(current_vm_locked, is_from_vm, to_set, 0,
+ false);
+
+ vm_notifications_info_get_pending(
+ current_vm_locked, is_from_vm, ids, &ids_count,
+ lists_sizes, &lists_count,
+ FFA_NOTIFICATIONS_INFO_GET_MAX_IDS, ¤t_state);
+
+ /*
+ * Here the number of IDs and list count should be the same.
+ * As we are testing with Global notifications, this is
+ * expected.
+ */
+ EXPECT_EQ(ids_count, i + 1);
+ EXPECT_EQ(lists_count, i + 1);
+ EXPECT_EQ(lists_sizes[i], 0);
+ EXPECT_EQ(to_set, notifications->global.info_get_retrieved);
+
+ /* Action must be reset to initial state for each VM. */
+ current_state = INIT;
+
+ /*
+ * Check that getting pending notifications gives the expected
+ * return and cleans the 'pending' and 'info_get_retrieved'
+ * bitmaps.
+ */
+ got = vm_notifications_get_pending_and_clear(current_vm_locked,
+ is_from_vm, 0);
+ EXPECT_EQ(got, to_set);
+
+ EXPECT_EQ(notifications->global.info_get_retrieved, 0U);
+ EXPECT_EQ(notifications->global.pending, 0U);
+
+ vm_unlock(¤t_vm_locked);
+ }
+}
+
+/**
+ * Validates simple getting of notifications info for per-vCPU notifications.
+ */
+TEST_F(vm, vm_notifications_info_get_per_vcpu)
+{
+ const ffa_notifications_bitmap_t per_vcpu = 0xFU;
+ ffa_notifications_bitmap_t got;
+
+ /*
+ * Following set of variables that are also expected to be used when
+ * handling ffa_notification_info_get.
+ */
+ uint16_t ids[FFA_NOTIFICATIONS_INFO_GET_MAX_IDS] = {0};
+ uint32_t ids_count = 0;
+ uint32_t lists_sizes[FFA_NOTIFICATIONS_INFO_GET_MAX_IDS] = {0};
+ uint32_t lists_count = 0;
+ enum notifications_info_get_state current_state = INIT;
+
+ CHECK(vm_get_count() >= 2);
+
+ for (unsigned int i = 0; i < 2; i++) {
+ struct_vm *current_vm = vm_find_index(0);
+ struct vm_locked current_vm_locked = vm_lock(current_vm);
+ struct notifications *notifications =
+ ¤t_vm->notifications.from_sp;
+ const bool is_from_vm = false;
+
+ vm_notifications_set(current_vm_locked, is_from_vm, per_vcpu, 0,
+ true);
+
+ vm_notifications_info_get_pending(
+ current_vm_locked, is_from_vm, ids, &ids_count,
+ lists_sizes, &lists_count,
+ FFA_NOTIFICATIONS_INFO_GET_MAX_IDS, ¤t_state);
+
+ /*
+ * Here the number of IDs and list count should be the same.
+ * As we are testing with Global notifications, this is
+ * expected.
+ */
+ EXPECT_EQ(ids_count, (i + 1) * 2);
+ EXPECT_EQ(lists_count, i + 1);
+ EXPECT_EQ(lists_sizes[i], 1);
+ EXPECT_EQ(per_vcpu,
+ notifications->per_vcpu[0].info_get_retrieved);
+
+ /* Action must be reset to initial state for each VM. */
+ current_state = INIT;
+
+ /*
+ * Check that getting pending notifications gives the expected
+ * return and cleans the 'pending' and 'info_get_retrieved'
+ * bitmaps.
+ */
+ got = vm_notifications_get_pending_and_clear(current_vm_locked,
+ is_from_vm, 0);
+ EXPECT_EQ(got, per_vcpu);
+
+ EXPECT_EQ(notifications->per_vcpu[0].info_get_retrieved, 0U);
+ EXPECT_EQ(notifications->per_vcpu[0].pending, 0U);
+
+ vm_unlock(¤t_vm_locked);
+ }
+}
+
+/**
+ * Validate getting of notifications information if all VCPUs have notifications
+ * pending.
+ */
+TEST_F(vm, vm_notifications_info_get_per_vcpu_all_vcpus)
+{
+ struct_vm *current_vm = nullptr;
+ struct vm_locked current_vm_locked;
+ const ffa_vcpu_count_t vcpu_count = MAX_CPUS;
+ ffa_notifications_bitmap_t got;
+ const ffa_notifications_bitmap_t global = 0xF0000;
+
+ /*
+ * Following set of variables that are also expected to be used when
+ * handling ffa_notification_info_get.
+ */
+ struct notifications *notifications;
+ const bool is_from_sp = false;
+ uint16_t ids[FFA_NOTIFICATIONS_INFO_GET_MAX_IDS] = {0};
+ uint32_t ids_count = 0;
+ uint32_t lists_sizes[FFA_NOTIFICATIONS_INFO_GET_MAX_IDS] = {0};
+ uint32_t lists_count = 0;
+ enum notifications_info_get_state current_state = INIT;
+
+ EXPECT_TRUE(vm_init_next(vcpu_count, &ppool, ¤t_vm, false));
+ current_vm_locked = vm_lock(current_vm);
+ notifications = ¤t_vm->notifications.from_sp;
+
+ for (unsigned int i = 0; i < vcpu_count; i++) {
+ vm_notifications_set(current_vm_locked, is_from_sp,
+ FFA_NOTIFICATION_MASK(i), i, true);
+ }
+
+ /*
+ * Adding a global notification should not change the list of IDs,
+ * because global notifications only require the VM ID to be included in
+ * the list, at least once.
+ */
+ vm_notifications_set(current_vm_locked, is_from_sp, global, 0, false);
+
+ vm_notifications_info_get_pending(current_vm_locked, is_from_sp, ids,
+ &ids_count, lists_sizes, &lists_count,
+ FFA_NOTIFICATIONS_INFO_GET_MAX_IDS,
+ ¤t_state);
+
+ /*
+ * This test has been conceived for the expected MAX_CPUS 4.
+ * All VCPUs have notifications of the same VM, to be broken down in 2
+ * lists with 3 VCPU IDs, and 1 VCPU ID respectively.
+ * The list of IDs should look like: {<vm_id>, 0, 1, 2, <vm_id>, 3}.
+ */
+ CHECK(MAX_CPUS == 4);
+ EXPECT_EQ(ids_count, 6U);
+ EXPECT_EQ(lists_count, 2U);
+ EXPECT_EQ(lists_sizes[0], 3);
+ EXPECT_EQ(lists_sizes[1], 1);
+
+ for (unsigned int i = 0; i < vcpu_count; i++) {
+ got = vm_notifications_get_pending_and_clear(current_vm_locked,
+ is_from_sp, i);
+
+ /*
+ * The first call to vm_notifications_get_pending_and_clear
+ * should also include the global notifications on the return.
+ */
+ ffa_notifications_bitmap_t to_check =
+ (i != 0) ? FFA_NOTIFICATION_MASK(i)
+ : FFA_NOTIFICATION_MASK(i) | global;
+
+ EXPECT_EQ(got, to_check);
+
+ EXPECT_EQ(notifications->per_vcpu[i].pending, 0);
+ EXPECT_EQ(notifications->per_vcpu[i].info_get_retrieved, 0);
+ }
+
+ vm_unlock(¤t_vm_locked);
+}
+
+/**
+ * Validate change of state from 'vm_notifications_info_get_pending', when the
+ * list of IDs is full.
+ */
+TEST_F(vm, vm_notifications_info_get_full_per_vcpu)
+{
+ struct_vm *current_vm = vm_find_index(0);
+ struct vm_locked current_vm_locked = vm_lock(current_vm);
+ struct notifications *notifications =
+ ¤t_vm->notifications.from_sp;
+ const bool is_from_vm = false;
+ ffa_notifications_bitmap_t got = 0;
+
+ /*
+ * Following set of variables that are also expected to be used when
+ * handling ffa_notification_info_get.
+ * For this 'ids_count' has been initialized such that it indicates
+ * there is no space in the list for a per-vCPU notification (VM ID and
+ * VCPU ID).
+ */
+ uint16_t ids[FFA_NOTIFICATIONS_INFO_GET_MAX_IDS] = {0};
+ uint32_t ids_count = FFA_NOTIFICATIONS_INFO_GET_MAX_IDS - 1;
+ uint32_t lists_sizes[FFA_NOTIFICATIONS_INFO_GET_MAX_IDS] = {0};
+ uint32_t lists_count = 10;
+ enum notifications_info_get_state current_state = INIT;
+ CHECK(vm_get_count() >= 2);
+
+ vm_notifications_set(current_vm_locked, is_from_vm,
+ FFA_NOTIFICATION_MASK(1), 0, true);
+
+ /* Call function to get notifications info, with only per-vCPU set. */
+ vm_notifications_info_get_pending(current_vm_locked, is_from_vm, ids,
+ &ids_count, lists_sizes, &lists_count,
+ FFA_NOTIFICATIONS_INFO_GET_MAX_IDS,
+ ¤t_state);
+
+ /*
+ * Verify that as soon as there isn't space to do the required
+ * insertion in the list, the 'vm_notifications_get_pending_and_clear'
+ * returns and changes list state to FULL.
+ * In this case returning, because it would need to add two IDs (VM ID
+ * and VCPU ID).
+ */
+ EXPECT_EQ(current_state, FULL);
+ EXPECT_EQ(ids_count, FFA_NOTIFICATIONS_INFO_GET_MAX_IDS - 1);
+ EXPECT_EQ(notifications->per_vcpu[0].info_get_retrieved, 0U);
+
+ /*
+ * At this point there is still room for the information of a global
+ * notification (only VM ID to be added). Reset 'current_state'
+ * for the insertion to happen at the last position of the array.
+ */
+ current_state = INIT;
+
+ /* Setting global notification */
+ vm_notifications_set(current_vm_locked, is_from_vm,
+ FFA_NOTIFICATION_MASK(2), 0, false);
+
+ vm_notifications_info_get_pending(current_vm_locked, is_from_vm, ids,
+ &ids_count, lists_sizes, &lists_count,
+ FFA_NOTIFICATIONS_INFO_GET_MAX_IDS,
+ ¤t_state);
+
+ /*
+ * Now List must be full, the set global notification must be part of
+ * 'info_get_retrieved', and the 'current_state' should be set to FULL
+ * due to the pending per-vCPU notification in VCPU 0.
+ */
+ EXPECT_EQ(ids_count, FFA_NOTIFICATIONS_INFO_GET_MAX_IDS);
+ EXPECT_EQ(current_state, FULL);
+ EXPECT_EQ(notifications->global.info_get_retrieved,
+ FFA_NOTIFICATION_MASK(2));
+
+ got = vm_notifications_get_pending_and_clear(current_vm_locked,
+ is_from_vm, 0);
+ EXPECT_EQ(got, FFA_NOTIFICATION_MASK(1) | FFA_NOTIFICATION_MASK(2));
+
+ vm_unlock(¤t_vm_locked);
+}
+
+TEST_F(vm, vm_notifications_info_get_full_global)
+{
+ struct_vm *current_vm = vm_find_index(0);
+ struct vm_locked current_vm_locked = vm_lock(current_vm);
+ ffa_notifications_bitmap_t got;
+ struct notifications *notifications;
+ const bool is_from_vm = false;
+ /*
+ * Following set of variables that are also expected to be used when
+ * handling ffa_notification_info_get.
+ * For this 'ids_count' has been initialized such that it indicates
+ * there is no space in the list for a global notification (VM ID only).
+ */
+ uint16_t ids[FFA_NOTIFICATIONS_INFO_GET_MAX_IDS] = {0};
+ uint32_t ids_count = FFA_NOTIFICATIONS_INFO_GET_MAX_IDS;
+ uint32_t lists_sizes[FFA_NOTIFICATIONS_INFO_GET_MAX_IDS] = {0};
+ uint32_t lists_count = 10;
+ enum notifications_info_get_state current_state = INIT;
+
+ CHECK(vm_get_count() >= 1);
+
+ current_vm = vm_find_index(0);
+
+ notifications = ¤t_vm->notifications.from_sp;
+
+ /* Set global notification. */
+ vm_notifications_set(current_vm_locked, is_from_vm,
+ FFA_NOTIFICATION_MASK(10), 0, false);
+
+ /* Get notifications info for the given notifications. */
+ vm_notifications_info_get_pending(current_vm_locked, is_from_vm, ids,
+ &ids_count, lists_sizes, &lists_count,
+ FFA_NOTIFICATIONS_INFO_GET_MAX_IDS,
+ ¤t_state);
+
+ /* Expect 'info_get_retrieved' bitmap to be 0. */
+ EXPECT_EQ(notifications->global.info_get_retrieved, 0U);
+ EXPECT_EQ(notifications->global.pending, FFA_NOTIFICATION_MASK(10));
+ EXPECT_EQ(ids_count, FFA_NOTIFICATIONS_INFO_GET_MAX_IDS);
+ EXPECT_EQ(current_state, FULL);
+
+ got = vm_notifications_get_pending_and_clear(current_vm_locked,
+ is_from_vm, 0);
+ vm_unlock(¤t_vm_locked);
+}
+
} /* namespace */