All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v3] efi/libstub: arm: Print CPU boot mode and MMU state at boot
@ 2020-06-16  8:50 ` Ard Biesheuvel
  0 siblings, 0 replies; 10+ messages in thread
From: Ard Biesheuvel @ 2020-06-16  8:50 UTC (permalink / raw)
  To: linux-efi
  Cc: linux-arm-kernel, Samer.El-Haj-Mahmoud, xypron.glpk,
	grant.likely, leif, Ard Biesheuvel

On 32-bit ARM, we may boot at HYP mode, or with the MMU and caches off
(or both), even though the EFI spec does not actually support this.
While booting at HYP mode is something we might tolerate, fiddling
with the caches is a more serious issue, as disabling the caches is
tricky to do safely from C code, and running without the Dcache makes
it impossible to support unaligned memory accesses, which is another
explicit requirement imposed by the EFI spec.

So take note of the CPU mode and MMU state in the EFI stub diagnostic
output so that we can easily diagnose any issues that may arise from
this. E.g.,

  EFI stub: Entering in SVC mode with MMU enabled

Also, capture the CPSR and SCTLR system register values at EFI stub
entry, and after ExitBootServices() returns, and check whether the
MMU and Dcache were disabled at any point. If this is the case, a
diagnostic message like the following will be emitted:

  efi: [Firmware Bug]: EFI stub was entered with MMU and Dcache disabled, please fix your firmware!
  efi: CPSR at EFI stub entry        : 0x600001d3
  efi: SCTLR at EFI stub entry       : 0x00c51838
  efi: CPSR after ExitBootServices() : 0x600001d3
  efi: SCTLR after ExitBootServices(): 0x00c50838

Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
---
Adding Grant and Samer to cc, as this may be something that should be taken
into account in the EBBR context? Note that this affects 64-bit only.

 arch/arm/include/asm/efi.h                |  7 +++
 drivers/firmware/efi/arm-init.c           | 37 ++++++++++++--
 drivers/firmware/efi/libstub/arm32-stub.c | 52 +++++++++++++++++++-
 drivers/firmware/efi/libstub/efi-stub.c   |  3 ++
 drivers/firmware/efi/libstub/efistub.h    |  2 +
 include/linux/efi.h                       |  1 +
 6 files changed, 98 insertions(+), 4 deletions(-)

diff --git a/arch/arm/include/asm/efi.h b/arch/arm/include/asm/efi.h
index 84dc0ba822f5..5dcf3c6011b7 100644
--- a/arch/arm/include/asm/efi.h
+++ b/arch/arm/include/asm/efi.h
@@ -87,4 +87,11 @@ static inline unsigned long efi_get_max_initrd_addr(unsigned long dram_base,
 	return dram_base + SZ_512M;
 }
 
+struct efi_arm_entry_state {
+	u32	cpsr_before_ebs;
+	u32	sctlr_before_ebs;
+	u32	cpsr_after_ebs;
+	u32	sctlr_after_ebs;
+};
+
 #endif /* _ASM_ARM_EFI_H */
diff --git a/drivers/firmware/efi/arm-init.c b/drivers/firmware/efi/arm-init.c
index c697e70ca7e7..4d1c272a4f79 100644
--- a/drivers/firmware/efi/arm-init.c
+++ b/drivers/firmware/efi/arm-init.c
@@ -52,9 +52,11 @@ static phys_addr_t __init efi_to_phys(unsigned long addr)
 }
 
 static __initdata unsigned long screen_info_table = EFI_INVALID_TABLE_ADDR;
+static __initdata unsigned long cpu_state_table = EFI_INVALID_TABLE_ADDR;
 
 static const efi_config_table_type_t arch_tables[] __initconst = {
 	{LINUX_EFI_ARM_SCREEN_INFO_TABLE_GUID, &screen_info_table},
+	{LINUX_EFI_ARM_CPU_STATE_TABLE_GUID, &cpu_state_table},
 	{}
 };
 
@@ -116,7 +118,8 @@ static int __init uefi_init(u64 efi_system_table)
 		goto out;
 	}
 	retval = efi_config_parse_tables(config_tables, systab->nr_tables,
-					 arch_tables);
+					 IS_ENABLED(CONFIG_ARM) ? arch_tables
+								: NULL);
 
 	early_memunmap(config_tables, table_size);
 out:
@@ -238,9 +241,37 @@ void __init efi_init(void)
 
 	init_screen_info();
 
+#ifdef CONFIG_ARM
 	/* ARM does not permit early mappings to persist across paging_init() */
-	if (IS_ENABLED(CONFIG_ARM))
-		efi_memmap_unmap();
+	efi_memmap_unmap();
+
+	if (cpu_state_table != EFI_INVALID_TABLE_ADDR) {
+		struct efi_arm_entry_state *state;
+		bool dump_state = true;
+
+		state = early_memremap_ro(cpu_state_table,
+					  sizeof(struct efi_arm_entry_state));
+		if (state == NULL) {
+			pr_warn("Unable to map CPU entry state table.\n");
+			return;
+		}
+
+		if ((state->sctlr_before_ebs & 1) == 0)
+			pr_warn(FW_BUG "EFI stub was entered with MMU and Dcache disabled, please fix your firmware!\n");
+		else if ((state->sctlr_after_ebs & 1) == 0)
+			pr_warn(FW_BUG "ExitBootServices() returned with MMU and Dcache disabled, please fix your firmware!\n");
+		else
+			dump_state = false;
+
+		if (dump_state || efi_enabled(EFI_DBG)) {
+			pr_info("CPSR at EFI stub entry        : 0x%08x\n", state->cpsr_before_ebs);
+			pr_info("SCTLR at EFI stub entry       : 0x%08x\n", state->sctlr_before_ebs);
+			pr_info("CPSR after ExitBootServices() : 0x%08x\n", state->cpsr_after_ebs);
+			pr_info("SCTLR after ExitBootServices(): 0x%08x\n", state->sctlr_after_ebs);
+		}
+		early_memunmap(state, sizeof(struct efi_arm_entry_state));
+	}
+#endif
 }
 
 static bool efifb_overlaps_pci_range(const struct of_pci_range *range)
diff --git a/drivers/firmware/efi/libstub/arm32-stub.c b/drivers/firmware/efi/libstub/arm32-stub.c
index 40243f524556..60783228db6e 100644
--- a/drivers/firmware/efi/libstub/arm32-stub.c
+++ b/drivers/firmware/efi/libstub/arm32-stub.c
@@ -7,10 +7,49 @@
 
 #include "efistub.h"
 
+static efi_guid_t cpu_state_guid = LINUX_EFI_ARM_CPU_STATE_TABLE_GUID;
+
+struct efi_arm_entry_state *efi_entry_state;
+
+static void get_cpu_state(u32 *cpsr, u32 *sctlr)
+{
+	asm("mrs %0, cpsr" : "=r"(*cpsr));
+	if ((*cpsr & MODE_MASK) == HYP_MODE)
+		asm("mrc p15, 4, %0, c1, c0, 0" : "=r"(*sctlr));
+	else
+		asm("mrc p15, 0, %0, c1, c0, 0" : "=r"(*sctlr));
+}
+
 efi_status_t check_platform_features(void)
 {
+	efi_status_t status;
+	u32 cpsr, sctlr;
 	int block;
 
+	get_cpu_state(&cpsr, &sctlr);
+
+	efi_info("Entering in %s mode with MMU %sabled\n",
+		 ((cpsr & MODE_MASK) == HYP_MODE) ? "HYP" : "SVC",
+		 (sctlr & 1) ? "en" : "dis");
+
+	status = efi_bs_call(allocate_pool, EFI_LOADER_DATA,
+			     sizeof(*efi_entry_state),
+			     (void **)&efi_entry_state);
+	if (status != EFI_SUCCESS) {
+		efi_err("allocate_pool() failed\n");
+		return status;
+	}
+
+	efi_entry_state->cpsr_before_ebs = cpsr;
+	efi_entry_state->sctlr_before_ebs = sctlr;
+
+	status = efi_bs_call(install_configuration_table, &cpu_state_guid,
+			     efi_entry_state);
+	if (status != EFI_SUCCESS) {
+		efi_err("install_configuration_table() failed\n");
+		goto free_state;
+	}
+
 	/* non-LPAE kernels can run anywhere */
 	if (!IS_ENABLED(CONFIG_ARM_LPAE))
 		return EFI_SUCCESS;
@@ -19,9 +58,20 @@ efi_status_t check_platform_features(void)
 	block = cpuid_feature_extract(CPUID_EXT_MMFR0, 0);
 	if (block < 5) {
 		efi_err("This LPAE kernel is not supported by your CPU\n");
-		return EFI_UNSUPPORTED;
+		status = EFI_UNSUPPORTED;
+		goto free_state;
 	}
 	return EFI_SUCCESS;
+
+free_state:
+	efi_bs_call(free_pool, efi_entry_state);
+	return status;
+}
+
+void efi_handle_post_ebs_state(void)
+{
+	get_cpu_state(&efi_entry_state->cpsr_after_ebs,
+		      &efi_entry_state->sctlr_after_ebs);
 }
 
 static efi_guid_t screen_info_guid = LINUX_EFI_ARM_SCREEN_INFO_TABLE_GUID;
diff --git a/drivers/firmware/efi/libstub/efi-stub.c b/drivers/firmware/efi/libstub/efi-stub.c
index e97370bdfdb0..3318ec3f8e5b 100644
--- a/drivers/firmware/efi/libstub/efi-stub.c
+++ b/drivers/firmware/efi/libstub/efi-stub.c
@@ -329,6 +329,9 @@ efi_status_t __efiapi efi_pe_entry(efi_handle_t handle,
 	if (status != EFI_SUCCESS)
 		goto fail_free_initrd;
 
+	if (IS_ENABLED(CONFIG_ARM))
+		efi_handle_post_ebs_state();
+
 	efi_enter_kernel(image_addr, fdt_addr, fdt_totalsize((void *)fdt_addr));
 	/* not reached */
 
diff --git a/drivers/firmware/efi/libstub/efistub.h b/drivers/firmware/efi/libstub/efistub.h
index bcd8c0a785f0..6217ce647ebf 100644
--- a/drivers/firmware/efi/libstub/efistub.h
+++ b/drivers/firmware/efi/libstub/efistub.h
@@ -771,4 +771,6 @@ efi_status_t efi_load_initrd(efi_loaded_image_t *image,
 			     unsigned long soft_limit,
 			     unsigned long hard_limit);
 
+void efi_handle_post_ebs_state(void);
+
 #endif
diff --git a/include/linux/efi.h b/include/linux/efi.h
index c3449c9699d0..bb35f3305e55 100644
--- a/include/linux/efi.h
+++ b/include/linux/efi.h
@@ -350,6 +350,7 @@ void efi_native_runtime_setup(void);
  * associated with ConOut
  */
 #define LINUX_EFI_ARM_SCREEN_INFO_TABLE_GUID	EFI_GUID(0xe03fc20a, 0x85dc, 0x406e,  0xb9, 0x0e, 0x4a, 0xb5, 0x02, 0x37, 0x1d, 0x95)
+#define LINUX_EFI_ARM_CPU_STATE_TABLE_GUID	EFI_GUID(0xef79e4aa, 0x3c3d, 0x4989,  0xb9, 0x02, 0x07, 0xa9, 0x43, 0xe5, 0x50, 0xd2)
 #define LINUX_EFI_LOADER_ENTRY_GUID		EFI_GUID(0x4a67b082, 0x0a4c, 0x41cf,  0xb6, 0xc7, 0x44, 0x0b, 0x29, 0xbb, 0x8c, 0x4f)
 #define LINUX_EFI_RANDOM_SEED_TABLE_GUID	EFI_GUID(0x1ce1e5bc, 0x7ceb, 0x42f2,  0x81, 0xe5, 0x8a, 0xad, 0xf1, 0x80, 0xf5, 0x7b)
 #define LINUX_EFI_TPM_EVENT_LOG_GUID		EFI_GUID(0xb7799cb0, 0xeca2, 0x4943,  0x96, 0x67, 0x1f, 0xae, 0x07, 0xb7, 0x47, 0xfa)
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH v3] efi/libstub: arm: Print CPU boot mode and MMU state at boot
@ 2020-06-16  8:50 ` Ard Biesheuvel
  0 siblings, 0 replies; 10+ messages in thread
From: Ard Biesheuvel @ 2020-06-16  8:50 UTC (permalink / raw)
  To: linux-efi
  Cc: Samer.El-Haj-Mahmoud, xypron.glpk, grant.likely, leif,
	Ard Biesheuvel, linux-arm-kernel

On 32-bit ARM, we may boot at HYP mode, or with the MMU and caches off
(or both), even though the EFI spec does not actually support this.
While booting at HYP mode is something we might tolerate, fiddling
with the caches is a more serious issue, as disabling the caches is
tricky to do safely from C code, and running without the Dcache makes
it impossible to support unaligned memory accesses, which is another
explicit requirement imposed by the EFI spec.

So take note of the CPU mode and MMU state in the EFI stub diagnostic
output so that we can easily diagnose any issues that may arise from
this. E.g.,

  EFI stub: Entering in SVC mode with MMU enabled

Also, capture the CPSR and SCTLR system register values at EFI stub
entry, and after ExitBootServices() returns, and check whether the
MMU and Dcache were disabled at any point. If this is the case, a
diagnostic message like the following will be emitted:

  efi: [Firmware Bug]: EFI stub was entered with MMU and Dcache disabled, please fix your firmware!
  efi: CPSR at EFI stub entry        : 0x600001d3
  efi: SCTLR at EFI stub entry       : 0x00c51838
  efi: CPSR after ExitBootServices() : 0x600001d3
  efi: SCTLR after ExitBootServices(): 0x00c50838

Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
---
Adding Grant and Samer to cc, as this may be something that should be taken
into account in the EBBR context? Note that this affects 64-bit only.

 arch/arm/include/asm/efi.h                |  7 +++
 drivers/firmware/efi/arm-init.c           | 37 ++++++++++++--
 drivers/firmware/efi/libstub/arm32-stub.c | 52 +++++++++++++++++++-
 drivers/firmware/efi/libstub/efi-stub.c   |  3 ++
 drivers/firmware/efi/libstub/efistub.h    |  2 +
 include/linux/efi.h                       |  1 +
 6 files changed, 98 insertions(+), 4 deletions(-)

diff --git a/arch/arm/include/asm/efi.h b/arch/arm/include/asm/efi.h
index 84dc0ba822f5..5dcf3c6011b7 100644
--- a/arch/arm/include/asm/efi.h
+++ b/arch/arm/include/asm/efi.h
@@ -87,4 +87,11 @@ static inline unsigned long efi_get_max_initrd_addr(unsigned long dram_base,
 	return dram_base + SZ_512M;
 }
 
+struct efi_arm_entry_state {
+	u32	cpsr_before_ebs;
+	u32	sctlr_before_ebs;
+	u32	cpsr_after_ebs;
+	u32	sctlr_after_ebs;
+};
+
 #endif /* _ASM_ARM_EFI_H */
diff --git a/drivers/firmware/efi/arm-init.c b/drivers/firmware/efi/arm-init.c
index c697e70ca7e7..4d1c272a4f79 100644
--- a/drivers/firmware/efi/arm-init.c
+++ b/drivers/firmware/efi/arm-init.c
@@ -52,9 +52,11 @@ static phys_addr_t __init efi_to_phys(unsigned long addr)
 }
 
 static __initdata unsigned long screen_info_table = EFI_INVALID_TABLE_ADDR;
+static __initdata unsigned long cpu_state_table = EFI_INVALID_TABLE_ADDR;
 
 static const efi_config_table_type_t arch_tables[] __initconst = {
 	{LINUX_EFI_ARM_SCREEN_INFO_TABLE_GUID, &screen_info_table},
+	{LINUX_EFI_ARM_CPU_STATE_TABLE_GUID, &cpu_state_table},
 	{}
 };
 
@@ -116,7 +118,8 @@ static int __init uefi_init(u64 efi_system_table)
 		goto out;
 	}
 	retval = efi_config_parse_tables(config_tables, systab->nr_tables,
-					 arch_tables);
+					 IS_ENABLED(CONFIG_ARM) ? arch_tables
+								: NULL);
 
 	early_memunmap(config_tables, table_size);
 out:
@@ -238,9 +241,37 @@ void __init efi_init(void)
 
 	init_screen_info();
 
+#ifdef CONFIG_ARM
 	/* ARM does not permit early mappings to persist across paging_init() */
-	if (IS_ENABLED(CONFIG_ARM))
-		efi_memmap_unmap();
+	efi_memmap_unmap();
+
+	if (cpu_state_table != EFI_INVALID_TABLE_ADDR) {
+		struct efi_arm_entry_state *state;
+		bool dump_state = true;
+
+		state = early_memremap_ro(cpu_state_table,
+					  sizeof(struct efi_arm_entry_state));
+		if (state == NULL) {
+			pr_warn("Unable to map CPU entry state table.\n");
+			return;
+		}
+
+		if ((state->sctlr_before_ebs & 1) == 0)
+			pr_warn(FW_BUG "EFI stub was entered with MMU and Dcache disabled, please fix your firmware!\n");
+		else if ((state->sctlr_after_ebs & 1) == 0)
+			pr_warn(FW_BUG "ExitBootServices() returned with MMU and Dcache disabled, please fix your firmware!\n");
+		else
+			dump_state = false;
+
+		if (dump_state || efi_enabled(EFI_DBG)) {
+			pr_info("CPSR at EFI stub entry        : 0x%08x\n", state->cpsr_before_ebs);
+			pr_info("SCTLR at EFI stub entry       : 0x%08x\n", state->sctlr_before_ebs);
+			pr_info("CPSR after ExitBootServices() : 0x%08x\n", state->cpsr_after_ebs);
+			pr_info("SCTLR after ExitBootServices(): 0x%08x\n", state->sctlr_after_ebs);
+		}
+		early_memunmap(state, sizeof(struct efi_arm_entry_state));
+	}
+#endif
 }
 
 static bool efifb_overlaps_pci_range(const struct of_pci_range *range)
