xref: /openbmc/linux/drivers/firmware/efi/libstub/mem.c (revision e7ea37b0)
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 #define EFI_MMAP_NR_SLACK_SLOTS	8
9 
10 static inline bool mmap_has_headroom(unsigned long buff_size,
11 				     unsigned long map_size,
12 				     unsigned long desc_size)
13 {
14 	unsigned long slack = buff_size - map_size;
15 
16 	return slack / desc_size >= EFI_MMAP_NR_SLACK_SLOTS;
17 }
18 
19 efi_status_t efi_get_memory_map(struct efi_boot_memmap *map)
20 {
21 	efi_memory_desc_t *m = NULL;
22 	efi_status_t status;
23 	unsigned long key;
24 	u32 desc_version;
25 
26 	*map->desc_size =	sizeof(*m);
27 	*map->map_size =	*map->desc_size * 32;
28 	*map->buff_size =	*map->map_size;
29 again:
30 	status = efi_bs_call(allocate_pool, EFI_LOADER_DATA,
31 			     *map->map_size, (void **)&m);
32 	if (status != EFI_SUCCESS)
33 		goto fail;
34 
35 	*map->desc_size = 0;
36 	key = 0;
37 	status = efi_bs_call(get_memory_map, map->map_size, m,
38 			     &key, map->desc_size, &desc_version);
39 	if (status == EFI_BUFFER_TOO_SMALL ||
40 	    !mmap_has_headroom(*map->buff_size, *map->map_size,
41 			       *map->desc_size)) {
42 		efi_bs_call(free_pool, m);
43 		/*
44 		 * Make sure there is some entries of headroom so that the
45 		 * buffer can be reused for a new map after allocations are
46 		 * no longer permitted.  Its unlikely that the map will grow to
47 		 * exceed this headroom once we are ready to trigger
48 		 * ExitBootServices()
49 		 */
50 		*map->map_size += *map->desc_size * EFI_MMAP_NR_SLACK_SLOTS;
51 		*map->buff_size = *map->map_size;
52 		goto again;
53 	}
54 
55 	if (status == EFI_SUCCESS) {
56 		if (map->key_ptr)
57 			*map->key_ptr = key;
58 		if (map->desc_ver)
59 			*map->desc_ver = desc_version;
60 	} else {
61 		efi_bs_call(free_pool, m);
62 	}
63 
64 fail:
65 	*map->map = m;
66 	return status;
67 }
68 
69 /**
70  * efi_allocate_pages() - Allocate memory pages
71  * @size:	minimum number of bytes to allocate
72  * @addr:	On return the address of the first allocated page. The first
73  *		allocated page has alignment EFI_ALLOC_ALIGN which is an
74  *		architecture dependent multiple of the page size.
75  * @max:	the address that the last allocated memory page shall not
76  *		exceed
77  *
78  * Allocate pages as EFI_LOADER_DATA. The allocated pages are aligned according
79  * to EFI_ALLOC_ALIGN. The last allocated page will not exceed the address
80  * given by @max.
81  *
82  * Return:	status code
83  */
84 efi_status_t efi_allocate_pages(unsigned long size, unsigned long *addr,
85 				unsigned long max)
86 {
87 	efi_physical_addr_t alloc_addr = ALIGN_DOWN(max + 1, EFI_ALLOC_ALIGN) - 1;
88 	int slack = EFI_ALLOC_ALIGN / EFI_PAGE_SIZE - 1;
89 	efi_status_t status;
90 
91 	size = round_up(size, EFI_ALLOC_ALIGN);
92 	status = efi_bs_call(allocate_pages, EFI_ALLOCATE_MAX_ADDRESS,
93 			     EFI_LOADER_DATA, size / EFI_PAGE_SIZE + slack,
94 			     &alloc_addr);
95 	if (status != EFI_SUCCESS)
96 		return status;
97 
98 	*addr = ALIGN((unsigned long)alloc_addr, EFI_ALLOC_ALIGN);
99 
100 	if (slack > 0) {
101 		int l = (alloc_addr % EFI_ALLOC_ALIGN) / EFI_PAGE_SIZE;
102 
103 		if (l) {
104 			efi_bs_call(free_pages, alloc_addr, slack - l + 1);
105 			slack = l - 1;
106 		}
107 		if (slack)
108 			efi_bs_call(free_pages, *addr + size, slack);
109 	}
110 	return EFI_SUCCESS;
111 }
112 /*
113  * Allocate at the lowest possible address that is not below 'min'.
114  */
115 efi_status_t efi_low_alloc_above(unsigned long size, unsigned long align,
116 				 unsigned long *addr, unsigned long min)
117 {
118 	unsigned long map_size, desc_size, buff_size;
119 	efi_memory_desc_t *map;
120 	efi_status_t status;
121 	unsigned long nr_pages;
122 	int i;
123 	struct efi_boot_memmap boot_map;
124 
125 	boot_map.map		= &map;
126 	boot_map.map_size	= &map_size;
127 	boot_map.desc_size	= &desc_size;
128 	boot_map.desc_ver	= NULL;
129 	boot_map.key_ptr	= NULL;
130 	boot_map.buff_size	= &buff_size;
131 
132 	status = efi_get_memory_map(&boot_map);
133 	if (status != EFI_SUCCESS)
134 		goto fail;
135 
136 	/*
137 	 * Enforce minimum alignment that EFI or Linux requires when
138 	 * requesting a specific address.  We are doing page-based (or
139 	 * larger) allocations, and both the address and size must meet
140 	 * alignment constraints.
141 	 */
142 	if (align < EFI_ALLOC_ALIGN)
143 		align = EFI_ALLOC_ALIGN;
144 
145 	size = round_up(size, EFI_ALLOC_ALIGN);
146 	nr_pages = size / EFI_PAGE_SIZE;
147 	for (i = 0; i < map_size / desc_size; i++) {
148 		efi_memory_desc_t *desc;
149 		unsigned long m = (unsigned long)map;
150 		u64 start, end;
151 
152 		desc = efi_early_memdesc_ptr(m, desc_size, i);
153 
154 		if (desc->type != EFI_CONVENTIONAL_MEMORY)
155 			continue;
156 
157 		if (efi_soft_reserve_enabled() &&
158 		    (desc->attribute & EFI_MEMORY_SP))
159 			continue;
160 
161 		if (desc->num_pages < nr_pages)
162 			continue;
163 
164 		start = desc->phys_addr;
165 		end = start + desc->num_pages * EFI_PAGE_SIZE;
166 
167 		if (start < min)
168 			start = min;
169 
170 		start = round_up(start, align);
171 		if ((start + size) > end)
172 			continue;
173 
174 		status = efi_bs_call(allocate_pages, EFI_ALLOCATE_ADDRESS,
175 				     EFI_LOADER_DATA, nr_pages, &start);
176 		if (status == EFI_SUCCESS) {
177 			*addr = start;
178 			break;
179 		}
180 	}
181 
182 	if (i == map_size / desc_size)
183 		status = EFI_NOT_FOUND;
184 
185 	efi_bs_call(free_pool, map);
186 fail:
187 	return status;
188 }
189 
190 void efi_free(unsigned long size, unsigned long addr)
191 {
192 	unsigned long nr_pages;
193 
194 	if (!size)
195 		return;
196 
197 	nr_pages = round_up(size, EFI_ALLOC_ALIGN) / EFI_PAGE_SIZE;
198 	efi_bs_call(free_pages, addr, nr_pages);
199 }
200 
201 /*
202  * Relocate a kernel image, either compressed or uncompressed.
203  * In the ARM64 case, all kernel images are currently
204  * uncompressed, and as such when we relocate it we need to
205  * allocate additional space for the BSS segment. Any low
206  * memory that this function should avoid needs to be
207  * unavailable in the EFI memory map, as if the preferred
208  * address is not available the lowest available address will
209  * be used.
210  */
211 efi_status_t efi_relocate_kernel(unsigned long *image_addr,
212 				 unsigned long image_size,
213 				 unsigned long alloc_size,
214 				 unsigned long preferred_addr,
215 				 unsigned long alignment,
216 				 unsigned long min_addr)
217 {
218 	unsigned long cur_image_addr;
219 	unsigned long new_addr = 0;
220 	efi_status_t status;
221 	unsigned long nr_pages;
222 	efi_physical_addr_t efi_addr = preferred_addr;
223 
224 	if (!image_addr || !image_size || !alloc_size)
225 		return EFI_INVALID_PARAMETER;
226 	if (alloc_size < image_size)
227 		return EFI_INVALID_PARAMETER;
228 
229 	cur_image_addr = *image_addr;
230 
231 	/*
232 	 * The EFI firmware loader could have placed the kernel image
233 	 * anywhere in memory, but the kernel has restrictions on the
234 	 * max physical address it can run at.  Some architectures
235 	 * also have a prefered address, so first try to relocate
236 	 * to the preferred address.  If that fails, allocate as low
237 	 * as possible while respecting the required alignment.
238 	 */
239 	nr_pages = round_up(alloc_size, EFI_ALLOC_ALIGN) / EFI_PAGE_SIZE;
240 	status = efi_bs_call(allocate_pages, EFI_ALLOCATE_ADDRESS,
241 			     EFI_LOADER_DATA, nr_pages, &efi_addr);
242 	new_addr = efi_addr;
243 	/*
244 	 * If preferred address allocation failed allocate as low as
245 	 * possible.
246 	 */
247 	if (status != EFI_SUCCESS) {
248 		status = efi_low_alloc_above(alloc_size, alignment, &new_addr,
249 					     min_addr);
250 	}
251 	if (status != EFI_SUCCESS) {
252 		pr_efi_err("Failed to allocate usable memory for kernel.\n");
253 		return status;
254 	}
255 
256 	/*
257 	 * We know source/dest won't overlap since both memory ranges
258 	 * have been allocated by UEFI, so we can safely use memcpy.
259 	 */
260 	memcpy((void *)new_addr, (void *)cur_image_addr, image_size);
261 
262 	/* Return the new address of the relocated image. */
263 	*image_addr = new_addr;
264 
265 	return status;
266 }
267