Commit | Line | Data |
---|---|---|
4febfb8d | 1 | // SPDX-License-Identifier: GPL-2.0 |
263b4a30 RF |
2 | /* |
3 | * FDT related Helper functions used by the EFI stub on multiple | |
4 | * architectures. This should be #included by the EFI stub | |
5 | * implementation files. | |
6 | * | |
7 | * Copyright 2013 Linaro Limited; author Roy Franz | |
263b4a30 RF |
8 | */ |
9 | ||
bd669475 AB |
10 | #include <linux/efi.h> |
11 | #include <linux/libfdt.h> | |
12 | #include <asm/efi.h> | |
13 | ||
f3cdfd23 AB |
14 | #include "efistub.h" |
15 | ||
ae8a442d SG |
16 | #define EFI_DT_ADDR_CELLS_DEFAULT 2 |
17 | #define EFI_DT_SIZE_CELLS_DEFAULT 2 | |
18 | ||
cd33a5c1 | 19 | static void fdt_update_cell_size(void *fdt) |
ae8a442d SG |
20 | { |
21 | int offset; | |
22 | ||
23 | offset = fdt_path_offset(fdt, "/"); | |
24 | /* Set the #address-cells and #size-cells values for an empty tree */ | |
25 | ||
ac9aff8e IM |
26 | fdt_setprop_u32(fdt, offset, "#address-cells", EFI_DT_ADDR_CELLS_DEFAULT); |
27 | fdt_setprop_u32(fdt, offset, "#size-cells", EFI_DT_SIZE_CELLS_DEFAULT); | |
ae8a442d SG |
28 | } |
29 | ||
cd33a5c1 | 30 | static efi_status_t update_fdt(void *orig_fdt, unsigned long orig_fdt_size, |
f4dc7fff | 31 | void *fdt, int new_fdt_size, char *cmdline_ptr) |
263b4a30 | 32 | { |
500899c2 | 33 | int node, num_rsv; |
263b4a30 RF |
34 | int status; |
35 | u32 fdt_val32; | |
36 | u64 fdt_val64; | |
37 | ||
ac9aff8e | 38 | /* Do some checks on provided FDT, if it exists: */ |
263b4a30 RF |
39 | if (orig_fdt) { |
40 | if (fdt_check_header(orig_fdt)) { | |
793473c2 | 41 | efi_err("Device Tree header not valid!\n"); |
263b4a30 RF |
42 | return EFI_LOAD_ERROR; |
43 | } | |
44 | /* | |
45 | * We don't get the size of the FDT if we get if from a | |
ac9aff8e | 46 | * configuration table: |
263b4a30 RF |
47 | */ |
48 | if (orig_fdt_size && fdt_totalsize(orig_fdt) > orig_fdt_size) { | |
793473c2 | 49 | efi_err("Truncated device tree! foo!\n"); |
263b4a30 RF |
50 | return EFI_LOAD_ERROR; |
51 | } | |
52 | } | |
53 | ||
ae8a442d | 54 | if (orig_fdt) { |
263b4a30 | 55 | status = fdt_open_into(orig_fdt, fdt, new_fdt_size); |
ae8a442d | 56 | } else { |
263b4a30 | 57 | status = fdt_create_empty_tree(fdt, new_fdt_size); |
ae8a442d SG |
58 | if (status == 0) { |
59 | /* | |
ac9aff8e IM |
60 | * Any failure from the following function is |
61 | * non-critical: | |
ae8a442d | 62 | */ |
cd33a5c1 | 63 | fdt_update_cell_size(fdt); |
ae8a442d SG |
64 | } |
65 | } | |
263b4a30 RF |
66 | |
67 | if (status != 0) | |
68 | goto fdt_set_fail; | |
69 | ||
0ceac9e0 MS |
70 | /* |
71 | * Delete all memory reserve map entries. When booting via UEFI, | |
72 | * kernel will use the UEFI memory map to find reserved regions. | |
73 | */ | |
74 | num_rsv = fdt_num_mem_rsv(fdt); | |
75 | while (num_rsv-- > 0) | |
76 | fdt_del_mem_rsv(fdt, num_rsv); | |
77 | ||
263b4a30 RF |
78 | node = fdt_subnode_offset(fdt, 0, "chosen"); |
79 | if (node < 0) { | |
80 | node = fdt_add_subnode(fdt, 0, "chosen"); | |
81 | if (node < 0) { | |
ac9aff8e IM |
82 | /* 'node' is an error code when negative: */ |
83 | status = node; | |
263b4a30 RF |
84 | goto fdt_set_fail; |
85 | } | |
86 | } | |
87 | ||
ac9aff8e | 88 | if (cmdline_ptr != NULL && strlen(cmdline_ptr) > 0) { |
263b4a30 RF |
89 | status = fdt_setprop(fdt, node, "bootargs", cmdline_ptr, |
90 | strlen(cmdline_ptr) + 1); | |
91 | if (status) | |
92 | goto fdt_set_fail; | |
93 | } | |
94 | ||
263b4a30 RF |
95 | /* Add FDT entries for EFI runtime services in chosen node. */ |
96 | node = fdt_subnode_offset(fdt, 0, "chosen"); | |
ccc27ae7 | 97 | fdt_val64 = cpu_to_fdt64((u64)(unsigned long)efi_system_table); |
ac9aff8e IM |
98 | |
99 | status = fdt_setprop_var(fdt, node, "linux,uefi-system-table", fdt_val64); | |
263b4a30 RF |
100 | if (status) |
101 | goto fdt_set_fail; | |
102 | ||
abfb7b68 | 103 | fdt_val64 = U64_MAX; /* placeholder */ |
ac9aff8e IM |
104 | |
105 | status = fdt_setprop_var(fdt, node, "linux,uefi-mmap-start", fdt_val64); | |
263b4a30 RF |
106 | if (status) |
107 | goto fdt_set_fail; | |
108 | ||
abfb7b68 | 109 | fdt_val32 = U32_MAX; /* placeholder */ |
ac9aff8e IM |
110 | |
111 | status = fdt_setprop_var(fdt, node, "linux,uefi-mmap-size", fdt_val32); | |
263b4a30 RF |
112 | if (status) |
113 | goto fdt_set_fail; | |
114 | ||
ac9aff8e | 115 | status = fdt_setprop_var(fdt, node, "linux,uefi-mmap-desc-size", fdt_val32); |
263b4a30 RF |
116 | if (status) |
117 | goto fdt_set_fail; | |
118 | ||
ac9aff8e | 119 | status = fdt_setprop_var(fdt, node, "linux,uefi-mmap-desc-ver", fdt_val32); |
263b4a30 RF |
120 | if (status) |
121 | goto fdt_set_fail; | |
122 | ||
d32de913 | 123 | if (IS_ENABLED(CONFIG_RANDOMIZE_BASE) && !efi_nokaslr) { |
2b5fe07a AB |
124 | efi_status_t efi_status; |
125 | ||
cd33a5c1 | 126 | efi_status = efi_get_random_bytes(sizeof(fdt_val64), |
2b5fe07a AB |
127 | (u8 *)&fdt_val64); |
128 | if (efi_status == EFI_SUCCESS) { | |
ac9aff8e | 129 | status = fdt_setprop_var(fdt, node, "kaslr-seed", fdt_val64); |
2b5fe07a AB |
130 | if (status) |
131 | goto fdt_set_fail; | |
2b5fe07a AB |
132 | } |
133 | } | |
72a58a63 | 134 | |
ac9aff8e | 135 | /* Shrink the FDT back to its minimum size: */ |
72a58a63 AB |
136 | fdt_pack(fdt); |
137 | ||
263b4a30 RF |
138 | return EFI_SUCCESS; |
139 | ||
140 | fdt_set_fail: | |
141 | if (status == -FDT_ERR_NOSPACE) | |
142 | return EFI_BUFFER_TOO_SMALL; | |
143 | ||
144 | return EFI_LOAD_ERROR; | |
145 | } | |
146 | ||
abfb7b68 AB |
147 | static efi_status_t update_fdt_memmap(void *fdt, struct efi_boot_memmap *map) |
148 | { | |
149 | int node = fdt_path_offset(fdt, "/chosen"); | |
150 | u64 fdt_val64; | |
151 | u32 fdt_val32; | |
152 | int err; | |
153 | ||
154 | if (node < 0) | |
155 | return EFI_LOAD_ERROR; | |
156 | ||
eab31265 | 157 | fdt_val64 = cpu_to_fdt64((unsigned long)map->map); |
ac9aff8e IM |
158 | |
159 | err = fdt_setprop_inplace_var(fdt, node, "linux,uefi-mmap-start", fdt_val64); | |
abfb7b68 AB |
160 | if (err) |
161 | return EFI_LOAD_ERROR; | |
162 | ||
eab31265 | 163 | fdt_val32 = cpu_to_fdt32(map->map_size); |
ac9aff8e IM |
164 | |
165 | err = fdt_setprop_inplace_var(fdt, node, "linux,uefi-mmap-size", fdt_val32); | |
abfb7b68 AB |
166 | if (err) |
167 | return EFI_LOAD_ERROR; | |
168 | ||
eab31265 | 169 | fdt_val32 = cpu_to_fdt32(map->desc_size); |
ac9aff8e IM |
170 | |
171 | err = fdt_setprop_inplace_var(fdt, node, "linux,uefi-mmap-desc-size", fdt_val32); | |
abfb7b68 AB |
172 | if (err) |
173 | return EFI_LOAD_ERROR; | |
174 | ||
eab31265 | 175 | fdt_val32 = cpu_to_fdt32(map->desc_ver); |
ac9aff8e IM |
176 | |
177 | err = fdt_setprop_inplace_var(fdt, node, "linux,uefi-mmap-desc-ver", fdt_val32); | |
abfb7b68 AB |
178 | if (err) |
179 | return EFI_LOAD_ERROR; | |
180 | ||
181 | return EFI_SUCCESS; | |
182 | } | |
183 | ||
ed9cc156 | 184 | struct exit_boot_struct { |
eab31265 | 185 | struct efi_boot_memmap *boot_memmap; |
ac9aff8e | 186 | efi_memory_desc_t *runtime_map; |
f80d2604 | 187 | int runtime_entry_count; |
ac9aff8e | 188 | void *new_fdt_addr; |
ed9cc156 JH |
189 | }; |
190 | ||
eab31265 | 191 | static efi_status_t exit_boot_func(struct efi_boot_memmap *map, void *priv) |
ed9cc156 JH |
192 | { |
193 | struct exit_boot_struct *p = priv; | |
eab31265 AB |
194 | |
195 | p->boot_memmap = map; | |
196 | ||
ed9cc156 JH |
197 | /* |
198 | * Update the memory map with virtual addresses. The function will also | |
199 | * populate @runtime_map with copies of just the EFI_MEMORY_RUNTIME | |
200 | * entries so that we can pass it straight to SetVirtualAddressMap() | |
201 | */ | |
eab31265 | 202 | efi_get_virtmap(map->map, map->map_size, map->desc_size, |
f80d2604 | 203 | p->runtime_map, &p->runtime_entry_count); |
ed9cc156 | 204 | |
c8f325a5 | 205 | return update_fdt_memmap(p->new_fdt_addr, map); |
ed9cc156 JH |
206 | } |
207 | ||
24d7c494 | 208 | #ifndef MAX_FDT_SIZE |
ac9aff8e | 209 | # define MAX_FDT_SIZE SZ_2M |
24d7c494 AB |
210 | #endif |
211 | ||
263b4a30 | 212 | /* |
f4dc7fff AB |
213 | * Allocate memory for a new FDT, then add EFI and commandline related fields |
214 | * to the FDT. This routine increases the FDT allocation size until the | |
215 | * allocated memory is large enough. EFI allocations are in EFI_PAGE_SIZE | |
216 | * granules, which are fixed at 4K bytes, so in most cases the first allocation | |
217 | * should succeed. EFI boot services are exited at the end of this function. | |
218 | * There must be no allocations between the get_memory_map() call and the | |
219 | * exit_boot_services() call, so the exiting of boot services is very tightly | |
220 | * tied to the creation of the FDT with the final memory map in it. | |
263b4a30 | 221 | */ |
4fc8e738 | 222 | static |
cd33a5c1 | 223 | efi_status_t allocate_new_fdt_and_exit_boot(void *handle, |
4fc8e738 | 224 | efi_loaded_image_t *image, |
263b4a30 | 225 | unsigned long *new_fdt_addr, |
4fc8e738 | 226 | char *cmdline_ptr) |
263b4a30 | 227 | { |
eab31265 | 228 | unsigned long desc_size; |
263b4a30 | 229 | u32 desc_ver; |
263b4a30 | 230 | efi_status_t status; |
ed9cc156 | 231 | struct exit_boot_struct priv; |
4fc8e738 AB |
232 | unsigned long fdt_addr = 0; |
233 | unsigned long fdt_size = 0; | |
dadb57ab | 234 | |
f80d2604 AB |
235 | if (!efi_novamap) { |
236 | status = efi_alloc_virtmap(&priv.runtime_map, &desc_size, | |
237 | &desc_ver); | |
238 | if (status != EFI_SUCCESS) { | |
239 | efi_err("Unable to retrieve UEFI memory map.\n"); | |
240 | return status; | |
241 | } | |
f3cdfd23 AB |
242 | } |
243 | ||
4fc8e738 AB |
244 | /* |
245 | * Unauthenticated device tree data is a security hazard, so ignore | |
246 | * 'dtb=' unless UEFI Secure Boot is disabled. We assume that secure | |
247 | * boot is enabled if we can't determine its state. | |
248 | */ | |
249 | if (!IS_ENABLED(CONFIG_EFI_ARMSTUB_DTB_LOADER) || | |
250 | efi_get_secureboot() != efi_secureboot_mode_disabled) { | |
251 | if (strstr(cmdline_ptr, "dtb=")) | |
252 | efi_err("Ignoring DTB from command line.\n"); | |
253 | } else { | |
254 | status = efi_load_dtb(image, &fdt_addr, &fdt_size); | |
255 | ||
256 | if (status != EFI_SUCCESS && status != EFI_NOT_READY) { | |
257 | efi_err("Failed to load device tree!\n"); | |
258 | goto fail; | |
259 | } | |
260 | } | |
261 | ||
262 | if (fdt_addr) { | |
263 | efi_info("Using DTB from command line\n"); | |
264 | } else { | |
265 | /* Look for a device tree configuration table entry. */ | |
266 | fdt_addr = (uintptr_t)get_fdt(&fdt_size); | |
267 | if (fdt_addr) | |
268 | efi_info("Using DTB from configuration table\n"); | |
269 | } | |
270 | ||
271 | if (!fdt_addr) | |
272 | efi_info("Generating empty DTB\n"); | |
273 | ||
68c9cdf3 | 274 | efi_info("Exiting boot services...\n"); |
263b4a30 | 275 | |
54649911 | 276 | status = efi_allocate_pages(MAX_FDT_SIZE, new_fdt_addr, ULONG_MAX); |
24d7c494 | 277 | if (status != EFI_SUCCESS) { |
793473c2 | 278 | efi_err("Unable to allocate memory for new device tree.\n"); |
24d7c494 AB |
279 | goto fail; |
280 | } | |
281 | ||
cd33a5c1 | 282 | status = update_fdt((void *)fdt_addr, fdt_size, |
f4dc7fff | 283 | (void *)*new_fdt_addr, MAX_FDT_SIZE, cmdline_ptr); |
263b4a30 | 284 | |
24d7c494 | 285 | if (status != EFI_SUCCESS) { |
793473c2 | 286 | efi_err("Unable to construct new device tree.\n"); |
24d7c494 | 287 | goto fail_free_new_fdt; |
263b4a30 RF |
288 | } |
289 | ||
f80d2604 | 290 | priv.new_fdt_addr = (void *)*new_fdt_addr; |
ac9aff8e | 291 | |
eab31265 | 292 | status = efi_exit_boot_services(handle, &priv, exit_boot_func); |
263b4a30 | 293 | |
f3cdfd23 AB |
294 | if (status == EFI_SUCCESS) { |
295 | efi_set_virtual_address_map_t *svam; | |
263b4a30 | 296 | |
980771f6 | 297 | if (efi_novamap) |
4e46c2a9 AB |
298 | return EFI_SUCCESS; |
299 | ||
f3cdfd23 | 300 | /* Install the new virtual address map */ |
ccc27ae7 | 301 | svam = efi_system_table->runtime->set_virtual_address_map; |
f80d2604 AB |
302 | status = svam(priv.runtime_entry_count * desc_size, desc_size, |
303 | desc_ver, priv.runtime_map); | |
f3cdfd23 AB |
304 | |
305 | /* | |
306 | * We are beyond the point of no return here, so if the call to | |
307 | * SetVirtualAddressMap() failed, we need to signal that to the | |
308 | * incoming kernel but proceed normally otherwise. | |
309 | */ | |
310 | if (status != EFI_SUCCESS) { | |
eab31265 | 311 | efi_memory_desc_t *p; |
f3cdfd23 AB |
312 | int l; |
313 | ||
314 | /* | |
315 | * Set the virtual address field of all | |
37926f96 AB |
316 | * EFI_MEMORY_RUNTIME entries to U64_MAX. This will |
317 | * signal the incoming kernel that no virtual | |
318 | * translation has been installed. | |
f3cdfd23 | 319 | */ |
eab31265 AB |
320 | for (l = 0; l < priv.boot_memmap->map_size; |
321 | l += priv.boot_memmap->desc_size) { | |
322 | p = (void *)priv.boot_memmap->map + l; | |
f3cdfd23 AB |
323 | |
324 | if (p->attribute & EFI_MEMORY_RUNTIME) | |
37926f96 | 325 | p->virt_addr = U64_MAX; |
f3cdfd23 AB |
326 | } |
327 | } | |
328 | return EFI_SUCCESS; | |
329 | } | |
263b4a30 | 330 | |
793473c2 | 331 | efi_err("Exit boot services failed.\n"); |
263b4a30 | 332 | |
263b4a30 | 333 | fail_free_new_fdt: |
cd33a5c1 | 334 | efi_free(MAX_FDT_SIZE, *new_fdt_addr); |
263b4a30 RF |
335 | |
336 | fail: | |
4fc8e738 | 337 | efi_free(fdt_size, fdt_addr); |
4b2543f7 HH |
338 | if (!efi_novamap) |
339 | efi_bs_call(free_pool, priv.runtime_map); | |
ac9aff8e | 340 | |
263b4a30 RF |
341 | return EFI_LOAD_ERROR; |
342 | } | |
343 | ||
4fc8e738 AB |
344 | efi_status_t efi_boot_kernel(void *handle, efi_loaded_image_t *image, |
345 | unsigned long kernel_addr, char *cmdline_ptr) | |
346 | { | |
347 | unsigned long fdt_addr; | |
348 | efi_status_t status; | |
349 | ||
350 | status = allocate_new_fdt_and_exit_boot(handle, image, &fdt_addr, | |
351 | cmdline_ptr); | |
352 | if (status != EFI_SUCCESS) { | |
353 | efi_err("Failed to update FDT and exit boot services\n"); | |
354 | return status; | |
355 | } | |
356 | ||
357 | if (IS_ENABLED(CONFIG_ARM)) | |
358 | efi_handle_post_ebs_state(); | |
359 | ||
360 | efi_enter_kernel(kernel_addr, fdt_addr, fdt_totalsize((void *)fdt_addr)); | |
361 | /* not reached */ | |
362 | } | |
363 | ||
cd33a5c1 | 364 | void *get_fdt(unsigned long *fdt_size) |
263b4a30 | 365 | { |
82d736ac | 366 | void *fdt; |
263b4a30 | 367 | |
cd33a5c1 | 368 | fdt = get_efi_config_table(DEVICE_TREE_GUID); |
263b4a30 | 369 | |
82d736ac MG |
370 | if (!fdt) |
371 | return NULL; | |
8c25db0a | 372 | |
82d736ac | 373 | if (fdt_check_header(fdt) != 0) { |
793473c2 | 374 | efi_err("Invalid header detected on UEFI supplied FDT, ignoring ...\n"); |
82d736ac | 375 | return NULL; |
6935b3c4 | 376 | } |
82d736ac MG |
377 | *fdt_size = fdt_totalsize(fdt); |
378 | return fdt; | |
263b4a30 | 379 | } |