diff options
-rw-r--r-- | cmd/bootefi.c | 234 | ||||
-rw-r--r-- | cmd/conitrace.c | 4 | ||||
-rw-r--r-- | cmd/efidebug.c | 5 | ||||
-rw-r--r-- | common/image-fit.c | 6 | ||||
-rw-r--r-- | doc/uefi/uefi.rst | 11 | ||||
-rw-r--r-- | fs/fs.c | 3 | ||||
-rw-r--r-- | include/efi.h | 5 | ||||
-rw-r--r-- | include/efi_api.h | 34 | ||||
-rw-r--r-- | include/efi_dt_fixup.h | 39 | ||||
-rw-r--r-- | include/efi_loader.h | 10 | ||||
-rw-r--r-- | lib/efi_loader/Makefile | 3 | ||||
-rw-r--r-- | lib/efi_loader/efi_console.c | 6 | ||||
-rw-r--r-- | lib/efi_loader/efi_dt_fixup.c | 160 | ||||
-rw-r--r-- | lib/efi_loader/efi_file.c | 317 | ||||
-rw-r--r-- | lib/efi_loader/efi_image_loader.c | 80 | ||||
-rw-r--r-- | lib/efi_loader/efi_memory.c | 2 | ||||
-rw-r--r-- | lib/efi_loader/efi_root_node.c | 6 | ||||
-rw-r--r-- | lib/efi_loader/helloworld.c | 167 | ||||
-rw-r--r-- | lib/efi_selftest/dtbdump.c | 310 | ||||
-rw-r--r-- | net/tftp.c | 9 | ||||
-rwxr-xr-x | tools/efivar.py | 39 |
21 files changed, 1096 insertions, 354 deletions
diff --git a/cmd/bootefi.c b/cmd/bootefi.c index fdf909f..fe70eec 100644 --- a/cmd/bootefi.c +++ b/cmd/bootefi.c @@ -29,6 +29,82 @@ DECLARE_GLOBAL_DATA_PTR; static struct efi_device_path *bootefi_image_path; static struct efi_device_path *bootefi_device_path; +static void *image_addr; +static size_t image_size; + +/** + * efi_clear_bootdev() - clear boot device + */ +static void efi_clear_bootdev(void) +{ + efi_free_pool(bootefi_device_path); + efi_free_pool(bootefi_image_path); + bootefi_device_path = NULL; + bootefi_image_path = NULL; + image_addr = NULL; + image_size = 0; +} + +/** + * efi_set_bootdev() - set boot device + * + * This function is called when a file is loaded, e.g. via the 'load' command. + * We use the path to this file to inform the UEFI binary about the boot device. + * + * @dev: device, e.g. "MMC" + * @devnr: number of the device, e.g. "1:2" + * @path: path to file loaded + * @buffer: buffer with file loaded + * @buffer_size: size of file loaded + */ +void efi_set_bootdev(const char *dev, const char *devnr, const char *path, + void *buffer, size_t buffer_size) +{ + struct efi_device_path *device, *image; + efi_status_t ret; + + /* Forget overwritten image */ + if (buffer + buffer_size >= image_addr && + image_addr + image_size >= buffer) + efi_clear_bootdev(); + + /* Remember only PE-COFF and FIT images */ + if (efi_check_pe(buffer, buffer_size, NULL) != EFI_SUCCESS) { +#ifdef CONFIG_FIT + if (!fit_check_format(buffer)) + return; + /* + * FIT images of type EFI_OS are started via command bootm. + * We should not use their boot device with the bootefi command. + */ + buffer = 0; + buffer_size = 0; +#else + return; +#endif + } + + /* efi_set_bootdev() is typically called repeatedly, recover memory */ + efi_clear_bootdev(); + + image_addr = buffer; + image_size = buffer_size; + + ret = efi_dp_from_name(dev, devnr, path, &device, &image); + if (ret == EFI_SUCCESS) { + bootefi_device_path = device; + if (image) { + /* FIXME: image should not contain device */ + struct efi_device_path *image_tmp = image; + + efi_dp_split_file_path(image, &device, &image); + efi_free_pool(image_tmp); + } + bootefi_image_path = image; + } else { + efi_clear_bootdev(); + } +} /** * efi_env_set_load_options() - set load options from environment variable @@ -136,86 +212,6 @@ done: } /** - * efi_reserve_memory() - add reserved memory to memory map - * - * @addr: start address of the reserved memory range - * @size: size of the reserved memory range - * @nomap: indicates that the memory range shall not be accessed by the - * UEFI payload - */ -static void efi_reserve_memory(u64 addr, u64 size, bool nomap) -{ - int type; - efi_uintn_t ret; - - /* Convert from sandbox address space. */ - addr = (uintptr_t)map_sysmem(addr, 0); - - if (nomap) - type = EFI_RESERVED_MEMORY_TYPE; - else - type = EFI_BOOT_SERVICES_DATA; - - ret = efi_add_memory_map(addr, size, type); - if (ret != EFI_SUCCESS) - log_err("Reserved memory mapping failed addr %llx size %llx\n", - addr, size); -} - -/** - * efi_carve_out_dt_rsv() - Carve out DT reserved memory ranges - * - * The mem_rsv entries of the FDT are added to the memory map. Any failures are - * ignored because this is not critical and we would rather continue to try to - * boot. - * - * @fdt: Pointer to device tree - */ -static void efi_carve_out_dt_rsv(void *fdt) -{ - int nr_rsv, i; - u64 addr, size; - int nodeoffset, subnode; - - nr_rsv = fdt_num_mem_rsv(fdt); - - /* Look for an existing entry and add it to the efi mem map. */ - for (i = 0; i < nr_rsv; i++) { - if (fdt_get_mem_rsv(fdt, i, &addr, &size) != 0) - continue; - efi_reserve_memory(addr, size, false); - } - - /* process reserved-memory */ - nodeoffset = fdt_subnode_offset(fdt, 0, "reserved-memory"); - if (nodeoffset >= 0) { - subnode = fdt_first_subnode(fdt, nodeoffset); - while (subnode >= 0) { - fdt_addr_t fdt_addr; - fdt_size_t fdt_size; - - /* check if this subnode has a reg property */ - fdt_addr = fdtdec_get_addr_size_auto_parent( - fdt, nodeoffset, subnode, - "reg", 0, &fdt_size, false); - /* - * The /reserved-memory node may have children with - * a size instead of a reg property. - */ - if (fdt_addr != FDT_ADDR_T_NONE && - fdtdec_get_is_enabled(fdt, subnode)) { - bool nomap; - - nomap = !!fdt_getprop(fdt, subnode, "no-map", - NULL); - efi_reserve_memory(fdt_addr, fdt_size, nomap); - } - subnode = fdt_next_subnode(fdt, subnode); - } - } -} - -/** * get_config_table() - get configuration table * * @guid: GUID of the configuration table @@ -398,43 +394,28 @@ static int do_bootefi_image(const char *image_opt) { void *image_buf; unsigned long addr, size; - const char *size_str; efi_status_t ret; #ifdef CONFIG_CMD_BOOTEFI_HELLO if (!strcmp(image_opt, "hello")) { - char *saddr; - - saddr = env_get("loadaddr"); + image_buf = __efi_helloworld_begin; size = __efi_helloworld_end - __efi_helloworld_begin; - - if (saddr) - addr = simple_strtoul(saddr, NULL, 16); - else - addr = CONFIG_SYS_LOAD_ADDR; - - image_buf = map_sysmem(addr, size); - memcpy(image_buf, __efi_helloworld_begin, size); - - efi_free_pool(bootefi_device_path); - efi_free_pool(bootefi_image_path); - bootefi_device_path = NULL; - bootefi_image_path = NULL; + efi_clear_bootdev(); } else #endif { - size_str = env_get("filesize"); - if (size_str) - size = simple_strtoul(size_str, NULL, 16); - else - size = 0; - - addr = simple_strtoul(image_opt, NULL, 16); + addr = strtoul(image_opt, NULL, 16); /* Check that a numeric value was passed */ - if (!addr && *image_opt != '0') + if (!addr) return CMD_RET_USAGE; - image_buf = map_sysmem(addr, size); + image_buf = map_sysmem(addr, 0); + + if (image_buf != image_addr) { + log_err("No UEFI binary known at %s\n", image_opt); + return CMD_RET_FAILURE; + } + size = image_size; } ret = efi_run_image(image_buf, size); @@ -567,11 +548,8 @@ static efi_status_t bootefi_test_prepare if (ret == EFI_SUCCESS) return ret; - efi_free_pool(bootefi_image_path); - bootefi_image_path = NULL; failure: - efi_free_pool(bootefi_device_path); - bootefi_device_path = NULL; + efi_clear_bootdev(); return ret; } @@ -691,39 +669,3 @@ U_BOOT_CMD( "Boots an EFI payload from memory", bootefi_help_text ); - -/** - * efi_set_bootdev() - set boot device - * - * This function is called when a file is loaded, e.g. via the 'load' command. - * We use the path to this file to inform the UEFI binary about the boot device. - * - * @dev: device, e.g. "MMC" - * @devnr: number of the device, e.g. "1:2" - * @path: path to file loaded - */ -void efi_set_bootdev(const char *dev, const char *devnr, const char *path) -{ - struct efi_device_path *device, *image; - efi_status_t ret; - - /* efi_set_bootdev is typically called repeatedly, recover memory */ - efi_free_pool(bootefi_device_path); - efi_free_pool(bootefi_image_path); - - ret = efi_dp_from_name(dev, devnr, path, &device, &image); - if (ret == EFI_SUCCESS) { - bootefi_device_path = device; - if (image) { - /* FIXME: image should not contain device */ - struct efi_device_path *image_tmp = image; - - efi_dp_split_file_path(image, &device, &image); - efi_free_pool(image_tmp); - } - bootefi_image_path = image; - } else { - bootefi_device_path = NULL; - bootefi_image_path = NULL; - } -} diff --git a/cmd/conitrace.c b/cmd/conitrace.c index 811f5c6..d50f3bf 100644 --- a/cmd/conitrace.c +++ b/cmd/conitrace.c @@ -30,8 +30,8 @@ static int do_conitrace(struct cmd_tbl *cmdtp, int flag, int argc, printf("%02x ", c); first = false; - /* 1 ms delay - serves to detect separate keystrokes */ - udelay(1000); + /* 10 ms delay - serves to detect separate keystrokes */ + udelay(10000); if (!tstc()) { printf("\n"); first = true; diff --git a/cmd/efidebug.c b/cmd/efidebug.c index 5fb7b1e..6de81ca 100644 --- a/cmd/efidebug.c +++ b/cmd/efidebug.c @@ -8,6 +8,7 @@ #include <charset.h> #include <common.h> #include <command.h> +#include <efi_dt_fixup.h> #include <efi_loader.h> #include <efi_rng.h> #include <exports.h> @@ -495,6 +496,10 @@ static const struct { "PXE Base Code", EFI_PXE_BASE_CODE_PROTOCOL_GUID, }, + { + "Device-Tree Fixup", + EFI_DT_FIXUP_PROTOCOL_GUID, + }, /* Configuration table GUIDs */ { "ACPI table", diff --git a/common/image-fit.c b/common/image-fit.c index 6a8787c..21c44bd 100644 --- a/common/image-fit.c +++ b/common/image-fit.c @@ -1553,6 +1553,12 @@ int fit_image_check_comp(const void *fit, int noffset, uint8_t comp) */ int fit_check_format(const void *fit) { + /* A FIT image must be a valid FDT */ + if (fdt_check_header(fit)) { + debug("Wrong FIT format: not a flattened device tree\n"); + return 0; + } + /* mandatory / node 'description' property */ if (fdt_getprop(fit, 0, FIT_DESC_PROP, NULL) == NULL) { debug("Wrong FIT format: no description\n"); diff --git a/doc/uefi/uefi.rst b/doc/uefi/uefi.rst index dc930d9..5a67737 100644 --- a/doc/uefi/uefi.rst +++ b/doc/uefi/uefi.rst @@ -59,13 +59,10 @@ Below you find the output of an example session starting GRUB:: 120832 bytes read in 7 ms (16.5 MiB/s) => bootefi ${kernel_addr_r} ${fdt_addr_r} -The bootefi command uses the device, the file name, and the file size -(environment variable 'filesize') of the most recently loaded file when setting -up the binary for execution. So the UEFI binary should be loaded last. - -The environment variable 'bootargs' is passed as load options in the UEFI system -table. The Linux kernel EFI stub uses the load options as command line -arguments. +When booting from a memory location it is unknown from which file it was loaded. +Therefore the bootefi command uses the device path of the block device partition +or the network adapter and the file name of the most recently loaded PE-COFF +file when setting up the loaded image protocol. Launching a UEFI binary from a FIT image ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -752,7 +752,8 @@ int do_load(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[], if (IS_ENABLED(CONFIG_CMD_BOOTEFI)) efi_set_bootdev(argv[1], (argc > 2) ? argv[2] : "", - (argc > 4) ? argv[4] : ""); + (argc > 4) ? argv[4] : "", map_sysmem(addr, 0), + len_read); printf("%llu bytes read in %lu ms", len_read, time); if (time > 0) { diff --git a/include/efi.h b/include/efi.h index 5695273..503fbf0 100644 --- a/include/efi.h +++ b/include/efi.h @@ -20,6 +20,11 @@ #include <linux/string.h> #include <linux/types.h> +/* Type INTN in UEFI specification */ +#define efi_intn_t ssize_t +/* Type UINTN in UEFI specification*/ +#define efi_uintn_t size_t + /* * EFI on x86_64 uses the Microsoft ABI which is not the default for GCC. * diff --git a/include/efi_api.h b/include/efi_api.h index ecb43a0..df9bee2 100644 --- a/include/efi_api.h +++ b/include/efi_api.h @@ -34,8 +34,6 @@ enum efi_timer_delay { EFI_TIMER_RELATIVE = 2 }; -#define efi_intn_t ssize_t -#define efi_uintn_t size_t typedef void *efi_hii_handle_t; typedef u16 *efi_string_t; typedef u16 efi_string_id_t; @@ -703,10 +701,10 @@ struct efi_simple_text_output_protocol { char extended_verification); efi_status_t (EFIAPI *output_string)( struct efi_simple_text_output_protocol *this, - const efi_string_t str); + const u16 *str); efi_status_t (EFIAPI *test_string)( struct efi_simple_text_output_protocol *this, - const efi_string_t str); + const u16 *str); efi_status_t(EFIAPI *query_mode)( struct efi_simple_text_output_protocol *this, unsigned long mode_number, unsigned long *columns, @@ -1589,35 +1587,35 @@ struct efi_file_io_token { struct efi_file_handle { u64 rev; - efi_status_t (EFIAPI *open)(struct efi_file_handle *file, + efi_status_t (EFIAPI *open)(struct efi_file_handle *this, struct efi_file_handle **new_handle, u16 *file_name, u64 open_mode, u64 attributes); - efi_status_t (EFIAPI *close)(struct efi_file_handle *file); - efi_status_t (EFIAPI *delete)(struct efi_file_handle *file); - efi_status_t (EFIAPI *read)(struct efi_file_handle *file, + efi_status_t (EFIAPI *close)(struct efi_file_handle *this); + efi_status_t (EFIAPI *delete)(struct efi_file_handle *this); + efi_status_t (EFIAPI *read)(struct efi_file_handle *this, efi_uintn_t *buffer_size, void *buffer); - efi_status_t (EFIAPI *write)(struct efi_file_handle *file, + efi_status_t (EFIAPI *write)(struct efi_file_handle *this, efi_uintn_t *buffer_size, void *buffer); - efi_status_t (EFIAPI *getpos)(struct efi_file_handle *file, + efi_status_t (EFIAPI *getpos)(struct efi_file_handle *this, u64 *pos); - efi_status_t (EFIAPI *setpos)(struct efi_file_handle *file, + efi_status_t (EFIAPI *setpos)(struct efi_file_handle *this, u64 pos); - efi_status_t (EFIAPI *getinfo)(struct efi_file_handle *file, + efi_status_t (EFIAPI *getinfo)(struct efi_file_handle *this, const efi_guid_t *info_type, efi_uintn_t *buffer_size, void *buffer); - efi_status_t (EFIAPI *setinfo)(struct efi_file_handle *file, + efi_status_t (EFIAPI *setinfo)(struct efi_file_handle *this, const efi_guid_t *info_type, efi_uintn_t buffer_size, void *buffer); - efi_status_t (EFIAPI *flush)(struct efi_file_handle *file); - efi_status_t (EFIAPI *open_ex)(struct efi_file_handle *file, + efi_status_t (EFIAPI *flush)(struct efi_file_handle *this); + efi_status_t (EFIAPI *open_ex)(struct efi_file_handle *this, struct efi_file_handle **new_handle, u16 *file_name, u64 open_mode, u64 attributes, struct efi_file_io_token *token); - efi_status_t (EFIAPI *read_ex)(struct efi_file_handle *file, + efi_status_t (EFIAPI *read_ex)(struct efi_file_handle *this, struct efi_file_io_token *token); - efi_status_t (EFIAPI *write_ex)(struct efi_file_handle *file, + efi_status_t (EFIAPI *write_ex)(struct efi_file_handle *this, struct efi_file_io_token *token); - efi_status_t (EFIAPI *flush_ex)(struct efi_file_handle *file, + efi_status_t (EFIAPI *flush_ex)(struct efi_file_handle *this, struct efi_file_io_token *token); }; diff --git a/include/efi_dt_fixup.h b/include/efi_dt_fixup.h new file mode 100644 index 0000000..9066e8d --- /dev/null +++ b/include/efi_dt_fixup.h @@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * EFI_DT_FIXUP_PROTOCOL + * + * Copyright (c) 2020 Heinrich Schuchardt + */ + +#include <efi_api.h> + +#define EFI_DT_FIXUP_PROTOCOL_GUID \ + EFI_GUID(0xe617d64c, 0xfe08, 0x46da, 0xf4, 0xdc, \ + 0xbb, 0xd5, 0x87, 0x0c, 0x73, 0x00) + +#define EFI_DT_FIXUP_PROTOCOL_REVISION 0x00010000 + +/* Add nodes and update properties */ +#define EFI_DT_APPLY_FIXUPS 0x00000001 +/* + * Reserve memory according to the /reserved-memory node + * and the memory reservation block + */ +#define EFI_DT_RESERVE_MEMORY 0x00000002 +/* Install the device-tree as configuration table */ +#define EFI_DT_INSTALL_TABLE 0x00000004 + +#define EFI_DT_ALL (EFI_DT_APPLY_FIXUPS | \ + EFI_DT_RESERVE_MEMORY | \ + EFI_DT_INSTALL_TABLE) + +struct efi_dt_fixup_protocol { + u64 revision; + efi_status_t (EFIAPI *fixup) (struct efi_dt_fixup_protocol *this, + void *dtb, + efi_uintn_t *buffer_size, + u32 flags); +}; + +extern struct efi_dt_fixup_protocol efi_dt_fixup_prot; +extern const efi_guid_t efi_guid_dt_fixup_protocol; diff --git a/include/efi_loader.h b/include/efi_loader.h index 4719fa9..e53d286 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -411,6 +411,8 @@ void efi_runtime_detach(void); /* efi_convert_pointer() - convert pointer to virtual address */ efi_status_t EFIAPI efi_convert_pointer(efi_uintn_t debug_disposition, void **address); +/* Carve out DT reserved memory ranges */ +void efi_carve_out_dt_rsv(void *fdt); /* Called by bootefi to make console interface available */ efi_status_t efi_console_register(void); /* Called by bootefi to make all disk storage accessible as EFI objects */ @@ -460,6 +462,8 @@ efi_status_t efi_set_watchdog(unsigned long timeout); /* Called from places to check whether a timer expired */ void efi_timer_check(void); +/* Check if a buffer contains a PE-COFF image */ +efi_status_t efi_check_pe(void *buffer, size_t size, void **nt_header); /* PE loader implementation */ efi_status_t efi_load_pe(struct efi_loaded_image_obj *handle, void *efi, size_t efi_size, @@ -472,7 +476,8 @@ void efi_restore_gd(void); /* Call this to relocate the runtime section to an address space */ void efi_runtime_relocate(ulong offset, struct efi_mem_desc *map); /* Call this to set the current device name */ -void efi_set_bootdev(const char *dev, const char *devnr, const char *path); +void efi_set_bootdev(const char *dev, const char *devnr, const char *path, + void *buffer, size_t buffer_size); /* Add a new object to the object list. */ void efi_add_handle(efi_handle_t obj); /* Create handle */ @@ -871,7 +876,8 @@ static inline efi_status_t efi_add_runtime_mmio(void *mmio_ptr, u64 len) /* No loader configured, stub out EFI_ENTRY */ static inline void efi_restore_gd(void) { } static inline void efi_set_bootdev(const char *dev, const char *devnr, - const char *path) { } + const char *path, void *buffer, + size_t buffer_size) { } static inline void efi_net_set_dhcp_ack(void *pkt, int len) { } static inline void efi_print_image_infos(void *pc) { } static inline efi_status_t efi_launch_capsules(void) diff --git a/lib/efi_loader/Makefile b/lib/efi_loader/Makefile index 462d4d9..412fa88 100644 --- a/lib/efi_loader/Makefile +++ b/lib/efi_loader/Makefile @@ -29,6 +29,9 @@ obj-y += efi_console.o obj-y += efi_device_path.o obj-$(CONFIG_EFI_DEVICE_PATH_TO_TEXT) += efi_device_path_to_text.o obj-y += efi_device_path_utilities.o +ifeq ($(CONFIG_GENERATE_ACPI_TABLE),) +obj-y += efi_dt_fixup.o +endif obj-y += efi_file.o obj-$(CONFIG_EFI_LOADER_HII) += efi_hii.o obj-y += efi_image_loader.o diff --git a/lib/efi_loader/efi_console.c b/lib/efi_loader/efi_console.c index 7051095..edcfce7 100644 --- a/lib/efi_loader/efi_console.c +++ b/lib/efi_loader/efi_console.c @@ -141,12 +141,12 @@ static int term_read_reply(int *n, int num, char end_char) */ static efi_status_t EFIAPI efi_cout_output_string( struct efi_simple_text_output_protocol *this, - const efi_string_t string) + const u16 *string) { struct simple_text_output_mode *con = &efi_con_mode; struct cout_mode *mode = &efi_cout_modes[con->mode]; char *buf, *pos; - u16 *p; + const u16 *p; efi_status_t ret = EFI_SUCCESS; EFI_ENTRY("%p, %p", this, string); @@ -230,7 +230,7 @@ out: */ static efi_status_t EFIAPI efi_cout_test_string( struct efi_simple_text_output_protocol *this, - const efi_string_t string) + const u16 *string) { EFI_ENTRY("%p, %p", this, string); return EFI_EXIT(EFI_SUCCESS); diff --git a/lib/efi_loader/efi_dt_fixup.c b/lib/efi_loader/efi_dt_fixup.c new file mode 100644 index 0000000..5f0ae5c --- /dev/null +++ b/lib/efi_loader/efi_dt_fixup.c @@ -0,0 +1,160 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * EFI_DT_FIXUP_PROTOCOL + * + * Copyright (c) 2020 Heinrich Schuchardt + */ + +#include <common.h> +#include <efi_dt_fixup.h> +#include <efi_loader.h> +#include <mapmem.h> + +static efi_status_t EFIAPI efi_dt_fixup(struct efi_dt_fixup_protocol *this, + void *dtb, + efi_uintn_t *buffer_size, + u32 flags); + +struct efi_dt_fixup_protocol efi_dt_fixup_prot = { + .revision = EFI_DT_FIXUP_PROTOCOL_REVISION, + .fixup = efi_dt_fixup +}; + +const efi_guid_t efi_guid_dt_fixup_protocol = EFI_DT_FIXUP_PROTOCOL_GUID; + +/** + * efi_reserve_memory() - add reserved memory to memory map + * + * @addr: start address of the reserved memory range + * @size: size of the reserved memory range + * @nomap: indicates that the memory range shall not be accessed by the + * UEFI payload + */ +static void efi_reserve_memory(u64 addr, u64 size, bool nomap) +{ + int type; + efi_uintn_t ret; + + /* Convert from sandbox address space. */ + addr = (uintptr_t)map_sysmem(addr, 0); + + if (nomap) + type = EFI_RESERVED_MEMORY_TYPE; + else + type = EFI_BOOT_SERVICES_DATA; + + ret = efi_add_memory_map(addr, size, type); + if (ret != EFI_SUCCESS) + log_err("Reserved memory mapping failed addr %llx size %llx\n", + addr, size); +} + +/** + * efi_carve_out_dt_rsv() - Carve out DT reserved memory ranges + * + * The mem_rsv entries of the FDT are added to the memory map. Any failures are + * ignored because this is not critical and we would rather continue to try to + * boot. + * + * @fdt: Pointer to device tree + */ +void efi_carve_out_dt_rsv(void *fdt) +{ + int nr_rsv, i; + u64 addr, size; + int nodeoffset, subnode; + + nr_rsv = fdt_num_mem_rsv(fdt); + + /* Look for an existing entry and add it to the efi mem map. */ + for (i = 0; i < nr_rsv; i++) { + if (fdt_get_mem_rsv(fdt, i, &addr, &size) != 0) + continue; + efi_reserve_memory(addr, size, false); + } + + /* process reserved-memory */ + nodeoffset = fdt_subnode_offset(fdt, 0, "reserved-memory"); + if (nodeoffset >= 0) { + subnode = fdt_first_subnode(fdt, nodeoffset); + while (subnode >= 0) { + fdt_addr_t fdt_addr; + fdt_size_t fdt_size; + + /* check if this subnode has a reg property */ + fdt_addr = fdtdec_get_addr_size_auto_parent( + fdt, nodeoffset, subnode, + "reg", 0, &fdt_size, false); + /* + * The /reserved-memory node may have children with + * a size instead of a reg property. + */ + if (fdt_addr != FDT_ADDR_T_NONE && + fdtdec_get_is_enabled(fdt, subnode)) { + bool nomap; + + nomap = !!fdt_getprop(fdt, subnode, "no-map", + NULL); + efi_reserve_memory(fdt_addr, fdt_size, nomap); + } + subnode = fdt_next_subnode(fdt, subnode); + } + } +} + +static efi_status_t EFIAPI efi_dt_fixup(struct efi_dt_fixup_protocol *this, + void *dtb, + efi_uintn_t *buffer_size, + u32 flags) +{ + efi_status_t ret; + size_t required_size; + bootm_headers_t img = { 0 }; + + EFI_ENTRY("%p, %p, %p, %d", this, dtb, buffer_size, flags); + + if (this != &efi_dt_fixup_prot || !dtb || !buffer_size || + !flags || (flags & ~EFI_DT_ALL)) { + ret = EFI_INVALID_PARAMETER; + goto out; + } + if (fdt_check_header(dtb)) { + ret = EFI_INVALID_PARAMETER; + goto out; + } + if (flags & EFI_DT_APPLY_FIXUPS) { + required_size = fdt_off_dt_strings(dtb) + + fdt_size_dt_strings(dtb) + + 0x3000; + } else { + required_size = fdt_totalsize(dtb); + } + if (required_size > *buffer_size) { + *buffer_size = required_size; + ret = EFI_BUFFER_TOO_SMALL; + goto out; + } + fdt_set_totalsize(dtb, *buffer_size); + + if (flags & EFI_DT_APPLY_FIXUPS) { + if (image_setup_libfdt(&img, dtb, 0, NULL)) { + log_err("failed to process device tree\n"); + ret = EFI_INVALID_PARAMETER; + goto out; + } + } + if (flags & EFI_DT_RESERVE_MEMORY) + efi_carve_out_dt_rsv(dtb); + + if (EFI_DT_INSTALL_TABLE) { + ret = efi_install_configuration_table(&efi_guid_fdt, dtb); + if (ret != EFI_SUCCESS) { + log_err("ERROR: failed to install device tree\n"); + goto out; + } + } + + ret = EFI_SUCCESS; +out: + return EFI_EXIT(ret); +} diff --git a/lib/efi_loader/efi_file.c b/lib/efi_loader/efi_file.c index 72b7ec1..8ece8e7 100644 --- a/lib/efi_loader/efi_file.c +++ b/lib/efi_loader/efi_file.c @@ -246,18 +246,16 @@ error: return NULL; } -static efi_status_t EFIAPI efi_file_open(struct efi_file_handle *file, - struct efi_file_handle **new_handle, - u16 *file_name, u64 open_mode, u64 attributes) +static efi_status_t efi_file_open_int(struct efi_file_handle *this, + struct efi_file_handle **new_handle, + u16 *file_name, u64 open_mode, + u64 attributes) { - struct file_handle *fh = to_fh(file); + struct file_handle *fh = to_fh(this); efi_status_t ret; - EFI_ENTRY("%p, %p, \"%ls\", %llx, %llu", file, new_handle, - file_name, open_mode, attributes); - /* Check parameters */ - if (!file || !new_handle || !file_name) { + if (!this || !new_handle || !file_name) { ret = EFI_INVALID_PARAMETER; goto out; } @@ -292,6 +290,75 @@ static efi_status_t EFIAPI efi_file_open(struct efi_file_handle *file, ret = EFI_NOT_FOUND; } out: + return ret; +} + +/** + * efi_file_open_() + * + * This function implements the Open service of the File Protocol. + * See the UEFI spec for details. + * + * @this: EFI_FILE_PROTOCOL instance + * @new_handle: on return pointer to file handle + * @file_name: file name + * @open_mode: mode to open the file (read, read/write, create/read/write) + * @attributes: attributes for newly created file + */ +static efi_status_t EFIAPI efi_file_open(struct efi_file_handle *this, + struct efi_file_handle **new_handle, + u16 *file_name, u64 open_mode, + u64 attributes) +{ + efi_status_t ret; + + EFI_ENTRY("%p, %p, \"%ls\", %llx, %llu", this, new_handle, + file_name, open_mode, attributes); + + ret = efi_file_open_int(this, new_handle, file_name, open_mode, + attributes); + + return EFI_EXIT(ret); +} + +/** + * efi_file_open_ex() - open file asynchronously + * + * This function implements the OpenEx service of the File Protocol. + * See the UEFI spec for details. + * + * @this: EFI_FILE_PROTOCOL instance + * @new_handle: on return pointer to file handle + * @file_name: file name + * @open_mode: mode to open the file (read, read/write, create/read/write) + * @attributes: attributes for newly created file + * @token: transaction token + */ +static efi_status_t EFIAPI efi_file_open_ex(struct efi_file_handle *this, + struct efi_file_handle **new_handle, + u16 *file_name, u64 open_mode, + u64 attributes, + struct efi_file_io_token *token) +{ + efi_status_t ret; + + EFI_ENTRY("%p, %p, \"%ls\", %llx, %llu, %p", this, new_handle, + file_name, open_mode, attributes, token); + + if (!token) { + ret = EFI_INVALID_PARAMETER; + goto out; + } + + ret = efi_file_open_int(this, new_handle, file_name, open_mode, + attributes); + + if (ret == EFI_SUCCESS && token->event) { + token->status = EFI_SUCCESS; + efi_signal_event(token->event); + } + +out: return EFI_EXIT(ret); } @@ -441,19 +508,15 @@ static efi_status_t dir_read(struct file_handle *fh, u64 *buffer_size, return EFI_SUCCESS; } -static efi_status_t EFIAPI efi_file_read(struct efi_file_handle *file, - efi_uintn_t *buffer_size, void *buffer) +static efi_status_t efi_file_read_int(struct efi_file_handle *this, + efi_uintn_t *buffer_size, void *buffer) { - struct file_handle *fh = to_fh(file); + struct file_handle *fh = to_fh(this); efi_status_t ret = EFI_SUCCESS; u64 bs; - EFI_ENTRY("%p, %p, %p", file, buffer_size, buffer); - - if (!buffer_size) { - ret = EFI_INVALID_PARAMETER; - goto error; - } + if (!this || !buffer_size || !buffer) + return EFI_INVALID_PARAMETER; bs = *buffer_size; if (fh->isdir) @@ -465,34 +528,77 @@ static efi_status_t EFIAPI efi_file_read(struct efi_file_handle *file, else *buffer_size = SIZE_MAX; -error: + return ret; +} + +/** + * efi_file_read() - read file + * + * This function implements the Read() service of the EFI_FILE_PROTOCOL. + * + * See the Unified Extensible Firmware Interface (UEFI) specification for + * details. + * + * @this: file protocol instance + * @buffer_size: number of bytes to read + * @buffer: read buffer + * Return: status code + */ +static efi_status_t EFIAPI efi_file_read(struct efi_file_handle *this, + efi_uintn_t *buffer_size, void *buffer) +{ + efi_status_t ret; + + EFI_ENTRY("%p, %p, %p", this, buffer_size, buffer); + + ret = efi_file_read_int(this, buffer_size, buffer); + return EFI_EXIT(ret); } /** - * efi_file_write() - write to file + * efi_file_read_ex() - read file asynchonously * - * This function implements the Write() service of the EFI_FILE_PROTOCOL. + * This function implements the ReadEx() service of the EFI_FILE_PROTOCOL. * * See the Unified Extensible Firmware Interface (UEFI) specification for * details. * - * @file: file handle - * @buffer_size: number of bytes to write - * @buffer: buffer with the bytes to write + * @this: file protocol instance + * @token: transaction token * Return: status code */ -static efi_status_t EFIAPI efi_file_write(struct efi_file_handle *file, - efi_uintn_t *buffer_size, - void *buffer) +static efi_status_t EFIAPI efi_file_read_ex(struct efi_file_handle *this, + struct efi_file_io_token *token) { - struct file_handle *fh = to_fh(file); + efi_status_t ret; + + EFI_ENTRY("%p, %p", this, token); + + if (!token) { + ret = EFI_INVALID_PARAMETER; + goto out; + } + + ret = efi_file_read_int(this, &token->buffer_size, token->buffer); + + if (ret == EFI_SUCCESS && token->event) { + token->status = EFI_SUCCESS; + efi_signal_event(token->event); + } + +out: + return EFI_EXIT(ret); +} + +static efi_status_t efi_file_write_int(struct efi_file_handle *this, + efi_uintn_t *buffer_size, void *buffer) +{ + struct file_handle *fh = to_fh(this); efi_status_t ret = EFI_SUCCESS; loff_t actwrite; - EFI_ENTRY("%p, %p, %p", file, buffer_size, buffer); - - if (!file || !buffer_size || !buffer) { + if (!this || !buffer_size || !buffer) { ret = EFI_INVALID_PARAMETER; goto out; } @@ -521,6 +627,67 @@ static efi_status_t EFIAPI efi_file_write(struct efi_file_handle *file, fh->offset += actwrite; out: + return ret; +} + +/** + * efi_file_write() - write to file + * + * This function implements the Write() service of the EFI_FILE_PROTOCOL. + * + * See the Unified Extensible Firmware Interface (UEFI) specification for + * details. + * + * @this: file protocol instance + * @buffer_size: number of bytes to write + * @buffer: buffer with the bytes to write + * Return: status code + */ +static efi_status_t EFIAPI efi_file_write(struct efi_file_handle *this, + efi_uintn_t *buffer_size, + void *buffer) +{ + efi_status_t ret; + + EFI_ENTRY("%p, %p, %p", this, buffer_size, buffer); + + ret = efi_file_write_int(this, buffer_size, buffer); + + return EFI_EXIT(ret); +} + +/** + * efi_file_write_ex() - write to file + * + * This function implements the WriteEx() service of the EFI_FILE_PROTOCOL. + * + * See the Unified Extensible Firmware Interface (UEFI) specification for + * details. + * + * @this: file protocol instance + * @token: transaction token + * Return: status code + */ +static efi_status_t EFIAPI efi_file_write_ex(struct efi_file_handle *this, + struct efi_file_io_token *token) +{ + efi_status_t ret; + + EFI_ENTRY("%p, %p", this, token); + + if (!token) { + ret = EFI_INVALID_PARAMETER; + goto out; + } + + ret = efi_file_write_int(this, &token->buffer_size, token->buffer); + + if (ret == EFI_SUCCESS && token->event) { + token->status = EFI_SUCCESS; + efi_signal_event(token->event); + } + +out: return EFI_EXIT(ret); } @@ -761,36 +928,84 @@ out: return EFI_EXIT(ret); } -static efi_status_t EFIAPI efi_file_flush(struct efi_file_handle *file) +/** + * efi_file_flush_int() - flush file + * + * This is the internal implementation of the Flush() and FlushEx() services of + * the EFI_FILE_PROTOCOL. + * + * @this: file protocol instance + * Return: status code + */ +static efi_status_t efi_file_flush_int(struct efi_file_handle *this) { - EFI_ENTRY("%p", file); - return EFI_EXIT(EFI_SUCCESS); -} + struct file_handle *fh = to_fh(this); -static efi_status_t EFIAPI efi_file_open_ex(struct efi_file_handle *file, - struct efi_file_handle **new_handle, - u16 *file_name, u64 open_mode, u64 attributes, - struct efi_file_io_token *token) -{ - return EFI_UNSUPPORTED; -} + if (!this) + return EFI_INVALID_PARAMETER; -static efi_status_t EFIAPI efi_file_read_ex(struct efi_file_handle *file, - struct efi_file_io_token *token) -{ - return EFI_UNSUPPORTED; + if (!(fh->open_mode & EFI_FILE_MODE_WRITE)) + return EFI_ACCESS_DENIED; + + /* TODO: flush for file position after end of file */ + return EFI_SUCCESS; } -static efi_status_t EFIAPI efi_file_write_ex(struct efi_file_handle *file, - struct efi_file_io_token *token) +/** + * efi_file_flush() - flush file + * + * This function implements the Flush() service of the EFI_FILE_PROTOCOL. + * + * See the Unified Extensible Firmware Interface (UEFI) specification for + * details. + * + * @this: file protocol instance + * Return: status code + */ +static efi_status_t EFIAPI efi_file_flush(struct efi_file_handle *this) { - return EFI_UNSUPPORTED; + efi_status_t ret; + + EFI_ENTRY("%p", this); + + ret = efi_file_flush_int(this); + + return EFI_EXIT(ret); } -static efi_status_t EFIAPI efi_file_flush_ex(struct efi_file_handle *file, - struct efi_file_io_token *token) +/** + * efi_file_flush_ex() - flush file + * + * This function implements the FlushEx() service of the EFI_FILE_PROTOCOL. + * + * See the Unified Extensible Firmware Interface (UEFI) specification for + * details. + * + * @this: file protocol instance + * @token: transaction token + * Return: status code + */ +static efi_status_t EFIAPI efi_file_flush_ex(struct efi_file_handle *this, + struct efi_file_io_token *token) { - return EFI_UNSUPPORTED; + efi_status_t ret; + + EFI_ENTRY("%p, %p", this, token); + + if (!token) { + ret = EFI_INVALID_PARAMETER; + goto out; + } + + ret = efi_file_flush_int(this); + + if (ret == EFI_SUCCESS && token->event) { + token->status = EFI_SUCCESS; + efi_signal_event(token->event); + } + +out: + return EFI_EXIT(ret); } static const struct efi_file_handle efi_file_handle_protocol = { diff --git a/lib/efi_loader/efi_image_loader.c b/lib/efi_loader/efi_image_loader.c index 94f76ef..d4dd9e94 100644 --- a/lib/efi_loader/efi_image_loader.c +++ b/lib/efi_loader/efi_image_loader.c @@ -675,6 +675,46 @@ static bool efi_image_authenticate(void *efi, size_t efi_size) } #endif /* CONFIG_EFI_SECURE_BOOT */ + +/** + * efi_check_pe() - check if a memory buffer contains a PE-COFF image + * + * @buffer: buffer to check + * @size: size of buffer + * @nt_header: on return pointer to NT header of PE-COFF image + * Return: EFI_SUCCESS if the buffer contains a PE-COFF image + */ +efi_status_t efi_check_pe(void *buffer, size_t size, void **nt_header) +{ + IMAGE_DOS_HEADER *dos = buffer; + IMAGE_NT_HEADERS32 *nt; + + if (size < sizeof(*dos)) + return EFI_INVALID_PARAMETER; + + /* Check for DOS magix */ + if (dos->e_magic != IMAGE_DOS_SIGNATURE) + return EFI_INVALID_PARAMETER; + + /* + * Check if the image section header fits into the file. Knowing that at + * least one section header follows we only need to check for the length + * of the 64bit header which is longer than the 32bit header. + */ + if (size < dos->e_lfanew + sizeof(IMAGE_NT_HEADERS32)) + return EFI_INVALID_PARAMETER; + nt = (IMAGE_NT_HEADERS32 *)((u8 *)buffer + dos->e_lfanew); + + /* Check for PE-COFF magic */ + if (nt->Signature != IMAGE_NT_SIGNATURE) + return EFI_INVALID_PARAMETER; + + if (nt_header) + *nt_header = nt; + + return EFI_SUCCESS; +} + /** * efi_load_pe() - relocate EFI binary * @@ -705,36 +745,10 @@ efi_status_t efi_load_pe(struct efi_loaded_image_obj *handle, int supported = 0; efi_status_t ret; - /* Sanity check for a file header */ - if (efi_size < sizeof(*dos)) { - log_err("Truncated DOS Header\n"); - ret = EFI_LOAD_ERROR; - goto err; - } - - dos = efi; - if (dos->e_magic != IMAGE_DOS_SIGNATURE) { - log_err("Invalid DOS Signature\n"); - ret = EFI_LOAD_ERROR; - goto err; - } - - /* - * Check if the image section header fits into the file. Knowing that at - * least one section header follows we only need to check for the length - * of the 64bit header which is longer than the 32bit header. - */ - if (efi_size < dos->e_lfanew + sizeof(IMAGE_NT_HEADERS64)) { - log_err("Invalid offset for Extended Header\n"); - ret = EFI_LOAD_ERROR; - goto err; - } - - nt = (void *) ((char *)efi + dos->e_lfanew); - if (nt->Signature != IMAGE_NT_SIGNATURE) { - log_err("Invalid NT Signature\n"); - ret = EFI_LOAD_ERROR; - goto err; + ret = efi_check_pe(efi, efi_size, (void **)&nt); + if (ret != EFI_SUCCESS) { + log_err("Not a PE-COFF file\n"); + return EFI_LOAD_ERROR; } for (i = 0; machines[i]; i++) @@ -746,8 +760,7 @@ efi_status_t efi_load_pe(struct efi_loaded_image_obj *handle, if (!supported) { log_err("Machine type 0x%04x is not supported\n", nt->FileHeader.Machine); - ret = EFI_LOAD_ERROR; - goto err; + return EFI_LOAD_ERROR; } num_sections = nt->FileHeader.NumberOfSections; @@ -757,8 +770,7 @@ efi_status_t efi_load_pe(struct efi_loaded_image_obj *handle, if (efi_size < ((void *)sections + sizeof(sections[0]) * num_sections - efi)) { log_err("Invalid number of sections: %d\n", num_sections); - ret = EFI_LOAD_ERROR; - goto err; + return EFI_LOAD_ERROR; } /* Authenticate an image */ diff --git a/lib/efi_loader/efi_memory.c b/lib/efi_loader/efi_memory.c index 11e7553..a3106ab 100644 --- a/lib/efi_loader/efi_memory.c +++ b/lib/efi_loader/efi_memory.c @@ -541,8 +541,6 @@ efi_status_t efi_free_pages(uint64_t memory, efi_uintn_t pages) ret = efi_add_memory_map_pg(memory, pages, EFI_CONVENTIONAL_MEMORY, false); - /* Merging of adjacent free regions is missing */ - if (ret != EFI_SUCCESS) return EFI_NOT_FOUND; diff --git a/lib/efi_loader/efi_root_node.c b/lib/efi_loader/efi_root_node.c index b17db31..b411a12 100644 --- a/lib/efi_loader/efi_root_node.c +++ b/lib/efi_loader/efi_root_node.c @@ -7,6 +7,7 @@ #include <common.h> #include <malloc.h> +#include <efi_dt_fixup.h> #include <efi_loader.h> const efi_guid_t efi_u_boot_guid = U_BOOT_GUID; @@ -60,6 +61,11 @@ efi_status_t efi_root_node_register(void) /* Device path utilities protocol */ &efi_guid_device_path_utilities_protocol, (void *)&efi_device_path_utilities, +#if !CONFIG_IS_ENABLED(GENERATE_ACPI_TABLE) + /* Device-tree fix-up protocol */ + &efi_guid_dt_fixup_protocol, + (void *)&efi_dt_fixup_prot, +#endif #if CONFIG_IS_ENABLED(EFI_UNICODE_COLLATION_PROTOCOL2) #if CONFIG_IS_ENABLED(EFI_UNICODE_COLLATION_PROTOCOL) /* Deprecated Unicode collation protocol */ diff --git a/lib/efi_loader/helloworld.c b/lib/efi_loader/helloworld.c index 9ae2ee3..5c8b7a9 100644 --- a/lib/efi_loader/helloworld.c +++ b/lib/efi_loader/helloworld.c @@ -1,43 +1,41 @@ // SPDX-License-Identifier: GPL-2.0+ /* - * EFI hello world + * Hello world EFI application * - * Copyright (c) 2016 Google, Inc - * Written by Simon Glass <sjg@chromium.org> + * Copyright 2020, Heinrich Schuchardt <xypron.glpk@gmx.de> * - * This program demonstrates calling a boottime service. - * It writes a greeting and the load options to the console. + * This test program is used to test the invocation of an EFI application. + * It writes + * + * * a greeting + * * the firmware's UEFI version + * * the installed configuration tables + * * the boot device's device path and the file path + * + * to the console. */ -#include <common.h> #include <efi_api.h> static const efi_guid_t loaded_image_guid = EFI_LOADED_IMAGE_PROTOCOL_GUID; +static const efi_guid_t device_path_to_text_protocol_guid = + EFI_DEVICE_PATH_TO_TEXT_PROTOCOL_GUID; +static const efi_guid_t device_path_guid = EFI_DEVICE_PATH_PROTOCOL_GUID; static const efi_guid_t fdt_guid = EFI_FDT_GUID; static const efi_guid_t acpi_guid = EFI_ACPI_TABLE_GUID; static const efi_guid_t smbios_guid = SMBIOS_TABLE_GUID; +static struct efi_system_table *systable; +static struct efi_boot_services *boottime; +static struct efi_simple_text_output_protocol *con_out; + /** - * efi_main() - entry point of the EFI application. - * - * @handle: handle of the loaded image - * @systable: system table - * @return: status code + * print_uefi_revision() - print UEFI revision number */ -efi_status_t EFIAPI efi_main(efi_handle_t handle, - struct efi_system_table *systable) +static void print_uefi_revision(void) { - struct efi_simple_text_output_protocol *con_out = systable->con_out; - struct efi_boot_services *boottime = systable->boottime; - struct efi_loaded_image *loaded_image; - efi_status_t ret; - efi_uintn_t i; u16 rev[] = L"0.0.0"; - /* UEFI requires CR LF */ - con_out->output_string(con_out, L"Hello, world!\r\n"); - - /* Print the revision number */ rev[0] = (systable->hdr.revision >> 16) + '0'; rev[4] = systable->hdr.revision & 0xffff; for (; rev[4] >= 10;) { @@ -53,15 +51,15 @@ efi_status_t EFIAPI efi_main(efi_handle_t handle, con_out->output_string(con_out, L"Running on UEFI "); con_out->output_string(con_out, rev); con_out->output_string(con_out, L"\r\n"); +} + +/** + * print_config_tables() - print configuration tables + */ +static void print_config_tables(void) +{ + efi_uintn_t i; - /* Get the loaded image protocol */ - ret = boottime->handle_protocol(handle, &loaded_image_guid, - (void **)&loaded_image); - if (ret != EFI_SUCCESS) { - con_out->output_string - (con_out, L"Cannot open loaded image protocol\r\n"); - goto out; - } /* Find configuration tables */ for (i = 0; i < systable->nr_tables; ++i) { if (!memcmp(&systable->tables[i].guid, &fdt_guid, @@ -77,6 +75,16 @@ efi_status_t EFIAPI efi_main(efi_handle_t handle, con_out->output_string (con_out, L"Have SMBIOS table\r\n"); } +} + +/** + * print_load_options() - print load options + * + * @systable: system table + * @con_out: simple text output protocol + */ +void print_load_options(struct efi_loaded_image *loaded_image) +{ /* Output the load options */ con_out->output_string(con_out, L"Load options: "); if (loaded_image->load_options_size && loaded_image->load_options) @@ -85,6 +93,105 @@ efi_status_t EFIAPI efi_main(efi_handle_t handle, else con_out->output_string(con_out, L"<none>"); con_out->output_string(con_out, L"\r\n"); +} + +/** + * print_device_path() - print device path + * + * @device_path: device path to print + * @dp2txt: device path to text protocol + */ +efi_status_t print_device_path(struct efi_device_path *device_path, + struct efi_device_path_to_text_protocol *dp2txt) +{ + u16 *string; + efi_status_t ret; + + if (!device_path) { + con_out->output_string(con_out, L"<none>\r\n"); + return EFI_SUCCESS; + } + + string = dp2txt->convert_device_path_to_text(device_path, true, false); + if (!string) { + con_out->output_string + (con_out, L"Cannot convert device path to text\r\n"); + return EFI_OUT_OF_RESOURCES; + } + con_out->output_string(con_out, string); + con_out->output_string(con_out, L"\r\n"); + ret = boottime->free_pool(string); + if (ret != EFI_SUCCESS) { + con_out->output_string(con_out, L"Cannot free pool memory\r\n"); + return ret; + } + return EFI_SUCCESS; +} + +/** + * efi_main() - entry point of the EFI application. + * + * @handle: handle of the loaded image + * @systab: system table + * @return: status code + */ +efi_status_t EFIAPI efi_main(efi_handle_t handle, + struct efi_system_table *systab) +{ + struct efi_loaded_image *loaded_image; + struct efi_device_path_to_text_protocol *device_path_to_text; + struct efi_device_path *device_path; + efi_status_t ret; + + systable = systab; + boottime = systable->boottime; + con_out = systable->con_out; + + /* UEFI requires CR LF */ + con_out->output_string(con_out, L"Hello, world!\r\n"); + + print_uefi_revision(); + print_config_tables(); + + /* Get the loaded image protocol */ + ret = boottime->handle_protocol(handle, &loaded_image_guid, + (void **)&loaded_image); + if (ret != EFI_SUCCESS) { + con_out->output_string + (con_out, L"Cannot open loaded image protocol\r\n"); + goto out; + } + print_load_options(loaded_image); + + /* Get the device path to text protocol */ + ret = boottime->locate_protocol(&device_path_to_text_protocol_guid, + NULL, (void **)&device_path_to_text); + if (ret != EFI_SUCCESS) { + con_out->output_string + (con_out, L"Cannot open device path to text protocol\r\n"); + goto out; + } + if (!loaded_image->device_handle) { + con_out->output_string + (con_out, L"Missing device handle\r\n"); + goto out; + } + ret = boottime->handle_protocol(loaded_image->device_handle, + &device_path_guid, + (void **)&device_path); + if (ret != EFI_SUCCESS) { + con_out->output_string + (con_out, L"Missing devide path for device handle\r\n"); + goto out; + } + con_out->output_string(con_out, L"Boot device: "); + ret = print_device_path(device_path, device_path_to_text); + if (ret != EFI_SUCCESS) + goto out; + con_out->output_string(con_out, L"File path: "); + ret = print_device_path(loaded_image->file_path, device_path_to_text); + if (ret != EFI_SUCCESS) + goto out; out: boottime->exit(handle, ret, 0, NULL); diff --git a/lib/efi_selftest/dtbdump.c b/lib/efi_selftest/dtbdump.c index d90e3eb..efef759 100644 --- a/lib/efi_selftest/dtbdump.c +++ b/lib/efi_selftest/dtbdump.c @@ -8,10 +8,12 @@ #include <common.h> #include <efi_api.h> +#include <efi_dt_fixup.h> #define BUFFER_SIZE 64 #define ESC 0x17 -#define DEFAULT_FILENAME L"dtb.dtb" + +#define efi_size_in_pages(size) ((size + EFI_PAGE_MASK) >> EFI_PAGE_SHIFT) static struct efi_simple_text_output_protocol *cerr; static struct efi_simple_text_output_protocol *cout; @@ -21,6 +23,22 @@ static const efi_guid_t fdt_guid = EFI_FDT_GUID; static const efi_guid_t loaded_image_guid = EFI_LOADED_IMAGE_PROTOCOL_GUID; static const efi_guid_t guid_simple_file_system_protocol = EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID; +static efi_handle_t handle; +static struct efi_system_table *systable; +static const efi_guid_t efi_dt_fixup_protocol_guid = EFI_DT_FIXUP_PROTOCOL_GUID; +static const efi_guid_t efi_file_info_guid = EFI_FILE_INFO_GUID; + +/** + * error() - print error string + * + * @string: error text + */ +static void error(u16 *string) +{ + cout->set_attribute(cout, EFI_LIGHTRED | EFI_BACKGROUND_BLACK); + cout->output_string(cout, string); + cout->set_attribute(cout, EFI_LIGHTBLUE | EFI_BACKGROUND_BLACK); +} /** * input() - read string from console @@ -39,6 +57,7 @@ static efi_status_t efi_input(u16 *buffer, efi_uintn_t buffer_size) /* Drain the console input */ ret = cin->reset(cin, true); + *buffer = 0; for (;;) { ret = bs->wait_for_event(1, &cin->wait_for_key, &index); if (ret != EFI_SUCCESS) @@ -62,6 +81,7 @@ static efi_status_t efi_input(u16 *buffer, efi_uintn_t buffer_size) break; case 0x0a: /* Linefeed */ case 0x0d: /* Carriage return */ + cout->output_string(cout, L"\n"); return EFI_SUCCESS; default: break; @@ -73,6 +93,7 @@ static efi_status_t efi_input(u16 *buffer, efi_uintn_t buffer_size) pos < buffer_size - 1) { *outbuf = key.unicode_char; buffer[pos++] = key.unicode_char; + buffer[pos] = 0; cout->output_string(cout, outbuf); } } @@ -117,60 +138,228 @@ void *get_dtb(struct efi_system_table *systable) } /** - * efi_main() - entry point of the EFI application. + * skip_whitespace() - skip over leading whitespace * - * @handle: handle of the loaded image - * @systable: system table - * @return: status code + * @pos: UTF-16 string + * Return: pointer to first non-whitespace */ -efi_status_t EFIAPI efi_main(efi_handle_t handle, - struct efi_system_table *systable) +u16 *skip_whitespace(u16 *pos) { - efi_uintn_t ret; - u16 filename[BUFFER_SIZE] = {0}; - efi_uintn_t dtb_size; + for (; *pos && *pos <= 0x20; ++pos) + ; + return pos; +} + +/** + * starts_with() - check if @string starts with @keyword + * + * @string: string to search for keyword + * @keyword: keyword to be searched + * Return: true fi @string starts with the keyword + */ +bool starts_with(u16 *string, u16 *keyword) +{ + for (; *keyword; ++string, ++keyword) { + if (*string != *keyword) + return false; + } + return true; +} + +/** + * do_help() - print help + */ +void do_help(void) +{ + error(L"load <dtb> - load device-tree from file\n"); + error(L"save <dtb> - save device-tree to file\n"); + error(L"exit - exit the shell\n"); +} + +/** + * do_load() - load and install device-tree + * + * @filename: file name + * Return: status code + */ +efi_status_t do_load(u16 *filename) +{ + struct efi_dt_fixup_protocol *dt_fixup_prot; struct efi_loaded_image *loaded_image; struct efi_simple_file_system_protocol *file_system; - struct efi_file_handle *root, *file; + struct efi_file_handle *root = NULL, *file = NULL; + u64 addr = 0; + struct efi_file_info *info; struct fdt_header *dtb; + efi_uintn_t buffer_size; + efi_uintn_t pages; + efi_status_t ret, ret2; - cerr = systable->std_err; - cout = systable->con_out; - cin = systable->con_in; - bs = systable->boottime; + ret = bs->locate_protocol(&efi_dt_fixup_protocol_guid, NULL, + (void **)&dt_fixup_prot); + if (ret != EFI_SUCCESS) { + error(L"Device-tree fix-up protocol not found\n"); + return ret; + } - cout->set_attribute(cout, EFI_LIGHTGRAY | EFI_BACKGROUND_BLACK); - cout->clear_screen(cout); - cout->set_attribute(cout, EFI_YELLOW | EFI_BACKGROUND_BLACK); - cout->output_string(cout, L"DTB Dump\n\n"); - cout->set_attribute(cout, EFI_LIGHTGRAY | EFI_BACKGROUND_BLACK); + filename = skip_whitespace(filename); + + ret = bs->open_protocol(handle, &loaded_image_guid, + (void **)&loaded_image, NULL, NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL); + if (ret != EFI_SUCCESS) { + error(L"Loaded image protocol not found\n"); + return ret; + } + /* Open the simple file system protocol */ + ret = bs->open_protocol(loaded_image->device_handle, + &guid_simple_file_system_protocol, + (void **)&file_system, NULL, NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL); + if (ret != EFI_SUCCESS) { + error(L"Failed to open simple file system protocol\n"); + goto out; + } + + /* Open volume */ + ret = file_system->open_volume(file_system, &root); + if (ret != EFI_SUCCESS) { + error(L"Failed to open volume\n"); + goto out; + } + + /* Open file */ + ret = root->open(root, &file, filename, EFI_FILE_MODE_READ, 0); + if (ret != EFI_SUCCESS) { + error(L"File not found\n"); + goto out; + } + /* Get file size */ + buffer_size = 0; + ret = file->getinfo(file, &efi_file_info_guid, &buffer_size, NULL); + if (ret != EFI_BUFFER_TOO_SMALL) { + error(L"Can't get file info size\n"); + goto out; + } + ret = bs->allocate_pool(EFI_LOADER_DATA, buffer_size, (void **)&info); + if (ret != EFI_SUCCESS) { + error(L"Out of memory\n"); + goto out; + } + ret = file->getinfo(file, &efi_file_info_guid, &buffer_size, info); + if (ret != EFI_SUCCESS) { + error(L"Can't get file info\n"); + goto out; + } + buffer_size = info->file_size; + pages = efi_size_in_pages(buffer_size); + ret = bs->free_pool(info); + if (ret != EFI_SUCCESS) + error(L"Can't free memory pool\n"); + /* Read file */ + ret = bs->allocate_pages(EFI_ALLOCATE_ANY_PAGES, + EFI_ACPI_RECLAIM_MEMORY, + pages, &addr); + if (ret != EFI_SUCCESS) { + error(L"Out of memory\n"); + goto out; + } + dtb = (struct fdt_header *)(uintptr_t)addr; + ret = file->read(file, &buffer_size, dtb); + if (ret != EFI_SUCCESS) { + error(L"Can't read file\n"); + goto out; + } + /* Fixup file, expecting EFI_BUFFER_TOO_SMALL */ + ret = dt_fixup_prot->fixup(dt_fixup_prot, dtb, &buffer_size, + EFI_DT_APPLY_FIXUPS | EFI_DT_RESERVE_MEMORY | + EFI_DT_INSTALL_TABLE); + if (ret == EFI_BUFFER_TOO_SMALL) { + /* Read file into larger buffer */ + ret = bs->free_pages(addr, pages); + if (ret != EFI_SUCCESS) + error(L"Can't free memory pages\n"); + pages = efi_size_in_pages(buffer_size); + ret = bs->allocate_pages(EFI_ALLOCATE_ANY_PAGES, + EFI_ACPI_RECLAIM_MEMORY, + pages, &addr); + if (ret != EFI_SUCCESS) { + error(L"Out of memory\n"); + goto out; + } + dtb = (struct fdt_header *)(uintptr_t)addr; + ret = file->setpos(file, 0); + if (ret != EFI_SUCCESS) { + error(L"Can't position file\n"); + goto out; + } + ret = file->read(file, &buffer_size, dtb); + if (ret != EFI_SUCCESS) { + error(L"Can't read file\n"); + goto out; + } + buffer_size = pages << EFI_PAGE_SHIFT; + ret = dt_fixup_prot->fixup( + dt_fixup_prot, dtb, &buffer_size, + EFI_DT_APPLY_FIXUPS | EFI_DT_RESERVE_MEMORY | + EFI_DT_INSTALL_TABLE); + } + if (ret == EFI_SUCCESS) + cout->output_string(cout, L"device-tree installed\n"); + else + error(L"Device-tree fix-up failed\n"); +out: + if (addr) { + ret2 = bs->free_pages(addr, pages); + if (ret2 != EFI_SUCCESS) + error(L"Can't free memory pages\n"); + } + if (file) { + ret2 = file->close(file); + if (ret2 != EFI_SUCCESS) + error(L"Can't close file\n"); + } + if (root) { + ret2 = root->close(root); + if (ret2 != EFI_SUCCESS) + error(L"Can't close volume\n"); + } + return ret; +} + +/** + * do_save() - save current device-tree + * + * @filename: file name + * Return: status code + */ +efi_status_t do_save(u16 *filename) +{ + struct efi_loaded_image *loaded_image; + struct efi_simple_file_system_protocol *file_system; + efi_uintn_t dtb_size; + struct efi_file_handle *root, *file; + struct fdt_header *dtb; + efi_uintn_t ret; dtb = get_dtb(systable); if (!dtb) { - cerr->output_string(cout, L"DTB not found\n"); + error(L"DTB not found\n"); return EFI_NOT_FOUND; } if (f2h(dtb->magic) != FDT_MAGIC) { - cerr->output_string(cout, L"Wrong device tree magic\n"); + error(L"Wrong device tree magic\n"); return EFI_NOT_FOUND; } dtb_size = f2h(dtb->totalsize); - cout->output_string(cout, L"Filename (" DEFAULT_FILENAME ")?\n"); - ret = efi_input(filename, sizeof(filename)); - if (ret != EFI_SUCCESS) - return ret; - if (!*filename) - memcpy(filename, DEFAULT_FILENAME, sizeof(DEFAULT_FILENAME)); - - cout->output_string(cout, L"\n"); + filename = skip_whitespace(filename); ret = bs->open_protocol(handle, &loaded_image_guid, (void **)&loaded_image, NULL, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL); if (ret != EFI_SUCCESS) { - cerr->output_string(cout, - L"Loaded image protocol not found\n"); + error(L"Loaded image protocol not found\n"); return ret; } @@ -180,15 +369,14 @@ efi_status_t EFIAPI efi_main(efi_handle_t handle, (void **)&file_system, NULL, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL); if (ret != EFI_SUCCESS) { - cerr->output_string( - cout, L"Failed to open simple file system protocol\n"); + error(L"Failed to open simple file system protocol\n"); return ret; } /* Open volume */ ret = file_system->open_volume(file_system, &root); if (ret != EFI_SUCCESS) { - cerr->output_string(cerr, L"Failed to open volume\n"); + error(L"Failed to open volume\n"); return ret; } /* Create file */ @@ -199,10 +387,10 @@ efi_status_t EFIAPI efi_main(efi_handle_t handle, /* Write file */ ret = file->write(file, &dtb_size, dtb); if (ret != EFI_SUCCESS) - cerr->output_string(cerr, L"Failed to write file\n"); + error(L"Failed to write file\n"); file->close(file); } else { - cerr->output_string(cerr, L"Failed to open file\n"); + error(L"Failed to open file\n"); } root->close(root); @@ -213,3 +401,51 @@ efi_status_t EFIAPI efi_main(efi_handle_t handle, return ret; } + +/** + * efi_main() - entry point of the EFI application. + * + * @handle: handle of the loaded image + * @systab: system table + * @return: status code + */ +efi_status_t EFIAPI efi_main(efi_handle_t image_handle, + struct efi_system_table *systab) +{ + handle = image_handle; + systable = systab; + cerr = systable->std_err; + cout = systable->con_out; + cin = systable->con_in; + bs = systable->boottime; + + cout->set_attribute(cout, EFI_LIGHTBLUE | EFI_BACKGROUND_BLACK); + cout->clear_screen(cout); + cout->set_attribute(cout, EFI_WHITE | EFI_BACKGROUND_BLACK); + cout->output_string(cout, L"DTB Dump\n========\n\n"); + cout->set_attribute(cout, EFI_LIGHTBLUE | EFI_BACKGROUND_BLACK); + + for (;;) { + u16 command[BUFFER_SIZE]; + u16 *pos; + efi_uintn_t ret; + + cout->output_string(cout, L"=> "); + ret = efi_input(command, sizeof(command)); + if (ret == EFI_ABORTED) + break; + pos = skip_whitespace(command); + if (starts_with(pos, L"exit")) + break; + else if (starts_with(pos, L"load ")) + do_load(pos + 5); + else if (starts_with(pos, L"save ")) + do_save(pos + 5); + else + do_help(); + } + + cout->set_attribute(cout, EFI_LIGHTGRAY | EFI_BACKGROUND_BLACK); + cout->clear_screen(cout); + return EFI_SUCCESS; +} @@ -329,6 +329,12 @@ static void tftp_complete(void) time_start * 1000, "/s"); } puts("\ndone\n"); + if (IS_ENABLED(CONFIG_CMD_BOOTEFI)) { + if (!tftp_put_active) + efi_set_bootdev("Net", "", tftp_filename, + map_sysmem(tftp_load_addr, 0), + net_boot_file_size); + } net_set_state(NETLOOP_SUCCESS); } @@ -841,9 +847,6 @@ void tftp_start(enum proto_t protocol) printf("Load address: 0x%lx\n", tftp_load_addr); puts("Loading: *\b"); tftp_state = STATE_SEND_RRQ; -#ifdef CONFIG_CMD_BOOTEFI - efi_set_bootdev("Net", "", tftp_filename); -#endif } time_start = get_timer(0); diff --git a/tools/efivar.py b/tools/efivar.py index ebfcab2..67729fa 100755 --- a/tools/efivar.py +++ b/tools/efivar.py @@ -51,21 +51,21 @@ var_guids = { } class EfiStruct: - # struct efi_var_file - var_file_fmt = '<QQLL' - var_file_size = struct.calcsize(var_file_fmt) - # struct efi_var_entry - var_entry_fmt = '<LLQ16s' - var_entry_size = struct.calcsize(var_entry_fmt) - # struct efi_time - var_time_fmt = '<H6BLh2B' - var_time_size = struct.calcsize(var_time_fmt) - # WIN_CERTIFICATE - var_win_cert_fmt = '<L2H' - var_win_cert_size = struct.calcsize(var_win_cert_fmt) - # WIN_CERTIFICATE_UEFI_GUID - var_win_cert_uefi_guid_fmt = var_win_cert_fmt+'16s' - var_win_cert_uefi_guid_size = struct.calcsize(var_win_cert_uefi_guid_fmt) + # struct efi_var_file + var_file_fmt = '<QQLL' + var_file_size = struct.calcsize(var_file_fmt) + # struct efi_var_entry + var_entry_fmt = '<LLQ16s' + var_entry_size = struct.calcsize(var_entry_fmt) + # struct efi_time + var_time_fmt = '<H6BLh2B' + var_time_size = struct.calcsize(var_time_fmt) + # WIN_CERTIFICATE + var_win_cert_fmt = '<L2H' + var_win_cert_size = struct.calcsize(var_win_cert_fmt) + # WIN_CERTIFICATE_UEFI_GUID + var_win_cert_uefi_guid_fmt = var_win_cert_fmt+'16s' + var_win_cert_uefi_guid_size = struct.calcsize(var_win_cert_uefi_guid_fmt) class EfiVariable: def __init__(self, size, attrs, time, guid, name, data): @@ -149,7 +149,7 @@ class EfiVariableStore: offs = 0 while offs < len(self.ents): var, loffs = self._next_var(offs) - if var.name == name and str(var.guid): + if var.name == name and str(var.guid) == guid: if var.attrs != attrs: print("err: attributes don't match") exit(1) @@ -292,7 +292,7 @@ def pkcs7_sign(cert, key, buf): # UEFI 2.8 Errata B "8.2.2 Using the EFI_VARIABLE_AUTHENTICATION_2 descriptor" def cmd_sign(args): - guid, name, attrs, data, size = parse_args(args) + guid, name, attrs, data, _ = parse_args(args) attrs |= EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS efi = EfiStruct() @@ -357,7 +357,10 @@ def main(): signp.set_defaults(func=cmd_sign) args = ap.parse_args() - args.func(args) + if hasattr(args, "func"): + args.func(args) + else: + ap.print_help() def group(a, *ns): for n in ns: |