Skip to content

Commit 6178617

Browse files
ardbiesheuvelctmarinas
authored andcommitted
efi: arm64: enter with MMU and caches enabled
Instead of cleaning the entire loaded kernel image to the PoC and disabling the MMU and caches before branching to the kernel's bare metal entry point, we can leave the MMU and caches enabled, and rely on EFI's cacheable 1:1 mapping of all of system RAM (which is mandated by the spec) to populate the initial page tables. This removes the need for managing coherency in software, which is tedious and error prone. Note that we still need to clean the executable region of the image to the PoU if this is required for I/D coherency, but only if we actually decided to move the image in memory, as otherwise, this will have been taken care of by the loader. This change affects both the builtin EFI stub as well as the zboot decompressor, which now carries the entire EFI stub along with the decompression code and the compressed image. Signed-off-by: Ard Biesheuvel <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Catalin Marinas <[email protected]>
1 parent 3dcf60b commit 6178617

File tree

7 files changed

+61
-85
lines changed

7 files changed

+61
-85
lines changed

arch/arm64/include/asm/efi.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,8 @@ static inline unsigned long efi_get_kimg_min_align(void)
105105
#define EFI_ALLOC_ALIGN SZ_64K
106106
#define EFI_ALLOC_LIMIT ((1UL << 48) - 1)
107107

108+
extern unsigned long primary_entry_offset(void);
109+
108110
/*
109111
* On ARM systems, virtually remapped UEFI runtime services are set up in two
110112
* distinct stages:

arch/arm64/kernel/image-vars.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
#error This file should only be included in vmlinux.lds.S
1111
#endif
1212

13-
PROVIDE(__efistub_primary_entry_offset = primary_entry - _text);
13+
PROVIDE(__efistub_primary_entry = primary_entry);
1414

1515
/*
1616
* The EFI stub has its own symbol namespace prefixed by __efistub_, to
@@ -21,10 +21,11 @@ PROVIDE(__efistub_primary_entry_offset = primary_entry - _text);
2121
* linked at. The routines below are all implemented in assembler in a
2222
* position independent manner
2323
*/
24-
PROVIDE(__efistub_dcache_clean_poc = __pi_dcache_clean_poc);
24+
PROVIDE(__efistub_caches_clean_inval_pou = __pi_caches_clean_inval_pou);
2525

2626
PROVIDE(__efistub__text = _text);
2727
PROVIDE(__efistub__end = _end);
28+
PROVIDE(__efistub___inittext_end = __inittext_end);
2829
PROVIDE(__efistub__edata = _edata);
2930
PROVIDE(__efistub_screen_info = screen_info);
3031
PROVIDE(__efistub__ctype = _ctype);

arch/arm64/mm/cache.S

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ SYM_FUNC_START(caches_clean_inval_pou)
5656
caches_clean_inval_pou_macro
5757
ret
5858
SYM_FUNC_END(caches_clean_inval_pou)
59+
SYM_FUNC_ALIAS(__pi_caches_clean_inval_pou, caches_clean_inval_pou)
5960

6061
/*
6162
* caches_clean_inval_user_pou(start,end)

drivers/firmware/efi/libstub/Makefile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ lib-$(CONFIG_EFI_GENERIC_STUB) += efi-stub.o string.o intrinsics.o systable.o \
8787
screen_info.o efi-stub-entry.o
8888

8989
lib-$(CONFIG_ARM) += arm32-stub.o
90-
lib-$(CONFIG_ARM64) += arm64.o arm64-stub.o arm64-entry.o smbios.o
90+
lib-$(CONFIG_ARM64) += arm64.o arm64-stub.o smbios.o
9191
lib-$(CONFIG_X86) += x86-stub.o
9292
lib-$(CONFIG_RISCV) += riscv.o riscv-stub.o
9393
lib-$(CONFIG_LOONGARCH) += loongarch.o loongarch-stub.o
@@ -141,7 +141,7 @@ STUBCOPY_RELOC-$(CONFIG_ARM) := R_ARM_ABS
141141
#
142142
STUBCOPY_FLAGS-$(CONFIG_ARM64) += --prefix-alloc-sections=.init \
143143
--prefix-symbols=__efistub_
144-
STUBCOPY_RELOC-$(CONFIG_ARM64) := R_AARCH64_ABS64
144+
STUBCOPY_RELOC-$(CONFIG_ARM64) := R_AARCH64_ABS
145145

146146
# For RISC-V, we don't need anything special other than arm64. Keep all the
147147
# symbols in .init section and make sure that no absolute symbols references

drivers/firmware/efi/libstub/arm64-entry.S

Lines changed: 0 additions & 67 deletions
This file was deleted.

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

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ efi_status_t handle_kernel_image(unsigned long *image_addr,
5858
efi_handle_t image_handle)
5959
{
6060
efi_status_t status;
61-
unsigned long kernel_size, kernel_memsize = 0;
61+
unsigned long kernel_size, kernel_codesize, kernel_memsize;
6262
u32 phys_seed = 0;
6363
u64 min_kimg_align = efi_get_kimg_min_align();
6464

@@ -93,6 +93,7 @@ efi_status_t handle_kernel_image(unsigned long *image_addr,
9393
SEGMENT_ALIGN >> 10);
9494

9595
kernel_size = _edata - _text;
96+
kernel_codesize = __inittext_end - _text;
9697
kernel_memsize = kernel_size + (_end - _edata);
9798
*reserve_size = kernel_memsize;
9899

@@ -121,7 +122,7 @@ efi_status_t handle_kernel_image(unsigned long *image_addr,
121122
*/
122123
*image_addr = (u64)_text;
123124
*reserve_size = 0;
124-
goto clean_image_to_poc;
125+
return EFI_SUCCESS;
125126
}
126127

