Skip to content

Commit 8204670

Browse files
committed
efi/libstub/arm64: Replace 'preferred' offset with alignment check
The notion of a 'preferred' load offset for the kernel dates back to the times when the kernel's primary mapping overlapped with the linear region, and memory below it could not be used at all. Today, the arm64 kernel does not really care where it is loaded in physical memory, as long as the alignment requirements are met, and so there is no point in unconditionally moving the kernel to a new location in memory at boot. Instead, we can - check for a KASLR seed, and randomly reallocate the kernel if one is provided - otherwise, check whether the alignment requirements are met for the current placement of the kernel, and just run it in place if they are - finally, do an ordinary page allocation and reallocate the kernel to a suitably aligned buffer anywhere in memory. By the same reasoning, there is no need to take TEXT_OFFSET into account if it is a round multiple of the minimum alignment, which is the usual case for relocatable kernels with TEXT_OFFSET randomization disabled. Otherwise, it suffices to use the relative misaligment of TEXT_OFFSET when reallocating the kernel. Signed-off-by: Ard Biesheuvel <[email protected]>
1 parent c37c916 commit 8204670

File tree

1 file changed

+25
-37
lines changed

1 file changed

+25
-37
lines changed

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

Lines changed: 25 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,15 @@ efi_status_t check_platform_features(void)
3434
return EFI_SUCCESS;
3535
}
3636

37+
/*
38+
* Relocatable kernels can fix up the misalignment with respect to
39+
* MIN_KIMG_ALIGN, so they only require a minimum alignment of EFI_KIMG_ALIGN
40+
* (which accounts for the alignment of statically allocated objects such as
41+
* the swapper stack.)
42+
*/
43+
static const u64 min_kimg_align = IS_ENABLED(CONFIG_RELOCATABLE) ? EFI_KIMG_ALIGN
44+
: MIN_KIMG_ALIGN;
45+
3746
efi_status_t handle_kernel_image(unsigned long *image_addr,
3847
unsigned long *image_size,
3948
unsigned long *reserve_addr,
@@ -43,7 +52,6 @@ efi_status_t handle_kernel_image(unsigned long *image_addr,
4352
{
4453
efi_status_t status;
4554
unsigned long kernel_size, kernel_memsize = 0;
46-
unsigned long preferred_offset;
4755
u64 phys_seed = 0;
4856

4957
if (IS_ENABLED(CONFIG_RANDOMIZE_BASE)) {
@@ -61,14 +69,8 @@ efi_status_t handle_kernel_image(unsigned long *image_addr,
6169
}
6270
}
6371

64-
/*
65-
* The preferred offset of the kernel Image is TEXT_OFFSET bytes beyond
66-
* a 2 MB aligned base, which itself may be lower than dram_base, as
67-
* long as the resulting offset equals or exceeds it.
68-
*/
69-
preferred_offset = round_down(dram_base, MIN_KIMG_ALIGN) + TEXT_OFFSET;
70-
if (preferred_offset < dram_base)
71-
preferred_offset += MIN_KIMG_ALIGN;
72+
if (image->image_base != _text)
73+
pr_efi_err("FIRMWARE BUG: efi_loaded_image_t::image_base has bogus value\n");
7274

7375
kernel_size = _edata - _text;
7476
kernel_memsize = kernel_size + (_end - _edata);
@@ -103,46 +105,32 @@ efi_status_t handle_kernel_image(unsigned long *image_addr,
103105

104106
*image_addr = *reserve_addr + offset;
105107
} else {
106-
/*
107-
* Else, try a straight allocation at the preferred offset.
108-
* This will work around the issue where, if dram_base == 0x0,
109-
* efi_low_alloc() refuses to allocate at 0x0 (to prevent the
110-
* address of the allocation to be mistaken for a FAIL return
111-
* value or a NULL pointer). It will also ensure that, on
112-
* platforms where the [dram_base, dram_base + TEXT_OFFSET)
113-
* interval is partially occupied by the firmware (like on APM
114-
* Mustang), we can still place the kernel at the address
115-
* 'dram_base + TEXT_OFFSET'.
116-
*/
117-
*image_addr = (unsigned long)_text;
118-
if (*image_addr == preferred_offset)
119-
return EFI_SUCCESS;
120-
121-
*image_addr = *reserve_addr = preferred_offset;
122-
*reserve_size = round_up(kernel_memsize, EFI_ALLOC_ALIGN);
123-
124-
status = efi_bs_call(allocate_pages, EFI_ALLOCATE_ADDRESS,
125-
EFI_LOADER_DATA,
126-
*reserve_size / EFI_PAGE_SIZE,
127-
(efi_physical_addr_t *)reserve_addr);
108+
status = EFI_OUT_OF_RESOURCES;
128109
}
129110

130111
if (status != EFI_SUCCESS) {
131-
*reserve_size = kernel_memsize + TEXT_OFFSET;
112+
if (IS_ALIGNED((u64)_text - TEXT_OFFSET, min_kimg_align)) {
113+
/*
114+
* Just execute from wherever we were loaded by the
115+
* UEFI PE/COFF loader if the alignment is suitable.
116+
*/
117+
*image_addr = (u64)_text;
118+
*reserve_size = 0;
119+
return EFI_SUCCESS;
120+
}
121+
122+
*reserve_size = kernel_memsize + TEXT_OFFSET % min_kimg_align;
132123
status = efi_low_alloc(*reserve_size,
133-
MIN_KIMG_ALIGN, reserve_addr);
124+
min_kimg_align, reserve_addr);
134125

135126
if (status != EFI_SUCCESS) {
136127
pr_efi_err("Failed to relocate kernel\n");
137128
*reserve_size = 0;
138129
return status;
139130
}
140-
*image_addr = *reserve_addr + TEXT_OFFSET;
131+
*image_addr = *reserve_addr + TEXT_OFFSET % min_kimg_align;
141132
}
142133

143-
if (image->image_base != _text)
144-
pr_efi_err("FIRMWARE BUG: efi_loaded_image_t::image_base has bogus value\n");
145-
146134
memcpy((void *)*image_addr, _text, kernel_size);
147135

148136
return EFI_SUCCESS;

0 commit comments

Comments
 (0)