diff --git a/drivers/firmware/efi/libstub/arm32-stub.c b/drivers/firmware/efi/libstub/arm32-stub.c
index 40243f524556..60783228db6e 100644
--- a/drivers/firmware/efi/libstub/arm32-stub.c
+++ b/drivers/firmware/efi/libstub/arm32-stub.c
@@ -7,10 +7,49 @@
 
 #include "efistub.h"
 
+static efi_guid_t cpu_state_guid = LINUX_EFI_ARM_CPU_STATE_TABLE_GUID;
+
+struct efi_arm_entry_state *efi_entry_state;
+
+static void get_cpu_state(u32 *cpsr, u32 *sctlr)
+{
+	asm("mrs %0, cpsr" : "=r"(*cpsr));
+	if ((*cpsr & MODE_MASK) == HYP_MODE)
+		asm("mrc p15, 4, %0, c1, c0, 0" : "=r"(*sctlr));
+	else
+		asm("mrc p15, 0, %0, c1, c0, 0" : "=r"(*sctlr));
+}
+
 efi_status_t check_platform_features(void)
 {
+	efi_status_t status;
+	u32 cpsr, sctlr;
 	int block;
 
+	get_cpu_state(&cpsr, &sctlr);
+
+	efi_info("Entering in %s mode with MMU %sabled\n",
+		 ((cpsr & MODE_MASK) == HYP_MODE) ? "HYP" : "SVC",
+		 (sctlr & 1) ? "en" : "dis");
+
+	status = efi_bs_call(allocate_pool, EFI_LOADER_DATA,
+			     sizeof(*efi_entry_state),
+			     (void **)&efi_entry_state);
+	if (status != EFI_SUCCESS) {
+		efi_err("allocate_pool() failed\n");
+		return status;
+	}
+
+	efi_entry_state->cpsr_before_ebs = cpsr;
+	efi_entry_state->sctlr_before_ebs = sctlr;
+
+	status = efi_bs_call(install_configuration_table, &cpu_state_guid,
+			     efi_entry_state);
+	if (status != EFI_SUCCESS) {
+		efi_err("install_configuration_table() failed\n");
+		goto free_state;
+	}
+
 	/* non-LPAE kernels can run anywhere */
 	if (!IS_ENABLED(CONFIG_ARM_LPAE))
 		return EFI_SUCCESS;
@@ -19,9 +58,20 @@ efi_status_t check_platform_features(void)
 	block = cpuid_feature_extract(CPUID_EXT_MMFR0, 0);
 	if (block < 5) {
 		efi_err("This LPAE kernel is not supported by your CPU\n");
-		return EFI_UNSUPPORTED;
+		status = EFI_UNSUPPORTED;
+		goto free_state;
 	}
 	return EFI_SUCCESS;
+
+free_state:
+	efi_bs_call(free_pool, efi_entry_state);
+	return status;
+}
+
+void efi_handle_post_ebs_state(void)
+{
+	get_cpu_state(&efi_entry_state->cpsr_after_ebs,
+		      &efi_entry_state->sctlr_after_ebs);
 }
 
 static efi_guid_t screen_info_guid = LINUX_EFI_ARM_SCREEN_INFO_TABLE_GUID;