127128
status = efi_allocate_pages_aligned(*reserve_size, reserve_addr,
@@ -137,14 +138,21 @@ efi_status_t handle_kernel_image(unsigned long *image_addr,
137138

138139
*image_addr = *reserve_addr;
139140
memcpy((void *)*image_addr, _text, kernel_size);
141+
caches_clean_inval_pou(*image_addr, *image_addr + kernel_codesize);
140142

141-
clean_image_to_poc:
143+
return EFI_SUCCESS;
144+
}
145+
146+
asmlinkage void primary_entry(void);
147+
148+
unsigned long primary_entry_offset(void)
149+
{
142150
/*
143-
* Clean the copied Image to the PoC, and ensure it is not shadowed by
144-
* stale icache entries from before relocation.
151+
* When built as part of the kernel, the EFI stub cannot branch to the
152+
* kernel proper via the image header, as the PE/COFF header is
153+
* strictly not part of the in-memory presentation of the image, only
154+
* of the file representation. So instead, we need to jump to the
155+
* actual entrypoint in the .text region of the image.
145156
*/
146-
dcache_clean_poc(*image_addr, *image_addr + kernel_size);
147-
asm("ic ialluis");
148-
149-
return EFI_SUCCESS;
157+
return (char *)primary_entry - _text;
150158
}

drivers/firmware/efi/libstub/arm64.c

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,12 @@ efi_status_t check_platform_features(void)
5656
return EFI_SUCCESS;
5757
}
5858

59+
#ifdef CONFIG_ARM64_WORKAROUND_CLEAN_CACHE
60+
#define DCTYPE "civac"
61+
#else
62+
#define DCTYPE "cvau"
63+
#endif
64+
5965
void efi_cache_sync_image(unsigned long image_base,
6066
unsigned long alloc_size,
6167
unsigned long code_size)
@@ -64,13 +70,38 @@ void efi_cache_sync_image(unsigned long image_base,
6470
u64 lsize = 4 << cpuid_feature_extract_unsigned_field(ctr,
6571
CTR_EL0_DminLine_SHIFT);
6672

67-
do {
68-
asm("dc civac, %0" :: "r"(image_base));
69-
image_base += lsize;
70-
alloc_size -= lsize;
71-
} while (alloc_size >= lsize);
73+
/* only perform the cache maintenance if needed for I/D coherency */
74+
if (!(ctr & BIT(CTR_EL0_IDC_SHIFT))) {
75+
do {
76+
asm("dc " DCTYPE ", %0" :: "r"(image_base));
77+
image_base += lsize;
78+
code_size -= lsize;
79+
} while (code_size >= lsize);
80+
}
7281

7382
asm("ic ialluis");
7483
dsb(ish);
7584
isb();
7685
}
86+
87+
unsigned long __weak primary_entry_offset(void)
88+
{
89+
/*
90+
* By default, we can invoke the kernel via the branch instruction in
91+
* the image header, so offset #0. This will be overridden by the EFI
92+
* stub build that is linked into the core kernel, as in that case, the
93+
* image header may not have been loaded into memory, or may be mapped
94+
* with non-executable permissions.
95+
*/
96+
return 0;
97+
}
98+
99+
void __noreturn efi_enter_kernel(unsigned long entrypoint,
100+
unsigned long fdt_addr,
101+
unsigned long fdt_size)
102+
{
103+
void (* __noreturn enter_kernel)(u64, u64, u64, u64);
104+
105+
enter_kernel = (void *)entrypoint + primary_entry_offset();
106+
enter_kernel(fdt_addr, 0, 0, 0);
107+
}

0 commit comments

Comments
 (0)