Merge pull request #1173 from etienne-lms/armv7-qemu

support to boot OP-TEE on AArch32/Armv7+example with Cortex-A15/Qemu
diff --git a/bl1/aarch32/bl1_exceptions.S b/bl1/aarch32/bl1_exceptions.S
index f73db40..a1e32f0 100644
--- a/bl1/aarch32/bl1_exceptions.S
+++ b/bl1/aarch32/bl1_exceptions.S
@@ -73,6 +73,11 @@
 	ldr	r1, [r8, #(ENTRY_POINT_INFO_PC_OFFSET + 4)]
 	msr	spsr, r1
 
+	/* Some BL32 stages expect lr_svc to provide the BL33 entry address */
+	cps	#MODE32_svc
+	ldr	lr, [r8, #ENTRY_POINT_INFO_LR_SVC_OFFSET]
+	cps	#MODE32_mon
+
 	add	r8, r8, #ENTRY_POINT_INFO_ARGS_OFFSET
 	ldm	r8, {r0, r1, r2, r3}
 	eret
diff --git a/bl2/bl2_image_load_v2.c b/bl2/bl2_image_load_v2.c
index ebbad45..f51dea8 100644
--- a/bl2/bl2_image_load_v2.c
+++ b/bl2/bl2_image_load_v2.c
@@ -93,8 +93,10 @@
 	assert(bl2_to_next_bl_params->h.version >= VERSION_2);
 	assert(bl2_to_next_bl_params->head->ep_info);
 
-	/* Populate arg0 for the next BL image */
-	bl2_to_next_bl_params->head->ep_info->args.arg0 = (u_register_t)bl2_to_next_bl_params;
+	/* Populate arg0 for the next BL image if not already provided */
+	if (bl2_to_next_bl_params->head->ep_info->args.arg0 == (u_register_t)0)
+		bl2_to_next_bl_params->head->ep_info->args.arg0 =
+					(u_register_t)bl2_to_next_bl_params;
 
 	/* Flush the parameters to be passed to next image */
 	plat_flush_next_bl_params();
diff --git a/bl32/optee/optee.mk b/bl32/optee/optee.mk
new file mode 100644
index 0000000..462020f
--- /dev/null
+++ b/bl32/optee/optee.mk
@@ -0,0 +1,15 @@
+#
+# Copyright (c) 2016-2018, ARM Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+# This makefile only aims at complying with ARM Trusted Firmware build process so
+# that "optee" is a valid ARM Trusted Firmware AArch32 Secure Playload identifier.
+
+ifneq ($(ARCH),aarch32)
+$(error This directory targets AArch32 support)
+endif
+
+$(eval $(call add_define,AARCH32_SP_OPTEE))
+
+$(info ARM Trusted Firmware built for OP-TEE payload support)
diff --git a/include/common/ep_info.h b/include/common/ep_info.h
index 3f6213f..3c2fe44 100644
--- a/include/common/ep_info.h
+++ b/include/common/ep_info.h
@@ -20,7 +20,8 @@
  ******************************************************************************/
 #define ENTRY_POINT_INFO_PC_OFFSET	U(0x08)
 #ifdef AARCH32
-#define ENTRY_POINT_INFO_ARGS_OFFSET	U(0x10)
+#define ENTRY_POINT_INFO_LR_SVC_OFFSET	U(0x10)
+#define ENTRY_POINT_INFO_ARGS_OFFSET	U(0x14)
 #else
 #define ENTRY_POINT_INFO_ARGS_OFFSET	U(0x18)
 #endif
@@ -93,6 +94,7 @@
 	uintptr_t pc;
 	uint32_t spsr;
 #ifdef AARCH32
+	uintptr_t lr_svc;
 	aapcs32_params_t args;
 #else
 	aapcs64_params_t args;
@@ -108,6 +110,12 @@
 		__builtin_offsetof(entry_point_info_t, pc), \
 		assert_BL31_pc_offset_mismatch);
 
+#ifdef AARCH32
+CASSERT(ENTRY_POINT_INFO_LR_SVC_OFFSET ==
+		__builtin_offsetof(entry_point_info_t, lr_svc),
+		assert_entrypoint_lr_offset_error);
+#endif
+
 CASSERT(ENTRY_POINT_INFO_ARGS_OFFSET == \
 		__builtin_offsetof(entry_point_info_t, args), \
 		assert_BL31_args_offset_mismatch);
diff --git a/lib/optee/optee_utils.c b/lib/optee/optee_utils.c
index deb948c..87e52bd 100644
--- a/lib/optee/optee_utils.c
+++ b/lib/optee/optee_utils.c
@@ -158,9 +158,12 @@
 	 *	and BL32_EXTRA2_IMAGE_ID to load pager and paged bin.
 	 */
 	if (!tee_validate_header(optee_header)) {
-		INFO("Invalid OPTEE header, legacy mode.\n");
-		/* Set legacy OPTEE runtime arch - aarch64 */
+		INFO("Invalid OPTEE header, set legacy mode.\n");
+#ifdef AARCH64
 		header_ep->args.arg0 = MODE_RW_64;
+#else
+		header_ep->args.arg0 = MODE_RW_32;
+#endif
 		return 0;
 	}
 
@@ -208,10 +211,16 @@
 	header_ep->args.arg2 = paged_image_info->image_size;
 
 	/* Set OPTEE runtime arch - aarch32/aarch64 */
-	if (optee_header->arch == 0)
+	if (optee_header->arch == 0) {
 		header_ep->args.arg0 = MODE_RW_32;
-	else
+	} else {
+#ifdef AARCH64
 		header_ep->args.arg0 = MODE_RW_64;
+#else
+		ERROR("Cannot boot an AArch64 OP-TEE\n");
+		return -1;
+#endif
+	}
 
 	return 0;
 }
