Skip to content

Commit 685d816

Browse files
committed
efi/libstub: Move efi_relocate_kernel() into separate source file
Move efi_relocate_kernel() into a separate source file, so that it only gets pulled into builds for architectures that use it. Since efi_relocate_kernel() is the only user of efi_low_alloc(), let's move that over as well. Signed-off-by: Ard Biesheuvel <[email protected]>
1 parent e71356f commit 685d816

File tree

4 files changed

+175
-184
lines changed

4 files changed

+175
-184
lines changed

drivers/firmware/efi/libstub/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ KCOV_INSTRUMENT := n
4343
lib-y := efi-stub-helper.o gop.o secureboot.o tpm.o \
4444
file.o mem.o random.o randomalloc.o pci.o \
4545
skip_spaces.o lib-cmdline.o lib-ctype.o \
46-
alignedmem.o
46+
alignedmem.o relocate.o
4747

4848
# include the stub's generic dependencies from lib/ when building for ARM/arm64
4949
efi-deps-y := fdt_rw.c fdt_ro.c fdt_wip.c fdt.c fdt_empty_tree.c fdt_sw.c

drivers/firmware/efi/libstub/efistub.h

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -639,21 +639,6 @@ char *efi_convert_cmdline(efi_loaded_image_t *image, int *cmd_line_len,
639639

640640
efi_status_t efi_get_memory_map(struct efi_boot_memmap *map);
641641

642-
efi_status_t efi_low_alloc_above(unsigned long size, unsigned long align,
643-
unsigned long *addr, unsigned long min);
644-
645-
static inline
646-
efi_status_t efi_low_alloc(unsigned long size, unsigned long align,
647-
unsigned long *addr)
648-
{
649-
/*
650-
* Don't allocate at 0x0. It will confuse code that
651-
* checks pointers against NULL. Skip the first 8
652-
* bytes so we start at a nice even number.
653-
*/
654-
return efi_low_alloc_above(size, align, addr, 0x8);
655-
}
656-
657642
efi_status_t efi_allocate_pages(unsigned long size, unsigned long *addr,
658643
unsigned long max);
659644

drivers/firmware/efi/libstub/mem.c

Lines changed: 0 additions & 168 deletions
Original file line numberDiff line numberDiff line change
@@ -111,96 +111,6 @@ efi_status_t efi_allocate_pages(unsigned long size, unsigned long *addr,
111111
return EFI_SUCCESS;
112112
}
113113

114-
/**
115-
* efi_low_alloc_above() - allocate pages at or above given address
116-
* @size: size of the memory area to allocate
117-
* @align: minimum alignment of the allocated memory area. It should
118-
* a power of two.
119-
* @addr: on exit the address of the allocated memory
120-
* @min: minimum address to used for the memory allocation
121-
*
122-
* Allocate at the lowest possible address that is not below @min as
123-
* EFI_LOADER_DATA. The allocated pages are aligned according to @align but at
124-
* least EFI_ALLOC_ALIGN. The first allocated page will not below the address
125-
* given by @min.
126-
*
127-
* Return: status code
128-
*/
129-
efi_status_t efi_low_alloc_above(unsigned long size, unsigned long align,
130-
unsigned long *addr, unsigned long min)
131-
{
132-
unsigned long map_size, desc_size, buff_size;
133-
efi_memory_desc_t *map;
134-
efi_status_t status;
135-
unsigned long nr_pages;
136-
int i;
137-
struct efi_boot_memmap boot_map;
138-
139-
boot_map.map = &map;
140-
boot_map.map_size = &map_size;
141-
boot_map.desc_size = &desc_size;
142-
boot_map.desc_ver = NULL;
143-
boot_map.key_ptr = NULL;
144-
boot_map.buff_size = &buff_size;
145-
146-
status = efi_get_memory_map(&boot_map);
147-
if (status != EFI_SUCCESS)
148-
goto fail;
149-
150-
/*
151-
* Enforce minimum alignment that EFI or Linux requires when
152-
* requesting a specific address. We are doing page-based (or
153-
* larger) allocations, and both the address and size must meet
154-
* alignment constraints.
155-
*/
156-
if (align < EFI_ALLOC_ALIGN)
157-
align = EFI_ALLOC_ALIGN;
158-
159-
size = round_up(size, EFI_ALLOC_ALIGN);
160-
nr_pages = size / EFI_PAGE_SIZE;
161-
for (i = 0; i < map_size / desc_size; i++) {
162-
efi_memory_desc_t *desc;
163-
unsigned long m = (unsigned long)map;
164-
u64 start, end;
165-
166-
desc = efi_early_memdesc_ptr(m, desc_size, i);
167-
168-
if (desc->type != EFI_CONVENTIONAL_MEMORY)
169-
continue;
170-
171-
if (efi_soft_reserve_enabled() &&
172-
(desc->attribute & EFI_MEMORY_SP))
173-
continue;
174-
175-
if (desc->num_pages < nr_pages)
176-
continue;
177-
178-
start = desc->phys_addr;
179-
end = start + desc->num_pages * EFI_PAGE_SIZE;
180-
181-
if (start < min)
182-
start = min;
183-
184-
start = round_up(start, align);
185-
if ((start + size) > end)
186-
continue;
187-
188-
status = efi_bs_call(allocate_pages, EFI_ALLOCATE_ADDRESS,
189-
EFI_LOADER_DATA, nr_pages, &start);
190-
if (status == EFI_SUCCESS) {
191-
*addr = start;
192-
break;
193-
}
194-
}
195-
196-
if (i == map_size / desc_size)
197-
status = EFI_NOT_FOUND;
198-
199-
efi_bs_call(free_pool, map);
200-
fail:
201-
return status;
202-
}
203-
204114
/**
205115
* efi_free() - free memory pages
206116
* @size: size of the memory area to free in bytes
@@ -222,81 +132,3 @@ void efi_free(unsigned long size, unsigned long addr)
222132
nr_pages = round_up(size, EFI_ALLOC_ALIGN) / EFI_PAGE_SIZE;
223133
efi_bs_call(free_pages, addr, nr_pages);
224134
}
225-
226-
/**
227-
* efi_relocate_kernel() - copy memory area
228-
* @image_addr: pointer to address of memory area to copy
229-
* @image_size: size of memory area to copy
230-
* @alloc_size: minimum size of memory to allocate, must be greater or
231-
* equal to image_size
232-
* @preferred_addr: preferred target address
233-
* @alignment: minimum alignment of the allocated memory area. It
234-
* should be a power of two.
235-
* @min_addr: minimum target address
236-
*
237-
* Copy a memory area to a newly allocated memory area aligned according
238-
* to @alignment but at least EFI_ALLOC_ALIGN. If the preferred address
239-
* is not available, the allocated address will not be below @min_addr.
240-
* On exit, @image_addr is updated to the target copy address that was used.
241-
*
242-
* This function is used to copy the Linux kernel verbatim. It does not apply
243-
* any relocation changes.
244-
*
245-
* Return: status code
246-
*/
247-
efi_status_t efi_relocate_kernel(unsigned long *image_addr,
248-
unsigned long image_size,
249-
unsigned long alloc_size,
250-
unsigned long preferred_addr,
251-
unsigned long alignment,
252-
unsigned long min_addr)
253-
{
254-
unsigned long cur_image_addr;
255-
unsigned long new_addr = 0;
256-
efi_status_t status;
257-
unsigned long nr_pages;
258-
efi_physical_addr_t efi_addr = preferred_addr;
259-
260-
if (!image_addr || !image_size || !alloc_size)
261-
return EFI_INVALID_PARAMETER;
262-
if (alloc_size < image_size)
263-
return EFI_INVALID_PARAMETER;
264-
265-
cur_image_addr = *image_addr;
266-
267-
/*
268-
* The EFI firmware loader could have placed the kernel image
269-
* anywhere in memory, but the kernel has restrictions on the
270-
* max physical address it can run at. Some architectures
271-
* also have a prefered address, so first try to relocate
272-
* to the preferred address. If that fails, allocate as low
273-
* as possible while respecting the required alignment.
274-
*/
275-
nr_pages = round_up(alloc_size, EFI_ALLOC_ALIGN) / EFI_PAGE_SIZE;
276-
status = efi_bs_call(allocate_pages, EFI_ALLOCATE_ADDRESS,
277-
EFI_LOADER_DATA, nr_pages, &efi_addr);
278-
new_addr = efi_addr;
279-
/*
280-
* If preferred address allocation failed allocate as low as
281-
* possible.
282-
*/
283-
if (status != EFI_SUCCESS) {
284-
status = efi_low_alloc_above(alloc_size, alignment, &new_addr,
285-
min_addr);
286-
}
287-
if (status != EFI_SUCCESS) {
288-
pr_efi_err("Failed to allocate usable memory for kernel.\n");
289-
return status;
290-
}
291-
292-
/*
293-
* We know source/dest won't overlap since both memory ranges
294-
* have been allocated by UEFI, so we can safely use memcpy.
295-
*/
296-
memcpy((void *)new_addr, (void *)cur_image_addr, image_size);
297-
298-
/* Return the new address of the relocated image. */
299-
*image_addr = new_addr;
300-
301-
return status;
302-
}
Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
#include <linux/efi.h>
4+
#include <asm/efi.h>
5+
6+
#include "efistub.h"
7+
8+
/**
9+
* efi_low_alloc_above() - allocate pages at or above given address
10+
* @size: size of the memory area to allocate
11+
* @align: minimum alignment of the allocated memory area. It should
12+
* a power of two.
13+
* @addr: on exit the address of the allocated memory
14+
* @min: minimum address to used for the memory allocation
15+
*
16+
* Allocate at the lowest possible address that is not below @min as
17+
* EFI_LOADER_DATA. The allocated pages are aligned according to @align but at
18+
* least EFI_ALLOC_ALIGN. The first allocated page will not below the address
19+
* given by @min.
20+
*
21+
* Return: status code
22+
*/
23+
static efi_status_t efi_low_alloc_above(unsigned long size, unsigned long align,
24+
unsigned long *addr, unsigned long min)
25+
{
26+
unsigned long map_size, desc_size, buff_size;
27+
efi_memory_desc_t *map;
28+
efi_status_t status;
29+
unsigned long nr_pages;
30+
int i;
31+
struct efi_boot_memmap boot_map;
32+
33+
boot_map.map = &map;
34+
boot_map.map_size = &map_size;
35+
boot_map.desc_size = &desc_size;
36+
boot_map.desc_ver = NULL;
37+
boot_map.key_ptr = NULL;
38+
boot_map.buff_size = &buff_size;
39+
40+
status = efi_get_memory_map(&boot_map);
41+
if (status != EFI_SUCCESS)
42+
goto fail;
43+
44+
/*
45+
* Enforce minimum alignment that EFI or Linux requires when
46+
* requesting a specific address. We are doing page-based (or
47+
* larger) allocations, and both the address and size must meet
48+
* alignment constraints.
49+
*/
50+
if (align < EFI_ALLOC_ALIGN)
51+
align = EFI_ALLOC_ALIGN;
52+
53+
size = round_up(size, EFI_ALLOC_ALIGN);
54+
nr_pages = size / EFI_PAGE_SIZE;
55+
for (i = 0; i < map_size / desc_size; i++) {
56+
efi_memory_desc_t *desc;
57+
unsigned long m = (unsigned long)map;
58+
u64 start, end;
59+
60+
desc = efi_early_memdesc_ptr(m, desc_size, i);
61+
62+
if (desc->type != EFI_CONVENTIONAL_MEMORY)
63+
continue;
64+
65+
if (efi_soft_reserve_enabled() &&
66+
(desc->attribute & EFI_MEMORY_SP))
67+
continue;
68+
69+
if (desc->num_pages < nr_pages)
70+
continue;
71+
72+
start = desc->phys_addr;
73+
end = start + desc->num_pages * EFI_PAGE_SIZE;
74+
75+
if (start < min)
76+
start = min;
77+
78+
start = round_up(start, align);
79+
if ((start + size) > end)
80+
continue;
81+
82+
status = efi_bs_call(allocate_pages, EFI_ALLOCATE_ADDRESS,
83+
EFI_LOADER_DATA, nr_pages, &start);
84+
if (status == EFI_SUCCESS) {
85+
*addr = start;
86+
break;
87+
}
88+
}
89+
90+
if (i == map_size / desc_size)
91+
status = EFI_NOT_FOUND;
92+
93+
efi_bs_call(free_pool, map);
94+
fail:
95+
return status;
96+
}
97+
98+
/**
99+
* efi_relocate_kernel() - copy memory area
100+
* @image_addr: pointer to address of memory area to copy
101+
* @image_size: size of memory area to copy
102+
* @alloc_size: minimum size of memory to allocate, must be greater or
103+
* equal to image_size
104+
* @preferred_addr: preferred target address
105+
* @alignment: minimum alignment of the allocated memory area. It
106+
* should be a power of two.
107+
* @min_addr: minimum target address
108+
*
109+
* Copy a memory area to a newly allocated memory area aligned according
110+
* to @alignment but at least EFI_ALLOC_ALIGN. If the preferred address
111+
* is not available, the allocated address will not be below @min_addr.
112+
* On exit, @image_addr is updated to the target copy address that was used.
113+
*
114+
* This function is used to copy the Linux kernel verbatim. It does not apply
115+
* any relocation changes.
116+
*
117+
* Return: status code
118+
*/
119+
efi_status_t efi_relocate_kernel(unsigned long *image_addr,
120+
unsigned long image_size,
121+
unsigned long alloc_size,
122+
unsigned long preferred_addr,
123+
unsigned long alignment,
124+
unsigned long min_addr)
125+
{
126+
unsigned long cur_image_addr;
127+
unsigned long new_addr = 0;
128+
efi_status_t status;
129+
unsigned long nr_pages;
130+
efi_physical_addr_t efi_addr = preferred_addr;
131+
132+
if (!image_addr || !image_size || !alloc_size)
133+
return EFI_INVALID_PARAMETER;
134+
if (alloc_size < image_size)
135+
return EFI_INVALID_PARAMETER;
136+
137+
cur_image_addr = *image_addr;
138+
139+
/*
140+
* The EFI firmware loader could have placed the kernel image
141+
* anywhere in memory, but the kernel has restrictions on the
142+
* max physical address it can run at. Some architectures
143+
* also have a prefered address, so first try to relocate
144+
* to the preferred address. If that fails, allocate as low
145+
* as possible while respecting the required alignment.
146+
*/
147+
nr_pages = round_up(alloc_size, EFI_ALLOC_ALIGN) / EFI_PAGE_SIZE;
148+
status = efi_bs_call(allocate_pages, EFI_ALLOCATE_ADDRESS,
149+
EFI_LOADER_DATA, nr_pages, &efi_addr);
150+
new_addr = efi_addr;
151+
/*
152+
* If preferred address allocation failed allocate as low as
153+
* possible.
154+
*/
155+
if (status != EFI_SUCCESS) {
156+
status = efi_low_alloc_above(alloc_size, alignment, &new_addr,
157+
min_addr);
158+
}
159+
if (status != EFI_SUCCESS) {
160+
pr_efi_err("Failed to allocate usable memory for kernel.\n");
161+
return status;
162+
}
163+
164+
/*
165+
* We know source/dest won't overlap since both memory ranges
166+
* have been allocated by UEFI, so we can safely use memcpy.
167+
*/
168+
memcpy((void *)new_addr, (void *)cur_image_addr, image_size);
169+
170+
/* Return the new address of the relocated image. */
171+
*image_addr = new_addr;
172+
173+
return status;
174+
}

0 commit comments

Comments
 (0)