xref: /OK3568_Linux_fs/kernel/drivers/firmware/efi/libstub/relocate.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun 
3*4882a593Smuzhiyun #include <linux/efi.h>
4*4882a593Smuzhiyun #include <asm/efi.h>
5*4882a593Smuzhiyun 
6*4882a593Smuzhiyun #include "efistub.h"
7*4882a593Smuzhiyun 
8*4882a593Smuzhiyun /**
9*4882a593Smuzhiyun  * efi_low_alloc_above() - allocate pages at or above given address
10*4882a593Smuzhiyun  * @size:	size of the memory area to allocate
11*4882a593Smuzhiyun  * @align:	minimum alignment of the allocated memory area. It should
12*4882a593Smuzhiyun  *		a power of two.
13*4882a593Smuzhiyun  * @addr:	on exit the address of the allocated memory
14*4882a593Smuzhiyun  * @min:	minimum address to used for the memory allocation
15*4882a593Smuzhiyun  *
16*4882a593Smuzhiyun  * Allocate at the lowest possible address that is not below @min as
17*4882a593Smuzhiyun  * EFI_LOADER_DATA. The allocated pages are aligned according to @align but at
18*4882a593Smuzhiyun  * least EFI_ALLOC_ALIGN. The first allocated page will not below the address
19*4882a593Smuzhiyun  * given by @min.
20*4882a593Smuzhiyun  *
21*4882a593Smuzhiyun  * Return:	status code
22*4882a593Smuzhiyun  */
efi_low_alloc_above(unsigned long size,unsigned long align,unsigned long * addr,unsigned long min)23*4882a593Smuzhiyun efi_status_t efi_low_alloc_above(unsigned long size, unsigned long align,
24*4882a593Smuzhiyun 				 unsigned long *addr, unsigned long min)
25*4882a593Smuzhiyun {
26*4882a593Smuzhiyun 	unsigned long map_size, desc_size, buff_size;
27*4882a593Smuzhiyun 	efi_memory_desc_t *map;
28*4882a593Smuzhiyun 	efi_status_t status;
29*4882a593Smuzhiyun 	unsigned long nr_pages;
30*4882a593Smuzhiyun 	int i;
31*4882a593Smuzhiyun 	struct efi_boot_memmap boot_map;
32*4882a593Smuzhiyun 
33*4882a593Smuzhiyun 	boot_map.map		= &map;
34*4882a593Smuzhiyun 	boot_map.map_size	= &map_size;
35*4882a593Smuzhiyun 	boot_map.desc_size	= &desc_size;
36*4882a593Smuzhiyun 	boot_map.desc_ver	= NULL;
37*4882a593Smuzhiyun 	boot_map.key_ptr	= NULL;
38*4882a593Smuzhiyun 	boot_map.buff_size	= &buff_size;
39*4882a593Smuzhiyun 
40*4882a593Smuzhiyun 	status = efi_get_memory_map(&boot_map);
41*4882a593Smuzhiyun 	if (status != EFI_SUCCESS)
42*4882a593Smuzhiyun 		goto fail;
43*4882a593Smuzhiyun 
44*4882a593Smuzhiyun 	/*
45*4882a593Smuzhiyun 	 * Enforce minimum alignment that EFI or Linux requires when
46*4882a593Smuzhiyun 	 * requesting a specific address.  We are doing page-based (or
47*4882a593Smuzhiyun 	 * larger) allocations, and both the address and size must meet
48*4882a593Smuzhiyun 	 * alignment constraints.
49*4882a593Smuzhiyun 	 */
50*4882a593Smuzhiyun 	if (align < EFI_ALLOC_ALIGN)
51*4882a593Smuzhiyun 		align = EFI_ALLOC_ALIGN;
52*4882a593Smuzhiyun 
53*4882a593Smuzhiyun 	size = round_up(size, EFI_ALLOC_ALIGN);
54*4882a593Smuzhiyun 	nr_pages = size / EFI_PAGE_SIZE;
55*4882a593Smuzhiyun 	for (i = 0; i < map_size / desc_size; i++) {
56*4882a593Smuzhiyun 		efi_memory_desc_t *desc;
57*4882a593Smuzhiyun 		unsigned long m = (unsigned long)map;
58*4882a593Smuzhiyun 		u64 start, end;
59*4882a593Smuzhiyun 
60*4882a593Smuzhiyun 		desc = efi_early_memdesc_ptr(m, desc_size, i);
61*4882a593Smuzhiyun 
62*4882a593Smuzhiyun 		if (desc->type != EFI_CONVENTIONAL_MEMORY)
63*4882a593Smuzhiyun 			continue;
64*4882a593Smuzhiyun 
65*4882a593Smuzhiyun 		if (efi_soft_reserve_enabled() &&
66*4882a593Smuzhiyun 		    (desc->attribute & EFI_MEMORY_SP))
67*4882a593Smuzhiyun 			continue;
68*4882a593Smuzhiyun 
69*4882a593Smuzhiyun 		if (desc->num_pages < nr_pages)
70*4882a593Smuzhiyun 			continue;
71*4882a593Smuzhiyun 
72*4882a593Smuzhiyun 		start = desc->phys_addr;
73*4882a593Smuzhiyun 		end = start + desc->num_pages * EFI_PAGE_SIZE;
74*4882a593Smuzhiyun 
75*4882a593Smuzhiyun 		if (start < min)
76*4882a593Smuzhiyun 			start = min;
77*4882a593Smuzhiyun 
78*4882a593Smuzhiyun 		start = round_up(start, align);
79*4882a593Smuzhiyun 		if ((start + size) > end)
80*4882a593Smuzhiyun 			continue;
81*4882a593Smuzhiyun 
82*4882a593Smuzhiyun 		status = efi_bs_call(allocate_pages, EFI_ALLOCATE_ADDRESS,
83*4882a593Smuzhiyun 				     EFI_LOADER_DATA, nr_pages, &start);
84*4882a593Smuzhiyun 		if (status == EFI_SUCCESS) {
85*4882a593Smuzhiyun 			*addr = start;
86*4882a593Smuzhiyun 			break;
87*4882a593Smuzhiyun 		}
88*4882a593Smuzhiyun 	}
89*4882a593Smuzhiyun 
90*4882a593Smuzhiyun 	if (i == map_size / desc_size)
91*4882a593Smuzhiyun 		status = EFI_NOT_FOUND;
92*4882a593Smuzhiyun 
93*4882a593Smuzhiyun 	efi_bs_call(free_pool, map);
94*4882a593Smuzhiyun fail:
95*4882a593Smuzhiyun 	return status;
96*4882a593Smuzhiyun }
97*4882a593Smuzhiyun 
98*4882a593Smuzhiyun /**
99*4882a593Smuzhiyun  * efi_relocate_kernel() - copy memory area
100*4882a593Smuzhiyun  * @image_addr:		pointer to address of memory area to copy
101*4882a593Smuzhiyun  * @image_size:		size of memory area to copy
102*4882a593Smuzhiyun  * @alloc_size:		minimum size of memory to allocate, must be greater or
103*4882a593Smuzhiyun  *			equal to image_size
104*4882a593Smuzhiyun  * @preferred_addr:	preferred target address
105*4882a593Smuzhiyun  * @alignment:		minimum alignment of the allocated memory area. It
106*4882a593Smuzhiyun  *			should be a power of two.
107*4882a593Smuzhiyun  * @min_addr:		minimum target address
108*4882a593Smuzhiyun  *
109*4882a593Smuzhiyun  * Copy a memory area to a newly allocated memory area aligned according
110*4882a593Smuzhiyun  * to @alignment but at least EFI_ALLOC_ALIGN. If the preferred address
111*4882a593Smuzhiyun  * is not available, the allocated address will not be below @min_addr.
112*4882a593Smuzhiyun  * On exit, @image_addr is updated to the target copy address that was used.
113*4882a593Smuzhiyun  *
114*4882a593Smuzhiyun  * This function is used to copy the Linux kernel verbatim. It does not apply
115*4882a593Smuzhiyun  * any relocation changes.
116*4882a593Smuzhiyun  *
117*4882a593Smuzhiyun  * Return:		status code
118*4882a593Smuzhiyun  */
efi_relocate_kernel(unsigned long * image_addr,unsigned long image_size,unsigned long alloc_size,unsigned long preferred_addr,unsigned long alignment,unsigned long min_addr)119*4882a593Smuzhiyun efi_status_t efi_relocate_kernel(unsigned long *image_addr,
120*4882a593Smuzhiyun 				 unsigned long image_size,
121*4882a593Smuzhiyun 				 unsigned long alloc_size,
122*4882a593Smuzhiyun 				 unsigned long preferred_addr,
123*4882a593Smuzhiyun 				 unsigned long alignment,
124*4882a593Smuzhiyun 				 unsigned long min_addr)
125*4882a593Smuzhiyun {
126*4882a593Smuzhiyun 	unsigned long cur_image_addr;
127*4882a593Smuzhiyun 	unsigned long new_addr = 0;
128*4882a593Smuzhiyun 	efi_status_t status;
129*4882a593Smuzhiyun 	unsigned long nr_pages;
130*4882a593Smuzhiyun 	efi_physical_addr_t efi_addr = preferred_addr;
131*4882a593Smuzhiyun 
132*4882a593Smuzhiyun 	if (!image_addr || !image_size || !alloc_size)
133*4882a593Smuzhiyun 		return EFI_INVALID_PARAMETER;
134*4882a593Smuzhiyun 	if (alloc_size < image_size)
135*4882a593Smuzhiyun 		return EFI_INVALID_PARAMETER;
136*4882a593Smuzhiyun 
137*4882a593Smuzhiyun 	cur_image_addr = *image_addr;
138*4882a593Smuzhiyun 
139*4882a593Smuzhiyun 	/*
140*4882a593Smuzhiyun 	 * The EFI firmware loader could have placed the kernel image
141*4882a593Smuzhiyun 	 * anywhere in memory, but the kernel has restrictions on the
142*4882a593Smuzhiyun 	 * max physical address it can run at.  Some architectures
143*4882a593Smuzhiyun 	 * also have a preferred address, so first try to relocate
144*4882a593Smuzhiyun 	 * to the preferred address.  If that fails, allocate as low
145*4882a593Smuzhiyun 	 * as possible while respecting the required alignment.
146*4882a593Smuzhiyun 	 */
147*4882a593Smuzhiyun 	nr_pages = round_up(alloc_size, EFI_ALLOC_ALIGN) / EFI_PAGE_SIZE;
148*4882a593Smuzhiyun 	status = efi_bs_call(allocate_pages, EFI_ALLOCATE_ADDRESS,
149*4882a593Smuzhiyun 			     EFI_LOADER_DATA, nr_pages, &efi_addr);
150*4882a593Smuzhiyun 	new_addr = efi_addr;
151*4882a593Smuzhiyun 	/*
152*4882a593Smuzhiyun 	 * If preferred address allocation failed allocate as low as
153*4882a593Smuzhiyun 	 * possible.
154*4882a593Smuzhiyun 	 */
155*4882a593Smuzhiyun 	if (status != EFI_SUCCESS) {
156*4882a593Smuzhiyun 		status = efi_low_alloc_above(alloc_size, alignment, &new_addr,
157*4882a593Smuzhiyun 					     min_addr);
158*4882a593Smuzhiyun 	}
159*4882a593Smuzhiyun 	if (status != EFI_SUCCESS) {
160*4882a593Smuzhiyun 		efi_err("Failed to allocate usable memory for kernel.\n");
161*4882a593Smuzhiyun 		return status;
162*4882a593Smuzhiyun 	}
163*4882a593Smuzhiyun 
164*4882a593Smuzhiyun 	/*
165*4882a593Smuzhiyun 	 * We know source/dest won't overlap since both memory ranges
166*4882a593Smuzhiyun 	 * have been allocated by UEFI, so we can safely use memcpy.
167*4882a593Smuzhiyun 	 */
168*4882a593Smuzhiyun 	memcpy((void *)new_addr, (void *)cur_image_addr, image_size);
169*4882a593Smuzhiyun 
170*4882a593Smuzhiyun 	/* Return the new address of the relocated image. */
171*4882a593Smuzhiyun 	*image_addr = new_addr;
172*4882a593Smuzhiyun 
173*4882a593Smuzhiyun 	return status;
174*4882a593Smuzhiyun }
175