From 30405f59881c73946b6b0ffdbf25804f9fbf1585 Mon Sep 17 00:00:00 2001
From: Peter Hoyes <Peter.Hoyes@arm.com>
Date: Wed, 14 Jul 2021 12:44:27 +0100
Subject: [PATCH 4/9] armv8: ARMV8_SWITCH_TO_EL1 improvements

Convert CONFIG_ARMV8_SWITCH_TO_EL1 to a Kconfig variable.

Add support for switching to EL1 to bootefi.

Add the environment variable armv8_switch_to_el1 to allow configuring
whether to switch to EL1 at runtime. This overrides the compile-time
option.

Issue-Id: SCM-3728
Upstream-Status: Inappropriate [other]
  Implementation pending further discussion
Signed-off-by: Peter Hoyes <Peter.Hoyes@arm.com>
Change-Id: If98478148d6d8d1f732acac5439276700614815f
---
 arch/arm/cpu/armv8/exception_level.c | 21 ++++++++++++++--
 arch/arm/lib/bootm.c                 | 36 ++++++++++++++++------------
 configs/vexpress_aemv8r_defconfig    |  1 +
 3 files changed, 41 insertions(+), 17 deletions(-)

diff --git a/arch/arm/cpu/armv8/exception_level.c b/arch/arm/cpu/armv8/exception_level.c
index b11936548f..4aad1550f4 100644
--- a/arch/arm/cpu/armv8/exception_level.c
+++ b/arch/arm/cpu/armv8/exception_level.c
@@ -40,19 +40,36 @@ static void entry_non_secure(struct jmp_buf_data *non_secure_jmp)
  * trusted firmware being one embodiment). The operating system shall be
  * started at exception level EL2. So here we check the exception level
  * and switch it if necessary.
+ *
+ * If armv8_switch_to_el1 (config or env var) is enabled, also switch to EL1
+ * before booting the operating system.
  */
 void switch_to_non_secure_mode(void)
 {
 	struct jmp_buf_data non_secure_jmp;
 
 	/* On AArch64 we need to make sure we call our payload in < EL3 */
-	if (current_el() == 3) {
+
+	int switch_to_el1 = env_get_yesno("armv8_switch_to_el1");
+#ifdef CONFIG_ARMV8_SWITCH_TO_EL1
+	if (switch_to_el1 == -1) {
+			switch_to_el1 = 1;
+	}
+#endif
+
+	if (current_el() > 2) {
 		if (setjmp(&non_secure_jmp))
 			return;
 		dcache_disable();	/* flush cache before switch to EL2 */
-
 		/* Move into EL2 and keep running there */
 		armv8_switch_to_el2((uintptr_t)&non_secure_jmp, 0, 0, 0,
 				    (uintptr_t)entry_non_secure, ES_TO_AARCH64);
+	} else if (switch_to_el1 == 1 && current_el() > 1) {
+		if (setjmp(&non_secure_jmp))
+			return;
+		dcache_disable();	/* flush cache before switch to EL1 */
+		/* Move into EL1 and keep running there */
+		armv8_switch_to_el1((uintptr_t)&non_secure_jmp, 0, 0, 0,
+				    (uintptr_t)entry_non_secure, ES_TO_AARCH64);
 	}
 }
diff --git a/arch/arm/lib/bootm.c b/arch/arm/lib/bootm.c
index 9f086f3b90..b044aeca88 100644
--- a/arch/arm/lib/bootm.c
+++ b/arch/arm/lib/bootm.c
@@ -270,7 +270,6 @@ __weak void update_os_arch_secondary_cores(uint8_t os_arch)
 {
 }
 
-#ifdef CONFIG_ARMV8_SWITCH_TO_EL1
 static void switch_to_el1(void)
 {
 	if ((IH_ARCH_DEFAULT == IH_ARCH_ARM64) &&
@@ -285,7 +284,6 @@ static void switch_to_el1(void)
 				    ES_TO_AARCH64);
 }
 #endif
-#endif
 
 /* Subcommand: GO */
 static void boot_jump_linux(bootm_headers_t *images, int flag)
@@ -312,21 +310,29 @@ static void boot_jump_linux(bootm_headers_t *images, int flag)
 
 		update_os_arch_secondary_cores(images->os.arch);
 
+		int armv8_switch_to_el1 = env_get_yesno("armv8_switch_to_el1");
 #ifdef CONFIG_ARMV8_SWITCH_TO_EL1
-		armv8_switch_to_el2((u64)images->ft_addr, 0, 0, 0,
-				    (u64)switch_to_el1, ES_TO_AARCH64);
-#else
-		if ((IH_ARCH_DEFAULT == IH_ARCH_ARM64) &&
-		    (images->os.arch == IH_ARCH_ARM))
-			armv8_switch_to_el2(0, (u64)gd->bd->bi_arch_number,
-					    (u64)images->ft_addr, 0,
-					    (u64)images->ep,
-					    ES_TO_AARCH32);
-		else
-			armv8_switch_to_el2((u64)images->ft_addr, 0, 0, 0,
-					    images->ep,
-					    ES_TO_AARCH64);
+		if (armv8_switch_to_el1 == -1) {
+			armv8_switch_to_el1 = 1;
+		}
 #endif
+		if (armv8_switch_to_el1 == 1) {
+			armv8_switch_to_el2((u64)images->ft_addr, 0, 0, 0,
+					    (u64)switch_to_el1, ES_TO_AARCH64);
+		} else {
+			if ((IH_ARCH_DEFAULT == IH_ARCH_ARM64) &&
+					(images->os.arch == IH_ARCH_ARM))
+				armv8_switch_to_el2(0,
+						    (u64)gd->bd->bi_arch_number,
+						    (u64)images->ft_addr, 0,
+						    (u64)images->ep,
+						    ES_TO_AARCH32);
+			else
+				armv8_switch_to_el2((u64)images->ft_addr,
+						    0, 0, 0,
+						    images->ep,
+						    ES_TO_AARCH64);
+		}
 	}
 #else
 	unsigned long machid = gd->bd->bi_arch_number;
diff --git a/configs/vexpress_aemv8r_defconfig b/configs/vexpress_aemv8r_defconfig
index 683d983c36..6044f82b00 100644
--- a/configs/vexpress_aemv8r_defconfig
+++ b/configs/vexpress_aemv8r_defconfig
@@ -17,3 +17,4 @@ CONFIG_SYS_PBSIZE=541
 # CONFIG_MMC is not set
 CONFIG_VIRTIO_MMIO=y
 CONFIG_ARMV8_EXCEPTION_VECTORS=n
+CONFIG_ARMV8_SWITCH_TO_EL1=y
-- 
2.25.1