diff --git a/plat/qemu/aarch32/plat_helpers.S b/plat/qemu/aarch32/plat_helpers.S
new file mode 100644
index 0000000..a9b1d8f
--- /dev/null
+++ b/plat/qemu/aarch32/plat_helpers.S
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 2015-2016, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <arch.h>
+#include <asm_macros.S>
+#include <assert_macros.S>
+#include <platform_def.h>
+
+	.globl	plat_my_core_pos
+	.globl	plat_get_my_entrypoint
+	.globl	platform_mem_init
+	.globl	plat_qemu_calc_core_pos
+	.globl	plat_crash_console_init
+	.globl	plat_crash_console_putc
+	.globl  plat_secondary_cold_boot_setup
+	.globl  plat_get_my_entrypoint
+	.globl  plat_is_my_cpu_primary
+
+
+func plat_my_core_pos
+	ldcopr	r0, MPIDR
+	b	plat_qemu_calc_core_pos
+endfunc plat_my_core_pos
+
+/*
+ *  unsigned int plat_qemu_calc_core_pos(u_register_t mpidr);
+ *  With this function: CorePos = (ClusterId * 4) + CoreId
+ */
+func plat_qemu_calc_core_pos
+	and	r1, r0, #MPIDR_CPU_MASK
+	and	r0, r0, #MPIDR_CLUSTER_MASK
+	add	r0, r1, r0, LSR #6
+	bx	lr
+endfunc plat_qemu_calc_core_pos
+
+	/* -----------------------------------------------------
+	 * unsigned int plat_is_my_cpu_primary (void);
+	 *
+	 * Find out whether the current cpu is the primary
+	 * cpu.
+	 * -----------------------------------------------------
+	 */
+func plat_is_my_cpu_primary
+	ldcopr	r0, MPIDR
+	ldr	r1, =(MPIDR_CLUSTER_MASK | MPIDR_CPU_MASK)
+	and	r0, r1
+	cmp	r0, #QEMU_PRIMARY_CPU
+	moveq	r0, #1
+	movne	r0, #0
+	bx	lr
+endfunc plat_is_my_cpu_primary
+
+	/* -----------------------------------------------------
+	 * void plat_secondary_cold_boot_setup (void);
+	 *
+	 * This function performs any platform specific actions
+	 * needed for a secondary cpu after a cold reset e.g
+	 * mark the cpu's presence, mechanism to place it in a
+	 * holding pen etc.
+	 * -----------------------------------------------------
+	 */
+func plat_secondary_cold_boot_setup
+	/* Calculate address of our hold entry */
+	bl	plat_my_core_pos
+	lsl	r0, r0, #PLAT_QEMU_HOLD_ENTRY_SHIFT
+	mov_imm	r2, PLAT_QEMU_HOLD_BASE
+
+	/* Wait until we have a go */
+poll_mailbox:
+	ldr	r1, [r2, r0]
+        cmp     r1, #0
+        beq     1f
+	mov_imm	r0, PLAT_QEMU_TRUSTED_MAILBOX_BASE
+	ldr	r1, [r0]
+	bx	r1
+1:
+	wfe
+	b	poll_mailbox
+endfunc plat_secondary_cold_boot_setup
+
+func plat_get_my_entrypoint
+	/* TODO support warm boot */
+	mov	r0, #0
+	bx	lr
+endfunc plat_get_my_entrypoint
+
+func platform_mem_init
+	bx	lr
+endfunc platform_mem_init
+
+	/* ---------------------------------------------
+	 * int plat_crash_console_init(void)
+	 * Function to initialize the crash console
+	 * without a C Runtime to print crash report.
+	 * Clobber list : x0, x1, x2
+	 * ---------------------------------------------
+	 */
+func plat_crash_console_init
+	mov_imm	r0, PLAT_QEMU_CRASH_UART_BASE
+	mov_imm	r1, PLAT_QEMU_CRASH_UART_CLK_IN_HZ
+	mov_imm	r2, PLAT_QEMU_CONSOLE_BAUDRATE
+	b	console_core_init
+endfunc plat_crash_console_init
+
+	/* ---------------------------------------------
+	 * int plat_crash_console_putc(int c)
+	 * Function to print a character on the crash
+	 * console without a C Runtime.
+	 * Clobber list : x1, x2
+	 * ---------------------------------------------
+	 */
+func plat_crash_console_putc
+	mov_imm	r1, PLAT_QEMU_CRASH_UART_BASE
+	b	console_core_putc
+endfunc plat_crash_console_putc
+
diff --git a/plat/qemu/include/platform_def.h b/plat/qemu/include/platform_def.h
index 0ae28ea..3eafb43 100644
--- a/plat/qemu/include/platform_def.h
+++ b/plat/qemu/include/platform_def.h
@@ -16,10 +16,17 @@
 
 #define PLATFORM_STACK_SIZE 0x1000
 
