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/aarch32/arch.c b/tftf/framework/aarch32/arch.c
new file mode 100644
index 0000000..763ea1a
--- /dev/null
+++ b/tftf/framework/aarch32/arch.c
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2018, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <arch_helpers.h>
+#include <debug.h>
+#include <tftf.h>
+
+
+void tftf_arch_setup(void)
+{
+	if (!IS_IN_HYP())
+		panic();
+
+	write_hcr(HCR_TGE_BIT);
+}
diff --git a/tftf/framework/aarch32/asm_debug.S b/tftf/framework/aarch32/asm_debug.S
new file mode 100644
index 0000000..d2b2c79
--- /dev/null
+++ b/tftf/framework/aarch32/asm_debug.S
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2018, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <arch.h>
+#include <asm_macros.S>
+
+#if ENABLE_ASSERTIONS
+
+	.globl	asm_assert
+
+/* Since the max decimal input number is 65536 */
+#define MAX_DEC_DIVISOR		10000
+
+/* The offset to add to get ascii for numerals '0 - 9' */
+#define ASCII_OFFSET_NUM	'0'
+
+.section .rodata.assert_str, "aS"
+assert_msg1:
+	.asciz "ASSERT: File "
+assert_msg2:
+	.asciz " Line "
+
+/* ---------------------------------------------------------------------------
+ * Assertion support in assembly.
+ * The below function helps to support assertions in assembly where we do not
+ * have a C runtime stack. Arguments to the function are :
+ * r0 - File name
+ * r1 - Line no
+ * Clobber list : lr, r0 - r6
+ * ---------------------------------------------------------------------------
+ */
+func asm_assert
+	/*
+	 * Only print the output if LOG_LEVEL is higher or equal to
+	 * LOG_LEVEL_INFO, which is the default value for builds with DEBUG=1.
+	 */
+	/* Stash the parameters already in r0 and r1 */
+	mov	r5, r0
+	mov	r6, r1
+
+	/* Initialize crash console and verify success */
+	bl	plat_crash_console_init
+	cmp	r0, #0
+	beq	1f
+
+	/* Print file name */
+	ldr	r4, =assert_msg1
+	bl	asm_print_str
+	mov	r4, r5
+	bl	asm_print_str
+
+	/* Print line number string */
+	ldr	r4, =assert_msg2
+	bl	asm_print_str
+
+	/* Test for maximum supported line number */
+	ldr	r4, =~0xffff
+	tst	r6, r4
+	bne	1f
+	mov	r4, r6
+
+	/* Print line number in decimal */
+	mov	r6, #10			/* Divide by 10 after every loop iteration */
+	ldr	r5, =MAX_DEC_DIVISOR
+dec_print_loop:
+	udiv	r0, r4, r5			/* Quotient */
+	mls	r4, r0, r5, r4			/* Remainder */
+	add	r0, r0, #ASCII_OFFSET_NUM	/* Convert to ASCII */
+	bl	plat_crash_console_putc
+	udiv	r5, r5, r6			/* Reduce divisor */
+	cmp	r5, #0
+	bne	dec_print_loop
+
+	bl	plat_crash_console_flush
+
+1:
+	wfi
+	b	1b
+endfunc asm_assert
+
+/*
+ * This function prints a string from address in r4
+ * Clobber: lr, r0 - r4
+ */
+func asm_print_str
+	mov	r3, lr
+1:
+	ldrb	r0, [r4], #0x1
+	cmp	r0, #0
+	beq	2f
+	bl	plat_crash_console_putc
+	b	1b
+2:
+	bx	r3
+endfunc asm_print_str
+
+/*
+ * This function prints a hexadecimal number in r4.
+ * In: r4 = the hexadecimal to print.
+ * Clobber: lr, r0 - r3, r5
+ */
+func asm_print_hex
+	mov	r3, lr
+	mov	r5, #32  /* No of bits to convert to ascii */
+1:
+	sub	r5, r5, #4
+	lsr	r0, r4, r5
+	and	r0, r0, #0xf
+	cmp	r0, #0xa
+	blo	2f
+	/* Add by 0x27 in addition to ASCII_OFFSET_NUM
+	 * to get ascii for characters 'a - f'.
+	 */
+	add	r0, r0, #0x27
+2:
+	add	r0, r0, #ASCII_OFFSET_NUM
+	bl	plat_crash_console_putc
+	cmp	r5, #0
+	bne	1b
+	bx	r3
+endfunc asm_print_hex
+
+#endif /* ENABLE_ASSERTIONS */
diff --git a/tftf/framework/aarch32/entrypoint.S b/tftf/framework/aarch32/entrypoint.S
new file mode 100644
index 0000000..04a7d4c
--- /dev/null
+++ b/tftf/framework/aarch32/entrypoint.S
@@ -0,0 +1,175 @@
+/*
+ * Copyright (c) 2018, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <arch.h>
+#include <asm_macros.S>
+#include <tftf.h>
+
+	.globl	tftf_entrypoint
+	.globl	tftf_hotplug_entry
+
+/* ----------------------------------------------------------------------------
+ * Cold boot entry point for the primary CPU.
+ * ----------------------------------------------------------------------------
+ */
+func tftf_entrypoint
+	/* --------------------------------------------------------------------
+	 * Set the exception vectors
+	 * --------------------------------------------------------------------
+	 */
+	ldr	r0, =tftf_vector
+	stcopr	r0, HVBAR
+
+	/* --------------------------------------------------------------------
+	 * Enable the instruction cache and asynchronous interrupts.
+	 * --------------------------------------------------------------------
+	 */
+	ldcopr	r0, HSCTLR
+	ldr	r1, =(HSCTLR_I_BIT | HSCTLR_A_BIT)
+	orr	r0, r0, r1
+	stcopr	r0, HSCTLR
+	isb
+
+	/* --------------------------------------------------------------------
+	 * This code is expected to be executed only by the primary CPU.
+	 * Save the mpid for the first core that executes and if a secondary
+	 * CPU has lost its way make it spin forever.
+	 * --------------------------------------------------------------------
+	 */
+	bl	save_primary_mpid
+
+	/* --------------------------------------------------------------------
+	 * Zero out NOBITS sections. There are 2 of them:
+	 *   - the .bss section;
+	 *   - the coherent memory section.
+	 * --------------------------------------------------------------------
+	 */
+	ldr	r0, =__BSS_START__
+	ldr	r1, =__BSS_SIZE__
+	bl	zeromem
+
+	ldr	r0, =__COHERENT_RAM_START__
+	ldr	r1, =__COHERENT_RAM_UNALIGNED_SIZE__
+	bl	zeromem
+
+	/* --------------------------------------------------------------------
+	 * Give ourselves a small coherent stack to ease the pain of
+	 * initializing the MMU
+	 * --------------------------------------------------------------------
+	 */
+	ldcopr	r0, MPIDR
+	bl	platform_set_coherent_stack
+
+	bl	tftf_early_platform_setup
+	bl	tftf_plat_arch_setup
+
+	/* --------------------------------------------------------------------
+	 * Give ourselves a stack allocated in Normal -IS-WBWA memory
+	 * --------------------------------------------------------------------
+	 */
+	ldcopr	r0, MPIDR
+	bl	platform_set_stack
+
+	/* --------------------------------------------------------------------
+	 * tftf_cold_boot_main() will perform the remaining architectural and
+	 * platform setup, initialise the test framework's state, then run the
+	 * tests.
+	 * --------------------------------------------------------------------
+	 */
+	b	tftf_cold_boot_main
+endfunc tftf_entrypoint
+
+/* ----------------------------------------------------------------------------
+ * Entry point for a CPU that has just been powered up.
+ * In : r0 - context_id
+ * ----------------------------------------------------------------------------
+ */
+func tftf_hotplug_entry
+
+	/* --------------------------------------------------------------------
+	 * Preserve the context_id in a callee-saved register
+	 * --------------------------------------------------------------------
+	 */
+	mov	r4, r0
+
+	/* --------------------------------------------------------------------
+	 * Set the exception vectors
+	 * --------------------------------------------------------------------
+	 */
+	ldr	r0, =tftf_vector
+	stcopr	r0, HVBAR
+
+	/* --------------------------------------------------------------------
+	 * Enable the instruction cache and asynchronous interrupts.
+	 * --------------------------------------------------------------------
+	 */
+	ldcopr	r0, HSCTLR
+	ldr	r1, =(HSCTLR_I_BIT | HSCTLR_A_BIT)
+	orr	r0, r0, r1
+	stcopr	r0, HSCTLR
+	isb
+
+	/* --------------------------------------------------------------------
+	 * Give ourselves a small coherent stack to ease the pain of
+	 * initializing the MMU
+	 * --------------------------------------------------------------------
+	 */
+	ldcopr	r0, MPIDR
+	bl	platform_set_coherent_stack
+
+	/* --------------------------------------------------------------------
+	 * Enable the MMU
+	 * --------------------------------------------------------------------
+	 */
+	bl	tftf_plat_enable_mmu
+
+	/* --------------------------------------------------------------------
+	 * Give ourselves a stack in normal memory.
+	 * --------------------------------------------------------------------
+	 */
+	ldcopr	r0, MPIDR
+	bl	platform_set_stack
+
+	/* --------------------------------------------------------------------
+	 * Save the context_id for later retrieval by tests
+	 * --------------------------------------------------------------------
+	 */
+	ldcopr	r0, MPIDR
+	ldr	r1, =MPID_MASK
+	and	r0, r0, r1
+	bl	platform_get_core_pos
+
+	mov	r1, r4
+
+	bl	tftf_set_cpu_on_ctx_id
+
+	/* --------------------------------------------------------------------
+	 * Jump to warm boot main function
+	 * --------------------------------------------------------------------
+	 */
+	b	tftf_warm_boot_main
+endfunc tftf_hotplug_entry
+
+/* ----------------------------------------------------------------------------
+ * Saves the mpid of the primary core and if the primary core
+ * is already saved then it loops infinitely.
+ * ----------------------------------------------------------------------------
+ */
+func save_primary_mpid
+	ldr	r1, =tftf_primary_core
+	ldr	r0, [r1]
+	mov	r2, #INVALID_MPID
+	cmp	r0, r2
+	bne	panic
+	ldr	r2, =MPID_MASK
+	ldcopr	r0, MPIDR
+	and	r0, r0, r2
+	str	r0, [r1]
+	bx	lr
+panic:
+	/* Primary core MPID already saved */
+	b	panic
+endfunc save_primary_mpid
diff --git a/tftf/framework/aarch32/exceptions.S b/tftf/framework/aarch32/exceptions.S
new file mode 100644
index 0000000..1e6c574
--- /dev/null
+++ b/tftf/framework/aarch32/exceptions.S
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2018, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <arch.h>
+#include <asm_macros.S>
+#include <tftf.h>
+
+	.globl	tftf_vector
+
+vector_base tftf_vector
+	b	tftf_entrypoint
+	b	.		/* Undef */
+	b	.		/* Syscall */
+	b	.		/* Prefetch abort */
+	b	.		/* Data abort */
+	b	.		/* Hyp trap */
+	b	tftf_intr_handle/* IRQ */
+	b	.		/* FIQ */
+
+/* ----------------------------------------------------------------------------
+ * The IRQ exception handler
+ * ----------------------------------------------------------------------------
+ */
+func tftf_intr_handle
+	push	{r0 - r3, lr}
+	bl	tftf_irq_handler_dispatcher
+	pop	{r0 - r3, lr}
+	eret
+endfunc tftf_intr_handle
diff --git a/tftf/framework/aarch64/arch.c b/tftf/framework/aarch64/arch.c
new file mode 100644
index 0000000..dfaa9d1
--- /dev/null
+++ b/tftf/framework/aarch64/arch.c
@@ -0,0 +1,16 @@
+/*
+ * Copyright (c) 2018, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <arch_helpers.h>
+
+void tftf_arch_setup(void)
+{
+	/* Do not try to configure EL2 if TFTF is running at NS-EL1 */
+	if (IS_IN_EL2()) {
+		write_hcr_el2(HCR_TGE_BIT);
+		isb();
+	}
+}
diff --git a/tftf/framework/aarch64/asm_debug.S b/tftf/framework/aarch64/asm_debug.S
new file mode 100644
index 0000000..32c454f
--- /dev/null
+++ b/tftf/framework/aarch64/asm_debug.S
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 2018, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <arch.h>
+#include <asm_macros.S>
+
+#if ENABLE_ASSERTIONS
+
+	.globl	asm_assert
+
+/* Since the max decimal input number is 65536 */
+#define MAX_DEC_DIVISOR		10000
+/* The offset to add to get ascii for numerals '0 - 9' */
+#define ASCII_OFFSET_NUM	0x30
+
+.section .rodata.assert_str, "aS"
+assert_msg1:
+	.asciz "ASSERT: File "
+assert_msg2:
+	.asciz " Line "
+
+	/*
+	 * This macro is intended to be used to print the
+	 * line number in decimal. Used by asm_assert macro.
+	 * The max number expected is 65536.
+	 * In: x4 = the decimal to print.
+	 * Clobber: x30, x0, x1, x2, x5, x6
+	 */
+	.macro asm_print_line_dec
+	mov	x6, #10		/* Divide by 10 after every loop iteration */
+	mov	x5, #MAX_DEC_DIVISOR
+dec_print_loop:
+	udiv	x0, x4, x5		/* Get the quotient */
+	msub	x4, x0, x5, x4		/* Find the remainder */
+	add	x0, x0, #ASCII_OFFSET_NUM		/* Convert to ascii */
+	bl	plat_crash_console_putc
+	udiv	x5, x5, x6		/* Reduce divisor */
+	cbnz	x5, dec_print_loop
+	.endm
+
+/* ---------------------------------------------------------------------------
+ * Assertion support in assembly.
+ * The below function helps to support assertions in assembly where we do not
+ * have a C runtime stack. Arguments to the function are :
+ * x0 - File name
+ * x1 - Line no
+ * Clobber list : x30, x0, x1, x2, x3, x4, x5, x6.
+ * ---------------------------------------------------------------------------
+ */
+func asm_assert
+	mov	x5, x0
+	mov	x6, x1
+	/* Ensure the console is initialized */
+	bl	plat_crash_console_init
+	/* Check if the console is initialized */
+	cbz	x0, _assert_loop
+	/* The console is initialized */
+	adr	x4, assert_msg1
+	bl	asm_print_str
+	mov	x4, x5
+	bl	asm_print_str
+	adr	x4, assert_msg2
+	bl	asm_print_str
+	/* Check if line number higher than max permitted */
+	tst	x6, #~0xffff
+	b.ne	_assert_loop
+	mov	x4, x6
+	asm_print_line_dec
+	bl	plat_crash_console_flush
+_assert_loop:
+	wfi
+	b	_assert_loop
+endfunc asm_assert
+
+/*
+ * This function prints a string from address in x4.
+ * In: x4 = pointer to string.
+ * Clobber: x30, x0, x1, x2, x3
+ */
+func asm_print_str
+	mov	x3, x30
+1:
+	ldrb	w0, [x4], #0x1
+	cbz	x0, 2f
+	bl	plat_crash_console_putc
+	b	1b
+2:
+	ret	x3
+endfunc asm_print_str
+
+/*
+ * This function prints a hexadecimal number in x4.
+ * In: x4 = the hexadecimal to print.
+ * Clobber: x30, x0 - x3, x5
+ */
+func asm_print_hex
+	mov	x3, x30
+	mov	x5, #64  /* No of bits to convert to ascii */
+1:
+	sub	x5, x5, #4
+	lsrv	x0, x4, x5
+	and	x0, x0, #0xf
+	cmp	x0, #0xA
+	b.lo	2f
+	/* Add by 0x27 in addition to ASCII_OFFSET_NUM
+	 * to get ascii for characters 'a - f'.
+	 */
+	add	x0, x0, #0x27
+2:
+	add	x0, x0, #ASCII_OFFSET_NUM
+	bl	plat_crash_console_putc
+	cbnz	x5, 1b
+	ret	x3
+endfunc asm_print_hex
+
+#endif /* ENABLE_ASSERTIONS */
diff --git a/tftf/framework/aarch64/entrypoint.S b/tftf/framework/aarch64/entrypoint.S
new file mode 100644
index 0000000..dfedeae
--- /dev/null
+++ b/tftf/framework/aarch64/entrypoint.S
@@ -0,0 +1,181 @@
+/*
+ * Copyright (c) 2018, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <arch.h>
+#include <asm_macros.S>
+#include <tftf.h>
+
+	.globl	tftf_entrypoint
+	.globl	tftf_hotplug_entry
+
+
+/* ----------------------------------------------------------------------------
+ * Cold boot entry point for the primary CPU.
+ * ----------------------------------------------------------------------------
+ */
+func tftf_entrypoint
+	/* --------------------------------------------------------------------
+	 * Set the exception vectors
+	 * --------------------------------------------------------------------
+	 */
+	adr	x0, tftf_vector
+	asm_write_vbar_el1_or_el2 x1
+
+	/* --------------------------------------------------------------------
+	 * Enable the instruction cache, stack pointer and data access
+	 * alignment checks
+	 * --------------------------------------------------------------------
+	 */
+	mov	x1, #(SCTLR_I_BIT | SCTLR_A_BIT | SCTLR_SA_BIT)
+	asm_read_sctlr_el1_or_el2
+	orr	x0, x0, x1
+	asm_write_sctlr_el1_or_el2 x1
+	isb
+
+	/* --------------------------------------------------------------------
+	 * This code is expected to be executed only by the primary CPU.
+	 * Save the mpid for the first core that executes and if a secondary
+	 * CPU has lost its way make it spin forever.
+	 * --------------------------------------------------------------------
+	 */
+	bl	save_primary_mpid
+
+	/* --------------------------------------------------------------------
+	 * Zero out NOBITS sections. There are 2 of them:
+	 *   - the .bss section;
+	 *   - the coherent memory section.
+	 * --------------------------------------------------------------------
+	 */
+	ldr	x0, =__BSS_START__
+	ldr	x1, =__BSS_SIZE__
+	bl	zeromem16
+
+	ldr	x0, =__COHERENT_RAM_START__
+	ldr	x1, =__COHERENT_RAM_UNALIGNED_SIZE__
+	bl	zeromem16
+
+	/* --------------------------------------------------------------------
+	 * Give ourselves a small coherent stack to ease the pain of
+	 * initializing the MMU
+	 * --------------------------------------------------------------------
+	 */
+	mrs	x0, mpidr_el1
+	bl	platform_set_coherent_stack
+
+	bl	tftf_early_platform_setup
+	bl	tftf_plat_arch_setup
+
+	/* --------------------------------------------------------------------
+	 * Give ourselves a stack allocated in Normal -IS-WBWA memory
+	 * --------------------------------------------------------------------
+	 */
+	mrs	x0, mpidr_el1
+	bl	platform_set_stack
+
+	/* --------------------------------------------------------------------
+	 * tftf_cold_boot_main() will perform the remaining architectural and
+	 * platform setup, initialise the test framework's state, then run the
+	 * tests.
+	 * --------------------------------------------------------------------
+	 */
+	b	tftf_cold_boot_main
+
+dead:
+	b	dead
+endfunc tftf_entrypoint
+
+/* ----------------------------------------------------------------------------
+ * Entry point for a CPU that has just been powered up.
+ * In : x0 - context_id
+ * ----------------------------------------------------------------------------
+ */
+func tftf_hotplug_entry
+
+	/* --------------------------------------------------------------------
+	 * Preserve the context_id in a callee-saved register
+	 * --------------------------------------------------------------------
+	 */
+	mov	x19, x0
+
+	/* --------------------------------------------------------------------
+	 * Set the exception vectors
+	 * --------------------------------------------------------------------
+	 */
+	adr	x0, tftf_vector
+	asm_write_vbar_el1_or_el2 x1
+
+	/* --------------------------------------------------------------------
+	 * Enable the instruction cache, stack pointer and data access
+	 * alignment checks
+	 * --------------------------------------------------------------------
+	 */
+	mov	x1, #(SCTLR_I_BIT | SCTLR_A_BIT | SCTLR_SA_BIT)
+	asm_read_sctlr_el1_or_el2
+	orr	x0, x0, x1
+	asm_write_sctlr_el1_or_el2 x1
+	isb
+
+	/* --------------------------------------------------------------------
+	 * Give ourselves a small coherent stack to ease the pain of
+	 * initializing the MMU
+	 * --------------------------------------------------------------------
+	 */
+	mrs	x0, mpidr_el1
+	bl	platform_set_coherent_stack
+
+	/* --------------------------------------------------------------------
+	 * Enable the MMU
+	 * --------------------------------------------------------------------
+	 */
+	bl	tftf_plat_enable_mmu
+
+	/* --------------------------------------------------------------------
+	 * Give ourselves a stack in normal memory.
+	 * --------------------------------------------------------------------
+	 */
+	mrs	x0, mpidr_el1
+	bl	platform_set_stack
+
+	/* --------------------------------------------------------------------
+	 * Save the context_id for later retrieval by tests
+	 * --------------------------------------------------------------------
+	 */
+	mrs	x0, mpidr_el1
+	and	x0, x0, #MPID_MASK
+	bl	platform_get_core_pos
+
+	mov	x1, x19
+
+	bl	tftf_set_cpu_on_ctx_id
+
+	/* --------------------------------------------------------------------
+	 * Jump to warm boot main function
+	 * --------------------------------------------------------------------
+	 */
+	b	tftf_warm_boot_main
+endfunc tftf_hotplug_entry
+
+/* ----------------------------------------------------------------------------
+ * Saves the mpid of the primary core and if the primary core
+ * is already saved then it loops infinitely.
+ * ----------------------------------------------------------------------------
+ */
+func save_primary_mpid
+	adrp	x1, tftf_primary_core
+	ldr	w0, [x1, :lo12:tftf_primary_core]
+	mov	w2, #INVALID_MPID
+	cmp	w0, w2
+	b.ne	panic
+	mov	x2, #MPID_MASK
+	mrs	x0, mpidr_el1
+	and	x0, x0, x2
+	str	w0, [x1, :lo12:tftf_primary_core]
+	ret
+panic:
+	/* Primary core MPID already saved */
+	b	.
+	ret
+endfunc save_primary_mpid
diff --git a/tftf/framework/aarch64/exceptions.S b/tftf/framework/aarch64/exceptions.S
new file mode 100644
index 0000000..08bef46
--- /dev/null
+++ b/tftf/framework/aarch64/exceptions.S
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 2018, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <asm_macros.S>
+
+	.globl	tftf_vector
+
+vector_base tftf_vector
+	//-----------------------------------------------------
+	// Current EL with SP0 : 0x0 - 0x180
+	//-----------------------------------------------------
+vector_entry SynchronousExceptionSP0
+	b	SynchronousExceptionSP0
+	check_vector_size SynchronousExceptionSP0
+
+vector_entry IrqSP0
+	b	IrqSP0
+	check_vector_size IrqSP0
+
+vector_entry FiqSP0
+	b	FiqSP0
+	check_vector_size FiqSP0
+
+vector_entry SErrorSP0
+	b	SErrorSP0
+	check_vector_size SErrorSP0
+
+	//-----------------------------------------------------
+	// Current EL with SPx: 0x200 - 0x380
+	//-----------------------------------------------------
+vector_entry SynchronousExceptionSPx
+	b	SynchronousExceptionSPx
+	check_vector_size SynchronousExceptionSPx
+
+vector_entry IrqSPx
+	/*
+	 * TODO: Investigate whether the Trusted Firmware-A code for context
+	 * save/restore could be reused
+	 */
+	stp	x29, x30, [sp, #-0x10]!
+	bl	save_regs
+	bl	tftf_irq_handler_dispatcher
+	bl	restore_regs
+	ldp	x29, x30, [sp], #0x10
+	eret
+	check_vector_size IrqSPx
+
+vector_entry FiqSPx
+	b	FiqSPx
+	check_vector_size FiqSPx
+
+vector_entry SErrorSPx
+	b	SErrorSPx
+	check_vector_size SErrorSPx
+
+	//-----------------------------------------------------
+	// Lower EL using AArch64 : 0x400 - 0x580
+	//-----------------------------------------------------
+vector_entry SynchronousExceptionA64
+	b	SynchronousExceptionA64
+	check_vector_size SynchronousExceptionA64
+
+vector_entry IrqA64
+	b	IrqA64
+	check_vector_size IrqA64
+
+vector_entry FiqA64
+	b	FiqA64
+	check_vector_size FiqA64
+
+vector_entry SErrorA64
+	b	SErrorA64
+	check_vector_size SErrorA64
+
+	//-----------------------------------------------------
+ 	// Lower EL using AArch32 : 0x0 - 0x180
+	//-----------------------------------------------------
+vector_entry SynchronousExceptionA32
+	b	SynchronousExceptionA32
+	check_vector_size SynchronousExceptionA32
+
+vector_entry IrqA32
+	b	IrqA32
+	check_vector_size IrqA32
+
+vector_entry FiqA32
+	b	FiqA32
+	check_vector_size FiqA32
+
+vector_entry SErrorA32
+	b	SErrorA32
+	check_vector_size SErrorA32
+
+
+// Note: Exceptions will always be from the same EL, so no need to save spsr
+func save_regs
+	sub	sp, sp, #0x100
+	stp	x0, x1, [sp, #0x0]
+	stp	x2, x3, [sp, #0x10]
+	stp	x4, x5, [sp, #0x20]
+	stp	x6, x7, [sp, #0x30]
+	stp	x8, x9, [sp, #0x40]
+	stp	x10, x11, [sp, #0x50]
+	stp	x12, x13, [sp, #0x60]
+	stp	x14, x15, [sp, #0x70]
+	stp	x16, x17, [sp, #0x80]
+	stp	x18, x19, [sp, #0x90]
+	stp	x20, x21, [sp, #0xa0]
+	stp	x22, x23, [sp, #0xb0]
+	stp	x24, x25, [sp, #0xc0]
+	stp	x26, x27, [sp, #0xd0]
+	mrs     x0, sp_el0
+	stp	x28, x0, [sp, #0xe0]
+	str	x0, [sp, #0xf0]
+	ret
+endfunc save_regs
+
+
+// Note: Exceptions will always be from the same EL, so no need to restore spsr
+func restore_regs
+	ldr	x9, [sp, #0xf0]
+	ldp	x28, x9, [sp, #0xe0]
+	msr	sp_el0, x9
+	ldp	x26, x27, [sp, #0xd0]
+	ldp	x24, x25, [sp, #0xc0]
+	ldp	x22, x23, [sp, #0xb0]
+	ldp	x20, x21, [sp, #0xa0]
+	ldp	x18, x19, [sp, #0x90]
+	ldp	x16, x17, [sp, #0x80]
+	ldp	x14, x15, [sp, #0x70]
+	ldp	x12, x13, [sp, #0x60]
+	ldp	x10, x11, [sp, #0x50]
+	ldp	x8, x9, [sp, #0x40]
+	ldp	x6, x7, [sp, #0x30]
+	ldp	x4, x5, [sp, #0x20]
+	ldp	x2, x3, [sp, #0x10]
+	ldp	x0, x1, [sp, #0x0]
+	add	sp, sp, #0x100
+	ret
+endfunc restore_regs
diff --git a/tftf/framework/debug.c b/tftf/framework/debug.c
new file mode 100644
index 0000000..4b4364d
--- /dev/null
+++ b/tftf/framework/debug.c
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2018, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <console.h>
+#include <debug.h>
+
+#if DEBUG
+void __attribute__((__noreturn__)) do_panic(const char *file, int line)
+{
+	printf("PANIC in file: %s line: %d\n", file, line);
+
+	console_flush();
+
+	while (1)
+		continue;
+}
+
+void __attribute__((__noreturn__)) do_bug_unreachable(const char *file, int line)
+{
+	mp_printf("BUG: Unreachable code!\n");
+	do_panic(file, line);
+}
+
+#else
+void __attribute__((__noreturn__)) do_panic(void)
+{
+	printf("PANIC\n");
+
+	console_flush();
+
+	while (1)
+		continue;
+}
+
+void __attribute__((__noreturn__)) do_bug_unreachable(void)
+{
+	mp_printf("BUG: Unreachable code!\n");
+	do_panic();
+}
+#endif
diff --git a/tftf/framework/framework.mk b/tftf/framework/framework.mk
new file mode 100644
index 0000000..f6e1d7b
--- /dev/null
+++ b/tftf/framework/framework.mk
@@ -0,0 +1,115 @@
+#
+# Copyright (c) 2018, Arm Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+
+AUTOGEN_DIR		:=	$(BUILD_PLAT)/autogen
+
+include lib/xlat_tables_v2/xlat_tables.mk
+include lib/compiler-rt/compiler-rt.mk
+
+TFTF_INCLUDES	:= 					\
+	-I${AUTOGEN_DIR} 				\
+	-Itftf/framework/include			\
+	-Iinclude/common				\
+	-Iinclude/common/${ARCH}			\
+	-Iinclude/drivers				\
+	-Iinclude/drivers/arm				\
+	-Iinclude/drivers/io				\
+	-Iinclude/lib					\
+	-Iinclude/lib/${ARCH}				\
+	-Iinclude/lib/extensions			\
+	-Iinclude/lib/stdlib				\
+	-Iinclude/lib/stdlib/sys			\
+	-Iinclude/lib/utils				\
+	-Iinclude/lib/xlat_tables			\
+	-Iinclude/plat/common				\
+	-Iinclude/runtime_services			\
+	-Iinclude/runtime_services/secure_el0_payloads	\
+	-Iinclude/runtime_services/secure_el1_payloads
+
+# Standard C library source files
+STD_LIB_SOURCES		:=	lib/stdlib/abort.c			\
+				lib/stdlib/assert.c			\
+				lib/stdlib/mem.c			\
+				lib/stdlib/printf.c			\
+				lib/stdlib/putchar.c			\
+				lib/stdlib/puts.c			\
+				lib/stdlib/rand.c			\
+				lib/stdlib/strchr.c			\
+				lib/stdlib/strcmp.c			\
+				lib/stdlib/strlen.c			\
+				lib/stdlib/strncmp.c			\
+				lib/stdlib/strncpy.c			\
+				lib/stdlib/subr_prf.c
+
+FRAMEWORK_SOURCES	:=	${AUTOGEN_DIR}/tests_list.c
+
+FRAMEWORK_SOURCES	+=	$(addprefix tftf/,			\
+	framework/${ARCH}/arch.c					\
+	framework/${ARCH}/asm_debug.S					\
+	framework/${ARCH}/entrypoint.S					\
+	framework/${ARCH}/exceptions.S					\
+	framework/debug.c						\
+	framework/main.c						\
+	framework/nvm_results_helpers.c					\
+	framework/report.c						\
+	framework/timer/timer_framework.c				\
+	tests/common/test_helpers.c					\
+)
+
+FRAMEWORK_SOURCES	+=						\
+	lib/${ARCH}/cache_helpers.S					\
+	lib/${ARCH}/misc_helpers.S					\
+	lib/delay/delay.c						\
+	lib/events/events.c						\
+	lib/extensions/amu/${ARCH}/amu.c				\
+	lib/extensions/amu/${ARCH}/amu_helpers.S			\
+	lib/irq/irq.c							\
+	lib/locks/${ARCH}/spinlock.S					\
+	lib/power_management/hotplug/hotplug.c				\
+	lib/power_management/suspend/${ARCH}/asm_tftf_suspend.S		\
+	lib/power_management/suspend/tftf_suspend.c			\
+	lib/psci/psci.c							\
+	lib/sdei/sdei.c							\
+	lib/smc/${ARCH}/asm_smc.S					\
+	lib/smc/${ARCH}/smc.c						\
+	${STD_LIB_SOURCES}						\
+	lib/trusted_os/trusted_os.c					\
+	lib/utils/mp_printf.c						\
+	lib/utils/uuid.c						\
+	${XLAT_TABLES_LIB_SRCS}						\
+	plat/common/${ARCH}/platform_helpers.S				\
+	plat/common/${ARCH}/platform_mp_stack.S 			\
+	plat/common/plat_common.c					\
+	plat/common/plat_state_id.c					\
+	plat/common/plat_topology.c					\
+	plat/common/tftf_nvm_accessors.c
+
+
+FRAMEWORK_SOURCES	+=	${COMPILER_RT_SRCS}
+
+TFTF_LINKERFILE		:=	tftf/framework/tftf.ld.S
+
+
+TFTF_DEFINES :=
+
+TEST_REPORTS ?= uart:raw
+$(info Selected reports: $(TEST_REPORTS))
+ifneq (,$(findstring uart:raw,$(TEST_REPORTS)))
+  TFTF_DEFINES += -DTEST_REPORT_UART_RAW
+endif
+ifneq (,$(findstring uart:junit,$(TEST_REPORTS)))
+  TFTF_DEFINES += -DTEST_REPORT_UART_JUNIT
+endif
+ifneq (,$(findstring semihosting:raw,$(TEST_REPORTS)))
+  TFTF_DEFINES += -DTEST_REPORT_SEMIHOSTING_RAW
+endif
+ifneq (,$(findstring semihosting:junit,$(TEST_REPORTS)))
+  TFTF_DEFINES += -DTEST_REPORT_SEMIHOSTING_JUNIT
+endif
+
+# Enable dynamic translation tables
+PLAT_XLAT_TABLES_DYNAMIC := 1
+$(eval $(call add_define,TFTF_DEFINES,PLAT_XLAT_TABLES_DYNAMIC))
diff --git a/tftf/framework/include/nvm.h b/tftf/framework/include/nvm.h
new file mode 100644
index 0000000..3544c2a
--- /dev/null
+++ b/tftf/framework/include/nvm.h
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2018, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef __NVM_H__
+#define __NVM_H__
+
+#ifndef __ASSEMBLY__
+#include <stddef.h>
+#include <tftf.h>
+#include "tests_list.h"
+
+#define TEST_BUFFER_SIZE 0x80
+
+typedef struct {
+	/*
+	 * @brief Last executed TFTF build message which consists of date and
+	 * time when TFTF is built.
+	 *
+	 * A mismatch with the build message of currently executing binary will
+	 * determine whether TFTF data structures stored in NVM needs to be
+	 * initialised or not.
+	 */
+	char build_message[BUILD_MESSAGE_SIZE];
+
+	/*
+	 * The following 2 fields track the progress in the test session. They
+	 * indicate which test case we are dealing with and the progress of this
+	 * test, i.e. whether it hasn't started yet, or it is being executed
+	 * right now, ...
+	 */
+	test_ref_t		test_to_run;
+	test_progress_t		test_progress;
+
+	/*
+	 * @brief Scratch buffer for test internal use.
+	 *
+	 * A buffer that the test can use as a scratch area for whatever it is
+	 * doing.
+	 */
+	char testcase_buffer[TEST_BUFFER_SIZE];
+
+	/*
+	 * @brief Results of tests.
+	 *
+	 * @note TESTCASE_RESULT_COUNT is defined in tests_list.h
+	 * (auto-generated file).
+	 */
+	TESTCASE_RESULT testcase_results[TESTCASE_RESULT_COUNT];
+
+	/*
+	 * @brief Size of \a result_buffer.
+	 */
+	unsigned result_buffer_size;
+
+	/*
+	 * Buffer containing the output of all tests.
+	 * Each test appends its output to the end of \a result_buffer.
+	 * Tests which produce no output write nothing in \a result_buffer.
+	 */
+	 char *result_buffer;
+} tftf_state_t;
+
+/*
+ * Helper macros to access fields of \a tftf_state_t structure.
+ */
+#define TFTF_STATE_OFFSET(_field)		offsetof(tftf_state_t, _field)
+
+/*
+ * Return 1 if we need to start a new test session;
+ *        0 if we need to resume an interrupted session.
+ */
+unsigned int new_test_session(void);
+
+/*
+ * @brief Initialize NVM if necessary.
+ *
+ * When TFTF is launched on the target, its data structures need
+ * to be initialised in NVM. However if some test resets the board
+ * (as part of its normal behaviour or because it crashed) then
+ * TFTF data structure must be left unchanged in order to resume
+ * the test session where it has been left.
+ *
+ * This function detects whether TFTF has just been launched and if so
+ * initialises its data structures. If TFTF has just reset then it does
+ * nothing.
+ *
+ * @return STATUS_SUCCESS on success, another status code on failure.
+ */
+STATUS tftf_init_nvm(void);
+
+/*
+ * @brief Clean NVM.
+ *
+ * Clean TFTF data structures in NVM.
+ * This function must be called when all tests have completed.
+ *
+ * @return STATUS_SUCCESS on success, another status code on failure.
+ */
+STATUS tftf_clean_nvm(void);
+
+/* Writes the buffer to the flash at offset with length equal to
+ * size
+ * Returns: STATUS_FAIL, STATUS_SUCCESS, STATUS_OUT_OF_RESOURCES
+ */
+STATUS tftf_nvm_write(unsigned long long offset, const void *buffer, size_t size);
+
+/* Reads the flash into buffer at offset with length equal to
+ * size
+ * Returns: STATUS_FAIL, STATUS_SUCCESS, STATUS_OUT_OF_RESOURCES
+ */
+STATUS tftf_nvm_read(unsigned long long offset, void *buffer, size_t size);
+#endif /*__ASSEMBLY__*/
+
+#endif
diff --git a/tftf/framework/include/tftf.h b/tftf/framework/include/tftf.h
new file mode 100644
index 0000000..8231e28
--- /dev/null
+++ b/tftf/framework/include/tftf.h
@@ -0,0 +1,169 @@
+/*
+ * Copyright (c) 2018, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef __TFTF_H__
+#define __TFTF_H__
+
+#ifndef __ASSEMBLY__
+#include <status.h>
+#include <stddef.h>
+#include <tftf_lib.h>
+
+#define TFTF_WELCOME_STR	"Booting trusted firmware test framework"
+
+/* Maximum size of test output (in bytes) */
+#define TESTCASE_OUTPUT_MAX_SIZE	512
+
+/* Size of build message used to differentiate different TFTF binaries */
+#define BUILD_MESSAGE_SIZE 		0x20
+
+extern const char build_message[];
+
+typedef test_result_t (*test_function_t)(void);
+
+typedef struct {
+	/* Test result (success, crashed, failed, ...). */
+	test_result_t		result;
+	unsigned long long	duration;
+	/*
+	 * Offset of test output string from TEST_NVM_RESULT_BUFFER_OFFSET.
+	 * Only relevant if test has an output, i.e. if \a output_size is not
+	 * zero.
+	 */
+	unsigned		output_offset;
+	/* Size of test output string, excluding final \0. */
+	unsigned		output_size;
+} TESTCASE_RESULT;
+
+typedef struct {
+	unsigned		index;
+	const char		*name;
+	const char		*description;
+	test_function_t		test;
+} test_case_t;
+
+typedef struct {
+	const char		*name;
+	const char		*description;
+	const test_case_t	*testcases;
+} test_suite_t;
+
+/*
+ * Reference to a specific test.
+ */
+typedef struct {
+	unsigned int		testsuite_idx;
+	unsigned int		testcase_idx;
+} test_ref_t;
+
+/*
+ * The progress in the execution of a test.
+ * This is used to implement the following state machine.
+ *
+ *  +-> TEST_READY (initial state of the test)                  <--------------+
+ *  |        |                                                                 |
+ *  |        |  Test framework prepares the test environment.                  |
+ *  |        |                                                                 |
+ *  |        v                                                                 |
+ *  |   TEST_IN_PROGRESS                                                       |
+ *  |        |                                                                 |
+ *  |        |  Hand over to the test function.                                |
+ *  |        |  If the test wants to reboot the platform  ---> TEST_REBOOTING  |
+ *  |        |  Test function returns into framework.                |         |
+ *  |        |                                                       | Reboot  |
+ *  |        |                                                       |         |
+ *  |        |                                                       +---------+
+ *  |        v
+ *  |   TEST_COMPLETE
+ *  |        |
+ *  |        |  Do some framework management.
+ *  |        |  Move to next test.
+ *  +--------+
+ */
+typedef enum {
+	TEST_PROGRESS_MIN = 0,
+	TEST_READY = TEST_PROGRESS_MIN,
+	TEST_IN_PROGRESS,
+	TEST_COMPLETE,
+	TEST_REBOOTING,
+
+	TEST_PROGRESS_MAX,
+} test_progress_t;
+
+#define TEST_PROGRESS_IS_VALID(_progress)	\
+	((_progress >= TEST_PROGRESS_MIN) && (_progress < TEST_PROGRESS_MAX))
+
+/*
+ * The definition of this global variable is generated by the script
+ * 'tftf_generate_test_list' during the build process
+ */
+extern const test_suite_t testsuites[];
+
+extern TESTCASE_RESULT testcase_results[];
+
+/* Set/Get the test to run in NVM */
+STATUS tftf_set_test_to_run(const test_ref_t test_to_run);
+STATUS tftf_get_test_to_run(test_ref_t *test_to_run);
+/* Set/Get the progress of the current test in NVM */
+STATUS tftf_set_test_progress(test_progress_t test_progress);
+STATUS tftf_get_test_progress(test_progress_t *test_progress);
+
+/**
+** Save test result into NVM.
+*/
+STATUS tftf_testcase_set_result(const test_case_t *testcase,
+				test_result_t result,
+				unsigned long long duration);
+/**
+** Get a testcase result from NVM.
+**
+** @param[in]  testcase The targeted testcase.
+** @param[out] result Testcase result. Only \a result.result and
+**   \a result.duration are of interest for the caller and the 2 other fields
+**   should be ignored (they correspond to a location in NVM).
+** @param[out] test_output Buffer to store the test output, if any.
+**   \a test_output must be big enough to hold the whole test output.
+**   Test output will be \a TESTCASE_OUTPUT_MAX_SIZE bytes maximum.
+*/
+STATUS tftf_testcase_get_result(const test_case_t *testcase, TESTCASE_RESULT *result, char *test_output);
+
+void tftf_report_generate(void);
+
+/*
+ * Exit the TFTF.
+ * This function can be used when a fatal error is encountered or as part of the
+ * normal termination process. It does the necessary cleanups then put the
+ * core in a low-power state.
+ */
+void __dead2 tftf_exit(void);
+
+void tftf_arch_setup(void);
+
+/*
+ * This function detects the power state format used by PSCI which can
+ * be either extended or original format. For the Original format,
+ * the State-ID can either be NULL or can be using the recommended encoding.
+ * This function needs to be invoked once during cold boot prior to the
+ * invocation of any PSCI power state helper functions.
+ */
+void tftf_detect_psci_pstate_format(void);
+
+/*
+ * Run the next test on the calling CPU.
+ * Once the test is complete, if the calling CPU is the last one to exit the
+ * test then do the necessary bookkeeping, report the overall test result and
+ * move on to the next test. Otherwise, shut down the calling CPU.
+ *
+ * This function never returns.
+ */
+void __dead2 run_tests(void);
+
+/* Entry point for a CPU that has just been powered up */
+void tftf_hotplug_entry(void);
+
+#endif /*__ASSEMBLY__*/
+
+#endif
diff --git a/tftf/framework/main.c b/tftf/framework/main.c
new file mode 100644
index 0000000..3f94dc9
--- /dev/null
+++ b/tftf/framework/main.c
@@ -0,0 +1,599 @@
+/*
+ * Copyright (c) 2018, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <arch_helpers.h>
+#include <arm_gic.h>
+#include <assert.h>
+#include <debug.h>
+#include <irq.h>
+#include <mmio.h>
+#include <nvm.h>
+#include <plat_topology.h>
+#include <platform.h>
+#include <platform_def.h>
+#include <power_management.h>
+#include <psci.h>
+#include <sgi.h>
+#include <string.h>
+#include <sys/types.h>
+#include <tftf.h>
+#include <tftf_lib.h>
+#include <timer.h>
+
+/* version information for TFTF */
+extern const char version_string[];
+
+unsigned int lead_cpu_mpid;
+
+/* Defined in hotplug.c */
+extern volatile test_function_t test_entrypoint[PLATFORM_CORE_COUNT];
+
+/* Per-CPU results for the current test */
+static test_result_t test_results[PLATFORM_CORE_COUNT];
+
+/* Context ID passed to tftf_psci_cpu_on() */
+static u_register_t cpu_on_ctx_id_arr[PLATFORM_CORE_COUNT];
+
+static unsigned int test_is_rebooting;
+
+static inline const test_suite_t *current_testsuite(void)
+{
+	test_ref_t test_to_run;
+	tftf_get_test_to_run(&test_to_run);
+	return &testsuites[test_to_run.testsuite_idx];
+}
+
+static inline const test_case_t *current_testcase(void)
+{
+	test_ref_t test_to_run;
+	tftf_get_test_to_run(&test_to_run);
+	return &testsuites[test_to_run.testsuite_idx].
+		testcases[test_to_run.testcase_idx];
+}
+
+/*
+ * Identify the next test in the tests list and update the NVM data to point to
+ * that test.
+ * If there is no more tests to execute, return NULL.
+ * Otherwise, return the test case.
+ */
+static const test_case_t *advance_to_next_test(void)
+{
+	test_ref_t test_to_run;
+	const test_case_t *testcase;
+	unsigned int testcase_idx;
+	unsigned int testsuite_idx;
+
+#if DEBUG
+	test_progress_t progress;
+	tftf_get_test_progress(&progress);
+	assert(progress == TEST_COMPLETE);
+#endif
+
+	tftf_get_test_to_run(&test_to_run);
+	testcase_idx = test_to_run.testcase_idx;
+	testsuite_idx = test_to_run.testsuite_idx;
+
+	/* Move to the next test case in the current test suite */
+	++testcase_idx;
+	testcase = &testsuites[testsuite_idx].testcases[testcase_idx];
+
+	if (testcase->name == NULL) {
+		/*
+		 * There's no more test cases in the current test suite so move
+		 * to the first test case of the next test suite.
+		 */
+		const test_suite_t *testsuite;
+		testcase_idx = 0;
+		++testsuite_idx;
+		testsuite = &testsuites[testsuite_idx];
+		testcase = &testsuite->testcases[0];
+
+		if (testsuite->name == NULL) {
+			/*
+			 * This was the last test suite so there's no more tests
+			 * at all.
+			 */
+			return NULL;
+		}
+	}
+
+	VERBOSE("Moving to test (%u,%u)\n", testsuite_idx, testcase_idx);
+	test_to_run.testsuite_idx = testsuite_idx;
+	test_to_run.testcase_idx = testcase_idx;
+	tftf_set_test_to_run(test_to_run);
+	tftf_set_test_progress(TEST_READY);
+
+	return testcase;
+}
+
+/*
+ * This function is executed only by the lead CPU.
+ * It prepares the environment for the next test to run.
+ */
+static void prepare_next_test(void)
+{
+	unsigned int mpid;
+	unsigned int core_pos;
+	unsigned int cpu_node;
+
+	/* This function should be called by the lead CPU only */
+	assert((read_mpidr_el1() & MPID_MASK) == lead_cpu_mpid);
+
+	/*
+	 * Only the lead CPU should be powered on at this stage. All other CPUs
+	 * should be powered off or powering off. If some CPUs are not powered
+	 * off yet, wait for them to power off.
+	 */
+	for_each_cpu(cpu_node) {
+		mpid = tftf_get_mpidr_from_node(cpu_node);
+		if (mpid == lead_cpu_mpid)
+			assert(tftf_is_cpu_online(mpid));
+		else
+			while (tftf_psci_affinity_info(mpid, MPIDR_AFFLVL0)
+					  == PSCI_STATE_ON)
+				;
+	}
+
+	/* No CPU should have entered the test yet */
+	assert(tftf_get_ref_cnt() == 0);
+
+	/* Populate the test entrypoint for the lead CPU */
+	core_pos = platform_get_core_pos(lead_cpu_mpid);
+	test_entrypoint[core_pos] = (test_function_t) current_testcase()->test;
+
+	for (unsigned int i = 0; i < PLATFORM_CORE_COUNT; ++i)
+		test_results[i] = TEST_RESULT_NA;
+
+	NOTICE("Starting unittest '%s - %s'\n",
+		current_testsuite()->name, current_testcase()->name);
+
+	/* Program the watchdog */
+	tftf_platform_watchdog_set();
+
+	/* TODO: Take a 1st timestamp to be able to measure test duration */
+
+	tftf_set_test_progress(TEST_IN_PROGRESS);
+}
+
+/*
+ * Go through individual CPUs' test results and determine the overall
+ * test result from that.
+ */
+static test_result_t get_overall_test_result(void)
+{
+	test_result_t result = TEST_RESULT_NA;
+	unsigned int cpu_mpid;
+	unsigned int cpu_node;
+	unsigned int core_pos;
+
+	for_each_cpu(cpu_node) {
+		cpu_mpid = tftf_get_mpidr_from_node(cpu_node);
+		core_pos = platform_get_core_pos(cpu_mpid);
+
+		switch (test_results[core_pos]) {
+		case TEST_RESULT_NA:
+			VERBOSE("CPU%u not involved in the test\n", core_pos);
+			/* Ignoring */
+			break;
+
+		case TEST_RESULT_SKIPPED:
+			/*
+			 * If at least one CPU skipped the test, consider the
+			 * whole test as skipped as well.
+			 */
+			NOTICE("CPU%u skipped the test\n", core_pos);
+			return TEST_RESULT_SKIPPED;
+
+		case TEST_RESULT_SUCCESS:
+			result = TEST_RESULT_SUCCESS;
+			break;
+
+		case TEST_RESULT_FAIL:
+			ERROR("CPU%u failed the test\n", core_pos);
+			return TEST_RESULT_FAIL;
+
+		case TEST_RESULT_CRASHED:
+			/*
+			 * Means the CPU never returned from the test whereas it
+			 * was supposed to. Either there is a bug in the test's
+			 * implementation or some sort of unexpected crash
+			 * happened.
+			 * If at least one CPU crashed, consider the whole test
+			 * as crashed as well.
+			 */
+			ERROR("CPU%u never returned from the test!\n", core_pos);
+			return TEST_RESULT_CRASHED;
+
+		default:
+			ERROR("Unknown test result value: %u\n",
+				test_results[core_pos]);
+			panic();
+		}
+	}
+
+	/*
+	 * At least one CPU (i.e. the lead CPU) should have participated in the
+	 * test.
+	 */
+	assert(result != TEST_RESULT_NA);
+	return result;
+}
+
+/*
+ * This function is executed by the last CPU to exit the test only.
+ * It does the necessary bookkeeping and reports the overall test result.
+ * If it was the last test, it will also generate the final test report.
+ * Otherwise, it will reset the platform, provided that the platform
+ * supports reset from non-trusted world. This ensures that the next test
+ * runs in a clean environment
+ *
+ * Return 1 if this was the last test, 0 otherwise.
+ */
+static unsigned int close_test(void)
+{
+	const test_case_t *next_test;
+
+#if DEBUG
+	/*
+	 * Check that the test didn't pretend resetting the platform, when in
+	 * fact it returned into the framework.
+	 *
+	 * If that happens, the test implementation should be fixed.
+	 * However, it is not a fatal error so just flag the problem in debug
+	 * builds.
+	 */
+	test_progress_t progress;
+	tftf_get_test_progress(&progress);
+	assert(progress != TEST_REBOOTING);
+#endif /* DEBUG */
+
+	tftf_set_test_progress(TEST_COMPLETE);
+	test_is_rebooting = 0;
+
+	/* TODO: Take a 2nd timestamp and compute test duration */
+
+	/* Reset watchdog */
+	tftf_platform_watchdog_reset();
+
+	/* Ensure no CPU is still executing the test */
+	assert(tftf_get_ref_cnt() == 0);
+
+	/* Save test result in NVM */
+	test_result_t overall_test_result = get_overall_test_result();
+	tftf_testcase_set_result(current_testcase(),
+				overall_test_result,
+				0);
+
+	NOTICE("Unittest '%s - %s' complete. Result: %s\n",
+	       current_testsuite()->name, current_testcase()->name,
+	       test_result_to_string(overall_test_result));
+
+	/* The test is finished, let's move to the next one (if any) */
+	next_test = advance_to_next_test();
+
+	/* If this was the last test then report all results */
+	if (!next_test) {
+		tftf_report_generate();
+		tftf_clean_nvm();
+		return 1;
+	} else {
+#if (PLAT_SUPPORTS_NS_RESET && !NEW_TEST_SESSION && USE_NVM)
+		/*
+		 * Reset the platform so that the next test runs in a clean
+		 * environment.
+		 */
+		INFO("Reset platform before executing next test:%p\n",
+				(void *) &(next_test->test));
+		tftf_plat_reset();
+		bug_unreachable();
+#endif
+	}
+
+	return 0;
+}
+
+/*
+ * Hand over to lead CPU, i.e.:
+ *  1) Power on lead CPU
+ *  2) Power down calling CPU
+ */
+static void __dead2 hand_over_to_lead_cpu(void)
+{
+	int ret;
+	unsigned int mpid = read_mpidr_el1() & MPID_MASK;
+	unsigned int core_pos = platform_get_core_pos(mpid);
+
+	VERBOSE("CPU%u: Hand over to lead CPU%u\n", core_pos,
+		platform_get_core_pos(lead_cpu_mpid));
+
+	/*
+	 * Power on lead CPU.
+	 * The entry point address passed as the 2nd argument of tftf_cpu_on()
+	 * doesn't matter because it will be overwritten by prepare_next_test().
+	 * Pass a NULL pointer to easily catch the problem in case something
+	 * goes wrong.
+	 */
+	ret = tftf_cpu_on(lead_cpu_mpid, 0, 0);
+	if (ret != PSCI_E_SUCCESS) {
+		ERROR("CPU%u: Failed to power on lead CPU%u (%d)\n",
+			core_pos, platform_get_core_pos(lead_cpu_mpid), ret);
+		panic();
+	}
+
+	/* Wait for lead CPU to be actually powered on */
+	while (!tftf_is_cpu_online(lead_cpu_mpid))
+		;
+
+	/*
+	 * Lead CPU has successfully booted, let's now power down the calling
+	 * core.
+	 */
+	tftf_cpu_off();
+	panic();
+}
+
+void __dead2 run_tests(void)
+{
+	unsigned int mpid = read_mpidr_el1() & MPID_MASK;
+	unsigned int core_pos = platform_get_core_pos(mpid);
+	unsigned int test_session_finished;
+	unsigned int cpus_cnt;
+
+	while (1) {
+		if (mpid == lead_cpu_mpid && (tftf_get_ref_cnt() == 0))
+			prepare_next_test();
+
+		/*
+		 * Increment the reference count to indicate that the CPU is
+		 * participating in the test.
+		 */
+		tftf_inc_ref_cnt();
+
+		/*
+		 * Mark the CPU's test result as "crashed". This is meant to be
+		 * overwritten by the actual test result when the CPU returns
+		 * from the test function into the framework. In case the CPU
+		 * crashes in the test (and thus, never returns from it), this
+		 * variable will hold the right value.
+		 */
+		test_results[core_pos] = TEST_RESULT_CRASHED;
+
+		/*
+		 * Jump to the test entrypoint for this core.
+		 * - For the lead CPU, it has been populated by
+		 *   prepare_next_test()
+		 * - For other CPUs, it has been populated by tftf_cpu_on() or
+		 *   tftf_try_cpu_on()
+		 */
+		while (test_entrypoint[core_pos] == 0)
+			;
+
+		test_results[core_pos] = test_entrypoint[core_pos]();
+		test_entrypoint[core_pos] = 0;
+
+		/*
+		 * Decrement the reference count to indicate that the CPU is not
+		 * participating in the test any longer.
+		 */
+		cpus_cnt = tftf_dec_ref_cnt();
+
+		/*
+		 * Last CPU to exit the test gets to do the necessary
+		 * bookkeeping and to report the overall test result.
+		 * Other CPUs shut down.
+		 */
+		if (cpus_cnt == 0) {
+			test_session_finished = close_test();
+			if (test_session_finished)
+				break;
+
+			if (mpid != lead_cpu_mpid) {
+				hand_over_to_lead_cpu();
+				bug_unreachable();
+			}
+		} else {
+			tftf_cpu_off();
+			panic();
+		}
+	}
+
+	tftf_exit();
+
+	/* Should never reach this point */
+	bug_unreachable();
+}
+
+u_register_t tftf_get_cpu_on_ctx_id(unsigned int core_pos)
+{
+	assert(core_pos < PLATFORM_CORE_COUNT);
+
+	return cpu_on_ctx_id_arr[core_pos];
+}
+
+void tftf_set_cpu_on_ctx_id(unsigned int core_pos, u_register_t context_id)
+{
+	assert(core_pos < PLATFORM_CORE_COUNT);
+
+	cpu_on_ctx_id_arr[core_pos] = context_id;
+}
+
+unsigned int tftf_is_rebooted(void)
+{
+	return test_is_rebooting;
+}
+
+/*
+ * Return 0 if the test session can be resumed
+ *        -1 otherwise.
+ */
+static int resume_test_session(void)
+{
+	test_ref_t test_to_run;
+	test_progress_t test_progress;
+	const test_case_t *next_test;
+
+	/* Get back on our feet. Where did we stop? */
+	tftf_get_test_to_run(&test_to_run);
+	tftf_get_test_progress(&test_progress);
+	assert(TEST_PROGRESS_IS_VALID(test_progress));
+
+	switch (test_progress) {
+	case TEST_READY:
+		/*
+		 * The TFTF has reset in the framework code, before the test
+		 * actually started.
+		 * Nothing to update, just start the test from scratch.
+		 */
+		break;
+
+	case TEST_IN_PROGRESS:
+		/*
+		 * The test crashed, i.e. it couldn't complete.
+		 * Update the test result in NVM then move to the next test.
+		 */
+		INFO("Test has crashed, moving to the next one\n");
+		tftf_testcase_set_result(current_testcase(),
+					TEST_RESULT_CRASHED,
+					0);
+		next_test = advance_to_next_test();
+		if (!next_test) {
+			INFO("No more tests\n");
+			return -1;
+		}
+		break;
+
+	case TEST_COMPLETE:
+		/*
+		 * The TFTF has reset in the framework code, after the test had
+		 * completed but before we finished the framework maintenance
+		 * required to move to the next test.
+		 *
+		 * In this case, we don't know the exact state of the data:
+		 * maybe we had the time to update the test result,
+		 * maybe we had the time to move to the next test.
+		 * We can't be sure so let's stay on the safe side and just
+		 * restart the test session from the beginning...
+		 */
+		NOTICE("The test framework has been interrupted in the middle "
+			"of critical maintenance operations.\n");
+		NOTICE("Can't recover execution.\n");
+		return -1;
+
+	case TEST_REBOOTING:
+		/*
+		 * Nothing to update about the test session, as we want to
+		 * re-enter the same test. Just remember that the test is
+		 * rebooting in case it queries this information.
+		 */
+		test_is_rebooting = 1;
+		break;
+
+	default:
+		bug_unreachable();
+	}
+
+	return 0;
+}
+
+/*
+ * C entry point in the TFTF.
+ * This function is executed by the primary CPU only.
+ */
+void __dead2 tftf_cold_boot_main(void)
+{
+	STATUS status;
+	int rc;
+
+	NOTICE("%s\n", TFTF_WELCOME_STR);
+	NOTICE("%s\n", build_message);
+	NOTICE("%s\n\n", version_string);
+
+#ifndef AARCH32
+	NOTICE("Running at NS-EL%u\n", IS_IN_EL(1) ? 1 : 2);
+#else
+	NOTICE("Running in AArch32 HYP mode\n");
+#endif
+
+	tftf_arch_setup();
+	tftf_platform_setup();
+	tftf_init_topology();
+
+	tftf_irq_setup();
+
+	rc = tftf_initialise_timer();
+	if (rc != 0) {
+		ERROR("Failed to initialize the timer subsystem (%d).\n", rc);
+		tftf_exit();
+	}
+
+	/* Enable the SGI used by the timer management framework */
+	tftf_irq_enable(IRQ_WAKE_SGI, GIC_HIGHEST_NS_PRIORITY);
+	enable_irq();
+
+	if (new_test_session()) {
+		NOTICE("Starting a new test session\n");
+		status = tftf_init_nvm();
+		if (status != STATUS_SUCCESS) {
+			/*
+			 * TFTF will have an undetermined behavior if its data
+			 * structures have not been initialised. There's no
+			 * point in continuing execution.
+			 */
+			ERROR("FATAL: Failed to initialise internal data structures in NVM.\n");
+			tftf_clean_nvm();
+			tftf_exit();
+		}
+	} else {
+		NOTICE("Resuming interrupted test session\n");
+		rc = resume_test_session();
+		if (rc < 0) {
+			tftf_report_generate();
+			tftf_clean_nvm();
+			tftf_exit();
+		}
+	}
+
+	/* Initialise the CPUs status map */
+	tftf_init_cpus_status_map();
+
+	/*
+	 * Detect power state format and get power state information for
+	 * a platform.
+	 */
+	tftf_init_pstate_framework();
+
+	/* The lead CPU is always the primary core. */
+	lead_cpu_mpid = read_mpidr_el1() & MPID_MASK;
+
+	/*
+	 * Hand over to lead CPU if required.
+	 * If the primary CPU is not the lead CPU for the first test then:
+	 *  1) Power on the lead CPU
+	 *  2) Power down the primary CPU
+	 */
+	if ((read_mpidr_el1() & MPID_MASK) != lead_cpu_mpid) {
+		hand_over_to_lead_cpu();
+		bug_unreachable();
+	}
+
+	/* Enter the test session */
+	run_tests();
+
+	/* Should never reach this point */
+	bug_unreachable();
+}
+
+void __dead2 tftf_exit(void)
+{
+	NOTICE("Exiting tests.\n");
+
+	/* Let the platform code clean up if required */
+	tftf_platform_end();
+
+	while (1)
+		wfi();
+}
diff --git a/tftf/framework/nvm_results_helpers.c b/tftf/framework/nvm_results_helpers.c
new file mode 100644
index 0000000..34ef19f
--- /dev/null
+++ b/tftf/framework/nvm_results_helpers.c
@@ -0,0 +1,277 @@
+/*
+ * Copyright (c) 2018, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <arch_helpers.h>
+#include <assert.h>
+#include <debug.h>
+#include <nvm.h>
+#include <platform.h>
+#include <spinlock.h>
+#include <stdarg.h>
+#include <string.h>
+
+/*
+ * Temporary buffer to store 1 test output.
+ * This will eventually be saved into NVM at the end of the execution
+ * of this test.
+ */
+static char testcase_output[TESTCASE_OUTPUT_MAX_SIZE];
+/*
+ * A test output can be written in several pieces by calling
+ * tftf_testcase_printf() multiple times. testcase_output_idx keeps the position
+ * of the last character written in testcase_output buffer and allows to easily
+ * append a new string at next call to tftf_testcase_printf().
+ */
+static unsigned int testcase_output_idx;
+
+/* Lock to avoid concurrent accesses to the testcase output buffer */
+static spinlock_t testcase_output_lock;
+
+static tftf_state_t tftf_init_state = {
+	.build_message		= "",
+	.test_to_run		= {
+		.testsuite_idx	= 0,
+		.testcase_idx	= 0,
+	},
+	.test_progress		= TEST_READY,
+	.testcase_buffer	= { 0 },
+	.testcase_results	= {
+		{
+			.result		= TEST_RESULT_NA,
+			.duration	= 0,
+			.output_offset	= 0,
+			.output_size	= 0,
+		}
+	},
+	.result_buffer_size	= 0,
+	.result_buffer		= NULL,
+};
+
+unsigned int new_test_session(void)
+{
+/* NEW_TEST_SESSION == 1 => we always want to start a new session */
+#if NEW_TEST_SESSION
+	INFO("Always starting a new test session (NEW_TEST_SESSION == 1)\n");
+	return 1;
+#endif
+	char saved_build_msg[BUILD_MESSAGE_SIZE];
+
+	/*
+	 * Check the validity of the build message stored in NVM.
+	 * It is invalid when it doesn't match with the TFTF binary currently
+	 * executing.
+	 */
+	tftf_nvm_read(TFTF_STATE_OFFSET(build_message), saved_build_msg,
+		BUILD_MESSAGE_SIZE);
+	return !!strncmp(build_message, saved_build_msg, BUILD_MESSAGE_SIZE);
+}
+
+STATUS tftf_init_nvm(void)
+{
+	INFO("Initialising NVM\n");
+
+	/* Copy the build message to identify the TFTF */
+	strncpy(tftf_init_state.build_message, build_message, BUILD_MESSAGE_SIZE);
+	return tftf_nvm_write(0, &tftf_init_state, sizeof(tftf_init_state));
+}
+
+STATUS tftf_clean_nvm(void)
+{
+	unsigned char corrupt_build_message = '\0';
+
+	/*
+	 * This will cause TFTF to re-initialise its data structures next time
+	 * it runs.
+	 */
+	return tftf_nvm_write(TFTF_STATE_OFFSET(build_message),
+			&corrupt_build_message,
+			sizeof(corrupt_build_message));
+}
+
+STATUS tftf_set_test_to_run(const test_ref_t test_to_run)
+{
+	return tftf_nvm_write(TFTF_STATE_OFFSET(test_to_run), &test_to_run,
+			sizeof(test_to_run));
+}
+
+STATUS tftf_get_test_to_run(test_ref_t *test_to_run)
+{
+	assert(test_to_run != NULL);
+	return tftf_nvm_read(TFTF_STATE_OFFSET(test_to_run), test_to_run,
+			sizeof(*test_to_run));
+}
+
+STATUS tftf_set_test_progress(test_progress_t test_progress)
+{
+	return tftf_nvm_write(TFTF_STATE_OFFSET(test_progress), &test_progress,
+			sizeof(test_progress));
+}
+
+STATUS tftf_get_test_progress(test_progress_t *test_progress)
+{
+	assert(test_progress != NULL);
+	return tftf_nvm_read(TFTF_STATE_OFFSET(test_progress), test_progress,
+			sizeof(*test_progress));
+}
+
+STATUS tftf_testcase_set_result(const test_case_t *testcase,
+				test_result_t result,
+				unsigned long long duration)
+{
+	STATUS status;
+	unsigned result_buffer_size = 0;
+	TESTCASE_RESULT test_result;
+
+	assert(testcase != NULL);
+
+	/* Initialize Test case result */
+	test_result.result = result;
+	test_result.duration = duration;
+	test_result.output_offset = 0;
+	test_result.output_size = strlen(testcase_output);
+
+	/* Does the test have an output? */
+	if (test_result.output_size != 0) {
+		/* Get the size of the buffer containing all tests outputs */
+		status = tftf_nvm_read(TFTF_STATE_OFFSET(result_buffer_size),
+				&result_buffer_size, sizeof(unsigned));
+		if (status != STATUS_SUCCESS)
+			goto reset_test_output;
+
+		/*
+		 * Write the output buffer at the end of the string buffer in
+		 * NVM
+		 */
+		test_result.output_offset = result_buffer_size;
+		status = tftf_nvm_write(
+			TFTF_STATE_OFFSET(result_buffer) + result_buffer_size,
+			testcase_output, test_result.output_size + 1);
+		if (status != STATUS_SUCCESS)
+			goto reset_test_output;
+
+		/* And update the buffer size into NVM */
+		result_buffer_size += test_result.output_size + 1;
+		status = tftf_nvm_write(TFTF_STATE_OFFSET(result_buffer_size),
+					&result_buffer_size, sizeof(unsigned));
+		if (status != STATUS_SUCCESS)
+			goto reset_test_output;
+	}
+
+	/* Write the test result into NVM */
+	status = tftf_nvm_write(TFTF_STATE_OFFSET(testcase_results) +
+				(testcase->index * sizeof(TESTCASE_RESULT)),
+				&test_result, sizeof(TESTCASE_RESULT));
+
+reset_test_output:
+	/* Reset test output buffer for the next test */
+	testcase_output_idx = 0;
+	testcase_output[0] = 0;
+
+	return status;
+}
+
+STATUS tftf_testcase_get_result(const test_case_t *testcase,
+				TESTCASE_RESULT *result,
+				char *test_output)
+{
+	STATUS status;
+	unsigned output_size;
+
+	assert(testcase != NULL);
+	assert(result != NULL);
+	assert(test_output != NULL);
+
+	status = tftf_nvm_read(TFTF_STATE_OFFSET(testcase_results)
+			+ (testcase->index * sizeof(TESTCASE_RESULT)),
+			result, sizeof(TESTCASE_RESULT));
+	if (status != STATUS_SUCCESS) {
+		return status;
+	}
+
+	output_size = result->output_size;
+
+	if (output_size != 0) {
+		status = tftf_nvm_read(TFTF_STATE_OFFSET(result_buffer)
+				+ result->output_offset,
+				test_output, output_size);
+		if (status != STATUS_SUCCESS)
+			return status;
+	}
+
+	test_output[output_size] = 0;
+
+	return STATUS_SUCCESS;
+}
+
+int tftf_testcase_printf(const char *format, ...)
+{
+	va_list ap;
+	int available;
+	int written = -1;
+
+	spin_lock(&testcase_output_lock);
+
+	assert(sizeof(testcase_output) >= testcase_output_idx);
+	available = sizeof(testcase_output) - testcase_output_idx;
+	if (available == 0) {
+		ERROR("%s: Output buffer is full ; the string won't be printed.\n",
+			__func__);
+		ERROR("%s: Consider increasing TESTCASE_OUTPUT_MAX_SIZE value.\n",
+			__func__);
+		goto release_lock;
+	}
+
+	va_start(ap, format);
+	written = vsnprintf(&testcase_output[testcase_output_idx], available,
+			format, ap);
+	va_end(ap);
+
+	if (written < 0) {
+		ERROR("%s: Output error (%d)", __func__, written);
+		goto release_lock;
+	}
+	/*
+	 * If vsnprintf() truncated the string due to the size limit passed as
+	 * an argument then its return value is the number of characters (not
+	 * including the trailing '\0') which would have been written to the
+	 * final string if enough space had been available. Thus, a return value
+	 * of size or more means that the output was truncated.
+	 *
+	 * Adjust the value of 'written' to reflect what has been actually
+	 * written.
+	 */
+	if (written >= available) {
+		ERROR("%s: String has been truncated (%u/%u bytes written).\n",
+			__func__, available - 1, written);
+		ERROR("%s: Consider increasing TESTCASE_OUTPUT_MAX_SIZE value.\n",
+			__func__);
+		written = available - 1;
+	}
+
+	/*
+	 * Update testcase_output_idx to point to the '\0' of the buffer.
+	 * The next call of tftf_testcase_printf() will overwrite '\0' to
+	 * append its new string to the buffer.
+	 */
+	testcase_output_idx += written;
+
+release_lock:
+	spin_unlock(&testcase_output_lock);
+	return written;
+}
+
+void tftf_notify_reboot(void)
+{
+#if DEBUG
+	/* This function must be called by tests, not by the framework */
+	test_progress_t test_progress;
+	tftf_get_test_progress(&test_progress);
+	assert(test_progress == TEST_IN_PROGRESS);
+#endif /* DEBUG */
+
+	VERBOSE("Test intends to reset\n");
+	tftf_set_test_progress(TEST_REBOOTING);
+}
diff --git a/tftf/framework/report.c b/tftf/framework/report.c
new file mode 100644
index 0000000..4b0a857
--- /dev/null
+++ b/tftf/framework/report.c
@@ -0,0 +1,250 @@
+/*
+ * Copyright (c) 2018, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <assert.h>
+#include <debug.h>
+#include <platform_def.h>  /* For TESTCASE_OUTPUT_MAX_SIZE */
+#include <semihosting.h>
+#include <stdio.h>
+#include <string.h>
+#include <tftf.h>
+
+struct tftf_report_ops {
+	long (*open)(const char *fname);
+	void (*write)(long handle, const char *str);
+	void (*close)(long handle);
+};
+
+#define TEST_REPORT_JUNIT_FILENAME "tftf_report_junit.xml"
+#define TEST_REPORT_RAW_FILENAME "tftf_report_raw.txt"
+
+#if defined(TEST_REPORT_UART_RAW) || defined(TEST_REPORT_UART_JUNIT)
+static long tftf_report_uart_open(const char *fname)
+{
+	printf("********** %s **********\n", fname);
+	return 0;
+}
+
+static void tftf_report_uart_write(long handle, const char *str)
+{
+	(void)handle;
+	assert(str);
+	/* Not using printf to avoid doing two copies. */
+	while (*str) {
+		putchar(*str++);
+	}
+}
+
+static void tftf_report_uart_close(long handle)
+{
+	(void)handle;
+	printf("************************\n");
+}
+
+const struct tftf_report_ops tftf_report_uart_ops = {
+	.open = tftf_report_uart_open,
+	.write = tftf_report_uart_write,
+	.close = tftf_report_uart_close,
+};
+#endif /* defined(TEST_REPORT_UART_RAW) || defined(TEST_REPORT_UART_JUNIT) */
+
+#if defined(TEST_REPORT_UART_RAW) || defined(TEST_REPORT_SEMIHOSTING_RAW)
+static unsigned int total_tests;
+static unsigned int tests_stats[TEST_RESULT_MAX];
+
+static void tftf_update_tests_statistics(test_result_t result)
+{
+	assert(TEST_RESULT_IS_VALID(result));
+	total_tests++;
+	tests_stats[result]++;
+}
+
+static const char *test_result_strings[TEST_RESULT_MAX] = {
+	"Skipped", "Passed", "Failed", "Crashed",
+};
+
+const char *test_result_to_string(test_result_t result)
+{
+	assert(TEST_RESULT_IS_VALID(result));
+	return test_result_strings[result];
+}
+
+static void tftf_report_generate_raw(const struct tftf_report_ops *rops,
+					const char *fname)
+{
+#define WRITE(str) rops->write(file_handle, str)
+#define BUFFER_SIZE 200
+	unsigned i, j;
+	long file_handle;
+	char buffer[BUFFER_SIZE];
+	const test_case_t *testcases;
+	TESTCASE_RESULT testcase_result;
+	char test_output[TESTCASE_OUTPUT_MAX_SIZE];
+	STATUS status;
+
+	file_handle = rops->open(fname);
+	if (file_handle == -1)
+		return;
+
+	/* Extract the result of all the testcases */
+	WRITE("========== TEST REPORT ==========\n");
+	for (i = 0; testsuites[i].name != NULL; i++) {
+		snprintf(buffer, BUFFER_SIZE, "# Test suite '%s':\n", testsuites[i].name);
+		WRITE(buffer);
+		testcases = testsuites[i].testcases;
+
+		for (j = 0; testcases[j].name != NULL; j++) {
+			status = tftf_testcase_get_result(&testcases[j], &testcase_result, test_output);
+			if (status != STATUS_SUCCESS) {
+				WRITE("Failed to get test result.\n");
+				continue;
+			}
+
+			tftf_update_tests_statistics(testcase_result.result);
+			/* TODO: print test duration */
+			snprintf(buffer, BUFFER_SIZE, "\t - %s: %s\n", testcases[j].name,
+				test_result_to_string(testcase_result.result));
+			WRITE(buffer);
+
+			if (strlen(test_output) != 0) {
+				WRITE("--- output ---\n");
+				snprintf(buffer, BUFFER_SIZE, "%s", test_output);
+				WRITE(buffer);
+				WRITE("--------------\n");
+			}
+		}
+	}
+	WRITE("=================================\n");
+
+	for (i = TEST_RESULT_MIN; i < TEST_RESULT_MAX; i++) {
+		snprintf(buffer, BUFFER_SIZE, "Tests %-8s: %d\n",
+			test_result_to_string(i), tests_stats[i]);
+		WRITE(buffer);
+	}
+	snprintf(buffer, BUFFER_SIZE, "%-14s: %d\n", "Total tests", total_tests);
+	WRITE(buffer);
+	WRITE("=================================\n");
+
+	rops->close(file_handle);
+#undef BUFFER_SIZE
+#undef WRITE
+}
+#endif /* defined(TEST_REPORT_UART_RAW) || defined(TEST_REPORT_SEMIHOSTING_RAW) */
+
+#if defined(TEST_REPORT_SEMIHOSTING_RAW) || defined(TEST_REPORT_SEMIHOSTING_JUNIT)
+static long tftf_report_semihosting_open(const char *fname)
+{
+	/* Create the report on the semihosting */
+	long handle = semihosting_file_open(fname, FOPEN_MODE_WPLUS);
+	if (handle == -1) {
+		ERROR("Failed to create test report file \"%s\" on semihosting"
+			" [status = %ld].\n", fname, handle);
+	}
+	NOTICE("Opened file \"%s\" on semihosting with handle %ld.\n", fname, handle);
+	return handle;
+}
+
+static void tftf_report_semihosting_write(long handle, const char *str)
+{
+	size_t length = strlen(str);
+	semihosting_file_write(handle, &length, (const uintptr_t) str);
+}
+
+static void tftf_report_semihosting_close(long handle)
+{
+	semihosting_file_close(handle);
+	NOTICE("Closing file with handle %ld on semihosting.\n", handle);
+}
+
+const struct tftf_report_ops tftf_report_semihosting_ops = {
+	.open = tftf_report_semihosting_open,
+	.write = tftf_report_semihosting_write,
+	.close = tftf_report_semihosting_close,
+};
+#endif /* defined(TEST_REPORT_SEMIHOSTING_RAW) || defined(TEST_REPORT_SEMIHOSTING_JUNIT) */
+
+
+#if defined(TEST_REPORT_UART_JUNIT) || defined(TEST_REPORT_SEMIHOSTING_JUNIT)
+static void tftf_report_generate_junit(const struct tftf_report_ops *rops,
+					const char *fname)
+{
+#define WRITE(str) rops->write(file_handle, str)
+#define BUFFER_SIZE 200
+
+	long file_handle;
+	unsigned i, j;
+	const test_case_t *testcases;
+	TESTCASE_RESULT result;
+	char buffer[BUFFER_SIZE];
+	char test_output[TESTCASE_OUTPUT_MAX_SIZE];
+
+	file_handle = rops->open(fname);
+
+	if (file_handle == -1) {
+		return;
+	}
+	WRITE("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
+	WRITE("<testsuites>\n");
+
+	/* Extract the result of all the testcases */
+	for (i = 0; testsuites[i].name != NULL; i++) {
+		snprintf(buffer, BUFFER_SIZE, "<testsuite name=\"%s\">\n",
+			testsuites[i].name);
+		WRITE(buffer);
+		testcases = testsuites[i].testcases;
+		for (j = 0; testcases[j].name != NULL; j++) {
+			tftf_testcase_get_result(&testcases[j], &result, test_output);
+
+			snprintf(buffer, BUFFER_SIZE, "  <testcase name=\"%s\" time=\"%llu\"",
+				testcases[j].name, result.duration);
+			WRITE(buffer);
+			if (result.result == TEST_RESULT_SUCCESS) {
+				WRITE("/>\n");
+			} else {
+				WRITE(">\n");
+				if (result.result == TEST_RESULT_SKIPPED) {
+					WRITE("    <skipped/>\n");
+				} else {
+					WRITE("    <error type=\"failed\">\n");
+					WRITE(test_output);
+					WRITE("    </error>\n");
+				}
+				WRITE("  </testcase>\n");
+			}
+		}
+		WRITE("</testsuite>\n");
+	}
+
+	WRITE("</testsuites>\n");
+	rops->close(file_handle);
+#undef BUFFER_SIZE
+#undef WRITE
+}
+#endif /* defined(TEST_REPORT_UART_JUNIT) || defined(TEST_REPORT_SEMIHOSTING_JUNIT) */
+
+void tftf_report_generate(void)
+{
+	int nb_reports = 0;
+#ifdef TEST_REPORT_UART_RAW
+	tftf_report_generate_raw(&tftf_report_uart_ops, "raw");
+	nb_reports++;
+#endif
+#ifdef TEST_REPORT_UART_JUNIT
+	tftf_report_generate_junit(&tftf_report_uart_ops, "junit");
+	nb_reports++;
+#endif
+#ifdef TEST_REPORT_SEMIHOSTING_RAW
+	tftf_report_generate_raw(&tftf_report_semihosting_ops,
+				TEST_REPORT_RAW_FILENAME);
+	nb_reports++;
+#endif
+#ifdef TEST_REPORT_SEMIHOSTING_JUNIT
+	tftf_report_generate_junit(&tftf_report_semihosting_ops,
+				TEST_REPORT_JUNIT_FILENAME);
+	nb_reports++;
+#endif
+	assert(nb_reports > 0);
+}
diff --git a/tftf/framework/tftf.ld.S b/tftf/framework/tftf.ld.S
new file mode 100644
index 0000000..9432a74
--- /dev/null
+++ b/tftf/framework/tftf.ld.S
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2018, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <platform_def.h>
+#include <xlat_tables_defs.h>
+
+OUTPUT_FORMAT(PLATFORM_LINKER_FORMAT)
+OUTPUT_ARCH(PLATFORM_LINKER_ARCH)
+ENTRY(tftf_entrypoint)
+
+MEMORY {
+    RAM (rwx): ORIGIN = DRAM_BASE, LENGTH = DRAM_SIZE
+}
+
+
+SECTIONS
+{
+    . = TFTF_BASE;
+    __TFTF_BASE__ = .;
+
+    ro . : {
+        __RO_START__ = .;
+        *entrypoint.o(.text*)
+        *(.text*)
+        *(.rodata*)
+        *(.vectors)
+        __RO_END_UNALIGNED__ = .;
+        /*
+         * Memory page(s) mapped to this section will be marked as
+         * read-only, executable.  No RW data from the next section must
+         * creep in.  Ensure the rest of the current memory page is unused.
+         */
+        . = NEXT(PAGE_SIZE);
+        __RO_END__ = .;
+    } >RAM
+
+    .data : {
+        __DATA_START__ = .;
+        *(.data*)
+        __DATA_END__ = .;
+    } >RAM
+
+    stacks (NOLOAD) : {
+        __STACKS_START__ = .;
+        *(tftf_normal_stacks)
+        __STACKS_END__ = .;
+    } >RAM
+
+    /*
+     * The .bss section gets initialised to 0 at runtime.
+     * Its base address must be 16-byte aligned.
+     */
+    .bss : ALIGN(16) {
+        __BSS_START__ = .;
+        *(SORT_BY_ALIGNMENT(.bss*))
+        *(COMMON)
+        __BSS_END__ = .;
+    } >RAM
+
+    /*
+     * The xlat_table section is for full, aligned page tables (4K).
+     * Removing them from .bss avoids forcing 4K alignment on
+     * the .bss section and eliminates the unecessary zero init
+     */
+    xlat_table (NOLOAD) : {
+        *(xlat_table)
+    } >RAM
+
+    /*
+     * The base address of the coherent memory section must be page-aligned (4K)
+     * to guarantee that the coherent data are stored on their own pages and
+     * are not mixed with normal data.  This is required to set up the correct
+     * memory attributes for the coherent data page tables.
+     */
+    coherent_ram (NOLOAD) : ALIGN(PAGE_SIZE) {
+        __COHERENT_RAM_START__ = .;
+        *(tftf_coherent_stacks)
+        *(tftf_coherent_mem)
+        __COHERENT_RAM_END_UNALIGNED__ = .;
+        /*
+         * Memory page(s) mapped to this section will be marked
+         * as device memory.  No other unexpected data must creep in.
+         * Ensure the rest of the current memory page is unused.
+         */
+        . = NEXT(PAGE_SIZE);
+        __COHERENT_RAM_END__ = .;
+    } >RAM
+
+    __COHERENT_RAM_UNALIGNED_SIZE__ =
+        __COHERENT_RAM_END_UNALIGNED__ - __COHERENT_RAM_START__;
+
+
+    __TFTF_END__ = .;
+
+    __BSS_SIZE__ = SIZEOF(.bss);
+
+}
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);
+}
+