diff --git a/drivers/firmware/efi/libstub/efi-stub.c b/drivers/firmware/efi/libstub/efi-stub.c
index e97370bdfdb0..3318ec3f8e5b 100644
--- a/drivers/firmware/efi/libstub/efi-stub.c
+++ b/drivers/firmware/efi/libstub/efi-stub.c
@@ -329,6 +329,9 @@ efi_status_t __efiapi efi_pe_entry(efi_handle_t handle,
 	if (status != EFI_SUCCESS)
 		goto fail_free_initrd;
 
+	if (IS_ENABLED(CONFIG_ARM))
+		efi_handle_post_ebs_state();
+
 	efi_enter_kernel(image_addr, fdt_addr, fdt_totalsize((void *)fdt_addr));
 	/* not reached */
 
diff --git a/drivers/firmware/efi/libstub/efistub.h b/drivers/firmware/efi/libstub/efistub.h
index bcd8c0a785f0..6217ce647ebf 100644
--- a/drivers/firmware/efi/libstub/efistub.h
+++ b/drivers/firmware/efi/libstub/efistub.h
@@ -771,4 +771,6 @@ efi_status_t efi_load_initrd(efi_loaded_image_t *image,
 			     unsigned long soft_limit,
 			     unsigned long hard_limit);
 
+void efi_handle_post_ebs_state(void);
+
 #endif
diff --git a/include/linux/efi.h b/include/linux/efi.h
index c3449c9699d0..bb35f3305e55 100644
--- a/include/linux/efi.h
+++ b/include/linux/efi.h
@@ -350,6 +350,7 @@ void efi_native_runtime_setup(void);
  * associated with ConOut
  */
 #define LINUX_EFI_ARM_SCREEN_INFO_TABLE_GUID	EFI_GUID(0xe03fc20a, 0x85dc, 0x406e,  0xb9, 0x0e, 0x4a, 0xb5, 0x02, 0x37, 0x1d, 0x95)
+#define LINUX_EFI_ARM_CPU_STATE_TABLE_GUID	EFI_GUID(0xef79e4aa, 0x3c3d, 0x4989,  0xb9, 0x02, 0x07, 0xa9, 0x43, 0xe5, 0x50, 0xd2)
 #define LINUX_EFI_LOADER_ENTRY_GUID		EFI_GUID(0x4a67b082, 0x0a4c, 0x41cf,  0xb6, 0xc7, 0x44, 0x0b, 0x29, 0xbb, 0x8c, 0x4f)
 #define LINUX_EFI_RANDOM_SEED_TABLE_GUID	EFI_GUID(0x1ce1e5bc, 0x7ceb, 0x42f2,  0x81, 0xe5, 0x8a, 0xad, 0xf1, 0x80, 0xf5, 0x7b)
 #define LINUX_EFI_TPM_EVENT_LOG_GUID		EFI_GUID(0xb7799cb0, 0xeca2, 0x4943,  0x96, 0x67, 0x1f, 0xae, 0x07, 0xb7, 0x47, 0xfa)
-- 
2.27.0


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: [PATCH v3] efi/libstub: arm: Print CPU boot mode and MMU state at boot
  2020-06-16  8:50 ` Ard Biesheuvel
@ 2020-06-16  8:51   ` Ard Biesheuvel
  -1 siblings, 0 replies; 10+ messages in thread
From: Ard Biesheuvel @ 2020-06-16  8:51 UTC (permalink / raw)
  To: linux-efi
  Cc: Linux ARM, Samer.El-Haj-Mahmoud, Heinrich Schuchardt,
	Grant Likely, Leif Lindholm

On Tue, 16 Jun 2020 at 10:50, Ard Biesheuvel <ardb@kernel.org> wrote:
>
> On 32-bit ARM, we may boot at HYP mode, or with the MMU and caches off
> (or both), even though the EFI spec does not actually support this.
> While booting at HYP mode is something we might tolerate, fiddling
> with the caches is a more serious issue, as disabling the caches is
> tricky to do safely from C code, and running without the Dcache makes
> it impossible to support unaligned memory accesses, which is another
> explicit requirement imposed by the EFI spec.
>
> So take note of the CPU mode and MMU state in the EFI stub diagnostic
> output so that we can easily diagnose any issues that may arise from
> this. E.g.,
>
>   EFI stub: Entering in SVC mode with MMU enabled
>
> Also, capture the CPSR and SCTLR system register values at EFI stub
> entry, and after ExitBootServices() returns, and check whether the
> MMU and Dcache were disabled at any point. If this is the case, a
> diagnostic message like the following will be emitted:
>
>   efi: [Firmware Bug]: EFI stub was entered with MMU and Dcache disabled, please fix your firmware!
>   efi: CPSR at EFI stub entry        : 0x600001d3
>   efi: SCTLR at EFI stub entry       : 0x00c51838
>   efi: CPSR after ExitBootServices() : 0x600001d3
>   efi: SCTLR after ExitBootServices(): 0x00c50838
>
> Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
> ---
> Adding Grant and Samer to cc, as this may be something that should be taken
> into account in the EBBR context? Note that this affects 64-bit only.
>

Affects *32* bit only


>  arch/arm/include/asm/efi.h                |  7 +++
>  drivers/firmware/efi/arm-init.c           | 37 ++++++++++++--
>  drivers/firmware/efi/libstub/arm32-stub.c | 52 +++++++++++++++++++-
>  drivers/firmware/efi/libstub/efi-stub.c   |  3 ++
>  drivers/firmware/efi/libstub/efistub.h    |  2 +
>  include/linux/efi.h                       |  1 +
>  6 files changed, 98 insertions(+), 4 deletions(-)
>
> diff --git a/arch/arm/include/asm/efi.h b/arch/arm/include/asm/efi.h
> index 84dc0ba822f5..5dcf3c6011b7 100644
> --- a/arch/arm/include/asm/efi.h
> +++ b/arch/arm/include/asm/efi.h
> @@ -87,4 +87,11 @@ static inline unsigned long efi_get_max_initrd_addr(unsigned long dram_base,
>         return dram_base + SZ_512M;
>  }
>
> +struct efi_arm_entry_state {
> +       u32     cpsr_before_ebs;
> +       u32     sctlr_before_ebs;
> +       u32     cpsr_after_ebs;
> +       u32     sctlr_after_ebs;
> +};
> +
>  #endif /* _ASM_ARM_EFI_H */
> diff --git a/drivers/firmware/efi/arm-init.c b/drivers/firmware/efi/arm-init.c
> index c697e70ca7e7..4d1c272a4f79 100644
> --- a/drivers/firmware/efi/arm-init.c
> +++ b/drivers/firmware/efi/arm-init.c
> @@ -52,9 +52,11 @@ static phys_addr_t __init efi_to_phys(unsigned long addr)
>  }
>
>  static __initdata unsigned long screen_info_table = EFI_INVALID_TABLE_ADDR;
> +static __initdata unsigned long cpu_state_table = EFI_INVALID_TABLE_ADDR;
>
>  static const efi_config_table_type_t arch_tables[] __initconst = {
>         {LINUX_EFI_ARM_SCREEN_INFO_TABLE_GUID, &screen_info_table},
> +       {LINUX_EFI_ARM_CPU_STATE_TABLE_GUID, &cpu_state_table},
>         {}
>  };
>
> @@ -116,7 +118,8 @@ static int __init uefi_init(u64 efi_system_table)
>                 goto out;
>         }
>         retval = efi_config_parse_tables(config_tables, systab->nr_tables,
> -                                        arch_tables);
> +                                        IS_ENABLED(CONFIG_ARM) ? arch_tables
> +                                                               : NULL);
>
>         early_memunmap(config_tables, table_size);
>  out:
> @@ -238,9 +241,37 @@ void __init efi_init(void)
>
>         init_screen_info();
>
> +#ifdef CONFIG_ARM
>         /* ARM does not permit early mappings to persist across paging_init() */
> -       if (IS_ENABLED(CONFIG_ARM))
> -               efi_memmap_unmap();
> +       efi_memmap_unmap();
> +
> +       if (cpu_state_table != EFI_INVALID_TABLE_ADDR) {
> +               struct efi_arm_entry_state *state;
> +               bool dump_state = true;
> +
> +               state = early_memremap_ro(cpu_state_table,
> +                                         sizeof(struct efi_arm_entry_state));
> +               if (state == NULL) {
> +                       pr_warn("Unable to map CPU entry state table.\n");
> +                       return;
> +               }
> +
> +               if ((state->sctlr_before_ebs & 1) == 0)
> +                       pr_warn(FW_BUG "EFI stub was entered with MMU and Dcache disabled, please fix your firmware!\n");
> +               else if ((state->sctlr_after_ebs & 1) == 0)
> +                       pr_warn(FW_BUG "ExitBootServices() returned with MMU and Dcache disabled, please fix your firmware!\n");
> +               else
> +                       dump_state = false;
> +
> +               if (dump_state || efi_enabled(EFI_DBG)) {
> +                       pr_info("CPSR at EFI stub entry        : 0x%08x\n", state->cpsr_before_ebs);
> +                       pr_info("SCTLR at EFI stub entry       : 0x%08x\n", state->sctlr_before_ebs);
> +                       pr_info("CPSR after ExitBootServices() : 0x%08x\n", state->cpsr_after_ebs);
> +                       pr_info("SCTLR after ExitBootServices(): 0x%08x\n", state->sctlr_after_ebs);
> +               }
> +               early_memunmap(state, sizeof(struct efi_arm_entry_state));
> +       }
> +#endif
>  }
>
>  static bool efifb_overlaps_pci_range(const struct of_pci_range *range)
> diff --git a/drivers/firmware/efi/libstub/arm32-stub.c b/drivers/firmware/efi/libstub/arm32-stub.c
> index 40243f524556..60783228db6e 100644
> --- a/drivers/firmware/efi/libstub/arm32-stub.c
> +++ b/drivers/firmware/efi/libstub/arm32-stub.c
> @@ -7,10 +7,49 @@
>
>  #include "efistub.h"
>
> +static efi_guid_t cpu_state_guid = LINUX_EFI_ARM_CPU_STATE_TABLE_GUID;
> +
> +struct efi_arm_entry_state *efi_entry_state;
> +
> +static void get_cpu_state(u32 *cpsr, u32 *sctlr)
> +{
> +       asm("mrs %0, cpsr" : "=r"(*cpsr));
> +       if ((*cpsr & MODE_MASK) == HYP_MODE)
> +               asm("mrc p15, 4, %0, c1, c0, 0" : "=r"(*sctlr));
> +       else
> +               asm("mrc p15, 0, %0, c1, c0, 0" : "=r"(*sctlr));
> +}
> +
>  efi_status_t check_platform_features(void)
>  {
> +       efi_status_t status;
> +       u32 cpsr, sctlr;
>         int block;
>
> +       get_cpu_state(&cpsr, &sctlr);
> +
> +       efi_info("Entering in %s mode with MMU %sabled\n",
> +                ((cpsr & MODE_MASK) == HYP_MODE) ? "HYP" : "SVC",
> +                (sctlr & 1) ? "en" : "dis");
> +
> +       status = efi_bs_call(allocate_pool, EFI_LOADER_DATA,
> +                            sizeof(*efi_entry_state),
> +                            (void **)&efi_entry_state);
> +       if (status != EFI_SUCCESS) {
> +               efi_err("allocate_pool() failed\n");
> +               return status;
> +       }
> +
> +       efi_entry_state->cpsr_before_ebs = cpsr;
> +       efi_entry_state->sctlr_before_ebs = sctlr;
> +
> +       status = efi_bs_call(install_configuration_table, &cpu_state_guid,
> +                            efi_entry_state);
> +       if (status != EFI_SUCCESS) {
> +               efi_err("install_configuration_table() failed\n");
> +               goto free_state;
> +       }
> +
>         /* non-LPAE kernels can run anywhere */
>         if (!IS_ENABLED(CONFIG_ARM_LPAE))
>                 return EFI_SUCCESS;
> @@ -19,9 +58,20 @@ efi_status_t check_platform_features(void)
>         block = cpuid_feature_extract(CPUID_EXT_MMFR0, 0);
>         if (block < 5) {
>                 efi_err("This LPAE kernel is not supported by your CPU\n");
> -               return EFI_UNSUPPORTED;
> +               status = EFI_UNSUPPORTED;
> +               goto free_state;
>         }
>         return EFI_SUCCESS;
> +
> +free_state:
> +       efi_bs_call(free_pool, efi_entry_state);
> +       return status;
> +}
> +
> +void efi_handle_post_ebs_state(void)
> +{
> +       get_cpu_state(&efi_entry_state->cpsr_after_ebs,
> +                     &efi_entry_state->sctlr_after_ebs);
>  }
>
>  static efi_guid_t screen_info_guid = LINUX_EFI_ARM_SCREEN_INFO_TABLE_GUID;
> diff --git a/drivers/firmware/efi/libstub/efi-stub.c b/drivers/firmware/efi/libstub/efi-stub.c
> index e97370bdfdb0..3318ec3f8e5b 100644
> --- a/drivers/firmware/efi/libstub/efi-stub.c
> +++ b/drivers/firmware/efi/libstub/efi-stub.c
> @@ -329,6 +329,9 @@ efi_status_t __efiapi efi_pe_entry(efi_handle_t handle,
>         if (status != EFI_SUCCESS)
>                 goto fail_free_initrd;
>
> +       if (IS_ENABLED(CONFIG_ARM))
> +               efi_handle_post_ebs_state();
> +
>         efi_enter_kernel(image_addr, fdt_addr, fdt_totalsize((void *)fdt_addr));
>         /* not reached */
>
> diff --git a/drivers/firmware/efi/libstub/efistub.h b/drivers/firmware/efi/libstub/efistub.h
> index bcd8c0a785f0..6217ce647ebf 100644
> --- a/drivers/firmware/efi/libstub/efistub.h
> +++ b/drivers/firmware/efi/libstub/efistub.h
> @@ -771,4 +771,6 @@ efi_status_t efi_load_initrd(efi_loaded_image_t *image,
>                              unsigned long soft_limit,
>                              unsigned long hard_limit);
>
> +void efi_handle_post_ebs_state(void);
> +
>  #endif
> diff --git a/include/linux/efi.h b/include/linux/efi.h
> index c3449c9699d0..bb35f3305e55 100644
> --- a/include/linux/efi.h
> +++ b/include/linux/efi.h
> @@ -350,6 +350,7 @@ void efi_native_runtime_setup(void);
>   * associated with ConOut
>   */
>  #define LINUX_EFI_ARM_SCREEN_INFO_TABLE_GUID   EFI_GUID(0xe03fc20a, 0x85dc, 0x406e,  0xb9, 0x0e, 0x4a, 0xb5, 0x02, 0x37, 0x1d, 0x95)
> +#define LINUX_EFI_ARM_CPU_STATE_TABLE_GUID     EFI_GUID(0xef79e4aa, 0x3c3d, 0x4989,  0xb9, 0x02, 0x07, 0xa9, 0x43, 0xe5, 0x50, 0xd2)
>  #define LINUX_EFI_LOADER_ENTRY_GUID            EFI_GUID(0x4a67b082, 0x0a4c, 0x41cf,  0xb6, 0xc7, 0x44, 0x0b, 0x29, 0xbb, 0x8c, 0x4f)
>  #define LINUX_EFI_RANDOM_SEED_TABLE_GUID       EFI_GUID(0x1ce1e5bc, 0x7ceb, 0x42f2,  0x81, 0xe5, 0x8a, 0xad, 0xf1, 0x80, 0xf5, 0x7b)
>  #define LINUX_EFI_TPM_EVENT_LOG_GUID           EFI_GUID(0xb7799cb0, 0xeca2, 0x4943,  0x96, 0x67, 0x1f, 0xae, 0x07, 0xb7, 0x47, 0xfa)
> --
> 2.27.0
>

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v3] efi/libstub: arm: Print CPU boot mode and MMU state at boot
@ 2020-06-16  8:51   ` Ard Biesheuvel
  0 siblings, 0 replies; 10+ messages in thread
From: Ard Biesheuvel @ 2020-06-16  8:51 UTC (permalink / raw)
  To: linux-efi
  Cc: Leif Lindholm, Heinrich Schuchardt, Samer.El-Haj-Mahmoud,
	Grant Likely, Linux ARM

On Tue, 16 Jun 2020 at 10:50, Ard Biesheuvel <ardb@kernel.org> wrote:
>
> On 32-bit ARM, we may boot at HYP mode, or with the MMU and caches off
> (or both), even though the EFI spec does not actually support this.
> While booting at HYP mode is something we might tolerate, fiddling
> with the caches is a more serious issue, as disabling the caches is
> tricky to do safely from C code, and running without the Dcache makes
> it impossible to support unaligned memory accesses, which is another
> explicit requirement imposed by the EFI spec.
>
> So take note of the CPU mode and MMU state in the EFI stub diagnostic
> output so that we can easily diagnose any issues that may arise from
> this. E.g.,
>
>   EFI stub: Entering in SVC mode with MMU enabled
>
> Also, capture the CPSR and SCTLR system register values at EFI stub
> entry, and after ExitBootServices() returns, and check whether the
> MMU and Dcache were disabled at any point. If this is the case, a
> diagnostic message like the following will be emitted:
>
>   efi: [Firmware Bug]: EFI stub was entered with MMU and Dcache disabled, please fix your firmware!
>   efi: CPSR at EFI stub entry        : 0x600001d3
>   efi: SCTLR at EFI stub entry       : 0x00c51838
>   efi: CPSR after ExitBootServices() : 0x600001d3
>   efi: SCTLR after ExitBootServices(): 0x00c50838
>
> Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
> ---
> Adding Grant and Samer to cc, as this may be something that should be taken
> into account in the EBBR context? Note that this affects 64-bit only.
>

Affects *32* bit only


>  arch/arm/include/asm/efi.h                |  7 +++
>  drivers/firmware/efi/arm-init.c           | 37 ++++++++++++--
>  drivers/firmware/efi/libstub/arm32-stub.c | 52 +++++++++++++++++++-
>  drivers/firmware/efi/libstub/efi-stub.c   |  3 ++
>  drivers/firmware/efi/libstub/efistub.h    |  2 +
>  include/linux/efi.h                       |  1 +
>  6 files changed, 98 insertions(+), 4 deletions(-)
>
> diff --git a/arch/arm/include/asm/efi.h b/arch/arm/include/asm/efi.h
> index 84dc0ba822f5..5dcf3c6011b7 100644
> --- a/arch/arm/include/asm/efi.h
> +++ b/arch/arm/include/asm/efi.h
> @@ -87,4 +87,11 @@ static inline unsigned long efi_get_max_initrd_addr(unsigned long dram_base,
>         return dram_base + SZ_512M;
>  }
>
> +struct efi_arm_entry_state {
> +       u32     cpsr_before_ebs;
> +       u32     sctlr_before_ebs;
> +       u32     cpsr_after_ebs;
> +       u32     sctlr_after_ebs;
> +};
> +
>  #endif /* _ASM_ARM_EFI_H */
> diff --git a/drivers/firmware/efi/arm-init.c b/drivers/firmware/efi/arm-init.c
> index c697e70ca7e7..4d1c272a4f79 100644
> --- a/drivers/firmware/efi/arm-init.c
> +++ b/drivers/firmware/efi/arm-init.c
> @@ -52,9 +52,11 @@ static phys_addr_t __init efi_to_phys(unsigned long addr)
>  }
>
>  static __initdata unsigned long screen_info_table = EFI_INVALID_TABLE_ADDR;
> +static __initdata unsigned long cpu_state_table = EFI_INVALID_TABLE_ADDR;
>
>  static const efi_config_table_type_t arch_tables[] __initconst = {
>         {LINUX_EFI_ARM_SCREEN_INFO_TABLE_GUID, &screen_info_table},
> +       {LINUX_EFI_ARM_CPU_STATE_TABLE_GUID, &cpu_state_table},
>         {}
>  };
>
> @@ -116,7 +118,8 @@ static int __init uefi_init(u64 efi_system_table)
>                 goto out;
>         }
>         retval = efi_config_parse_tables(config_tables, systab->nr_tables,
> -                                        arch_tables);
> +                                        IS_ENABLED(CONFIG_ARM) ? arch_tables
> +                                                               : NULL);
>
>         early_memunmap(config_tables, table_size);
>  out:
> @@ -238,9 +241,37 @@ void __init efi_init(void)
>
>         init_screen_info();
>
> +#ifdef CONFIG_ARM
>         /* ARM does not permit early mappings to persist across paging_init() */
> -       if (IS_ENABLED(CONFIG_ARM))
> -               efi_memmap_unmap();
> +       efi_memmap_unmap();
> +
> +       if (cpu_state_table != EFI_INVALID_TABLE_ADDR) {
> +               struct efi_arm_entry_state *state;
> +               bool dump_state = true;
> +
> +               state = early_memremap_ro(cpu_state_table,
> +                                         sizeof(struct efi_arm_entry_state));
> +               if (state == NULL) {
> +                       pr_warn("Unable to map CPU entry state table.\n");
> +                       return;
> +               }
> +
> +               if ((state->sctlr_before_ebs & 1) == 0)
> +                       pr_warn(FW_BUG "EFI stub was entered with MMU and Dcache disabled, please fix your firmware!\n");
> +               else if ((state->sctlr_after_ebs & 1) == 0)
> +                       pr_warn(FW_BUG "ExitBootServices() returned with MMU and Dcache disabled, please fix your firmware!\n");
> +               else
> +                       dump_state = false;
> +
> +               if (dump_state || efi_enabled(EFI_DBG)) {
> +                       pr_info("CPSR at EFI stub entry        : 0x%08x\n", state->cpsr_before_ebs);
> +                       pr_info("SCTLR at EFI stub entry       : 0x%08x\n", state->sctlr_before_ebs);
> +                       pr_info("CPSR after ExitBootServices() : 0x%08x\n", state->cpsr_after_ebs);
> +                       pr_info("SCTLR after ExitBootServices(): 0x%08x\n", state->sctlr_after_ebs);
> +               }
> +               early_memunmap(state, sizeof(struct efi_arm_entry_state));
> +       }
> +#endif
>  }
>
>  static bool efifb_overlaps_pci_range(const struct of_pci_range *range)
> diff --git a/drivers/firmware/efi/libstub/arm32-stub.c b/drivers/firmware/efi/libstub/arm32-stub.c
> index 40243f524556..60783228db6e 100644
> --- a/drivers/firmware/efi/libstub/arm32-stub.c
> +++ b/drivers/firmware/efi/libstub/arm32-stub.c
> @@ -7,10 +7,49 @@
>
>  #include "efistub.h"
>
> +static efi_guid_t cpu_state_guid = LINUX_EFI_ARM_CPU_STATE_TABLE_GUID;
> +
> +struct efi_arm_entry_state *efi_entry_state;
> +
> +static void get_cpu_state(u32 *cpsr, u32 *sctlr)
> +{
> +       asm("mrs %0, cpsr" : "=r"(*cpsr));
> +       if ((*cpsr & MODE_MASK) == HYP_MODE)
> +               asm("mrc p15, 4, %0, c1, c0, 0" : "=r"(*sctlr));
> +       else
> +               asm("mrc p15, 0, %0, c1, c0, 0" : "=r"(*sctlr));
> +}
> +
>  efi_status_t check_platform_features(void)
>  {
> +       efi_status_t status;
> +       u32 cpsr, sctlr;
>         int block;
>
> +       get_cpu_state(&cpsr, &sctlr);
> +
> +       efi_info("Entering in %s mode with MMU %sabled\n",
> +                ((cpsr & MODE_MASK) == HYP_MODE) ? "HYP" : "SVC",
> +                (sctlr & 1) ? "en" : "dis");
> +
> +       status = efi_bs_call(allocate_pool, EFI_LOADER_DATA,
> +                            sizeof(*efi_entry_state),
> +                            (void **)&efi_entry_state);
> +       if (status != EFI_SUCCESS) {
> +               efi_err("allocate_pool() failed\n");
> +               return status;
> +       }
> +
> +       efi_entry_state->cpsr_before_ebs = cpsr;
> +       efi_entry_state->sctlr_before_ebs = sctlr;
> +
> +       status = efi_bs_call(install_configuration_table, &cpu_state_guid,
> +                            efi_entry_state);
> +       if (status != EFI_SUCCESS) {
> +               efi_err("install_configuration_table() failed\n");
> +               goto free_state;
> +       }
> +
>         /* non-LPAE kernels can run anywhere */
>         if (!IS_ENABLED(CONFIG_ARM_LPAE))
>                 return EFI_SUCCESS;
> @@ -19,9 +58,20 @@ efi_status_t check_platform_features(void)
>         block = cpuid_feature_extract(CPUID_EXT_MMFR0, 0);
>         if (block < 5) {
>                 efi_err("This LPAE kernel is not supported by your CPU\n");
> -               return EFI_UNSUPPORTED;
> +               status = EFI_UNSUPPORTED;
> +               goto free_state;
>         }
>         return EFI_SUCCESS;
> +
> +free_state:
> +       efi_bs_call(free_pool, efi_entry_state);
> +       return status;
> +}
> +
> +void efi_handle_post_ebs_state(void)
> +{
> +       get_cpu_state(&efi_entry_state->cpsr_after_ebs,
> +                     &efi_entry_state->sctlr_after_ebs);
>  }
>
>  static efi_guid_t screen_info_guid = LINUX_EFI_ARM_SCREEN_INFO_TABLE_GUID;
> diff --git a/drivers/firmware/efi/libstub/efi-stub.c b/drivers/firmware/efi/libstub/efi-stub.c
> index e97370bdfdb0..3318ec3f8e5b 100644
> --- a/drivers/firmware/efi/libstub/efi-stub.c
> +++ b/drivers/firmware/efi/libstub/efi-stub.c
> @@ -329,6 +329,9 @@ efi_status_t __efiapi efi_pe_entry(efi_handle_t handle,
>         if (status != EFI_SUCCESS)
>                 goto fail_free_initrd;
>
> +       if (IS_ENABLED(CONFIG_ARM))
> +               efi_handle_post_ebs_state();
> +
>         efi_enter_kernel(image_addr, fdt_addr, fdt_totalsize((void *)fdt_addr));
>         /* not reached */
>
> diff --git a/drivers/firmware/efi/libstub/efistub.h b/drivers/firmware/efi/libstub/efistub.h
> index bcd8c0a785f0..6217ce647ebf 100644
> --- a/drivers/firmware/efi/libstub/efistub.h
> +++ b/drivers/firmware/efi/libstub/efistub.h
> @@ -771,4 +771,6 @@ efi_status_t efi_load_initrd(efi_loaded_image_t *image,
>                              unsigned long soft_limit,
>                              unsigned long hard_limit);
>
> +void efi_handle_post_ebs_state(void);
> +
>  #endif
> diff --git a/include/linux/efi.h b/include/linux/efi.h
> index c3449c9699d0..bb35f3305e55 100644
> --- a/include/linux/efi.h
> +++ b/include/linux/efi.h
> @@ -350,6 +350,7 @@ void efi_native_runtime_setup(void);
>   * associated with ConOut
>   */
>  #define LINUX_EFI_ARM_SCREEN_INFO_TABLE_GUID   EFI_GUID(0xe03fc20a, 0x85dc, 0x406e,  0xb9, 0x0e, 0x4a, 0xb5, 0x02, 0x37, 0x1d, 0x95)
> +#define LINUX_EFI_ARM_CPU_STATE_TABLE_GUID     EFI_GUID(0xef79e4aa, 0x3c3d, 0x4989,  0xb9, 0x02, 0x07, 0xa9, 0x43, 0xe5, 0x50, 0xd2)
>  #define LINUX_EFI_LOADER_ENTRY_GUID            EFI_GUID(0x4a67b082, 0x0a4c, 0x41cf,  0xb6, 0xc7, 0x44, 0x0b, 0x29, 0xbb, 0x8c, 0x4f)
>  #define LINUX_EFI_RANDOM_SEED_TABLE_GUID       EFI_GUID(0x1ce1e5bc, 0x7ceb, 0x42f2,  0x81, 0xe5, 0x8a, 0xad, 0xf1, 0x80, 0xf5, 0x7b)
>  #define LINUX_EFI_TPM_EVENT_LOG_GUID           EFI_GUID(0xb7799cb0, 0xeca2, 0x4943,  0x96, 0x67, 0x1f, 0xae, 0x07, 0xb7, 0x47, 0xfa)
> --
> 2.27.0
>

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v3] efi/libstub: arm: Print CPU boot mode and MMU state at boot
  2020-06-16  8:51   ` Ard Biesheuvel
