Skip to content

Commit 2a55280

Browse files
committed
efi/libstub: arm: Print CPU boot mode and MMU state at boot
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 <[email protected]> Reviewed-by: Leif Lindholm <[email protected]>
1 parent 62956be commit 2a55280

File tree

6 files changed

+98
-3
lines changed

6 files changed

+98
-3
lines changed

arch/arm/include/asm/efi.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,4 +87,11 @@ static inline unsigned long efi_get_max_initrd_addr(unsigned long dram_base,
8787
return dram_base + SZ_512M;
8888
}
8989

90+
struct efi_arm_entry_state {
91+
u32 cpsr_before_ebs;
92+
u32 sctlr_before_ebs;
93+
u32 cpsr_after_ebs;
94+
u32 sctlr_after_ebs;
95+
};
96+
9097
#endif /* _ASM_ARM_EFI_H */

drivers/firmware/efi/arm-init.c

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,11 @@ static phys_addr_t __init efi_to_phys(unsigned long addr)
5252
}
5353

5454
static __initdata unsigned long screen_info_table = EFI_INVALID_TABLE_ADDR;
55+
static __initdata unsigned long cpu_state_table = EFI_INVALID_TABLE_ADDR;
5556

5657
static const efi_config_table_type_t arch_tables[] __initconst = {
5758
{LINUX_EFI_ARM_SCREEN_INFO_TABLE_GUID, &screen_info_table},
59+
{LINUX_EFI_ARM_CPU_STATE_TABLE_GUID, &cpu_state_table},
5860
{}
5961
};
6062

@@ -240,9 +242,37 @@ void __init efi_init(void)
240242

241243
init_screen_info();
242244

245+
#ifdef CONFIG_ARM
243246
/* ARM does not permit early mappings to persist across paging_init() */
244-
if (IS_ENABLED(CONFIG_ARM))
245-
efi_memmap_unmap();
247+
efi_memmap_unmap();
248+
249+
if (cpu_state_table != EFI_INVALID_TABLE_ADDR) {
250+
struct efi_arm_entry_state *state;
251+
bool dump_state = true;
252+
253+
state = early_memremap_ro(cpu_state_table,
254+
sizeof(struct efi_arm_entry_state));
255+
if (state == NULL) {
256+
pr_warn("Unable to map CPU entry state table.\n");
257+
return;
258+
}
259+
260+
if ((state->sctlr_before_ebs & 1) == 0)
261+
pr_warn(FW_BUG "EFI stub was entered with MMU and Dcache disabled, please fix your firmware!\n");
262+
else if ((state->sctlr_after_ebs & 1) == 0)
263+
pr_warn(FW_BUG "ExitBootServices() returned with MMU and Dcache disabled, please fix your firmware!\n");
264+
else
265+
dump_state = false;
266+
267+
if (dump_state || efi_enabled(EFI_DBG)) {
268+
pr_info("CPSR at EFI stub entry : 0x%08x\n", state->cpsr_before_ebs);
269+
pr_info("SCTLR at EFI stub entry : 0x%08x\n", state->sctlr_before_ebs);
270+
pr_info("CPSR after ExitBootServices() : 0x%08x\n", state->cpsr_after_ebs);
271+
pr_info("SCTLR after ExitBootServices(): 0x%08x\n", state->sctlr_after_ebs);
272+
}
273+
early_memunmap(state, sizeof(struct efi_arm_entry_state));
274+
}
275+
#endif
246276
}
247277

248278
static bool efifb_overlaps_pci_range(const struct of_pci_range *range)

drivers/firmware/efi/libstub/arm32-stub.c

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,49 @@
77

88
#include "efistub.h"
99