+#if ARM_ARCH_MAJOR == 7
+#define PLATFORM_MAX_CPUS_PER_CLUSTER	4
+#define PLATFORM_CLUSTER_COUNT		1
+#define PLATFORM_CLUSTER0_CORE_COUNT	PLATFORM_MAX_CPUS_PER_CLUSTER
+#define PLATFORM_CLUSTER1_CORE_COUNT	0
+#else
 #define PLATFORM_MAX_CPUS_PER_CLUSTER	4
 #define PLATFORM_CLUSTER_COUNT		2
 #define PLATFORM_CLUSTER0_CORE_COUNT	PLATFORM_MAX_CPUS_PER_CLUSTER
 #define PLATFORM_CLUSTER1_CORE_COUNT	PLATFORM_MAX_CPUS_PER_CLUSTER
+#endif
 #define PLATFORM_CORE_COUNT		(PLATFORM_CLUSTER0_CORE_COUNT + \
 					 PLATFORM_CLUSTER1_CORE_COUNT)
 
diff --git a/plat/qemu/platform.mk b/plat/qemu/platform.mk
index 3d15a74..334fbe4 100644
--- a/plat/qemu/platform.mk
+++ b/plat/qemu/platform.mk
@@ -4,6 +4,25 @@
 # SPDX-License-Identifier: BSD-3-Clause
 #
 
+ifeq (${ARM_ARCH_MAJOR},7)
+# ARMv7 Qemu support in trusted firmware expects the Cortex-A15 model.
+# Qemu Cortex-A15 model does not implement the virtualization extension.
+# For this reason, we cannot set ARM_CORTEX_A15=yes and must define all
+# the ARMv7 build directives.
+MARCH32_DIRECTIVE 	:= 	-mcpu=cortex-a15
+$(eval $(call add_define,ARMV7_SUPPORTS_LARGE_PAGE_ADDRESSING))
+$(eval $(call add_define,ARMV7_SUPPORTS_GENERIC_TIMER))
+# Qemu expects a BL32 boot stage.
+NEED_BL32		:=	yes
+endif # ARMv7
+
+ifeq (${SPD},opteed)
+add-lib-optee 		:= 	yes
+endif
+ifeq ($(AARCH32_SP),optee)
+add-lib-optee 		:= 	yes
+endif
+
 include lib/libfdt/libfdt.mk
 
 # Enable new version of image loading on QEMU platforms
@@ -15,10 +34,13 @@
 
 PLAT_PATH               :=      plat/qemu/
 PLAT_INCLUDES		:=	-Iinclude/plat/arm/common/		\
-				-Iinclude/plat/arm/common/aarch64/	\
 				-Iplat/qemu/include			\
 				-Iinclude/common/tbbr
 
+ifeq (${ARM_ARCH_MAJOR},8)
+PLAT_INCLUDES		+=	-Iinclude/plat/arm/common/${ARCH}
+endif
+
 # Use translation tables library v2 by default
 ARM_XLAT_TABLES_LIB_V1		:=	0
 $(eval $(call assert_boolean,ARM_XLAT_TABLES_LIB_V1))
@@ -26,11 +48,11 @@
 
 
 PLAT_BL_COMMON_SOURCES	:=	plat/qemu/qemu_common.c			\
-				drivers/arm/pl011/aarch64/pl011_console.S
+				drivers/arm/pl011/${ARCH}/pl011_console.S
 
 ifeq (${ARM_XLAT_TABLES_LIB_V1}, 1)
 PLAT_BL_COMMON_SOURCES	+=	lib/xlat_tables/xlat_tables_common.c		\
-				lib/xlat_tables/aarch64/xlat_tables.c
+				lib/xlat_tables/${ARCH}/xlat_tables.c
 else
 include lib/xlat_tables_v2/xlat_tables.mk
 
