test: add unit tests for timer management

This patch adds unit tests for helper functions introduced by the
timer management utility.

Change-Id: Ie8a295489fe0259b33a470bf1c69bf5283569ac5
Signed-off-by: Madhukar Pappireddy <madhukar.pappireddy@arm.com>
diff --git a/src/BUILD.gn b/src/BUILD.gn
index 5a3d575..406b4db 100644
--- a/src/BUILD.gn
+++ b/src/BUILD.gn
@@ -32,7 +32,6 @@
     "init.c",
     "load.c",
     "main.c",
-    "timer_mgmt.c",
   ]
   deps = [
     ":src_testable",
@@ -55,6 +54,7 @@
     "cpu.c",
     "manifest.c",
     "sp_pkg.c",
+    "timer_mgmt.c",
     "vcpu.c",
   ]
 
diff --git a/src/vm_test.cc b/src/vm_test.cc
index fd77a42..2fec26d 100644
--- a/src/vm_test.cc
+++ b/src/vm_test.cc
@@ -12,6 +12,7 @@
 #include "hf/check.h"
 #include "hf/list.h"
 #include "hf/mpool.h"
+#include "hf/timer_mgmt.h"
 #include "hf/vm.h"
 }
 
@@ -159,6 +160,82 @@
 	}
 }
 
+TEST_F(vm, vcpu_arch_timer)
+{
+	const cpu_id_t cpu_ids[2] = {0, 1};
+	struct_vcpu *vm0_vcpu;
+	struct_vcpu *vm1_vcpu;
+	struct_vcpu *deadline_vcpu;
+	struct_vcpu *target_vcpu;
+	struct vcpu_locked vcpu_locked;
+	struct cpu *cpu0;
+	struct cpu *cpu1;
+
+	/* Initialie CPU module with two physical CPUs. */
+	cpu_module_init(cpu_ids, 2);
+	cpu0 = cpu_find_index(0);
+	cpu1 = cpu_find_index(1);
+
+	/* Two UP endpoints are deployed for this test. */
+	CHECK(vm_get_count() >= 2);
+	vm0_vcpu = vm_get_vcpu(vm_find_index(0), 0);
+	vm1_vcpu = vm_get_vcpu(vm_find_index(1), 0);
+
+	/* The execution context of each VM is scheduled on CPU0. */
+	vm0_vcpu->cpu = cpu0;
+	vm1_vcpu->cpu = cpu0;
+
+	/*
+	 * Enable the timer peripheral for each vCPU and setup an arbitraty
+	 * countdown value.
+	 */
+	vm0_vcpu->regs.arch_timer.cval = 555555;
+	vm1_vcpu->regs.arch_timer.cval = 999999;
+	vm0_vcpu->regs.arch_timer.ctl = 1;
+	vm1_vcpu->regs.arch_timer.ctl = 1;
+
+	/* No vCPU is being tracked through either timer list. */
+	deadline_vcpu = timer_find_vcpu_nearest_deadline(cpu0);
+	EXPECT_TRUE(deadline_vcpu == NULL);
+	deadline_vcpu = timer_find_vcpu_nearest_deadline(cpu1);
+	EXPECT_TRUE(deadline_vcpu == NULL);
+
+	/* vCPU of VM0 and VM1 are being added to the list. */
+	timer_vcpu_manage(vm0_vcpu);
+	timer_vcpu_manage(vm1_vcpu);
+
+	deadline_vcpu = timer_find_vcpu_nearest_deadline(cpu0);
+	EXPECT_EQ(deadline_vcpu, vm0_vcpu);
+
+	/* Remove one of the vCPUs from the CPU0 list. */
+	vm0_vcpu->regs.arch_timer.cval = 0;
+	vm0_vcpu->regs.arch_timer.ctl = 0;
+	timer_vcpu_manage(vm0_vcpu);
+
+	/* This leaves one vCPU entry on CPU0 list. */
+	deadline_vcpu = timer_find_vcpu_nearest_deadline(cpu0);
+	EXPECT_EQ(deadline_vcpu, vm1_vcpu);
+
+	/* Attempt to migrate VM1 vCPU from CPU0 to CPU1. */
+	vcpu_locked = vcpu_lock(vm1_vcpu);
+	timer_migrate_to_other_cpu(cpu1, vcpu_locked);
+	vcpu_unlock(&vcpu_locked);
+
+	/*
+	 * After migration, ensure the list is empty on CPU0 but non-empty on
+	 * CPU1.
+	 */
+	deadline_vcpu = timer_find_vcpu_nearest_deadline(cpu0);
+	EXPECT_TRUE(deadline_vcpu == NULL);
+
+	/*
+	 * vCPU of VM1 is now running on CPU1. It must be the target vCPU when
+	 * the timer has expired.
+	 */
+	target_vcpu = timer_find_target_vcpu(vm1_vcpu);
+	EXPECT_EQ(target_vcpu, vm1_vcpu);
+}
+
 /**
  * Validates updates and check functions for binding notifications to endpoints.
  */