@ 2020-06-16 10:39     ` Leif Lindholm
  -1 siblings, 0 replies; 10+ messages in thread
From: Leif Lindholm @ 2020-06-16 10:39 UTC (permalink / raw)
  To: Ard Biesheuvel
  Cc: linux-efi, Linux ARM, Samer.El-Haj-Mahmoud, Heinrich Schuchardt,
	Grant Likely

On Tue, Jun 16, 2020 at 10:51:48 +0200, Ard Biesheuvel wrote:
> On Tue, 16 Jun 2020 at 10:50, Ard Biesheuvel <ardb@kernel.org> wrote:
> >
> > On 32-bit ARM, we may boot at HYP mode, or with the MMU and caches off
> > (or both), even though the EFI spec does not actually support this.
> > While booting at HYP mode is something we might tolerate, fiddling
> > with the caches is a more serious issue, as disabling the caches is
> > tricky to do safely from C code, and running without the Dcache makes
> > it impossible to support unaligned memory accesses, which is another
> > explicit requirement imposed by the EFI spec.
> >
> > So take note of the CPU mode and MMU state in the EFI stub diagnostic
> > output so that we can easily diagnose any issues that may arise from
> > this. E.g.,
> >
> >   EFI stub: Entering in SVC mode with MMU enabled
> >
> > Also, capture the CPSR and SCTLR system register values at EFI stub
> > entry, and after ExitBootServices() returns, and check whether the
> > MMU and Dcache were disabled at any point. If this is the case, a
> > diagnostic message like the following will be emitted:
> >
> >   efi: [Firmware Bug]: EFI stub was entered with MMU and Dcache disabled, please fix your firmware!
> >   efi: CPSR at EFI stub entry        : 0x600001d3
> >   efi: SCTLR at EFI stub entry       : 0x00c51838
> >   efi: CPSR after ExitBootServices() : 0x600001d3
> >   efi: SCTLR after ExitBootServices(): 0x00c50838
> >
> > Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
> > ---
> > Adding Grant and Samer to cc, as this may be something that should be taken
> > into account in the EBBR context? Note that this affects 64-bit only.
> >
> 
> Affects *32* bit only

One question and a teeny tiny nit below.

> >  arch/arm/include/asm/efi.h                |  7 +++
> >  drivers/firmware/efi/arm-init.c           | 37 ++++++++++++--
> >  drivers/firmware/efi/libstub/arm32-stub.c | 52 +++++++++++++++++++-
> >  drivers/firmware/efi/libstub/efi-stub.c   |  3 ++
> >  drivers/firmware/efi/libstub/efistub.h    |  2 +
> >  include/linux/efi.h                       |  1 +
> >  6 files changed, 98 insertions(+), 4 deletions(-)
> >
> > diff --git a/arch/arm/include/asm/efi.h b/arch/arm/include/asm/efi.h
> > index 84dc0ba822f5..5dcf3c6011b7 100644
> > --- a/arch/arm/include/asm/efi.h
> > +++ b/arch/arm/include/asm/efi.h
> > @@ -87,4 +87,11 @@ static inline unsigned long efi_get_max_initrd_addr(unsigned long dram_base,
> >         return dram_base + SZ_512M;
> >  }
> >
> > +struct efi_arm_entry_state {
> > +       u32     cpsr_before_ebs;
> > +       u32     sctlr_before_ebs;
> > +       u32     cpsr_after_ebs;
> > +       u32     sctlr_after_ebs;
> > +};
> > +
> >  #endif /* _ASM_ARM_EFI_H */
> > diff --git a/drivers/firmware/efi/arm-init.c b/drivers/firmware/efi/arm-init.c
> > index c697e70ca7e7..4d1c272a4f79 100644
> > --- a/drivers/firmware/efi/arm-init.c
> > +++ b/drivers/firmware/efi/arm-init.c
> > @@ -52,9 +52,11 @@ static phys_addr_t __init efi_to_phys(unsigned long addr)
> >  }
> >
> >  static __initdata unsigned long screen_info_table = EFI_INVALID_TABLE_ADDR;
> > +static __initdata unsigned long cpu_state_table = EFI_INVALID_TABLE_ADDR;
> >
> >  static const efi_config_table_type_t arch_tables[] __initconst = {
> >         {LINUX_EFI_ARM_SCREEN_INFO_TABLE_GUID, &screen_info_table},
> > +       {LINUX_EFI_ARM_CPU_STATE_TABLE_GUID, &cpu_state_table},
> >         {}
> >  };
> >
> > @@ -116,7 +118,8 @@ static int __init uefi_init(u64 efi_system_table)
> >                 goto out;
> >         }
> >         retval = efi_config_parse_tables(config_tables, systab->nr_tables,
> > -                                        arch_tables);
> > +                                        IS_ENABLED(CONFIG_ARM) ? arch_tables
> > +                                                               : NULL);

It this worthwhile?
If it turns out we have no need for an arch_table on arm64, should we
still include the static table, holding values only relevant for
32-bit, in arm64 builds at all?

> >
> >         early_memunmap(config_tables, table_size);
> >  out:
> > @@ -238,9 +241,37 @@ void __init efi_init(void)
> >
> >         init_screen_info();
> >
> > +#ifdef CONFIG_ARM
> >         /* ARM does not permit early mappings to persist across paging_init() */
> > -       if (IS_ENABLED(CONFIG_ARM))
> > -               efi_memmap_unmap();
> > +       efi_memmap_unmap();
> > +
> > +       if (cpu_state_table != EFI_INVALID_TABLE_ADDR) {
> > +               struct efi_arm_entry_state *state;
> > +               bool dump_state = true;
> > +
> > +               state = early_memremap_ro(cpu_state_table,
> > +                                         sizeof(struct efi_arm_entry_state));
> > +               if (state == NULL) {
> > +                       pr_warn("Unable to map CPU entry state table.\n");
> > +                       return;
> > +               }
> > +
> > +               if ((state->sctlr_before_ebs & 1) == 0)
> > +                       pr_warn(FW_BUG "EFI stub was entered with MMU and Dcache disabled, please fix your firmware!\n");
> > +               else if ((state->sctlr_after_ebs & 1) == 0)
> > +                       pr_warn(FW_BUG "ExitBootServices() returned with MMU and Dcache disabled, please fix your firmware!\n");
> > +               else
> > +                       dump_state = false;
> > +
> > +               if (dump_state || efi_enabled(EFI_DBG)) {
> > +                       pr_info("CPSR at EFI stub entry        : 0x%08x\n", state->cpsr_before_ebs);
> > +                       pr_info("SCTLR at EFI stub entry       : 0x%08x\n", state->sctlr_before_ebs);
> > +                       pr_info("CPSR after ExitBootServices() : 0x%08x\n", state->cpsr_after_ebs);
> > +                       pr_info("SCTLR after ExitBootServices(): 0x%08x\n", state->sctlr_after_ebs);
> > +               }
> > +               early_memunmap(state, sizeof(struct efi_arm_entry_state));
> > +       }
> > +#endif
> >  }
> >
> >  static bool efifb_overlaps_pci_range(const struct of_pci_range *range)
> > diff --git a/drivers/firmware/efi/libstub/arm32-stub.c b/drivers/firmware/efi/libstub/arm32-stub.c
> > index 40243f524556..60783228db6e 100644
> > --- a/drivers/firmware/efi/libstub/arm32-stub.c
> > +++ b/drivers/firmware/efi/libstub/arm32-stub.c
> > @@ -7,10 +7,49 @@
> >
> >  #include "efistub.h"
> >
> > +static efi_guid_t cpu_state_guid = LINUX_EFI_ARM_CPU_STATE_TABLE_GUID;
> > +
> > +struct efi_arm_entry_state *efi_entry_state;
> > +
> > +static void get_cpu_state(u32 *cpsr, u32 *sctlr)
> > +{
> > +       asm("mrs %0, cpsr" : "=r"(*cpsr));
> > +       if ((*cpsr & MODE_MASK) == HYP_MODE)
> > +               asm("mrc p15, 4, %0, c1, c0, 0" : "=r"(*sctlr));
> > +       else
> > +               asm("mrc p15, 0, %0, c1, c0, 0" : "=r"(*sctlr));
> > +}
> > +
> >  efi_status_t check_platform_features(void)
> >  {
> > +       efi_status_t status;
> > +       u32 cpsr, sctlr;
> >         int block;
> >
> > +       get_cpu_state(&cpsr, &sctlr);
> > +
> > +       efi_info("Entering in %s mode with MMU %sabled\n",
> > +                ((cpsr & MODE_MASK) == HYP_MODE) ? "HYP" : "SVC",
> > +                (sctlr & 1) ? "en" : "dis");
> > +
> > +       status = efi_bs_call(allocate_pool, EFI_LOADER_DATA,
> > +                            sizeof(*efi_entry_state),
> > +                            (void **)&efi_entry_state);
> > +       if (status != EFI_SUCCESS) {
> > +               efi_err("allocate_pool() failed\n");
> > +               return status;
> > +       }
> > +
> > +       efi_entry_state->cpsr_before_ebs = cpsr;
> > +       efi_entry_state->sctlr_before_ebs = sctlr;
> > +
> > +       status = efi_bs_call(install_configuration_table, &cpu_state_guid,
> > +                            efi_entry_state);
> > +       if (status != EFI_SUCCESS) {
> > +               efi_err("install_configuration_table() failed\n");
> > +               goto free_state;
> > +       }
> > +
> >         /* non-LPAE kernels can run anywhere */
> >         if (!IS_ENABLED(CONFIG_ARM_LPAE))
> >                 return EFI_SUCCESS;
> > @@ -19,9 +58,20 @@ efi_status_t check_platform_features(void)
> >         block = cpuid_feature_extract(CPUID_EXT_MMFR0, 0);
> >         if (block < 5) {
> >                 efi_err("This LPAE kernel is not supported by your CPU\n");
> > -               return EFI_UNSUPPORTED;
> > +               status = EFI_UNSUPPORTED;
> > +               goto free_state;
> >         }
> >         return EFI_SUCCESS;
> > +
> > +free_state:
> > +       efi_bs_call(free_pool, efi_entry_state);
> > +       return status;
> > +}
> > +
> > +void efi_handle_post_ebs_state(void)
> > +{
> > +       get_cpu_state(&efi_entry_state->cpsr_after_ebs,
> > +                     &efi_entry_state->sctlr_after_ebs);
> >  }
> >
> >  static efi_guid_t screen_info_guid = LINUX_EFI_ARM_SCREEN_INFO_TABLE_GUID;
> > diff --git a/drivers/firmware/efi/libstub/efi-stub.c b/drivers/firmware/efi/libstub/efi-stub.c
> > index e97370bdfdb0..3318ec3f8e5b 100644
> > --- a/drivers/firmware/efi/libstub/efi-stub.c
> > +++ b/drivers/firmware/efi/libstub/efi-stub.c
> > @@ -329,6 +329,9 @@ efi_status_t __efiapi efi_pe_entry(efi_handle_t handle,
> >         if (status != EFI_SUCCESS)
> >                 goto fail_free_initrd;
> >
> > +       if (IS_ENABLED(CONFIG_ARM))
> > +               efi_handle_post_ebs_state();
> > +
> >         efi_enter_kernel(image_addr, fdt_addr, fdt_totalsize((void *)fdt_addr));
> >         /* not reached */
> >
> > diff --git a/drivers/firmware/efi/libstub/efistub.h b/drivers/firmware/efi/libstub/efistub.h
> > index bcd8c0a785f0..6217ce647ebf 100644
> > --- a/drivers/firmware/efi/libstub/efistub.h
> > +++ b/drivers/firmware/efi/libstub/efistub.h
> > @@ -771,4 +771,6 @@ efi_status_t efi_load_initrd(efi_loaded_image_t *image,
> >                              unsigned long soft_limit,
> >                              unsigned long hard_limit);
> >
> > +void efi_handle_post_ebs_state(void);
> > +

This declaration is made for both arm/arm64.
(I said it was tiny, feel free to ignore.)

/
    Leif

> >  #endif
> > diff --git a/include/linux/efi.h b/include/linux/efi.h
> > index c3449c9699d0..bb35f3305e55 100644
> > --- a/include/linux/efi.h
> > +++ b/include/linux/efi.h
> > @@ -350,6 +350,7 @@ void efi_native_runtime_setup(void);
> >   * associated with ConOut
> >   */
> >  #define LINUX_EFI_ARM_SCREEN_INFO_TABLE_GUID   EFI_GUID(0xe03fc20a, 0x85dc, 0x406e,  0xb9, 0x0e, 0x4a, 0xb5, 0x02, 0x37, 0x1d, 0x95)
> > +#define LINUX_EFI_ARM_CPU_STATE_TABLE_GUID     EFI_GUID(0xef79e4aa, 0x3c3d, 0x4989,  0xb9, 0x02, 0x07, 0xa9, 0x43, 0xe5, 0x50, 0xd2)
> >  #define LINUX_EFI_LOADER_ENTRY_GUID            EFI_GUID(0x4a67b082, 0x0a4c, 0x41cf,  0xb6, 0xc7, 0x44, 0x0b, 0x29, 0xbb, 0x8c, 0x4f)
> >  #define LINUX_EFI_RANDOM_SEED_TABLE_GUID       EFI_GUID(0x1ce1e5bc, 0x7ceb, 0x42f2,  0x81, 0xe5, 0x8a, 0xad, 0xf1, 0x80, 0xf5, 0x7b)
> >  #define LINUX_EFI_TPM_EVENT_LOG_GUID           EFI_GUID(0xb7799cb0, 0xeca2, 0x4943,  0x96, 0x67, 0x1f, 0xae, 0x07, 0xb7, 0x47, 0xfa)
> > --
> > 2.27.0
> >

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v3] efi/libstub: arm: Print CPU boot mode and MMU state at boot
@ 2020-06-16 10:39     ` Leif Lindholm
  0 siblings, 0 replies; 10+ messages in thread
From: Leif Lindholm @ 2020-06-16 10:39 UTC (permalink / raw)
  To: Ard Biesheuvel
  Cc: Heinrich Schuchardt, Samer.El-Haj-Mahmoud, linux-efi,
	Grant Likely, Linux ARM

On Tue, Jun 16, 2020 at 10:51:48 +0200, Ard Biesheuvel wrote:
> On Tue, 16 Jun 2020 at 10:50, Ard Biesheuvel <ardb@kernel.org> wrote:
> >
> > On 32-bit ARM, we may boot at HYP mode, or with the MMU and caches off
> > (or both), even though the EFI spec does not actually support this.
> > While booting at HYP mode is something we might tolerate, fiddling
> > with the caches is a more serious issue, as disabling the caches is
> > tricky to do safely from C code, and running without the Dcache makes
> > it impossible to support unaligned memory accesses, which is another
> > explicit requirement imposed by the EFI spec.
> >
> > So take note of the CPU mode and MMU state in the EFI stub diagnostic
> > output so that we can easily diagnose any issues that may arise from
> > this. E.g.,
> >
> >   EFI stub: Entering in SVC mode with MMU enabled
> >
> > Also, capture the CPSR and SCTLR system register values at EFI stub
> > entry, and after ExitBootServices() returns, and check whether the
> > MMU and Dcache were disabled at any point. If this is the case, a
> > diagnostic message like the following will be emitted:
> >
> >   efi: [Firmware Bug]: EFI stub was entered with MMU and Dcache disabled, please fix your firmware!
> >   efi: CPSR at EFI stub entry        : 0x600001d3
> >   efi: SCTLR at EFI stub entry       : 0x00c51838
> >   efi: CPSR after ExitBootServices() : 0x600001d3
> >   efi: SCTLR after ExitBootServices(): 0x00c50838
> >
> > Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
> > ---
> > Adding Grant and Samer to cc, as this may be something that should be taken
> > into account in the EBBR context? Note that this affects 64-bit only.
> >
> 
> Affects *32* bit only

One question and a teeny tiny nit below.

> >  arch/arm/include/asm/efi.h                |  7 +++
> >  drivers/firmware/efi/arm-init.c           | 37 ++++++++++++--
> >  drivers/firmware/efi/libstub/arm32-stub.c | 52 +++++++++++++++++++-
> >  drivers/firmware/efi/libstub/efi-stub.c   |  3 ++
> >  drivers/firmware/efi/libstub/efistub.h    |  2 +
> >  include/linux/efi.h                       |  1 +
> >  6 files changed, 98 insertions(+), 4 deletions(-)
> >
> > diff --git a/arch/arm/include/asm/efi.h b/arch/arm/include/asm/efi.h
> > index 84dc0ba822f5..5dcf3c6011b7 100644
> > --- a/arch/arm/include/asm/efi.h
> > +++ b/arch/arm/include/asm/efi.h
> > @@ -87,4 +87,11 @@ static inline unsigned long efi_get_max_initrd_addr(unsigned long dram_base,
> >         return dram_base + SZ_512M;
> >  }
> >
> > +struct efi_arm_entry_state {
> > +       u32     cpsr_before_ebs;
> > +       u32     sctlr_before_ebs;
> > +       u32     cpsr_after_ebs;
> > +       u32     sctlr_after_ebs;
> > +};
> > +
> >  #endif /* _ASM_ARM_EFI_H */
> > diff --git a/drivers/firmware/efi/arm-init.c b/drivers/firmware/efi/arm-init.c
> > index c697e70ca7e7..4d1c272a4f79 100644
> > --- a/drivers/firmware/efi/arm-init.c
> > +++ b/drivers/firmware/efi/arm-init.c
> > @@ -52,9 +52,11 @@ static phys_addr_t __init efi_to_phys(unsigned long addr)
> >  }
> >
> >  static __initdata unsigned long screen_info_table = EFI_INVALID_TABLE_ADDR;
> > +static __initdata unsigned long cpu_state_table = EFI_INVALID_TABLE_ADDR;
> >
> >  static const efi_config_table_type_t arch_tables[] __initconst = {
> >         {LINUX_EFI_ARM_SCREEN_INFO_TABLE_GUID, &screen_info_table},
> > +       {LINUX_EFI_ARM_CPU_STATE_TABLE_GUID, &cpu_state_table},
> >         {}
> >  };
> >
> > @@ -116,7 +118,8 @@ static int __init uefi_init(u64 efi_system_table)
> >                 goto out;
> >         }
> >         retval = efi_config_parse_tables(config_tables, systab->nr_tables,
> > -                                        arch_tables);
> > +                                        IS_ENABLED(CONFIG_ARM) ? arch_tables
> > +                                                               : NULL);

It this worthwhile?
If it turns out we have no need for an arch_table on arm64, should we
still include the static table, holding values only relevant for
32-bit, in arm64 builds at all?