@@ -87,22 +109,27 @@
 				drivers/io/io_fip.c			\
 				drivers/io/io_memmap.c			\
 				lib/semihosting/semihosting.c		\
-				lib/semihosting/aarch64/semihosting_call.S \
+				lib/semihosting/${ARCH}/semihosting_call.S \
 				plat/qemu/qemu_io_storage.c		\
-				lib/cpus/aarch64/aem_generic.S		\
-				lib/cpus/aarch64/cortex_a53.S		\
-				lib/cpus/aarch64/cortex_a57.S		\
-				plat/qemu/aarch64/plat_helpers.S	\
+				plat/qemu/${ARCH}/plat_helpers.S	\
 				plat/qemu/qemu_bl1_setup.c
 
+ifeq (${ARM_ARCH_MAJOR},8)
+BL1_SOURCES		+=	lib/cpus/aarch64/aem_generic.S		\
+				lib/cpus/aarch64/cortex_a53.S		\
+				lib/cpus/aarch64/cortex_a57.S
+else
+BL1_SOURCES		+=	lib/cpus/${ARCH}/cortex_a15.S
+endif
+
 BL2_SOURCES		+=	drivers/io/io_semihosting.c		\
 				drivers/io/io_storage.c			\
 				drivers/io/io_fip.c			\
 				drivers/io/io_memmap.c			\
 				lib/semihosting/semihosting.c		\
-				lib/semihosting/aarch64/semihosting_call.S\
+				lib/semihosting/${ARCH}/semihosting_call.S\
 				plat/qemu/qemu_io_storage.c		\
-				plat/qemu/aarch64/plat_helpers.S	\
+				plat/qemu/${ARCH}/plat_helpers.S	\
 				plat/qemu/qemu_bl2_setup.c		\
 				plat/qemu/dt.c				\
 				$(LIBFDT_SRCS)
@@ -111,11 +138,12 @@
 				plat/qemu/qemu_image_load.c		\
 				common/desc_image_load.c
 endif
-ifeq (${SPD},opteed)
+ifeq ($(add-lib-optee),yes)
 BL2_SOURCES		+=	lib/optee/optee_utils.c
 endif
 
 
+ifeq (${ARM_ARCH_MAJOR},8)
 BL31_SOURCES		+=	lib/cpus/aarch64/aem_generic.S		\
 				lib/cpus/aarch64/cortex_a53.S		\
 				lib/cpus/aarch64/cortex_a57.S		\
@@ -128,7 +156,7 @@
 				plat/qemu/aarch64/plat_helpers.S	\
 				plat/qemu/qemu_bl31_setup.c		\
 				plat/qemu/qemu_gic.c
-
+endif
 
 # Add the build options to pack Trusted OS Extra1 and Trusted OS Extra2 images
 # in the FIP if the platform requires.
diff --git a/plat/qemu/qemu_bl1_setup.c b/plat/qemu/qemu_bl1_setup.c
index 5a70558..7c820fd 100644
--- a/plat/qemu/qemu_bl1_setup.c
+++ b/plat/qemu/qemu_bl1_setup.c
@@ -57,9 +57,15 @@
  * does basic initialization. Later architectural setup (bl1_arch_setup())
  * does not do anything platform specific.
  *****************************************************************************/
