diff --git a/include/plat/arm/common/plat_arm.h b/include/plat/arm/common/plat_arm.h
index 2a32e419b0f132ee9304fcda43b95e4f30be7c6c..06912eba7ceb0f79d956f17ddd26dd52bff17247 100644
--- a/include/plat/arm/common/plat_arm.h
+++ b/include/plat/arm/common/plat_arm.h
@@ -47,8 +47,10 @@
  */
 void arm_setup_page_tables(unsigned long total_base,
 			unsigned long total_size,
-			unsigned long ro_start,
-			unsigned long ro_limit
+			unsigned long code_start,
+			unsigned long code_limit,
+			unsigned long rodata_start,
+			unsigned long rodata_limit
 #if USE_COHERENT_MEM
 			, unsigned long coh_start,
 			unsigned long coh_limit
diff --git a/include/plat/common/common_def.h b/include/plat/common/common_def.h
index 9fac9fa22131c648f82f4251985ad5eee05ac758..d6b777278f11342ee2769db407959dd668337c8f 100644
--- a/include/plat/common/common_def.h
+++ b/include/plat/common/common_def.h
@@ -80,5 +80,44 @@
 	.ep_info.pc = BL2_BASE,				\
 }
 
-#endif /* __COMMON_DEF_H__ */
+/*
+ * The following constants identify the extents of the code & read-only data
+ * regions. These addresses are used by the MMU setup code and therefore they
+ * must be page-aligned.
+ *
+ * When the code and read-only data are mapped as a single atomic section
+ * (i.e. when SEPARATE_CODE_AND_RODATA=0) then we treat the whole section as
+ * code by specifying the read-only data section as empty.
+ *
+ * BL1 is different than the other images in the sense that its read-write data
+ * originally lives in Trusted ROM and needs to be relocated in Trusted SRAM at
+ * run-time. Therefore, the read-write data in ROM can be mapped with the same
+ * memory attributes as the read-only data region. For this reason, BL1 uses
+ * different macros.
+ *
+ * Note that BL1_ROM_END is not necessarily aligned on a page boundary as it
+ * just points to the end of BL1's actual content in Trusted ROM. Therefore it
+ * needs to be rounded up to the next page size in order to map the whole last
+ * page of it with the right memory attributes.
+ */
+#if SEPARATE_CODE_AND_RODATA
+#define BL_CODE_BASE		(unsigned long)(&__TEXT_START__)
+#define BL_CODE_LIMIT		(unsigned long)(&__TEXT_END__)
+#define BL_RO_DATA_BASE		(unsigned long)(&__RODATA_START__)
+#define BL_RO_DATA_LIMIT	(unsigned long)(&__RODATA_END__)
+
+#define BL1_CODE_LIMIT		BL_CODE_LIMIT
+#define BL1_RO_DATA_BASE	(unsigned long)(&__RODATA_START__)
+#define BL1_RO_DATA_LIMIT	round_up(BL1_ROM_END, PAGE_SIZE)
+#else
+#define BL_CODE_BASE		(unsigned long)(&__RO_START__)
+#define BL_CODE_LIMIT		(unsigned long)(&__RO_END__)
+#define BL_RO_DATA_BASE		0
+#define BL_RO_DATA_LIMIT	0
 
+#define BL1_CODE_LIMIT		round_up(BL1_ROM_END, PAGE_SIZE)
+#define BL1_RO_DATA_BASE	0
+#define BL1_RO_DATA_LIMIT	0
+#endif /* SEPARATE_CODE_AND_RODATA */
+
+#endif /* __COMMON_DEF_H__ */
diff --git a/plat/arm/common/aarch64/arm_common.c b/plat/arm/common/aarch64/arm_common.c
index c0a7e6b47ad58c7d37a785b9d4ca519f4f2477e2..36ba4c18bab68d03c9d7b89e48871b86bcc20323 100644
--- a/plat/arm/common/aarch64/arm_common.c
+++ b/plat/arm/common/aarch64/arm_common.c
@@ -55,13 +55,16 @@ extern const mmap_region_t plat_arm_mmap[];
  * The extents of the generic memory regions are specified by the function
  * arguments and consist of:
  * - Trusted SRAM seen by the BL image;