> >
> >         early_memunmap(config_tables, table_size);
> >  out:
> > @@ -238,9 +241,37 @@ void __init efi_init(void)
> >
> >         init_screen_info();
> >
> > +#ifdef CONFIG_ARM
> >         /* ARM does not permit early mappings to persist across paging_init() */
> > -       if (IS_ENABLED(CONFIG_ARM))
> > -               efi_memmap_unmap();
> > +       efi_memmap_unmap();
> > +
> > +       if (cpu_state_table != EFI_INVALID_TABLE_ADDR) {
> > +               struct efi_arm_entry_state *state;
> > +               bool dump_state = true;
> > +
> > +               state = early_memremap_ro(cpu_state_table,
> > +                                         sizeof(struct efi_arm_entry_state));
> > +               if (state == NULL) {
> > +                       pr_warn("Unable to map CPU entry state table.\n");
> > +                       return;
> > +               }
> > +
> > +               if ((state->sctlr_before_ebs & 1) == 0)
> > +                       pr_warn(FW_BUG "EFI stub was entered with MMU and Dcache disabled, please fix your firmware!\n");
> > +               else if ((state->sctlr_after_ebs & 1) == 0)
> > +                       pr_warn(FW_BUG "ExitBootServices() returned with MMU and Dcache disabled, please fix your firmware!\n");
> > +               else
> > +                       dump_state = false;
> > +
> > +               if (dump_state || efi_enabled(EFI_DBG)) {
> > +                       pr_info("CPSR at EFI stub entry        : 0x%08x\n", state->cpsr_before_ebs);
> > +                       pr_info("SCTLR at EFI stub entry       : 0x%08x\n", state->sctlr_before_ebs);
> > +                       pr_info("CPSR after ExitBootServices() : 0x%08x\n", state->cpsr_after_ebs);
> > +                       pr_info("SCTLR after ExitBootServices(): 0x%08x\n", state->sctlr_after_ebs);
> > +               }
> > +               early_memunmap(state, sizeof(struct efi_arm_entry_state));
> > +       }
> > +#endif
> >  }
> >
> >  static bool efifb_overlaps_pci_range(const struct of_pci_range *range)
> > diff --git a/drivers/firmware/efi/libstub/arm32-stub.c b/drivers/firmware/efi/libstub/arm32-stub.c
> > index 40243f524556..60783228db6e 100644
> > --- a/drivers/firmware/efi/libstub/arm32-stub.c
> > +++ b/drivers/firmware/efi/libstub/arm32-stub.c
> > @@ -7,10 +7,49 @@
> >
> >  #include "efistub.h"
> >
> > +static efi_guid_t cpu_state_guid = LINUX_EFI_ARM_CPU_STATE_TABLE_GUID;
> > +
> > +struct efi_arm_entry_state *efi_entry_state;
> > +
> > +static void get_cpu_state(u32 *cpsr, u32 *sctlr)
> > +{
> > +       asm("mrs %0, cpsr" : "=r"(*cpsr));
> > +       if ((*cpsr & MODE_MASK) == HYP_MODE)
> > +               asm("mrc p15, 4, %0, c1, c0, 0" : "=r"(*sctlr));
> > +       else
> > +               asm("mrc p15, 0, %0, c1, c0, 0" : "=r"(*sctlr));
> > +}
> > +
> >  efi_status_t check_platform_features(void)
> >  {
> > +       efi_status_t status;
> > +       u32 cpsr, sctlr;
> >         int block;
> >
> > +       get_cpu_state(&cpsr, &sctlr);
> > +
> > +       efi_info("Entering in %s mode with MMU %sabled\n",
> > +                ((cpsr & MODE_MASK) == HYP_MODE) ? "HYP" : "SVC",
> > +                (sctlr & 1) ? "en" : "dis");
> > +
> > +       status = efi_bs_call(allocate_pool, EFI_LOADER_DATA,
> > +                            sizeof(*efi_entry_state),
> > +                            (void **)&efi_entry_state);
> > +       if (status != EFI_SUCCESS) {
> > +               efi_err("allocate_pool() failed\n");
> > +               return status;
> > +       }
> > +
> > +       efi_entry_state->cpsr_before_ebs = cpsr;
> > +       efi_entry_state->sctlr_before_ebs = sctlr;
> > +
> > +       status = efi_bs_call(install_configuration_table, &cpu_state_guid,
> > +                            efi_entry_state);
> > +       if (status != EFI_SUCCESS) {
> > +               efi_err("install_configuration_table() failed\n");
> > +               goto free_state;
> > +       }
> > +
> >         /* non-LPAE kernels can run anywhere */
> >         if (!IS_ENABLED(CONFIG_ARM_LPAE))
> >                 return EFI_SUCCESS;
> > @@ -19,9 +58,20 @@ efi_status_t check_platform_features(void)
> >         block = cpuid_feature_extract(CPUID_EXT_MMFR0, 0);
> >         if (block < 5) {
> >                 efi_err("This LPAE kernel is not supported by your CPU\n");
> > -               return EFI_UNSUPPORTED;
> > +               status = EFI_UNSUPPORTED;
> > +               goto free_state;
> >         }
> >         return EFI_SUCCESS;
> > +
> > +free_state:
> > +       efi_bs_call(free_pool, efi_entry_state);
> > +       return status;
> > +}
> > +
> > +void efi_handle_post_ebs_state(void)
> > +{
> > +       get_cpu_state(&efi_entry_state->cpsr_after_ebs,
> > +                     &efi_entry_state->sctlr_after_ebs);
> >  }
> >
> >  static efi_guid_t screen_info_guid = LINUX_EFI_ARM_SCREEN_INFO_TABLE_GUID;
> > diff --git a/drivers/firmware/efi/libstub/efi-stub.c b/drivers/firmware/efi/libstub/efi-stub.c
> > index e97370bdfdb0..3318ec3f8e5b 100644
> > --- a/drivers/firmware/efi/libstub/efi-stub.c
> > +++ b/drivers/firmware/efi/libstub/efi-stub.c
> > @@ -329,6 +329,9 @@ efi_status_t __efiapi efi_pe_entry(efi_handle_t handle,
> >         if (status != EFI_SUCCESS)
> >                 goto fail_free_initrd;
> >
> > +       if (IS_ENABLED(CONFIG_ARM))
> > +               efi_handle_post_ebs_state();
> > +
> >         efi_enter_kernel(image_addr, fdt_addr, fdt_totalsize((void *)fdt_addr));
> >         /* not reached */
> >
> > diff --git a/drivers/firmware/efi/libstub/efistub.h b/drivers/firmware/efi/libstub/efistub.h
> > index bcd8c0a785f0..6217ce647ebf 100644
> > --- a/drivers/firmware/efi/libstub/efistub.h
> > +++ b/drivers/firmware/efi/libstub/efistub.h
> > @@ -771,4 +771,6 @@ efi_status_t efi_load_initrd(efi_loaded_image_t *image,
> >                              unsigned long soft_limit,
> >                              unsigned long hard_limit);
> >
> > +void efi_handle_post_ebs_state(void);
> > +

This declaration is made for both arm/arm64.
(I said it was tiny, feel free to ignore.)

/
    Leif

> >  #endif
> > diff --git a/include/linux/efi.h b/include/linux/efi.h
> > index c3449c9699d0..bb35f3305e55 100644
> > --- a/include/linux/efi.h
> > +++ b/include/linux/efi.h
> > @@ -350,6 +350,7 @@ void efi_native_runtime_setup(void);
> >   * associated with ConOut
> >   */
> >  #define LINUX_EFI_ARM_SCREEN_INFO_TABLE_GUID   EFI_GUID(0xe03fc20a, 0x85dc, 0x406e,  0xb9, 0x0e, 0x4a, 0xb5, 0x02, 0x37, 0x1d, 0x95)
> > +#define LINUX_EFI_ARM_CPU_STATE_TABLE_GUID     EFI_GUID(0xef79e4aa, 0x3c3d, 0x4989,  0xb9, 0x02, 0x07, 0xa9, 0x43, 0xe5, 0x50, 0xd2)
> >  #define LINUX_EFI_LOADER_ENTRY_GUID            EFI_GUID(0x4a67b082, 0x0a4c, 0x41cf,  0xb6, 0xc7, 0x44, 0x0b, 0x29, 0xbb, 0x8c, 0x4f)
> >  #define LINUX_EFI_RANDOM_SEED_TABLE_GUID       EFI_GUID(0x1ce1e5bc, 0x7ceb, 0x42f2,  0x81, 0xe5, 0x8a, 0xad, 0xf1, 0x80, 0xf5, 0x7b)
> >  #define LINUX_EFI_TPM_EVENT_LOG_GUID           EFI_GUID(0xb7799cb0, 0xeca2, 0x4943,  0x96, 0x67, 0x1f, 0xae, 0x07, 0xb7, 0x47, 0xfa)
> > --
> > 2.27.0
> >

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v3] efi/libstub: arm: Print CPU boot mode and MMU state at boot
  2020-06-16 10:39     ` Leif Lindholm
@ 2020-06-16 10:46       ` Ard Biesheuvel
  -1 siblings, 0 replies; 10+ messages in thread
From: Ard Biesheuvel @ 2020-06-16 10:46 UTC (permalink / raw)
  To: Leif Lindholm
  Cc: linux-efi, Linux ARM, Samer.El-Haj-Mahmoud, Heinrich Schuchardt,
	Grant Likely

On Tue, 16 Jun 2020 at 12:39, Leif Lindholm <leif@nuviainc.com> wrote:
>
> On Tue, Jun 16, 2020 at 10:51:48 +0200, Ard Biesheuvel wrote:
> > On Tue, 16 Jun 2020 at 10:50, Ard Biesheuvel <ardb@kernel.org> wrote:
> > >
> > > On 32-bit ARM, we may boot at HYP mode, or with the MMU and caches off
> > > (or both), even though the EFI spec does not actually support this.
> > > While booting at HYP mode is something we might tolerate, fiddling
> > > with the caches is a more serious issue, as disabling the caches is
> > > tricky to do safely from C code, and running without the Dcache makes
> > > it impossible to support unaligned memory accesses, which is another
> > > explicit requirement imposed by the EFI spec.
> > >
> > > So take note of the CPU mode and MMU state in the EFI stub diagnostic
> > > output so that we can easily diagnose any issues that may arise from
> > > this. E.g.,
> > >
> > >   EFI stub: Entering in SVC mode with MMU enabled
> > >
> > > Also, capture the CPSR and SCTLR system register values at EFI stub
> > > entry, and after ExitBootServices() returns, and check whether the
> > > MMU and Dcache were disabled at any point. If this is the case, a
> > > diagnostic message like the following will be emitted:
> > >
> > >   efi: [Firmware Bug]: EFI stub was entered with MMU and Dcache disabled, please fix your firmware!
> > >   efi: CPSR at EFI stub entry        : 0x600001d3
> > >   efi: SCTLR at EFI stub entry       : 0x00c51838
> > >   efi: CPSR after ExitBootServices() : 0x600001d3
> > >   efi: SCTLR after ExitBootServices(): 0x00c50838
> > >
> > > Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
> > > ---
> > > Adding Grant and Samer to cc, as this may be something that should be taken
> > > into account in the EBBR context? Note that this affects 64-bit only.
> > >
> >
> > Affects *32* bit only
>
> One question and a teeny tiny nit below.
>
> > >  arch/arm/include/asm/efi.h                |  7 +++
> > >  drivers/firmware/efi/arm-init.c           | 37 ++++++++++++--
> > >  drivers/firmware/efi/libstub/arm32-stub.c | 52 +++++++++++++++++++-
> > >  drivers/firmware/efi/libstub/efi-stub.c   |  3 ++
> > >  drivers/firmware/efi/libstub/efistub.h    |  2 +
> > >  include/linux/efi.h                       |  1 +
> > >  6 files changed, 98 insertions(+), 4 deletions(-)
> > >
> > > diff --git a/arch/arm/include/asm/efi.h b/arch/arm/include/asm/efi.h
> > > index 84dc0ba822f5..5dcf3c6011b7 100644
> > > --- a/arch/arm/include/asm/efi.h
> > > +++ b/arch/arm/include/asm/efi.h
> > > @@ -87,4 +87,11 @@ static inline unsigned long efi_get_max_initrd_addr(unsigned long dram_base,
> > >         return dram_base + SZ_512M;
> > >  }
> > >
> > > +struct efi_arm_entry_state {
> > > +       u32     cpsr_before_ebs;
> > > +       u32     sctlr_before_ebs;
> > > +       u32     cpsr_after_ebs;
> > > +       u32     sctlr_after_ebs;
> > > +};
> > > +
> > >  #endif /* _ASM_ARM_EFI_H */
> > > diff --git a/drivers/firmware/efi/arm-init.c b/drivers/firmware/efi/arm-init.c
> > > index c697e70ca7e7..4d1c272a4f79 100644
> > > --- a/drivers/firmware/efi/arm-init.c
> > > +++ b/drivers/firmware/efi/arm-init.c
> > > @@ -52,9 +52,11 @@ static phys_addr_t __init efi_to_phys(unsigned long addr)
> > >  }
> > >
> > >  static __initdata unsigned long screen_info_table = EFI_INVALID_TABLE_ADDR;
> > > +static __initdata unsigned long cpu_state_table = EFI_INVALID_TABLE_ADDR;
> > >
> > >  static const efi_config_table_type_t arch_tables[] __initconst = {
> > >         {LINUX_EFI_ARM_SCREEN_INFO_TABLE_GUID, &screen_info_table},
> > > +       {LINUX_EFI_ARM_CPU_STATE_TABLE_GUID, &cpu_state_table},
> > >         {}
> > >  };
> > >
> > > @@ -116,7 +118,8 @@ static int __init uefi_init(u64 efi_system_table)
> > >                 goto out;
> > >         }
> > >         retval = efi_config_parse_tables(config_tables, systab->nr_tables,
> > > -                                        arch_tables);
> > > +                                        IS_ENABLED(CONFIG_ARM) ? arch_tables
> > > +                                                               : NULL);
>
> It this worthwhile?
> If it turns out we have no need for an arch_table on arm64, should we
> still include the static table, holding values only relevant for
> 32-bit, in arm64 builds at all?
>

On arm64, the stub and the kernel proper are the same binary, so there
should never be a need to use configuration tables to carry
information from the stub to the kernel proper.

As this is the only reference, the compiler will simply drop the table
from the build, and it could even optimize away the '!=
EFI_INVALID_TABLE_ADDR' checks and conditional code, as it can observe
that those variables never change values.

However, it appears that the latter does not actually occur in
practice, so I will rewrite the test below:

> > >
> > >         early_memunmap(config_tables, table_size);
> > >  out:
> > > @@ -238,9 +241,37 @@ void __init efi_init(void)
> > >
> > >         init_screen_info();
> > >
> > > +#ifdef CONFIG_ARM
> > >         /* ARM does not permit early mappings to persist across paging_init() */
> > > -       if (IS_ENABLED(CONFIG_ARM))
> > > -               efi_memmap_unmap();
> > > +       efi_memmap_unmap();
> > > +
> > > +       if (cpu_state_table != EFI_INVALID_TABLE_ADDR) {

if (IS_ENABLED(CONFIG_ARM)) && cpu_state_table != EFI_INVALID_TABLE_ADDR

and do the same for screen_info_table (but in a separate patch)

> > > +               struct efi_arm_entry_state *state;
> > > +               bool dump_state = true;
> > > +
> > > +               state = early_memremap_ro(cpu_state_table,
> > > +                                         sizeof(struct efi_arm_entry_state));
> > > +               if (state == NULL) {
> > > +                       pr_warn("Unable to map CPU entry state table.\n");
> > > +                       return;
> > > +               }
> > > +
> > > +               if ((state->sctlr_before_ebs & 1) == 0)
> > > +                       pr_warn(FW_BUG "EFI stub was entered with MMU and Dcache disabled, please fix your firmware!\n");
> > > +               else if ((state->sctlr_after_ebs & 1) == 0)
> > > +                       pr_warn(FW_BUG "ExitBootServices() returned with MMU and Dcache disabled, please fix your firmware!\n");
> > > +               else
> > > +                       dump_state = false;
> > > +
> > > +               if (dump_state || efi_enabled(EFI_DBG)) {
> > > +                       pr_info("CPSR at EFI stub entry        : 0x%08x\n", state->cpsr_before_ebs);
> > > +                       pr_info("SCTLR at EFI stub entry       : 0x%08x\n", state->sctlr_before_ebs);
> > > +                       pr_info("CPSR after ExitBootServices() : 0x%08x\n", state->cpsr_after_ebs);
> > > +                       pr_info("SCTLR after ExitBootServices(): 0x%08x\n", state->sctlr_after_ebs);
> > > +               }
> > > +               early_memunmap(state, sizeof(struct efi_arm_entry_state));
> > > +       }
> > > +#endif
> > >  }
> > >
> > >  static bool efifb_overlaps_pci_range(const struct of_pci_range *range)
> > > diff --git a/drivers/firmware/efi/libstub/arm32-stub.c b/drivers/firmware/efi/libstub/arm32-stub.c
> > > index 40243f524556..60783228db6e 100644
> > > --- a/drivers/firmware/efi/libstub/arm32-stub.c
> > > +++ b/drivers/firmware/efi/libstub/arm32-stub.c
> > > @@ -7,10 +7,49 @@
> > >
> > >  #include "efistub.h"
> > >
> > > +static efi_guid_t cpu_state_guid = LINUX_EFI_ARM_CPU_STATE_TABLE_GUID;
> > > +
> > > +struct efi_arm_entry_state *efi_entry_state;
> > > +
> > > +static void get_cpu_state(u32 *cpsr, u32 *sctlr)
> > > +{
> > > +       asm("mrs %0, cpsr" : "=r"(*cpsr));
> > > +       if ((*cpsr & MODE_MASK) == HYP_MODE)
> > > +               asm("mrc p15, 4, %0, c1, c0, 0" : "=r"(*sctlr));
> > > +       else
> > > +               asm("mrc p15, 0, %0, c1, c0, 0" : "=r"(*sctlr));
> > > +}
> > > +
> > >  efi_status_t check_platform_features(void)
> > >  {
> > > +       efi_status_t status;
> > > +       u32 cpsr, sctlr;
> > >         int block;
> > >
> > > +       get_cpu_state(&cpsr, &sctlr);
> > > +
> > > +       efi_info("Entering in %s mode with MMU %sabled\n",
> > > +                ((cpsr & MODE_MASK) == HYP_MODE) ? "HYP" : "SVC",
> > > +                (sctlr & 1) ? "en" : "dis");
> > > +
> > > +       status = efi_bs_call(allocate_pool, EFI_LOADER_DATA,
> > > +                            sizeof(*efi_entry_state),
> > > +                            (void **)&efi_entry_state);
> > > +       if (status != EFI_SUCCESS) {
> > > +               efi_err("allocate_pool() failed\n");
> > > +               return status;
> > > +       }
> > > +
> > > +       efi_entry_state->cpsr_before_ebs = cpsr;
> > > +       efi_entry_state->sctlr_before_ebs = sctlr;
> > > +
> > > +       status = efi_bs_call(install_configuration_table, &cpu_state_guid,
> > > +                            efi_entry_state);
> > > +       if (status != EFI_SUCCESS) {
> > > +               efi_err("install_configuration_table() failed\n");
> > > +               goto free_state;
> > > +       }
> > > +
> > >         /* non-LPAE kernels can run anywhere */
> > >         if (!IS_ENABLED(CONFIG_ARM_LPAE))
> > >                 return EFI_SUCCESS;
> > > @@ -19,9 +58,20 @@ efi_status_t check_platform_features(void)
> > >         block = cpuid_feature_extract(CPUID_EXT_MMFR0, 0);
> > >         if (block < 5) {
> > >                 efi_err("This LPAE kernel is not supported by your CPU\n");
> > > -               return EFI_UNSUPPORTED;
> > > +               status = EFI_UNSUPPORTED;
> > > +               goto free_state;
> > >         }
> > >         return EFI_SUCCESS;
> > > +
> > > +free_state:
> > > +       efi_bs_call(free_pool, efi_entry_state);
> > > +       return status;
> > > +}
> > > +
> > > +void efi_handle_post_ebs_state(void)
> > > +{
> > > +       get_cpu_state(&efi_entry_state->cpsr_after_ebs,
> > > +                     &efi_entry_state->sctlr_after_ebs);
> > >  }
> > >
> > >  static efi_guid_t screen_info_guid = LINUX_EFI_ARM_SCREEN_INFO_TABLE_GUID;
> > > diff --git a/drivers/firmware/efi/libstub/efi-stub.c b/drivers/firmware/efi/libstub/efi-stub.c
> > > index e97370bdfdb0..3318ec3f8e5b 100644
> > > --- a/drivers/firmware/efi/libstub/efi-stub.c
> > > +++ b/drivers/firmware/efi/libstub/efi-stub.c
> > > @@ -329,6 +329,9 @@ efi_status_t __efiapi efi_pe_entry(efi_handle_t handle,
> > >         if (status != EFI_SUCCESS)
> > >                 goto fail_free_initrd;
> > >
> > > +       if (IS_ENABLED(CONFIG_ARM))
> > > +               efi_handle_post_ebs_state();
> > > +
> > >         efi_enter_kernel(image_addr, fdt_addr, fdt_totalsize((void *)fdt_addr));
> > >         /* not reached */
> > >
> > > diff --git a/drivers/firmware/efi/libstub/efistub.h b/drivers/firmware/efi/libstub/efistub.h
> > > index bcd8c0a785f0..6217ce647ebf 100644
> > > --- a/drivers/firmware/efi/libstub/efistub.h
> > > +++ b/drivers/firmware/efi/libstub/efistub.h
> > > @@ -771,4 +771,6 @@ efi_status_t efi_load_initrd(efi_loaded_image_t *image,
> > >                              unsigned long soft_limit,
> > >                              unsigned long hard_limit);
> > >
> > > +void efi_handle_post_ebs_state(void);
> > > +
>
> This declaration is made for both arm/arm64.
> (I said it was tiny, feel free to ignore.)
>

Yes, that was deliberate. Forward declarations don't take up any
space, and so there is no need for an #ifdef here.


> > >  #endif
> > > diff --git a/include/linux/efi.h b/include/linux/efi.h
> > > index c3449c9699d0..bb35f3305e55 100644
> > > --- a/include/linux/efi.h
> > > +++ b/include/linux/efi.h
> > > @@ -350,6 +350,7 @@ void efi_native_runtime_setup(void);
> > >   * associated with ConOut
> > >   */
> > >  #define LINUX_EFI_ARM_SCREEN_INFO_TABLE_GUID   EFI_GUID(0xe03fc20a, 0x85dc, 0x406e,  0xb9, 0x0e, 0x4a, 0xb5, 0x02, 0x37, 0x1d, 0x95)
> > > +#define LINUX_EFI_ARM_CPU_STATE_TABLE_GUID     EFI_GUID(0xef79e4aa, 0x3c3d, 0x4989,  0xb9, 0x02, 0x07, 0xa9, 0x43, 0xe5, 0x50, 0xd2)
> > >  #define LINUX_EFI_LOADER_ENTRY_GUID            EFI_GUID(0x4a67b082, 0x0a4c, 0x41cf,  0xb6, 0xc7, 0x44, 0x0b, 0x29, 0xbb, 0x8c, 0x4f)
> > >  #define LINUX_EFI_RANDOM_SEED_TABLE_GUID       EFI_GUID(0x1ce1e5bc, 0x7ceb, 0x42f2,  0x81, 0xe5, 0x8a, 0xad, 0xf1, 0x80, 0xf5, 0x7b)
> > >  #define LINUX_EFI_TPM_EVENT_LOG_GUID           EFI_GUID(0xb7799cb0, 0xeca2, 0x4943,  0x96, 0x67, 0x1f, 0xae, 0x07, 0xb7, 0x47, 0xfa)
> > > --
> > > 2.27.0
> > >

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v3] efi/libstub: arm: Print CPU boot mode and MMU state at boot
@ 2020-06-16 10:46       ` Ard Biesheuvel
  0 siblings, 0 replies; 10+ messages in thread
From: Ard Biesheuvel @ 2020-06-16 10:46 UTC (permalink / raw)
  To: Leif Lindholm
  Cc: Heinrich Schuchardt, Samer.El-Haj-Mahmoud, linux-efi,
	Grant Likely, Linux ARM

On Tue, 16 Jun 2020 at 12:39, Leif Lindholm <leif@nuviainc.com> wrote:
>
> On Tue, Jun 16, 2020 at 10:51:48 +0200, Ard Biesheuvel wrote:
> > On Tue, 16 Jun 2020 at 10:50, Ard Biesheuvel <ardb@kernel.org> wrote:
> > >
> > > On 32-bit ARM, we may boot at HYP mode, or with the MMU and caches off
> > > (or both), even though the EFI spec does not actually support this.
> > > While booting at HYP mode is something we might tolerate, fiddling
> > > with the caches is a more serious issue, as disabling the caches is
> > > tricky to do safely from C code, and running without the Dcache makes
> > > it impossible to support unaligned memory accesses, which is another
> > > explicit requirement imposed by the EFI spec.
> > >
> > > So take note of the CPU mode and MMU state in the EFI stub diagnostic
> > > output so that we can easily diagnose any issues that may arise from
> > > this. E.g.,
> > >
> > >   EFI stub: Entering in SVC mode with MMU enabled
> > >
> > > Also, capture the CPSR and SCTLR system register values at EFI stub
> > > entry, and after ExitBootServices() returns, and check whether the
> > > MMU and Dcache were disabled at any point. If this is the case, a
> > > diagnostic message like the following will be emitted:
> > >
> > >   efi: [Firmware Bug]: EFI stub was entered with MMU and Dcache disabled, please fix your firmware!
> > >   efi: CPSR at EFI stub entry        : 0x600001d3
> > >   efi: SCTLR at EFI stub entry       : 0x00c51838
> > >   efi: CPSR after ExitBootServices() : 0x600001d3
> > >   efi: SCTLR after ExitBootServices(): 0x00c50838
> > >
> > > Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
> > > ---
> > > Adding Grant and Samer to cc, as this may be something that should be taken
> > > into account in the EBBR context? Note that this affects 64-bit only.
> > >
> >
> > Affects *32* bit only
>
> One question and a teeny tiny nit below.
>
> > >  arch/arm/include/asm/efi.h                |  7 +++
> > >  drivers/firmware/efi/arm-init.c           | 37 ++++++++++++--
> > >  drivers/firmware/efi/libstub/arm32-stub.c | 52 +++++++++++++++++++-
> > >  drivers/firmware/efi/libstub/efi-stub.c   |  3 ++
> > >  drivers/firmware/efi/libstub/efistub.h    |  2 +
> > >  include/linux/efi.h                       |  1 +
> > >  6 files changed, 98 insertions(+), 4 deletions(-)
> > >
> > > diff --git a/arch/arm/include/asm/efi.h b/arch/arm/include/asm/efi.h
> > > index 84dc0ba822f5..5dcf3c6011b7 100644
> > > --- a/arch/arm/include/asm/efi.h
> > > +++ b/arch/arm/include/asm/efi.h
> > > @@ -87,4 +87,11 @@ static inline unsigned long efi_get_max_initrd_addr(unsigned long dram_base,
> > >         return dram_base + SZ_512M;
> > >  }
> > >
> > > +struct efi_arm_entry_state {
> > > +       u32     cpsr_before_ebs;
> > > +       u32     sctlr_before_ebs;
> > > +       u32     cpsr_after_ebs;
> > > +       u32     sctlr_after_ebs;
> > > +};
> > > +
> > >  #endif /* _ASM_ARM_EFI_H */
> > > diff --git a/drivers/firmware/efi/arm-init.c b/drivers/firmware/efi/arm-init.c
> > > index c697e70ca7e7..4d1c272a4f79 100644
> > > --- a/drivers/firmware/efi/arm-init.c
> > > +++ b/drivers/firmware/efi/arm-init.c
> > > @@ -52,9 +52,11 @@ static phys_addr_t __init efi_to_phys(unsigned long addr)
> > >  }
> > >
> > >  static __initdata unsigned long screen_info_table = EFI_INVALID_TABLE_ADDR;
> > > +static __initdata unsigned long cpu_state_table = EFI_INVALID_TABLE_ADDR;
> > >
> > >  static const efi_config_table_type_t arch_tables[] __initconst = {
> > >         {LINUX_EFI_ARM_SCREEN_INFO_TABLE_GUID, &screen_info_table},
> > > +       {LINUX_EFI_ARM_CPU_STATE_TABLE_GUID, &cpu_state_table},
> > >         {}
> > >  };
> > >
> > > @@ -116,7 +118,8 @@ static int __init uefi_init(u64 efi_system_table)
> > >                 goto out;
> > >         }
> > >         retval = efi_config_parse_tables(config_tables, systab->nr_tables,
> > > -                                        arch_tables);
> > > +                                        IS_ENABLED(CONFIG_ARM) ? arch_tables
> > > +                                                               : NULL);
>
> It this worthwhile?
> If it turns out we have no need for an arch_table on arm64, should we
> still include the static table, holding values only relevant for
> 32-bit, in arm64 builds at all?
>

On arm64, the stub and the kernel proper are the same binary, so there
should never be a need to use configuration tables to carry
information from the stub to the kernel proper.

As this is the only reference, the compiler will simply drop the table
from the build, and it could even optimize away the '!=
EFI_INVALID_TABLE_ADDR' checks and conditional code, as it can observe
that those variables never change values.

However, it appears that the latter does not actually occur in
practice, so I will rewrite the test below:

> > >
> > >         early_memunmap(config_tables, table_size);
> > >  out:
> > > @@ -238,9 +241,37 @@ void __init efi_init(void)
> > >
> > >         init_screen_info();
> > >
> > > +#ifdef CONFIG_ARM
> > >         /* ARM does not permit early mappings to persist across paging_init() */
> > > -       if (IS_ENABLED(CONFIG_ARM))
> > > -               efi_memmap_unmap();
> > > +       efi_memmap_unmap();
> > > +
> > > +       if (cpu_state_table != EFI_INVALID_TABLE_ADDR) {

if (IS_ENABLED(CONFIG_ARM)) && cpu_state_table != EFI_INVALID_TABLE_ADDR

and do the same for screen_info_table (but in a separate patch)

> > > +               struct efi_arm_entry_state *state;
> > > +               bool dump_state = true;
> > > +
> > > +               state = early_memremap_ro(cpu_state_table,
> > > +                                         sizeof(struct efi_arm_entry_state));
> > > +               if (state == NULL) {
> > > +                       pr_warn("Unable to map CPU entry state table.\n");
> > > +                       return;
> > > +               }
> > > +
> > > +               if ((state->sctlr_before_ebs & 1) == 0)
> > > +                       pr_warn(FW_BUG "EFI stub was entered with MMU and Dcache disabled, please fix your firmware!\n");
> > > +               else if ((state->sctlr_after_ebs & 1) == 0)
> > > +                       pr_warn(FW_BUG "ExitBootServices() returned with MMU and Dcache disabled, please fix your firmware!\n");
> > > +               else
> > > +                       dump_state = false;
> > > +
> > > +               if (dump_state || efi_enabled(EFI_DBG)) {
> > > +                       pr_info("CPSR at EFI stub entry        : 0x%08x\n", state->cpsr_before_ebs);
> > > +                       pr_info("SCTLR at EFI stub entry       : 0x%08x\n", state->sctlr_before_ebs);
> > > +                       pr_info("CPSR after ExitBootServices() : 0x%08x\n", state->cpsr_after_ebs);
> > > +                       pr_info("SCTLR after ExitBootServices(): 0x%08x\n", state->sctlr_after_ebs);
> > > +               }
> > > +               early_memunmap(state, sizeof(struct efi_arm_entry_state));
> > > +       }
> > > +#endif
> > >  }
> > >
> > >  static bool efifb_overlaps_pci_range(const struct of_pci_range *range)
> > > diff --git a/drivers/firmware/efi/libstub/arm32-stub.c b/drivers/firmware/efi/libstub/arm32-stub.c
> > > index 40243f524556..60783228db6e 100644
> > > --- a/drivers/firmware/efi/libstub/arm32-stub.c
> > > +++ b/drivers/firmware/efi/libstub/arm32-stub.c
> > > @@ -7,10 +7,49 @@
> > >
> > >  #include "efistub.h"
> > >
> > > +static efi_guid_t cpu_state_guid = LINUX_EFI_ARM_CPU_STATE_TABLE_GUID;
> > > +
> > > +struct efi_arm_entry_state *efi_entry_state;
> > > +
> > > +static void get_cpu_state(u32 *cpsr, u32 *sctlr)
> > > +{
> > > +       asm("mrs %0, cpsr" : "=r"(*cpsr));
> > > +       if ((*cpsr & MODE_MASK) == HYP_MODE)
> > > +               asm("mrc p15, 4, %0, c1, c0, 0" : "=r"(*sctlr));
> > > +       else
> > > +               asm("mrc p15, 0, %0, c1, c0, 0" : "=r"(*sctlr));
> > > +}
> > > +
> > >  efi_status_t check_platform_features(void)
> > >  {
> > > +       efi_status_t status;
> > > +       u32 cpsr, sctlr;
> > >         int block;
> > >
> > > +       get_cpu_state(&cpsr, &sctlr);
> > > +
> > > +       efi_info("Entering in %s mode with MMU %sabled\n",
> > > +                ((cpsr & MODE_MASK) == HYP_MODE) ? "HYP" : "SVC",
> > > +                (sctlr & 1) ? "en" : "dis");
> > > +
> > > +       status = efi_bs_call(allocate_pool, EFI_LOADER_DATA,
> > > +                            sizeof(*efi_entry_state),
> > > +                            (void **)&efi_entry_state);
> > > +       if (status != EFI_SUCCESS) {
> > > +               efi_err("allocate_pool() failed\n");
> > > +               return status;
> > > +       }
> > > +
> > > +       efi_entry_state->cpsr_before_ebs = cpsr;
> > > +       efi_entry_state->sctlr_before_ebs = sctlr;
> > > +
> > > +       status = efi_bs_call(install_configuration_table, &cpu_state_guid,
> > > +                            efi_entry_state);
> > > +       if (status != EFI_SUCCESS) {
> > > +               efi_err("install_configuration_table() failed\n");
> > > +               goto free_state;
> > > +       }
> > > +
> > >         /* non-LPAE kernels can run anywhere */
> > >         if (!IS_ENABLED(CONFIG_ARM_LPAE))
> > >                 return EFI_SUCCESS;
> > > @@ -19,9 +58,20 @@ efi_status_t check_platform_features(void)
> > >         block = cpuid_feature_extract(CPUID_EXT_MMFR0, 0);
> > >         if (block < 5) {
> > >                 efi_err("This LPAE kernel is not supported by your CPU\n");
> > > -               return EFI_UNSUPPORTED;
> > > +               status = EFI_UNSUPPORTED;
> > > +               goto free_state;
> > >         }
> > >         return EFI_SUCCESS;
> > > +
> > > +free_state:
> > > +       efi_bs_call(free_pool, efi_entry_state);
> > > +       return status;
> > > +}
> > > +
> > > +void efi_handle_post_ebs_state(void)
> > > +{
> > > +       get_cpu_state(&efi_entry_state->cpsr_after_ebs,
> > > +                     &efi_entry_state->sctlr_after_ebs);
> > >  }
> > >
> > >  static efi_guid_t screen_info_guid = LINUX_EFI_ARM_SCREEN_INFO_TABLE_GUID;
> > > diff --git a/drivers/firmware/efi/libstub/efi-stub.c b/drivers/firmware/efi/libstub/efi-stub.c
> > > index e97370bdfdb0..3318ec3f8e5b 100644
> > > --- a/drivers/firmware/efi/libstub/efi-stub.c
> > > +++ b/drivers/firmware/efi/libstub/efi-stub.c
> > > @@ -329,6 +329,9 @@ efi_status_t __efiapi efi_pe_entry(efi_handle_t handle,
> > >         if (status != EFI_SUCCESS)
> > >                 goto fail_free_initrd;
> > >
> > > +       if (IS_ENABLED(CONFIG_ARM))
> > > +               efi_handle_post_ebs_state();
> > > +
> > >         efi_enter_kernel(image_addr, fdt_addr, fdt_totalsize((void *)fdt_addr));
> > >         /* not reached */
> > >
> > > diff --git a/drivers/firmware/efi/libstub/efistub.h b/drivers/firmware/efi/libstub/efistub.h
> > > index bcd8c0a785f0..6217ce647ebf 100644
> > > --- a/drivers/firmware/efi/libstub/efistub.h
> > > +++ b/drivers/firmware/efi/libstub/efistub.h
> > > @@ -771,4 +771,6 @@ efi_status_t efi_load_initrd(efi_loaded_image_t *image,
> > >                              unsigned long soft_limit,
> > >                              unsigned long hard_limit);
> > >
> > > +void efi_handle_post_ebs_state(void);
> > > +
>
> This declaration is made for both arm/arm64.
> (I said it was tiny, feel free to ignore.)
>

Yes, that was deliberate. Forward declarations don't take up any
space, and so there is no need for an #ifdef here.


> > >  #endif
> > > diff --git a/include/linux/efi.h b/include/linux/efi.h
> > > index c3449c9699d0..bb35f3305e55 100644
> > > --- a/include/linux/efi.h
> > > +++ b/include/linux/efi.h
> > > @@ -350,6 +350,7 @@ void efi_native_runtime_setup(void);
> > >   * associated with ConOut
> > >   */
> > >  #define LINUX_EFI_ARM_SCREEN_INFO_TABLE_GUID   EFI_GUID(0xe03fc20a, 0x85dc, 0x406e,  0xb9, 0x0e, 0x4a, 0xb5, 0x02, 0x37, 0x1d, 0x95)
> > > +#define LINUX_EFI_ARM_CPU_STATE_TABLE_GUID     EFI_GUID(0xef79e4aa, 0x3c3d, 0x4989,  0xb9, 0x02, 0x07, 0xa9, 0x43, 0xe5, 0x50, 0xd2)
> > >  #define LINUX_EFI_LOADER_ENTRY_GUID            EFI_GUID(0x4a67b082, 0x0a4c, 0x41cf,  0xb6, 0xc7, 0x44, 0x0b, 0x29, 0xbb, 0x8c, 0x4f)
> > >  #define LINUX_EFI_RANDOM_SEED_TABLE_GUID       EFI_GUID(0x1ce1e5bc, 0x7ceb, 0x42f2,  0x81, 0xe5, 0x8a, 0xad, 0xf1, 0x80, 0xf5, 0x7b)
> > >  #define LINUX_EFI_TPM_EVENT_LOG_GUID           EFI_GUID(0xb7799cb0, 0xeca2, 0x4943,  0x96, 0x67, 0x1f, 0xae, 0x07, 0xb7, 0x47, 0xfa)
> > > --
> > > 2.27.0
> > >

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v3] efi/libstub: arm: Print CPU boot mode and MMU state at boot
  2020-06-16 10:46       ` Ard Biesheuvel