+#ifdef AARCH32
+#define QEMU_CONFIGURE_BL1_MMU(...)	qemu_configure_mmu_secure(__VA_ARGS__)
+#else
+#define QEMU_CONFIGURE_BL1_MMU(...)	qemu_configure_mmu_el3(__VA_ARGS__)
+#endif
+
 void bl1_plat_arch_setup(void)
 {
-	qemu_configure_mmu_el3(bl1_tzram_layout.total_base,
+	QEMU_CONFIGURE_BL1_MMU(bl1_tzram_layout.total_base,
 				bl1_tzram_layout.total_size,
 				BL1_RO_BASE, BL1_RO_LIMIT,
 				BL_COHERENT_RAM_BASE, BL_COHERENT_RAM_END);
diff --git a/plat/qemu/qemu_bl2_mem_params_desc.c b/plat/qemu/qemu_bl2_mem_params_desc.c
index 47f88ac..9965cfd 100644
--- a/plat/qemu/qemu_bl2_mem_params_desc.c
+++ b/plat/qemu/qemu_bl2_mem_params_desc.c
@@ -34,6 +34,7 @@
 	  .next_handoff_image_id = INVALID_IMAGE_ID,
 	},
 #else /* EL3_PAYLOAD_BASE */
+#ifdef AARCH64
 	/* Fill BL31 related information */
 	{ .image_id = BL31_IMAGE_ID,
 
@@ -57,16 +58,27 @@
 	  .next_handoff_image_id = BL33_IMAGE_ID,
 # endif
 	},
+#endif /* AARCH64 */
 # ifdef QEMU_LOAD_BL32
+
+#ifdef AARCH64
+#define BL32_EP_ATTRIBS		(SECURE | EXECUTABLE)
+#define BL32_IMG_ATTRIBS	0
+#else
+#define BL32_EP_ATTRIBS		(SECURE | EXECUTABLE | EP_FIRST_EXE)
+#define BL32_IMG_ATTRIBS	IMAGE_ATTRIB_PLAT_SETUP
+#endif
+
 	/* Fill BL32 related information */
 	{ .image_id = BL32_IMAGE_ID,
 
 	  SET_STATIC_PARAM_HEAD(ep_info, PARAM_EP, VERSION_2,
-				entry_point_info_t, SECURE | EXECUTABLE),
+				entry_point_info_t, BL32_EP_ATTRIBS),
 	  .ep_info.pc = BL32_BASE,
 
-	  SET_STATIC_PARAM_HEAD(image_info, PARAM_EP, VERSION_2, image_info_t,
-				0),
+	  SET_STATIC_PARAM_HEAD(image_info, PARAM_EP, VERSION_2,
+				image_info_t, BL32_IMG_ATTRIBS),
+
 	  .image_info.image_base = BL32_BASE,
 	  .image_info.image_max_size = BL32_LIMIT - BL32_BASE,
 
@@ -103,7 +115,7 @@
 
 	   SET_STATIC_PARAM_HEAD(image_info, PARAM_EP, VERSION_2,
 				 image_info_t, IMAGE_ATTRIB_SKIP_LOADING),
-#ifdef SPD_opteed
+#if defined(SPD_opteed) || defined(AARCH32_SP_OPTEE)
 	   .image_info.image_base = QEMU_OPTEE_PAGEABLE_LOAD_BASE,
 	   .image_info.image_max_size = QEMU_OPTEE_PAGEABLE_LOAD_SIZE,
 #endif
diff --git a/plat/qemu/qemu_bl2_setup.c b/plat/qemu/qemu_bl2_setup.c
index 60d9623..9e4a4a0 100644
--- a/plat/qemu/qemu_bl2_setup.c
+++ b/plat/qemu/qemu_bl2_setup.c
@@ -9,10 +9,9 @@
 #include <console.h>
 #include <debug.h>
 #include <desc_image_load.h>
-#ifdef SPD_opteed
 #include <optee_utils.h>
-#endif
 #include <libfdt.h>
+#include <platform.h>
 #include <platform_def.h>
 #include <string.h>
 #include <utils.h>
@@ -183,9 +182,15 @@
 	/* TODO Initialize timer */
 }
 
