/src/u-boot/lib/efi_loader/efi_helper.c
Line | Count | Source |
1 | | // SPDX-License-Identifier: GPL-2.0+ |
2 | | /* |
3 | | * Copyright (c) 2020, Linaro Limited |
4 | | */ |
5 | | |
6 | | #define LOG_CATEGORY LOGC_EFI |
7 | | |
8 | | #include <blkmap.h> |
9 | | #include <bootm.h> |
10 | | #include <efi_device_path.h> |
11 | | #include <env.h> |
12 | | #include <image.h> |
13 | | #include <log.h> |
14 | | #include <malloc.h> |
15 | | #include <mapmem.h> |
16 | | #include <dm.h> |
17 | | #include <fs.h> |
18 | | #include <efi.h> |
19 | | #include <efi_api.h> |
20 | | #include <efi_load_initrd.h> |
21 | | #include <efi_loader.h> |
22 | | #include <efi_variable.h> |
23 | | #include <host_arch.h> |
24 | | #include <linux/libfdt.h> |
25 | | #include <linux/list.h> |
26 | | |
27 | | #undef BOOTEFI_NAME |
28 | | |
29 | | #if HOST_ARCH == HOST_ARCH_X86_64 |
30 | 0 | #define HOST_BOOTEFI_NAME "BOOTX64.EFI" |
31 | 0 | #define HOST_PXE_ARCH 0x6 |
32 | | #elif HOST_ARCH == HOST_ARCH_X86 |
33 | | #define HOST_BOOTEFI_NAME "BOOTIA32.EFI" |
34 | | #define HOST_PXE_ARCH 0x7 |
35 | | #elif HOST_ARCH == HOST_ARCH_AARCH64 |
36 | | #define HOST_BOOTEFI_NAME "BOOTAA64.EFI" |
37 | | #define HOST_PXE_ARCH 0xb |
38 | | #elif HOST_ARCH == HOST_ARCH_ARM |
39 | | #define HOST_BOOTEFI_NAME "BOOTARM.EFI" |
40 | | #define HOST_PXE_ARCH 0xa |
41 | | #elif HOST_ARCH == HOST_ARCH_RISCV32 |
42 | | #define HOST_BOOTEFI_NAME "BOOTRISCV32.EFI" |
43 | | #define HOST_PXE_ARCH 0x19 |
44 | | #elif HOST_ARCH == HOST_ARCH_RISCV64 |
45 | | #define HOST_BOOTEFI_NAME "BOOTRISCV64.EFI" |
46 | | #define HOST_PXE_ARCH 0x1b |
47 | | #else |
48 | | #error Unsupported Host architecture |
49 | | #endif |
50 | | |
51 | | #if defined(CONFIG_SANDBOX) |
52 | 0 | #define BOOTEFI_NAME "BOOTSBOX.EFI" |
53 | | #elif defined(CONFIG_ARM64) |
54 | | #define BOOTEFI_NAME "BOOTAA64.EFI" |
55 | | #elif defined(CONFIG_ARM) |
56 | | #define BOOTEFI_NAME "BOOTARM.EFI" |
57 | | #elif defined(CONFIG_X86_64) |
58 | | #define BOOTEFI_NAME "BOOTX64.EFI" |
59 | | #elif defined(CONFIG_X86) |
60 | | #define BOOTEFI_NAME "BOOTIA32.EFI" |
61 | | #elif defined(CONFIG_ARCH_RV32I) |
62 | | #define BOOTEFI_NAME "BOOTRISCV32.EFI" |
63 | | #elif defined(CONFIG_ARCH_RV64I) |
64 | | #define BOOTEFI_NAME "BOOTRISCV64.EFI" |
65 | | #else |
66 | | #error Unsupported UEFI architecture |
67 | | #endif |
68 | | |
69 | | #if defined(CONFIG_CMD_EFIDEBUG) || defined(CONFIG_EFI_LOAD_FILE2_INITRD) |
70 | | /* GUID used by Linux to identify the LoadFile2 protocol with the initrd */ |
71 | | const efi_guid_t efi_lf2_initrd_guid = EFI_INITRD_MEDIA_GUID; |
72 | | #endif |
73 | | |
74 | | const char *efi_get_basename(void) |
75 | 0 | { |
76 | 0 | return efi_use_host_arch() ? HOST_BOOTEFI_NAME : BOOTEFI_NAME; |
77 | 0 | } |
78 | | |
79 | | int efi_get_pxe_arch(void) |
80 | 0 | { |
81 | 0 | if (efi_use_host_arch()) |
82 | 0 | return HOST_PXE_ARCH; |
83 | | |
84 | | /* http://www.iana.org/assignments/dhcpv6-parameters/dhcpv6-parameters.xml */ |
85 | 0 | if (IS_ENABLED(CONFIG_ARM64)) |
86 | 0 | return 0xb; |
87 | 0 | else if (IS_ENABLED(CONFIG_ARM)) |
88 | 0 | return 0xa; |
89 | 0 | else if (IS_ENABLED(CONFIG_X86_64)) |
90 | 0 | return 0x6; |
91 | 0 | else if (IS_ENABLED(CONFIG_X86)) |
92 | 0 | return 0x7; |
93 | 0 | else if (IS_ENABLED(CONFIG_ARCH_RV32I)) |
94 | 0 | return 0x19; |
95 | 0 | else if (IS_ENABLED(CONFIG_ARCH_RV64I)) |
96 | 0 | return 0x1b; |
97 | | |
98 | 0 | return -EINVAL; |
99 | 0 | } |
100 | | |
101 | | /** |
102 | | * efi_create_current_boot_var() - Return Boot#### name were #### is replaced by |
103 | | * the value of BootCurrent |
104 | | * |
105 | | * @var_name: variable name |
106 | | * @var_name_size: size of var_name |
107 | | * |
108 | | * Return: Status code |
109 | | */ |
110 | | static efi_status_t efi_create_current_boot_var(u16 var_name[], |
111 | | size_t var_name_size) |
112 | 0 | { |
113 | 0 | efi_uintn_t boot_current_size; |
114 | 0 | efi_status_t ret; |
115 | 0 | u16 boot_current; |
116 | 0 | u16 *pos; |
117 | |
|
118 | 0 | boot_current_size = sizeof(boot_current); |
119 | 0 | ret = efi_get_variable_int(u"BootCurrent", |
120 | 0 | &efi_global_variable_guid, NULL, |
121 | 0 | &boot_current_size, &boot_current, NULL); |
122 | 0 | if (ret != EFI_SUCCESS) |
123 | 0 | goto out; |
124 | | |
125 | 0 | pos = efi_create_indexed_name(var_name, var_name_size, "Boot", |
126 | 0 | boot_current); |
127 | 0 | if (!pos) { |
128 | 0 | ret = EFI_OUT_OF_RESOURCES; |
129 | 0 | goto out; |
130 | 0 | } |
131 | | |
132 | 0 | out: |
133 | 0 | return ret; |
134 | 0 | } |
135 | | |
136 | | /** |
137 | | * efi_get_dp_from_boot() - Retrieve and return a device path from an EFI |
138 | | * Boot### variable. |
139 | | * A boot option may contain an array of device paths. |
140 | | * We use a VenMedia() with a specific GUID to identify |
141 | | * the usage of the array members. This function is |
142 | | * used to extract a specific device path |
143 | | * |
144 | | * @guid: vendor GUID of the VenMedia() device path node identifying the |
145 | | * device path |
146 | | * |
147 | | * Return: device path or NULL. Caller must free the returned value |
148 | | */ |
149 | | struct efi_device_path *efi_get_dp_from_boot(const efi_guid_t *guid) |
150 | 0 | { |
151 | 0 | struct efi_device_path *file_path = NULL; |
152 | 0 | struct efi_load_option lo; |
153 | 0 | void *var_value; |
154 | 0 | efi_uintn_t size; |
155 | 0 | efi_status_t ret; |
156 | 0 | u16 var_name[16]; |
157 | |
|
158 | 0 | ret = efi_create_current_boot_var(var_name, sizeof(var_name)); |
159 | 0 | if (ret != EFI_SUCCESS) |
160 | 0 | return NULL; |
161 | | |
162 | 0 | var_value = efi_get_var(var_name, &efi_global_variable_guid, &size); |
163 | 0 | if (!var_value) |
164 | 0 | return NULL; |
165 | | |
166 | 0 | ret = efi_deserialize_load_option(&lo, var_value, &size); |
167 | 0 | if (ret != EFI_SUCCESS) |
168 | 0 | goto err; |
169 | | |
170 | 0 | file_path = efi_dp_from_lo(&lo, guid); |
171 | |
|
172 | 0 | err: |
173 | 0 | free(var_value); |
174 | 0 | return file_path; |
175 | 0 | } |
176 | | |
177 | | /** |
178 | | * efi_load_option_dp_join() - join device-paths for load option |
179 | | * |
180 | | * @dp: in: binary device-path, out: joined device-path |
181 | | * @dp_size: size of joined device-path |
182 | | * @initrd_dp: initrd device-path or NULL |
183 | | * @fdt_dp: device-tree device-path or NULL |
184 | | * Return: status_code |
185 | | */ |
186 | | efi_status_t efi_load_option_dp_join(struct efi_device_path **dp, |
187 | | size_t *dp_size, |
188 | | struct efi_device_path *initrd_dp, |
189 | | struct efi_device_path *fdt_dp) |
190 | 0 | { |
191 | 0 | if (!dp) |
192 | 0 | return EFI_INVALID_PARAMETER; |
193 | | |
194 | 0 | *dp_size = efi_dp_size(*dp); |
195 | |
|
196 | 0 | if (initrd_dp) { |
197 | 0 | struct efi_device_path *tmp_dp = *dp; |
198 | |
|
199 | 0 | *dp = efi_dp_concat(tmp_dp, initrd_dp, *dp_size); |
200 | 0 | efi_free_pool(tmp_dp); |
201 | 0 | if (!*dp) |
202 | 0 | return EFI_OUT_OF_RESOURCES; |
203 | 0 | *dp_size += efi_dp_size(initrd_dp) + sizeof(EFI_DP_END); |
204 | 0 | } |
205 | | |
206 | 0 | if (fdt_dp) { |
207 | 0 | struct efi_device_path *tmp_dp = *dp; |
208 | |
|
209 | 0 | *dp = efi_dp_concat(tmp_dp, fdt_dp, *dp_size); |
210 | 0 | efi_free_pool(tmp_dp); |
211 | 0 | if (!*dp) |
212 | 0 | return EFI_OUT_OF_RESOURCES; |
213 | 0 | *dp_size += efi_dp_size(fdt_dp) + sizeof(EFI_DP_END); |
214 | 0 | } |
215 | | |
216 | 0 | *dp_size += sizeof(EFI_DP_END); |
217 | |
|
218 | 0 | return EFI_SUCCESS; |
219 | 0 | } |
220 | | |
221 | | const struct guid_to_hash_map { |
222 | | efi_guid_t guid; |
223 | | const char algo[32]; |
224 | | u32 bits; |
225 | | } guid_to_hash[] = { |
226 | | { |
227 | | EFI_CERT_X509_SHA256_GUID, |
228 | | "sha256", |
229 | | SHA256_SUM_LEN * 8, |
230 | | }, |
231 | | { |
232 | | EFI_CERT_SHA256_GUID, |
233 | | "sha256", |
234 | | SHA256_SUM_LEN * 8, |
235 | | }, |
236 | | { |
237 | | EFI_CERT_X509_SHA384_GUID, |
238 | | "sha384", |
239 | | SHA384_SUM_LEN * 8, |
240 | | }, |
241 | | { |
242 | | EFI_CERT_X509_SHA512_GUID, |
243 | | "sha512", |
244 | | SHA512_SUM_LEN * 8, |
245 | | }, |
246 | | }; |
247 | | |
248 | 0 | #define MAX_GUID_TO_HASH_COUNT ARRAY_SIZE(guid_to_hash) |
249 | | |
250 | | /** guid_to_sha_str - return the sha string e.g "sha256" for a given guid |
251 | | * used on EFI security databases |
252 | | * |
253 | | * @guid: guid to check |
254 | | * |
255 | | * Return: len or 0 if no match is found |
256 | | */ |
257 | | const char *guid_to_sha_str(const efi_guid_t *guid) |
258 | 0 | { |
259 | 0 | size_t i; |
260 | |
|
261 | 0 | for (i = 0; i < MAX_GUID_TO_HASH_COUNT; i++) { |
262 | 0 | if (!guidcmp(guid, &guid_to_hash[i].guid)) |
263 | 0 | return guid_to_hash[i].algo; |
264 | 0 | } |
265 | | |
266 | 0 | return NULL; |
267 | 0 | } |
268 | | |
269 | | /** algo_to_len - return the sha size in bytes for a given string |
270 | | * |
271 | | * @algo: string indicating hashing algorithm to check |
272 | | * |
273 | | * Return: length of hash in bytes or 0 if no match is found |
274 | | */ |
275 | | int algo_to_len(const char *algo) |
276 | 0 | { |
277 | 0 | size_t i; |
278 | |
|
279 | 0 | for (i = 0; i < MAX_GUID_TO_HASH_COUNT; i++) { |
280 | 0 | if (!strcmp(algo, guid_to_hash[i].algo)) |
281 | 0 | return guid_to_hash[i].bits / 8; |
282 | 0 | } |
283 | | |
284 | 0 | return 0; |
285 | 0 | } |
286 | | |
287 | | /** efi_link_dev - link the efi_handle_t and udevice |
288 | | * |
289 | | * @handle: efi handle to associate with udevice |
290 | | * @dev: udevice to associate with efi handle |
291 | | * |
292 | | * Return: 0 on success, negative on failure |
293 | | */ |
294 | | int efi_link_dev(efi_handle_t handle, struct udevice *dev) |
295 | 0 | { |
296 | 0 | handle->dev = dev; |
297 | 0 | return dev_tag_set_ptr(dev, DM_TAG_EFI, handle); |
298 | 0 | } |
299 | | |
300 | | /** |
301 | | * efi_unlink_dev() - unlink udevice and handle |
302 | | * |
303 | | * @handle: EFI handle to unlink |
304 | | * |
305 | | * Return: 0 on success, negative on failure |
306 | | */ |
307 | | int efi_unlink_dev(efi_handle_t handle) |
308 | 0 | { |
309 | 0 | int ret; |
310 | |
|
311 | 0 | ret = dev_tag_del(handle->dev, DM_TAG_EFI); |
312 | 0 | if (ret) |
313 | 0 | return ret; |
314 | 0 | handle->dev = NULL; |
315 | |
|
316 | 0 | return 0; |
317 | 0 | } |
318 | | |
319 | | static int u16_tohex(u16 c) |
320 | 0 | { |
321 | 0 | if (c >= '0' && c <= '9') |
322 | 0 | return c - '0'; |
323 | 0 | if (c >= 'A' && c <= 'F') |
324 | 0 | return c - 'A' + 10; |
325 | | |
326 | | /* not hexadecimal */ |
327 | 0 | return -1; |
328 | 0 | } |
329 | | |
330 | | bool efi_varname_is_load_option(u16 *var_name16, int *index) |
331 | 0 | { |
332 | 0 | int id, i, digit; |
333 | |
|
334 | 0 | if (memcmp(var_name16, u"Boot", 8)) |
335 | 0 | return false; |
336 | | |
337 | 0 | for (id = 0, i = 0; i < 4; i++) { |
338 | 0 | digit = u16_tohex(var_name16[4 + i]); |
339 | 0 | if (digit < 0) |
340 | 0 | break; |
341 | 0 | id = (id << 4) + digit; |
342 | 0 | } |
343 | 0 | if (i == 4 && !var_name16[8]) { |
344 | 0 | if (index) |
345 | 0 | *index = id; |
346 | 0 | return true; |
347 | 0 | } |
348 | | |
349 | 0 | return false; |
350 | 0 | } |
351 | | |
352 | | /** |
353 | | * efi_next_variable_name() - get next variable name |
354 | | * |
355 | | * This function is a wrapper of efi_get_next_variable_name_int(). |
356 | | * If efi_get_next_variable_name_int() returns EFI_BUFFER_TOO_SMALL, |
357 | | * @size and @buf are updated by new buffer size and realloced buffer. |
358 | | * |
359 | | * @size: pointer to the buffer size |
360 | | * @buf: pointer to the buffer |
361 | | * @guid: pointer to the guid |
362 | | * Return: status code |
363 | | */ |
364 | | efi_status_t efi_next_variable_name(efi_uintn_t *size, u16 **buf, efi_guid_t *guid) |
365 | 0 | { |
366 | 0 | u16 *p; |
367 | 0 | efi_status_t ret; |
368 | 0 | efi_uintn_t buf_size = *size; |
369 | |
|
370 | 0 | ret = efi_get_next_variable_name_int(&buf_size, *buf, guid); |
371 | 0 | if (ret == EFI_NOT_FOUND) |
372 | 0 | return ret; |
373 | 0 | if (ret == EFI_BUFFER_TOO_SMALL) { |
374 | 0 | p = realloc(*buf, buf_size); |
375 | 0 | if (!p) |
376 | 0 | return EFI_OUT_OF_RESOURCES; |
377 | | |
378 | 0 | *buf = p; |
379 | 0 | *size = buf_size; |
380 | 0 | ret = efi_get_next_variable_name_int(&buf_size, *buf, guid); |
381 | 0 | } |
382 | | |
383 | 0 | return ret; |
384 | 0 | } |
385 | | |
386 | | /** |
387 | | * efi_search_bootorder() - search the boot option index in BootOrder |
388 | | * |
389 | | * @bootorder: pointer to the BootOrder variable |
390 | | * @num: number of BootOrder entry |
391 | | * @target: target boot option index to search |
392 | | * @index: pointer to store the index of BootOrder variable |
393 | | * Return: true if exists, false otherwise |
394 | | */ |
395 | | bool efi_search_bootorder(u16 *bootorder, efi_uintn_t num, u32 target, u32 *index) |
396 | 0 | { |
397 | 0 | u32 i; |
398 | |
|
399 | 0 | for (i = 0; i < num; i++) { |
400 | 0 | if (target == bootorder[i]) { |
401 | 0 | if (index) |
402 | 0 | *index = i; |
403 | |
|
404 | 0 | return true; |
405 | 0 | } |
406 | 0 | } |
407 | | |
408 | 0 | return false; |
409 | 0 | } |
410 | | |
411 | | /** |
412 | | * efi_env_set_load_options() - set load options from environment variable |
413 | | * |
414 | | * @handle: the image handle |
415 | | * @env_var: name of the environment variable |
416 | | * @load_options: pointer to load options (output) |
417 | | * Return: status code |
418 | | */ |
419 | | efi_status_t efi_env_set_load_options(efi_handle_t handle, |
420 | | const char *env_var, |
421 | | u16 **load_options) |
422 | 0 | { |
423 | 0 | const char *env = env_get(env_var); |
424 | 0 | size_t size; |
425 | 0 | u16 *pos; |
426 | 0 | efi_status_t ret; |
427 | |
|
428 | 0 | *load_options = NULL; |
429 | 0 | if (!env) |
430 | 0 | return EFI_SUCCESS; |
431 | 0 | size = sizeof(u16) * (utf8_utf16_strlen(env) + 1); |
432 | 0 | pos = calloc(size, 1); |
433 | 0 | if (!pos) |
434 | 0 | return EFI_OUT_OF_RESOURCES; |
435 | 0 | *load_options = pos; |
436 | 0 | utf8_utf16_strcpy(&pos, env); |
437 | 0 | ret = efi_set_load_options(handle, size, *load_options); |
438 | 0 | if (ret != EFI_SUCCESS) { |
439 | 0 | free(*load_options); |
440 | 0 | *load_options = NULL; |
441 | 0 | } |
442 | 0 | return ret; |
443 | 0 | } |
444 | | |
445 | | /** |
446 | | * copy_fdt() - Copy the device tree to a new location available to EFI |
447 | | * |
448 | | * The FDT is copied to a suitable location within the EFI memory map. |
449 | | * Additional 12 KiB are added to the space in case the device tree needs to be |
450 | | * expanded later with fdt_open_into(). |
451 | | * |
452 | | * @fdtp: On entry a pointer to the flattened device tree. |
453 | | * On exit a pointer to the copy of the flattened device tree. |
454 | | * FDT start |
455 | | * Return: status code |
456 | | */ |
457 | | static efi_status_t copy_fdt(void **fdtp) |
458 | 0 | { |
459 | 0 | efi_status_t ret = 0; |
460 | 0 | void *fdt, *new_fdt; |
461 | 0 | static u64 new_fdt_addr; |
462 | 0 | static efi_uintn_t fdt_pages; |
463 | 0 | ulong fdt_size; |
464 | 0 |
|
465 | 0 | /* |
466 | 0 | * Remove the configuration table that might already be |
467 | 0 | * installed, ignoring EFI_NOT_FOUND if no device-tree |
468 | 0 | * is installed |
469 | 0 | */ |
470 | 0 | efi_install_configuration_table(&efi_guid_fdt, NULL); |
471 | 0 |
|
472 | 0 | if (new_fdt_addr) { |
473 | 0 | log_debug("%s: Found allocated memory at %#llx, with %#zx pages\n", |
474 | 0 | __func__, new_fdt_addr, fdt_pages); |
475 | 0 |
|
476 | 0 | ret = efi_free_pages(new_fdt_addr, fdt_pages); |
477 | 0 | if (ret != EFI_SUCCESS) |
478 | 0 | log_err("Unable to free up existing FDT memory region\n"); |
479 | 0 |
|
480 | 0 | new_fdt_addr = 0; |
481 | 0 | fdt_pages = 0; |
482 | 0 | } |
483 | 0 |
|
484 | 0 | /* |
485 | 0 | * Give us at least 12 KiB of breathing room in case the device tree |
486 | 0 | * needs to be expanded later. |
487 | 0 | */ |
488 | 0 | fdt = *fdtp; |
489 | 0 | fdt_pages = efi_size_in_pages(fdt_totalsize(fdt) + CONFIG_SYS_FDT_PAD); |
490 | 0 | fdt_size = fdt_pages << EFI_PAGE_SHIFT; |
491 | 0 |
|
492 | 0 | ret = efi_allocate_pages(EFI_ALLOCATE_ANY_PAGES, |
493 | 0 | EFI_ACPI_RECLAIM_MEMORY, fdt_pages, |
494 | 0 | &new_fdt_addr); |
495 | 0 | if (ret != EFI_SUCCESS) { |
496 | 0 | log_err("Failed to reserve space for FDT\n"); |
497 | 0 | return ret; |
498 | 0 | } |
499 | 0 | log_debug("%s: Allocated memory at %#llx, with %#zx pages\n", |
500 | 0 | __func__, new_fdt_addr, fdt_pages); |
501 | 0 |
|
502 | 0 | new_fdt = (void *)(uintptr_t)new_fdt_addr; |
503 | 0 | memcpy(new_fdt, fdt, fdt_totalsize(fdt)); |
504 | 0 | fdt_set_totalsize(new_fdt, fdt_size); |
505 | 0 |
|
506 | 0 | *fdtp = new_fdt; |
507 | 0 |
|
508 | 0 | return EFI_SUCCESS; |
509 | 0 | } |
510 | | |
511 | | /** |
512 | | * efi_get_configuration_table() - get configuration table |
513 | | * |
514 | | * @guid: GUID of the configuration table |
515 | | * Return: pointer to configuration table or NULL |
516 | | */ |
517 | | void *efi_get_configuration_table(const efi_guid_t *guid) |
518 | 0 | { |
519 | 0 | size_t i; |
520 | |
|
521 | 0 | for (i = 0; i < systab.nr_tables; i++) { |
522 | 0 | if (!guidcmp(guid, &systab.tables[i].guid)) |
523 | 0 | return systab.tables[i].table; |
524 | 0 | } |
525 | 0 | return NULL; |
526 | 0 | } |
527 | | |
528 | | /** |
529 | | * efi_install_fdt() - install device tree |
530 | | * |
531 | | * If fdt is not EFI_FDT_USE_INTERNAL, the device tree located at that memory |
532 | | * address will be installed as configuration table, otherwise the device |
533 | | * tree located at the address indicated by environment variable fdt_addr or as |
534 | | * fallback fdtcontroladdr will be used. |
535 | | * |
536 | | * On architectures using ACPI tables device trees shall not be installed as |
537 | | * configuration table. |
538 | | * |
539 | | * @fdt: address of device tree or EFI_FDT_USE_INTERNAL to use |
540 | | * the hardware device tree as indicated by environment variable |
541 | | * fdt_addr or as fallback the internal device tree as indicated by |
542 | | * the environment variable fdtcontroladdr |
543 | | * Return: status code |
544 | | */ |
545 | | efi_status_t efi_install_fdt(void *fdt) |
546 | 0 | { |
547 | 0 | struct bootm_headers img = { 0 }; |
548 | 0 | efi_status_t ret; |
549 | | |
550 | | /* |
551 | | * The EBBR spec requires that we have either an FDT or an ACPI table |
552 | | * but not both. |
553 | | */ |
554 | 0 | if (CONFIG_IS_ENABLED(GENERATE_ACPI_TABLE) && fdt) |
555 | 0 | log_warning("Can't have ACPI table and device tree - ignoring DT.\n"); |
556 | |
|
557 | 0 | if (fdt == EFI_FDT_USE_INTERNAL) { |
558 | 0 | const char *fdt_opt; |
559 | 0 | uintptr_t fdt_addr; |
560 | | |
561 | | /* Check if there is a hardware device tree */ |
562 | 0 | fdt_opt = env_get("fdt_addr"); |
563 | | /* Use our own device tree as fallback */ |
564 | 0 | if (!fdt_opt) { |
565 | 0 | fdt_opt = env_get("fdtcontroladdr"); |
566 | 0 | if (!fdt_opt) { |
567 | 0 | log_err("need device tree\n"); |
568 | 0 | return EFI_NOT_FOUND; |
569 | 0 | } |
570 | 0 | } |
571 | 0 | fdt_addr = hextoul(fdt_opt, NULL); |
572 | 0 | if (!fdt_addr) { |
573 | 0 | log_err("invalid $fdt_addr or $fdtcontroladdr\n"); |
574 | 0 | return EFI_LOAD_ERROR; |
575 | 0 | } |
576 | 0 | fdt = map_sysmem(fdt_addr, 0); |
577 | 0 | } |
578 | | |
579 | | /* Install device tree */ |
580 | 0 | if (fdt_check_header(fdt)) { |
581 | 0 | log_err("invalid device tree\n"); |
582 | 0 | return EFI_LOAD_ERROR; |
583 | 0 | } |
584 | | |
585 | 0 | if (CONFIG_IS_ENABLED(GENERATE_ACPI_TABLE)) { |
586 | | /* Create memory reservations as indicated by the device tree */ |
587 | 0 | efi_carve_out_dt_rsv(fdt); |
588 | 0 | return EFI_SUCCESS; |
589 | 0 | } |
590 | | |
591 | | /* Prepare device tree for payload */ |
592 | 0 | ret = copy_fdt(&fdt); |
593 | 0 | if (ret) { |
594 | 0 | log_err("out of memory\n"); |
595 | 0 | return EFI_OUT_OF_RESOURCES; |
596 | 0 | } |
597 | | |
598 | 0 | if (image_setup_libfdt(&img, fdt, false)) { |
599 | 0 | log_err("failed to process device tree\n"); |
600 | 0 | return EFI_LOAD_ERROR; |
601 | 0 | } |
602 | | |
603 | | /* Create memory reservations as indicated by the device tree */ |
604 | 0 | efi_carve_out_dt_rsv(fdt); |
605 | |
|
606 | 0 | efi_try_purge_rng_seed(fdt); |
607 | |
|
608 | 0 | if (CONFIG_IS_ENABLED(EFI_TCG2_PROTOCOL_MEASURE_DTB)) { |
609 | 0 | ret = efi_tcg2_measure_dtb(fdt); |
610 | 0 | if (ret == EFI_SECURITY_VIOLATION) { |
611 | 0 | log_err("failed to measure DTB\n"); |
612 | 0 | return ret; |
613 | 0 | } |
614 | 0 | } |
615 | | |
616 | | /* Install device tree as UEFI table */ |
617 | 0 | ret = efi_install_configuration_table(&efi_guid_fdt, fdt); |
618 | 0 | if (ret != EFI_SUCCESS) { |
619 | 0 | log_err("failed to install device tree\n"); |
620 | 0 | return ret; |
621 | 0 | } |
622 | | |
623 | 0 | return EFI_SUCCESS; |
624 | 0 | } |
625 | | |
626 | | /** |
627 | | * efi_install_initrd() - install initrd |
628 | | * |
629 | | * Install the initrd located at @initrd using the EFI_LOAD_FILE2 |
630 | | * protocol. |
631 | | * |
632 | | * @initrd: address of initrd or NULL if none is provided |
633 | | * @initrd_sz: size of initrd |
634 | | * Return: status code |
635 | | */ |
636 | | efi_status_t efi_install_initrd(void *initrd, size_t initd_sz) |
637 | 0 | { |
638 | 0 | efi_status_t ret; |
639 | 0 | struct efi_device_path *dp_initrd; |
640 | |
|
641 | 0 | if (!initrd) |
642 | 0 | return EFI_SUCCESS; |
643 | | |
644 | 0 | dp_initrd = efi_dp_from_mem(EFI_LOADER_DATA, (uintptr_t)initrd, initd_sz); |
645 | 0 | if (!dp_initrd) |
646 | 0 | return EFI_OUT_OF_RESOURCES; |
647 | | |
648 | 0 | ret = efi_initrd_register(dp_initrd); |
649 | 0 | if (ret != EFI_SUCCESS) |
650 | 0 | efi_free_pool(dp_initrd); |
651 | |
|
652 | 0 | return ret; |
653 | 0 | } |
654 | | |
655 | | /** |
656 | | * do_bootefi_exec() - execute EFI binary |
657 | | * |
658 | | * The image indicated by @handle is started. When it returns the allocated |
659 | | * memory for the @load_options is freed. |
660 | | * |
661 | | * @handle: handle of loaded image |
662 | | * @load_options: load options |
663 | | * Return: status code |
664 | | * |
665 | | * Load the EFI binary into a newly assigned memory unwinding the relocation |
666 | | * information, install the loaded image protocol, and call the binary. |
667 | | */ |
668 | | efi_status_t do_bootefi_exec(efi_handle_t handle, void *load_options) |
669 | 0 | { |
670 | 0 | efi_status_t ret; |
671 | 0 | efi_uintn_t exit_data_size = 0; |
672 | 0 | u16 *exit_data = NULL; |
673 | 0 | struct efi_event *evt; |
674 | | |
675 | | /* On ARM switch from EL3 or secure mode to EL2 or non-secure mode */ |
676 | 0 | switch_to_non_secure_mode(); |
677 | | |
678 | | /* |
679 | | * The UEFI standard requires that the watchdog timer is set to five |
680 | | * minutes when invoking an EFI boot option. |
681 | | * |
682 | | * Unified Extensible Firmware Interface (UEFI), version 2.7 Errata A |
683 | | * 7.5. Miscellaneous Boot Services - EFI_BOOT_SERVICES.SetWatchdogTimer |
684 | | */ |
685 | 0 | ret = efi_set_watchdog(300); |
686 | 0 | if (ret != EFI_SUCCESS) { |
687 | 0 | log_err("failed to set watchdog timer\n"); |
688 | 0 | goto out; |
689 | 0 | } |
690 | | |
691 | | /* Call our payload! */ |
692 | 0 | ret = EFI_CALL(efi_start_image(handle, &exit_data_size, &exit_data)); |
693 | 0 | if (ret != EFI_SUCCESS) { |
694 | 0 | log_err("## Application failed, r = %lu\n", |
695 | 0 | ret & ~EFI_ERROR_MASK); |
696 | 0 | if (exit_data) { |
697 | 0 | log_err("## %ls\n", exit_data); |
698 | 0 | efi_free_pool(exit_data); |
699 | 0 | } |
700 | 0 | } |
701 | |
|
702 | 0 | out: |
703 | 0 | free(load_options); |
704 | | |
705 | | /* Notify EFI_EVENT_GROUP_RETURN_TO_EFIBOOTMGR event group. */ |
706 | 0 | list_for_each_entry(evt, &efi_events, link) { |
707 | 0 | if (evt->group && |
708 | 0 | !guidcmp(evt->group, |
709 | 0 | &efi_guid_event_group_return_to_efibootmgr)) { |
710 | 0 | efi_signal_event(evt); |
711 | 0 | EFI_CALL(systab.boottime->close_event(evt)); |
712 | 0 | break; |
713 | 0 | } |
714 | 0 | } |
715 | | |
716 | | /* Control is returned to U-Boot, disable EFI watchdog */ |
717 | 0 | efi_set_watchdog(0); |
718 | |
|
719 | 0 | return ret; |
720 | 0 | } |
721 | | |
722 | | /** |
723 | | * pmem_node_efi_memmap_setup() - Add pmem node and tweak EFI memmap |
724 | | * @fdt: The devicetree to which pmem node is added |
725 | | * @addr: start address of the pmem node |
726 | | * @size: size of the memory of the pmem node |
727 | | * |
728 | | * The function adds the pmem node to the device-tree along with removing |
729 | | * the corresponding region from the EFI memory map. Used primarily to |
730 | | * pass the information of a RAM based ISO image to the OS. |
731 | | * |
732 | | * Return: 0 on success, -ve value on error |
733 | | */ |
734 | | static int pmem_node_efi_memmap_setup(void *fdt, u64 addr, u64 size) |
735 | 0 | { |
736 | 0 | int ret; |
737 | 0 | u64 pages; |
738 | 0 | efi_status_t status; |
739 | |
|
740 | 0 | ret = fdt_fixup_pmem_region(fdt, addr, size); |
741 | 0 | if (ret) { |
742 | 0 | log_err("Failed to setup pmem node for addr %#llx, size %#llx, err %d\n", |
743 | 0 | addr, size, ret); |
744 | 0 | return ret; |
745 | 0 | } |
746 | | |
747 | | /* Remove the pmem region from the EFI memory map */ |
748 | 0 | pages = efi_size_in_pages(size + (addr & EFI_PAGE_MASK)); |
749 | 0 | status = efi_update_memory_map(addr, pages, EFI_CONVENTIONAL_MEMORY, |
750 | 0 | false, true); |
751 | 0 | if (status != EFI_SUCCESS) |
752 | 0 | return -1; |
753 | | |
754 | 0 | return 0; |
755 | 0 | } |
756 | | |
757 | | int fdt_efi_pmem_setup(void *fdt) |
758 | 0 | { |
759 | 0 | return blkmap_get_preserved_pmem_slices(pmem_node_efi_memmap_setup, |
760 | 0 | fdt); |
761 | 0 | } |