Trusted Firmware-A Tests, version 2.0

This is the first public version of the tests for the Trusted
Firmware-A project. Please see the documentation provided in the
source tree for more details.

Change-Id: I6f3452046a1351ac94a71b3525c30a4ca8db7867
Signed-off-by: Sandrine Bailleux <sandrine.bailleux@arm.com>
Co-authored-by: amobal01 <amol.balasokamble@arm.com>
Co-authored-by: Antonio Nino Diaz <antonio.ninodiaz@arm.com>
Co-authored-by: Asha R <asha.r@arm.com>
Co-authored-by: Chandni Cherukuri <chandni.cherukuri@arm.com>
Co-authored-by: David Cunado <david.cunado@arm.com>
Co-authored-by: Dimitris Papastamos <dimitris.papastamos@arm.com>
Co-authored-by: Douglas Raillard <douglas.raillard@arm.com>
Co-authored-by: dp-arm <dimitris.papastamos@arm.com>
Co-authored-by: Jeenu Viswambharan <jeenu.viswambharan@arm.com>
Co-authored-by: Jonathan Wright <jonathan.wright@arm.com>
Co-authored-by: Kévin Petit <kevin.petit@arm.com>
Co-authored-by: Roberto Vargas <roberto.vargas@arm.com>
Co-authored-by: Sathees Balya <sathees.balya@arm.com>
Co-authored-by: Shawon Roy <Shawon.Roy@arm.com>
Co-authored-by: Soby Mathew <soby.mathew@arm.com>
Co-authored-by: Thomas Abraham <thomas.abraham@arm.com>
Co-authored-by: Vikram Kanigiri <vikram.kanigiri@arm.com>
Co-authored-by: Yatharth Kochar <yatharth.kochar@arm.com>
diff --git a/tftf/framework/timer/timer_framework.c b/tftf/framework/timer/timer_framework.c
new file mode 100644
index 0000000..e5e9a0f
--- /dev/null
+++ b/tftf/framework/timer/timer_framework.c
@@ -0,0 +1,567 @@
+/*
+ * Copyright (c) 2018, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <arch.h>
+#include <arch_helpers.h>
+#include <arm_gic.h>
+#include <debug.h>
+#include <errno.h>
+#include <irq.h>
+#include <mmio.h>
+#include <platform.h>
+#include <platform_def.h>
+#include <power_management.h>
+#include <sgi.h>
+#include <spinlock.h>
+#include <stddef.h>
+#include <sys/types.h>
+#include <tftf.h>
+#include <timer.h>
+
+
+/* Helper macros */
+#define TIMER_STEP_VALUE (plat_timer_info->timer_step_value)
+#define TIMER_IRQ (plat_timer_info->timer_irq)
+#define PROGRAM_TIMER(a) plat_timer_info->program(a)
+#define INVALID_CORE	UINT32_MAX
+#define INVALID_TIME	UINT64_MAX
+#define MAX_TIME_OUT_MS	10000
+
+/*
+ * Pointer containing available timer information for the platform.
+ */
+static const plat_timer_t *plat_timer_info;
+/*
+ * Interrupt requested time by cores in terms of absolute time.
+ */
+static volatile unsigned long long interrupt_req_time[PLATFORM_CORE_COUNT];
+/*
+ * Contains the target core number of the timer interrupt.
+ */
+static unsigned int current_prog_core = INVALID_CORE;
+/*
+ * Lock to get a consistent view for programming the timer
+ */
+static spinlock_t timer_lock;
+/*
+ * Number of system ticks per millisec
+ */
+static unsigned int systicks_per_ms;
+
+/*
+ * Stores per CPU timer handler invoked on expiration of the requested timeout.
+ */
+static irq_handler_t timer_handler[PLATFORM_CORE_COUNT];
+
+/* Helper function */
+static inline unsigned long long get_current_time_ms(void)
+{
+	assert(systicks_per_ms);
+	return mmio_read_64(SYS_CNT_BASE1 + CNTPCT_LO) / systicks_per_ms;
+}
+
+static inline unsigned long long get_current_prog_time(void)
+{
+	return current_prog_core == INVALID_CORE ?
+		0 : interrupt_req_time[current_prog_core];
+}
+
+int tftf_initialise_timer(void)
+{
+	int rc;
+	unsigned int i;
+
+	/*
+	 * Get platform specific timer information
+	 */
+	rc = plat_initialise_timer_ops(&plat_timer_info);
+	if (rc) {
+		ERROR("%s %d: No timer data found\n", __func__, __LINE__);
+		return rc;
+	}
+
+	/* Systems can't support single tick as a step value */
+	assert(TIMER_STEP_VALUE);
+
+	/* Initialise the array to max possible time */
+	for (i = 0; i < PLATFORM_CORE_COUNT; i++)
+		interrupt_req_time[i] = INVALID_TIME;
+
+	tftf_irq_register_handler(TIMER_IRQ, tftf_timer_framework_handler);
+	arm_gic_set_intr_priority(TIMER_IRQ, GIC_HIGHEST_NS_PRIORITY);
+	arm_gic_intr_enable(TIMER_IRQ);
+
+	/* Save the systicks per millisecond */
+	systicks_per_ms = read_cntfrq_el0() / 1000;
+
+	return 0;
+}
+
+/*
+ * It returns the core number of next timer request to be serviced or
+ * -1 if there is no request from any core. The next service request
+ * is the core whose interrupt needs to be fired first.
+ */
+static inline unsigned int get_lowest_req_core(void)
+{
+	unsigned long long lowest_timer = INVALID_TIME;
+	unsigned int lowest_core_req = INVALID_CORE;
+	unsigned int i;
+
+	/*
+	 * If 2 cores requested same value, give precedence
+	 * to the core with lowest core number
+	 */
+	for (i = 0; i < PLATFORM_CORE_COUNT; i++) {
+		if (interrupt_req_time[i] < lowest_timer) {
+			lowest_timer = interrupt_req_time[i];
+			lowest_core_req =  i;
+		}
+	}
+
+	return lowest_core_req;
+}
+
+int tftf_program_timer(unsigned long time_out_ms)
+{
+	unsigned int core_pos;
+	unsigned long long current_time;
+	u_register_t flags;
+	int rc = 0;
+
+	/*
+	 * Some timer implementations have a very small max timeouts due to
+	 * this if a request is asked for greater than the max time supported
+	 * by them either it has to be broken down and remembered or use
+	 * some other technique. Since that use case is not intended and
+	 * and to make the timer framework simple, max timeout requests
+	 * accepted by timer implementations can't be greater than
+	 * 10 seconds. Hence, all timer peripherals used in timer framework
+	 * has to support a timeout with interval of at least MAX_TIMEOUT.
+	 */
+	if ((time_out_ms > MAX_TIME_OUT_MS) || (time_out_ms == 0)) {
+		ERROR("%s : Greater than max timeout request\n", __func__);
+		return -1;
+	} else if (time_out_ms < TIMER_STEP_VALUE) {
+		time_out_ms = TIMER_STEP_VALUE;
+	}
+
+	core_pos = platform_get_core_pos(read_mpidr_el1());
+	/* A timer interrupt request is already available for the core */
+	assert(interrupt_req_time[core_pos] == INVALID_TIME);
+
+	flags = read_daif();
+	disable_irq();
+	spin_lock(&timer_lock);
+
+	assert((current_prog_core < PLATFORM_CORE_COUNT) ||
+		(current_prog_core == INVALID_CORE));
+
+	/*
+	 * Read time after acquiring timer_lock to account for any time taken
+	 * by lock contention.
+	 */
+	current_time = get_current_time_ms();
+
+	/* Update the requested time */
+	interrupt_req_time[core_pos] = current_time + time_out_ms;
+
+	VERBOSE("Need timer interrupt at: %lld current_prog_time:%lld\n"
+			" current time: %lld\n", interrupt_req_time[core_pos],
+					get_current_prog_time(),
+					get_current_time_ms());
+
+	/*
+	 * If the interrupt request time is less than the current programmed
+	 * by timer_step_value or timer is not programmed. Program it with
+	 * requested time and retarget the timer interrupt to the current
+	 * core.
+	 */
+	if ((!get_current_prog_time()) || (interrupt_req_time[core_pos] <
+				(get_current_prog_time() - TIMER_STEP_VALUE))) {
+
+		arm_gic_set_intr_target(TIMER_IRQ, core_pos);
+
+		rc = PROGRAM_TIMER(time_out_ms);
+		/* We don't expect timer programming to fail */
+		if (rc)
+			ERROR("%s %d: rc = %d\n", __func__, __LINE__, rc);
+
+		current_prog_core = core_pos;
+	}
+
+	spin_unlock(&timer_lock);
+	/* Restore DAIF flags */
+	write_daif(flags);
+	isb();
+
+	return rc;
+}
+
+int tftf_program_timer_and_suspend(unsigned long milli_secs,
+				   unsigned int pwr_state,
+				   int *timer_rc, int *suspend_rc)
+{
+	int rc = 0;
+	u_register_t flags;
+
+	/* Default to successful return codes */
+	int timer_rc_val = 0;
+	int suspend_rc_val = PSCI_E_SUCCESS;
+
+	/* Preserve DAIF flags. IRQs need to be disabled for this to work. */
+	flags = read_daif();
+	disable_irq();
+
+	/*
+	 * Even with IRQs masked, the timer IRQ will wake the CPU up.
+	 *
+	 * If the timer IRQ happens before entering suspend mode (because the
+	 * timer took too long to program, for example) the fact that the IRQ is
+	 * pending will prevent the CPU from entering suspend mode and not being
+	 * able to wake up.
+	 */
+	timer_rc_val = tftf_program_timer(milli_secs);
+	if (timer_rc_val == 0) {
+		suspend_rc_val = tftf_cpu_suspend(pwr_state);
+		if (suspend_rc_val != PSCI_E_SUCCESS) {
+			rc = -1;
+			INFO("%s %d: suspend_rc = %d\n", __func__, __LINE__,
+				suspend_rc_val);
+		}
+	} else {
+		rc = -1;
+		INFO("%s %d: timer_rc = %d\n", __func__, __LINE__, timer_rc_val);
+	}
+
+	/* Restore previous DAIF flags */
+	write_daif(flags);
+	isb();
+
+	if (timer_rc)
+		*timer_rc = timer_rc_val;
+	if (suspend_rc)
+		*suspend_rc = suspend_rc_val;
+	/*
+	 * If IRQs were disabled when calling this function, the timer IRQ
+	 * handler won't be called and the timer interrupt will be pending, but
+	 * that isn't necessarily a problem.
+	 */
+
+	return rc;
+}
+
+int tftf_program_timer_and_sys_suspend(unsigned long milli_secs,
+				   int *timer_rc, int *suspend_rc)
+{
+	int rc = 0;
+	u_register_t flags;
+
+	/* Default to successful return codes */
+	int timer_rc_val = 0;
+	int suspend_rc_val = PSCI_E_SUCCESS;
+
+	/* Preserve DAIF flags. IRQs need to be disabled for this to work. */
+	flags = read_daif();
+	disable_irq();
+
+	/*
+	 * Even with IRQs masked, the timer IRQ will wake the CPU up.
+	 *
+	 * If the timer IRQ happens before entering suspend mode (because the
+	 * timer took too long to program, for example) the fact that the IRQ is
+	 * pending will prevent the CPU from entering suspend mode and not being
+	 * able to wake up.
+	 */
+	timer_rc_val = tftf_program_timer(milli_secs);
+	if (timer_rc_val == 0) {
+		suspend_rc_val = tftf_system_suspend();
+		if (suspend_rc_val != PSCI_E_SUCCESS) {
+			rc = -1;
+			INFO("%s %d: suspend_rc = %d\n", __func__, __LINE__,
+				suspend_rc_val);
+		}
+	} else {
+		rc = -1;
+		INFO("%s %d: timer_rc = %d\n", __func__, __LINE__, timer_rc_val);
+	}
+
+	/* Restore previous DAIF flags */
+	write_daif(flags);
+	isb();
+
+	/*
+	 * If IRQs were disabled when calling this function, the timer IRQ
+	 * handler won't be called and the timer interrupt will be pending, but
+	 * that isn't necessarily a problem.
+	 */
+	if (timer_rc)
+		*timer_rc = timer_rc_val;
+	if (suspend_rc)
+		*suspend_rc = suspend_rc_val;
+
+	return rc;
+}
+
+int tftf_timer_sleep(unsigned long milli_secs)
+{
+	int ret, power_state;
+	uint32_t stateid;
+
+	ret = tftf_psci_make_composite_state_id(MPIDR_AFFLVL0,
+			PSTATE_TYPE_STANDBY, &stateid);
+	if (ret != PSCI_E_SUCCESS)
+		return -1;
+
+	power_state = tftf_make_psci_pstate(MPIDR_AFFLVL0, PSTATE_TYPE_STANDBY,
+			stateid);
+	ret = tftf_program_timer_and_suspend(milli_secs, power_state,
+								NULL, NULL);
+	if (ret != 0)
+		return -1;
+
+	return 0;
+}
+
+int tftf_cancel_timer(void)
+{
+	unsigned int core_pos = platform_get_core_pos(read_mpidr_el1());
+	unsigned int next_timer_req_core_pos;
+	unsigned long long current_time;
+	u_register_t flags;
+	int rc = 0;
+
+	/*
+	 * IRQ is disabled so that if a timer is fired after taking a lock,
+	 * it will remain pending and a core does not hit IRQ handler trying
+	 * to acquire an already locked spin_lock causing dead lock.
+	 */
+	flags = read_daif();
+	disable_irq();
+	spin_lock(&timer_lock);
+
+	interrupt_req_time[core_pos] = INVALID_TIME;
+
+	if (core_pos == current_prog_core) {
+		/*
+		 * Cancel the programmed interrupt at the peripheral. If the
+		 * timer interrupt is level triggered and fired this also
+		 * deactivates the pending interrupt.
+		 */
+		rc = plat_timer_info->cancel();
+		/* We don't expect cancel timer to fail */
+		if (rc) {
+			ERROR("%s %d: rc = %d\n", __func__, __LINE__, rc);
+			goto exit;
+		}
+
+		/*
+		 * For edge triggered interrupts, if an IRQ is fired before
+		 * cancel timer is executed, the signal remains pending. So,
+		 * clear the Timer IRQ if it is already pending.
+		 */
+		if (arm_gic_is_intr_pending(TIMER_IRQ))
+			arm_gic_intr_clear(TIMER_IRQ);
+
+		/* Get next timer consumer */
+		next_timer_req_core_pos = get_lowest_req_core();
+		if (next_timer_req_core_pos != INVALID_CORE) {
+
+			/* Retarget to the next_timer_req_core_pos */
+			arm_gic_set_intr_target(TIMER_IRQ, next_timer_req_core_pos);
+			current_prog_core = next_timer_req_core_pos;
+
+			current_time = get_current_time_ms();
+
+			/*
+			 * If the next timer request is lesser than or in a
+			 * window of TIMER_STEP_VALUE from current time,
+			 * program it to fire after TIMER_STEP_VALUE.
+			 */
+			if (interrupt_req_time[next_timer_req_core_pos] >
+					 current_time + TIMER_STEP_VALUE)
+				rc = PROGRAM_TIMER(interrupt_req_time[next_timer_req_core_pos] - current_time);
+			else
+				rc = PROGRAM_TIMER(TIMER_STEP_VALUE);
+			VERBOSE("Cancel and program new timer for core_pos: "
+						"%d %lld\n",
+						next_timer_req_core_pos,
+						get_current_prog_time());
+			/* We don't expect timer programming to fail */
+			if (rc)
+				ERROR("%s %d: rc = %d\n", __func__, __LINE__, rc);
+		} else {
+			current_prog_core = INVALID_CORE;
+			VERBOSE("Cancelling timer : %d\n", core_pos);
+		}
+	}
+exit:
+	spin_unlock(&timer_lock);
+
+	/* Restore DAIF flags */
+	write_daif(flags);
+	isb();
+
+	return rc;
+}
+
+int tftf_timer_framework_handler(void *data)
+{
+	unsigned int handler_core_pos = platform_get_core_pos(read_mpidr_el1());
+	unsigned int next_timer_req_core_pos;
+	unsigned long long current_time;
+	int rc = 0;
+
+	assert(interrupt_req_time[handler_core_pos] != INVALID_TIME);
+	spin_lock(&timer_lock);
+
+	current_time = get_current_time_ms();
+	/* Check if we interrupt is targeted correctly */
+	assert(handler_core_pos == current_prog_core);
+
+	interrupt_req_time[handler_core_pos] = INVALID_TIME;
+
+	/* Execute the driver handler */
+	if (plat_timer_info->handler)
+		plat_timer_info->handler();
+
+	if (arm_gic_is_intr_pending(TIMER_IRQ)) {
+		/*
+		 * We might never manage to acquire the printf lock here
+		 * (because we are in ISR context) but we're gonna panic right
+		 * after anyway so it doesn't really matter.
+		 */
+		ERROR("Timer IRQ still pending. Fatal error.\n");
+		panic();
+	}
+
+	/*
+	 * Execute the handler requested by the core, the handlers for the
+	 * other cores will be executed as part of handling IRQ_WAKE_SGI.
+	 */
+	if (timer_handler[handler_core_pos])
+		timer_handler[handler_core_pos](data);
+
+	/* Send interrupts to all the CPUS in the min time block */
+	for (int i = 0; i < PLATFORM_CORE_COUNT; i++) {
+		if ((interrupt_req_time[i] <=
+				(current_time + TIMER_STEP_VALUE))) {
+			interrupt_req_time[i] = INVALID_TIME;
+			tftf_send_sgi(IRQ_WAKE_SGI, i);
+		}
+	}
+
+	/* Get the next lowest requested timer core and program it */
+	next_timer_req_core_pos = get_lowest_req_core();
+	if (next_timer_req_core_pos != INVALID_CORE) {
+		/* Check we have not exceeded the time for next core */
+		assert(interrupt_req_time[next_timer_req_core_pos] >
+							current_time);
+		arm_gic_set_intr_target(TIMER_IRQ, next_timer_req_core_pos);
+		rc = PROGRAM_TIMER(interrupt_req_time[next_timer_req_core_pos]
+				 - current_time);
+	}
+	/* Update current program core to the newer one */
+	current_prog_core = next_timer_req_core_pos;
+
+	spin_unlock(&timer_lock);
+
+	return rc;
+}
+
+int tftf_timer_register_handler(irq_handler_t irq_handler)
+{
+	unsigned int core_pos = platform_get_core_pos(read_mpidr_el1());
+	int ret;
+
+	/* Validate no handler is registered */
+	assert(!timer_handler[core_pos]);
+	timer_handler[core_pos] = irq_handler;
+
+	/*
+	 * Also register same handler to IRQ_WAKE_SGI, as it can be waken
+	 * by it.
+	 */
+	ret = tftf_irq_register_handler(IRQ_WAKE_SGI, irq_handler);
+	assert(!ret);
+
+	return ret;
+}
+
+int tftf_timer_unregister_handler(void)
+{
+	unsigned int core_pos = platform_get_core_pos(read_mpidr_el1());
+	int ret;
+
+	/*
+	 * Unregister the handler for IRQ_WAKE_SGI also
+	 */
+	ret = tftf_irq_unregister_handler(IRQ_WAKE_SGI);
+	assert(!ret);
+	/* Validate a handler is registered */
+	assert(timer_handler[core_pos]);
+	timer_handler[core_pos] = 0;
+
+	return ret;
+}
+
+unsigned int tftf_get_timer_irq(void)
+{
+	/*
+	 * Check if the timer info is initialised
+	 */
+	assert(TIMER_IRQ);
+	return TIMER_IRQ;
+}
+
+unsigned int tftf_get_timer_step_value(void)
+{
+	assert(TIMER_STEP_VALUE);
+
+	return TIMER_STEP_VALUE;
+}
+
+/*
+ * There are 4 cases that could happen when a system is resuming from system
+ * suspend. The cases are:
+ * 1. The resumed core is the last core to power down and the
+ * timer interrupt was targeted to it.  In this case, target the
+ * interrupt to our core and set the appropriate priority and enable it.
+ *
+ * 2. The resumed core was the last core to power down but the timer interrupt
+ * is targeted to another core because of timer request grouping within
+ * TIMER_STEP_VALUE. In this case, re-target the interrupt to our core
+ * and set the appropriate priority and enable it
+ *
+ * 3. The system suspend request was down-graded by firmware and the timer
+ * interrupt is targeted to another core which woke up first. In this case,
+ * that core will wake us up and the interrupt_req_time[] corresponding to
+ * our core will be cleared. In this case, no need to do anything as GIC
+ * state is preserved.
+ *
+ * 4. The system suspend is woken up by another external interrupt other
+ * than the timer framework interrupt. In this case, just enable the
+ * timer interrupt and set the correct priority at GICD.
+ */
+void tftf_timer_gic_state_restore(void)
+{
+	unsigned int core_pos = platform_get_core_pos(read_mpidr_el1());
+	spin_lock(&timer_lock);
+
+	arm_gic_set_intr_priority(TIMER_IRQ, GIC_HIGHEST_NS_PRIORITY);
+	arm_gic_intr_enable(TIMER_IRQ);
+
+	/* Check if the programmed core is the woken up core */
+	if (interrupt_req_time[core_pos] == INVALID_TIME) {
+		INFO("The programmed core is not the one woken up\n");
+	} else {
+		current_prog_core = core_pos;
+		arm_gic_set_intr_target(TIMER_IRQ, core_pos);
+	}
+
+	spin_unlock(&timer_lock);
+}
+