+#ifdef AARCH32
+#define QEMU_CONFIGURE_BL2_MMU(...)	qemu_configure_mmu_secure(__VA_ARGS__)
+#else
+#define QEMU_CONFIGURE_BL2_MMU(...)	qemu_configure_mmu_el1(__VA_ARGS__)
+#endif
+
 void bl2_plat_arch_setup(void)
 {
-	qemu_configure_mmu_el1(bl2_tzram_layout.total_base,
+	QEMU_CONFIGURE_BL2_MMU(bl2_tzram_layout.total_base,
 			      bl2_tzram_layout.total_size,
 			      BL2_RO_BASE, BL2_RO_LIMIT,
 			      BL_COHERENT_RAM_BASE, BL_COHERENT_RAM_END);
@@ -196,11 +201,16 @@
  ******************************************************************************/
 static uint32_t qemu_get_spsr_for_bl32_entry(void)
 {
+#ifdef AARCH64
 	/*
 	 * The Secure Payload Dispatcher service is responsible for
 	 * setting the SPSR prior to entry into the BL3-2 image.
 	 */
 	return 0;
+#else
+	return SPSR_MODE32(MODE32_svc, SPSR_T_ARM, SPSR_E_LITTLE,
+			   DISABLE_ALL_EXCEPTIONS);
+#endif
 }
 
 /*******************************************************************************
@@ -208,8 +218,9 @@
  ******************************************************************************/
 static uint32_t qemu_get_spsr_for_bl33_entry(void)
 {
-	unsigned int mode;
 	uint32_t spsr;
+#ifdef AARCH64
+	unsigned int mode;
 
 	/* Figure out what mode we enter the non-secure world in */
 	mode = EL_IMPLEMENTED(2) ? MODE_EL2 : MODE_EL1;
@@ -220,6 +231,11 @@
 	 * well.
 	 */
 	spsr = SPSR_64(mode, MODE_SP_ELX, DISABLE_ALL_EXCEPTIONS);
+#else
+	spsr = SPSR_MODE32(MODE32_svc,
+		    plat_get_ns_image_entrypoint() & 0x1,
+		    SPSR_E_LITTLE, DISABLE_ALL_EXCEPTIONS);
+#endif
 	return spsr;
 }
 
@@ -228,7 +244,7 @@
 {
 	int err = 0;
 	bl_mem_params_node_t *bl_mem_params = get_bl_mem_params_node(image_id);
-#ifdef SPD_opteed
+#if defined(SPD_opteed) || defined(AARCH32_SP_OPTEE)
 	bl_mem_params_node_t *pager_mem_params = NULL;
 	bl_mem_params_node_t *paged_mem_params = NULL;
 #endif
@@ -236,9 +252,8 @@
 	assert(bl_mem_params);
 
 	switch (image_id) {
-# ifdef AARCH64
 	case BL32_IMAGE_ID:
-#ifdef SPD_opteed
+#if defined(SPD_opteed) || defined(AARCH32_SP_OPTEE)
 		pager_mem_params = get_bl_mem_params_node(BL32_EXTRA1_IMAGE_ID);
 		assert(pager_mem_params);
 
@@ -252,16 +267,31 @@
 			WARN("OPTEE header parse error.\n");
 		}
 
+#if defined(SPD_opteed)
 		/*
 		 * OP-TEE expect to receive DTB address in x2.
 		 * This will be copied into x2 by dispatcher.
 		 */
 		bl_mem_params->ep_info.args.arg3 = PLAT_QEMU_DT_BASE;
+#else /* case AARCH32_SP_OPTEE */
+		bl_mem_params->ep_info.args.arg0 =
+					bl_mem_params->ep_info.args.arg1;
+		bl_mem_params->ep_info.args.arg1 = 0;
+		bl_mem_params->ep_info.args.arg2 = PLAT_QEMU_DT_BASE;
+		bl_mem_params->ep_info.args.arg3 = 0;
+#endif
 #endif
 		bl_mem_params->ep_info.spsr = qemu_get_spsr_for_bl32_entry();
 		break;
-# endif
+
 	case BL33_IMAGE_ID:
+#ifdef AARCH32_SP_OPTEE
+		/* AArch32 only core: OP-TEE expects NSec EP in register LR */
+		pager_mem_params = get_bl_mem_params_node(BL32_IMAGE_ID);
+		assert(pager_mem_params);
+		pager_mem_params->ep_info.lr_svc = bl_mem_params->ep_info.pc;
+#endif
+
 		/* BL33 expects to receive the primary CPU MPID (through r0) */
 		bl_mem_params->ep_info.args.arg0 = 0xffff & read_mpidr();
 		bl_mem_params->ep_info.spsr = qemu_get_spsr_for_bl33_entry();
@@ -349,7 +379,7 @@
 }
 #endif /* !LOAD_IMAGE_V2 */
 
-unsigned long plat_get_ns_image_entrypoint(void)
+uintptr_t plat_get_ns_image_entrypoint(void)
 {
 	return NS_IMAGE_OFFSET;
 }
diff --git a/plat/qemu/qemu_common.c b/plat/qemu/qemu_common.c
index e34b16f..6f9fcc6 100644
--- a/plat/qemu/qemu_common.c
+++ b/plat/qemu/qemu_common.c
@@ -85,6 +85,16 @@
 	{0}
 };
 #endif
+#ifdef IMAGE_BL32
+static const mmap_region_t plat_qemu_mmap[] = {
+	MAP_SHARED_RAM,
+	MAP_DEVICE0,
+#ifdef MAP_DEVICE1
+	MAP_DEVICE1,
+#endif
+	{0}
+};
+#endif
 
 /*******************************************************************************
  * Macro generating the code for the function setting up the pagetables as per
@@ -92,7 +102,7 @@
  ******************************************************************************/
 
 #define DEFINE_CONFIGURE_MMU_EL(_el)					\
-	void qemu_configure_mmu_el##_el(unsigned long total_base,	\
+	void qemu_configure_mmu_##_el(unsigned long total_base,	\
 				   unsigned long total_size,		\
 				   unsigned long ro_start,		\
 				   unsigned long ro_limit,		\
@@ -111,11 +121,15 @@
 		mmap_add(plat_qemu_mmap);				\
 		init_xlat_tables();					\
 									\
-		enable_mmu_el##_el(0);					\
+		enable_mmu_##_el(0);					\
 	}
 
 /* Define EL1 and EL3 variants of the function initialising the MMU */
-DEFINE_CONFIGURE_MMU_EL(1)
-DEFINE_CONFIGURE_MMU_EL(3)
+#ifdef AARCH32
+DEFINE_CONFIGURE_MMU_EL(secure)
+#else
+DEFINE_CONFIGURE_MMU_EL(el1)
+DEFINE_CONFIGURE_MMU_EL(el3)
+#endif
 
 
diff --git a/plat/qemu/qemu_private.h b/plat/qemu/qemu_private.h
index 1671ec7..202d604 100644
--- a/plat/qemu/qemu_private.h
+++ b/plat/qemu/qemu_private.h
@@ -9,6 +9,11 @@
 
 #include <sys/types.h>
 