@ 2020-06-16 10:48         ` Ard Biesheuvel
  -1 siblings, 0 replies; 10+ messages in thread
From: Ard Biesheuvel @ 2020-06-16 10:48 UTC (permalink / raw)
  To: Leif Lindholm
  Cc: linux-efi, Linux ARM, Samer.El-Haj-Mahmoud, Heinrich Schuchardt,
	Grant Likely

On Tue, 16 Jun 2020 at 12:46, Ard Biesheuvel <ardb@kernel.org> wrote:
>
> On Tue, 16 Jun 2020 at 12:39, Leif Lindholm <leif@nuviainc.com> wrote:
> >
> > On Tue, Jun 16, 2020 at 10:51:48 +0200, Ard Biesheuvel wrote:
> > > On Tue, 16 Jun 2020 at 10:50, Ard Biesheuvel <ardb@kernel.org> wrote:
> > > >
> > > > On 32-bit ARM, we may boot at HYP mode, or with the MMU and caches off
> > > > (or both), even though the EFI spec does not actually support this.
> > > > While booting at HYP mode is something we might tolerate, fiddling
> > > > with the caches is a more serious issue, as disabling the caches is
> > > > tricky to do safely from C code, and running without the Dcache makes
> > > > it impossible to support unaligned memory accesses, which is another
> > > > explicit requirement imposed by the EFI spec.
> > > >
> > > > So take note of the CPU mode and MMU state in the EFI stub diagnostic
> > > > output so that we can easily diagnose any issues that may arise from
> > > > this. E.g.,
> > > >
> > > >   EFI stub: Entering in SVC mode with MMU enabled
> > > >
> > > > Also, capture the CPSR and SCTLR system register values at EFI stub
> > > > entry, and after ExitBootServices() returns, and check whether the
> > > > MMU and Dcache were disabled at any point. If this is the case, a
> > > > diagnostic message like the following will be emitted:
> > > >
> > > >   efi: [Firmware Bug]: EFI stub was entered with MMU and Dcache disabled, please fix your firmware!
> > > >   efi: CPSR at EFI stub entry        : 0x600001d3
> > > >   efi: SCTLR at EFI stub entry       : 0x00c51838
> > > >   efi: CPSR after ExitBootServices() : 0x600001d3
> > > >   efi: SCTLR after ExitBootServices(): 0x00c50838
> > > >
> > > > Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
> > > > ---
> > > > Adding Grant and Samer to cc, as this may be something that should be taken
> > > > into account in the EBBR context? Note that this affects 64-bit only.
> > > >
> > >
> > > Affects *32* bit only
> >
> > One question and a teeny tiny nit below.
> >
> > > >  arch/arm/include/asm/efi.h                |  7 +++
> > > >  drivers/firmware/efi/arm-init.c           | 37 ++++++++++++--
> > > >  drivers/firmware/efi/libstub/arm32-stub.c | 52 +++++++++++++++++++-
> > > >  drivers/firmware/efi/libstub/efi-stub.c   |  3 ++
> > > >  drivers/firmware/efi/libstub/efistub.h    |  2 +
> > > >  include/linux/efi.h                       |  1 +
> > > >  6 files changed, 98 insertions(+), 4 deletions(-)
> > > >
> > > > diff --git a/arch/arm/include/asm/efi.h b/arch/arm/include/asm/efi.h
> > > > index 84dc0ba822f5..5dcf3c6011b7 100644
> > > > --- a/arch/arm/include/asm/efi.h
> > > > +++ b/arch/arm/include/asm/efi.h
> > > > @@ -87,4 +87,11 @@ static inline unsigned long efi_get_max_initrd_addr(unsigned long dram_base,
> > > >         return dram_base + SZ_512M;
> > > >  }
> > > >
> > > > +struct efi_arm_entry_state {
> > > > +       u32     cpsr_before_ebs;
> > > > +       u32     sctlr_before_ebs;
> > > > +       u32     cpsr_after_ebs;
> > > > +       u32     sctlr_after_ebs;
> > > > +};
> > > > +
> > > >  #endif /* _ASM_ARM_EFI_H */
> > > > diff --git a/drivers/firmware/efi/arm-init.c b/drivers/firmware/efi/arm-init.c
> > > > index c697e70ca7e7..4d1c272a4f79 100644
> > > > --- a/drivers/firmware/efi/arm-init.c
> > > > +++ b/drivers/firmware/efi/arm-init.c
> > > > @@ -52,9 +52,11 @@ static phys_addr_t __init efi_to_phys(unsigned long addr)
> > > >  }
> > > >
> > > >  static __initdata unsigned long screen_info_table = EFI_INVALID_TABLE_ADDR;
> > > > +static __initdata unsigned long cpu_state_table = EFI_INVALID_TABLE_ADDR;
> > > >
> > > >  static const efi_config_table_type_t arch_tables[] __initconst = {
> > > >         {LINUX_EFI_ARM_SCREEN_INFO_TABLE_GUID, &screen_info_table},
> > > > +       {LINUX_EFI_ARM_CPU_STATE_TABLE_GUID, &cpu_state_table},
> > > >         {}
> > > >  };
> > > >
> > > > @@ -116,7 +118,8 @@ static int __init uefi_init(u64 efi_system_table)
> > > >                 goto out;
> > > >         }
> > > >         retval = efi_config_parse_tables(config_tables, systab->nr_tables,
> > > > -                                        arch_tables);
> > > > +                                        IS_ENABLED(CONFIG_ARM) ? arch_tables
> > > > +                                                               : NULL);
> >
> > It this worthwhile?
> > If it turns out we have no need for an arch_table on arm64, should we
> > still include the static table, holding values only relevant for
> > 32-bit, in arm64 builds at all?
> >
>
> On arm64, the stub and the kernel proper are the same binary, so there
> should never be a need to use configuration tables to carry
> information from the stub to the kernel proper.
>
> As this is the only reference, the compiler will simply drop the table
> from the build, and it could even optimize away the '!=
> EFI_INVALID_TABLE_ADDR' checks and conditional code, as it can observe
> that those variables never change values.
>
> However, it appears that the latter does not actually occur in
> practice, so I will rewrite the test below:
>
> > > >
> > > >         early_memunmap(config_tables, table_size);
> > > >  out:
> > > > @@ -238,9 +241,37 @@ void __init efi_init(void)
> > > >
> > > >         init_screen_info();
> > > >
> > > > +#ifdef CONFIG_ARM
> > > >         /* ARM does not permit early mappings to persist across paging_init() */
> > > > -       if (IS_ENABLED(CONFIG_ARM))
> > > > -               efi_memmap_unmap();
> > > > +       efi_memmap_unmap();
> > > > +
> > > > +       if (cpu_state_table != EFI_INVALID_TABLE_ADDR) {
>
> if (IS_ENABLED(CONFIG_ARM)) && cpu_state_table != EFI_INVALID_TABLE_ADDR
>

Ah never mind, this is already inside #ifdef CONFIG_ARM

> and do the same for screen_info_table (but in a separate patch)
>

but this I will still do

> > > > +               struct efi_arm_entry_state *state;
> > > > +               bool dump_state = true;
> > > > +
> > > > +               state = early_memremap_ro(cpu_state_table,
> > > > +                                         sizeof(struct efi_arm_entry_state));
> > > > +               if (state == NULL) {
> > > > +                       pr_warn("Unable to map CPU entry state table.\n");
> > > > +                       return;
> > > > +               }
> > > > +
> > > > +               if ((state->sctlr_before_ebs & 1) == 0)
> > > > +                       pr_warn(FW_BUG "EFI stub was entered with MMU and Dcache disabled, please fix your firmware!\n");
> > > > +               else if ((state->sctlr_after_ebs & 1) == 0)
> > > > +                       pr_warn(FW_BUG "ExitBootServices() returned with MMU and Dcache disabled, please fix your firmware!\n");
> > > > +               else
> > > > +                       dump_state = false;
> > > > +
> > > > +               if (dump_state || efi_enabled(EFI_DBG)) {
> > > > +                       pr_info("CPSR at EFI stub entry        : 0x%08x\n", state->cpsr_before_ebs);
> > > > +                       pr_info("SCTLR at EFI stub entry       : 0x%08x\n", state->sctlr_before_ebs);
> > > > +                       pr_info("CPSR after ExitBootServices() : 0x%08x\n", state->cpsr_after_ebs);
> > > > +                       pr_info("SCTLR after ExitBootServices(): 0x%08x\n", state->sctlr_after_ebs);
> > > > +               }
> > > > +               early_memunmap(state, sizeof(struct efi_arm_entry_state));
> > > > +       }
> > > > +#endif
> > > >  }
> > > >
> > > >  static bool efifb_overlaps_pci_range(const struct of_pci_range *range)
> > > > diff --git a/drivers/firmware/efi/libstub/arm32-stub.c b/drivers/firmware/efi/libstub/arm32-stub.c
> > > > index 40243f524556..60783228db6e 100644
> > > > --- a/drivers/firmware/efi/libstub/arm32-stub.c
> > > > +++ b/drivers/firmware/efi/libstub/arm32-stub.c
> > > > @@ -7,10 +7,49 @@
> > > >
> > > >  #include "efistub.h"
> > > >
> > > > +static efi_guid_t cpu_state_guid = LINUX_EFI_ARM_CPU_STATE_TABLE_GUID;
> > > > +
> > > > +struct efi_arm_entry_state *efi_entry_state;
> > > > +
> > > > +static void get_cpu_state(u32 *cpsr, u32 *sctlr)
> > > > +{
> > > > +       asm("mrs %0, cpsr" : "=r"(*cpsr));
> > > > +       if ((*cpsr & MODE_MASK) == HYP_MODE)
> > > > +               asm("mrc p15, 4, %0, c1, c0, 0" : "=r"(*sctlr));
> > > > +       else
> > > > +               asm("mrc p15, 0, %0, c1, c0, 0" : "=r"(*sctlr));
> > > > +}
> > > > +
> > > >  efi_status_t check_platform_features(void)
> > > >  {
> > > > +       efi_status_t status;
> > > > +       u32 cpsr, sctlr;
> > > >         int block;
> > > >
> > > > +       get_cpu_state(&cpsr, &sctlr);
> > > > +
> > > > +       efi_info("Entering in %s mode with MMU %sabled\n",
> > > > +                ((cpsr & MODE_MASK) == HYP_MODE) ? "HYP" : "SVC",
> > > > +                (sctlr & 1) ? "en" : "dis");
> > > > +
> > > > +       status = efi_bs_call(allocate_pool, EFI_LOADER_DATA,
> > > > +                            sizeof(*efi_entry_state),
> > > > +                            (void **)&efi_entry_state);
> > > > +       if (status != EFI_SUCCESS) {
> > > > +               efi_err("allocate_pool() failed\n");
> > > > +               return status;
> > > > +       }
> > > > +
> > > > +       efi_entry_state->cpsr_before_ebs = cpsr;
> > > > +       efi_entry_state->sctlr_before_ebs = sctlr;
> > > > +
> > > > +       status = efi_bs_call(install_configuration_table, &cpu_state_guid,
> > > > +                            efi_entry_state);
> > > > +       if (status != EFI_SUCCESS) {
> > > > +               efi_err("install_configuration_table() failed\n");
> > > > +               goto free_state;
> > > > +       }
> > > > +
> > > >         /* non-LPAE kernels can run anywhere */
> > > >         if (!IS_ENABLED(CONFIG_ARM_LPAE))
> > > >                 return EFI_SUCCESS;
> > > > @@ -19,9 +58,20 @@ efi_status_t check_platform_features(void)
> > > >         block = cpuid_feature_extract(CPUID_EXT_MMFR0, 0);
> > > >         if (block < 5) {
> > > >                 efi_err("This LPAE kernel is not supported by your CPU\n");
> > > > -               return EFI_UNSUPPORTED;
> > > > +               status = EFI_UNSUPPORTED;
> > > > +               goto free_state;
> > > >         }
> > > >         return EFI_SUCCESS;
> > > > +
> > > > +free_state:
> > > > +       efi_bs_call(free_pool, efi_entry_state);
> > > > +       return status;
> > > > +}
> > > > +
> > > > +void efi_handle_post_ebs_state(void)
> > > > +{
> > > > +       get_cpu_state(&efi_entry_state->cpsr_after_ebs,
> > > > +                     &efi_entry_state->sctlr_after_ebs);
> > > >  }
> > > >
> > > >  static efi_guid_t screen_info_guid = LINUX_EFI_ARM_SCREEN_INFO_TABLE_GUID;
> > > > diff --git a/drivers/firmware/efi/libstub/efi-stub.c b/drivers/firmware/efi/libstub/efi-stub.c
> > > > index e97370bdfdb0..3318ec3f8e5b 100644
> > > > --- a/drivers/firmware/efi/libstub/efi-stub.c
> > > > +++ b/drivers/firmware/efi/libstub/efi-stub.c
> > > > @@ -329,6 +329,9 @@ efi_status_t __efiapi efi_pe_entry(efi_handle_t handle,
> > > >         if (status != EFI_SUCCESS)
> > > >                 goto fail_free_initrd;
> > > >
> > > > +       if (IS_ENABLED(CONFIG_ARM))
> > > > +               efi_handle_post_ebs_state();
> > > > +
> > > >         efi_enter_kernel(image_addr, fdt_addr, fdt_totalsize((void *)fdt_addr));
> > > >         /* not reached */
> > > >
> > > > diff --git a/drivers/firmware/efi/libstub/efistub.h b/drivers/firmware/efi/libstub/efistub.h
> > > > index bcd8c0a785f0..6217ce647ebf 100644
> > > > --- a/drivers/firmware/efi/libstub/efistub.h
> > > > +++ b/drivers/firmware/efi/libstub/efistub.h
> > > > @@ -771,4 +771,6 @@ efi_status_t efi_load_initrd(efi_loaded_image_t *image,
> > > >                              unsigned long soft_limit,
> > > >                              unsigned long hard_limit);
> > > >
> > > > +void efi_handle_post_ebs_state(void);
> > > > +
> >
> > This declaration is made for both arm/arm64.
> > (I said it was tiny, feel free to ignore.)
> >
>
> Yes, that was deliberate. Forward declarations don't take up any
> space, and so there is no need for an #ifdef here.
>
>
> > > >  #endif
> > > > diff --git a/include/linux/efi.h b/include/linux/efi.h
> > > > index c3449c9699d0..bb35f3305e55 100644
> > > > --- a/include/linux/efi.h
> > > > +++ b/include/linux/efi.h
> > > > @@ -350,6 +350,7 @@ void efi_native_runtime_setup(void);
> > > >   * associated with ConOut
> > > >   */
> > > >  #define LINUX_EFI_ARM_SCREEN_INFO_TABLE_GUID   EFI_GUID(0xe03fc20a, 0x85dc, 0x406e,  0xb9, 0x0e, 0x4a, 0xb5, 0x02, 0x37, 0x1d, 0x95)
> > > > +#define LINUX_EFI_ARM_CPU_STATE_TABLE_GUID     EFI_GUID(0xef79e4aa, 0x3c3d, 0x4989,  0xb9, 0x02, 0x07, 0xa9, 0x43, 0xe5, 0x50, 0xd2)
> > > >  #define LINUX_EFI_LOADER_ENTRY_GUID            EFI_GUID(0x4a67b082, 0x0a4c, 0x41cf,  0xb6, 0xc7, 0x44, 0x0b, 0x29, 0xbb, 0x8c, 0x4f)
> > > >  #define LINUX_EFI_RANDOM_SEED_TABLE_GUID       EFI_GUID(0x1ce1e5bc, 0x7ceb, 0x42f2,  0x81, 0xe5, 0x8a, 0xad, 0xf1, 0x80, 0xf5, 0x7b)
> > > >  #define LINUX_EFI_TPM_EVENT_LOG_GUID           EFI_GUID(0xb7799cb0, 0xeca2, 0x4943,  0x96, 0x67, 0x1f, 0xae, 0x07, 0xb7, 0x47, 0xfa)
> > > > --
> > > > 2.27.0
> > > >

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v3] efi/libstub: arm: Print CPU boot mode and MMU state at boot
@ 2020-06-16 10:48         ` Ard Biesheuvel
  0 siblings, 0 replies; 10+ messages in thread