10+
static efi_guid_t cpu_state_guid = LINUX_EFI_ARM_CPU_STATE_TABLE_GUID;
11+
12+
struct efi_arm_entry_state *efi_entry_state;
13+
14+
static void get_cpu_state(u32 *cpsr, u32 *sctlr)
15+
{
16+
asm("mrs %0, cpsr" : "=r"(*cpsr));
17+
if ((*cpsr & MODE_MASK) == HYP_MODE)
18+
asm("mrc p15, 4, %0, c1, c0, 0" : "=r"(*sctlr));
19+
else
20+
asm("mrc p15, 0, %0, c1, c0, 0" : "=r"(*sctlr));
21+
}
22+
1023
efi_status_t check_platform_features(void)
1124
{
25+
efi_status_t status;
26+
u32 cpsr, sctlr;
1227
int block;
1328

29+
get_cpu_state(&cpsr, &sctlr);
30+
31+
efi_info("Entering in %s mode with MMU %sabled\n",
32+
((cpsr & MODE_MASK) == HYP_MODE) ? "HYP" : "SVC",
33+
(sctlr & 1) ? "en" : "dis");
34+
35+
status = efi_bs_call(allocate_pool, EFI_LOADER_DATA,
36+
sizeof(*efi_entry_state),
37+
(void **)&efi_entry_state);
38+
if (status != EFI_SUCCESS) {
39+
efi_err("allocate_pool() failed\n");
40+
return status;
41+
}
42+
43+
efi_entry_state->cpsr_before_ebs = cpsr;
44+
efi_entry_state->sctlr_before_ebs = sctlr;
45+
46+
status = efi_bs_call(install_configuration_table, &cpu_state_guid,
47+
efi_entry_state);
48+
if (status != EFI_SUCCESS) {
49+
efi_err("install_configuration_table() failed\n");
50+
goto free_state;
51+
}
52+
1453
/* non-LPAE kernels can run anywhere */
1554
if (!IS_ENABLED(CONFIG_ARM_LPAE))
1655
return EFI_SUCCESS;
@@ -19,9 +58,22 @@ efi_status_t check_platform_features(void)
1958
block = cpuid_feature_extract(CPUID_EXT_MMFR0, 0);
2059
if (block < 5) {
2160
efi_err("This LPAE kernel is not supported by your CPU\n");
22-
return EFI_UNSUPPORTED;
61+
status = EFI_UNSUPPORTED;
62+
goto drop_table;
2363
}
2464
return EFI_SUCCESS;
65+
66+
drop_table:
67+
efi_bs_call(install_configuration_table, &cpu_state_guid, NULL);
68+
free_state:
69+
efi_bs_call(free_pool, efi_entry_state);
70+
return status;
71+
}
72+
73+
void efi_handle_post_ebs_state(void)
74+
{
75+
get_cpu_state(&efi_entry_state->cpsr_after_ebs,
76+
&efi_entry_state->sctlr_after_ebs);
2577
}
2678

2779
static efi_guid_t screen_info_guid = LINUX_EFI_ARM_SCREEN_INFO_TABLE_GUID;

drivers/firmware/efi/libstub/efi-stub.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,9 @@ efi_status_t __efiapi efi_pe_entry(efi_handle_t handle,
329329
if (status != EFI_SUCCESS)
330330
goto fail_free_initrd;
331331

332+
if (IS_ENABLED(CONFIG_ARM))
333+
efi_handle_post_ebs_state();
334+
332335
efi_enter_kernel(image_addr, fdt_addr, fdt_totalsize((void *)fdt_addr));
333336
/* not reached */
334337

drivers/firmware/efi/libstub/efistub.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -777,4 +777,6 @@ efi_status_t efi_load_initrd(efi_loaded_image_t *image,
777777
unsigned long soft_limit,
778778
unsigned long hard_limit);
779779

780+
void efi_handle_post_ebs_state(void);
781+
780782
#endif

include/linux/efi.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,7 @@ void efi_native_runtime_setup(void);
350350
* associated with ConOut
351351
*/
352352
#define LINUX_EFI_ARM_SCREEN_INFO_TABLE_GUID EFI_GUID(0xe03fc20a, 0x85dc, 0x406e, 0xb9, 0x0e, 0x4a, 0xb5, 0x02, 0x37, 0x1d, 0x95)
353+
#define LINUX_EFI_ARM_CPU_STATE_TABLE_GUID EFI_GUID(0xef79e4aa, 0x3c3d, 0x4989, 0xb9, 0x02, 0x07, 0xa9, 0x43, 0xe5, 0x50, 0xd2)
353354
#define LINUX_EFI_LOADER_ENTRY_GUID EFI_GUID(0x4a67b082, 0x0a4c, 0x41cf, 0xb6, 0xc7, 0x44, 0x0b, 0x29, 0xbb, 0x8c, 0x4f)
354355
#define LINUX_EFI_RANDOM_SEED_TABLE_GUID EFI_GUID(0x1ce1e5bc, 0x7ceb, 0x42f2, 0x81, 0xe5, 0x8a, 0xad, 0xf1, 0x80, 0xf5, 0x7b)
355356
#define LINUX_EFI_TPM_EVENT_LOG_GUID EFI_GUID(0xb7799cb0, 0xeca2, 0x4943, 0x96, 0x67, 0x1f, 0xae, 0x07, 0xb7, 0x47, 0xfa)

0 commit comments

Comments
 (0)