stdcalltest: Add secure timer

Add an SMC that trusty-test can use to start
a secure timer used by the FP tests to verify
that the FP registers are correctly saved
while handling secure interrupts.

Bug: 284057071
Change-Id: Idd94114107a5020e85425679af405fa7c8c94da5
diff --git a/app/stdcalltest/stdcalltest.c b/app/stdcalltest/stdcalltest.c
index e6cb6d3..5616a90 100644
--- a/app/stdcalltest/stdcalltest.c
+++ b/app/stdcalltest/stdcalltest.c
@@ -27,11 +27,14 @@
  */
 #if WITH_LIB_SM
 
+#define LOCAL_TRACE 0
+
 #include <arch/arch_ops.h>
 #include <arch/ops.h>
 #include <err.h>
 #include <inttypes.h>
 #include <kernel/thread.h>
+#include <kernel/timer.h>
 #include <kernel/vm.h>
 #include <lib/sm.h>
 #include <lib/sm/sm_err.h>
@@ -299,6 +302,43 @@
 }
 #endif
 
+/* 1ms x5000=5s should be long enough for the test to finish */
+#define FPSIMD_TIMER_PERIOD_NS (1000000)
+#define FPSIMD_TIMER_TICKS (5000)
+
+static struct timer fpsimd_timers[SMP_MAX_CPUS];
+static uint fpsimd_timer_ticks[SMP_MAX_CPUS];
+
+static enum handler_return fpsimd_timer_cb(struct timer* timer,
+                                           lk_time_ns_t now,
+                                           void* arg) {
+    uint cpu = arch_curr_cpu_num();
+
+    fpsimd_timer_ticks[cpu]--;
+    if (!fpsimd_timer_ticks[cpu]) {
+        LTRACEF("Disabling FP test timer on cpu %u\n", cpu);
+        timer_cancel(&fpsimd_timers[cpu]);
+    }
+
+    return INT_NO_RESCHEDULE;
+}
+
+static long stdcalltest_clobber_fpsimd_timer(struct smc32_args* args) {
+    uint cpu = arch_curr_cpu_num();
+    bool start_timer = !fpsimd_timer_ticks[cpu];
+
+    DEBUG_ASSERT(arch_ints_disabled());
+
+    LTRACEF("Enabling FP test timer on cpu %u\n", cpu);
+    fpsimd_timer_ticks[cpu] = FPSIMD_TIMER_TICKS;
+    if (start_timer) {
+        timer_set_periodic_ns(&fpsimd_timers[cpu], FPSIMD_TIMER_PERIOD_NS,
+                              fpsimd_timer_cb, NULL);
+    }
+
+    return 1;
+}
+
 static long stdcalltest_stdcall(struct smc32_args* args) {
     switch (args->smc_nr) {
     case SMC_SC_TEST_VERSION:
@@ -334,14 +374,28 @@
     }
 }
 
+static long stdcalltest_nopcall(struct smc32_args* args) {
+    switch (args->params[0]) {
+    case SMC_NC_TEST_CLOBBER_FPSIMD_TIMER:
+        return stdcalltest_clobber_fpsimd_timer(args);
+    default:
+        return SM_ERR_UNDEFINED_SMC;
+    }
+}
+
 static struct smc32_entity stdcalltest_sm_entity = {
         .stdcall_handler = stdcalltest_stdcall,
         .fastcall_handler = stdcalltest_fastcall,
+        .nopcall_handler = stdcalltest_nopcall,
 };
 
 static void stdcalltest_init(uint level) {
     int err;
 
+    for (size_t i = 0; i < SMP_MAX_CPUS; i++) {
+        timer_initialize(&fpsimd_timers[i]);
+    }
+
     err = sm_register_entity(SMC_ENTITY_TEST, &stdcalltest_sm_entity);
     if (err) {
         printf("trusty error register entity: %d\n", err);
diff --git a/app/stdcalltest/stdcalltest.h b/app/stdcalltest/stdcalltest.h
index b30d72f..de37e08 100644
--- a/app/stdcalltest/stdcalltest.h
+++ b/app/stdcalltest/stdcalltest.h
@@ -95,4 +95,17 @@
  */
 #define SMC_FC_TEST_CLOBBER_FPSIMD_CHECK SMC_FASTCALL_NR(SMC_ENTITY_TEST, 1)
 
+/**
+ * SMC_NC_TEST_CLOBBER_FPSIMD_TIMER - Trigger the FP/SIMD test timer.
+ *
+ * Return: 1 on success, or one of the libsm errors otherwise.
+ *
+ * Trigger a secure timer that runs periodically a fixed number of
+ * times, then automatically disables itself.
+ *
+ * The timer is not strictly required for the test, so failing to
+ * start or stop the timer is not an error per se.
+ */
+#define SMC_NC_TEST_CLOBBER_FPSIMD_TIMER SMC_STDCALL_NR(SMC_ENTITY_TEST, 0)
+
 #define TRUSTY_STDCALLTEST_API_VERSION 1