From: Ard Biesheuvel @ 2020-06-16 10:48 UTC (permalink / raw)
  To: Leif Lindholm
  Cc: Heinrich Schuchardt, Samer.El-Haj-Mahmoud, linux-efi,
	Grant Likely, Linux ARM

On Tue, 16 Jun 2020 at 12:46, Ard Biesheuvel <ardb@kernel.org> wrote:
>
> On Tue, 16 Jun 2020 at 12:39, Leif Lindholm <leif@nuviainc.com> wrote:
> >
> > On Tue, Jun 16, 2020 at 10:51:48 +0200, Ard Biesheuvel wrote:
> > > On Tue, 16 Jun 2020 at 10:50, Ard Biesheuvel <ardb@kernel.org> wrote:
> > > >
> > > > On 32-bit ARM, we may boot at HYP mode, or with the MMU and caches off
> > > > (or both), even though the EFI spec does not actually support this.
> > > > While booting at HYP mode is something we might tolerate, fiddling
> > > > with the caches is a more serious issue, as disabling the caches is
> > > > tricky to do safely from C code, and running without the Dcache makes
> > > > it impossible to support unaligned memory accesses, which is another
> > > > explicit requirement imposed by the EFI spec.
> > > >
> > > > So take note of the CPU mode and MMU state in the EFI stub diagnostic
> > > > output so that we can easily diagnose any issues that may arise from
> > > > this. E.g.,
> > > >
> > > >   EFI stub: Entering in SVC mode with MMU enabled
> > > >
> > > > Also, capture the CPSR and SCTLR system register values at EFI stub
> > > > entry, and after ExitBootServices() returns, and check whether the
> > > > MMU and Dcache were disabled at any point. If this is the case, a
> > > > diagnostic message like the following will be emitted:
> > > >
> > > >   efi: [Firmware Bug]: EFI stub was entered with MMU and Dcache disabled, please fix your firmware!
> > > >   efi: CPSR at EFI stub entry        : 0x600001d3
> > > >   efi: SCTLR at EFI stub entry       : 0x00c51838
> > > >   efi: CPSR after ExitBootServices() : 0x600001d3
> > > >   efi: SCTLR after ExitBootServices(): 0x00c50838
> > > >
> > > > Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
> > > > ---
> > > > Adding Grant and Samer to cc, as this may be something that should be taken
> > > > into account in the EBBR context? Note that this affects 64-bit only.
> > > >
> > >
> > > Affects *32* bit only
> >
> > One question and a teeny tiny nit below.
> >
> > > >  arch/arm/include/asm/efi.h                |  7 +++
> > > >  drivers/firmware/efi/arm-init.c           | 37 ++++++++++++--
> > > >  drivers/firmware/efi/libstub/arm32-stub.c | 52 +++++++++++++++++++-
> > > >  drivers/firmware/efi/libstub/efi-stub.c   |  3 ++
> > > >  drivers/firmware/efi/libstub/efistub.h    |  2 +
> > > >  include/linux/efi.h                       |  1 +
> > > >  6 files changed, 98 insertions(+), 4 deletions(-)
> > > >
> > > > diff --git a/arch/arm/include/asm/efi.h b/arch/arm/include/asm/efi.h
> > > > index 84dc0ba822f5..5dcf3c6011b7 100644
> > > > --- a/arch/arm/include/asm/efi.h
> > > > +++ b/arch/arm/include/asm/efi.h
> > > > @@ -87,4 +87,11 @@ static inline unsigned long efi_get_max_initrd_addr(unsigned long dram_base,
> > > >         return dram_base + SZ_512M;
> > > >  }
> > > >
> > > > +struct efi_arm_entry_state {
> > > > +       u32     cpsr_before_ebs;
> > > > +       u32     sctlr_before_ebs;
> > > > +       u32     cpsr_after_ebs;
> > > > +       u32     sctlr_after_ebs;
> > > > +};
> > > > +
> > > >  #endif /* _ASM_ARM_EFI_H */
> > > > diff --git a/drivers/firmware/efi/arm-init.c b/drivers/firmware/efi/arm-init.c
> > > > index c697e70ca7e7..4d1c272a4f79 100644
> > > > --- a/drivers/firmware/efi/arm-init.c
> > > > +++ b/drivers/firmware/efi/arm-init.c
> > > > @@ -52,9 +52,11 @@ static phys_addr_t __init efi_to_phys(unsigned long addr)
> > > >  }
> > > >
> > > >  static __initdata unsigned long screen_info_table = EFI_INVALID_TABLE_ADDR;
> > > > +static __initdata unsigned long cpu_state_table = EFI_INVALID_TABLE_ADDR;
> > > >
> > > >  static const efi_config_table_type_t arch_tables[] __initconst = {
> > > >         {LINUX_EFI_ARM_SCREEN_INFO_TABLE_GUID, &screen_info_table},
> > > > +       {LINUX_EFI_ARM_CPU_STATE_TABLE_GUID, &cpu_state_table},
> > > >         {}
> > > >  };
> > > >
> > > > @@ -116,7 +118,8 @@ static int __init uefi_init(u64 efi_system_table)
> > > >                 goto out;
> > > >         }
> > > >         retval = efi_config_parse_tables(config_tables, systab->nr_tables,
> > > > -                                        arch_tables);
> > > > +                                        IS_ENABLED(CONFIG_ARM) ? arch_tables
> > > > +                                                               : NULL);
> >
> > It this worthwhile?
> > If it turns out we have no need for an arch_table on arm64, should we
> > still include the static table, holding values only relevant for
> > 32-bit, in arm64 builds at all?
> >
>
> On arm64, the stub and the kernel proper are the same binary, so there
> should never be a need to use configuration tables to carry
> information from the stub to the kernel proper.
>
> As this is the only reference, the compiler will simply drop the table
> from the build, and it could even optimize away the '!=
> EFI_INVALID_TABLE_ADDR' checks and conditional code, as it can observe
> that those variables never change values.
>
> However, it appears that the latter does not actually occur in
> practice, so I will rewrite the test below:
>
> > > >
> > > >         early_memunmap(config_tables, table_size);
> > > >  out:
> > > > @@ -238,9 +241,37 @@ void __init efi_init(void)
> > > >
> > > >         init_screen_info();
> > > >
> > > > +#ifdef CONFIG_ARM
> > > >         /* ARM does not permit early mappings to persist across paging_init() */
> > > > -       if (IS_ENABLED(CONFIG_ARM))
> > > > -               efi_memmap_unmap();
> > > > +       efi_memmap_unmap();
> > > > +
> > > > +       if (cpu_state_table != EFI_INVALID_TABLE_ADDR) {
>
> if (IS_ENABLED(CONFIG_ARM)) && cpu_state_table != EFI_INVALID_TABLE_ADDR
>

Ah never mind, this is already inside #ifdef CONFIG_ARM

> and do the same for screen_info_table (but in a separate patch)
>

but this I will still do

> > > > +               struct efi_arm_entry_state *state;
> > > > +               bool dump_state = true;
> > > > +
> > > > +               state = early_memremap_ro(cpu_state_table,
> > > > +                                         sizeof(struct efi_arm_entry_state));
> > > > +               if (state == NULL) {
> > > > +                       pr_warn("Unable to map CPU entry state table.\n");
> > > > +                       return;
> > > > +               }
> > > > +
> > > > +               if ((state->sctlr_before_ebs & 1) == 0)
> > > > +                       pr_warn(FW_BUG "EFI stub was entered with MMU and Dcache disabled, please fix your firmware!\n");
> > > > +               else if ((state->sctlr_after_ebs & 1) == 0)
> > > > +                       pr_warn(FW_BUG "ExitBootServices() returned with MMU and Dcache disabled, please fix your firmware!\n");
> > > > +               else
> > > > +                       dump_state = false;
> > > > +
> > > > +               if (dump_state || efi_enabled(EFI_DBG)) {
> > > > +                       pr_info("CPSR at EFI stub entry        : 0x%08x\n", state->cpsr_before_ebs);
> > > > +                       pr_info("SCTLR at EFI stub entry       : 0x%08x\n", state->sctlr_before_ebs);
> > > > +                       pr_info("CPSR after ExitBootServices() : 0x%08x\n", state->cpsr_after_ebs);
> > > > +                       pr_info("SCTLR after ExitBootServices(): 0x%08x\n", state->sctlr_after_ebs);
> > > > +               }
> > > > +               early_memunmap(state, sizeof(struct efi_arm_entry_state));
> > > > +       }
> > > > +#endif
> > > >  }
> > > >
> > > >  static bool efifb_overlaps_pci_range(const struct of_pci_range *range)
> > > > diff --git a/drivers/firmware/efi/libstub/arm32-stub.c b/drivers/firmware/efi/libstub/arm32-stub.c
> > > > index 40243f524556..60783228db6e 100644
> > > > --- a/drivers/firmware/efi/libstub/arm32-stub.c
> > > > +++ b/drivers/firmware/efi/libstub/arm32-stub.c
> > > > @@ -7,10 +7,49 @@
> > > >
> > > >  #include "efistub.h"
> > > >
> > > > +static efi_guid_t cpu_state_guid = LINUX_EFI_ARM_CPU_STATE_TABLE_GUID;
> > > > +
> > > > +struct efi_arm_entry_state *efi_entry_state;
> > > > +
> > > > +static void get_cpu_state(u32 *cpsr, u32 *sctlr)
> > > > +{
> > > > +       asm("mrs %0, cpsr" : "=r"(*cpsr));
> > > > +       if ((*cpsr & MODE_MASK) == HYP_MODE)
> > > > +               asm("mrc p15, 4, %0, c1, c0, 0" : "=r"(*sctlr));
> > > > +       else
> > > > +               asm("mrc p15, 0, %0, c1, c0, 0" : "=r"(*sctlr));
> > > > +}
> > > > +
> > > >  efi_status_t check_platform_features(void)
> > > >  {
> > > > +       efi_status_t status;
> > > > +       u32 cpsr, sctlr;
> > > >         int block;
> > > >
> > > > +       get_cpu_state(&cpsr, &sctlr);
> > > > +
> > > > +       efi_info("Entering in %s mode with MMU %sabled\n",
> > > > +                ((cpsr & MODE_MASK) == HYP_MODE) ? "HYP" : "SVC",
> > > > +                (sctlr & 1) ? "en" : "dis");
> > > > +
> > > > +       status = efi_bs_call(allocate_pool, EFI_LOADER_DATA,
> > > > +                            sizeof(*efi_entry_state),
> > > > +                            (void **)&efi_entry_state);
> > > > +       if (status != EFI_SUCCESS) {
> > > > +               efi_err("allocate_pool() failed\n");
> > > > +               return status;
> > > > +       }
> > > > +
> > > > +       efi_entry_state->cpsr_before_ebs = cpsr;
> > > > +       efi_entry_state->sctlr_before_ebs = sctlr;
> > > > +
> > > > +       status = efi_bs_call(install_configuration_table, &cpu_state_guid,
> > > > +                            efi_entry_state);
> > > > +       if (status != EFI_SUCCESS) {
> > > > +               efi_err("install_configuration_table() failed\n");
> > > > +               goto free_state;
> > > > +       }
> > > > +
> > > >         /* non-LPAE kernels can run anywhere */
> > > >         if (!IS_ENABLED(CONFIG_ARM_LPAE))
> > > >                 return EFI_SUCCESS;
> > > > @@ -19,9 +58,20 @@ efi_status_t check_platform_features(void)
> > > >         block = cpuid_feature_extract(CPUID_EXT_MMFR0, 0);
> > > >         if (block < 5) {
> > > >                 efi_err("This LPAE kernel is not supported by your CPU\n");
> > > > -               return EFI_UNSUPPORTED;
> > > > +               status = EFI_UNSUPPORTED;
> > > > +               goto free_state;
> > > >         }
> > > >         return EFI_SUCCESS;
> > > > +
> > > > +free_state:
> > > > +       efi_bs_call(free_pool, efi_entry_state);
> > > > +       return status;
> > > > +}
> > > > +
> > > > +void efi_handle_post_ebs_state(void)
> > > > +{
> > > > +       get_cpu_state(&efi_entry_state->cpsr_after_ebs,
> > > > +                     &efi_entry_state->sctlr_after_ebs);
> > > >  }
> > > >
> > > >  static efi_guid_t screen_info_guid = LINUX_EFI_ARM_SCREEN_INFO_TABLE_GUID;
> > > > diff --git a/drivers/firmware/efi/libstub/efi-stub.c b/drivers/firmware/efi/libstub/efi-stub.c
> > > > index e97370bdfdb0..3318ec3f8e5b 100644
> > > > --- a/drivers/firmware/efi/libstub/efi-stub.c
> > > > +++ b/drivers/firmware/efi/libstub/efi-stub.c
> > > > @@ -329,6 +329,9 @@ efi_status_t __efiapi efi_pe_entry(efi_handle_t handle,
> > > >         if (status != EFI_SUCCESS)
> > > >                 goto fail_free_initrd;
> > > >
> > > > +       if (IS_ENABLED(CONFIG_ARM))
> > > > +               efi_handle_post_ebs_state();
> > > > +
> > > >         efi_enter_kernel(image_addr, fdt_addr, fdt_totalsize((void *)fdt_addr));
> > > >         /* not reached */
> > > >
> > > > diff --git a/drivers/firmware/efi/libstub/efistub.h b/drivers/firmware/efi/libstub/efistub.h
> > > > index bcd8c0a785f0..6217ce647ebf 100644
> > > > --- a/drivers/firmware/efi/libstub/efistub.h
> > > > +++ b/drivers/firmware/efi/libstub/efistub.h
> > > > @@ -771,4 +771,6 @@ efi_status_t efi_load_initrd(efi_loaded_image_t *image,
> > > >                              unsigned long soft_limit,
> > > >                              unsigned long hard_limit);
> > > >
> > > > +void efi_handle_post_ebs_state(void);
> > > > +
> >
> > This declaration is made for both arm/arm64.
> > (I said it was tiny, feel free to ignore.)
> >
>
> Yes, that was deliberate. Forward declarations don't take up any
> space, and so there is no need for an #ifdef here.
>
>
> > > >  #endif
> > > > diff --git a/include/linux/efi.h b/include/linux/efi.h
> > > > index c3449c9699d0..bb35f3305e55 100644
> > > > --- a/include/linux/efi.h
> > > > +++ b/include/linux/efi.h
> > > > @@ -350,6 +350,7 @@ void efi_native_runtime_setup(void);
> > > >   * associated with ConOut
> > > >   */
> > > >  #define LINUX_EFI_ARM_SCREEN_INFO_TABLE_GUID   EFI_GUID(0xe03fc20a, 0x85dc, 0x406e,  0xb9, 0x0e, 0x4a, 0xb5, 0x02, 0x37, 0x1d, 0x95)
> > > > +#define LINUX_EFI_ARM_CPU_STATE_TABLE_GUID     EFI_GUID(0xef79e4aa, 0x3c3d, 0x4989,  0xb9, 0x02, 0x07, 0xa9, 0x43, 0xe5, 0x50, 0xd2)
> > > >  #define LINUX_EFI_LOADER_ENTRY_GUID            EFI_GUID(0x4a67b082, 0x0a4c, 0x41cf,  0xb6, 0xc7, 0x44, 0x0b, 0x29, 0xbb, 0x8c, 0x4f)
> > > >  #define LINUX_EFI_RANDOM_SEED_TABLE_GUID       EFI_GUID(0x1ce1e5bc, 0x7ceb, 0x42f2,  0x81, 0xe5, 0x8a, 0xad, 0xf1, 0x80, 0xf5, 0x7b)
> > > >  #define LINUX_EFI_TPM_EVENT_LOG_GUID           EFI_GUID(0xb7799cb0, 0xeca2, 0x4943,  0x96, 0x67, 0x1f, 0xae, 0x07, 0xb7, 0x47, 0xfa)
> > > > --
> > > > 2.27.0
> > > >

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2020-06-16 10:48 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-06-16  8:50 [PATCH v3] efi/libstub: arm: Print CPU boot mode and MMU state at boot Ard Biesheuvel
2020-06-16  8:50 ` Ard Biesheuvel
2020-06-16  8:51 ` Ard Biesheuvel
2020-06-16  8:51   ` Ard Biesheuvel
2020-06-16 10:39   ` Leif Lindholm
2020-06-16 10:39     ` Leif Lindholm
2020-06-16 10:46     ` Ard Biesheuvel
2020-06-16 10:46       ` Ard Biesheuvel
2020-06-16 10:48       ` Ard Biesheuvel
2020-06-16 10:48         ` Ard Biesheuvel

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.