- * - Read-only section (code and read-only data);
+ * - Code section;
+ * - Read-only data section;
  * - Coherent memory region, if applicable.
  */
 void arm_setup_page_tables(unsigned long total_base,
 			   unsigned long total_size,
-			   unsigned long ro_start,
-			   unsigned long ro_limit
+			   unsigned long code_start,
+			   unsigned long code_limit,
+			   unsigned long rodata_start,
+			   unsigned long rodata_limit
 #if USE_COHERENT_MEM
 			   ,
 			   unsigned long coh_start,
@@ -76,16 +79,24 @@ void arm_setup_page_tables(unsigned long total_base,
 	mmap_add_region(total_base, total_base,
 			total_size,
 			MT_MEMORY | MT_RW | MT_SECURE);
-	/* Re-map the read-only section */
-	mmap_add_region(ro_start, ro_start,
-			ro_limit - ro_start,
-			MT_MEMORY | MT_RO | MT_SECURE);
+
+	/* Re-map the code section */
+	mmap_add_region(code_start, code_start,
+			code_limit - code_start,
+			MT_CODE | MT_SECURE);
+
+	/* Re-map the read-only data section */
+	mmap_add_region(rodata_start, rodata_start,
+			rodata_limit - rodata_start,
+			MT_RO_DATA | MT_SECURE);
+
 #if USE_COHERENT_MEM
 	/* Re-map the coherent memory region */
 	mmap_add_region(coh_start, coh_start,
 			coh_limit - coh_start,
 			MT_DEVICE | MT_RW | MT_SECURE);
 #endif
+
 	/* Now (re-)map the platform-specific memory regions */
 	mmap_add(plat_arm_get_mmap());
 
diff --git a/plat/arm/common/arm_bl1_setup.c b/plat/arm/common/arm_bl1_setup.c
index 34996604b4f7e27e6ef44c4bb84d5c102fb9a77f..c94f0cd773a99d50852116e9dcd68219197bcb38 100644
--- a/plat/arm/common/arm_bl1_setup.c
+++ b/plat/arm/common/arm_bl1_setup.c
@@ -120,16 +120,12 @@ void bl1_early_platform_setup(void)
  *****************************************************************************/
 void arm_bl1_plat_arch_setup(void)
 {
-	/*
-	 * BL1_ROM_END is not necessarily aligned on a page boundary as it
-	 * just points to the end of BL1's actual content in Trusted ROM.
-	 * Therefore it needs to be rounded up to the next page size in order to
-	 * map the whole last page of it with the right memory attributes.
-	 */
 	arm_setup_page_tables(bl1_tzram_layout.total_base,
 			      bl1_tzram_layout.total_size,
-			      BL1_RO_BASE,
-			      round_up(BL1_ROM_END, PAGE_SIZE)
+			      BL_CODE_BASE,
+			      BL1_CODE_LIMIT,
+			      BL1_RO_DATA_BASE,
+			      BL1_RO_DATA_LIMIT
 #if USE_COHERENT_MEM
 			      , BL1_COHERENT_RAM_BASE,
 			      BL1_COHERENT_RAM_LIMIT
diff --git a/plat/arm/common/arm_bl2_setup.c b/plat/arm/common/arm_bl2_setup.c
index e8e7928c02d5af5b833a1fdfed38d17716e57359..b6afaa7f519844f63df910e091b6956310c6046e 100644
--- a/plat/arm/common/arm_bl2_setup.c
+++ b/plat/arm/common/arm_bl2_setup.c
@@ -36,16 +36,6 @@
 #include <plat_arm.h>
 #include <string.h>
 
-
-/*
- * The next 2 constants identify the extents of the code & RO data 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
- * __RO_START__ and __RO_END__ linker symbols refer to page-aligned addresses.
- */
-#define BL2_RO_BASE (unsigned long)(&__RO_START__)
-#define BL2_RO_LIMIT (unsigned long)(&__RO_END__)
-
 #if USE_COHERENT_MEM
 /*
  * The next 2 constants identify the extents of the coherent memory region.
@@ -236,8 +226,10 @@ void arm_bl2_plat_arch_setup(void)
 {
 	arm_setup_page_tables(bl2_tzram_layout.total_base,
 			      bl2_tzram_layout.total_size,
-			      BL2_RO_BASE,
-			      BL2_RO_LIMIT
+			      BL_CODE_BASE,
+			      BL_CODE_LIMIT,
+			      BL_RO_DATA_BASE,
+			      BL_RO_DATA_LIMIT
 #if USE_COHERENT_MEM
 			      , BL2_COHERENT_RAM_BASE,
 			      BL2_COHERENT_RAM_LIMIT
diff --git a/plat/arm/common/arm_bl2u_setup.c b/plat/arm/common/arm_bl2u_setup.c
index 8185f9f5174c598ab4a22c098776cfabdcfabedd..de7d0c2f9e582d3a3cb102b3efbac875f1ff92c3 100644
--- a/plat/arm/common/arm_bl2u_setup.c
+++ b/plat/arm/common/arm_bl2u_setup.c
@@ -36,16 +36,6 @@
 #include <plat_arm.h>
 #include <string.h>
 
-
-/*
- * The next 2 constants identify the extents of the code & RO data 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
- * __RO_START__ and __RO_END__ linker symbols refer to page-aligned addresses.
- */
-#define BL2U_RO_BASE (unsigned long)(&__RO_START__)
-#define BL2U_RO_LIMIT (unsigned long)(&__RO_END__)
-
 #if USE_COHERENT_MEM
 /*
  * The next 2 constants identify the extents of the coherent memory region.
@@ -104,8 +94,10 @@ void arm_bl2u_plat_arch_setup(void)
 {
 	arm_setup_page_tables(BL2U_BASE,
 			      BL31_LIMIT,
-			      BL2U_RO_BASE,
-			      BL2U_RO_LIMIT
+			      BL_CODE_BASE,
+			      BL_CODE_LIMIT,
+			      BL_RO_DATA_BASE,
+			      BL_RO_DATA_LIMIT
 #if USE_COHERENT_MEM
 			      ,
 			      BL2U_COHERENT_RAM_BASE,
diff --git a/plat/arm/common/arm_bl31_setup.c b/plat/arm/common/arm_bl31_setup.c
index 9cfa3b8b7d1ead6e3b2672f5945fb6e8dc5091b4..87cafced8501f326aceaa2f70231a5ffe35a2c05 100644
--- a/plat/arm/common/arm_bl31_setup.c
+++ b/plat/arm/common/arm_bl31_setup.c
@@ -38,16 +38,6 @@
 #include <plat_arm.h>
 #include <platform.h>
 
-
-/*
- * The next 3 constants identify the extents of the code, RO data region and the
- * limit of the BL31 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 BL31_RO_BASE (unsigned long)(&__RO_START__)
-#define BL31_RO_LIMIT (unsigned long)(&__RO_END__)
 #define BL31_END (unsigned long)(&__BL31_END__)
 
 #if USE_COHERENT_MEM
@@ -253,10 +243,12 @@ void bl31_plat_runtime_setup(void)
  ******************************************************************************/
 void arm_bl31_plat_arch_setup(void)
 {
-	arm_setup_page_tables(BL31_RO_BASE,
-			      (BL31_END - BL31_RO_BASE),
-			      BL31_RO_BASE,
-			      BL31_RO_LIMIT
+	arm_setup_page_tables(BL31_BASE,
+			      BL31_END - BL31_BASE,
+			      BL_CODE_BASE,
+			      BL_CODE_LIMIT,
+			      BL_RO_DATA_BASE,
+			      BL_RO_DATA_LIMIT
 #if USE_COHERENT_MEM
 			      , BL31_COHERENT_RAM_BASE,
 			      BL31_COHERENT_RAM_LIMIT
diff --git a/plat/arm/common/arm_common.mk b/plat/arm/common/arm_common.mk
index bcb3f6f6ece98cff6ca8b309081c9f4233a409f2..9e5ddea7703c68d748e6dd108793262ab7f96699 100644
--- a/plat/arm/common/arm_common.mk
+++ b/plat/arm/common/arm_common.mk
@@ -85,6 +85,11 @@ $(eval $(call add_define,ARM_BL31_IN_DRAM))
 # Enable PSCI_STAT_COUNT/RESIDENCY APIs on ARM platforms
 ENABLE_PSCI_STAT = 1
 
+# On ARM platforms, separate the code and read-only data sections to allow
+# mapping the former as executable and the latter as execute-never.
+SEPARATE_CODE_AND_RODATA	:=	1
+
+
 PLAT_INCLUDES		+=	-Iinclude/common/tbbr				\
 				-Iinclude/plat/arm/common			\
 				-Iinclude/plat/arm/common/aarch64
diff --git a/plat/arm/common/tsp/arm_tsp_setup.c b/plat/arm/common/tsp/arm_tsp_setup.c
index 6c6ceea0816e4798720feacf03d916c81b1d2858..09029f4c2a1b5a7c7494c417b816de976c935be0 100644
--- a/plat/arm/common/tsp/arm_tsp_setup.c
+++ b/plat/arm/common/tsp/arm_tsp_setup.c
@@ -35,16 +35,6 @@
 #include <platform_tsp.h>
 #include <plat_arm.h>
 
-
-/*
- * The next 3 constants identify the extents of the code & RO data region and
- * the limit of the BL32 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__ & & __BL32_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
@@ -98,10 +88,12 @@ void tsp_platform_setup(void)
  ******************************************************************************/
 void tsp_plat_arch_setup(void)
 {
-	arm_setup_page_tables(BL32_RO_BASE,
-			      (BL32_END - BL32_RO_BASE),
-			      BL32_RO_BASE,
-			      BL32_RO_LIMIT
+	arm_setup_page_tables(BL32_BASE,
+			      (BL32_END - BL32_BASE),
+			      BL_CODE_BASE,
+			      BL_CODE_LIMIT,
+			      BL_RO_DATA_BASE,
+			      BL_RO_DATA_LIMIT
 #if USE_COHERENT_MEM
 			      , BL32_COHERENT_RAM_BASE,
 			      BL32_COHERENT_RAM_LIMIT
diff --git a/plat/xilinx/zynqmp/bl31_zynqmp_setup.c b/plat/xilinx/zynqmp/bl31_zynqmp_setup.c
index 2ea8b1c982dad835c1f2ab211bcbd51e913733d2..ffed591cf9aa04e5040e8613f3006571f1d8d44c 100644
--- a/plat/xilinx/zynqmp/bl31_zynqmp_setup.c
+++ b/plat/xilinx/zynqmp/bl31_zynqmp_setup.c
@@ -158,6 +158,8 @@ void bl31_plat_arch_setup(void)
 			      BL31_COHERENT_RAM_LIMIT - BL31_RO_BASE,
 			      BL31_RO_BASE,
 			      BL31_RO_LIMIT,
+			      0,
+			      0,
 			      BL31_COHERENT_RAM_BASE,
 			      BL31_COHERENT_RAM_LIMIT);
 	enable_mmu_el3(0);
diff --git a/plat/xilinx/zynqmp/tsp/tsp_plat_setup.c b/plat/xilinx/zynqmp/tsp/tsp_plat_setup.c
index 19e2c12987a304c7b21f708e23e4f60bc4ee6260..ae66fa4157c5901c410d688a5c68ae5b7b347682 100644
--- a/plat/xilinx/zynqmp/tsp/tsp_plat_setup.c
+++ b/plat/xilinx/zynqmp/tsp/tsp_plat_setup.c
@@ -93,7 +93,9 @@ void tsp_plat_arch_setup(void)
 	arm_setup_page_tables(BL32_RO_BASE,
 			      (BL32_END - BL32_RO_BASE),
 			      BL32_RO_BASE,
-			      BL32_RO_LIMIT
+			      BL32_RO_LIMIT,
+			      0,
+			      0
 #if USE_COHERENT_MEM
 			      , BL32_COHERENT_RAM_BASE,
 			      BL32_COHERENT_RAM_LIMIT