+void qemu_configure_mmu_secure(unsigned long total_base,
+			unsigned long total_size,
+			unsigned long ro_start, unsigned long ro_limit,
+			unsigned long coh_start, unsigned long coh_limit);
+
 void qemu_configure_mmu_el1(unsigned long total_base, unsigned long total_size,
 			unsigned long ro_start, unsigned long ro_limit,
 			unsigned long coh_start, unsigned long coh_limit);
diff --git a/plat/qemu/sp_min/sp_min-qemu.mk b/plat/qemu/sp_min/sp_min-qemu.mk
new file mode 100644
index 0000000..5e8875b
--- /dev/null
+++ b/plat/qemu/sp_min/sp_min-qemu.mk
@@ -0,0 +1,22 @@
+#
+# Copyright (c) 2017, ARM Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+
+BL32_SOURCES		+=	plat/qemu/sp_min/sp_min_setup.c		\
+				plat/qemu/aarch32/plat_helpers.S	\
+				plat/qemu/qemu_gic.c 			\
+				plat/qemu/qemu_pm.c			\
+				plat/qemu/topology.c
+
+BL32_SOURCES		+=	lib/cpus/aarch32/aem_generic.S		\
+				lib/cpus/aarch32/cortex_a15.S
+
+BL32_SOURCES		+=	plat/common/aarch32/platform_mp_stack.S \
+				plat/common/plat_psci_common.c
+
+
+BL32_SOURCES		+=	drivers/arm/gic/v2/gicv2_helpers.c	\
+				drivers/arm/gic/v2/gicv2_main.c		\
+				drivers/arm/gic/common/gic_common.c
diff --git a/plat/qemu/sp_min/sp_min_setup.c b/plat/qemu/sp_min/sp_min_setup.c
new file mode 100644
index 0000000..fd8fa1c
--- /dev/null
+++ b/plat/qemu/sp_min/sp_min_setup.c
@@ -0,0 +1,168 @@
+/*
+ * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <arch_helpers.h>
+#include <arm_gic.h>
+#include <assert.h>
+#include <bl_common.h>
+#include <console.h>
+#include <debug.h>
+#include <gic_common.h>
+#include <gicv2.h>
+#include <mmio.h>
+#include <platform.h>
+#include <platform_def.h>
+#include <string.h>
+#include <xlat_tables.h>
+#include "../qemu_private.h"
+
+#if RESET_TO_SP_MIN
+#error qemu does not support RESET_TO_SP_MIN
+#endif
+
+static entry_point_info_t bl33_image_ep_info;
+
+/*
+ * The next 3 constants identify the extents of the code, RO data region and the
+ * limit of the BL3-1 image.  These addresses are used by the MMU setup code and
+ * therefore they must be page-aligned.  It is the responsibility of the linker
+ * script to ensure that __RO_START__, __RO_END__ & __BL31_END__ linker symbols
+ * refer to page-aligned addresses.
+ */
+#define BL32_RO_BASE (unsigned long)(&__RO_START__)
+#define BL32_RO_LIMIT (unsigned long)(&__RO_END__)
+#define BL32_END (unsigned long)(&__BL32_END__)
+
+#if USE_COHERENT_MEM
+/*
+ * The next 2 constants identify the extents of the coherent memory region.
+ * These addresses are used by the MMU setup code and therefore they must be
+ * page-aligned.  It is the responsibility of the linker script to ensure that
+ * __COHERENT_RAM_START__ and __COHERENT_RAM_END__ linker symbols
+ * refer to page-aligned addresses.
+ */
+#define BL32_COHERENT_RAM_BASE (unsigned long)(&__COHERENT_RAM_START__)
+#define BL32_COHERENT_RAM_LIMIT (unsigned long)(&__COHERENT_RAM_END__)
+#endif
+
+/******************************************************************************
+ * On a GICv2 system, the Group 1 secure interrupts are treated as Group 0
+ * interrupts.
+ *****************************************************************************/
+#define PLATFORM_G1S_PROPS(grp)						\
+	INTR_PROP_DESC(QEMU_IRQ_SEC_SGI_0, GIC_HIGHEST_SEC_PRIORITY,	\
+					   grp, GIC_INTR_CFG_LEVEL),	\
+	INTR_PROP_DESC(QEMU_IRQ_SEC_SGI_1, GIC_HIGHEST_SEC_PRIORITY,	\
+					   grp, GIC_INTR_CFG_LEVEL),	\
+	INTR_PROP_DESC(QEMU_IRQ_SEC_SGI_2, GIC_HIGHEST_SEC_PRIORITY,	\
+					   grp, GIC_INTR_CFG_LEVEL),	\
+	INTR_PROP_DESC(QEMU_IRQ_SEC_SGI_3, GIC_HIGHEST_SEC_PRIORITY,	\
+					   grp, GIC_INTR_CFG_LEVEL),	\
+	INTR_PROP_DESC(QEMU_IRQ_SEC_SGI_4, GIC_HIGHEST_SEC_PRIORITY,	\
+					   grp, GIC_INTR_CFG_LEVEL),	\
+	INTR_PROP_DESC(QEMU_IRQ_SEC_SGI_5, GIC_HIGHEST_SEC_PRIORITY,	\
+					   grp, GIC_INTR_CFG_LEVEL),	\
+	INTR_PROP_DESC(QEMU_IRQ_SEC_SGI_6, GIC_HIGHEST_SEC_PRIORITY,	\
+					   grp, GIC_INTR_CFG_LEVEL),	\
+	INTR_PROP_DESC(QEMU_IRQ_SEC_SGI_7, GIC_HIGHEST_SEC_PRIORITY,	\
+					   grp, GIC_INTR_CFG_LEVEL)
+
+#define PLATFORM_G0_PROPS(grp)
+
+static const interrupt_prop_t stih410_interrupt_props[] = {
+	PLATFORM_G1S_PROPS(GICV2_INTR_GROUP0),
+	PLATFORM_G0_PROPS(GICV2_INTR_GROUP0)
+};
+
+static unsigned int target_mask_array[PLATFORM_CORE_COUNT];
+
+static const struct gicv2_driver_data plat_gicv2_driver_data = {
+	.gicd_base = GICD_BASE,
+	.gicc_base = GICC_BASE,
+	.interrupt_props = stih410_interrupt_props,
+	.interrupt_props_num = ARRAY_SIZE(stih410_interrupt_props),
+	.target_masks = target_mask_array,
+	.target_masks_num = ARRAY_SIZE(target_mask_array),
+};
+
+/*******************************************************************************
+ * Return a pointer to the 'entry_point_info' structure of the next image for
+ * the security state specified. BL33 corresponds to the non-secure image type
+ * while BL32 corresponds to the secure image type. A NULL pointer is returned
+ * if the image does not exist.
+ ******************************************************************************/
+entry_point_info_t *sp_min_plat_get_bl33_ep_info(void)
+{
+	entry_point_info_t *next_image_info = &bl33_image_ep_info;
+
+	/*
+	 * None of the images on the ARM development platforms can have 0x0
+	 * as the entrypoint
+	 */
+	if (next_image_info->pc)
+		return next_image_info;
+	else
+		return NULL;
+}
+
+void sp_min_early_platform_setup(void *from_bl2, void *plat_params_from_bl2)
+{
+	bl_params_t *params_from_bl2 = (bl_params_t *)from_bl2;
+
+	/* Initialize the console to provide early debug support */
+	console_init(PLAT_QEMU_BOOT_UART_BASE, PLAT_QEMU_BOOT_UART_CLK_IN_HZ,
+			PLAT_QEMU_CONSOLE_BAUDRATE);
+
+	ERROR("qemu sp_min, console init\n");
+	/*
+	 * Check params passed from BL2
+	 */
+	assert(params_from_bl2);
+	assert(params_from_bl2->h.type == PARAM_BL_PARAMS);
+	assert(params_from_bl2->h.version >= VERSION_2);
+
+	bl_params_node_t *bl_params = params_from_bl2->head;
+
+	/*
+	 * Copy BL33 entry point information from BL2's address space.
+	 */
+	while (bl_params) {
+		if (bl_params->image_id == BL33_IMAGE_ID)
+			bl33_image_ep_info = *bl_params->ep_info;
+
+		bl_params = bl_params->next_params_info;
+	}
+
+	if (!bl33_image_ep_info.pc)
+		panic();
+}
+
+void sp_min_plat_arch_setup(void)
+{
+	qemu_configure_mmu_secure(BL32_RO_BASE, BL32_END - BL32_RO_BASE,
+				  BL32_RO_BASE, BL32_RO_LIMIT,
+				  BL_COHERENT_RAM_BASE, BL_COHERENT_RAM_END);
+
+}
+
+void sp_min_platform_setup(void)
+{
+	/* Initialize the gic cpu and distributor interfaces */
+	gicv2_driver_init(&plat_gicv2_driver_data);
+	gicv2_distif_init();
+	gicv2_pcpu_distif_init();
+	gicv2_cpuif_enable();
+}
+
+unsigned int plat_get_syscnt_freq2(void)
+{
+	return SYS_COUNTER_FREQ_IN_TICKS;
+}
+
+void sp_min_plat_fiq_handler(uint32_t id)
+{
+	VERBOSE("[sp_min] interrupt #%d\n", id);
+}